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) {
273 if ((t = strrchr (CurrentFolder, '/')) != NULL)
280 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (val));
285 static int path_from_string (struct option_t* dst, const char* val,
286 char* errbuf, size_t errlen) {
287 char path[_POSIX_PATH_MAX];
293 mem_free ((char**) dst->data);
298 strfcpy (path, val, sizeof (path));
299 mutt_expand_path (path, sizeof (path));
300 str_replace ((char **) dst->data, path);
304 static int str_from_string (struct option_t* dst, const char* val,
305 char* errbuf, size_t errlen) {
309 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
312 str_replace ((char**) dst->data, val);
316 static int user_from_string (struct option_t* dst, const char* val,
317 char* errbuf, size_t errlen) {
318 /* if dst == NULL, we may get here in case the user did unset it,
319 * see parse_set() where item is free()'d before coming here; so
320 * just silently ignore it */
323 if (str_len ((char*) dst->data) == 0)
324 dst->data = (unsigned long) str_dup (val);
326 char* s = (char*) dst->data;
327 str_replace (&s, val);
329 if (str_len (dst->init) == 0)
330 dst->init = str_dup ((char*) dst->data);
334 static void quad_to_string (char* dst, size_t dstlen,
335 struct option_t* option) {
336 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
337 snprintf (dst, dstlen, "%s=%s", option->option,
338 vals[quadoption (option->data)]);
341 static int quad_from_string (struct option_t* dst, const char* val,
342 char* errbuf, size_t errlen) {
347 if (ascii_strncasecmp (val, "yes", 3) == 0)
349 else if (ascii_strncasecmp (val, "no", 2) == 0)
351 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
353 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
359 set_quadoption (dst->data, flag);
363 static void sort_to_string (char* dst, size_t dstlen,
364 struct option_t* option) {
365 const struct mapping_t *map = get_sortmap (option);
369 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
373 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
376 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
377 (*((short *) option->data) & SORT_REVERSE) ?
379 (*((short *) option->data) & SORT_LAST) ? "last-" :
383 static int sort_from_string (struct option_t* dst, const char* val,
384 char* errbuf, size_t errlen) {
385 const struct mapping_t *map = NULL;
386 if (!(map = get_sortmap (dst))) {
388 snprintf (errbuf, errlen, _("%s: Unknown type."),
392 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
397 static void rx_to_string (char* dst, size_t dstlen,
398 struct option_t* option) {
399 rx_t* p = (rx_t*) option->data;
400 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
401 NONULL (p->pattern));
404 static int rx_from_string (struct option_t* dst, const char* val,
405 char* errbuf, size_t errlen) {
408 int flags = 0, e = 0, not = 0;
414 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
416 snprintf (errbuf, errlen,
417 "Operation not permitted when in attach-message mode.");
421 if (!((rx_t*) dst->data))
422 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
424 p = (rx_t*) dst->data;
426 /* something to do? */
427 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
430 if (str_cmp (dst->option, "mask") != 0)
431 flags |= mutt_which_case (val);
434 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
439 rx = mem_malloc (sizeof (regex_t));
441 if ((e = REGCOMP (rx, s, flags)) != 0) {
442 regerror (e, rx, errbuf, errlen);
452 str_replace (&p->pattern, val);
456 if (str_cmp (dst->option, "reply_regexp") == 0)
457 mutt_adjust_all_subjects ();
462 static void magic_to_string (char* dst, size_t dstlen,
463 struct option_t* option) {
464 const char* s = NULL;
465 switch (option->data) {
466 case M_MBOX: s = "mbox"; break;
467 case M_MMDF: s = "MMDF"; break;
468 case M_MH: s = "MH"; break;
469 case M_MAILDIR: s = "Maildir"; break;
470 default: s = "unknown"; break;
472 snprintf (dst, dstlen, "%s=%s", option->option, s);
475 static int magic_from_string (struct option_t* dst, const char* val,
476 char* errbuf, size_t errlen) {
479 if (!dst || !val || !*val)
481 if (ascii_strncasecmp (val, "mbox", 4) == 0)
483 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
485 else if (ascii_strncasecmp (val, "mh", 2) == 0)
487 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
493 *((short*) dst->data) = flag;
498 static void addr_to_string (char* dst, size_t dstlen,
499 struct option_t* option) {
502 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
503 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
506 static int addr_from_string (struct option_t* dst, const char* val,
507 char* errbuf, size_t errlen) {
508 if (!dst || !val || !*val)
510 rfc822_free_address ((ADDRESS**) dst->data);
511 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
515 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
516 struct option_t* option = NULL;
517 char* tmp = NULL, *t = NULL;
520 if (!(option = hash_find (ConfigOptions, val))) {
521 debug_print (1, ("var '%s' not found\n", val));
525 tmp = mem_malloc (dstlen+1);
526 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
528 /* as we get things of type $var=value and don't want to bloat the
529 * above "just" for expansion, we do the stripping here */
530 debug_print (1, ("orig == '%s'\n", tmp));
531 t = strchr (tmp, '=');
535 if (t[l-1] == '"' && *t == '"') {
540 memcpy (dst, t, l+1);
542 debug_print (1, ("stripped == '%s'\n", dst));
547 /* for synonym warning reports: adds synonym to end of list */
548 static void syn_add (struct option_t* n, struct option_t* o) {
549 syn_t* tmp = mem_malloc (sizeof (syn_t));
550 tmp->f = str_dup (CurRCFile);
554 list_push_back (&Synonyms, tmp);
557 /* for synonym warning reports: free single item (for list_del()) */
558 static void syn_del (void** p) {
559 mem_free(&(*(syn_t**) p)->f);
563 void toggle_quadoption (int opt)
566 int b = (opt % 4) * 2;
568 QuadOptions[n] ^= (1 << b);
571 void set_quadoption (int opt, int flag)
574 int b = (opt % 4) * 2;
576 QuadOptions[n] &= ~(0x3 << b);
577 QuadOptions[n] |= (flag & 0x3) << b;
580 int quadoption (int opt)
583 int b = (opt % 4) * 2;
585 return (QuadOptions[n] >> b) & 0x3;
588 int query_quadoption (int opt, const char *prompt)
590 int v = quadoption (opt);
598 v = mutt_yesorno (prompt, (v == M_ASKYES));
599 CLEARLINE (LINES - 1);
606 static void add_to_list (LIST ** list, const char *str)
608 LIST *t, *last = NULL;
610 /* don't add a NULL or empty string to the list */
611 if (!str || *str == '\0')
614 /* check to make sure the item is not already on this list */
615 for (last = *list; last; last = last->next) {
616 if (ascii_strcasecmp (str, last->data) == 0) {
617 /* already on the list, so just ignore it */
625 if (!*list || last) {
626 t = (LIST *) mem_calloc (1, sizeof (LIST));
627 t->data = str_dup (str);
637 static int add_to_rx_list (list2_t** list, const char *s, int flags,
646 if (!(rx = rx_compile (s, flags))) {
647 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
651 i = rx_lookup ((*list), rx->pattern);
655 list_push_back (list, rx);
659 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
660 const char *templ, BUFFER * err)
662 SPAM_LIST *t = NULL, *last = NULL;
667 if (!pat || !*pat || !templ)
670 if (!(rx = rx_compile (pat, REG_ICASE))) {
671 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
675 /* check to make sure the item is not already on this list */
676 for (last = *list; last; last = last->next) {
677 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
678 /* Already on the list. Formerly we just skipped this case, but
679 * now we're supporting removals, which means we're supporting
680 * re-adds conceptually. So we probably want this to imply a
681 * removal, then do an add. We can achieve the removal by freeing
682 * the template, and leaving t pointed at the current item.
685 mem_free(t->template);
692 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
693 * update. Otherwise we want to make a new one to link at the list's end.
696 t = mutt_new_spam_list ();
704 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
705 t->template = str_dup (templ);
707 /* Find highest match number in template string */
709 for (p = templ; *p;) {
714 while (*p && isdigit ((int) *p))
720 t->nmatch++; /* match 0 is always the whole expr */
725 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
727 SPAM_LIST *spam, *prev;
730 /* Being first is a special case. */
734 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
737 mem_free(&spam->template);
743 for (spam = prev->next; spam;) {
744 if (!str_cmp (spam->rx->pattern, pat)) {
745 prev->next = spam->next;
747 mem_free(spam->template);
760 static void remove_from_list (LIST ** l, const char *str)
762 LIST *p, *last = NULL;
764 if (str_cmp ("*", str) == 0)
765 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
770 if (ascii_strcasecmp (str, p->data) == 0) {
773 last->next = p->next;
786 static int remove_from_rx_list (list2_t** l, const char *str)
790 if (str_cmp ("*", str) == 0) {
791 list_del (l, (list_del_t*) rx_free);
795 i = rx_lookup ((*l), str);
797 rx_t* r = list_pop_idx ((*l), i);
805 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
810 struct option_t* option = NULL;
812 memset (&token, 0, sizeof (token));
813 mutt_extract_token (tmp, s, 0);
815 /* is the item defined as a variable or a function? */
816 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
819 for (i = 0; !res && i < MENU_MAX; i++) {
820 struct binding_t *b = km_get_table (Menus[i].value);
825 for (j = 0; b[j].name; j++)
826 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
827 && (str_len (b[j].name) == str_len (tmp->data))) {
833 /* check for feature_* */
834 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
835 (j = str_len (tmp->data)) > 8) {
837 while (Features[i]) {
838 if (str_len (Features[i]) == j-8 &&
839 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
849 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
851 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
855 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
858 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
859 mutt_error ("Error: %s", err->data);
860 mem_free (&token.data);
863 mem_free (&token.data);
868 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
872 mutt_extract_token (buf, s, 0);
874 /* don't add "*" to the unignore list */
875 if (strcmp (buf->data, "*"))
876 add_to_list (&UnIgnore, buf->data);
878 remove_from_list (&Ignore, buf->data);
880 while (MoreArgs (s));
885 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
889 mutt_extract_token (buf, s, 0);
890 remove_from_list (&UnIgnore, buf->data);
891 add_to_list (&Ignore, buf->data);
893 while (MoreArgs (s));
898 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
902 mutt_extract_token (buf, s, 0);
903 add_to_list ((LIST **) data, buf->data);
905 while (MoreArgs (s));
910 static void _alternates_clean (void)
914 if (Context && Context->msgcount) {
915 for (i = 0; i < Context->msgcount; i++)
916 Context->hdrs[i]->recip_valid = 0;
920 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
923 _alternates_clean ();
925 mutt_extract_token (buf, s, 0);
926 remove_from_rx_list (&UnAlternates, buf->data);
928 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
931 while (MoreArgs (s));
936 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
939 _alternates_clean ();
941 mutt_extract_token (buf, s, 0);
942 remove_from_rx_list (&Alternates, buf->data);
944 if (str_cmp (buf->data, "*") &&
945 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
949 while (MoreArgs (s));
954 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
959 memset (&templ, 0, sizeof (templ));
961 /* Insist on at least one parameter */
964 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
966 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
970 /* Extract the first token, a regexp */
971 mutt_extract_token (buf, s, 0);
973 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
974 if (data == M_SPAM) {
975 /* If there's a second parameter, it's a template for the spam tag. */
977 mutt_extract_token (&templ, s, 0);
979 /* Add to the spam list. */
980 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
981 mem_free (&templ.data);
984 mem_free (&templ.data);
987 /* If not, try to remove from the nospam list. */
989 remove_from_rx_list (&NoSpamList, buf->data);
995 /* M_NOSPAM is for nospam commands. */
996 else if (data == M_NOSPAM) {
997 /* nospam only ever has one parameter. */
999 /* "*" is a special case. */
1000 if (!str_cmp (buf->data, "*")) {
1001 mutt_free_spam_list (&SpamList);
1002 list_del (&NoSpamList, (list_del_t*) rx_free);
1006 /* If it's on the spam list, just remove it. */
1007 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1010 /* Otherwise, add it to the nospam list. */
1011 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1017 /* This should not happen. */
1018 strfcpy (err->data, "This is no good at all.", err->dsize);
1022 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1026 mutt_extract_token (buf, s, 0);
1028 * Check for deletion of entire list
1030 if (str_cmp (buf->data, "*") == 0) {
1031 mutt_free_list ((LIST **) data);
1034 remove_from_list ((LIST **) data, buf->data);
1036 while (MoreArgs (s));
1041 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1045 mutt_extract_token (buf, s, 0);
1046 remove_from_rx_list (&UnMailLists, buf->data);
1048 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1051 while (MoreArgs (s));
1056 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1060 mutt_extract_token (buf, s, 0);
1061 remove_from_rx_list (&SubscribedLists, buf->data);
1062 remove_from_rx_list (&MailLists, buf->data);
1064 if (str_cmp (buf->data, "*") &&
1065 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1068 while (MoreArgs (s));
1073 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1077 mutt_extract_token (buf, s, 0);
1078 remove_from_rx_list (&UnMailLists, buf->data);
1079 remove_from_rx_list (&UnSubscribedLists, buf->data);
1081 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1083 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1086 while (MoreArgs (s));
1091 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1095 mutt_extract_token (buf, s, 0);
1096 remove_from_rx_list (&SubscribedLists, buf->data);
1098 if (str_cmp (buf->data, "*") &&
1099 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1102 while (MoreArgs (s));
1107 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1110 ALIAS *tmp, *last = NULL;
1113 mutt_extract_token (buf, s, 0);
1115 if (str_cmp ("*", buf->data) == 0) {
1116 if (CurrentMenu == MENU_ALIAS) {
1117 for (tmp = Aliases; tmp; tmp = tmp->next)
1119 set_option (OPTFORCEREDRAWINDEX);
1122 mutt_free_alias (&Aliases);
1126 for (tmp = Aliases; tmp; tmp = tmp->next) {
1127 if (str_casecmp (buf->data, tmp->name) == 0) {
1128 if (CurrentMenu == MENU_ALIAS) {
1130 set_option (OPTFORCEREDRAWINDEX);
1135 last->next = tmp->next;
1137 Aliases = tmp->next;
1139 mutt_free_alias (&tmp);
1145 while (MoreArgs (s));
1149 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1152 ALIAS *tmp = Aliases;
1156 if (!MoreArgs (s)) {
1157 strfcpy (err->data, _("alias: no address"), err->dsize);
1161 mutt_extract_token (buf, s, 0);
1163 debug_print (2, ("first token is '%s'.\n", buf->data));
1165 /* check to see if an alias with this name already exists */
1166 for (; tmp; tmp = tmp->next) {
1167 if (!str_casecmp (tmp->name, buf->data))
1173 /* create a new alias */
1174 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1176 tmp->name = str_dup (buf->data);
1177 /* give the main addressbook code a chance */
1178 if (CurrentMenu == MENU_ALIAS)
1179 set_option (OPTMENUCALLER);
1182 /* override the previous value */
1183 rfc822_free_address (&tmp->addr);
1184 if (CurrentMenu == MENU_ALIAS)
1185 set_option (OPTFORCEREDRAWINDEX);
1188 mutt_extract_token (buf, s,
1189 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1190 debug_print (2, ("second token is '%s'.\n", buf->data));
1191 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1196 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1197 snprintf (err->data, err->dsize,
1198 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1202 if (DebugLevel >= 2) {
1205 for (a = tmp->addr; a; a = a->next) {
1207 debug_print (2, ("%s\n", a->mailbox));
1209 debug_print (2, ("group %s\n", a->mailbox));
1217 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1220 LIST *tmp = UserHeader;
1225 mutt_extract_token (buf, s, 0);
1226 if (str_cmp ("*", buf->data) == 0)
1227 mutt_free_list (&UserHeader);
1232 l = str_len (buf->data);
1233 if (buf->data[l - 1] == ':')
1237 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1238 && tmp->data[l] == ':') {
1241 last->next = tmp->next;
1243 UserHeader = tmp->next;
1246 mutt_free_list (&ptr);
1255 while (MoreArgs (s));
1259 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1266 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1267 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1268 strfcpy (err->data, _("invalid header field"), err->dsize);
1271 keylen = p - buf->data + 1;
1274 for (tmp = UserHeader;; tmp = tmp->next) {
1275 /* see if there is already a field by this name */
1276 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1277 /* replace the old value */
1278 mem_free (&tmp->data);
1279 tmp->data = buf->data;
1280 memset (buf, 0, sizeof (BUFFER));
1286 tmp->next = mutt_new_list ();
1290 tmp = mutt_new_list ();
1293 tmp->data = buf->data;
1294 memset (buf, 0, sizeof (BUFFER));
1299 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1300 char* errbuf, size_t errlen) {
1303 if (str_ncmp ("reverse-", s, 8) == 0) {
1305 flags = SORT_REVERSE;
1308 if (str_ncmp ("last-", s, 5) == 0) {
1313 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1315 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1319 *((short*) dst->data) = i | flags;
1323 /* if additional data more == 1, we want to resolve synonyms */
1324 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1325 char buf[LONG_STRING];
1326 struct option_t* ptr = (struct option_t*) p;
1328 if (DTYPE (ptr->type) == DT_SYN) {
1331 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1333 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1335 mutt_option_value (ptr->option, buf, sizeof (buf));
1336 if (str_len (ptr->init) == 0 && buf && *buf)
1337 ptr->init = str_dup (buf);
1340 static struct option_t* add_option (const char* name, const char* init,
1341 short type, short dup) {
1342 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1344 debug_print (1, ("adding $%s\n", name));
1346 option->option = str_dup (name);
1347 option->type = type;
1349 option->init = dup ? str_dup (init) : (char*) init;
1353 /* creates new option_t* of type DT_USER for $user_ var */
1354 static struct option_t* add_user_option (const char* name) {
1355 return (add_option (name, NULL, DT_USER, 1));
1358 /* free()'s option_t* */
1359 static void del_option (void* p) {
1360 struct option_t* ptr = (struct option_t*) p;
1361 char* s = (char*) ptr->data;
1362 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1363 mem_free (&ptr->option);
1365 mem_free (&ptr->init);
1369 /* if additional data more == 1, we want to resolve synonyms */
1370 static void mutt_restore_default (const char* name, void* p,
1371 unsigned long more) {
1372 char errbuf[STRING];
1373 struct option_t* ptr = (struct option_t*) p;
1375 if (DTYPE (ptr->type) == DT_SYN) {
1378 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1382 if (FuncTable[DTYPE (ptr->type)].opt_from_string &&
1383 FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1384 sizeof (errbuf)) < 0) {
1386 fprintf (stderr, _("Invalid default setting found. Please report this "
1387 "error:\n\"%s\"\n"), errbuf);
1391 if (ptr->flags & R_INDEX)
1392 set_option (OPTFORCEREDRAWINDEX);
1393 if (ptr->flags & R_PAGER)
1394 set_option (OPTFORCEREDRAWPAGER);
1395 if (ptr->flags & R_RESORT_SUB)
1396 set_option (OPTSORTSUBTHREADS);
1397 if (ptr->flags & R_RESORT)
1398 set_option (OPTNEEDRESORT);
1399 if (ptr->flags & R_RESORT_INIT)
1400 set_option (OPTRESORTINIT);
1401 if (ptr->flags & R_TREE)
1402 set_option (OPTREDRAWTREE);
1405 /* check whether value for $dsn_return would be valid */
1406 static int check_dsn_return (const char* option, unsigned long p,
1407 char* errbuf, size_t errlen) {
1408 char* val = (char*) p;
1409 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1410 str_ncmp (val, "full", 4) != 0) {
1412 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1418 /* check whether value for $dsn_notify would be valid */
1419 static int check_dsn_notify (const char* option, unsigned long p,
1420 char* errbuf, size_t errlen) {
1421 list2_t* list = NULL;
1423 char* val = (char*) p;
1427 list = list_from_str (val, ",");
1428 if (list_empty (list))
1431 for (i = 0; i < list->length; i++)
1432 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1433 str_ncmp (list->data[i], "failure", 7) != 0 &&
1434 str_ncmp (list->data[i], "delay", 5) != 0 &&
1435 str_ncmp (list->data[i], "success", 7) != 0) {
1437 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1438 (char*) list->data[i], "dsn_notify");
1442 list_del (&list, (list_del_t*) _mem_free);
1446 static int check_num (const char* option, unsigned long p,
1447 char* errbuf, size_t errlen) {
1450 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1457 static int check_debug (const char* option, unsigned long p,
1458 char* errbuf, size_t errlen) {
1459 if ((int) p <= DEBUG_MAX_LEVEL &&
1460 (int) p >= DEBUG_MIN_LEVEL)
1464 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1469 static int check_history (const char* option, unsigned long p,
1470 char* errbuf, size_t errlen) {
1471 if (!check_num ("history", p, errbuf, errlen))
1473 mutt_init_history ();
1477 static int check_special (const char* name, unsigned long val,
1478 char* errbuf, size_t errlen) {
1481 for (i = 0; SpecialVars[i].name; i++) {
1482 if (str_cmp (SpecialVars[i].name, name) == 0) {
1483 return (SpecialVars[i].check (SpecialVars[i].name,
1484 val, errbuf, errlen));
1490 static const struct mapping_t* get_sortmap (struct option_t* option) {
1491 const struct mapping_t* map = NULL;
1493 switch (option->type & DT_SUBTYPE_MASK) {
1495 map = SortAliasMethods;
1497 case DT_SORT_BROWSER:
1498 map = SortBrowserMethods;
1501 if ((WithCrypto & APPLICATION_PGP))
1502 map = SortKeyMethods;
1505 map = SortAuxMethods;
1514 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1517 int query, unset, inv, reset, r = 0;
1518 struct option_t* option = NULL;
1520 while (MoreArgs (s)) {
1521 /* reset state variables */
1523 unset = data & M_SET_UNSET;
1524 inv = data & M_SET_INV;
1525 reset = data & M_SET_RESET;
1527 if (*s->dptr == '?') {
1531 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1535 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1539 else if (*s->dptr == '&') {
1544 /* get the variable name */
1545 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1547 /* resolve synonyms */
1548 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1549 DTYPE (option->type == DT_SYN)) {
1550 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1551 syn_add (newopt, option);
1555 /* see if we need to add $user_ var */
1556 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1557 /* there's no option named like this yet so only add one
1558 * if the action isn't any of: reset, unset, query */
1559 if (!(reset || unset || query || *s->dptr != '=')) {
1560 debug_print (1, ("adding user option '%s'\n", tmp->data));
1561 option = add_user_option (tmp->data);
1562 hash_insert (ConfigOptions, option->option, option, 0);
1566 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1567 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1573 if (query || unset || inv) {
1574 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1578 if (s && *s->dptr == '=') {
1579 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1583 if (!str_cmp ("all", tmp->data)) {
1584 hash_map (ConfigOptions, mutt_restore_default, 1);
1587 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1588 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1592 mutt_restore_default (NULL, option, 1);
1594 else if (DTYPE (option->type) == DT_BOOL) {
1595 /* XXX this currently ignores the function table
1596 * as we don't get invert and stuff into it */
1597 if (s && *s->dptr == '=') {
1598 if (unset || inv || query) {
1599 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1604 mutt_extract_token (tmp, s, 0);
1605 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1607 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1610 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1616 bool_to_string (err->data, err->dsize, option);
1621 unset_option (option->data);
1623 toggle_option (option->data);
1625 set_option (option->data);
1627 else if (DTYPE (option->type) == DT_STR ||
1628 DTYPE (option->type) == DT_PATH ||
1629 DTYPE (option->type) == DT_ADDR ||
1630 DTYPE (option->type) == DT_MAGIC ||
1631 DTYPE (option->type) == DT_NUM ||
1632 DTYPE (option->type) == DT_SORT ||
1633 DTYPE (option->type) == DT_RX ||
1634 DTYPE (option->type) == DT_USER ||
1635 DTYPE (option->type) == DT_SYS) {
1637 /* XXX maybe we need to get unset into handlers? */
1638 if (DTYPE (option->type) == DT_STR ||
1639 DTYPE (option->type) == DT_PATH ||
1640 DTYPE (option->type) == DT_ADDR ||
1641 DTYPE (option->type) == DT_USER ||
1642 DTYPE (option->type) == DT_SYS) {
1644 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1645 snprintf (err->data, err->dsize, _("$%s is read-only"),
1649 } else if (DTYPE (option->type) == DT_ADDR)
1650 rfc822_free_address ((ADDRESS **) option->data);
1651 else if (DTYPE (option->type) == DT_USER)
1652 /* to unset $user_ means remove */
1653 hash_delete (ConfigOptions, option->option,
1654 option, del_option);
1656 mem_free ((void *) option->data);
1661 if (query || *s->dptr != '=') {
1662 FuncTable[DTYPE (option->type)].opt_to_string
1663 (err->data, err->dsize, option);
1667 /* the $muttng_ variables are read-only */
1668 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1669 snprintf (err->data, err->dsize, _("$%s is read-only"),
1675 mutt_extract_token (tmp, s, 0);
1676 if (!FuncTable[DTYPE (option->type)].opt_from_string
1677 (option, tmp->data, err->data, err->dsize))
1681 else if (DTYPE (option->type) == DT_QUAD) {
1684 quad_to_string (err->data, err->dsize, option);
1688 if (*s->dptr == '=') {
1690 mutt_extract_token (tmp, s, 0);
1691 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1692 set_quadoption (option->data, M_YES);
1693 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1694 set_quadoption (option->data, M_NO);
1695 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1696 set_quadoption (option->data, M_ASKYES);
1697 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1698 set_quadoption (option->data, M_ASKNO);
1700 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1701 tmp->data, option->option);
1708 toggle_quadoption (option->data);
1710 set_quadoption (option->data, M_NO);
1712 set_quadoption (option->data, M_YES);
1716 snprintf (err->data, err->dsize, _("%s: unknown type"),
1722 if (option->flags & R_INDEX)
1723 set_option (OPTFORCEREDRAWINDEX);
1724 if (option->flags & R_PAGER)
1725 set_option (OPTFORCEREDRAWPAGER);
1726 if (option->flags & R_RESORT_SUB)
1727 set_option (OPTSORTSUBTHREADS);
1728 if (option->flags & R_RESORT)
1729 set_option (OPTNEEDRESORT);
1730 if (option->flags & R_RESORT_INIT)
1731 set_option (OPTRESORTINIT);
1732 if (option->flags & R_TREE)
1733 set_option (OPTREDRAWTREE);
1740 /* reads the specified initialization file. returns -1 if errors were found
1741 so that we can pause to let the user know... */
1742 static int source_rc (const char *rcfile, BUFFER * err)
1745 int line = 0, rc = 0, conv = 0;
1747 char *linebuf = NULL;
1748 char *currentline = NULL;
1752 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1754 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1755 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1759 memset (&token, 0, sizeof (token));
1760 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1761 conv = ConfigCharset && (*ConfigCharset) && Charset;
1763 currentline = str_dup (linebuf);
1766 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1769 currentline = linebuf;
1774 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1775 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1776 if (--rc < -MAXERRS) {
1778 mem_free (¤tline);
1787 mem_free (¤tline);
1789 mem_free (&token.data);
1790 mem_free (&linebuf);
1793 mutt_wait_filter (pid);
1795 /* the muttrc source keyword */
1796 snprintf (err->data, err->dsize,
1797 rc >= -MAXERRS ? _("source: errors in %s")
1798 : _("source: reading aborted due too many errors in %s"),
1807 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1810 char path[_POSIX_PATH_MAX];
1814 if (mutt_extract_token (tmp, s, 0) != 0) {
1815 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1819 strfcpy (path, tmp->data, sizeof (path));
1820 mutt_expand_path (path, sizeof (path));
1822 rc += source_rc (path, err);
1824 while (MoreArgs (s));
1826 return ((rc < 0) ? -1 : 0);
1829 /* line command to execute
1831 token scratch buffer to be used by parser. caller should free
1832 token->data when finished. the reason for this variable is
1833 to avoid having to allocate and deallocate a lot of memory
1834 if we are parsing many lines. the caller can pass in the
1835 memory to use, which avoids having to create new space for
1836 every call to this function.
1838 err where to write error messages */
1839 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1844 memset (&expn, 0, sizeof (expn));
1845 expn.data = expn.dptr = line;
1846 expn.dsize = str_len (line);
1850 debug_print (1, ("expand '%s'\n", line));
1853 while (*expn.dptr) {
1854 if (*expn.dptr == '#')
1855 break; /* rest of line is a comment */
1856 if (*expn.dptr == ';') {
1860 mutt_extract_token (token, &expn, 0);
1861 for (i = 0; Commands[i].name; i++) {
1862 if (!str_cmp (token->data, Commands[i].name)) {
1863 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1868 if (!Commands[i].name) {
1869 snprintf (err->data, err->dsize, _("%s: unknown command"),
1870 NONULL (token->data));
1877 mem_free (&expn.data);
1882 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1883 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1884 /* initial string that starts completion. No telling how much crap
1885 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1886 char User_typed[LONG_STRING] = { 0 };
1888 int Num_matched = 0; /* Number of matches for completion */
1889 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1890 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1892 /* helper function for completion. Changes the dest buffer if
1893 necessary/possible to aid completion.
1894 dest == completion result gets here.
1895 src == candidate for completion.
1896 try == user entered data for completion.
1897 len == length of dest buffer.
1899 static void candidate (char *dest, char *try, char *src, int len)
1903 if (strstr (src, try) == src) {
1904 Matches[Num_matched++] = src;
1906 strfcpy (dest, src, len);
1908 for (l = 0; src[l] && src[l] == dest[l]; l++);
1914 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1918 int spaces; /* keep track of the number of leading spaces on the line */
1921 spaces = buffer - pt;
1923 pt = buffer + pos - spaces;
1924 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1927 if (pt == buffer) { /* complete cmd */
1928 /* first TAB. Collect all the matches */
1931 strfcpy (User_typed, pt, sizeof (User_typed));
1932 memset (Matches, 0, sizeof (Matches));
1933 memset (Completed, 0, sizeof (Completed));
1934 for (num = 0; Commands[num].name; num++)
1935 candidate (Completed, User_typed, Commands[num].name,
1936 sizeof (Completed));
1937 Matches[Num_matched++] = User_typed;
1939 /* All matches are stored. Longest non-ambiguous string is ""
1940 * i.e. dont change 'buffer'. Fake successful return this time */
1941 if (User_typed[0] == 0)
1945 if (Completed[0] == 0 && User_typed[0])
1948 /* Num_matched will _always_ be atleast 1 since the initial
1949 * user-typed string is always stored */
1950 if (numtabs == 1 && Num_matched == 2)
1951 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1952 else if (numtabs > 1 && Num_matched > 2)
1953 /* cycle thru all the matches */
1954 snprintf (Completed, sizeof (Completed), "%s",
1955 Matches[(numtabs - 2) % Num_matched]);
1957 /* return the completed command */
1958 strncpy (buffer, Completed, len - spaces);
1960 else if (!str_ncmp (buffer, "set", 3)
1961 || !str_ncmp (buffer, "unset", 5)
1962 || !str_ncmp (buffer, "reset", 5)
1963 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1964 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1967 /* loop through all the possible prefixes (no, inv, ...) */
1968 if (!str_ncmp (buffer, "set", 3)) {
1969 for (num = 0; prefixes[num]; num++) {
1970 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1971 pt += str_len (prefixes[num]);
1977 /* first TAB. Collect all the matches */
1980 strfcpy (User_typed, pt, sizeof (User_typed));
1981 memset (Matches, 0, sizeof (Matches));
1982 memset (Completed, 0, sizeof (Completed));
1983 for (num = 0; MuttVars[num].option; num++)
1984 candidate (Completed, User_typed, MuttVars[num].option,
1985 sizeof (Completed));
1986 Matches[Num_matched++] = User_typed;
1988 /* All matches are stored. Longest non-ambiguous string is ""
1989 * i.e. dont change 'buffer'. Fake successful return this time */
1990 if (User_typed[0] == 0)
1994 if (Completed[0] == 0 && User_typed[0])
1997 /* Num_matched will _always_ be atleast 1 since the initial
1998 * user-typed string is always stored */
1999 if (numtabs == 1 && Num_matched == 2)
2000 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2001 else if (numtabs > 1 && Num_matched > 2)
2002 /* cycle thru all the matches */
2003 snprintf (Completed, sizeof (Completed), "%s",
2004 Matches[(numtabs - 2) % Num_matched]);
2006 strncpy (pt, Completed, buffer + len - pt - spaces);
2008 else if (!str_ncmp (buffer, "exec", 4)) {
2009 struct binding_t *menu = km_get_table (CurrentMenu);
2011 if (!menu && CurrentMenu != MENU_PAGER)
2015 /* first TAB. Collect all the matches */
2018 strfcpy (User_typed, pt, sizeof (User_typed));
2019 memset (Matches, 0, sizeof (Matches));
2020 memset (Completed, 0, sizeof (Completed));
2021 for (num = 0; menu[num].name; num++)
2022 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2023 /* try the generic menu */
2024 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2026 for (num = 0; menu[num].name; num++)
2027 candidate (Completed, User_typed, menu[num].name,
2028 sizeof (Completed));
2030 Matches[Num_matched++] = User_typed;
2032 /* All matches are stored. Longest non-ambiguous string is ""
2033 * i.e. dont change 'buffer'. Fake successful return this time */
2034 if (User_typed[0] == 0)
2038 if (Completed[0] == 0 && User_typed[0])
2041 /* Num_matched will _always_ be atleast 1 since the initial
2042 * user-typed string is always stored */
2043 if (numtabs == 1 && Num_matched == 2)
2044 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2045 else if (numtabs > 1 && Num_matched > 2)
2046 /* cycle thru all the matches */
2047 snprintf (Completed, sizeof (Completed), "%s",
2048 Matches[(numtabs - 2) % Num_matched]);
2050 strncpy (pt, Completed, buffer + len - pt - spaces);
2058 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2060 char var[STRING], *pt = buffer;
2062 struct option_t* option = NULL;
2068 spaces = buffer - pt;
2070 pt = buffer + pos - spaces;
2071 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2073 pt++; /* move past the space */
2074 if (*pt == '=') /* abort if no var before the '=' */
2077 if (str_ncmp (buffer, "set", 3) == 0) {
2078 strfcpy (var, pt, sizeof (var));
2079 /* ignore the trailing '=' when comparing */
2080 var[str_len (var) - 1] = 0;
2081 if (!(option = hash_find (ConfigOptions, var)))
2082 return 0; /* no such variable. */
2084 char tmp[LONG_STRING], tmp2[LONG_STRING];
2086 size_t dlen = buffer + len - pt - spaces;
2087 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2091 if ((DTYPE (option->type) == DT_STR) ||
2092 (DTYPE (option->type) == DT_PATH) ||
2093 (DTYPE (option->type) == DT_RX)) {
2094 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2095 if (DTYPE (option->type) == DT_PATH)
2096 mutt_pretty_mailbox (tmp);
2098 else if (DTYPE (option->type) == DT_ADDR) {
2099 rfc822_write_address (tmp, sizeof (tmp),
2100 *((ADDRESS **) option->data), 0);
2102 else if (DTYPE (option->type) == DT_QUAD)
2103 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2104 else if (DTYPE (option->type) == DT_NUM)
2105 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2106 else if (DTYPE (option->type) == DT_SORT) {
2107 const struct mapping_t *map;
2110 switch (option->type & DT_SUBTYPE_MASK) {
2112 map = SortAliasMethods;
2114 case DT_SORT_BROWSER:
2115 map = SortBrowserMethods;
2118 if ((WithCrypto & APPLICATION_PGP))
2119 map = SortKeyMethods;
2128 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2130 snprintf (tmp, sizeof (tmp), "%s%s%s",
2131 (*((short *) option->data) & SORT_REVERSE) ?
2133 (*((short *) option->data) & SORT_LAST) ? "last-" :
2136 else if (DTYPE (option->type) == DT_MAGIC) {
2138 switch (DefaultMagic) {
2154 strfcpy (tmp, p, sizeof (tmp));
2156 else if (DTYPE (option->type) == DT_BOOL)
2157 strfcpy (tmp, option (option->data) ? "yes" : "no",
2162 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2163 if (*s == '\\' || *s == '"')
2169 strfcpy (tmp, pt, sizeof (tmp));
2170 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2178 /* Implement the -Q command line flag */
2179 int mutt_query_variables (LIST * queries)
2183 char errbuff[STRING];
2184 char command[STRING];
2188 memset (&err, 0, sizeof (err));
2189 memset (&token, 0, sizeof (token));
2192 err.dsize = sizeof (errbuff);
2194 for (p = queries; p; p = p->next) {
2195 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2196 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2197 fprintf (stderr, "%s\n", err.data);
2198 mem_free (&token.data);
2201 printf ("%s\n", err.data);
2204 mem_free (&token.data);
2208 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2212 for (i = 0; map[i].name; i++)
2213 if (map[i].value == val)
2214 return (map[i].name);
2218 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2222 for (i = 0; map[i].name; i++)
2223 if (ascii_strcasecmp (map[i].name, name) == 0)
2224 return (map[i].value);
2228 static int mutt_execute_commands (LIST * p)
2231 char errstr[SHORT_STRING];
2233 memset (&err, 0, sizeof (err));
2235 err.dsize = sizeof (errstr);
2236 memset (&token, 0, sizeof (token));
2237 for (; p; p = p->next) {
2238 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2239 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2240 mem_free (&token.data);
2244 mem_free (&token.data);
2248 void mutt_init (int skip_sys_rc, LIST * commands)
2251 struct utsname utsname;
2252 char *p, buffer[STRING], error[STRING];
2253 int i, default_rc = 0, need_pause = 0;
2256 memset (&err, 0, sizeof (err));
2258 err.dsize = sizeof (error);
2260 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2261 * to have some room for $user_ vars */
2262 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2263 for (i = 0; MuttVars[i].option; i++) {
2264 if (DTYPE (MuttVars[i].type) != DT_SYS)
2265 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2267 hash_insert (ConfigOptions, MuttVars[i].option,
2268 add_option (MuttVars[i].option, MuttVars[i].init,
2273 * XXX - use something even more difficult to predict?
2275 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2276 "\033]9;%ld\a", (long) time (NULL));
2278 /* on one of the systems I use, getcwd() does not return the same prefix
2279 as is listed in the passwd file */
2280 if ((p = getenv ("HOME")))
2281 Homedir = str_dup (p);
2283 /* Get some information about the user */
2284 if ((pw = getpwuid (getuid ()))) {
2287 Username = str_dup (pw->pw_name);
2289 Homedir = str_dup (pw->pw_dir);
2291 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2292 Shell = str_dup (pw->pw_shell);
2298 fputs (_("unable to determine home directory"), stderr);
2301 if ((p = getenv ("USER")))
2302 Username = str_dup (p);
2305 fputs (_("unable to determine username"), stderr);
2308 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2311 debug_start(Homedir);
2313 /* And about the host... */
2315 /* some systems report the FQDN instead of just the hostname */
2316 if ((p = strchr (utsname.nodename, '.'))) {
2317 Hostname = str_substrdup (utsname.nodename, p);
2319 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2322 Hostname = str_dup (utsname.nodename);
2325 #define DOMAIN buffer
2326 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2327 Fqdn = str_dup ("@");
2330 if (*DOMAIN != '@') {
2331 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2332 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2335 Fqdn = str_dup (NONULL (Hostname));
2342 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2344 fgets (buffer, sizeof (buffer), f);
2345 p = (char*) &buffer;
2348 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2352 NewsServer = str_dup (p);
2356 if ((p = getenv ("NNTPSERVER")))
2357 NewsServer = str_dup (p);
2360 if ((p = getenv ("MAIL")))
2361 Spoolfile = str_dup (p);
2362 else if ((p = getenv ("MAILDIR")))
2363 Spoolfile = str_dup (p);
2366 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2368 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2370 Spoolfile = str_dup (buffer);
2373 if ((p = getenv ("MAILCAPS")))
2374 MailcapPath = str_dup (p);
2376 /* Default search path from RFC1524 */
2378 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2379 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2382 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2384 p = getenv ("VISUAL");
2386 p = getenv ("EDITOR");
2390 Editor = str_dup (p);
2391 Visual = str_dup (p);
2393 if ((p = getenv ("REPLYTO")) != NULL) {
2396 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2398 memset (&buf, 0, sizeof (buf));
2399 buf.data = buf.dptr = buffer;
2400 buf.dsize = str_len (buffer);
2402 memset (&token, 0, sizeof (token));
2403 parse_my_hdr (&token, &buf, 0, &err);
2404 mem_free (&token.data);
2407 if ((p = getenv ("EMAIL")) != NULL)
2408 From = rfc822_parse_adrlist (NULL, p);
2410 mutt_set_langinfo_charset ();
2411 mutt_set_charset (Charset);
2414 /* Set standard defaults */
2415 hash_map (ConfigOptions, mutt_set_default, 0);
2416 hash_map (ConfigOptions, mutt_restore_default, 0);
2418 CurrentMenu = MENU_MAIN;
2421 #ifndef LOCALES_HACK
2422 /* Do we have a locale definition? */
2423 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2424 ((p = getenv ("LANG")) != NULL && p[0]) ||
2425 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2426 set_option (OPTLOCALES);
2430 /* Unset suspend by default if we're the session leader */
2431 if (getsid (0) == getpid ())
2432 unset_option (OPTSUSPEND);
2435 mutt_init_history ();
2444 * When changing the code which looks for a configuration file,
2445 * please also change the corresponding code in muttbug.sh.in.
2455 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2457 if (access (buffer, F_OK) == -1)
2459 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2460 if (access (buffer, F_OK) == -1)
2462 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2463 NONULL (Homedir), MUTT_VERSION);
2464 if (access (buffer, F_OK) == -1)
2466 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2470 Muttrc = str_dup (buffer);
2473 strfcpy (buffer, Muttrc, sizeof (buffer));
2475 mutt_expand_path (buffer, sizeof (buffer));
2476 Muttrc = str_dup (buffer);
2478 mem_free (&AliasFile);
2479 AliasFile = str_dup (NONULL (Muttrc));
2481 /* Process the global rc file if it exists and the user hasn't explicity
2482 requested not to via "-n". */
2484 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2486 if (access (buffer, F_OK) == -1)
2487 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2488 if (access (buffer, F_OK) == -1)
2489 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2491 if (access (buffer, F_OK) == -1)
2492 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2493 if (access (buffer, F_OK) != -1) {
2494 if (source_rc (buffer, &err) != 0) {
2495 fputs (err.data, stderr);
2496 fputc ('\n', stderr);
2502 /* Read the user's initialization file. */
2503 if (access (Muttrc, F_OK) != -1) {
2504 if (!option (OPTNOCURSES))
2506 if (source_rc (Muttrc, &err) != 0) {
2507 fputs (err.data, stderr);
2508 fputc ('\n', stderr);
2512 else if (!default_rc) {
2513 /* file specified by -F does not exist */
2514 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2515 mutt_endwin (buffer);
2519 if (mutt_execute_commands (commands) != 0)
2522 /* warn about synonym variables */
2523 if (!list_empty(Synonyms)) {
2525 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2526 for (i = 0; i < Synonyms->length; i++) {
2527 struct option_t* newopt = NULL, *oldopt = NULL;
2528 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2529 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2530 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2531 oldopt ? NONULL (oldopt->option) : "",
2532 newopt ? NONULL (newopt->option) : "",
2533 NONULL(((syn_t*) Synonyms->data[i])->f),
2534 ((syn_t*) Synonyms->data[i])->l);
2536 fprintf (stderr, _("Warning: synonym variables are scheduled"
2537 " for removal.\n"));
2538 list_del (&Synonyms, syn_del);
2542 if (need_pause && !option (OPTNOCURSES)) {
2543 if (mutt_any_key_to_continue (NULL) == -1)
2548 set_option (OPTWEED); /* turn weeding on by default */
2552 int mutt_get_hook_type (const char *name)
2554 struct command_t *c;
2556 for (c = Commands; c->name; c++)
2557 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2562 /* compare two option_t*'s for sorting -t/-T output */
2563 static int opt_cmp (const void* a, const void* b) {
2564 return (str_cmp ((*(struct option_t**) a)->option,
2565 (*(struct option_t**) b)->option));
2568 /* callback for hash_map() to put all non-synonym vars into list */
2569 static void opt_sel_full (const char* key, void* data,
2570 unsigned long more) {
2571 list2_t** l = (list2_t**) more;
2572 struct option_t* option = (struct option_t*) data;
2574 if (DTYPE (option->type) == DT_SYN)
2576 list_push_back (l, option);
2579 /* callback for hash_map() to put all changed non-synonym vars into list */
2580 static void opt_sel_diff (const char* key, void* data,
2581 unsigned long more) {
2582 list2_t** l = (list2_t**) more;
2583 struct option_t* option = (struct option_t*) data;
2584 char buf[LONG_STRING];
2586 if (DTYPE (option->type) == DT_SYN)
2589 mutt_option_value (option->option, buf, sizeof (buf));
2590 if (str_cmp (buf, option->init) != 0)
2591 list_push_back (l, option);
2594 /* dump out the value of all the variables we have */
2595 int mutt_dump_variables (int full) {
2597 char outbuf[STRING];
2598 list2_t* tmp = NULL;
2599 struct option_t* option = NULL;
2601 /* get all non-synonyms into list... */
2602 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2603 (unsigned long) &tmp);
2605 if (!list_empty(tmp)) {
2606 /* ...and dump list sorted */
2607 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2608 for (i = 0; i < tmp->length; i++) {
2609 option = (struct option_t*) tmp->data[i];
2610 FuncTable[DTYPE (option->type)].opt_to_string
2611 (outbuf, sizeof (outbuf), option);
2612 printf ("%s\n", outbuf);
2615 list_del (&tmp, NULL);