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);
448 if (p->pattern && p->rx) {
453 str_replace (&p->pattern, val);
457 if (str_cmp (dst->option, "reply_regexp") == 0)
458 mutt_adjust_all_subjects ();
463 static void magic_to_string (char* dst, size_t dstlen,
464 struct option_t* option) {
465 const char* s = NULL;
466 switch (option->data) {
467 case M_MBOX: s = "mbox"; break;
468 case M_MMDF: s = "MMDF"; break;
469 case M_MH: s = "MH"; break;
470 case M_MAILDIR: s = "Maildir"; break;
471 default: s = "unknown"; break;
473 snprintf (dst, dstlen, "%s=%s", option->option, s);
476 static int magic_from_string (struct option_t* dst, const char* val,
477 char* errbuf, size_t errlen) {
480 if (!dst || !val || !*val)
482 if (ascii_strncasecmp (val, "mbox", 4) == 0)
484 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
486 else if (ascii_strncasecmp (val, "mh", 2) == 0)
488 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
494 *((short*) dst->data) = flag;
499 static void addr_to_string (char* dst, size_t dstlen,
500 struct option_t* option) {
503 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
504 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
507 static int addr_from_string (struct option_t* dst, const char* val,
508 char* errbuf, size_t errlen) {
509 if (!dst || !val || !*val)
511 rfc822_free_address ((ADDRESS**) dst->data);
512 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
516 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
517 struct option_t* option = NULL;
518 char* tmp = NULL, *t = NULL;
521 if (!(option = hash_find (ConfigOptions, val))) {
522 debug_print (1, ("var '%s' not found\n", val));
526 tmp = mem_malloc (dstlen+1);
527 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
529 /* as we get things of type $var=value and don't want to bloat the
530 * above "just" for expansion, we do the stripping here */
531 debug_print (1, ("orig == '%s'\n", tmp));
532 t = strchr (tmp, '=');
536 if (t[l-1] == '"' && *t == '"') {
541 memcpy (dst, t, l+1);
543 debug_print (1, ("stripped == '%s'\n", dst));
548 /* for synonym warning reports: adds synonym to end of list */
549 static void syn_add (struct option_t* n, struct option_t* o) {
550 syn_t* tmp = mem_malloc (sizeof (syn_t));
551 tmp->f = str_dup (CurRCFile);
555 list_push_back (&Synonyms, tmp);
558 /* for synonym warning reports: free single item (for list_del()) */
559 static void syn_del (void** p) {
560 mem_free(&(*(syn_t**) p)->f);
564 void toggle_quadoption (int opt)
567 int b = (opt % 4) * 2;
569 QuadOptions[n] ^= (1 << b);
572 void set_quadoption (int opt, int flag)
575 int b = (opt % 4) * 2;
577 QuadOptions[n] &= ~(0x3 << b);
578 QuadOptions[n] |= (flag & 0x3) << b;
581 int quadoption (int opt)
584 int b = (opt % 4) * 2;
586 return (QuadOptions[n] >> b) & 0x3;
589 int query_quadoption (int opt, const char *prompt)
591 int v = quadoption (opt);
599 v = mutt_yesorno (prompt, (v == M_ASKYES));
600 CLEARLINE (LINES - 1);
607 static void add_to_list (LIST ** list, const char *str)
609 LIST *t, *last = NULL;
611 /* don't add a NULL or empty string to the list */
612 if (!str || *str == '\0')
615 /* check to make sure the item is not already on this list */
616 for (last = *list; last; last = last->next) {
617 if (ascii_strcasecmp (str, last->data) == 0) {
618 /* already on the list, so just ignore it */
626 if (!*list || last) {
627 t = (LIST *) mem_calloc (1, sizeof (LIST));
628 t->data = str_dup (str);
638 static int add_to_rx_list (list2_t** list, const char *s, int flags,
647 if (!(rx = rx_compile (s, flags))) {
648 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
652 i = rx_lookup ((*list), rx->pattern);
656 list_push_back (list, rx);
660 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
661 const char *templ, BUFFER * err)
663 SPAM_LIST *t = NULL, *last = NULL;
668 if (!pat || !*pat || !templ)
671 if (!(rx = rx_compile (pat, REG_ICASE))) {
672 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
676 /* check to make sure the item is not already on this list */
677 for (last = *list; last; last = last->next) {
678 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
679 /* Already on the list. Formerly we just skipped this case, but
680 * now we're supporting removals, which means we're supporting
681 * re-adds conceptually. So we probably want this to imply a
682 * removal, then do an add. We can achieve the removal by freeing
683 * the template, and leaving t pointed at the current item.
686 mem_free(t->template);
693 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
694 * update. Otherwise we want to make a new one to link at the list's end.
697 t = mutt_new_spam_list ();
705 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
706 t->template = str_dup (templ);
708 /* Find highest match number in template string */
710 for (p = templ; *p;) {
715 while (*p && isdigit ((int) *p))
721 t->nmatch++; /* match 0 is always the whole expr */
726 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
728 SPAM_LIST *spam, *prev;
731 /* Being first is a special case. */
735 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
738 mem_free(&spam->template);
744 for (spam = prev->next; spam;) {
745 if (!str_cmp (spam->rx->pattern, pat)) {
746 prev->next = spam->next;
748 mem_free(spam->template);
761 static void remove_from_list (LIST ** l, const char *str)
763 LIST *p, *last = NULL;
765 if (str_cmp ("*", str) == 0)
766 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
771 if (ascii_strcasecmp (str, p->data) == 0) {
774 last->next = p->next;
787 static int remove_from_rx_list (list2_t** l, const char *str)
791 if (str_cmp ("*", str) == 0) {
792 list_del (l, (list_del_t*) rx_free);
796 i = rx_lookup ((*l), str);
798 rx_t* r = list_pop_idx ((*l), i);
806 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
811 struct option_t* option = NULL;
813 memset (&token, 0, sizeof (token));
814 mutt_extract_token (tmp, s, 0);
816 /* is the item defined as a variable or a function? */
817 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
820 for (i = 0; !res && i < MENU_MAX; i++) {
821 struct binding_t *b = km_get_table (Menus[i].value);
826 for (j = 0; b[j].name; j++)
827 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
828 && (str_len (b[j].name) == str_len (tmp->data))) {
834 /* check for feature_* */
835 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
836 (j = str_len (tmp->data)) > 8) {
838 while (Features[i]) {
839 if (str_len (Features[i]) == j-8 &&
840 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
850 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
852 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
856 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
859 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
860 mutt_error ("Error: %s", err->data);
861 mem_free (&token.data);
864 mem_free (&token.data);
869 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
873 mutt_extract_token (buf, s, 0);
875 /* don't add "*" to the unignore list */
876 if (strcmp (buf->data, "*"))
877 add_to_list (&UnIgnore, buf->data);
879 remove_from_list (&Ignore, buf->data);
881 while (MoreArgs (s));
886 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
890 mutt_extract_token (buf, s, 0);
891 remove_from_list (&UnIgnore, buf->data);
892 add_to_list (&Ignore, buf->data);
894 while (MoreArgs (s));
899 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
903 mutt_extract_token (buf, s, 0);
904 add_to_list ((LIST **) data, buf->data);
906 while (MoreArgs (s));
911 static void _alternates_clean (void)
915 if (Context && Context->msgcount) {
916 for (i = 0; i < Context->msgcount; i++)
917 Context->hdrs[i]->recip_valid = 0;
921 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
924 _alternates_clean ();
926 mutt_extract_token (buf, s, 0);
927 remove_from_rx_list (&UnAlternates, buf->data);
929 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
932 while (MoreArgs (s));
937 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
940 _alternates_clean ();
942 mutt_extract_token (buf, s, 0);
943 remove_from_rx_list (&Alternates, buf->data);
945 if (str_cmp (buf->data, "*") &&
946 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
950 while (MoreArgs (s));
955 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
960 memset (&templ, 0, sizeof (templ));
962 /* Insist on at least one parameter */
965 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
967 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
971 /* Extract the first token, a regexp */
972 mutt_extract_token (buf, s, 0);
974 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
975 if (data == M_SPAM) {
976 /* If there's a second parameter, it's a template for the spam tag. */
978 mutt_extract_token (&templ, s, 0);
980 /* Add to the spam list. */
981 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
982 mem_free (&templ.data);
985 mem_free (&templ.data);
988 /* If not, try to remove from the nospam list. */
990 remove_from_rx_list (&NoSpamList, buf->data);
996 /* M_NOSPAM is for nospam commands. */
997 else if (data == M_NOSPAM) {
998 /* nospam only ever has one parameter. */
1000 /* "*" is a special case. */
1001 if (!str_cmp (buf->data, "*")) {
1002 mutt_free_spam_list (&SpamList);
1003 list_del (&NoSpamList, (list_del_t*) rx_free);
1007 /* If it's on the spam list, just remove it. */
1008 if (remove_from_spam_list (&SpamList, buf->data) != 0)
1011 /* Otherwise, add it to the nospam list. */
1012 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
1018 /* This should not happen. */
1019 strfcpy (err->data, "This is no good at all.", err->dsize);
1023 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1027 mutt_extract_token (buf, s, 0);
1029 * Check for deletion of entire list
1031 if (str_cmp (buf->data, "*") == 0) {
1032 mutt_free_list ((LIST **) data);
1035 remove_from_list ((LIST **) data, buf->data);
1037 while (MoreArgs (s));
1042 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1046 mutt_extract_token (buf, s, 0);
1047 remove_from_rx_list (&UnMailLists, buf->data);
1049 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1052 while (MoreArgs (s));
1057 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1061 mutt_extract_token (buf, s, 0);
1062 remove_from_rx_list (&SubscribedLists, buf->data);
1063 remove_from_rx_list (&MailLists, buf->data);
1065 if (str_cmp (buf->data, "*") &&
1066 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1069 while (MoreArgs (s));
1074 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1078 mutt_extract_token (buf, s, 0);
1079 remove_from_rx_list (&UnMailLists, buf->data);
1080 remove_from_rx_list (&UnSubscribedLists, buf->data);
1082 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1084 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1087 while (MoreArgs (s));
1092 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1096 mutt_extract_token (buf, s, 0);
1097 remove_from_rx_list (&SubscribedLists, buf->data);
1099 if (str_cmp (buf->data, "*") &&
1100 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1103 while (MoreArgs (s));
1108 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1111 ALIAS *tmp, *last = NULL;
1114 mutt_extract_token (buf, s, 0);
1116 if (str_cmp ("*", buf->data) == 0) {
1117 if (CurrentMenu == MENU_ALIAS) {
1118 for (tmp = Aliases; tmp; tmp = tmp->next)
1120 set_option (OPTFORCEREDRAWINDEX);
1123 mutt_free_alias (&Aliases);
1127 for (tmp = Aliases; tmp; tmp = tmp->next) {
1128 if (str_casecmp (buf->data, tmp->name) == 0) {
1129 if (CurrentMenu == MENU_ALIAS) {
1131 set_option (OPTFORCEREDRAWINDEX);
1136 last->next = tmp->next;
1138 Aliases = tmp->next;
1140 mutt_free_alias (&tmp);
1146 while (MoreArgs (s));
1150 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1153 ALIAS *tmp = Aliases;
1157 if (!MoreArgs (s)) {
1158 strfcpy (err->data, _("alias: no address"), err->dsize);
1162 mutt_extract_token (buf, s, 0);
1164 debug_print (2, ("first token is '%s'.\n", buf->data));
1166 /* check to see if an alias with this name already exists */
1167 for (; tmp; tmp = tmp->next) {
1168 if (!str_casecmp (tmp->name, buf->data))
1174 /* create a new alias */
1175 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1177 tmp->name = str_dup (buf->data);
1178 /* give the main addressbook code a chance */
1179 if (CurrentMenu == MENU_ALIAS)
1180 set_option (OPTMENUCALLER);
1183 /* override the previous value */
1184 rfc822_free_address (&tmp->addr);
1185 if (CurrentMenu == MENU_ALIAS)
1186 set_option (OPTFORCEREDRAWINDEX);
1189 mutt_extract_token (buf, s,
1190 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1191 debug_print (2, ("second token is '%s'.\n", buf->data));
1192 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1197 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1198 snprintf (err->data, err->dsize,
1199 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1203 if (DebugLevel >= 2) {
1206 for (a = tmp->addr; a; a = a->next) {
1208 debug_print (2, ("%s\n", a->mailbox));
1210 debug_print (2, ("group %s\n", a->mailbox));
1218 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1221 LIST *tmp = UserHeader;
1226 mutt_extract_token (buf, s, 0);
1227 if (str_cmp ("*", buf->data) == 0)
1228 mutt_free_list (&UserHeader);
1233 l = str_len (buf->data);
1234 if (buf->data[l - 1] == ':')
1238 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1239 && tmp->data[l] == ':') {
1242 last->next = tmp->next;
1244 UserHeader = tmp->next;
1247 mutt_free_list (&ptr);
1256 while (MoreArgs (s));
1260 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1267 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1268 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1269 strfcpy (err->data, _("invalid header field"), err->dsize);
1272 keylen = p - buf->data + 1;
1275 for (tmp = UserHeader;; tmp = tmp->next) {
1276 /* see if there is already a field by this name */
1277 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1278 /* replace the old value */
1279 mem_free (&tmp->data);
1280 tmp->data = buf->data;
1281 memset (buf, 0, sizeof (BUFFER));
1287 tmp->next = mutt_new_list ();
1291 tmp = mutt_new_list ();
1294 tmp->data = buf->data;
1295 memset (buf, 0, sizeof (BUFFER));
1300 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1301 char* errbuf, size_t errlen) {
1304 if (str_ncmp ("reverse-", s, 8) == 0) {
1306 flags = SORT_REVERSE;
1309 if (str_ncmp ("last-", s, 5) == 0) {
1314 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1316 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1320 *((short*) dst->data) = i | flags;
1324 /* if additional data more == 1, we want to resolve synonyms */
1325 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1326 char buf[LONG_STRING];
1327 struct option_t* ptr = (struct option_t*) p;
1329 if (DTYPE (ptr->type) == DT_SYN) {
1332 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1334 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1336 mutt_option_value (ptr->option, buf, sizeof (buf));
1337 if (str_len (ptr->init) == 0 && buf && *buf)
1338 ptr->init = str_dup (buf);
1341 static struct option_t* add_option (const char* name, const char* init,
1342 short type, short dup) {
1343 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1345 debug_print (1, ("adding $%s\n", name));
1347 option->option = str_dup (name);
1348 option->type = type;
1350 option->init = dup ? str_dup (init) : (char*) init;
1354 /* creates new option_t* of type DT_USER for $user_ var */
1355 static struct option_t* add_user_option (const char* name) {
1356 return (add_option (name, NULL, DT_USER, 1));
1359 /* free()'s option_t* */
1360 static void del_option (void* p) {
1361 struct option_t* ptr = (struct option_t*) p;
1362 char* s = (char*) ptr->data;
1363 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1364 mem_free (&ptr->option);
1366 mem_free (&ptr->init);
1370 static int init_expand (char** dst, const char* src) {
1377 memset (&token, 0, sizeof (BUFFER));
1378 memset (&in, 0, sizeof (BUFFER));
1379 len = str_len (src) + 2;
1380 in.data = mem_malloc (len+1);
1381 snprintf (in.data, len, "\"%s\"", src);
1384 mutt_extract_token (&token, &in, 0);
1385 if (token.data && *token.data)
1386 *dst = str_dup (token.data);
1388 *dst = str_dup ("");
1389 mem_free (&in.data);
1390 mem_free (&token.data);
1392 *dst = str_dup ("");
1396 /* if additional data more == 1, we want to resolve synonyms */
1397 static void mutt_restore_default (const char* name, void* p,
1398 unsigned long more) {
1399 char errbuf[STRING];
1400 struct option_t* ptr = (struct option_t*) p;
1403 if (DTYPE (ptr->type) == DT_SYN) {
1406 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1410 if (FuncTable[DTYPE (ptr->type)].opt_from_string) {
1411 init_expand (&init, ptr->init);
1412 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, init, errbuf,
1413 sizeof (errbuf)) < 0) {
1415 fprintf (stderr, _("Invalid default setting found. Please report this "
1416 "error:\n\"%s\"\n"), errbuf);
1422 if (ptr->flags & R_INDEX)
1423 set_option (OPTFORCEREDRAWINDEX);
1424 if (ptr->flags & R_PAGER)
1425 set_option (OPTFORCEREDRAWPAGER);
1426 if (ptr->flags & R_RESORT_SUB)
1427 set_option (OPTSORTSUBTHREADS);
1428 if (ptr->flags & R_RESORT)
1429 set_option (OPTNEEDRESORT);
1430 if (ptr->flags & R_RESORT_INIT)
1431 set_option (OPTRESORTINIT);
1432 if (ptr->flags & R_TREE)
1433 set_option (OPTREDRAWTREE);
1436 /* check whether value for $dsn_return would be valid */
1437 static int check_dsn_return (const char* option, unsigned long p,
1438 char* errbuf, size_t errlen) {
1439 char* val = (char*) p;
1440 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1441 str_ncmp (val, "full", 4) != 0) {
1443 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1449 /* check whether value for $dsn_notify would be valid */
1450 static int check_dsn_notify (const char* option, unsigned long p,
1451 char* errbuf, size_t errlen) {
1452 list2_t* list = NULL;
1454 char* val = (char*) p;
1458 list = list_from_str (val, ",");
1459 if (list_empty (list))
1462 for (i = 0; i < list->length; i++)
1463 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1464 str_ncmp (list->data[i], "failure", 7) != 0 &&
1465 str_ncmp (list->data[i], "delay", 5) != 0 &&
1466 str_ncmp (list->data[i], "success", 7) != 0) {
1468 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1469 (char*) list->data[i], "dsn_notify");
1473 list_del (&list, (list_del_t*) _mem_free);
1477 static int check_num (const char* option, unsigned long p,
1478 char* errbuf, size_t errlen) {
1481 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1488 static int check_debug (const char* option, unsigned long p,
1489 char* errbuf, size_t errlen) {
1490 if ((int) p <= DEBUG_MAX_LEVEL &&
1491 (int) p >= DEBUG_MIN_LEVEL)
1495 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1500 static int check_history (const char* option, unsigned long p,
1501 char* errbuf, size_t errlen) {
1502 if (!check_num ("history", p, errbuf, errlen))
1504 mutt_init_history ();
1508 static int check_special (const char* name, unsigned long val,
1509 char* errbuf, size_t errlen) {
1512 for (i = 0; SpecialVars[i].name; i++) {
1513 if (str_cmp (SpecialVars[i].name, name) == 0) {
1514 return (SpecialVars[i].check (SpecialVars[i].name,
1515 val, errbuf, errlen));
1521 static const struct mapping_t* get_sortmap (struct option_t* option) {
1522 const struct mapping_t* map = NULL;
1524 switch (option->type & DT_SUBTYPE_MASK) {
1526 map = SortAliasMethods;
1528 case DT_SORT_BROWSER:
1529 map = SortBrowserMethods;
1532 if ((WithCrypto & APPLICATION_PGP))
1533 map = SortKeyMethods;
1536 map = SortAuxMethods;
1545 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1548 int query, unset, inv, reset, r = 0;
1549 struct option_t* option = NULL;
1551 while (MoreArgs (s)) {
1552 /* reset state variables */
1554 unset = data & M_SET_UNSET;
1555 inv = data & M_SET_INV;
1556 reset = data & M_SET_RESET;
1558 if (*s->dptr == '?') {
1562 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1566 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1570 else if (*s->dptr == '&') {
1575 /* get the variable name */
1576 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1578 /* resolve synonyms */
1579 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1580 DTYPE (option->type == DT_SYN)) {
1581 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1582 syn_add (newopt, option);
1586 /* see if we need to add $user_ var */
1587 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1588 /* there's no option named like this yet so only add one
1589 * if the action isn't any of: reset, unset, query */
1590 if (!(reset || unset || query || *s->dptr != '=')) {
1591 debug_print (1, ("adding user option '%s'\n", tmp->data));
1592 option = add_user_option (tmp->data);
1593 hash_insert (ConfigOptions, option->option, option, 0);
1597 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1598 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1604 if (query || unset || inv) {
1605 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1609 if (s && *s->dptr == '=') {
1610 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1614 if (!str_cmp ("all", tmp->data)) {
1615 hash_map (ConfigOptions, mutt_restore_default, 1);
1618 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1619 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1623 mutt_restore_default (NULL, option, 1);
1625 else if (DTYPE (option->type) == DT_BOOL) {
1626 /* XXX this currently ignores the function table
1627 * as we don't get invert and stuff into it */
1628 if (s && *s->dptr == '=') {
1629 if (unset || inv || query) {
1630 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1635 mutt_extract_token (tmp, s, 0);
1636 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1638 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1641 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1647 bool_to_string (err->data, err->dsize, option);
1652 unset_option (option->data);
1654 toggle_option (option->data);
1656 set_option (option->data);
1658 else if (DTYPE (option->type) == DT_STR ||
1659 DTYPE (option->type) == DT_PATH ||
1660 DTYPE (option->type) == DT_ADDR ||
1661 DTYPE (option->type) == DT_MAGIC ||
1662 DTYPE (option->type) == DT_NUM ||
1663 DTYPE (option->type) == DT_SORT ||
1664 DTYPE (option->type) == DT_RX ||
1665 DTYPE (option->type) == DT_USER ||
1666 DTYPE (option->type) == DT_SYS) {
1668 /* XXX maybe we need to get unset into handlers? */
1669 if (DTYPE (option->type) == DT_STR ||
1670 DTYPE (option->type) == DT_PATH ||
1671 DTYPE (option->type) == DT_ADDR ||
1672 DTYPE (option->type) == DT_USER ||
1673 DTYPE (option->type) == DT_SYS) {
1675 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1676 snprintf (err->data, err->dsize, _("$%s is read-only"),
1680 } else if (DTYPE (option->type) == DT_ADDR)
1681 rfc822_free_address ((ADDRESS **) option->data);
1682 else if (DTYPE (option->type) == DT_USER)
1683 /* to unset $user_ means remove */
1684 hash_delete (ConfigOptions, option->option,
1685 option, del_option);
1687 mem_free ((void *) option->data);
1692 if (query || *s->dptr != '=') {
1693 FuncTable[DTYPE (option->type)].opt_to_string
1694 (err->data, err->dsize, option);
1698 /* the $muttng_ variables are read-only */
1699 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1700 snprintf (err->data, err->dsize, _("$%s is read-only"),
1706 mutt_extract_token (tmp, s, 0);
1707 if (!FuncTable[DTYPE (option->type)].opt_from_string
1708 (option, tmp->data, err->data, err->dsize))
1712 else if (DTYPE (option->type) == DT_QUAD) {
1715 quad_to_string (err->data, err->dsize, option);
1719 if (*s->dptr == '=') {
1721 mutt_extract_token (tmp, s, 0);
1722 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1723 set_quadoption (option->data, M_YES);
1724 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1725 set_quadoption (option->data, M_NO);
1726 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1727 set_quadoption (option->data, M_ASKYES);
1728 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1729 set_quadoption (option->data, M_ASKNO);
1731 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1732 tmp->data, option->option);
1739 toggle_quadoption (option->data);
1741 set_quadoption (option->data, M_NO);
1743 set_quadoption (option->data, M_YES);
1747 snprintf (err->data, err->dsize, _("%s: unknown type"),
1753 if (option->flags & R_INDEX)
1754 set_option (OPTFORCEREDRAWINDEX);
1755 if (option->flags & R_PAGER)
1756 set_option (OPTFORCEREDRAWPAGER);
1757 if (option->flags & R_RESORT_SUB)
1758 set_option (OPTSORTSUBTHREADS);
1759 if (option->flags & R_RESORT)
1760 set_option (OPTNEEDRESORT);
1761 if (option->flags & R_RESORT_INIT)
1762 set_option (OPTRESORTINIT);
1763 if (option->flags & R_TREE)
1764 set_option (OPTREDRAWTREE);
1771 /* reads the specified initialization file. returns -1 if errors were found
1772 so that we can pause to let the user know... */
1773 static int source_rc (const char *rcfile, BUFFER * err)
1776 int line = 0, rc = 0, conv = 0;
1778 char *linebuf = NULL;
1779 char *currentline = NULL;
1783 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1785 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1786 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1790 memset (&token, 0, sizeof (token));
1791 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1792 conv = ConfigCharset && (*ConfigCharset) && Charset;
1794 currentline = str_dup (linebuf);
1797 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1800 currentline = linebuf;
1805 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1806 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1807 if (--rc < -MAXERRS) {
1809 mem_free (¤tline);
1818 mem_free (¤tline);
1820 mem_free (&token.data);
1821 mem_free (&linebuf);
1824 mutt_wait_filter (pid);
1826 /* the muttrc source keyword */
1827 snprintf (err->data, err->dsize,
1828 rc >= -MAXERRS ? _("source: errors in %s")
1829 : _("source: reading aborted due too many errors in %s"),
1838 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1841 char path[_POSIX_PATH_MAX];
1845 if (mutt_extract_token (tmp, s, 0) != 0) {
1846 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1850 strfcpy (path, tmp->data, sizeof (path));
1851 mutt_expand_path (path, sizeof (path));
1853 rc += source_rc (path, err);
1855 while (MoreArgs (s));
1857 return ((rc < 0) ? -1 : 0);
1860 /* line command to execute
1862 token scratch buffer to be used by parser. caller should free
1863 token->data when finished. the reason for this variable is
1864 to avoid having to allocate and deallocate a lot of memory
1865 if we are parsing many lines. the caller can pass in the
1866 memory to use, which avoids having to create new space for
1867 every call to this function.
1869 err where to write error messages */
1870 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1875 memset (&expn, 0, sizeof (expn));
1876 expn.data = expn.dptr = line;
1877 expn.dsize = str_len (line);
1881 debug_print (1, ("expand '%s'\n", line));
1884 while (*expn.dptr) {
1885 if (*expn.dptr == '#')
1886 break; /* rest of line is a comment */
1887 if (*expn.dptr == ';') {
1891 mutt_extract_token (token, &expn, 0);
1892 for (i = 0; Commands[i].name; i++) {
1893 if (!str_cmp (token->data, Commands[i].name)) {
1894 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1899 if (!Commands[i].name) {
1900 snprintf (err->data, err->dsize, _("%s: unknown command"),
1901 NONULL (token->data));
1908 mem_free (&expn.data);
1913 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1914 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1915 /* initial string that starts completion. No telling how much crap
1916 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1917 char User_typed[LONG_STRING] = { 0 };
1919 int Num_matched = 0; /* Number of matches for completion */
1920 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1921 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1923 /* helper function for completion. Changes the dest buffer if
1924 necessary/possible to aid completion.
1925 dest == completion result gets here.
1926 src == candidate for completion.
1927 try == user entered data for completion.
1928 len == length of dest buffer.
1930 static void candidate (char *dest, char *try, char *src, int len)
1934 if (strstr (src, try) == src) {
1935 Matches[Num_matched++] = src;
1937 strfcpy (dest, src, len);
1939 for (l = 0; src[l] && src[l] == dest[l]; l++);
1945 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1949 int spaces; /* keep track of the number of leading spaces on the line */
1952 spaces = buffer - pt;
1954 pt = buffer + pos - spaces;
1955 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1958 if (pt == buffer) { /* complete cmd */
1959 /* first TAB. Collect all the matches */
1962 strfcpy (User_typed, pt, sizeof (User_typed));
1963 memset (Matches, 0, sizeof (Matches));
1964 memset (Completed, 0, sizeof (Completed));
1965 for (num = 0; Commands[num].name; num++)
1966 candidate (Completed, User_typed, Commands[num].name,
1967 sizeof (Completed));
1968 Matches[Num_matched++] = User_typed;
1970 /* All matches are stored. Longest non-ambiguous string is ""
1971 * i.e. dont change 'buffer'. Fake successful return this time */
1972 if (User_typed[0] == 0)
1976 if (Completed[0] == 0 && User_typed[0])
1979 /* Num_matched will _always_ be atleast 1 since the initial
1980 * user-typed string is always stored */
1981 if (numtabs == 1 && Num_matched == 2)
1982 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1983 else if (numtabs > 1 && Num_matched > 2)
1984 /* cycle thru all the matches */
1985 snprintf (Completed, sizeof (Completed), "%s",
1986 Matches[(numtabs - 2) % Num_matched]);
1988 /* return the completed command */
1989 strncpy (buffer, Completed, len - spaces);
1991 else if (!str_ncmp (buffer, "set", 3)
1992 || !str_ncmp (buffer, "unset", 5)
1993 || !str_ncmp (buffer, "reset", 5)
1994 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1995 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1998 /* loop through all the possible prefixes (no, inv, ...) */
1999 if (!str_ncmp (buffer, "set", 3)) {
2000 for (num = 0; prefixes[num]; num++) {
2001 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
2002 pt += str_len (prefixes[num]);
2008 /* first TAB. Collect all the matches */
2011 strfcpy (User_typed, pt, sizeof (User_typed));
2012 memset (Matches, 0, sizeof (Matches));
2013 memset (Completed, 0, sizeof (Completed));
2014 for (num = 0; MuttVars[num].option; num++)
2015 candidate (Completed, User_typed, MuttVars[num].option,
2016 sizeof (Completed));
2017 Matches[Num_matched++] = User_typed;
2019 /* All matches are stored. Longest non-ambiguous string is ""
2020 * i.e. dont change 'buffer'. Fake successful return this time */
2021 if (User_typed[0] == 0)
2025 if (Completed[0] == 0 && User_typed[0])
2028 /* Num_matched will _always_ be atleast 1 since the initial
2029 * user-typed string is always stored */
2030 if (numtabs == 1 && Num_matched == 2)
2031 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2032 else if (numtabs > 1 && Num_matched > 2)
2033 /* cycle thru all the matches */
2034 snprintf (Completed, sizeof (Completed), "%s",
2035 Matches[(numtabs - 2) % Num_matched]);
2037 strncpy (pt, Completed, buffer + len - pt - spaces);
2039 else if (!str_ncmp (buffer, "exec", 4)) {
2040 struct binding_t *menu = km_get_table (CurrentMenu);
2042 if (!menu && CurrentMenu != MENU_PAGER)
2046 /* first TAB. Collect all the matches */
2049 strfcpy (User_typed, pt, sizeof (User_typed));
2050 memset (Matches, 0, sizeof (Matches));
2051 memset (Completed, 0, sizeof (Completed));
2052 for (num = 0; menu[num].name; num++)
2053 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2054 /* try the generic menu */
2055 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2057 for (num = 0; menu[num].name; num++)
2058 candidate (Completed, User_typed, menu[num].name,
2059 sizeof (Completed));
2061 Matches[Num_matched++] = User_typed;
2063 /* All matches are stored. Longest non-ambiguous string is ""
2064 * i.e. dont change 'buffer'. Fake successful return this time */
2065 if (User_typed[0] == 0)
2069 if (Completed[0] == 0 && User_typed[0])
2072 /* Num_matched will _always_ be atleast 1 since the initial
2073 * user-typed string is always stored */
2074 if (numtabs == 1 && Num_matched == 2)
2075 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2076 else if (numtabs > 1 && Num_matched > 2)
2077 /* cycle thru all the matches */
2078 snprintf (Completed, sizeof (Completed), "%s",
2079 Matches[(numtabs - 2) % Num_matched]);
2081 strncpy (pt, Completed, buffer + len - pt - spaces);
2089 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2091 char var[STRING], *pt = buffer;
2093 struct option_t* option = NULL;
2099 spaces = buffer - pt;
2101 pt = buffer + pos - spaces;
2102 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2104 pt++; /* move past the space */
2105 if (*pt == '=') /* abort if no var before the '=' */
2108 if (str_ncmp (buffer, "set", 3) == 0) {
2109 strfcpy (var, pt, sizeof (var));
2110 /* ignore the trailing '=' when comparing */
2111 var[str_len (var) - 1] = 0;
2112 if (!(option = hash_find (ConfigOptions, var)))
2113 return 0; /* no such variable. */
2115 char tmp[LONG_STRING], tmp2[LONG_STRING];
2117 size_t dlen = buffer + len - pt - spaces;
2118 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2122 if ((DTYPE (option->type) == DT_STR) ||
2123 (DTYPE (option->type) == DT_PATH) ||
2124 (DTYPE (option->type) == DT_RX)) {
2125 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2126 if (DTYPE (option->type) == DT_PATH)
2127 mutt_pretty_mailbox (tmp);
2129 else if (DTYPE (option->type) == DT_ADDR) {
2130 rfc822_write_address (tmp, sizeof (tmp),
2131 *((ADDRESS **) option->data), 0);
2133 else if (DTYPE (option->type) == DT_QUAD)
2134 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2135 else if (DTYPE (option->type) == DT_NUM)
2136 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2137 else if (DTYPE (option->type) == DT_SORT) {
2138 const struct mapping_t *map;
2141 switch (option->type & DT_SUBTYPE_MASK) {
2143 map = SortAliasMethods;
2145 case DT_SORT_BROWSER:
2146 map = SortBrowserMethods;
2149 if ((WithCrypto & APPLICATION_PGP))
2150 map = SortKeyMethods;
2159 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2161 snprintf (tmp, sizeof (tmp), "%s%s%s",
2162 (*((short *) option->data) & SORT_REVERSE) ?
2164 (*((short *) option->data) & SORT_LAST) ? "last-" :
2167 else if (DTYPE (option->type) == DT_MAGIC) {
2169 switch (DefaultMagic) {
2185 strfcpy (tmp, p, sizeof (tmp));
2187 else if (DTYPE (option->type) == DT_BOOL)
2188 strfcpy (tmp, option (option->data) ? "yes" : "no",
2193 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2194 if (*s == '\\' || *s == '"')
2200 strfcpy (tmp, pt, sizeof (tmp));
2201 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2209 /* Implement the -Q command line flag */
2210 int mutt_query_variables (LIST * queries)
2214 char errbuff[STRING];
2215 char command[STRING];
2219 memset (&err, 0, sizeof (err));
2220 memset (&token, 0, sizeof (token));
2223 err.dsize = sizeof (errbuff);
2225 for (p = queries; p; p = p->next) {
2226 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2227 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2228 fprintf (stderr, "%s\n", err.data);
2229 mem_free (&token.data);
2232 printf ("%s\n", err.data);
2235 mem_free (&token.data);
2239 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2243 for (i = 0; map[i].name; i++)
2244 if (map[i].value == val)
2245 return (map[i].name);
2249 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2253 for (i = 0; map[i].name; i++)
2254 if (ascii_strcasecmp (map[i].name, name) == 0)
2255 return (map[i].value);
2259 static int mutt_execute_commands (LIST * p)
2262 char errstr[SHORT_STRING];
2264 memset (&err, 0, sizeof (err));
2266 err.dsize = sizeof (errstr);
2267 memset (&token, 0, sizeof (token));
2268 for (; p; p = p->next) {
2269 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2270 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2271 mem_free (&token.data);
2275 mem_free (&token.data);
2279 void mutt_init (int skip_sys_rc, LIST * commands)
2282 struct utsname utsname;
2283 char *p, buffer[STRING], error[STRING];
2284 int i, default_rc = 0, need_pause = 0;
2287 memset (&err, 0, sizeof (err));
2289 err.dsize = sizeof (error);
2291 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2292 * to have some room for $user_ vars */
2293 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2294 for (i = 0; MuttVars[i].option; i++) {
2295 if (DTYPE (MuttVars[i].type) != DT_SYS)
2296 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2298 hash_insert (ConfigOptions, MuttVars[i].option,
2299 add_option (MuttVars[i].option, MuttVars[i].init,
2304 * XXX - use something even more difficult to predict?
2306 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2307 "\033]9;%ld\a", (long) time (NULL));
2309 /* on one of the systems I use, getcwd() does not return the same prefix
2310 as is listed in the passwd file */
2311 if ((p = getenv ("HOME")))
2312 Homedir = str_dup (p);
2314 /* Get some information about the user */
2315 if ((pw = getpwuid (getuid ()))) {
2318 Username = str_dup (pw->pw_name);
2320 Homedir = str_dup (pw->pw_dir);
2322 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2323 Shell = str_dup (pw->pw_shell);
2329 fputs (_("unable to determine home directory"), stderr);
2332 if ((p = getenv ("USER")))
2333 Username = str_dup (p);
2336 fputs (_("unable to determine username"), stderr);
2339 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2342 debug_start(Homedir);
2344 /* And about the host... */
2346 /* some systems report the FQDN instead of just the hostname */
2347 if ((p = strchr (utsname.nodename, '.'))) {
2348 Hostname = str_substrdup (utsname.nodename, p);
2350 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2353 Hostname = str_dup (utsname.nodename);
2356 #define DOMAIN buffer
2357 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2358 Fqdn = str_dup ("@");
2361 if (*DOMAIN != '@') {
2362 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2363 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2366 Fqdn = str_dup (NONULL (Hostname));
2373 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2375 fgets (buffer, sizeof (buffer), f);
2376 p = (char*) &buffer;
2379 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2383 NewsServer = str_dup (p);
2387 if ((p = getenv ("NNTPSERVER")))
2388 NewsServer = str_dup (p);
2391 if ((p = getenv ("MAIL")))
2392 Spoolfile = str_dup (p);
2393 else if ((p = getenv ("MAILDIR")))
2394 Spoolfile = str_dup (p);
2397 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2399 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2401 Spoolfile = str_dup (buffer);
2404 if ((p = getenv ("MAILCAPS")))
2405 MailcapPath = str_dup (p);
2407 /* Default search path from RFC1524 */
2409 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2410 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2413 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2415 p = getenv ("VISUAL");
2417 p = getenv ("EDITOR");
2421 Editor = str_dup (p);
2422 Visual = str_dup (p);
2424 if ((p = getenv ("REPLYTO")) != NULL) {
2427 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2429 memset (&buf, 0, sizeof (buf));
2430 buf.data = buf.dptr = buffer;
2431 buf.dsize = str_len (buffer);
2433 memset (&token, 0, sizeof (token));
2434 parse_my_hdr (&token, &buf, 0, &err);
2435 mem_free (&token.data);
2438 if ((p = getenv ("EMAIL")) != NULL)
2439 From = rfc822_parse_adrlist (NULL, p);
2441 mutt_set_langinfo_charset ();
2442 mutt_set_charset (Charset);
2445 /* Set standard defaults */
2446 hash_map (ConfigOptions, mutt_set_default, 0);
2447 hash_map (ConfigOptions, mutt_restore_default, 0);
2449 CurrentMenu = MENU_MAIN;
2452 #ifndef LOCALES_HACK
2453 /* Do we have a locale definition? */
2454 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2455 ((p = getenv ("LANG")) != NULL && p[0]) ||
2456 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2457 set_option (OPTLOCALES);
2461 /* Unset suspend by default if we're the session leader */
2462 if (getsid (0) == getpid ())
2463 unset_option (OPTSUSPEND);
2466 mutt_init_history ();
2475 * When changing the code which looks for a configuration file,
2476 * please also change the corresponding code in muttbug.sh.in.
2486 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2488 if (access (buffer, F_OK) == -1)
2490 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2491 if (access (buffer, F_OK) == -1)
2493 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2494 NONULL (Homedir), MUTT_VERSION);
2495 if (access (buffer, F_OK) == -1)
2497 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2501 Muttrc = str_dup (buffer);
2504 strfcpy (buffer, Muttrc, sizeof (buffer));
2506 mutt_expand_path (buffer, sizeof (buffer));
2507 Muttrc = str_dup (buffer);
2509 mem_free (&AliasFile);
2510 AliasFile = str_dup (NONULL (Muttrc));
2512 /* Process the global rc file if it exists and the user hasn't explicity
2513 requested not to via "-n". */
2515 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2517 if (access (buffer, F_OK) == -1)
2518 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2519 if (access (buffer, F_OK) == -1)
2520 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2522 if (access (buffer, F_OK) == -1)
2523 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2524 if (access (buffer, F_OK) != -1) {
2525 if (source_rc (buffer, &err) != 0) {
2526 fputs (err.data, stderr);
2527 fputc ('\n', stderr);
2533 /* Read the user's initialization file. */
2534 if (access (Muttrc, F_OK) != -1) {
2535 if (!option (OPTNOCURSES))
2537 if (source_rc (Muttrc, &err) != 0) {
2538 fputs (err.data, stderr);
2539 fputc ('\n', stderr);
2543 else if (!default_rc) {
2544 /* file specified by -F does not exist */
2545 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2546 mutt_endwin (buffer);
2550 if (mutt_execute_commands (commands) != 0)
2553 /* warn about synonym variables */
2554 if (!list_empty(Synonyms)) {
2556 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2557 for (i = 0; i < Synonyms->length; i++) {
2558 struct option_t* newopt = NULL, *oldopt = NULL;
2559 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2560 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2561 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2562 oldopt ? NONULL (oldopt->option) : "",
2563 newopt ? NONULL (newopt->option) : "",
2564 NONULL(((syn_t*) Synonyms->data[i])->f),
2565 ((syn_t*) Synonyms->data[i])->l);
2567 fprintf (stderr, _("Warning: synonym variables are scheduled"
2568 " for removal.\n"));
2569 list_del (&Synonyms, syn_del);
2573 if (need_pause && !option (OPTNOCURSES)) {
2574 if (mutt_any_key_to_continue (NULL) == -1)
2579 set_option (OPTWEED); /* turn weeding on by default */
2583 int mutt_get_hook_type (const char *name)
2585 struct command_t *c;
2587 for (c = Commands; c->name; c++)
2588 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2593 /* compare two option_t*'s for sorting -t/-T output */
2594 static int opt_cmp (const void* a, const void* b) {
2595 return (str_cmp ((*(struct option_t**) a)->option,
2596 (*(struct option_t**) b)->option));
2599 /* callback for hash_map() to put all non-synonym vars into list */
2600 static void opt_sel_full (const char* key, void* data,
2601 unsigned long more) {
2602 list2_t** l = (list2_t**) more;
2603 struct option_t* option = (struct option_t*) data;
2605 if (DTYPE (option->type) == DT_SYN)
2607 list_push_back (l, option);
2610 /* callback for hash_map() to put all changed non-synonym vars into list */
2611 static void opt_sel_diff (const char* key, void* data,
2612 unsigned long more) {
2613 list2_t** l = (list2_t**) more;
2614 struct option_t* option = (struct option_t*) data;
2615 char buf[LONG_STRING];
2617 if (DTYPE (option->type) == DT_SYN)
2620 mutt_option_value (option->option, buf, sizeof (buf));
2621 if (str_cmp (buf, option->init) != 0)
2622 list_push_back (l, option);
2625 /* dump out the value of all the variables we have */
2626 int mutt_dump_variables (int full) {
2628 char outbuf[STRING];
2629 list2_t* tmp = NULL;
2630 struct option_t* option = NULL;
2632 /* get all non-synonyms into list... */
2633 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2634 (unsigned long) &tmp);
2636 if (!list_empty(tmp)) {
2637 /* ...and dump list sorted */
2638 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2639 for (i = 0; i < tmp->length; i++) {
2640 option = (struct option_t*) tmp->data[i];
2641 FuncTable[DTYPE (option->type)].opt_to_string
2642 (outbuf, sizeof (outbuf), option);
2643 printf ("%s\n", outbuf);
2646 list_del (&tmp, NULL);