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 /* use this to check only */
93 static int check_special (const char* option, unsigned long val,
94 char* errbuf, size_t errlen);
96 /* variable <-> sanity check function mappings
97 * when changing these, make sure the proper _from_string handler
102 int (*check) (const char* option, unsigned long val,
103 char* errbuf, size_t errlen);
105 { "dsn_notify", check_dsn_notify },
106 { "dsn_return", check_dsn_return },
107 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
108 { "smtp_use_tls", mutt_libesmtp_check_usetls },
110 { "history", check_history },
111 { "pager_index_lines", check_num },
116 /* protos for config type handles: convert value to string */
117 static void bool_to_string (char* dst, size_t dstlen, struct option_t* option);
118 static void num_to_string (char* dst, size_t dstlen, struct option_t* option);
119 static void str_to_string (char* dst, size_t dstlen, struct option_t* option);
120 static void quad_to_string (char* dst, size_t dstlen, struct option_t* option);
121 static void sort_to_string (char* dst, size_t dstlen, struct option_t* option);
122 static void rx_to_string (char* dst, size_t dstlen, struct option_t* option);
123 static void magic_to_string (char* dst, size_t dstlen, struct option_t* option);
124 static void addr_to_string (char* dst, size_t dstlen, struct option_t* option);
125 static void user_to_string (char* dst, size_t dstlen, struct option_t* option);
127 /* protos for config type handles: convert to value from string */
128 static int bool_from_string (struct option_t* dst, const char* val,
129 char* errbuf, size_t errlen);
130 static int num_from_string (struct option_t* dst, const char* val,
131 char* errbuf, size_t errlen);
132 static int str_from_string (struct option_t* dst, const char* val,
133 char* errbuf, size_t errlen);
134 static int path_from_string (struct option_t* dst, const char* val,
135 char* errbuf, size_t errlen);
136 static int quad_from_string (struct option_t* dst, const char* val,
137 char* errbuf, size_t errlen);
138 static int sort_from_string (struct option_t* dst, const char* val,
139 char* errbuf, size_t errlen);
140 static int rx_from_string (struct option_t* dst, const char* val,
141 char* errbuf, size_t errlen);
142 static int magic_from_string (struct option_t* dst, const char* val,
143 char* errbuf, size_t errlen);
144 static int addr_from_string (struct option_t* dst, const char* val,
145 char* errbuf, size_t errlen);
146 static int user_from_string (struct option_t* dst, const char* val,
147 char* errbuf, size_t errlen);
151 void (*opt_to_string) (char* dst, size_t dstlen, struct option_t* option);
152 int (*opt_from_string) (struct option_t* dst, const char* val,
153 char* errbuf, size_t errlen);
155 { 0, NULL, NULL }, /* there's no DT_ type with 0 */
156 { DT_BOOL, bool_to_string, bool_from_string },
157 { DT_NUM, num_to_string, num_from_string },
158 { DT_STR, str_to_string, str_from_string },
159 { DT_PATH, str_to_string, path_from_string },
160 { DT_QUAD, quad_to_string, quad_from_string },
161 { DT_SORT, sort_to_string, sort_from_string },
162 { DT_RX, rx_to_string, rx_from_string },
163 { DT_MAGIC, magic_to_string, magic_from_string },
164 /* synonyms should be resolved already so we don't need this
165 * but must define it as DT_ is used for indexing */
166 { DT_SYN, NULL, NULL },
167 { DT_ADDR, addr_to_string, addr_from_string },
168 { DT_USER, user_to_string, user_from_string },
171 static void bool_to_string (char* dst, size_t dstlen,
172 struct option_t* option) {
173 snprintf (dst, dstlen, "%s=%s", option->option,
174 option (option->data) ? "yes" : "no");
177 static int bool_from_string (struct option_t* dst, const char* val,
178 char* errbuf, size_t errlen) {
183 if (ascii_strncasecmp (val, "yes", 3) == 0)
185 else if (ascii_strncasecmp (val, "no", 2) == 0)
191 set_option (dst->data);
193 unset_option (dst->data);
197 static void num_to_string (char* dst, size_t dstlen,
198 struct option_t* option) {
200 const char* fmt = (str_cmp (option->option, "umask") == 0) ?
202 snprintf (dst, dstlen, fmt, option->option,
203 *((short*) option->data));
206 static int num_from_string (struct option_t* dst, const char* val,
207 char* errbuf, size_t errlen) {
208 int num = 0, old = 0;
214 num = strtol (val, &t, 0);
216 if (!*val || *t || (short) num != num) {
218 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
224 /* just temporarily accept new val so that check_special for
225 * $history already has it when doing history's init() */
226 old = *((short*) dst->data);
227 *((short*) dst->data) = (short) num;
229 if (!check_special (dst->option, (unsigned long) num, errbuf, errlen)) {
230 *((short*) dst->data) = old;
237 static void str_to_string (char* dst, size_t dstlen,
238 struct option_t* option) {
239 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
240 NONULL (*((char**) option->data)));
243 static void user_to_string (char* dst, size_t dstlen,
244 struct option_t* option) {
245 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
246 NONULL (((char*) option->data)));
249 static int path_from_string (struct option_t* dst, const char* val,
250 char* errbuf, size_t errlen) {
251 char path[_POSIX_PATH_MAX];
257 mem_free ((char**) dst->data);
262 strfcpy (path, val, sizeof (path));
263 mutt_expand_path (path, sizeof (path));
264 str_replace ((char **) dst->data, path);
268 static int str_from_string (struct option_t* dst, const char* val,
269 char* errbuf, size_t errlen) {
273 if (!check_special (dst->option, (unsigned long) val, errbuf, errlen))
276 str_replace ((char**) dst->data, val);
280 static int user_from_string (struct option_t* dst, const char* val,
281 char* errbuf, size_t errlen) {
282 /* if dst == NULL, we may get here in case the user did unset it,
283 * see parse_set() where item is free()'d before coming here; so
284 * just silently ignore it */
287 if (str_len ((char*) dst->data) == 0)
288 dst->data = (unsigned long) str_dup (val);
290 char* s = (char*) dst->data;
291 str_replace (&s, val);
293 if (str_len (dst->init) == 0)
294 dst->init = str_dup ((char*) dst->data);
298 static void quad_to_string (char* dst, size_t dstlen,
299 struct option_t* option) {
300 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
301 snprintf (dst, dstlen, "%s=%s", option->option,
302 vals[quadoption (option->data)]);
305 static int quad_from_string (struct option_t* dst, const char* val,
306 char* errbuf, size_t errlen) {
311 if (ascii_strncasecmp (val, "yes", 3) == 0)
313 else if (ascii_strncasecmp (val, "no", 2) == 0)
315 else if (ascii_strncasecmp (val, "ask-yes", 7) == 0)
317 else if (ascii_strncasecmp (val, "ask-no", 6) == 0)
323 set_quadoption (dst->data, flag);
327 static void sort_to_string (char* dst, size_t dstlen,
328 struct option_t* option) {
329 const struct mapping_t *map = get_sortmap (option);
333 snprintf (dst, sizeof (dst), "%s=unknown", option->option);
337 p = mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
340 snprintf (dst, dstlen, "%s=%s%s%s", option->option,
341 (*((short *) option->data) & SORT_REVERSE) ?
343 (*((short *) option->data) & SORT_LAST) ? "last-" :
347 static int sort_from_string (struct option_t* dst, const char* val,
348 char* errbuf, size_t errlen) {
349 const struct mapping_t *map = NULL;
350 if (!(map = get_sortmap (dst))) {
352 snprintf (errbuf, errlen, _("%s: Unknown type."),
356 if (parse_sort (dst, val, map, errbuf, errlen) == -1)
361 static void rx_to_string (char* dst, size_t dstlen,
362 struct option_t* option) {
363 rx_t* p = (rx_t*) option->data;
364 snprintf (dst, dstlen, "%s=\"%s\"", option->option,
365 NONULL (p->pattern));
368 static int rx_from_string (struct option_t* dst, const char* val,
369 char* errbuf, size_t errlen) {
372 int flags = 0, e = 0, not = 0;
378 if (option (OPTATTACHMSG) && !str_cmp (dst->option, "reply_regexp")) {
380 snprintf (errbuf, errlen,
381 "Operation not permitted when in attach-message mode.");
385 if (!((rx_t*) dst->data))
386 *((rx_t**) dst->data) = mem_calloc (1, sizeof (rx_t));
388 p = (rx_t*) dst->data;
390 /* something to do? */
391 if (!val || !*val || (p->pattern && str_cmp (p->pattern, val) == 0))
394 if (str_cmp (dst->option, "mask") != 0)
395 flags |= mutt_which_case (val);
398 if (str_cmp (dst->option, "mask") == 0 && *s == '!') {
403 rx = mem_malloc (sizeof (regex_t));
405 if ((e = REGCOMP (rx, s, flags)) != 0) {
406 regerror (e, rx, errbuf, errlen);
416 str_replace (&p->pattern, val);
420 if (str_cmp (dst->option, "reply_regexp") == 0)
421 mutt_adjust_all_subjects ();
426 static void magic_to_string (char* dst, size_t dstlen,
427 struct option_t* option) {
428 const char* s = NULL;
429 switch (option->data) {
430 case M_MBOX: s = "mbox"; break;
431 case M_MMDF: s = "MMDF"; break;
432 case M_MH: s = "MH"; break;
433 case M_MAILDIR: s = "Maildir"; break;
434 default: s = "unknown"; break;
436 snprintf (dst, dstlen, "%s=%s", option->option, s);
439 static int magic_from_string (struct option_t* dst, const char* val,
440 char* errbuf, size_t errlen) {
443 if (!dst || !val || !*val)
445 if (ascii_strncasecmp (val, "mbox", 4) == 0)
447 else if (ascii_strncasecmp (val, "mmdf", 4) == 0)
449 else if (ascii_strncasecmp (val, "mh", 2) == 0)
451 else if (ascii_strncasecmp (val, "maildir", 7) == 0)
457 *((short*) dst->data) = flag;
462 static void addr_to_string (char* dst, size_t dstlen,
463 struct option_t* option) {
466 rfc822_write_address (s, sizeof (s), *((ADDRESS**) option->data), 0);
467 snprintf (dst, dstlen, "%s=\"%s\"", option->option, NONULL (s));
470 static int addr_from_string (struct option_t* dst, const char* val,
471 char* errbuf, size_t errlen) {
472 if (!dst || !val || !*val)
474 rfc822_free_address ((ADDRESS**) dst->data);
475 *((ADDRESS**) dst->data) = rfc822_parse_adrlist (NULL, val);
479 int mutt_option_value (const char* val, char* dst, size_t dstlen) {
480 struct option_t* option = NULL;
481 char* tmp = NULL, *t = NULL;
484 if (!(option = hash_find (ConfigOptions, val))) {
485 debug_print (1, ("var '%s' not found\n", val));
489 tmp = mem_malloc (dstlen+1);
490 FuncTable[DTYPE (option->type)].opt_to_string (tmp, dstlen, option);
492 /* as we get things of type $var=value and don't want to bloat the
493 * above "just" for expansion, we do the stripping here */
494 debug_print (1, ("orig == '%s'\n", tmp));
495 t = strchr (tmp, '=');
499 if (t[l-1] == '"' && *t == '"') {
504 memcpy (dst, t, l+1);
506 debug_print (1, ("stripped == '%s'\n", dst));
511 /* for synonym warning reports: adds synonym to end of list */
512 static void syn_add (struct option_t* n, struct option_t* o) {
513 syn_t* tmp = mem_malloc (sizeof (syn_t));
514 tmp->f = str_dup (CurRCFile);
518 list_push_back (&Synonyms, tmp);
521 /* for synonym warning reports: free single item (for list_del()) */
522 static void syn_del (void** p) {
523 mem_free(&(*(syn_t**) p)->f);
527 void toggle_quadoption (int opt)
530 int b = (opt % 4) * 2;
532 QuadOptions[n] ^= (1 << b);
535 void set_quadoption (int opt, int flag)
538 int b = (opt % 4) * 2;
540 QuadOptions[n] &= ~(0x3 << b);
541 QuadOptions[n] |= (flag & 0x3) << b;
544 int quadoption (int opt)
547 int b = (opt % 4) * 2;
549 return (QuadOptions[n] >> b) & 0x3;
552 int query_quadoption (int opt, const char *prompt)
554 int v = quadoption (opt);
562 v = mutt_yesorno (prompt, (v == M_ASKYES));
563 CLEARLINE (LINES - 1);
570 static void add_to_list (LIST ** list, const char *str)
572 LIST *t, *last = NULL;
574 /* don't add a NULL or empty string to the list */
575 if (!str || *str == '\0')
578 /* check to make sure the item is not already on this list */
579 for (last = *list; last; last = last->next) {
580 if (ascii_strcasecmp (str, last->data) == 0) {
581 /* already on the list, so just ignore it */
589 if (!*list || last) {
590 t = (LIST *) mem_calloc (1, sizeof (LIST));
591 t->data = str_dup (str);
601 static int add_to_rx_list (list2_t** list, const char *s, int flags,
610 if (!(rx = rx_compile (s, flags))) {
611 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
615 i = rx_lookup ((*list), rx->pattern);
619 list_push_back (list, rx);
623 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
624 const char *templ, BUFFER * err)
626 SPAM_LIST *t = NULL, *last = NULL;
631 if (!pat || !*pat || !templ)
634 if (!(rx = rx_compile (pat, REG_ICASE))) {
635 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
639 /* check to make sure the item is not already on this list */
640 for (last = *list; last; last = last->next) {
641 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
642 /* Already on the list. Formerly we just skipped this case, but
643 * now we're supporting removals, which means we're supporting
644 * re-adds conceptually. So we probably want this to imply a
645 * removal, then do an add. We can achieve the removal by freeing
646 * the template, and leaving t pointed at the current item.
649 mem_free(t->template);
656 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
657 * update. Otherwise we want to make a new one to link at the list's end.
660 t = mutt_new_spam_list ();
668 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
669 t->template = str_dup (templ);
671 /* Find highest match number in template string */
673 for (p = templ; *p;) {
678 while (*p && isdigit ((int) *p))
684 t->nmatch++; /* match 0 is always the whole expr */
689 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
691 SPAM_LIST *spam, *prev;
694 /* Being first is a special case. */
698 if (spam->rx && !str_cmp (spam->rx->pattern, pat)) {
701 mem_free(&spam->template);
707 for (spam = prev->next; spam;) {
708 if (!str_cmp (spam->rx->pattern, pat)) {
709 prev->next = spam->next;
711 mem_free(spam->template);
724 static void remove_from_list (LIST ** l, const char *str)
726 LIST *p, *last = NULL;
728 if (str_cmp ("*", str) == 0)
729 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
734 if (ascii_strcasecmp (str, p->data) == 0) {
737 last->next = p->next;
750 static int remove_from_rx_list (list2_t** l, const char *str)
754 if (str_cmp ("*", str) == 0) {
755 list_del (l, (list_del_t*) rx_free);
759 i = rx_lookup ((*l), str);
761 rx_t* r = list_pop_idx ((*l), i);
769 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
774 struct option_t* option = NULL;
776 memset (&token, 0, sizeof (token));
777 mutt_extract_token (tmp, s, 0);
779 /* is the item defined as a variable or a function? */
780 if ((option = hash_find (ConfigOptions, tmp->data)))
783 for (i = 0; !res && i < MENU_MAX; i++) {
784 struct binding_t *b = km_get_table (Menus[i].value);
789 for (j = 0; b[j].name; j++)
790 if (!ascii_strncasecmp (tmp->data, b[j].name, str_len (tmp->data))
791 && (str_len (b[j].name) == str_len (tmp->data))) {
797 /* check for feature_* */
802 j = str_len (tmp->data);
803 /* need at least input of 'feature_X' */
807 while (Features[i].name) {
808 if (str_len (Features[i].name) == j &&
809 ascii_strncasecmp (Features[i].name, p, j)) {
820 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
822 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
825 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
827 if ((data && res) || (!data && !res)) {
828 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
829 mutt_error ("Error: %s", err->data);
830 mem_free (&token.data);
833 mem_free (&token.data);
838 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
842 mutt_extract_token (buf, s, 0);
844 /* don't add "*" to the unignore list */
845 if (strcmp (buf->data, "*"))
846 add_to_list (&UnIgnore, buf->data);
848 remove_from_list (&Ignore, buf->data);
850 while (MoreArgs (s));
855 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
859 mutt_extract_token (buf, s, 0);
860 remove_from_list (&UnIgnore, buf->data);
861 add_to_list (&Ignore, buf->data);
863 while (MoreArgs (s));
868 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
872 mutt_extract_token (buf, s, 0);
873 add_to_list ((LIST **) data, buf->data);
875 while (MoreArgs (s));
880 static void _alternates_clean (void)
884 if (Context && Context->msgcount) {
885 for (i = 0; i < Context->msgcount; i++)
886 Context->hdrs[i]->recip_valid = 0;
890 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
893 _alternates_clean ();
895 mutt_extract_token (buf, s, 0);
896 remove_from_rx_list (&UnAlternates, buf->data);
898 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
901 while (MoreArgs (s));
906 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
909 _alternates_clean ();
911 mutt_extract_token (buf, s, 0);
912 remove_from_rx_list (&Alternates, buf->data);
914 if (str_cmp (buf->data, "*") &&
915 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
919 while (MoreArgs (s));
924 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
929 memset (&templ, 0, sizeof (templ));
931 /* Insist on at least one parameter */
934 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
936 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
940 /* Extract the first token, a regexp */
941 mutt_extract_token (buf, s, 0);
943 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
944 if (data == M_SPAM) {
945 /* If there's a second parameter, it's a template for the spam tag. */
947 mutt_extract_token (&templ, s, 0);
949 /* Add to the spam list. */
950 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
951 mem_free (&templ.data);
954 mem_free (&templ.data);
957 /* If not, try to remove from the nospam list. */
959 remove_from_rx_list (&NoSpamList, buf->data);
965 /* M_NOSPAM is for nospam commands. */
966 else if (data == M_NOSPAM) {
967 /* nospam only ever has one parameter. */
969 /* "*" is a special case. */
970 if (!str_cmp (buf->data, "*")) {
971 mutt_free_spam_list (&SpamList);
972 list_del (&NoSpamList, (list_del_t*) rx_free);
976 /* If it's on the spam list, just remove it. */
977 if (remove_from_spam_list (&SpamList, buf->data) != 0)
980 /* Otherwise, add it to the nospam list. */
981 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
987 /* This should not happen. */
988 strfcpy (err->data, "This is no good at all.", err->dsize);
992 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
996 mutt_extract_token (buf, s, 0);
998 * Check for deletion of entire list
1000 if (str_cmp (buf->data, "*") == 0) {
1001 mutt_free_list ((LIST **) data);
1004 remove_from_list ((LIST **) data, buf->data);
1006 while (MoreArgs (s));
1011 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
1015 mutt_extract_token (buf, s, 0);
1016 remove_from_rx_list (&UnMailLists, buf->data);
1018 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1021 while (MoreArgs (s));
1026 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
1030 mutt_extract_token (buf, s, 0);
1031 remove_from_rx_list (&SubscribedLists, buf->data);
1032 remove_from_rx_list (&MailLists, buf->data);
1034 if (str_cmp (buf->data, "*") &&
1035 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1038 while (MoreArgs (s));
1043 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1047 mutt_extract_token (buf, s, 0);
1048 remove_from_rx_list (&UnMailLists, buf->data);
1049 remove_from_rx_list (&UnSubscribedLists, buf->data);
1051 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1053 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1056 while (MoreArgs (s));
1061 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
1065 mutt_extract_token (buf, s, 0);
1066 remove_from_rx_list (&SubscribedLists, buf->data);
1068 if (str_cmp (buf->data, "*") &&
1069 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1072 while (MoreArgs (s));
1077 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
1080 ALIAS *tmp, *last = NULL;
1083 mutt_extract_token (buf, s, 0);
1085 if (str_cmp ("*", buf->data) == 0) {
1086 if (CurrentMenu == MENU_ALIAS) {
1087 for (tmp = Aliases; tmp; tmp = tmp->next)
1089 set_option (OPTFORCEREDRAWINDEX);
1092 mutt_free_alias (&Aliases);
1096 for (tmp = Aliases; tmp; tmp = tmp->next) {
1097 if (str_casecmp (buf->data, tmp->name) == 0) {
1098 if (CurrentMenu == MENU_ALIAS) {
1100 set_option (OPTFORCEREDRAWINDEX);
1105 last->next = tmp->next;
1107 Aliases = tmp->next;
1109 mutt_free_alias (&tmp);
1115 while (MoreArgs (s));
1119 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
1122 ALIAS *tmp = Aliases;
1126 if (!MoreArgs (s)) {
1127 strfcpy (err->data, _("alias: no address"), err->dsize);
1131 mutt_extract_token (buf, s, 0);
1133 debug_print (2, ("first token is '%s'.\n", buf->data));
1135 /* check to see if an alias with this name already exists */
1136 for (; tmp; tmp = tmp->next) {
1137 if (!str_casecmp (tmp->name, buf->data))
1143 /* create a new alias */
1144 tmp = (ALIAS *) mem_calloc (1, sizeof (ALIAS));
1146 tmp->name = str_dup (buf->data);
1147 /* give the main addressbook code a chance */
1148 if (CurrentMenu == MENU_ALIAS)
1149 set_option (OPTMENUCALLER);
1152 /* override the previous value */
1153 rfc822_free_address (&tmp->addr);
1154 if (CurrentMenu == MENU_ALIAS)
1155 set_option (OPTFORCEREDRAWINDEX);
1158 mutt_extract_token (buf, s,
1159 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1160 debug_print (2, ("second token is '%s'.\n", buf->data));
1161 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1166 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
1167 snprintf (err->data, err->dsize,
1168 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
1172 if (DebugLevel >= 2) {
1175 for (a = tmp->addr; a; a = a->next) {
1177 debug_print (2, ("%s\n", a->mailbox));
1179 debug_print (2, ("group %s\n", a->mailbox));
1187 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
1190 LIST *tmp = UserHeader;
1195 mutt_extract_token (buf, s, 0);
1196 if (str_cmp ("*", buf->data) == 0)
1197 mutt_free_list (&UserHeader);
1202 l = str_len (buf->data);
1203 if (buf->data[l - 1] == ':')
1207 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
1208 && tmp->data[l] == ':') {
1211 last->next = tmp->next;
1213 UserHeader = tmp->next;
1216 mutt_free_list (&ptr);
1225 while (MoreArgs (s));
1229 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1236 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1237 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1238 strfcpy (err->data, _("invalid header field"), err->dsize);
1241 keylen = p - buf->data + 1;
1244 for (tmp = UserHeader;; tmp = tmp->next) {
1245 /* see if there is already a field by this name */
1246 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1247 /* replace the old value */
1248 mem_free (&tmp->data);
1249 tmp->data = buf->data;
1250 memset (buf, 0, sizeof (BUFFER));
1256 tmp->next = mutt_new_list ();
1260 tmp = mutt_new_list ();
1263 tmp->data = buf->data;
1264 memset (buf, 0, sizeof (BUFFER));
1269 parse_sort (struct option_t* dst, const char *s, const struct mapping_t *map,
1270 char* errbuf, size_t errlen) {
1273 if (str_ncmp ("reverse-", s, 8) == 0) {
1275 flags = SORT_REVERSE;
1278 if (str_ncmp ("last-", s, 5) == 0) {
1283 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1285 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), s, dst->option);
1289 *((short*) dst->data) = i | flags;
1293 /* if additional data more == 1, we want to resolve synonyms */
1294 static void mutt_set_default (const char* name, void* p, unsigned long more) {
1295 char buf[LONG_STRING];
1296 struct option_t* ptr = (struct option_t*) p;
1298 if (DTYPE (ptr->type) == DT_SYN) {
1301 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1303 if (!ptr || *ptr->init)
1305 mutt_option_value (ptr->option, buf, sizeof (buf));
1306 if (str_len (ptr->init) == 0 && buf && *buf)
1307 ptr->init = str_dup (buf);
1310 /* creates new option_t* of type DT_USER for $user_ var */
1311 static struct option_t* add_user_option (const char* name) {
1312 struct option_t* option = mem_calloc (1, sizeof (struct option_t));
1313 option->option = str_dup (name);
1314 option->type = DT_USER;
1318 /* free()'s option_t* */
1319 static void del_user_option (void* p) {
1320 struct option_t* ptr = (struct option_t*) p;
1321 char* s = (char*) ptr->data;
1322 debug_print (1, ("removing option '%s' from table\n", NONULL (ptr->option)));
1323 mem_free (&ptr->option);
1325 mem_free (&ptr->init);
1329 /* if additional data more == 1, we want to resolve synonyms */
1330 static void mutt_restore_default (const char* name, void* p,
1331 unsigned long more) {
1332 char errbuf[STRING];
1333 struct option_t* ptr = (struct option_t*) p;
1335 if (DTYPE (ptr->type) == DT_SYN) {
1338 ptr = hash_find (ConfigOptions, (char*) ptr->data);
1342 if (FuncTable[DTYPE (ptr->type)].opt_from_string (ptr, ptr->init, errbuf,
1343 sizeof (errbuf)) < 0) {
1345 fprintf (stderr, _("Invalid default setting found. Please report this "
1346 "error:\n\"%s\"\n"), errbuf);
1350 if (ptr->flags & R_INDEX)
1351 set_option (OPTFORCEREDRAWINDEX);
1352 if (ptr->flags & R_PAGER)
1353 set_option (OPTFORCEREDRAWPAGER);
1354 if (ptr->flags & R_RESORT_SUB)
1355 set_option (OPTSORTSUBTHREADS);
1356 if (ptr->flags & R_RESORT)
1357 set_option (OPTNEEDRESORT);
1358 if (ptr->flags & R_RESORT_INIT)
1359 set_option (OPTRESORTINIT);
1360 if (ptr->flags & R_TREE)
1361 set_option (OPTREDRAWTREE);
1364 /* check whether value for $dsn_return would be valid */
1365 static int check_dsn_return (const char* option, unsigned long p,
1366 char* errbuf, size_t errlen) {
1367 char* val = (char*) p;
1368 if (val && *val && str_ncmp (val, "hdrs", 4) != 0 &&
1369 str_ncmp (val, "full", 4) != 0) {
1371 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1377 /* check whether value for $dsn_notify would be valid */
1378 static int check_dsn_notify (const char* option, unsigned long p,
1379 char* errbuf, size_t errlen) {
1380 list2_t* list = NULL;
1382 char* val = (char*) p;
1386 list = list_from_str (val, ",");
1387 if (list_empty (list))
1390 for (i = 0; i < list->length; i++)
1391 if (str_ncmp (list->data[i], "never", 5) != 0 &&
1392 str_ncmp (list->data[i], "failure", 7) != 0 &&
1393 str_ncmp (list->data[i], "delay", 5) != 0 &&
1394 str_ncmp (list->data[i], "success", 7) != 0) {
1396 snprintf (errbuf, errlen, _("'%s' is invalid for $%s"),
1397 (char*) list->data[i], "dsn_notify");
1401 list_del (&list, (list_del_t*) _mem_free);
1405 static int check_num (const char* option, unsigned long p,
1406 char* errbuf, size_t errlen) {
1409 snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1415 static int check_history (const char* option, unsigned long p,
1416 char* errbuf, size_t errlen) {
1417 if (!check_num ("history", p, errbuf, errlen))
1419 mutt_init_history ();
1423 static int check_special (const char* name, unsigned long val,
1424 char* errbuf, size_t errlen) {
1427 for (i = 0; SpecialVars[i].name; i++) {
1428 if (str_cmp (SpecialVars[i].name, name) == 0) {
1429 return (SpecialVars[i].check (SpecialVars[i].name,
1430 val, errbuf, errlen));
1436 static const struct mapping_t* get_sortmap (struct option_t* option) {
1437 const struct mapping_t* map = NULL;
1439 switch (option->type & DT_SUBTYPE_MASK) {
1441 map = SortAliasMethods;
1443 case DT_SORT_BROWSER:
1444 map = SortBrowserMethods;
1447 if ((WithCrypto & APPLICATION_PGP))
1448 map = SortKeyMethods;
1451 map = SortAuxMethods;
1460 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1463 int query, unset, inv, reset, r = 0;
1464 struct option_t* option = NULL;
1466 while (MoreArgs (s)) {
1467 /* reset state variables */
1469 unset = data & M_SET_UNSET;
1470 inv = data & M_SET_INV;
1471 reset = data & M_SET_RESET;
1473 if (*s->dptr == '?') {
1477 else if (str_ncmp ("no", s->dptr, 2) == 0) {
1481 else if (str_ncmp ("inv", s->dptr, 3) == 0) {
1485 else if (*s->dptr == '&') {
1490 /* get the variable name */
1491 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1493 /* resolve synonyms */
1494 if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1495 DTYPE (option->type == DT_SYN)) {
1496 struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1497 syn_add (newopt, option);
1501 /* see if we need to add $user_ var */
1502 if (!option && ascii_strncmp ("user_", tmp->data, 5) == 0) {
1503 /* there's no option named like this yet so only add one
1504 * if the action isn't any of: reset, unset, query */
1505 if (!(reset || unset || query || *s->dptr != '=')) {
1506 debug_print (1, ("adding user option '%s'\n", tmp->data));
1507 option = add_user_option (tmp->data);
1508 hash_insert (ConfigOptions, option->option, option, 0);
1512 if (!option && !(reset && str_cmp ("all", tmp->data) == 0)) {
1513 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1519 if (query || unset || inv) {
1520 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1524 if (s && *s->dptr == '=') {
1525 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1529 if (!str_cmp ("all", tmp->data)) {
1530 hash_map (ConfigOptions, mutt_restore_default, 1);
1534 mutt_restore_default (NULL, option, 1);
1536 else if (DTYPE (option->type) == DT_BOOL) {
1537 /* XXX this currently ignores the function table
1538 * as we don't get invert and stuff into it */
1539 if (s && *s->dptr == '=') {
1540 if (unset || inv || query) {
1541 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1546 mutt_extract_token (tmp, s, 0);
1547 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1549 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1552 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1558 bool_to_string (err->data, err->dsize, option);
1563 unset_option (option->data);
1565 toggle_option (option->data);
1567 set_option (option->data);
1569 else if (DTYPE (option->type) == DT_STR ||
1570 DTYPE (option->type) == DT_PATH ||
1571 DTYPE (option->type) == DT_ADDR ||
1572 DTYPE (option->type) == DT_MAGIC ||
1573 DTYPE (option->type) == DT_NUM ||
1574 DTYPE (option->type) == DT_SORT ||
1575 DTYPE (option->type) == DT_RX ||
1576 DTYPE (option->type) == DT_USER) {
1578 /* XXX maybe we need to get unset into handlers? */
1579 if (DTYPE (option->type) == DT_STR ||
1580 DTYPE (option->type) == DT_PATH ||
1581 DTYPE (option->type) == DT_ADDR ||
1582 DTYPE (option->type) == DT_USER) {
1584 if (DTYPE (option->type) == DT_ADDR)
1585 rfc822_free_address ((ADDRESS **) option->data);
1586 else if (DTYPE (option->type) == DT_USER)
1587 /* to unset $user_ means remove */
1588 hash_delete (ConfigOptions, option->option,
1589 option, del_user_option);
1591 mem_free ((void *) option->data);
1596 if (query || *s->dptr != '=') {
1597 FuncTable[DTYPE (option->type)].opt_to_string
1598 (err->data, err->dsize, option);
1603 mutt_extract_token (tmp, s, 0);
1604 if (!FuncTable[DTYPE (option->type)].opt_from_string
1605 (option, tmp->data, err->data, err->dsize))
1608 else if (DTYPE (option->type) == DT_QUAD) {
1611 quad_to_string (err->data, err->dsize, option);
1615 if (*s->dptr == '=') {
1617 mutt_extract_token (tmp, s, 0);
1618 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1619 set_quadoption (option->data, M_YES);
1620 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1621 set_quadoption (option->data, M_NO);
1622 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1623 set_quadoption (option->data, M_ASKYES);
1624 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1625 set_quadoption (option->data, M_ASKNO);
1627 snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1628 tmp->data, option->option);
1635 toggle_quadoption (option->data);
1637 set_quadoption (option->data, M_NO);
1639 set_quadoption (option->data, M_YES);
1643 snprintf (err->data, err->dsize, _("%s: unknown type"),
1649 if (option->flags & R_INDEX)
1650 set_option (OPTFORCEREDRAWINDEX);
1651 if (option->flags & R_PAGER)
1652 set_option (OPTFORCEREDRAWPAGER);
1653 if (option->flags & R_RESORT_SUB)
1654 set_option (OPTSORTSUBTHREADS);
1655 if (option->flags & R_RESORT)
1656 set_option (OPTNEEDRESORT);
1657 if (option->flags & R_RESORT_INIT)
1658 set_option (OPTRESORTINIT);
1659 if (option->flags & R_TREE)
1660 set_option (OPTREDRAWTREE);
1667 /* reads the specified initialization file. returns -1 if errors were found
1668 so that we can pause to let the user know... */
1669 static int source_rc (const char *rcfile, BUFFER * err)
1672 int line = 0, rc = 0, conv = 0;
1674 char *linebuf = NULL;
1675 char *currentline = NULL;
1679 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1681 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1682 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1686 memset (&token, 0, sizeof (token));
1687 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1688 conv = ConfigCharset && (*ConfigCharset) && Charset;
1690 currentline = str_dup (linebuf);
1693 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1696 currentline = linebuf;
1701 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1702 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1703 if (--rc < -MAXERRS) {
1705 mem_free (¤tline);
1714 mem_free (¤tline);
1716 mem_free (&token.data);
1717 mem_free (&linebuf);
1720 mutt_wait_filter (pid);
1722 /* the muttrc source keyword */
1723 snprintf (err->data, err->dsize,
1724 rc >= -MAXERRS ? _("source: errors in %s")
1725 : _("source: reading aborted due too many errors in %s"),
1734 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1737 char path[_POSIX_PATH_MAX];
1741 if (mutt_extract_token (tmp, s, 0) != 0) {
1742 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1746 strfcpy (path, tmp->data, sizeof (path));
1747 mutt_expand_path (path, sizeof (path));
1749 rc += source_rc (path, err);
1751 while (MoreArgs (s));
1753 return ((rc < 0) ? -1 : 0);
1756 /* line command to execute
1758 token scratch buffer to be used by parser. caller should free
1759 token->data when finished. the reason for this variable is
1760 to avoid having to allocate and deallocate a lot of memory
1761 if we are parsing many lines. the caller can pass in the
1762 memory to use, which avoids having to create new space for
1763 every call to this function.
1765 err where to write error messages */
1766 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1771 memset (&expn, 0, sizeof (expn));
1772 expn.data = expn.dptr = line;
1773 expn.dsize = str_len (line);
1778 while (*expn.dptr) {
1779 if (*expn.dptr == '#')
1780 break; /* rest of line is a comment */
1781 if (*expn.dptr == ';') {
1785 mutt_extract_token (token, &expn, 0);
1786 for (i = 0; Commands[i].name; i++) {
1787 if (!str_cmp (token->data, Commands[i].name)) {
1788 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1793 if (!Commands[i].name) {
1794 snprintf (err->data, err->dsize, _("%s: unknown command"),
1795 NONULL (token->data));
1802 mem_free (&expn.data);
1807 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1808 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1809 /* initial string that starts completion. No telling how much crap
1810 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1811 char User_typed[LONG_STRING] = { 0 };
1813 int Num_matched = 0; /* Number of matches for completion */
1814 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1815 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1817 /* helper function for completion. Changes the dest buffer if
1818 necessary/possible to aid completion.
1819 dest == completion result gets here.
1820 src == candidate for completion.
1821 try == user entered data for completion.
1822 len == length of dest buffer.
1824 static void candidate (char *dest, char *try, char *src, int len)
1828 if (strstr (src, try) == src) {
1829 Matches[Num_matched++] = src;
1831 strfcpy (dest, src, len);
1833 for (l = 0; src[l] && src[l] == dest[l]; l++);
1839 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1843 int spaces; /* keep track of the number of leading spaces on the line */
1846 spaces = buffer - pt;
1848 pt = buffer + pos - spaces;
1849 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1852 if (pt == buffer) { /* complete cmd */
1853 /* first TAB. Collect all the matches */
1856 strfcpy (User_typed, pt, sizeof (User_typed));
1857 memset (Matches, 0, sizeof (Matches));
1858 memset (Completed, 0, sizeof (Completed));
1859 for (num = 0; Commands[num].name; num++)
1860 candidate (Completed, User_typed, Commands[num].name,
1861 sizeof (Completed));
1862 Matches[Num_matched++] = User_typed;
1864 /* All matches are stored. Longest non-ambiguous string is ""
1865 * i.e. dont change 'buffer'. Fake successful return this time */
1866 if (User_typed[0] == 0)
1870 if (Completed[0] == 0 && User_typed[0])
1873 /* Num_matched will _always_ be atleast 1 since the initial
1874 * user-typed string is always stored */
1875 if (numtabs == 1 && Num_matched == 2)
1876 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1877 else if (numtabs > 1 && Num_matched > 2)
1878 /* cycle thru all the matches */
1879 snprintf (Completed, sizeof (Completed), "%s",
1880 Matches[(numtabs - 2) % Num_matched]);
1882 /* return the completed command */
1883 strncpy (buffer, Completed, len - spaces);
1885 else if (!str_ncmp (buffer, "set", 3)
1886 || !str_ncmp (buffer, "unset", 5)
1887 || !str_ncmp (buffer, "reset", 5)
1888 || !str_ncmp (buffer, "toggle", 6)) { /* complete variables */
1889 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1892 /* loop through all the possible prefixes (no, inv, ...) */
1893 if (!str_ncmp (buffer, "set", 3)) {
1894 for (num = 0; prefixes[num]; num++) {
1895 if (!str_ncmp (pt, prefixes[num], str_len (prefixes[num]))) {
1896 pt += str_len (prefixes[num]);
1902 /* first TAB. Collect all the matches */
1905 strfcpy (User_typed, pt, sizeof (User_typed));
1906 memset (Matches, 0, sizeof (Matches));
1907 memset (Completed, 0, sizeof (Completed));
1908 for (num = 0; MuttVars[num].option; num++)
1909 candidate (Completed, User_typed, MuttVars[num].option,
1910 sizeof (Completed));
1911 Matches[Num_matched++] = User_typed;
1913 /* All matches are stored. Longest non-ambiguous string is ""
1914 * i.e. dont change 'buffer'. Fake successful return this time */
1915 if (User_typed[0] == 0)
1919 if (Completed[0] == 0 && User_typed[0])
1922 /* Num_matched will _always_ be atleast 1 since the initial
1923 * user-typed string is always stored */
1924 if (numtabs == 1 && Num_matched == 2)
1925 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1926 else if (numtabs > 1 && Num_matched > 2)
1927 /* cycle thru all the matches */
1928 snprintf (Completed, sizeof (Completed), "%s",
1929 Matches[(numtabs - 2) % Num_matched]);
1931 strncpy (pt, Completed, buffer + len - pt - spaces);
1933 else if (!str_ncmp (buffer, "exec", 4)) {
1934 struct binding_t *menu = km_get_table (CurrentMenu);
1936 if (!menu && CurrentMenu != MENU_PAGER)
1940 /* first TAB. Collect all the matches */
1943 strfcpy (User_typed, pt, sizeof (User_typed));
1944 memset (Matches, 0, sizeof (Matches));
1945 memset (Completed, 0, sizeof (Completed));
1946 for (num = 0; menu[num].name; num++)
1947 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1948 /* try the generic menu */
1949 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1951 for (num = 0; menu[num].name; num++)
1952 candidate (Completed, User_typed, menu[num].name,
1953 sizeof (Completed));
1955 Matches[Num_matched++] = User_typed;
1957 /* All matches are stored. Longest non-ambiguous string is ""
1958 * i.e. dont change 'buffer'. Fake successful return this time */
1959 if (User_typed[0] == 0)
1963 if (Completed[0] == 0 && User_typed[0])
1966 /* Num_matched will _always_ be atleast 1 since the initial
1967 * user-typed string is always stored */
1968 if (numtabs == 1 && Num_matched == 2)
1969 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1970 else if (numtabs > 1 && Num_matched > 2)
1971 /* cycle thru all the matches */
1972 snprintf (Completed, sizeof (Completed), "%s",
1973 Matches[(numtabs - 2) % Num_matched]);
1975 strncpy (pt, Completed, buffer + len - pt - spaces);
1983 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1985 char var[STRING], *pt = buffer;
1987 struct option_t* option = NULL;
1993 spaces = buffer - pt;
1995 pt = buffer + pos - spaces;
1996 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1998 pt++; /* move past the space */
1999 if (*pt == '=') /* abort if no var before the '=' */
2002 if (str_ncmp (buffer, "set", 3) == 0) {
2003 strfcpy (var, pt, sizeof (var));
2004 /* ignore the trailing '=' when comparing */
2005 var[str_len (var) - 1] = 0;
2006 if (!(option = hash_find (ConfigOptions, var)))
2007 return 0; /* no such variable. */
2009 char tmp[LONG_STRING], tmp2[LONG_STRING];
2011 size_t dlen = buffer + len - pt - spaces;
2012 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2016 if ((DTYPE (option->type) == DT_STR) ||
2017 (DTYPE (option->type) == DT_PATH) ||
2018 (DTYPE (option->type) == DT_RX)) {
2019 strfcpy (tmp, NONULL (*((char **) option->data)), sizeof (tmp));
2020 if (DTYPE (option->type) == DT_PATH)
2021 mutt_pretty_mailbox (tmp);
2023 else if (DTYPE (option->type) == DT_ADDR) {
2024 rfc822_write_address (tmp, sizeof (tmp),
2025 *((ADDRESS **) option->data), 0);
2027 else if (DTYPE (option->type) == DT_QUAD)
2028 strfcpy (tmp, vals[quadoption (option->data)], sizeof (tmp));
2029 else if (DTYPE (option->type) == DT_NUM)
2030 snprintf (tmp, sizeof (tmp), "%d", (*((short *) option->data)));
2031 else if (DTYPE (option->type) == DT_SORT) {
2032 const struct mapping_t *map;
2035 switch (option->type & DT_SUBTYPE_MASK) {
2037 map = SortAliasMethods;
2039 case DT_SORT_BROWSER:
2040 map = SortBrowserMethods;
2043 if ((WithCrypto & APPLICATION_PGP))
2044 map = SortKeyMethods;
2053 mutt_getnamebyvalue (*((short *) option->data) & SORT_MASK,
2055 snprintf (tmp, sizeof (tmp), "%s%s%s",
2056 (*((short *) option->data) & SORT_REVERSE) ?
2058 (*((short *) option->data) & SORT_LAST) ? "last-" :
2061 else if (DTYPE (option->type) == DT_MAGIC) {
2063 switch (DefaultMagic) {
2079 strfcpy (tmp, p, sizeof (tmp));
2081 else if (DTYPE (option->type) == DT_BOOL)
2082 strfcpy (tmp, option (option->data) ? "yes" : "no",
2087 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
2088 if (*s == '\\' || *s == '"')
2094 strfcpy (tmp, pt, sizeof (tmp));
2095 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2103 /* Implement the -Q command line flag */
2104 int mutt_query_variables (LIST * queries)
2108 char errbuff[STRING];
2109 char command[STRING];
2113 memset (&err, 0, sizeof (err));
2114 memset (&token, 0, sizeof (token));
2117 err.dsize = sizeof (errbuff);
2119 for (p = queries; p; p = p->next) {
2120 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2121 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2122 fprintf (stderr, "%s\n", err.data);
2123 mem_free (&token.data);
2126 printf ("%s\n", err.data);
2129 mem_free (&token.data);
2133 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2137 for (i = 0; map[i].name; i++)
2138 if (map[i].value == val)
2139 return (map[i].name);
2143 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2147 for (i = 0; map[i].name; i++)
2148 if (ascii_strcasecmp (map[i].name, name) == 0)
2149 return (map[i].value);
2153 static int mutt_execute_commands (LIST * p)
2156 char errstr[SHORT_STRING];
2158 memset (&err, 0, sizeof (err));
2160 err.dsize = sizeof (errstr);
2161 memset (&token, 0, sizeof (token));
2162 for (; p; p = p->next) {
2163 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2164 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2165 mem_free (&token.data);
2169 mem_free (&token.data);
2173 void mutt_init (int skip_sys_rc, LIST * commands)
2176 struct utsname utsname;
2177 char *p, buffer[STRING], error[STRING];
2178 int i, default_rc = 0, need_pause = 0;
2181 memset (&err, 0, sizeof (err));
2183 err.dsize = sizeof (error);
2185 /* use 3*sizeof(muttvars) to have some room for $user_ vars */
2186 ConfigOptions = hash_create (sizeof (MuttVars) * 3);
2187 for (i = 0; MuttVars[i].option; i++)
2188 hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2191 * XXX - use something even more difficult to predict?
2193 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2194 "\033]9;%ld\a", (long) time (NULL));
2196 /* on one of the systems I use, getcwd() does not return the same prefix
2197 as is listed in the passwd file */
2198 if ((p = getenv ("HOME")))
2199 Homedir = str_dup (p);
2201 /* Get some information about the user */
2202 if ((pw = getpwuid (getuid ()))) {
2205 Username = str_dup (pw->pw_name);
2207 Homedir = str_dup (pw->pw_dir);
2209 Realname = str_dup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2210 Shell = str_dup (pw->pw_shell);
2215 fputs (_("unable to determine home directory"), stderr);
2218 if ((p = getenv ("USER")))
2219 Username = str_dup (p);
2222 fputs (_("unable to determine username"), stderr);
2225 Shell = str_dup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2228 debug_start(Homedir);
2230 /* And about the host... */
2232 /* some systems report the FQDN instead of just the hostname */
2233 if ((p = strchr (utsname.nodename, '.'))) {
2234 Hostname = str_substrdup (utsname.nodename, p);
2236 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2239 Hostname = str_dup (utsname.nodename);
2242 #define DOMAIN buffer
2243 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2244 Fqdn = str_dup ("@");
2247 if (*DOMAIN != '@') {
2248 Fqdn = mem_malloc (str_len (DOMAIN) + str_len (Hostname) + 2);
2249 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2252 Fqdn = str_dup (NONULL (Hostname));
2259 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2261 fgets (buffer, sizeof (buffer), f);
2262 p = (char*) &buffer;
2265 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2269 NewsServer = str_dup (p);
2273 if ((p = getenv ("NNTPSERVER")))
2274 NewsServer = str_dup (p);
2277 if ((p = getenv ("MAIL")))
2278 Spoolfile = str_dup (p);
2279 else if ((p = getenv ("MAILDIR")))
2280 Spoolfile = str_dup (p);
2283 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2285 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2287 Spoolfile = str_dup (buffer);
2290 if ((p = getenv ("MAILCAPS")))
2291 MailcapPath = str_dup (p);
2293 /* Default search path from RFC1524 */
2295 str_dup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2296 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2299 Tempdir = str_dup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2301 p = getenv ("VISUAL");
2303 p = getenv ("EDITOR");
2307 Editor = str_dup (p);
2308 Visual = str_dup (p);
2310 if ((p = getenv ("REPLYTO")) != NULL) {
2313 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2315 memset (&buf, 0, sizeof (buf));
2316 buf.data = buf.dptr = buffer;
2317 buf.dsize = str_len (buffer);
2319 memset (&token, 0, sizeof (token));
2320 parse_my_hdr (&token, &buf, 0, &err);
2321 mem_free (&token.data);
2324 if ((p = getenv ("EMAIL")) != NULL)
2325 From = rfc822_parse_adrlist (NULL, p);
2327 mutt_set_langinfo_charset ();
2328 mutt_set_charset (Charset);
2331 /* Set standard defaults */
2332 hash_map (ConfigOptions, mutt_set_default, 0);
2333 hash_map (ConfigOptions, mutt_restore_default, 0);
2335 CurrentMenu = MENU_MAIN;
2338 #ifndef LOCALES_HACK
2339 /* Do we have a locale definition? */
2340 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2341 ((p = getenv ("LANG")) != NULL && p[0]) ||
2342 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2343 set_option (OPTLOCALES);
2347 /* Unset suspend by default if we're the session leader */
2348 if (getsid (0) == getpid ())
2349 unset_option (OPTSUSPEND);
2352 mutt_init_history ();
2361 * When changing the code which looks for a configuration file,
2362 * please also change the corresponding code in muttbug.sh.in.
2372 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2374 if (access (buffer, F_OK) == -1)
2376 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2377 if (access (buffer, F_OK) == -1)
2379 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2380 NONULL (Homedir), MUTT_VERSION);
2381 if (access (buffer, F_OK) == -1)
2383 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2387 Muttrc = str_dup (buffer);
2390 strfcpy (buffer, Muttrc, sizeof (buffer));
2392 mutt_expand_path (buffer, sizeof (buffer));
2393 Muttrc = str_dup (buffer);
2395 mem_free (&AliasFile);
2396 AliasFile = str_dup (NONULL (Muttrc));
2398 /* Process the global rc file if it exists and the user hasn't explicity
2399 requested not to via "-n". */
2401 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2403 if (access (buffer, F_OK) == -1)
2404 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2405 if (access (buffer, F_OK) == -1)
2406 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2408 if (access (buffer, F_OK) == -1)
2409 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2410 if (access (buffer, F_OK) != -1) {
2411 if (source_rc (buffer, &err) != 0) {
2412 fputs (err.data, stderr);
2413 fputc ('\n', stderr);
2419 /* Read the user's initialization file. */
2420 if (access (Muttrc, F_OK) != -1) {
2421 if (!option (OPTNOCURSES))
2423 if (source_rc (Muttrc, &err) != 0) {
2424 fputs (err.data, stderr);
2425 fputc ('\n', stderr);
2429 else if (!default_rc) {
2430 /* file specified by -F does not exist */
2431 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2432 mutt_endwin (buffer);
2436 if (mutt_execute_commands (commands) != 0)
2439 /* warn about synonym variables */
2440 if (!list_empty(Synonyms)) {
2442 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2443 for (i = 0; i < Synonyms->length; i++) {
2444 struct option_t* newopt = NULL, *oldopt = NULL;
2445 newopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->n;
2446 oldopt = (struct option_t*) ((syn_t*) Synonyms->data[i])->o;
2447 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2448 oldopt ? NONULL (oldopt->option) : "",
2449 newopt ? NONULL (newopt->option) : "",
2450 NONULL(((syn_t*) Synonyms->data[i])->f),
2451 ((syn_t*) Synonyms->data[i])->l);
2453 fprintf (stderr, _("Warning: synonym variables are scheduled"
2454 " for removal.\n"));
2455 list_del (&Synonyms, syn_del);
2459 if (need_pause && !option (OPTNOCURSES)) {
2460 if (mutt_any_key_to_continue (NULL) == -1)
2465 set_option (OPTWEED); /* turn weeding on by default */
2469 int mutt_get_hook_type (const char *name)
2471 struct command_t *c;
2473 for (c = Commands; c->name; c++)
2474 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2479 /* compare two option_t*'s for sorting -t/-T output */
2480 static int opt_cmp (const void* a, const void* b) {
2481 return (str_cmp ((*(struct option_t**) a)->option,
2482 (*(struct option_t**) b)->option));
2485 /* callback for hash_map() to put all non-synonym vars into list */
2486 static void opt_sel_full (const char* key, void* data,
2487 unsigned long more) {
2488 list2_t** l = (list2_t**) more;
2489 struct option_t* option = (struct option_t*) data;
2491 if (DTYPE (option->type) == DT_SYN)
2493 list_push_back (l, option);
2496 /* callback for hash_map() to put all changed non-synonym vars into list */
2497 static void opt_sel_diff (const char* key, void* data,
2498 unsigned long more) {
2499 list2_t** l = (list2_t**) more;
2500 struct option_t* option = (struct option_t*) data;
2501 char buf[LONG_STRING];
2503 if (DTYPE (option->type) == DT_SYN)
2506 mutt_option_value (option->option, buf, sizeof (buf));
2507 if (str_cmp (buf, option->init) != 0)
2508 list_push_back (l, option);
2511 /* dump out the value of all the variables we have */
2512 int mutt_dump_variables (int full) {
2514 char outbuf[STRING];
2515 list2_t* tmp = NULL;
2516 struct option_t* option = NULL;
2518 /* get all non-synonyms into list... */
2519 hash_map (ConfigOptions, full ? opt_sel_full : opt_sel_diff,
2520 (unsigned long) &tmp);
2522 if (!list_empty(tmp)) {
2523 /* ...and dump list sorted */
2524 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2525 for (i = 0; i < tmp->length; i++) {
2526 option = (struct option_t*) tmp->data[i];
2527 FuncTable[DTYPE (option->type)].opt_to_string
2528 (outbuf, sizeof (outbuf), option);
2529 printf ("%s\n", outbuf);
2532 list_del (&tmp, NULL);