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 snprintf (dst, dstlen, "%s=\"%s\"", option->option, option->init);
263 static int path_from_string (struct option_t* dst, const char* val,
264 char* errbuf, size_t errlen) {
265 char path[_POSIX_PATH_MAX];
271 mem_free ((char**) dst->data);
276 strfcpy (path, val, sizeof (path));
277 mutt_expand_path (path, sizeof (path));
278 str_replace ((char **) dst->data, path);
282 static int str_from_string (struct option_t* dst, const char* val,
283 char* errbuf, size_t errlen) {
287 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
290 str_replace ((char**) dst->data, val);
294 static int user_from_string (struct option_t* dst, const char* val,
295 char* errbuf, size_t errlen) {
296 /* if dst == NULL, we may get here in case the user did unset it,
297 * see parse_set() where item is free()'d before coming here; so
298 * just silently ignore it */
301 if (str_len ((char*) dst->data) == 0)
302 dst->data = (unsigned long) str_dup (val);
304 char* s = (char*) dst->data;
305 str_replace (&s, val);
307 if (str_len (dst->init) == 0)
308 dst->init = str_dup ((char*) dst->data);
312 static void quad_to_string (char* dst, size_t dstlen,
313 struct option_t* option) {
314 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
315 snprintf (dst, dstlen, "%s=%s", option->option,
316 vals[quadoption (option->data)]);
319 static int quad_from_string (struct option_t* dst, const char* val,
320 char* errbuf, size_t errlen) {
325 if (ascii_strncasecmp (val, "yes", 3) == 0)
327 else if (ascii_strncasecmp (val, "no", 2) == 0)
329 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
331 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
337 set_quadoption (dst->data, flag);
341 static void sort_to_string (char* dst, size_t dstlen,
342 struct option_t* option) {
343 const struct mapping_t *map = get_sortmap (option);
347 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
351 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
354 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
355 (*((short *) option->data) & SORT_REVERSE) ?
357 (*((short *) option->data) & SORT_LAST) ? "last-" :
361 static int sort_from_string (struct option_t* dst, const char* val,
362 char* errbuf, size_t errlen) {
363 const struct mapping_t *map = NULL;
364 if (!(map = get_sortmap (dst))) {
366 snprintf (errbuf, errlen, _("%s: Unknown type."),
370 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
375 static void rx_to_string (char* dst, size_t dstlen,
376 struct option_t* option) {
377 rx_t* p = (rx_t*) option->data;
378 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
379 NONULL (p->pattern));
382 static int rx_from_string (struct option_t* dst, const char* val,
383 char* errbuf, size_t errlen) {
386 int flags = 0, e = 0, not = 0;
392 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
394 snprintf (errbuf, errlen,
395 "Operation not permitted when in attach-message mode.");
399 if (!((rx_t*) dst->data))
400 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
402 p = (rx_t*) dst->data;
404 /* something to do? */
405 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
408 if (str_cmp (dst->option, "mask") != 0)
409 flags |= mutt_which_case (val);
412 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
417 rx = mem_malloc (sizeof (regex_t));
419 if ((e = REGCOMP (rx, s, flags)) != 0) {
420 regerror (e, rx, errbuf, errlen);
430 str_replace (&p->pattern, val);
434 if (str_cmp (dst->option, "reply_regexp") == 0)
435 mutt_adjust_all_subjects ();
440 static void magic_to_string (char* dst, size_t dstlen,
441 struct option_t* option) {
442 const char* s = NULL;
443 switch (option->data) {
444 case M_MBOX: s = "mbox"; break;
445 case M_MMDF: s = "MMDF"; break;
446 case M_MH: s = "MH"; break;
447 case M_MAILDIR: s = "Maildir"; break;
448 default: s = "unknown"; break;
450 snprintf (dst, dstlen, "%s=%s", option->option, s);
453 static int magic_from_string (struct option_t* dst, const char* val,
454 char* errbuf, size_t errlen) {
457 if (!dst || !val || !*val)
459 if (ascii_strncasecmp (val, "mbox", 4) == 0)
461 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
463 else if (ascii_strncasecmp (val, "mh", 2) == 0)
465 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
471 *((short*) dst->data) = flag;
476 static void addr_to_string (char* dst, size_t dstlen,
477 struct option_t* option) {
480 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
481 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
484 static int addr_from_string (struct option_t* dst, const char* val,
485 char* errbuf, size_t errlen) {
486 if (!dst || !val || !*val)
488 rfc822_free_address ((ADDRESS**) dst->data);
489 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
493 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
494 struct option_t* option = NULL;
495 char* tmp = NULL, *t = NULL;
498 if (!(option = hash_find (ConfigOptions, val))) {
499 debug_print (1, ("var '%s' not found\n", val));
503 tmp = mem_malloc (dstlen+1);
504 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
506 /* as we get things of type $var=value and don't want to bloat the
507 * above "just" for expansion, we do the stripping here */
508 debug_print (1, ("orig == '%s'\n", tmp));
509 t = strchr (tmp, '=');
513 if (t[l-1] == '"' && *t == '"') {
518 memcpy (dst, t, l+1);
520 debug_print (1, ("stripped == '%s'\n", dst));
525 /* for synonym warning reports: adds synonym to end of list */
526 static void syn_add (struct option_t* n, struct option_t* o) {
527 syn_t* tmp = mem_malloc (sizeof (syn_t));
528 tmp->f = str_dup (CurRCFile);
532 list_push_back (&Synonyms, tmp);
535 /* for synonym warning reports: free single item (for list_del()) */
536 static void syn_del (void** p) {
537 mem_free(&(*(syn_t**) p)->f);
541 void toggle_quadoption (int opt)
544 int b = (opt % 4) * 2;
546 QuadOptions[n] ^= (1 << b);
549 void set_quadoption (int opt, int flag)
552 int b = (opt % 4) * 2;
554 QuadOptions[n] &= ~(0x3 << b);
555 QuadOptions[n] |= (flag & 0x3) << b;
558 int quadoption (int opt)
561 int b = (opt % 4) * 2;
563 return (QuadOptions[n] >> b) & 0x3;
566 int query_quadoption (int opt, const char *prompt)
568 int v = quadoption (opt);
576 v = mutt_yesorno (prompt, (v == M_ASKYES));
577 CLEARLINE (LINES - 1);
584 static void add_to_list (LIST ** list, const char *str)
586 LIST *t, *last = NULL;
588 /* don't add a NULL or empty string to the list */
589 if (!str || *str == '\0')
592 /* check to make sure the item is not already on this list */
593 for (last = *list; last; last = last->next) {
594 if (ascii_strcasecmp (str, last->data) == 0) {
595 /* already on the list, so just ignore it */
603 if (!*list || last) {
604 t = (LIST *) mem_calloc (1, sizeof (LIST));
605 t->data = str_dup (str);
615 static int add_to_rx_list (list2_t** list, const char *s, int flags,
624 if (!(rx = rx_compile (s, flags))) {
625 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
629 i = rx_lookup ((*list), rx->pattern);
633 list_push_back (list, rx);
637 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
638 const char *templ, BUFFER * err)
640 SPAM_LIST *t = NULL, *last = NULL;
645 if (!pat || !*pat || !templ)
648 if (!(rx = rx_compile (pat, REG_ICASE))) {
649 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
653 /* check to make sure the item is not already on this list */
654 for (last = *list; last; last = last->next) {
655 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
656 /* Already on the list. Formerly we just skipped this case, but
657 * now we're supporting removals, which means we're supporting
658 * re-adds conceptually. So we probably want this to imply a
659 * removal, then do an add. We can achieve the removal by freeing
660 * the template, and leaving t pointed at the current item.
663 mem_free(t->template);
670 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
671 * update. Otherwise we want to make a new one to link at the list's end.
674 t = mutt_new_spam_list ();
682 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
683 t->template = str_dup (templ);
685 /* Find highest match number in template string */
687 for (p = templ; *p;) {
692 while (*p && isdigit ((int) *p))
698 t->nmatch++; /* match 0 is always the whole expr */
703 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
705 SPAM_LIST *spam, *prev;
708 /* Being first is a special case. */
712 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
715 mem_free(&spam->template);
721 for (spam = prev->next; spam;) {
722 if (!str_cmp (spam->rx->pattern, pat)) {
723 prev->next = spam->next;
725 mem_free(spam->template);
738 static void remove_from_list (LIST ** l, const char *str)
740 LIST *p, *last = NULL;
742 if (str_cmp ("*", str) == 0)
743 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
748 if (ascii_strcasecmp (str, p->data) == 0) {
751 last->next = p->next;
764 static int remove_from_rx_list (list2_t** l, const char *str)
768 if (str_cmp ("*", str) == 0) {
769 list_del (l, (list_del_t*) rx_free);
773 i = rx_lookup ((*l), str);
775 rx_t* r = list_pop_idx ((*l), i);
783 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
788 struct option_t* option = NULL;
790 memset (&token, 0, sizeof (token));
791 mutt_extract_token (tmp, s, 0);
793 /* is the item defined as a variable or a function? */
794 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL)
797 for (i = 0; !res && i < MENU_MAX; i++) {
798 struct binding_t *b = km_get_table (Menus[i].value);
803 for (j = 0; b[j].name; j++)
804 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
805 && (str_len (b[j].name) == str_len (tmp->data))) {
811 /* check for feature_* */
812 if (!res && ascii_strncasecmp (tmp->data, "feature_", 8) == 0 &&
813 (j = str_len (tmp->data)) > 8) {
815 while (Features[i]) {
816 if (str_len (Features[i]) == j-8 &&
817 ascii_strncasecmp (Features[i], tmp->data+8, j-8) == 0) {
827 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
829 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
833 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
836 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
837 mutt_error ("Error: %s", err->data);
838 mem_free (&token.data);
841 mem_free (&token.data);
846 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
850 mutt_extract_token (buf, s, 0);
852 /* don't add "*" to the unignore list */
853 if (strcmp (buf->data, "*"))
854 add_to_list (&UnIgnore, buf->data);
856 remove_from_list (&Ignore, buf->data);
858 while (MoreArgs (s));
863 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
867 mutt_extract_token (buf, s, 0);
868 remove_from_list (&UnIgnore, buf->data);
869 add_to_list (&Ignore, buf->data);
871 while (MoreArgs (s));
876 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
880 mutt_extract_token (buf, s, 0);
881 add_to_list ((LIST **) data, buf->data);
883 while (MoreArgs (s));
888 static void _alternates_clean (void)
892 if (Context && Context->msgcount) {
893 for (i = 0; i < Context->msgcount; i++)
894 Context->hdrs[i]->recip_valid = 0;
898 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
901 _alternates_clean ();
903 mutt_extract_token (buf, s, 0);
904 remove_from_rx_list (&UnAlternates, buf->data);
906 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
909 while (MoreArgs (s));
914 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
917 _alternates_clean ();
919 mutt_extract_token (buf, s, 0);
920 remove_from_rx_list (&Alternates, buf->data);
922 if (str_cmp (buf->data, "*") &&
923 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
927 while (MoreArgs (s));
932 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
937 memset (&templ, 0, sizeof (templ));
939 /* Insist on at least one parameter */
942 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
944 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
948 /* Extract the first token, a regexp */
949 mutt_extract_token (buf, s, 0);
951 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
952 if (data == M_SPAM) {
953 /* If there's a second parameter, it's a template for the spam tag. */
955 mutt_extract_token (&templ, s, 0);
957 /* Add to the spam list. */
958 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
959 mem_free (&templ.data);
962 mem_free (&templ.data);
965 /* If not, try to remove from the nospam list. */
967 remove_from_rx_list (&NoSpamList, buf->data);
973 /* M_NOSPAM is for nospam commands. */
974 else if (data == M_NOSPAM) {
975 /* nospam only ever has one parameter. */
977 /* "*" is a special case. */
978 if (!str_cmp (buf->data, "*")) {
979 mutt_free_spam_list (&SpamList);
980 list_del (&NoSpamList, (list_del_t*) rx_free);
984 /* If it's on the spam list, just remove it. */
985 if (remove_from_spam_list (&SpamList, buf->data) != 0)
988 /* Otherwise, add it to the nospam list. */
989 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
995 /* This should not happen. */
996 strfcpy (err->data, "This is no good at all.", err->dsize);
1000 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
1004 mutt_extract_token (buf, s, 0);
1006 * Check for deletion of entire list
1008 if (str_cmp (buf->data, "*") == 0) {
1009 mutt_free_list ((LIST **) data);
1012 remove_from_list ((LIST **) data, buf->data);
1014 while (MoreArgs (s));
1019 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1023 mutt_extract_token (buf, s, 0);
1024 remove_from_rx_list (&UnMailLists, buf->data);
1026 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1029 while (MoreArgs (s));
1034 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1038 mutt_extract_token (buf, s, 0);
1039 remove_from_rx_list (&SubscribedLists, buf->data);
1040 remove_from_rx_list (&MailLists, buf->data);
1042 if (str_cmp (buf->data, "*") &&
1043 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1046 while (MoreArgs (s));
1051 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1055 mutt_extract_token (buf, s, 0);
1056 remove_from_rx_list (&UnMailLists, buf->data);
1057 remove_from_rx_list (&UnSubscribedLists, buf->data);
1059 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1061 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1064 while (MoreArgs (s));
1069 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1073 mutt_extract_token (buf, s, 0);
1074 remove_from_rx_list (&SubscribedLists, buf->data);
1076 if (str_cmp (buf->data, "*") &&
1077 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1080 while (MoreArgs (s));
1085 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1088 ALIAS *tmp, *last = NULL;
1091 mutt_extract_token (buf, s, 0);
1093 if (str_cmp ("*", buf->data) == 0) {
1094 if (CurrentMenu == MENU_ALIAS) {
1095 for (tmp = Aliases; tmp; tmp = tmp->next)
1097 set_option (OPTFORCEREDRAWINDEX);
1100 mutt_free_alias (&Aliases);
1104 for (tmp = Aliases; tmp; tmp = tmp->next) {
1105 if (str_casecmp (buf->data, tmp->name) == 0) {
1106 if (CurrentMenu == MENU_ALIAS) {
1108 set_option (OPTFORCEREDRAWINDEX);
1113 last->next = tmp->next;
1115 Aliases = tmp->next;
1117 mutt_free_alias (&tmp);
1123 while (MoreArgs (s));
1127 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1130 ALIAS *tmp = Aliases;
1134 if (!MoreArgs (s)) {
1135 strfcpy (err->data, _("alias: no address"), err->dsize);
1139 mutt_extract_token (buf, s, 0);
1141 debug_print (2, ("first token is '%s'.\n", buf->data));
1143 /* check to see if an alias with this name already exists */
1144 for (; tmp; tmp = tmp->next) {
1145 if (!str_casecmp (tmp->name, buf->data))
1151 /* create a new alias */
1152 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1154 tmp->name = str_dup (buf->data);
1155 /* give the main addressbook code a chance */
1156 if (CurrentMenu == MENU_ALIAS)
1157 set_option (OPTMENUCALLER);
1160 /* override the previous value */
1161 rfc822_free_address (&tmp->addr);
1162 if (CurrentMenu == MENU_ALIAS)
1163 set_option (OPTFORCEREDRAWINDEX);
1166 mutt_extract_token (buf, s,
1167 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1168 debug_print (2, ("second token is '%s'.\n", buf->data));
1169 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1174 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1175 snprintf (err->data, err->dsize,
1176 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1180 if (DebugLevel >= 2) {
1183 for (a = tmp->addr; a; a = a->next) {
1185 debug_print (2, ("%s\n", a->mailbox));
1187 debug_print (2, ("group %s\n", a->mailbox));
1195 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1198 LIST *tmp = UserHeader;
1203 mutt_extract_token (buf, s, 0);
1204 if (str_cmp ("*", buf->data) == 0)
1205 mutt_free_list (&UserHeader);
1210 l = str_len (buf->data);
1211 if (buf->data[l - 1] == ':')
1215 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1216 && tmp->data[l] == ':') {
1219 last->next = tmp->next;
1221 UserHeader = tmp->next;
1224 mutt_free_list (&ptr);
1233 while (MoreArgs (s));
1237 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1244 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1245 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1246 strfcpy (err->data, _("invalid header field"), err->dsize);
1249 keylen = p - buf->data + 1;
1252 for (tmp = UserHeader;; tmp = tmp->next) {
1253 /* see if there is already a field by this name */
1254 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1255 /* replace the old value */
1256 mem_free (&tmp->data);
1257 tmp->data = buf->data;
1258 memset (buf, 0, sizeof (BUFFER));
1264 tmp->next = mutt_new_list ();
1268 tmp = mutt_new_list ();
1271 tmp->data = buf->data;
1272 memset (buf, 0, sizeof (BUFFER));
1277 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1278 char* errbuf, size_t errlen) {
1281 if (str_ncmp ("reverse-", s, 8) == 0) {
1283 flags = SORT_REVERSE;
1286 if (str_ncmp ("last-", s, 5) == 0) {
1291 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1293 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1297 *((short*) dst->data) = i | flags;
1301 /* if additional data more == 1, we want to resolve synonyms */
1302 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1303 char buf[LONG_STRING];
1304 struct option_t* ptr = (struct option_t*) p;
1306 if (DTYPE (ptr->type) == DT_SYN) {
1309 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1311 if (!ptr || *ptr->init || !FuncTable[DTYPE (ptr->type)].opt_from_string)
1313 mutt_option_value (ptr->option, buf, sizeof (buf));
1314 if (str_len (ptr->init) == 0 && buf && *buf)
1315 ptr->init = str_dup (buf);
1318 static struct option_t* add_option (const char* name, const char* init,
1319 short type, short dup) {
1320 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1322 debug_print (1, ("adding $%s\n", name));
1324 option->option = str_dup (name);
1325 option->type = type;
1327 option->init = dup ? str_dup (init) : (char*) init;
1331 /* creates new option_t* of type DT_USER for $user_ var */
1332 static struct option_t* add_user_option (const char* name) {
1333 return (add_option (name, NULL, DT_USER, 1));
1336 /* free()'s option_t* */
1337 static void del_option (void* p) {
1338 struct option_t* ptr = (struct option_t*) p;
1339 char* s = (char*) ptr->data;
1340 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1341 mem_free (&ptr->option);
1343 mem_free (&ptr->init);
1347 /* if additional data more == 1, we want to resolve synonyms */
1348 static void mutt_restore_default (const char* name, void* p,
1349 unsigned long more) {
1350 char errbuf[STRING];
1351 struct option_t* ptr = (struct option_t*) p;
1353 if (DTYPE (ptr->type) == DT_SYN) {
1356 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1360 if (FuncTable[DTYPE (ptr->type)].opt_from_string &&
1361 FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1362 sizeof (errbuf)) < 0) {
1364 fprintf (stderr, _("Invalid default setting found. Please report this "
1365 "error:\n\"%s\"\n"), errbuf);
1369 if (ptr->flags & R_INDEX)
1370 set_option (OPTFORCEREDRAWINDEX);
1371 if (ptr->flags & R_PAGER)
1372 set_option (OPTFORCEREDRAWPAGER);
1373 if (ptr->flags & R_RESORT_SUB)
1374 set_option (OPTSORTSUBTHREADS);
1375 if (ptr->flags & R_RESORT)
1376 set_option (OPTNEEDRESORT);
1377 if (ptr->flags & R_RESORT_INIT)
1378 set_option (OPTRESORTINIT);
1379 if (ptr->flags & R_TREE)
1380 set_option (OPTREDRAWTREE);
1383 /* check whether value for $dsn_return would be valid */
1384 static int check_dsn_return (const char* option, unsigned long p,
1385 char* errbuf, size_t errlen) {
1386 char* val = (char*) p;
1387 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1388 str_ncmp (val, "full", 4) != 0) {
1390 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1396 /* check whether value for $dsn_notify would be valid */
1397 static int check_dsn_notify (const char* option, unsigned long p,
1398 char* errbuf, size_t errlen) {
1399 list2_t* list = NULL;
1401 char* val = (char*) p;
1405 list = list_from_str (val, ",");
1406 if (list_empty (list))
1409 for (i = 0; i < list->length; i++)
1410 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1411 str_ncmp (list->data[i], "failure", 7) != 0 &&
1412 str_ncmp (list->data[i], "delay", 5) != 0 &&
1413 str_ncmp (list->data[i], "success", 7) != 0) {
1415 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1416 (char*) list->data[i], "dsn_notify");
1420 list_del (&list, (list_del_t*) _mem_free);
1424 static int check_num (const char* option, unsigned long p,
1425 char* errbuf, size_t errlen) {
1428 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1435 static int check_debug (const char* option, unsigned long p,
1436 char* errbuf, size_t errlen) {
1437 if ((int) p <= DEBUG_MAX_LEVEL &&
1438 (int) p >= DEBUG_MIN_LEVEL)
1442 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1447 static int check_history (const char* option, unsigned long p,
1448 char* errbuf, size_t errlen) {
1449 if (!check_num ("history", p, errbuf, errlen))
1451 mutt_init_history ();
1455 static int check_special (const char* name, unsigned long val,
1456 char* errbuf, size_t errlen) {
1459 for (i = 0; SpecialVars[i].name; i++) {
1460 if (str_cmp (SpecialVars[i].name, name) == 0) {
1461 return (SpecialVars[i].check (SpecialVars[i].name,
1462 val, errbuf, errlen));
1468 static const struct mapping_t* get_sortmap (struct option_t* option) {
1469 const struct mapping_t* map = NULL;
1471 switch (option->type & DT_SUBTYPE_MASK) {
1473 map = SortAliasMethods;
1475 case DT_SORT_BROWSER:
1476 map = SortBrowserMethods;
1479 if ((WithCrypto & APPLICATION_PGP))
1480 map = SortKeyMethods;
1483 map = SortAuxMethods;
1492 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1495 int query, unset, inv, reset, r = 0;
1496 struct option_t* option = NULL;
1498 while (MoreArgs (s)) {
1499 /* reset state variables */
1501 unset = data & M_SET_UNSET;
1502 inv = data & M_SET_INV;
1503 reset = data & M_SET_RESET;
1505 if (*s->dptr == '?') {
1509 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1513 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1517 else if (*s->dptr == '&') {
1522 /* get the variable name */
1523 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1525 /* resolve synonyms */
1526 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1527 DTYPE (option->type == DT_SYN)) {
1528 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1529 syn_add (newopt, option);
1533 /* see if we need to add $user_ var */
1534 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1535 /* there's no option named like this yet so only add one
1536 * if the action isn't any of: reset, unset, query */
1537 if (!(reset || unset || query || *s->dptr != '=')) {
1538 debug_print (1, ("adding user option '%s'\n", tmp->data));
1539 option = add_user_option (tmp->data);
1540 hash_insert (ConfigOptions, option->option, option, 0);
1544 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1545 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1551 if (query || unset || inv) {
1552 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1556 if (s && *s->dptr == '=') {
1557 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1561 if (!str_cmp ("all", tmp->data)) {
1562 hash_map (ConfigOptions, mutt_restore_default, 1);
1565 else if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1566 snprintf (err->data, err->dsize, _("$%s is read-only"), option->option);
1570 mutt_restore_default (NULL, option, 1);
1572 else if (DTYPE (option->type) == DT_BOOL) {
1573 /* XXX this currently ignores the function table
1574 * as we don't get invert and stuff into it */
1575 if (s && *s->dptr == '=') {
1576 if (unset || inv || query) {
1577 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1582 mutt_extract_token (tmp, s, 0);
1583 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1585 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1588 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1594 bool_to_string (err->data, err->dsize, option);
1599 unset_option (option->data);
1601 toggle_option (option->data);
1603 set_option (option->data);
1605 else if (DTYPE (option->type) == DT_STR ||
1606 DTYPE (option->type) == DT_PATH ||
1607 DTYPE (option->type) == DT_ADDR ||
1608 DTYPE (option->type) == DT_MAGIC ||
1609 DTYPE (option->type) == DT_NUM ||
1610 DTYPE (option->type) == DT_SORT ||
1611 DTYPE (option->type) == DT_RX ||
1612 DTYPE (option->type) == DT_USER ||
1613 DTYPE (option->type) == DT_SYS) {
1615 /* XXX maybe we need to get unset into handlers? */
1616 if (DTYPE (option->type) == DT_STR ||
1617 DTYPE (option->type) == DT_PATH ||
1618 DTYPE (option->type) == DT_ADDR ||
1619 DTYPE (option->type) == DT_USER ||
1620 DTYPE (option->type) == DT_SYS) {
1622 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1623 snprintf (err->data, err->dsize, _("$%s is read-only"),
1627 } else if (DTYPE (option->type) == DT_ADDR)
1628 rfc822_free_address ((ADDRESS **) option->data);
1629 else if (DTYPE (option->type) == DT_USER)
1630 /* to unset $user_ means remove */
1631 hash_delete (ConfigOptions, option->option,
1632 option, del_option);
1634 mem_free ((void *) option->data);
1639 if (query || *s->dptr != '=') {
1640 FuncTable[DTYPE (option->type)].opt_to_string
1641 (err->data, err->dsize, option);
1645 /* the $muttng_ variables are read-only */
1646 if (!FuncTable[DTYPE (option->type)].opt_from_string) {
1647 snprintf (err->data, err->dsize, _("$%s is read-only"),
1653 mutt_extract_token (tmp, s, 0);
1654 if (!FuncTable[DTYPE (option->type)].opt_from_string
1655 (option, tmp->data, err->data, err->dsize))
1659 else if (DTYPE (option->type) == DT_QUAD) {
1662 quad_to_string (err->data, err->dsize, option);
1666 if (*s->dptr == '=') {
1668 mutt_extract_token (tmp, s, 0);
1669 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1670 set_quadoption (option->data, M_YES);
1671 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1672 set_quadoption (option->data, M_NO);
1673 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1674 set_quadoption (option->data, M_ASKYES);
1675 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1676 set_quadoption (option->data, M_ASKNO);
1678 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1679 tmp->data, option->option);
1686 toggle_quadoption (option->data);
1688 set_quadoption (option->data, M_NO);
1690 set_quadoption (option->data, M_YES);
1694 snprintf (err->data, err->dsize, _("%s: unknown type"),
1700 if (option->flags & R_INDEX)
1701 set_option (OPTFORCEREDRAWINDEX);
1702 if (option->flags & R_PAGER)
1703 set_option (OPTFORCEREDRAWPAGER);
1704 if (option->flags & R_RESORT_SUB)
1705 set_option (OPTSORTSUBTHREADS);
1706 if (option->flags & R_RESORT)
1707 set_option (OPTNEEDRESORT);
1708 if (option->flags & R_RESORT_INIT)
1709 set_option (OPTRESORTINIT);
1710 if (option->flags & R_TREE)
1711 set_option (OPTREDRAWTREE);
1718 /* reads the specified initialization file. returns -1 if errors were found
1719 so that we can pause to let the user know... */
1720 static int source_rc (const char *rcfile, BUFFER * err)
1723 int line = 0, rc = 0, conv = 0;
1725 char *linebuf = NULL;
1726 char *currentline = NULL;
1730 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1732 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1733 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1737 memset (&token, 0, sizeof (token));
1738 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1739 conv = ConfigCharset && (*ConfigCharset) && Charset;
1741 currentline = str_dup (linebuf);
1744 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1747 currentline = linebuf;
1752 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1753 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1754 if (--rc < -MAXERRS) {
1756 mem_free (¤tline);
1765 mem_free (¤tline);
1767 mem_free (&token.data);
1768 mem_free (&linebuf);
1771 mutt_wait_filter (pid);
1773 /* the muttrc source keyword */
1774 snprintf (err->data, err->dsize,
1775 rc >= -MAXERRS ? _("source: errors in %s")
1776 : _("source: reading aborted due too many errors in %s"),
1785 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1788 char path[_POSIX_PATH_MAX];
1792 if (mutt_extract_token (tmp, s, 0) != 0) {
1793 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1797 strfcpy (path, tmp->data, sizeof (path));
1798 mutt_expand_path (path, sizeof (path));
1800 rc += source_rc (path, err);
1802 while (MoreArgs (s));
1804 return ((rc < 0) ? -1 : 0);
1807 /* line command to execute
1809 token scratch buffer to be used by parser. caller should free
1810 token->data when finished. the reason for this variable is
1811 to avoid having to allocate and deallocate a lot of memory
1812 if we are parsing many lines. the caller can pass in the
1813 memory to use, which avoids having to create new space for
1814 every call to this function.
1816 err where to write error messages */
1817 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1822 memset (&expn, 0, sizeof (expn));
1823 expn.data = expn.dptr = line;
1824 expn.dsize = str_len (line);
1829 while (*expn.dptr) {
1830 if (*expn.dptr == '#')
1831 break; /* rest of line is a comment */
1832 if (*expn.dptr == ';') {
1836 mutt_extract_token (token, &expn, 0);
1837 for (i = 0; Commands[i].name; i++) {
1838 if (!str_cmp (token->data, Commands[i].name)) {
1839 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1844 if (!Commands[i].name) {
1845 snprintf (err->data, err->dsize, _("%s: unknown command"),
1846 NONULL (token->data));
1853 mem_free (&expn.data);
1858 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1859 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1860 /* initial string that starts completion. No telling how much crap
1861 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1862 char User_typed[LONG_STRING] = { 0 };
1864 int Num_matched = 0; /* Number of matches for completion */
1865 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1866 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1868 /* helper function for completion. Changes the dest buffer if
1869 necessary/possible to aid completion.
1870 dest == completion result gets here.
1871 src == candidate for completion.
1872 try == user entered data for completion.
1873 len == length of dest buffer.
1875 static void candidate (char *dest, char *try, char *src, int len)
1879 if (strstr (src, try) == src) {
1880 Matches[Num_matched++] = src;
1882 strfcpy (dest, src, len);
1884 for (l = 0; src[l] && src[l] == dest[l]; l++);
1890 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1894 int spaces; /* keep track of the number of leading spaces on the line */
1897 spaces = buffer - pt;
1899 pt = buffer + pos - spaces;
1900 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1903 if (pt == buffer) { /* complete cmd */
1904 /* first TAB. Collect all the matches */
1907 strfcpy (User_typed, pt, sizeof (User_typed));
1908 memset (Matches, 0, sizeof (Matches));
1909 memset (Completed, 0, sizeof (Completed));
1910 for (num = 0; Commands[num].name; num++)
1911 candidate (Completed, User_typed, Commands[num].name,
1912 sizeof (Completed));
1913 Matches[Num_matched++] = User_typed;
1915 /* All matches are stored. Longest non-ambiguous string is ""
1916 * i.e. dont change 'buffer'. Fake successful return this time */
1917 if (User_typed[0] == 0)
1921 if (Completed[0] == 0 && User_typed[0])
1924 /* Num_matched will _always_ be atleast 1 since the initial
1925 * user-typed string is always stored */
1926 if (numtabs == 1 && Num_matched == 2)
1927 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1928 else if (numtabs > 1 && Num_matched > 2)
1929 /* cycle thru all the matches */
1930 snprintf (Completed, sizeof (Completed), "%s",
1931 Matches[(numtabs - 2) % Num_matched]);
1933 /* return the completed command */
1934 strncpy (buffer, Completed, len - spaces);
1936 else if (!str_ncmp (buffer, "set", 3)
1937 || !str_ncmp (buffer, "unset", 5)
1938 || !str_ncmp (buffer, "reset", 5)
1939 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1940 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1943 /* loop through all the possible prefixes (no, inv, ...) */
1944 if (!str_ncmp (buffer, "set", 3)) {
1945 for (num = 0; prefixes[num]; num++) {
1946 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1947 pt += str_len (prefixes[num]);
1953 /* first TAB. Collect all the matches */
1956 strfcpy (User_typed, pt, sizeof (User_typed));
1957 memset (Matches, 0, sizeof (Matches));
1958 memset (Completed, 0, sizeof (Completed));
1959 for (num = 0; MuttVars[num].option; num++)
1960 candidate (Completed, User_typed, MuttVars[num].option,
1961 sizeof (Completed));
1962 Matches[Num_matched++] = User_typed;
1964 /* All matches are stored. Longest non-ambiguous string is ""
1965 * i.e. dont change 'buffer'. Fake successful return this time */
1966 if (User_typed[0] == 0)
1970 if (Completed[0] == 0 && User_typed[0])
1973 /* Num_matched will _always_ be atleast 1 since the initial
1974 * user-typed string is always stored */
1975 if (numtabs == 1 && Num_matched == 2)
1976 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1977 else if (numtabs > 1 && Num_matched > 2)
1978 /* cycle thru all the matches */
1979 snprintf (Completed, sizeof (Completed), "%s",
1980 Matches[(numtabs - 2) % Num_matched]);
1982 strncpy (pt, Completed, buffer + len - pt - spaces);
1984 else if (!str_ncmp (buffer, "exec", 4)) {
1985 struct binding_t *menu = km_get_table (CurrentMenu);
1987 if (!menu && CurrentMenu != MENU_PAGER)
1991 /* first TAB. Collect all the matches */
1994 strfcpy (User_typed, pt, sizeof (User_typed));
1995 memset (Matches, 0, sizeof (Matches));
1996 memset (Completed, 0, sizeof (Completed));
1997 for (num = 0; menu[num].name; num++)
1998 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1999 /* try the generic menu */
2000 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
2002 for (num = 0; menu[num].name; num++)
2003 candidate (Completed, User_typed, menu[num].name,
2004 sizeof (Completed));
2006 Matches[Num_matched++] = User_typed;
2008 /* All matches are stored. Longest non-ambiguous string is ""
2009 * i.e. dont change 'buffer'. Fake successful return this time */
2010 if (User_typed[0] == 0)
2014 if (Completed[0] == 0 && User_typed[0])
2017 /* Num_matched will _always_ be atleast 1 since the initial
2018 * user-typed string is always stored */
2019 if (numtabs == 1 && Num_matched == 2)
2020 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
2021 else if (numtabs > 1 && Num_matched > 2)
2022 /* cycle thru all the matches */
2023 snprintf (Completed, sizeof (Completed), "%s",
2024 Matches[(numtabs - 2) % Num_matched]);
2026 strncpy (pt, Completed, buffer + len - pt - spaces);
2034 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2036 char var[STRING], *pt = buffer;
2038 struct option_t* option = NULL;
2044 spaces = buffer - pt;
2046 pt = buffer + pos - spaces;
2047 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2049 pt++; /* move past the space */
2050 if (*pt == '=') /* abort if no var before the '=' */
2053 if (str_ncmp (buffer, "set", 3) == 0) {
2054 strfcpy (var, pt, sizeof (var));
2055 /* ignore the trailing '=' when comparing */
2056 var[str_len (var) - 1] = 0;
2057 if (!(option = hash_find (ConfigOptions, var)))
2058 return 0; /* no such variable. */
2060 char tmp[LONG_STRING], tmp2[LONG_STRING];
2062 size_t dlen = buffer + len - pt - spaces;
2063 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2067 if ((DTYPE (option->type) == DT_STR) ||
2068 (DTYPE (option->type) == DT_PATH) ||
2069 (DTYPE (option->type) == DT_RX)) {
2070 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2071 if (DTYPE (option->type) == DT_PATH)
2072 mutt_pretty_mailbox (tmp);
2074 else if (DTYPE (option->type) == DT_ADDR) {
2075 rfc822_write_address (tmp, sizeof (tmp),
2076 *((ADDRESS **) option->data), 0);
2078 else if (DTYPE (option->type) == DT_QUAD)
2079 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2080 else if (DTYPE (option->type) == DT_NUM)
2081 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2082 else if (DTYPE (option->type) == DT_SORT) {
2083 const struct mapping_t *map;
2086 switch (option->type & DT_SUBTYPE_MASK) {
2088 map = SortAliasMethods;
2090 case DT_SORT_BROWSER:
2091 map = SortBrowserMethods;
2094 if ((WithCrypto & APPLICATION_PGP))
2095 map = SortKeyMethods;
2104 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2106 snprintf (tmp, sizeof (tmp), "%s%s%s",
2107 (*((short *) option->data) & SORT_REVERSE) ?
2109 (*((short *) option->data) & SORT_LAST) ? "last-" :
2112 else if (DTYPE (option->type) == DT_MAGIC) {
2114 switch (DefaultMagic) {
2130 strfcpy (tmp, p, sizeof (tmp));
2132 else if (DTYPE (option->type) == DT_BOOL)
2133 strfcpy (tmp, option (option->data) ? "yes" : "no",
2138 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2139 if (*s == '\\' || *s == '"')
2145 strfcpy (tmp, pt, sizeof (tmp));
2146 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2154 /* Implement the -Q command line flag */
2155 int mutt_query_variables (LIST * queries)
2159 char errbuff[STRING];
2160 char command[STRING];
2164 memset (&err, 0, sizeof (err));
2165 memset (&token, 0, sizeof (token));
2168 err.dsize = sizeof (errbuff);
2170 for (p = queries; p; p = p->next) {
2171 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2172 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2173 fprintf (stderr, "%s\n", err.data);
2174 mem_free (&token.data);
2177 printf ("%s\n", err.data);
2180 mem_free (&token.data);
2184 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2188 for (i = 0; map[i].name; i++)
2189 if (map[i].value == val)
2190 return (map[i].name);
2194 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2198 for (i = 0; map[i].name; i++)
2199 if (ascii_strcasecmp (map[i].name, name) == 0)
2200 return (map[i].value);
2204 static int mutt_execute_commands (LIST * p)
2207 char errstr[SHORT_STRING];
2209 memset (&err, 0, sizeof (err));
2211 err.dsize = sizeof (errstr);
2212 memset (&token, 0, sizeof (token));
2213 for (; p; p = p->next) {
2214 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2215 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2216 mem_free (&token.data);
2220 mem_free (&token.data);
2224 void mutt_init (int skip_sys_rc, LIST * commands)
2227 struct utsname utsname;
2228 char *p, buffer[STRING], error[STRING];
2229 int i, default_rc = 0, need_pause = 0;
2232 memset (&err, 0, sizeof (err));
2234 err.dsize = sizeof (error);
2236 /* use 3*sizeof(muttvars) instead of 2*sizeof()
2237 * to have some room for $user_ vars */
2238 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2239 for (i = 0; MuttVars[i].option; i++) {
2240 if (DTYPE (MuttVars[i].type) != DT_SYS)
2241 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2243 hash_insert (ConfigOptions, MuttVars[i].option,
2244 add_option (MuttVars[i].option, MuttVars[i].init,
2249 * XXX - use something even more difficult to predict?
2251 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2252 "\033]9;%ld\a", (long) time (NULL));
2254 /* on one of the systems I use, getcwd() does not return the same prefix
2255 as is listed in the passwd file */
2256 if ((p = getenv ("HOME")))
2257 Homedir = str_dup (p);
2259 /* Get some information about the user */
2260 if ((pw = getpwuid (getuid ()))) {
2263 Username = str_dup (pw->pw_name);
2265 Homedir = str_dup (pw->pw_dir);
2267 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2268 Shell = str_dup (pw->pw_shell);
2273 fputs (_("unable to determine home directory"), stderr);
2276 if ((p = getenv ("USER")))
2277 Username = str_dup (p);
2280 fputs (_("unable to determine username"), stderr);
2283 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2286 debug_start(Homedir);
2288 /* And about the host... */
2290 /* some systems report the FQDN instead of just the hostname */
2291 if ((p = strchr (utsname.nodename, '.'))) {
2292 Hostname = str_substrdup (utsname.nodename, p);
2294 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2297 Hostname = str_dup (utsname.nodename);
2300 #define DOMAIN buffer
2301 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2302 Fqdn = str_dup ("@");
2305 if (*DOMAIN != '@') {
2306 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2307 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2310 Fqdn = str_dup (NONULL (Hostname));
2317 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2319 fgets (buffer, sizeof (buffer), f);
2320 p = (char*) &buffer;
2323 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2327 NewsServer = str_dup (p);
2331 if ((p = getenv ("NNTPSERVER")))
2332 NewsServer = str_dup (p);
2335 if ((p = getenv ("MAIL")))
2336 Spoolfile = str_dup (p);
2337 else if ((p = getenv ("MAILDIR")))
2338 Spoolfile = str_dup (p);
2341 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2343 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2345 Spoolfile = str_dup (buffer);
2348 if ((p = getenv ("MAILCAPS")))
2349 MailcapPath = str_dup (p);
2351 /* Default search path from RFC1524 */
2353 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2354 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2357 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2359 p = getenv ("VISUAL");
2361 p = getenv ("EDITOR");
2365 Editor = str_dup (p);
2366 Visual = str_dup (p);
2368 if ((p = getenv ("REPLYTO")) != NULL) {
2371 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2373 memset (&buf, 0, sizeof (buf));
2374 buf.data = buf.dptr = buffer;
2375 buf.dsize = str_len (buffer);
2377 memset (&token, 0, sizeof (token));
2378 parse_my_hdr (&token, &buf, 0, &err);
2379 mem_free (&token.data);
2382 if ((p = getenv ("EMAIL")) != NULL)
2383 From = rfc822_parse_adrlist (NULL, p);
2385 mutt_set_langinfo_charset ();
2386 mutt_set_charset (Charset);
2389 /* Set standard defaults */
2390 hash_map (ConfigOptions, mutt_set_default, 0);
2391 hash_map (ConfigOptions, mutt_restore_default, 0);
2393 CurrentMenu = MENU_MAIN;
2396 #ifndef LOCALES_HACK
2397 /* Do we have a locale definition? */
2398 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2399 ((p = getenv ("LANG")) != NULL && p[0]) ||
2400 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2401 set_option (OPTLOCALES);
2405 /* Unset suspend by default if we're the session leader */
2406 if (getsid (0) == getpid ())
2407 unset_option (OPTSUSPEND);
2410 mutt_init_history ();
2419 * When changing the code which looks for a configuration file,
2420 * please also change the corresponding code in muttbug.sh.in.
2430 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2432 if (access (buffer, F_OK) == -1)
2434 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2435 if (access (buffer, F_OK) == -1)
2437 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2438 NONULL (Homedir), MUTT_VERSION);
2439 if (access (buffer, F_OK) == -1)
2441 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2445 Muttrc = str_dup (buffer);
2448 strfcpy (buffer, Muttrc, sizeof (buffer));
2450 mutt_expand_path (buffer, sizeof (buffer));
2451 Muttrc = str_dup (buffer);
2453 mem_free (&AliasFile);
2454 AliasFile = str_dup (NONULL (Muttrc));
2456 /* Process the global rc file if it exists and the user hasn't explicity
2457 requested not to via "-n". */
2459 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2461 if (access (buffer, F_OK) == -1)
2462 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2463 if (access (buffer, F_OK) == -1)
2464 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2466 if (access (buffer, F_OK) == -1)
2467 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2468 if (access (buffer, F_OK) != -1) {
2469 if (source_rc (buffer, &err) != 0) {
2470 fputs (err.data, stderr);
2471 fputc ('\n', stderr);
2477 /* Read the user's initialization file. */
2478 if (access (Muttrc, F_OK) != -1) {
2479 if (!option (OPTNOCURSES))
2481 if (source_rc (Muttrc, &err) != 0) {
2482 fputs (err.data, stderr);
2483 fputc ('\n', stderr);
2487 else if (!default_rc) {
2488 /* file specified by -F does not exist */
2489 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2490 mutt_endwin (buffer);
2494 if (mutt_execute_commands (commands) != 0)
2497 /* warn about synonym variables */
2498 if (!list_empty(Synonyms)) {
2500 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2501 for (i = 0; i < Synonyms->length; i++) {
2502 struct option_t* newopt = NULL, *oldopt = NULL;
2503 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2504 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2505 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2506 oldopt ? NONULL (oldopt->option) : "",
2507 newopt ? NONULL (newopt->option) : "",
2508 NONULL(((syn_t*) Synonyms->data[i])->f),
2509 ((syn_t*) Synonyms->data[i])->l);
2511 fprintf (stderr, _("Warning: synonym variables are scheduled"
2512 " for removal.\n"));
2513 list_del (&Synonyms, syn_del);
2517 if (need_pause && !option (OPTNOCURSES)) {
2518 if (mutt_any_key_to_continue (NULL) == -1)
2523 set_option (OPTWEED); /* turn weeding on by default */
2527 int mutt_get_hook_type (const char *name)
2529 struct command_t *c;
2531 for (c = Commands; c->name; c++)
2532 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2537 /* compare two option_t*'s for sorting -t/-T output */
2538 static int opt_cmp (const void* a, const void* b) {
2539 return (str_cmp ((*(struct option_t**) a)->option,
2540 (*(struct option_t**) b)->option));
2543 /* callback for hash_map() to put all non-synonym vars into list */
2544 static void opt_sel_full (const char* key, void* data,
2545 unsigned long more) {
2546 list2_t** l = (list2_t**) more;
2547 struct option_t* option = (struct option_t*) data;
2549 if (DTYPE (option->type) == DT_SYN)
2551 list_push_back (l, option);
2554 /* callback for hash_map() to put all changed non-synonym vars into list */
2555 static void opt_sel_diff (const char* key, void* data,
2556 unsigned long more) {
2557 list2_t** l = (list2_t**) more;
2558 struct option_t* option = (struct option_t*) data;
2559 char buf[LONG_STRING];
2561 if (DTYPE (option->type) == DT_SYN)
2564 mutt_option_value (option->option, buf, sizeof (buf));
2565 if (str_cmp (buf, option->init) != 0)
2566 list_push_back (l, option);
2569 /* dump out the value of all the variables we have */
2570 int mutt_dump_variables (int full) {
2572 char outbuf[STRING];
2573 list2_t* tmp = NULL;
2574 struct option_t* option = NULL;
2576 /* get all non-synonyms into list... */
2577 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2578 (unsigned long) &tmp);
2580 if (!list_empty(tmp)) {
2581 /* ...and dump list sorted */
2582 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2583 for (i = 0; i < tmp->length; i++) {
2584 option = (struct option_t*) tmp->data[i];
2585 FuncTable[DTYPE (option->type)].opt_to_string
2586 (outbuf, sizeof (outbuf), option);
2587 printf ("%s\n", outbuf);
2590 list_del (&tmp, NULL);