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);
134 /* protos for config type handles: convert to value from string */
135 static int bool_from_string (struct option_t* dst, const char* val,
136 char* errbuf, size_t errlen);
137 static int num_from_string (struct option_t* dst, const char* val,
138 char* errbuf, size_t errlen);
139 static int str_from_string (struct option_t* dst, const char* val,
140 char* errbuf, size_t errlen);
141 static int path_from_string (struct option_t* dst, const char* val,
142 char* errbuf, size_t errlen);
143 static int quad_from_string (struct option_t* dst, const char* val,
144 char* errbuf, size_t errlen);
145 static int sort_from_string (struct option_t* dst, const char* val,
146 char* errbuf, size_t errlen);
147 static int rx_from_string (struct option_t* dst, const char* val,
148 char* errbuf, size_t errlen);
149 static int magic_from_string (struct option_t* dst, const char* val,
150 char* errbuf, size_t errlen);
151 static int addr_from_string (struct option_t* dst, const char* val,
152 char* errbuf, size_t errlen);
153 static int user_from_string (struct option_t* dst, const char* val,
154 char* errbuf, size_t errlen);
158 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
159 int (*opt_from_string) (struct option_t* dst, const char* val,
160 char* errbuf, size_t errlen);
162 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
163 { DT_BOOL, bool_to_string, bool_from_string },
164 { DT_NUM, num_to_string, num_from_string },
165 { DT_STR, str_to_string, str_from_string },
166 { DT_PATH, str_to_string, path_from_string },
167 { DT_QUAD, quad_to_string, quad_from_string },
168 { DT_SORT, sort_to_string, sort_from_string },
169 { DT_RX, rx_to_string, rx_from_string },
170 { DT_MAGIC, magic_to_string, magic_from_string },
171 /* synonyms should be resolved already so we don't need this
172 * but must define it as DT_ is used for indexing */
173 { DT_SYN, NULL, NULL },
174 { DT_ADDR, addr_to_string, addr_from_string },
175 { DT_USER, user_to_string, user_from_string },
178 static void bool_to_string (char* dst, size_t dstlen,
179 struct option_t* option) {
180 snprintf (dst, dstlen, "%s=%s", option->option,
181 option (option->data) ? "yes" : "no");
184 static int bool_from_string (struct option_t* dst, const char* val,
185 char* errbuf, size_t errlen) {
190 if (ascii_strncasecmp (val, "yes", 3) == 0)
192 else if (ascii_strncasecmp (val, "no", 2) == 0)
198 set_option (dst->data);
200 unset_option (dst->data);
204 static void num_to_string (char* dst, size_t dstlen,
205 struct option_t* option) {
207 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
209 snprintf (dst, dstlen, fmt, option->option,
210 *((short*) option->data));
213 static int num_from_string (struct option_t* dst, const char* val,
214 char* errbuf, size_t errlen) {
215 int num = 0, old = 0;
221 num = strtol (val, &t, 0);
223 if (!*val || *t || (short) num != num) {
225 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
231 /* just temporarily accept new val so that check_special for
232 * $history already has it when doing history's init() */
233 old = *((short*) dst->data);
234 *((short*) dst->data) = (short) num;
236 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
237 *((short*) dst->data) = old;
244 static void str_to_string (char* dst, size_t dstlen,
245 struct option_t* option) {
246 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
247 NONULL (*((char**) option->data)));
250 static void user_to_string (char* dst, size_t dstlen,
251 struct option_t* option) {
252 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
253 NONULL (((char*) option->data)));
256 static int path_from_string (struct option_t* dst, const char* val,
257 char* errbuf, size_t errlen) {
258 char path[_POSIX_PATH_MAX];
264 mem_free ((char**) dst->data);
269 strfcpy (path, val, sizeof (path));
270 mutt_expand_path (path, sizeof (path));
271 str_replace ((char **) dst->data, path);
275 static int str_from_string (struct option_t* dst, const char* val,
276 char* errbuf, size_t errlen) {
280 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
283 str_replace ((char**) dst->data, val);
287 static int user_from_string (struct option_t* dst, const char* val,
288 char* errbuf, size_t errlen) {
289 /* if dst == NULL, we may get here in case the user did unset it,
290 * see parse_set() where item is free()'d before coming here; so
291 * just silently ignore it */
294 if (str_len ((char*) dst->data) == 0)
295 dst->data = (unsigned long) str_dup (val);
297 char* s = (char*) dst->data;
298 str_replace (&s, val);
300 if (str_len (dst->init) == 0)
301 dst->init = str_dup ((char*) dst->data);
305 static void quad_to_string (char* dst, size_t dstlen,
306 struct option_t* option) {
307 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
308 snprintf (dst, dstlen, "%s=%s", option->option,
309 vals[quadoption (option->data)]);
312 static int quad_from_string (struct option_t* dst, const char* val,
313 char* errbuf, size_t errlen) {
318 if (ascii_strncasecmp (val, "yes", 3) == 0)
320 else if (ascii_strncasecmp (val, "no", 2) == 0)
322 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
324 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
330 set_quadoption (dst->data, flag);
334 static void sort_to_string (char* dst, size_t dstlen,
335 struct option_t* option) {
336 const struct mapping_t *map = get_sortmap (option);
340 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
344 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
347 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
348 (*((short *) option->data) & SORT_REVERSE) ?
350 (*((short *) option->data) & SORT_LAST) ? "last-" :
354 static int sort_from_string (struct option_t* dst, const char* val,
355 char* errbuf, size_t errlen) {
356 const struct mapping_t *map = NULL;
357 if (!(map = get_sortmap (dst))) {
359 snprintf (errbuf, errlen, _("%s: Unknown type."),
363 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
368 static void rx_to_string (char* dst, size_t dstlen,
369 struct option_t* option) {
370 rx_t* p = (rx_t*) option->data;
371 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
372 NONULL (p->pattern));
375 static int rx_from_string (struct option_t* dst, const char* val,
376 char* errbuf, size_t errlen) {
379 int flags = 0, e = 0, not = 0;
385 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
387 snprintf (errbuf, errlen,
388 "Operation not permitted when in attach-message mode.");
392 if (!((rx_t*) dst->data))
393 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
395 p = (rx_t*) dst->data;
397 /* something to do? */
398 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
401 if (str_cmp (dst->option, "mask") != 0)
402 flags |= mutt_which_case (val);
405 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
410 rx = mem_malloc (sizeof (regex_t));
412 if ((e = REGCOMP (rx, s, flags)) != 0) {
413 regerror (e, rx, errbuf, errlen);
423 str_replace (&p->pattern, val);
427 if (str_cmp (dst->option, "reply_regexp") == 0)
428 mutt_adjust_all_subjects ();
433 static void magic_to_string (char* dst, size_t dstlen,
434 struct option_t* option) {
435 const char* s = NULL;
436 switch (option->data) {
437 case M_MBOX: s = "mbox"; break;
438 case M_MMDF: s = "MMDF"; break;
439 case M_MH: s = "MH"; break;
440 case M_MAILDIR: s = "Maildir"; break;
441 default: s = "unknown"; break;
443 snprintf (dst, dstlen, "%s=%s", option->option, s);
446 static int magic_from_string (struct option_t* dst, const char* val,
447 char* errbuf, size_t errlen) {
450 if (!dst || !val || !*val)
452 if (ascii_strncasecmp (val, "mbox", 4) == 0)
454 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
456 else if (ascii_strncasecmp (val, "mh", 2) == 0)
458 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
464 *((short*) dst->data) = flag;
469 static void addr_to_string (char* dst, size_t dstlen,
470 struct option_t* option) {
473 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
474 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
477 static int addr_from_string (struct option_t* dst, const char* val,
478 char* errbuf, size_t errlen) {
479 if (!dst || !val || !*val)
481 rfc822_free_address ((ADDRESS**) dst->data);
482 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
486 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
487 struct option_t* option = NULL;
488 char* tmp = NULL, *t = NULL;
491 if (!(option = hash_find (ConfigOptions, val))) {
492 debug_print (1, ("var '%s' not found\n", val));
496 tmp = mem_malloc (dstlen+1);
497 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
499 /* as we get things of type $var=value and don't want to bloat the
500 * above "just" for expansion, we do the stripping here */
501 debug_print (1, ("orig == '%s'\n", tmp));
502 t = strchr (tmp, '=');
506 if (t[l-1] == '"' && *t == '"') {
511 memcpy (dst, t, l+1);
513 debug_print (1, ("stripped == '%s'\n", dst));
518 /* for synonym warning reports: adds synonym to end of list */
519 static void syn_add (struct option_t* n, struct option_t* o) {
520 syn_t* tmp = mem_malloc (sizeof (syn_t));
521 tmp->f = str_dup (CurRCFile);
525 list_push_back (&Synonyms, tmp);
528 /* for synonym warning reports: free single item (for list_del()) */
529 static void syn_del (void** p) {
530 mem_free(&(*(syn_t**) p)->f);
534 void toggle_quadoption (int opt)
537 int b = (opt % 4) * 2;
539 QuadOptions[n] ^= (1 << b);
542 void set_quadoption (int opt, int flag)
545 int b = (opt % 4) * 2;
547 QuadOptions[n] &= ~(0x3 << b);
548 QuadOptions[n] |= (flag & 0x3) << b;
551 int quadoption (int opt)
554 int b = (opt % 4) * 2;
556 return (QuadOptions[n] >> b) & 0x3;
559 int query_quadoption (int opt, const char *prompt)
561 int v = quadoption (opt);
569 v = mutt_yesorno (prompt, (v == M_ASKYES));
570 CLEARLINE (LINES - 1);
577 static void add_to_list (LIST ** list, const char *str)
579 LIST *t, *last = NULL;
581 /* don't add a NULL or empty string to the list */
582 if (!str || *str == '\0')
585 /* check to make sure the item is not already on this list */
586 for (last = *list; last; last = last->next) {
587 if (ascii_strcasecmp (str, last->data) == 0) {
588 /* already on the list, so just ignore it */
596 if (!*list || last) {
597 t = (LIST *) mem_calloc (1, sizeof (LIST));
598 t->data = str_dup (str);
608 static int add_to_rx_list (list2_t** list, const char *s, int flags,
617 if (!(rx = rx_compile (s, flags))) {
618 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
622 i = rx_lookup ((*list), rx->pattern);
626 list_push_back (list, rx);
630 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
631 const char *templ, BUFFER * err)
633 SPAM_LIST *t = NULL, *last = NULL;
638 if (!pat || !*pat || !templ)
641 if (!(rx = rx_compile (pat, REG_ICASE))) {
642 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
646 /* check to make sure the item is not already on this list */
647 for (last = *list; last; last = last->next) {
648 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
649 /* Already on the list. Formerly we just skipped this case, but
650 * now we're supporting removals, which means we're supporting
651 * re-adds conceptually. So we probably want this to imply a
652 * removal, then do an add. We can achieve the removal by freeing
653 * the template, and leaving t pointed at the current item.
656 mem_free(t->template);
663 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
664 * update. Otherwise we want to make a new one to link at the list's end.
667 t = mutt_new_spam_list ();
675 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
676 t->template = str_dup (templ);
678 /* Find highest match number in template string */
680 for (p = templ; *p;) {
685 while (*p && isdigit ((int) *p))
691 t->nmatch++; /* match 0 is always the whole expr */
696 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
698 SPAM_LIST *spam, *prev;
701 /* Being first is a special case. */
705 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
708 mem_free(&spam->template);
714 for (spam = prev->next; spam;) {
715 if (!str_cmp (spam->rx->pattern, pat)) {
716 prev->next = spam->next;
718 mem_free(spam->template);
731 static void remove_from_list (LIST ** l, const char *str)
733 LIST *p, *last = NULL;
735 if (str_cmp ("*", str) == 0)
736 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
741 if (ascii_strcasecmp (str, p->data) == 0) {
744 last->next = p->next;
757 static int remove_from_rx_list (list2_t** l, const char *str)
761 if (str_cmp ("*", str) == 0) {
762 list_del (l, (list_del_t*) rx_free);
766 i = rx_lookup ((*l), str);
768 rx_t* r = list_pop_idx ((*l), i);
776 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
781 struct option_t* option = NULL;
783 memset (&token, 0, sizeof (token));
784 mutt_extract_token (tmp, s, 0);
786 /* is the item defined as a variable or a function? */
787 if ((option = hash_find (ConfigOptions, tmp->data)))
790 for (i = 0; !res && i < MENU_MAX; i++) {
791 struct binding_t *b = km_get_table (Menus[i].value);
796 for (j = 0; b[j].name; j++)
797 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
798 && (str_len (b[j].name) == str_len (tmp->data))) {
804 /* check for feature_* */
809 j = str_len (tmp->data);
810 /* need at least input of 'feature_X' */
814 while (Features[i].name) {
815 if (str_len (Features[i].name) == j &&
816 ascii_strncasecmp (Features[i].name, p, j)) {
827 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
829 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
832 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
834 if ((data && res) || (!data && !res)) {
835 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
836 mutt_error ("Error: %s", err->data);
837 mem_free (&token.data);
840 mem_free (&token.data);
845 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
849 mutt_extract_token (buf, s, 0);
851 /* don't add "*" to the unignore list */
852 if (strcmp (buf->data, "*"))
853 add_to_list (&UnIgnore, buf->data);
855 remove_from_list (&Ignore, buf->data);
857 while (MoreArgs (s));
862 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
866 mutt_extract_token (buf, s, 0);
867 remove_from_list (&UnIgnore, buf->data);
868 add_to_list (&Ignore, buf->data);
870 while (MoreArgs (s));
875 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
879 mutt_extract_token (buf, s, 0);
880 add_to_list ((LIST **) data, buf->data);
882 while (MoreArgs (s));
887 static void _alternates_clean (void)
891 if (Context && Context->msgcount) {
892 for (i = 0; i < Context->msgcount; i++)
893 Context->hdrs[i]->recip_valid = 0;
897 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
900 _alternates_clean ();
902 mutt_extract_token (buf, s, 0);
903 remove_from_rx_list (&UnAlternates, buf->data);
905 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
908 while (MoreArgs (s));
913 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
916 _alternates_clean ();
918 mutt_extract_token (buf, s, 0);
919 remove_from_rx_list (&Alternates, buf->data);
921 if (str_cmp (buf->data, "*") &&
922 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
926 while (MoreArgs (s));
931 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
936 memset (&templ, 0, sizeof (templ));
938 /* Insist on at least one parameter */
941 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
943 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
947 /* Extract the first token, a regexp */
948 mutt_extract_token (buf, s, 0);
950 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
951 if (data == M_SPAM) {
952 /* If there's a second parameter, it's a template for the spam tag. */
954 mutt_extract_token (&templ, s, 0);
956 /* Add to the spam list. */
957 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
958 mem_free (&templ.data);
961 mem_free (&templ.data);
964 /* If not, try to remove from the nospam list. */
966 remove_from_rx_list (&NoSpamList, buf->data);
972 /* M_NOSPAM is for nospam commands. */
973 else if (data == M_NOSPAM) {
974 /* nospam only ever has one parameter. */
976 /* "*" is a special case. */
977 if (!str_cmp (buf->data, "*")) {
978 mutt_free_spam_list (&SpamList);
979 list_del (&NoSpamList, (list_del_t*) rx_free);
983 /* If it's on the spam list, just remove it. */
984 if (remove_from_spam_list (&SpamList, buf->data) != 0)
987 /* Otherwise, add it to the nospam list. */
988 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
994 /* This should not happen. */
995 strfcpy (err->data, "This is no good at all.", err->dsize);
999 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1003 mutt_extract_token (buf, s, 0);
1005 * Check for deletion of entire list
1007 if (str_cmp (buf->data, "*") == 0) {
1008 mutt_free_list ((LIST **) data);
1011 remove_from_list ((LIST **) data, buf->data);
1013 while (MoreArgs (s));
1018 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1022 mutt_extract_token (buf, s, 0);
1023 remove_from_rx_list (&UnMailLists, buf->data);
1025 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1028 while (MoreArgs (s));
1033 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1037 mutt_extract_token (buf, s, 0);
1038 remove_from_rx_list (&SubscribedLists, buf->data);
1039 remove_from_rx_list (&MailLists, buf->data);
1041 if (str_cmp (buf->data, "*") &&
1042 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1045 while (MoreArgs (s));
1050 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1054 mutt_extract_token (buf, s, 0);
1055 remove_from_rx_list (&UnMailLists, buf->data);
1056 remove_from_rx_list (&UnSubscribedLists, buf->data);
1058 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1060 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1063 while (MoreArgs (s));
1068 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1072 mutt_extract_token (buf, s, 0);
1073 remove_from_rx_list (&SubscribedLists, buf->data);
1075 if (str_cmp (buf->data, "*") &&
1076 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1079 while (MoreArgs (s));
1084 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1087 ALIAS *tmp, *last = NULL;
1090 mutt_extract_token (buf, s, 0);
1092 if (str_cmp ("*", buf->data) == 0) {
1093 if (CurrentMenu == MENU_ALIAS) {
1094 for (tmp = Aliases; tmp; tmp = tmp->next)
1096 set_option (OPTFORCEREDRAWINDEX);
1099 mutt_free_alias (&Aliases);
1103 for (tmp = Aliases; tmp; tmp = tmp->next) {
1104 if (str_casecmp (buf->data, tmp->name) == 0) {
1105 if (CurrentMenu == MENU_ALIAS) {
1107 set_option (OPTFORCEREDRAWINDEX);
1112 last->next = tmp->next;
1114 Aliases = tmp->next;
1116 mutt_free_alias (&tmp);
1122 while (MoreArgs (s));
1126 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1129 ALIAS *tmp = Aliases;
1133 if (!MoreArgs (s)) {
1134 strfcpy (err->data, _("alias: no address"), err->dsize);
1138 mutt_extract_token (buf, s, 0);
1140 debug_print (2, ("first token is '%s'.\n", buf->data));
1142 /* check to see if an alias with this name already exists */
1143 for (; tmp; tmp = tmp->next) {
1144 if (!str_casecmp (tmp->name, buf->data))
1150 /* create a new alias */
1151 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1153 tmp->name = str_dup (buf->data);
1154 /* give the main addressbook code a chance */
1155 if (CurrentMenu == MENU_ALIAS)
1156 set_option (OPTMENUCALLER);
1159 /* override the previous value */
1160 rfc822_free_address (&tmp->addr);
1161 if (CurrentMenu == MENU_ALIAS)
1162 set_option (OPTFORCEREDRAWINDEX);
1165 mutt_extract_token (buf, s,
1166 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1167 debug_print (2, ("second token is '%s'.\n", buf->data));
1168 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1173 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1174 snprintf (err->data, err->dsize,
1175 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1179 if (DebugLevel >= 2) {
1182 for (a = tmp->addr; a; a = a->next) {
1184 debug_print (2, ("%s\n", a->mailbox));
1186 debug_print (2, ("group %s\n", a->mailbox));
1194 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1197 LIST *tmp = UserHeader;
1202 mutt_extract_token (buf, s, 0);
1203 if (str_cmp ("*", buf->data) == 0)
1204 mutt_free_list (&UserHeader);
1209 l = str_len (buf->data);
1210 if (buf->data[l - 1] == ':')
1214 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1215 && tmp->data[l] == ':') {
1218 last->next = tmp->next;
1220 UserHeader = tmp->next;
1223 mutt_free_list (&ptr);
1232 while (MoreArgs (s));
1236 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1243 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1244 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1245 strfcpy (err->data, _("invalid header field"), err->dsize);
1248 keylen = p - buf->data + 1;
1251 for (tmp = UserHeader;; tmp = tmp->next) {
1252 /* see if there is already a field by this name */
1253 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1254 /* replace the old value */
1255 mem_free (&tmp->data);
1256 tmp->data = buf->data;
1257 memset (buf, 0, sizeof (BUFFER));
1263 tmp->next = mutt_new_list ();
1267 tmp = mutt_new_list ();
1270 tmp->data = buf->data;
1271 memset (buf, 0, sizeof (BUFFER));
1276 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1277 char* errbuf, size_t errlen) {
1280 if (str_ncmp ("reverse-", s, 8) == 0) {
1282 flags = SORT_REVERSE;
1285 if (str_ncmp ("last-", s, 5) == 0) {
1290 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1292 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1296 *((short*) dst->data) = i | flags;
1300 /* if additional data more == 1, we want to resolve synonyms */
1301 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1302 char buf[LONG_STRING];
1303 struct option_t* ptr = (struct option_t*) p;
1305 if (DTYPE (ptr->type) == DT_SYN) {
1308 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1310 if (!ptr || *ptr->init)
1312 mutt_option_value (ptr->option, buf, sizeof (buf));
1313 if (str_len (ptr->init) == 0 && buf && *buf)
1314 ptr->init = str_dup (buf);
1317 /* creates new option_t* of type DT_USER for $user_ var */
1318 static struct option_t* add_user_option (const char* name) {
1319 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1320 option->option = str_dup (name);
1321 option->type = DT_USER;
1325 /* free()'s option_t* */
1326 static void del_user_option (void* p) {
1327 struct option_t* ptr = (struct option_t*) p;
1328 char* s = (char*) ptr->data;
1329 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1330 mem_free (&ptr->option);
1332 mem_free (&ptr->init);
1336 /* if additional data more == 1, we want to resolve synonyms */
1337 static void mutt_restore_default (const char* name, void* p,
1338 unsigned long more) {
1339 char errbuf[STRING];
1340 struct option_t* ptr = (struct option_t*) p;
1342 if (DTYPE (ptr->type) == DT_SYN) {
1345 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1349 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1350 sizeof (errbuf)) < 0) {
1352 fprintf (stderr, _("Invalid default setting found. Please report this "
1353 "error:\n\"%s\"\n"), errbuf);
1357 if (ptr->flags & R_INDEX)
1358 set_option (OPTFORCEREDRAWINDEX);
1359 if (ptr->flags & R_PAGER)
1360 set_option (OPTFORCEREDRAWPAGER);
1361 if (ptr->flags & R_RESORT_SUB)
1362 set_option (OPTSORTSUBTHREADS);
1363 if (ptr->flags & R_RESORT)
1364 set_option (OPTNEEDRESORT);
1365 if (ptr->flags & R_RESORT_INIT)
1366 set_option (OPTRESORTINIT);
1367 if (ptr->flags & R_TREE)
1368 set_option (OPTREDRAWTREE);
1371 /* check whether value for $dsn_return would be valid */
1372 static int check_dsn_return (const char* option, unsigned long p,
1373 char* errbuf, size_t errlen) {
1374 char* val = (char*) p;
1375 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1376 str_ncmp (val, "full", 4) != 0) {
1378 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1384 /* check whether value for $dsn_notify would be valid */
1385 static int check_dsn_notify (const char* option, unsigned long p,
1386 char* errbuf, size_t errlen) {
1387 list2_t* list = NULL;
1389 char* val = (char*) p;
1393 list = list_from_str (val, ",");
1394 if (list_empty (list))
1397 for (i = 0; i < list->length; i++)
1398 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1399 str_ncmp (list->data[i], "failure", 7) != 0 &&
1400 str_ncmp (list->data[i], "delay", 5) != 0 &&
1401 str_ncmp (list->data[i], "success", 7) != 0) {
1403 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1404 (char*) list->data[i], "dsn_notify");
1408 list_del (&list, (list_del_t*) _mem_free);
1412 static int check_num (const char* option, unsigned long p,
1413 char* errbuf, size_t errlen) {
1416 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1423 static int check_debug (const char* option, unsigned long p,
1424 char* errbuf, size_t errlen) {
1425 if ((int) p <= DEBUG_MAX_LEVEL &&
1426 (int) p >= DEBUG_MIN_LEVEL)
1430 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1435 static int check_history (const char* option, unsigned long p,
1436 char* errbuf, size_t errlen) {
1437 if (!check_num ("history", p, errbuf, errlen))
1439 mutt_init_history ();
1443 static int check_special (const char* name, unsigned long val,
1444 char* errbuf, size_t errlen) {
1447 for (i = 0; SpecialVars[i].name; i++) {
1448 if (str_cmp (SpecialVars[i].name, name) == 0) {
1449 return (SpecialVars[i].check (SpecialVars[i].name,
1450 val, errbuf, errlen));
1456 static const struct mapping_t* get_sortmap (struct option_t* option) {
1457 const struct mapping_t* map = NULL;
1459 switch (option->type & DT_SUBTYPE_MASK) {
1461 map = SortAliasMethods;
1463 case DT_SORT_BROWSER:
1464 map = SortBrowserMethods;
1467 if ((WithCrypto & APPLICATION_PGP))
1468 map = SortKeyMethods;
1471 map = SortAuxMethods;
1480 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1483 int query, unset, inv, reset, r = 0;
1484 struct option_t* option = NULL;
1486 while (MoreArgs (s)) {
1487 /* reset state variables */
1489 unset = data & M_SET_UNSET;
1490 inv = data & M_SET_INV;
1491 reset = data & M_SET_RESET;
1493 if (*s->dptr == '?') {
1497 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1501 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1505 else if (*s->dptr == '&') {
1510 /* get the variable name */
1511 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1513 /* resolve synonyms */
1514 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1515 DTYPE (option->type == DT_SYN)) {
1516 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1517 syn_add (newopt, option);
1521 /* see if we need to add $user_ var */
1522 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1523 /* there's no option named like this yet so only add one
1524 * if the action isn't any of: reset, unset, query */
1525 if (!(reset || unset || query || *s->dptr != '=')) {
1526 debug_print (1, ("adding user option '%s'\n", tmp->data));
1527 option = add_user_option (tmp->data);
1528 hash_insert (ConfigOptions, option->option, option, 0);
1532 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1533 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1539 if (query || unset || inv) {
1540 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1544 if (s && *s->dptr == '=') {
1545 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1549 if (!str_cmp ("all", tmp->data)) {
1550 hash_map (ConfigOptions, mutt_restore_default, 1);
1554 mutt_restore_default (NULL, option, 1);
1556 else if (DTYPE (option->type) == DT_BOOL) {
1557 /* XXX this currently ignores the function table
1558 * as we don't get invert and stuff into it */
1559 if (s && *s->dptr == '=') {
1560 if (unset || inv || query) {
1561 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1566 mutt_extract_token (tmp, s, 0);
1567 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1569 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1572 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1578 bool_to_string (err->data, err->dsize, option);
1583 unset_option (option->data);
1585 toggle_option (option->data);
1587 set_option (option->data);
1589 else if (DTYPE (option->type) == DT_STR ||
1590 DTYPE (option->type) == DT_PATH ||
1591 DTYPE (option->type) == DT_ADDR ||
1592 DTYPE (option->type) == DT_MAGIC ||
1593 DTYPE (option->type) == DT_NUM ||
1594 DTYPE (option->type) == DT_SORT ||
1595 DTYPE (option->type) == DT_RX ||
1596 DTYPE (option->type) == DT_USER) {
1598 /* XXX maybe we need to get unset into handlers? */
1599 if (DTYPE (option->type) == DT_STR ||
1600 DTYPE (option->type) == DT_PATH ||
1601 DTYPE (option->type) == DT_ADDR ||
1602 DTYPE (option->type) == DT_USER) {
1604 if (DTYPE (option->type) == DT_ADDR)
1605 rfc822_free_address ((ADDRESS **) option->data);
1606 else if (DTYPE (option->type) == DT_USER)
1607 /* to unset $user_ means remove */
1608 hash_delete (ConfigOptions, option->option,
1609 option, del_user_option);
1611 mem_free ((void *) option->data);
1616 if (query || *s->dptr != '=') {
1617 FuncTable[DTYPE (option->type)].opt_to_string
1618 (err->data, err->dsize, option);
1623 mutt_extract_token (tmp, s, 0);
1624 if (!FuncTable[DTYPE (option->type)].opt_from_string
1625 (option, tmp->data, err->data, err->dsize))
1628 else if (DTYPE (option->type) == DT_QUAD) {
1631 quad_to_string (err->data, err->dsize, option);
1635 if (*s->dptr == '=') {
1637 mutt_extract_token (tmp, s, 0);
1638 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1639 set_quadoption (option->data, M_YES);
1640 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1641 set_quadoption (option->data, M_NO);
1642 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1643 set_quadoption (option->data, M_ASKYES);
1644 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1645 set_quadoption (option->data, M_ASKNO);
1647 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1648 tmp->data, option->option);
1655 toggle_quadoption (option->data);
1657 set_quadoption (option->data, M_NO);
1659 set_quadoption (option->data, M_YES);
1663 snprintf (err->data, err->dsize, _("%s: unknown type"),
1669 if (option->flags & R_INDEX)
1670 set_option (OPTFORCEREDRAWINDEX);
1671 if (option->flags & R_PAGER)
1672 set_option (OPTFORCEREDRAWPAGER);
1673 if (option->flags & R_RESORT_SUB)
1674 set_option (OPTSORTSUBTHREADS);
1675 if (option->flags & R_RESORT)
1676 set_option (OPTNEEDRESORT);
1677 if (option->flags & R_RESORT_INIT)
1678 set_option (OPTRESORTINIT);
1679 if (option->flags & R_TREE)
1680 set_option (OPTREDRAWTREE);
1687 /* reads the specified initialization file. returns -1 if errors were found
1688 so that we can pause to let the user know... */
1689 static int source_rc (const char *rcfile, BUFFER * err)
1692 int line = 0, rc = 0, conv = 0;
1694 char *linebuf = NULL;
1695 char *currentline = NULL;
1699 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1701 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1702 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1706 memset (&token, 0, sizeof (token));
1707 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1708 conv = ConfigCharset && (*ConfigCharset) && Charset;
1710 currentline = str_dup (linebuf);
1713 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1716 currentline = linebuf;
1721 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1722 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1723 if (--rc < -MAXERRS) {
1725 mem_free (¤tline);
1734 mem_free (¤tline);
1736 mem_free (&token.data);
1737 mem_free (&linebuf);
1740 mutt_wait_filter (pid);
1742 /* the muttrc source keyword */
1743 snprintf (err->data, err->dsize,
1744 rc >= -MAXERRS ? _("source: errors in %s")
1745 : _("source: reading aborted due too many errors in %s"),
1754 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1757 char path[_POSIX_PATH_MAX];
1761 if (mutt_extract_token (tmp, s, 0) != 0) {
1762 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1766 strfcpy (path, tmp->data, sizeof (path));
1767 mutt_expand_path (path, sizeof (path));
1769 rc += source_rc (path, err);
1771 while (MoreArgs (s));
1773 return ((rc < 0) ? -1 : 0);
1776 /* line command to execute
1778 token scratch buffer to be used by parser. caller should free
1779 token->data when finished. the reason for this variable is
1780 to avoid having to allocate and deallocate a lot of memory
1781 if we are parsing many lines. the caller can pass in the
1782 memory to use, which avoids having to create new space for
1783 every call to this function.
1785 err where to write error messages */
1786 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1791 memset (&expn, 0, sizeof (expn));
1792 expn.data = expn.dptr = line;
1793 expn.dsize = str_len (line);
1798 while (*expn.dptr) {
1799 if (*expn.dptr == '#')
1800 break; /* rest of line is a comment */
1801 if (*expn.dptr == ';') {
1805 mutt_extract_token (token, &expn, 0);
1806 for (i = 0; Commands[i].name; i++) {
1807 if (!str_cmp (token->data, Commands[i].name)) {
1808 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1813 if (!Commands[i].name) {
1814 snprintf (err->data, err->dsize, _("%s: unknown command"),
1815 NONULL (token->data));
1822 mem_free (&expn.data);
1827 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1828 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1829 /* initial string that starts completion. No telling how much crap
1830 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1831 char User_typed[LONG_STRING] = { 0 };
1833 int Num_matched = 0; /* Number of matches for completion */
1834 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1835 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1837 /* helper function for completion. Changes the dest buffer if
1838 necessary/possible to aid completion.
1839 dest == completion result gets here.
1840 src == candidate for completion.
1841 try == user entered data for completion.
1842 len == length of dest buffer.
1844 static void candidate (char *dest, char *try, char *src, int len)
1848 if (strstr (src, try) == src) {
1849 Matches[Num_matched++] = src;
1851 strfcpy (dest, src, len);
1853 for (l = 0; src[l] && src[l] == dest[l]; l++);
1859 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1863 int spaces; /* keep track of the number of leading spaces on the line */
1866 spaces = buffer - pt;
1868 pt = buffer + pos - spaces;
1869 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1872 if (pt == buffer) { /* complete cmd */
1873 /* first TAB. Collect all the matches */
1876 strfcpy (User_typed, pt, sizeof (User_typed));
1877 memset (Matches, 0, sizeof (Matches));
1878 memset (Completed, 0, sizeof (Completed));
1879 for (num = 0; Commands[num].name; num++)
1880 candidate (Completed, User_typed, Commands[num].name,
1881 sizeof (Completed));
1882 Matches[Num_matched++] = User_typed;
1884 /* All matches are stored. Longest non-ambiguous string is ""
1885 * i.e. dont change 'buffer'. Fake successful return this time */
1886 if (User_typed[0] == 0)
1890 if (Completed[0] == 0 && User_typed[0])
1893 /* Num_matched will _always_ be atleast 1 since the initial
1894 * user-typed string is always stored */
1895 if (numtabs == 1 && Num_matched == 2)
1896 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1897 else if (numtabs > 1 && Num_matched > 2)
1898 /* cycle thru all the matches */
1899 snprintf (Completed, sizeof (Completed), "%s",
1900 Matches[(numtabs - 2) % Num_matched]);
1902 /* return the completed command */
1903 strncpy (buffer, Completed, len - spaces);
1905 else if (!str_ncmp (buffer, "set", 3)
1906 || !str_ncmp (buffer, "unset", 5)
1907 || !str_ncmp (buffer, "reset", 5)
1908 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1909 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1912 /* loop through all the possible prefixes (no, inv, ...) */
1913 if (!str_ncmp (buffer, "set", 3)) {
1914 for (num = 0; prefixes[num]; num++) {
1915 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1916 pt += str_len (prefixes[num]);
1922 /* first TAB. Collect all the matches */
1925 strfcpy (User_typed, pt, sizeof (User_typed));
1926 memset (Matches, 0, sizeof (Matches));
1927 memset (Completed, 0, sizeof (Completed));
1928 for (num = 0; MuttVars[num].option; num++)
1929 candidate (Completed, User_typed, MuttVars[num].option,
1930 sizeof (Completed));
1931 Matches[Num_matched++] = User_typed;
1933 /* All matches are stored. Longest non-ambiguous string is ""
1934 * i.e. dont change 'buffer'. Fake successful return this time */
1935 if (User_typed[0] == 0)
1939 if (Completed[0] == 0 && User_typed[0])
1942 /* Num_matched will _always_ be atleast 1 since the initial
1943 * user-typed string is always stored */
1944 if (numtabs == 1 && Num_matched == 2)
1945 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1946 else if (numtabs > 1 && Num_matched > 2)
1947 /* cycle thru all the matches */
1948 snprintf (Completed, sizeof (Completed), "%s",
1949 Matches[(numtabs - 2) % Num_matched]);
1951 strncpy (pt, Completed, buffer + len - pt - spaces);
1953 else if (!str_ncmp (buffer, "exec", 4)) {
1954 struct binding_t *menu = km_get_table (CurrentMenu);
1956 if (!menu && CurrentMenu != MENU_PAGER)
1960 /* first TAB. Collect all the matches */
1963 strfcpy (User_typed, pt, sizeof (User_typed));
1964 memset (Matches, 0, sizeof (Matches));
1965 memset (Completed, 0, sizeof (Completed));
1966 for (num = 0; menu[num].name; num++)
1967 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1968 /* try the generic menu */
1969 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1971 for (num = 0; menu[num].name; num++)
1972 candidate (Completed, User_typed, menu[num].name,
1973 sizeof (Completed));
1975 Matches[Num_matched++] = User_typed;
1977 /* All matches are stored. Longest non-ambiguous string is ""
1978 * i.e. dont change 'buffer'. Fake successful return this time */
1979 if (User_typed[0] == 0)
1983 if (Completed[0] == 0 && User_typed[0])
1986 /* Num_matched will _always_ be atleast 1 since the initial
1987 * user-typed string is always stored */
1988 if (numtabs == 1 && Num_matched == 2)
1989 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1990 else if (numtabs > 1 && Num_matched > 2)
1991 /* cycle thru all the matches */
1992 snprintf (Completed, sizeof (Completed), "%s",
1993 Matches[(numtabs - 2) % Num_matched]);
1995 strncpy (pt, Completed, buffer + len - pt - spaces);
2003 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2005 char var[STRING], *pt = buffer;
2007 struct option_t* option = NULL;
2013 spaces = buffer - pt;
2015 pt = buffer + pos - spaces;
2016 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2018 pt++; /* move past the space */
2019 if (*pt == '=') /* abort if no var before the '=' */
2022 if (str_ncmp (buffer, "set", 3) == 0) {
2023 strfcpy (var, pt, sizeof (var));
2024 /* ignore the trailing '=' when comparing */
2025 var[str_len (var) - 1] = 0;
2026 if (!(option = hash_find (ConfigOptions, var)))
2027 return 0; /* no such variable. */
2029 char tmp[LONG_STRING], tmp2[LONG_STRING];
2031 size_t dlen = buffer + len - pt - spaces;
2032 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2036 if ((DTYPE (option->type) == DT_STR) ||
2037 (DTYPE (option->type) == DT_PATH) ||
2038 (DTYPE (option->type) == DT_RX)) {
2039 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2040 if (DTYPE (option->type) == DT_PATH)
2041 mutt_pretty_mailbox (tmp);
2043 else if (DTYPE (option->type) == DT_ADDR) {
2044 rfc822_write_address (tmp, sizeof (tmp),
2045 *((ADDRESS **) option->data), 0);
2047 else if (DTYPE (option->type) == DT_QUAD)
2048 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2049 else if (DTYPE (option->type) == DT_NUM)
2050 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2051 else if (DTYPE (option->type) == DT_SORT) {
2052 const struct mapping_t *map;
2055 switch (option->type & DT_SUBTYPE_MASK) {
2057 map = SortAliasMethods;
2059 case DT_SORT_BROWSER:
2060 map = SortBrowserMethods;
2063 if ((WithCrypto & APPLICATION_PGP))
2064 map = SortKeyMethods;
2073 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2075 snprintf (tmp, sizeof (tmp), "%s%s%s",
2076 (*((short *) option->data) & SORT_REVERSE) ?
2078 (*((short *) option->data) & SORT_LAST) ? "last-" :
2081 else if (DTYPE (option->type) == DT_MAGIC) {
2083 switch (DefaultMagic) {
2099 strfcpy (tmp, p, sizeof (tmp));
2101 else if (DTYPE (option->type) == DT_BOOL)
2102 strfcpy (tmp, option (option->data) ? "yes" : "no",
2107 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2108 if (*s == '\\' || *s == '"')
2114 strfcpy (tmp, pt, sizeof (tmp));
2115 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2123 /* Implement the -Q command line flag */
2124 int mutt_query_variables (LIST * queries)
2128 char errbuff[STRING];
2129 char command[STRING];
2133 memset (&err, 0, sizeof (err));
2134 memset (&token, 0, sizeof (token));
2137 err.dsize = sizeof (errbuff);
2139 for (p = queries; p; p = p->next) {
2140 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2141 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2142 fprintf (stderr, "%s\n", err.data);
2143 mem_free (&token.data);
2146 printf ("%s\n", err.data);
2149 mem_free (&token.data);
2153 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2157 for (i = 0; map[i].name; i++)
2158 if (map[i].value == val)
2159 return (map[i].name);
2163 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2167 for (i = 0; map[i].name; i++)
2168 if (ascii_strcasecmp (map[i].name, name) == 0)
2169 return (map[i].value);
2173 static int mutt_execute_commands (LIST * p)
2176 char errstr[SHORT_STRING];
2178 memset (&err, 0, sizeof (err));
2180 err.dsize = sizeof (errstr);
2181 memset (&token, 0, sizeof (token));
2182 for (; p; p = p->next) {
2183 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2184 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2185 mem_free (&token.data);
2189 mem_free (&token.data);
2193 void mutt_init (int skip_sys_rc, LIST * commands)
2196 struct utsname utsname;
2197 char *p, buffer[STRING], error[STRING];
2198 int i, default_rc = 0, need_pause = 0;
2201 memset (&err, 0, sizeof (err));
2203 err.dsize = sizeof (error);
2205 /* use 3*sizeof(muttvars) to have some room for $user_ vars */
2206 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2207 for (i = 0; MuttVars[i].option; i++)
2208 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2211 * XXX - use something even more difficult to predict?
2213 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2214 "\033]9;%ld\a", (long) time (NULL));
2216 /* on one of the systems I use, getcwd() does not return the same prefix
2217 as is listed in the passwd file */
2218 if ((p = getenv ("HOME")))
2219 Homedir = str_dup (p);
2221 /* Get some information about the user */
2222 if ((pw = getpwuid (getuid ()))) {
2225 Username = str_dup (pw->pw_name);
2227 Homedir = str_dup (pw->pw_dir);
2229 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2230 Shell = str_dup (pw->pw_shell);
2235 fputs (_("unable to determine home directory"), stderr);
2238 if ((p = getenv ("USER")))
2239 Username = str_dup (p);
2242 fputs (_("unable to determine username"), stderr);
2245 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2248 debug_start(Homedir);
2250 /* And about the host... */
2252 /* some systems report the FQDN instead of just the hostname */
2253 if ((p = strchr (utsname.nodename, '.'))) {
2254 Hostname = str_substrdup (utsname.nodename, p);
2256 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2259 Hostname = str_dup (utsname.nodename);
2262 #define DOMAIN buffer
2263 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2264 Fqdn = str_dup ("@");
2267 if (*DOMAIN != '@') {
2268 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2269 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2272 Fqdn = str_dup (NONULL (Hostname));
2279 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2281 fgets (buffer, sizeof (buffer), f);
2282 p = (char*) &buffer;
2285 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2289 NewsServer = str_dup (p);
2293 if ((p = getenv ("NNTPSERVER")))
2294 NewsServer = str_dup (p);
2297 if ((p = getenv ("MAIL")))
2298 Spoolfile = str_dup (p);
2299 else if ((p = getenv ("MAILDIR")))
2300 Spoolfile = str_dup (p);
2303 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2305 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2307 Spoolfile = str_dup (buffer);
2310 if ((p = getenv ("MAILCAPS")))
2311 MailcapPath = str_dup (p);
2313 /* Default search path from RFC1524 */
2315 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2316 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2319 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2321 p = getenv ("VISUAL");
2323 p = getenv ("EDITOR");
2327 Editor = str_dup (p);
2328 Visual = str_dup (p);
2330 if ((p = getenv ("REPLYTO")) != NULL) {
2333 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2335 memset (&buf, 0, sizeof (buf));
2336 buf.data = buf.dptr = buffer;
2337 buf.dsize = str_len (buffer);
2339 memset (&token, 0, sizeof (token));
2340 parse_my_hdr (&token, &buf, 0, &err);
2341 mem_free (&token.data);
2344 if ((p = getenv ("EMAIL")) != NULL)
2345 From = rfc822_parse_adrlist (NULL, p);
2347 mutt_set_langinfo_charset ();
2348 mutt_set_charset (Charset);
2351 /* Set standard defaults */
2352 hash_map (ConfigOptions, mutt_set_default, 0);
2353 hash_map (ConfigOptions, mutt_restore_default, 0);
2355 CurrentMenu = MENU_MAIN;
2358 #ifndef LOCALES_HACK
2359 /* Do we have a locale definition? */
2360 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2361 ((p = getenv ("LANG")) != NULL && p[0]) ||
2362 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2363 set_option (OPTLOCALES);
2367 /* Unset suspend by default if we're the session leader */
2368 if (getsid (0) == getpid ())
2369 unset_option (OPTSUSPEND);
2372 mutt_init_history ();
2381 * When changing the code which looks for a configuration file,
2382 * please also change the corresponding code in muttbug.sh.in.
2392 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2394 if (access (buffer, F_OK) == -1)
2396 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2397 if (access (buffer, F_OK) == -1)
2399 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2400 NONULL (Homedir), MUTT_VERSION);
2401 if (access (buffer, F_OK) == -1)
2403 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2407 Muttrc = str_dup (buffer);
2410 strfcpy (buffer, Muttrc, sizeof (buffer));
2412 mutt_expand_path (buffer, sizeof (buffer));
2413 Muttrc = str_dup (buffer);
2415 mem_free (&AliasFile);
2416 AliasFile = str_dup (NONULL (Muttrc));
2418 /* Process the global rc file if it exists and the user hasn't explicity
2419 requested not to via "-n". */
2421 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2423 if (access (buffer, F_OK) == -1)
2424 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2425 if (access (buffer, F_OK) == -1)
2426 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2428 if (access (buffer, F_OK) == -1)
2429 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2430 if (access (buffer, F_OK) != -1) {
2431 if (source_rc (buffer, &err) != 0) {
2432 fputs (err.data, stderr);
2433 fputc ('\n', stderr);
2439 /* Read the user's initialization file. */
2440 if (access (Muttrc, F_OK) != -1) {
2441 if (!option (OPTNOCURSES))
2443 if (source_rc (Muttrc, &err) != 0) {
2444 fputs (err.data, stderr);
2445 fputc ('\n', stderr);
2449 else if (!default_rc) {
2450 /* file specified by -F does not exist */
2451 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2452 mutt_endwin (buffer);
2456 if (mutt_execute_commands (commands) != 0)
2459 /* warn about synonym variables */
2460 if (!list_empty(Synonyms)) {
2462 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2463 for (i = 0; i < Synonyms->length; i++) {
2464 struct option_t* newopt = NULL, *oldopt = NULL;
2465 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2466 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2467 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2468 oldopt ? NONULL (oldopt->option) : "",
2469 newopt ? NONULL (newopt->option) : "",
2470 NONULL(((syn_t*) Synonyms->data[i])->f),
2471 ((syn_t*) Synonyms->data[i])->l);
2473 fprintf (stderr, _("Warning: synonym variables are scheduled"
2474 " for removal.\n"));
2475 list_del (&Synonyms, syn_del);
2479 if (need_pause && !option (OPTNOCURSES)) {
2480 if (mutt_any_key_to_continue (NULL) == -1)
2485 set_option (OPTWEED); /* turn weeding on by default */
2489 int mutt_get_hook_type (const char *name)
2491 struct command_t *c;
2493 for (c = Commands; c->name; c++)
2494 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2499 /* compare two option_t*'s for sorting -t/-T output */
2500 static int opt_cmp (const void* a, const void* b) {
2501 return (str_cmp ((*(struct option_t**) a)->option,
2502 (*(struct option_t**) b)->option));
2505 /* callback for hash_map() to put all non-synonym vars into list */
2506 static void opt_sel_full (const char* key, void* data,
2507 unsigned long more) {
2508 list2_t** l = (list2_t**) more;
2509 struct option_t* option = (struct option_t*) data;
2511 if (DTYPE (option->type) == DT_SYN)
2513 list_push_back (l, option);
2516 /* callback for hash_map() to put all changed non-synonym vars into list */
2517 static void opt_sel_diff (const char* key, void* data,
2518 unsigned long more) {
2519 list2_t** l = (list2_t**) more;
2520 struct option_t* option = (struct option_t*) data;
2521 char buf[LONG_STRING];
2523 if (DTYPE (option->type) == DT_SYN)
2526 mutt_option_value (option->option, buf, sizeof (buf));
2527 if (str_cmp (buf, option->init) != 0)
2528 list_push_back (l, option);
2531 /* dump out the value of all the variables we have */
2532 int mutt_dump_variables (int full) {
2534 char outbuf[STRING];
2535 list2_t* tmp = NULL;
2536 struct option_t* option = NULL;
2538 /* get all non-synonyms into list... */
2539 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2540 (unsigned long) &tmp);
2542 if (!list_empty(tmp)) {
2543 /* ...and dump list sorted */
2544 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2545 for (i = 0; i < tmp->length; i++) {
2546 option = (struct option_t*) tmp->data[i];
2547 FuncTable[DTYPE (option->type)].opt_to_string
2548 (outbuf, sizeof (outbuf), option);
2549 printf ("%s\n", outbuf);
2552 list_del (&tmp, NULL);