2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 #include "mutt_curses.h"
26 #include "mutt_regex.h"
31 #include "mutt_crypt.h"
32 #include "mutt_idna.h"
34 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
48 #include <sys/utsname.h>
52 void toggle_quadoption (int opt)
55 int b = (opt % 4) * 2;
57 QuadOptions[n] ^= (1 << b);
60 void set_quadoption (int opt, int flag)
63 int b = (opt % 4) * 2;
65 QuadOptions[n] &= ~(0x3 << b);
66 QuadOptions[n] |= (flag & 0x3) << b;
69 int quadoption (int opt)
72 int b = (opt % 4) * 2;
74 return (QuadOptions[n] >> b) & 0x3;
77 int query_quadoption (int opt, const char *prompt)
79 int v = quadoption (opt);
88 v = mutt_yesorno (prompt, (v == M_ASKYES));
89 CLEARLINE (LINES - 1);
96 /* given the variable ``s'', return the index into the rc_vars array which
97 matches, or -1 if the variable is not found. */
98 int mutt_option_index (char *s)
102 for (i = 0; MuttVars[i].option; i++)
103 if (mutt_strcmp (s, MuttVars[i].option) == 0)
104 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
108 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
111 char qc = 0; /* quote char */
114 /* reset the destination pointer to the beginning of the buffer */
115 dest->dptr = dest->data;
118 while ((ch = *tok->dptr))
122 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
123 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
124 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
125 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
126 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
133 qc = 0; /* end of quote */
134 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
136 else if (ch == '\\' && qc != '\'')
139 return -1; /* premature end of token */
140 switch (ch = *tok->dptr++)
145 return -1; /* premature end of token */
146 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
151 mutt_buffer_addch (dest, '\r');
154 mutt_buffer_addch (dest, '\n');
157 mutt_buffer_addch (dest, '\t');
160 mutt_buffer_addch (dest, '\f');
163 mutt_buffer_addch (dest, '\033');
166 if (isdigit ((unsigned char) ch) &&
167 isdigit ((unsigned char) *tok->dptr) &&
168 isdigit ((unsigned char) *(tok->dptr + 1)))
171 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
175 mutt_buffer_addch (dest, ch);
178 else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
181 return -1; /* premature end of token */
184 mutt_buffer_addch (dest, ch);
186 mutt_buffer_addch (dest, '\033');
187 else if (isalpha ((unsigned char) ch))
188 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
191 mutt_buffer_addch (dest, '^');
192 mutt_buffer_addch (dest, ch);
195 else if (ch == '`' && (!qc || qc == '"'))
206 if ((pc = strpbrk (pc, "\\`")))
208 /* skip any quoted chars */
212 } while (pc && *pc != '`');
215 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
218 cmd = mutt_substrdup (tok->dptr, pc);
219 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
221 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
230 memset (&expn, 0, sizeof (expn));
231 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
233 mutt_wait_filter (pid);
235 /* if we got output, make a new string consiting of the shell ouptput
236 plus whatever else was left on the original line */
237 /* BUT: If this is inside a quoted string, directly add output to
241 mutt_buffer_addstr (dest, expn.data);
246 expnlen = mutt_strlen (expn.data);
247 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
248 ptr = safe_malloc (tok->dsize);
249 memcpy (ptr, expn.data, expnlen);
250 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
255 tok->destroy = 1; /* mark that the caller should destroy this data */
260 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
262 char *env = NULL, *var = NULL;
264 if (*tok->dptr == '{')
267 if ((pc = strchr (tok->dptr, '}')))
269 var = mutt_substrdup (tok->dptr, pc);
275 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_'; pc++)
277 var = mutt_substrdup (tok->dptr, pc);
280 if (var && (env = getenv (var)))
281 mutt_buffer_addstr (dest, env);
285 mutt_buffer_addch (dest, ch);
287 mutt_buffer_addch (dest, 0); /* terminate the string */
292 static void add_to_list (LIST **list, const char *str)
294 LIST *t, *last = NULL;
296 /* don't add a NULL or empty string to the list */
297 if (!str || *str == '\0')
300 /* check to make sure the item is not already on this list */
301 for (last = *list; last; last = last->next)
303 if (ascii_strcasecmp (str, last->data) == 0)
305 /* already on the list, so just ignore it */
315 t = (LIST *) safe_calloc (1, sizeof (LIST));
316 t->data = safe_strdup (str);
327 static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
329 RX_LIST *t, *last = NULL;
335 if (!(rx = mutt_compile_regexp (s, flags)))
337 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
341 /* check to make sure the item is not already on this list */
342 for (last = *list; last; last = last->next)
344 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
346 /* already on the list, so just ignore it */
356 t = mutt_new_rx_list();
367 mutt_free_regexp (&rx);
372 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
374 SPAM_LIST *t = NULL, *last = NULL;
379 if (!pat || !*pat || !templ)
382 if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
384 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
388 /* check to make sure the item is not already on this list */
389 for (last = *list; last; last = last->next)
391 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
393 /* Already on the list. Formerly we just skipped this case, but
394 * now we're supporting removals, which means we're supporting
395 * re-adds conceptually. So we probably want this to imply a
396 * removal, then do an add. We can achieve the removal by freeing
397 * the template, and leaving t pointed at the current item.
400 safe_free(&t->template);
407 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
408 * update. Otherwise we want to make a new one to link at the list's end.
412 t = mutt_new_spam_list();
420 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
421 t->template = safe_strdup(templ);
423 /* Find highest match number in template string */
432 while (*p && isdigit((int)*p))
438 t->nmatch++; /* match 0 is always the whole expr */
443 static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
445 SPAM_LIST *spam, *prev;
448 /* Being first is a special case. */
450 if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
453 mutt_free_regexp(&spam->rx);
454 safe_free(&spam->template);
460 for (spam = prev->next; spam;)
462 if (!mutt_strcmp(spam->rx->pattern, pat))
464 prev->next = spam->next;
465 mutt_free_regexp(&spam->rx);
466 safe_free(&spam->template);
479 static void remove_from_list (LIST **l, const char *str)
481 LIST *p, *last = NULL;
483 if (mutt_strcmp ("*", str) == 0)
484 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
491 if (ascii_strcasecmp (str, p->data) == 0)
495 last->next = p->next;
509 static int remove_from_rx_list (RX_LIST **l, const char *str)
511 RX_LIST *p, *last = NULL;
514 if (mutt_strcmp ("*", str) == 0)
516 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
525 if (ascii_strcasecmp (str, p->rx->pattern) == 0)
527 mutt_free_regexp (&p->rx);
529 last->next = p->next;
545 static int parse_ifdef (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
550 memset (&token, 0, sizeof (token));
551 mutt_extract_token (tmp, s, 0);
553 /* is the item defined as a variable or a function? */
554 if (!(res = (mutt_option_index (tmp->data) != -1)))
555 for (i = 0; !res && i < MENU_MAX; i++)
557 struct binding_t *b = km_get_table (Menus[i].value);
562 for (j = 0; b[j].name; j++)
563 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
564 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data)))
573 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
576 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
580 if (mutt_parse_rc_line (tmp->data, &token, err) == -1)
582 mutt_error ("Erreur: %s", err->data);
591 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
595 mutt_extract_token (buf, s, 0);
597 /* don't add "*" to the unignore list */
598 if (strcmp (buf->data, "*"))
599 add_to_list (&UnIgnore, buf->data);
601 remove_from_list (&Ignore, buf->data);
603 while (MoreArgs (s));
608 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
612 mutt_extract_token (buf, s, 0);
613 remove_from_list (&UnIgnore, buf->data);
614 add_to_list (&Ignore, buf->data);
616 while (MoreArgs (s));
621 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
625 mutt_extract_token (buf, s, 0);
626 add_to_list ((LIST **) data, buf->data);
628 while (MoreArgs (s));
633 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
637 mutt_extract_token (buf, s, 0);
638 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
642 while (MoreArgs (s));
647 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
649 return _parse_rx_list (buf, s, data, err, REG_ICASE);
652 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
656 mutt_extract_token (buf, s, 0);
657 if (mutt_strcmp (buf->data, "*") == 0)
659 mutt_free_rx_list ((RX_LIST **) data);
662 remove_from_rx_list ((RX_LIST **) data, buf->data);
664 while (MoreArgs (s));
669 static void _alternates_clean (void)
672 if (Context && Context->msgcount)
674 for (i = 0; i < Context->msgcount; i++)
675 Context->hdrs[i]->recip_valid = 0;
679 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
684 mutt_extract_token (buf, s, 0);
685 remove_from_rx_list (&UnAlternates, buf->data);
687 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
690 while (MoreArgs (s));
695 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
700 mutt_extract_token (buf, s, 0);
701 remove_from_rx_list (&Alternates, buf->data);
703 if (mutt_strcmp (buf->data, "*") &&
704 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
708 while (MoreArgs (s));
713 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
717 memset(&templ, 0, sizeof(templ));
719 /* Insist on at least one parameter */
723 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
725 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
729 /* Extract the first token, a regexp */
730 mutt_extract_token (buf, s, 0);
732 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
735 /* If there's a second parameter, it's a template for the spam tag. */
738 mutt_extract_token (&templ, s, 0);
740 /* Add to the spam list. */
741 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
748 /* If not, try to remove from the nospam list. */
751 remove_from_rx_list(&NoSpamList, buf->data);
757 /* M_NOSPAM is for nospam commands. */
758 else if (data == M_NOSPAM)
760 /* nospam only ever has one parameter. */
762 /* "*" is a special case. */
763 if (!mutt_strcmp(buf->data, "*"))
765 mutt_free_spam_list (&SpamList);
766 mutt_free_rx_list (&NoSpamList);
770 /* If it's on the spam list, just remove it. */
771 if (remove_from_spam_list(&SpamList, buf->data) != 0)
774 /* Otherwise, add it to the nospam list. */
775 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
781 /* This should not happen. */
782 strfcpy(err->data, "This is no good at all.", err->dsize);
786 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
790 mutt_extract_token (buf, s, 0);
792 * Check for deletion of entire list
794 if (mutt_strcmp (buf->data, "*") == 0)
796 mutt_free_list ((LIST **) data);
799 remove_from_list ((LIST **) data, buf->data);
801 while (MoreArgs (s));
806 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
810 mutt_extract_token (buf, s, 0);
811 remove_from_rx_list (&UnMailLists, buf->data);
813 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
816 while (MoreArgs (s));
821 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
825 mutt_extract_token (buf, s, 0);
826 remove_from_rx_list (&SubscribedLists, buf->data);
827 remove_from_rx_list (&MailLists, buf->data);
829 if (mutt_strcmp (buf->data, "*") &&
830 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
833 while (MoreArgs (s));
838 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
842 mutt_extract_token (buf, s, 0);
843 remove_from_rx_list (&UnMailLists, buf->data);
844 remove_from_rx_list (&UnSubscribedLists, buf->data);
846 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
848 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
851 while (MoreArgs (s));
856 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
860 mutt_extract_token (buf, s, 0);
861 remove_from_rx_list (&SubscribedLists, buf->data);
863 if (mutt_strcmp (buf->data, "*") &&
864 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
867 while (MoreArgs (s));
872 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
874 ALIAS *tmp, *last = NULL;
878 mutt_extract_token (buf, s, 0);
880 if (mutt_strcmp ("*", buf->data) == 0)
882 if (CurrentMenu == MENU_ALIAS)
884 for (tmp = Aliases; tmp ; tmp = tmp->next)
886 set_option (OPTFORCEREDRAWINDEX);
889 mutt_free_alias (&Aliases);
893 for (tmp = Aliases; tmp; tmp = tmp->next)
895 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
897 if (CurrentMenu == MENU_ALIAS)
900 set_option (OPTFORCEREDRAWINDEX);
905 last->next = tmp->next;
909 mutt_free_alias (&tmp);
915 while (MoreArgs (s));
919 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
921 ALIAS *tmp = Aliases;
927 strfcpy (err->data, _("alias: no address"), err->dsize);
931 mutt_extract_token (buf, s, 0);
933 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
936 /* check to see if an alias with this name already exists */
937 for (; tmp; tmp = tmp->next)
939 if (!mutt_strcasecmp (tmp->name, buf->data))
946 /* create a new alias */
947 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
949 tmp->name = safe_strdup (buf->data);
950 /* give the main addressbook code a chance */
951 if (CurrentMenu == MENU_ALIAS)
952 set_option (OPTMENUCALLER);
956 /* override the previous value */
957 rfc822_free_address (&tmp->addr);
958 if (CurrentMenu == MENU_ALIAS)
959 set_option (OPTFORCEREDRAWINDEX);
962 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
963 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
965 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
970 if (mutt_addrlist_to_idna (tmp->addr, &estr))
972 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
980 for (a = tmp->addr; a; a = a->next)
983 dprint (2, (debugfile, "parse_alias: %s\n",
986 dprint (2, (debugfile, "parse_alias: Group %s\n",
995 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
998 LIST *tmp = UserHeader;
1004 mutt_extract_token (buf, s, 0);
1005 if (mutt_strcmp ("*", buf->data) == 0)
1006 mutt_free_list (&UserHeader);
1012 l = mutt_strlen (buf->data);
1013 if (buf->data[l - 1] == ':')
1018 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1022 last->next = tmp->next;
1024 UserHeader = tmp->next;
1027 mutt_free_list (&ptr);
1037 while (MoreArgs (s));
1041 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1047 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1048 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1050 strfcpy (err->data, _("invalid header field"), err->dsize);
1053 keylen = p - buf->data + 1;
1057 for (tmp = UserHeader; ; tmp = tmp->next)
1059 /* see if there is already a field by this name */
1060 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1062 /* replace the old value */
1064 tmp->data = buf->data;
1065 memset (buf, 0, sizeof (BUFFER));
1071 tmp->next = mutt_new_list ();
1076 tmp = mutt_new_list ();
1079 tmp->data = buf->data;
1080 memset (buf, 0, sizeof (BUFFER));
1085 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1089 if (mutt_strncmp ("reverse-", s, 8) == 0)
1092 flags = SORT_REVERSE;
1095 if (mutt_strncmp ("last-", s, 5) == 0)
1101 if ((i = mutt_getvaluebyname (s, map)) == -1)
1103 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1112 static void mutt_set_default (struct option_t *p)
1114 switch (p->type & DT_MASK)
1117 if (!p->init && *((char **) p->data))
1118 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1121 if (!p->init && *((char **) p->data))
1123 char *cp = safe_strdup (*((char **) p->data));
1124 /* mutt_pretty_mailbox (cp); */
1125 p->init = (unsigned long) cp;
1129 if (!p->init && *((ADDRESS **) p->data))
1131 char tmp[HUGE_STRING];
1133 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1134 p->init = (unsigned long) safe_strdup (tmp);
1139 REGEXP *pp = (REGEXP *) p->data;
1140 if (!p->init && pp->pattern)
1141 p->init = (unsigned long) safe_strdup (pp->pattern);
1147 static void mutt_restore_default (struct option_t *p)
1149 switch (p->type & DT_MASK)
1153 mutt_str_replace ((char **) p->data, (char *) p->init);
1158 char path[_POSIX_PATH_MAX];
1160 strfcpy (path, (char *) p->init, sizeof (path));
1161 mutt_expand_path (path, sizeof (path));
1162 mutt_str_replace ((char **) p->data, path);
1168 rfc822_free_address ((ADDRESS **) p->data);
1169 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1174 set_option (p->data);
1176 unset_option (p->data);
1179 set_quadoption (p->data, p->init);
1184 *((short *) p->data) = p->init;
1188 REGEXP *pp = (REGEXP *) p->data;
1191 FREE (&pp->pattern);
1200 char *s = (char *) p->init;
1202 pp->rx = safe_calloc (1, sizeof (regex_t));
1203 if (mutt_strcmp (p->option, "mask") != 0)
1204 flags |= mutt_which_case ((const char *) p->init);
1205 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1210 if (REGCOMP (pp->rx, s, flags) != 0)
1212 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1213 p->option, pp->pattern);
1214 FREE (&pp->pattern);
1223 if (p->flags & R_INDEX)
1224 set_option (OPTFORCEREDRAWINDEX);
1225 if (p->flags & R_PAGER)
1226 set_option (OPTFORCEREDRAWPAGER);
1227 if (p->flags & R_RESORT_SUB)
1228 set_option (OPTSORTSUBTHREADS);
1229 if (p->flags & R_RESORT)
1230 set_option (OPTNEEDRESORT);
1231 if (p->flags & R_RESORT_INIT)
1232 set_option (OPTRESORTINIT);
1233 if (p->flags & R_TREE)
1234 set_option (OPTREDRAWTREE);
1237 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1239 int idx, query, unset, inv, reset, r = 0;
1240 char *p, scratch[_POSIX_PATH_MAX];
1242 while (MoreArgs (s))
1244 /* reset state variables */
1246 unset = data & M_SET_UNSET;
1247 inv = data & M_SET_INV;
1248 reset = data & M_SET_RESET;
1250 if (*s->dptr == '?')
1255 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1260 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1265 else if (*s->dptr == '&')
1271 /* get the variable name */
1272 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1274 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1275 !(reset && !mutt_strcmp ("all", tmp->data)))
1277 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1284 if (query || unset || inv)
1286 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1290 if (s && *s->dptr == '=')
1292 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1296 if (!mutt_strcmp ("all", tmp->data))
1298 for (idx = 0; MuttVars[idx].option; idx++)
1299 mutt_restore_default (&MuttVars[idx]);
1303 mutt_restore_default (&MuttVars[idx]);
1305 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1307 if (s && *s->dptr == '=')
1309 if (unset || inv || query)
1311 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1316 mutt_extract_token (tmp, s, 0);
1317 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1319 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1323 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1330 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1331 ? _("%s is set") : _("%s is unset"), tmp->data);
1336 unset_option (MuttVars[idx].data);
1338 toggle_option (MuttVars[idx].data);
1340 set_option (MuttVars[idx].data);
1342 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1343 DTYPE (MuttVars[idx].type) == DT_PATH ||
1344 DTYPE (MuttVars[idx].type) == DT_ADDR)
1348 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1349 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1351 FREE ((void *)MuttVars[idx].data);
1353 else if (query || *s->dptr != '=')
1358 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1361 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1365 val = *((char **) MuttVars[idx].data);
1367 /* user requested the value of this variable */
1368 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1376 /* copy the value of the string */
1377 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1378 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1380 FREE ((void *)MuttVars[idx].data);
1382 mutt_extract_token (tmp, s, 0);
1383 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1385 strfcpy (scratch, tmp->data, sizeof (scratch));
1386 mutt_expand_path (scratch, sizeof (scratch));
1387 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1389 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1391 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1392 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1393 mutt_set_charset (Charset);
1397 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1401 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1403 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1407 if (query || *s->dptr != '=')
1409 /* user requested the value of this variable */
1410 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1411 NONULL (ptr->pattern));
1415 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1417 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1424 /* copy the value of the string */
1425 mutt_extract_token (tmp, s, 0);
1427 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1431 /* $mask is case-sensitive */
1432 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1433 flags |= mutt_which_case (tmp->data);
1436 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1445 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1446 if ((e = REGCOMP (rx, p, flags)) != 0)
1448 regerror (e, rx, err->data, err->dsize);
1454 /* get here only if everything went smootly */
1457 FREE (&ptr->pattern);
1458 regfree ((regex_t *) ptr->rx);
1462 ptr->pattern = safe_strdup (tmp->data);
1466 /* $reply_regexp and $alterantes require special treatment */
1468 if (Context && Context->msgcount &&
1469 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1471 regmatch_t pmatch[1];
1474 #define CUR_ENV Context->hdrs[i]->env
1475 for (i = 0; i < Context->msgcount; i++)
1477 if (CUR_ENV && CUR_ENV->subject)
1479 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1480 CUR_ENV->subject, 1, pmatch, 0)) ?
1482 CUR_ENV->subject + pmatch[0].rm_eo;
1489 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1491 if (query || *s->dptr != '=')
1493 switch (DefaultMagic)
1511 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1517 /* copy the value of the string */
1518 mutt_extract_token (tmp, s, 0);
1519 if (mx_set_magic (tmp->data))
1521 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1526 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1528 short *ptr = (short *) MuttVars[idx].data;
1532 if (query || *s->dptr != '=')
1534 /* user requested the value of this variable */
1535 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1541 mutt_extract_token (tmp, s, 0);
1542 val = strtol (tmp->data, &t, 0);
1544 if (!*tmp->data || *t || (short) val != val)
1546 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1553 /* these ones need a sanity check */
1554 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1558 mutt_init_history ();
1560 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1566 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1570 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1572 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1573 vals [ quadoption (MuttVars[idx].data) ]);
1577 if (*s->dptr == '=')
1580 mutt_extract_token (tmp, s, 0);
1581 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1582 set_quadoption (MuttVars[idx].data, M_YES);
1583 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1584 set_quadoption (MuttVars[idx].data, M_NO);
1585 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1586 set_quadoption (MuttVars[idx].data, M_ASKYES);
1587 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1588 set_quadoption (MuttVars[idx].data, M_ASKNO);
1591 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1599 toggle_quadoption (MuttVars[idx].data);
1601 set_quadoption (MuttVars[idx].data, M_NO);
1603 set_quadoption (MuttVars[idx].data, M_YES);
1606 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1608 const struct mapping_t *map = NULL;
1610 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1613 map = SortAliasMethods;
1615 case DT_SORT_BROWSER:
1616 map = SortBrowserMethods;
1619 if ((WithCrypto & APPLICATION_PGP))
1620 map = SortKeyMethods;
1623 map = SortAuxMethods;
1632 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1637 if (query || *s->dptr != '=')
1639 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1641 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1642 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1643 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1648 mutt_extract_token (tmp, s , 0);
1650 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1658 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1663 if (MuttVars[idx].flags & R_INDEX)
1664 set_option (OPTFORCEREDRAWINDEX);
1665 if (MuttVars[idx].flags & R_PAGER)
1666 set_option (OPTFORCEREDRAWPAGER);
1667 if (MuttVars[idx].flags & R_RESORT_SUB)
1668 set_option (OPTSORTSUBTHREADS);
1669 if (MuttVars[idx].flags & R_RESORT)
1670 set_option (OPTNEEDRESORT);
1671 if (MuttVars[idx].flags & R_RESORT_INIT)
1672 set_option (OPTRESORTINIT);
1673 if (MuttVars[idx].flags & R_TREE)
1674 set_option (OPTREDRAWTREE);
1681 /* reads the specified initialization file. returns -1 if errors were found
1682 so that we can pause to let the user know... */
1683 static int source_rc (const char *rcfile, BUFFER *err)
1686 int line = 0, rc = 0, conv = 0;
1688 char *linebuf = NULL;
1689 char *currentline = NULL;
1693 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1696 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1698 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1702 memset (&token, 0, sizeof (token));
1703 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1705 conv=ConfigCharset && (*ConfigCharset) && Charset;
1708 currentline=safe_strdup(linebuf);
1709 if (!currentline) continue;
1710 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1713 currentline=linebuf;
1715 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1717 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1718 if (--rc < -MAXERRS)
1720 if (conv) FREE(¤tline);
1736 mutt_wait_filter (pid);
1739 /* the muttrc source keyword */
1740 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1741 : _("source: reading aborted due too many errors in %s"), rcfile);
1749 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1751 char path[_POSIX_PATH_MAX];
1756 if (mutt_extract_token (tmp, s, 0) != 0)
1758 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1762 strfcpy (path, tmp->data, sizeof (path));
1763 mutt_expand_path (path, sizeof (path));
1765 rc += source_rc (path, err);
1767 while (MoreArgs (s));
1769 return ((rc < 0) ? -1 : 0);
1772 /* line command to execute
1774 token scratch buffer to be used by parser. caller should free
1775 token->data when finished. the reason for this variable is
1776 to avoid having to allocate and deallocate a lot of memory
1777 if we are parsing many lines. the caller can pass in the
1778 memory to use, which avoids having to create new space for
1779 every call to this function.
1781 err where to write error messages */
1782 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1787 memset (&expn, 0, sizeof (expn));
1788 expn.data = expn.dptr = line;
1789 expn.dsize = mutt_strlen (line);
1796 if (*expn.dptr == '#')
1797 break; /* rest of line is a comment */
1798 if (*expn.dptr == ';')
1803 mutt_extract_token (token, &expn, 0);
1804 for (i = 0; Commands[i].name; i++)
1806 if (!mutt_strcmp (token->data, Commands[i].name))
1808 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1813 if (!Commands[i].name)
1815 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1827 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1828 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1829 /* initial string that starts completion. No telling how much crap
1830 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1831 char User_typed [LONG_STRING] = {0};
1833 int Num_matched = 0; /* Number of matches for completion */
1834 char Completed [STRING] = {0}; /* completed string (command or variable) */
1835 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1837 /* helper function for completion. Changes the dest buffer if
1838 necessary/possible to aid completion.
1839 dest == completion result gets here.
1840 src == candidate for completion.
1841 try == user entered data for completion.
1842 len == length of dest buffer.
1844 static void candidate (char *dest, char *try, char *src, int len)
1848 if (strstr (src, try) == src)
1850 Matches[Num_matched++] = src;
1852 strfcpy (dest, src, len);
1855 for (l = 0; src[l] && src[l] == dest[l]; l++);
1861 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1865 int spaces; /* keep track of the number of leading spaces on the line */
1868 spaces = buffer - pt;
1870 pt = buffer + pos - spaces;
1871 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1874 if (pt == buffer) /* complete cmd */
1876 /* first TAB. Collect all the matches */
1880 strfcpy (User_typed, pt, sizeof (User_typed));
1881 memset (Matches, 0, sizeof (Matches));
1882 memset (Completed, 0, sizeof (Completed));
1883 for (num = 0; Commands[num].name; num++)
1884 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1885 Matches[Num_matched++] = User_typed;
1887 /* All matches are stored. Longest non-ambiguous string is ""
1888 * i.e. dont change 'buffer'. Fake successful return this time */
1889 if (User_typed[0] == 0)
1893 if (Completed[0] == 0 && User_typed[0])
1896 /* Num_matched will _always_ be atleast 1 since the initial
1897 * user-typed string is always stored */
1898 if (numtabs == 1 && Num_matched == 2)
1899 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1900 else if (numtabs > 1 && Num_matched > 2)
1901 /* cycle thru all the matches */
1902 snprintf(Completed, sizeof(Completed), "%s",
1903 Matches[(numtabs - 2) % Num_matched]);
1905 /* return the completed command */
1906 strncpy (buffer, Completed, len - spaces);
1908 else if (!mutt_strncmp (buffer, "set", 3)
1909 || !mutt_strncmp (buffer, "unset", 5)
1910 || !mutt_strncmp (buffer, "reset", 5)
1911 || !mutt_strncmp (buffer, "toggle", 6))
1912 { /* complete variables */
1913 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1916 /* loop through all the possible prefixes (no, inv, ...) */
1917 if (!mutt_strncmp (buffer, "set", 3))
1919 for (num = 0; prefixes[num]; num++)
1921 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1923 pt += mutt_strlen (prefixes[num]);
1929 /* first TAB. Collect all the matches */
1933 strfcpy (User_typed, pt, sizeof (User_typed));
1934 memset (Matches, 0, sizeof (Matches));
1935 memset (Completed, 0, sizeof (Completed));
1936 for (num = 0; MuttVars[num].option; num++)
1937 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1938 Matches[Num_matched++] = User_typed;
1940 /* All matches are stored. Longest non-ambiguous string is ""
1941 * i.e. dont change 'buffer'. Fake successful return this time */
1942 if (User_typed[0] == 0)
1946 if (Completed[0] == 0 && User_typed[0])
1949 /* Num_matched will _always_ be atleast 1 since the initial
1950 * user-typed string is always stored */
1951 if (numtabs == 1 && Num_matched == 2)
1952 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1953 else if (numtabs > 1 && Num_matched > 2)
1954 /* cycle thru all the matches */
1955 snprintf(Completed, sizeof(Completed), "%s",
1956 Matches[(numtabs - 2) % Num_matched]);
1958 strncpy (pt, Completed, buffer + len - pt - spaces);
1960 else if (!mutt_strncmp (buffer, "exec", 4))
1962 struct binding_t *menu = km_get_table (CurrentMenu);
1964 if (!menu && CurrentMenu != MENU_PAGER)
1968 /* first TAB. Collect all the matches */
1972 strfcpy (User_typed, pt, sizeof (User_typed));
1973 memset (Matches, 0, sizeof (Matches));
1974 memset (Completed, 0, sizeof (Completed));
1975 for (num = 0; menu[num].name; num++)
1976 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1977 /* try the generic menu */
1978 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
1981 for (num = 0; menu[num].name; num++)
1982 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1984 Matches[Num_matched++] = User_typed;
1986 /* All matches are stored. Longest non-ambiguous string is ""
1987 * i.e. dont change 'buffer'. Fake successful return this time */
1988 if (User_typed[0] == 0)
1992 if (Completed[0] == 0 && User_typed[0])
1995 /* Num_matched will _always_ be atleast 1 since the initial
1996 * user-typed string is always stored */
1997 if (numtabs == 1 && Num_matched == 2)
1998 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1999 else if (numtabs > 1 && Num_matched > 2)
2000 /* cycle thru all the matches */
2001 snprintf(Completed, sizeof(Completed), "%s",
2002 Matches[(numtabs - 2) % Num_matched]);
2004 strncpy (pt, Completed, buffer + len - pt - spaces);
2012 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2014 char var[STRING], *pt = buffer;
2021 spaces = buffer - pt;
2023 pt = buffer + pos - spaces;
2024 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2026 pt++; /* move past the space */
2027 if (*pt == '=') /* abort if no var before the '=' */
2030 if (mutt_strncmp (buffer, "set", 3) == 0)
2033 strfcpy (var, pt, sizeof (var));
2034 /* ignore the trailing '=' when comparing */
2035 var[mutt_strlen (var) - 1] = 0;
2036 if ((idx = mutt_option_index (var)) == -1)
2037 return 0; /* no such variable. */
2040 char tmp [LONG_STRING], tmp2[LONG_STRING];
2042 size_t dlen = buffer + len - pt - spaces;
2043 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2047 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2048 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2049 (DTYPE(MuttVars[idx].type) == DT_RX))
2051 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2052 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2053 mutt_pretty_mailbox (tmp);
2055 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2057 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2059 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2060 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2061 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2062 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2063 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2065 const struct mapping_t *map;
2068 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2071 map = SortAliasMethods;
2073 case DT_SORT_BROWSER:
2074 map = SortBrowserMethods;
2077 if ((WithCrypto & APPLICATION_PGP))
2078 map = SortKeyMethods;
2086 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2087 snprintf (tmp, sizeof (tmp), "%s%s%s",
2088 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2089 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2092 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2093 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2097 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2099 if (*s == '\\' || *s == '"')
2105 strfcpy (tmp, pt, sizeof (tmp));
2106 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2114 /* Implement the -Q command line flag */
2115 int mutt_query_variables (LIST *queries)
2119 char errbuff[STRING];
2120 char command[STRING];
2124 memset (&err, 0, sizeof (err));
2125 memset (&token, 0, sizeof (token));
2128 err.dsize = sizeof (errbuff);
2130 for (p = queries; p; p = p->next)
2132 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2133 if (mutt_parse_rc_line (command, &token, &err) == -1)
2135 fprintf (stderr, "%s\n", err.data);
2139 printf ("%s\n", err.data);
2146 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2150 for (i=0; map[i].name; i++)
2151 if (map[i].value == val)
2152 return (map[i].name);
2156 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2160 for (i = 0; map[i].name; i++)
2161 if (ascii_strcasecmp (map[i].name, name) == 0)
2162 return (map[i].value);
2167 static void start_debug (void)
2171 char buf[_POSIX_PATH_MAX];
2172 char buf2[_POSIX_PATH_MAX];
2174 /* rotate the old debug logs */
2175 for (i=3; i>=0; i--)
2177 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2178 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2181 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2184 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2185 fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2186 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2191 static int mutt_execute_commands (LIST *p)
2194 char errstr[SHORT_STRING];
2196 memset (&err, 0, sizeof (err));
2198 err.dsize = sizeof (errstr);
2199 memset (&token, 0, sizeof (token));
2200 for (; p; p = p->next)
2202 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2204 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2213 void mutt_init (int skip_sys_rc, LIST *commands)
2216 struct utsname utsname;
2217 char *p, buffer[STRING], error[STRING];
2218 int i, default_rc = 0, need_pause = 0;
2221 memset (&err, 0, sizeof (err));
2223 err.dsize = sizeof (error);
2226 * XXX - use something even more difficult to predict?
2228 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2229 "\033]9;%ld\a", (long) time (NULL));
2231 /* on one of the systems I use, getcwd() does not return the same prefix
2232 as is listed in the passwd file */
2233 if ((p = getenv ("HOME")))
2234 Homedir = safe_strdup (p);
2236 /* Get some information about the user */
2237 if ((pw = getpwuid (getuid ())))
2241 Username = safe_strdup (pw->pw_name);
2243 Homedir = safe_strdup (pw->pw_dir);
2245 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2246 Shell = safe_strdup (pw->pw_shell);
2253 fputs (_("unable to determine home directory"), stderr);
2256 if ((p = getenv ("USER")))
2257 Username = safe_strdup (p);
2261 fputs (_("unable to determine username"), stderr);
2264 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2268 /* Start up debugging mode if requested */
2273 /* And about the host... */
2275 /* some systems report the FQDN instead of just the hostname */
2276 if ((p = strchr (utsname.nodename, '.')))
2278 Hostname = mutt_substrdup (utsname.nodename, p);
2280 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2283 Hostname = safe_strdup (utsname.nodename);
2286 #define DOMAIN buffer
2287 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2288 Fqdn = safe_strdup ("@");
2293 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2294 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2297 Fqdn = safe_strdup(NONULL(Hostname));
2304 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2307 fgets (buffer, sizeof (buffer), f);
2311 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2313 NewsServer = safe_strdup (p);
2317 if ((p = getenv ("NNTPSERVER")))
2318 NewsServer = safe_strdup (p);
2321 if ((p = getenv ("MAIL")))
2322 Spoolfile = safe_strdup (p);
2323 else if ((p = getenv ("MAILDIR")))
2324 Spoolfile = safe_strdup (p);
2328 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2330 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2332 Spoolfile = safe_strdup (buffer);
2335 if ((p = getenv ("MAILCAPS")))
2336 MailcapPath = safe_strdup (p);
2339 /* Default search path from RFC1524 */
2340 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2343 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2345 p = getenv ("VISUAL");
2348 p = getenv ("EDITOR");
2352 Editor = safe_strdup (p);
2353 Visual = safe_strdup (p);
2355 if ((p = getenv ("REPLYTO")) != NULL)
2359 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2361 memset (&buf, 0, sizeof (buf));
2362 buf.data = buf.dptr = buffer;
2363 buf.dsize = mutt_strlen (buffer);
2365 memset (&token, 0, sizeof (token));
2366 parse_my_hdr (&token, &buf, 0, &err);
2370 if ((p = getenv ("EMAIL")) != NULL)
2371 From = rfc822_parse_adrlist (NULL, p);
2373 mutt_set_langinfo_charset ();
2374 mutt_set_charset (Charset);
2377 /* Set standard defaults */
2378 for (i = 0; MuttVars[i].option; i++)
2380 mutt_set_default (&MuttVars[i]);
2381 mutt_restore_default (&MuttVars[i]);
2384 CurrentMenu = MENU_MAIN;
2387 #ifndef LOCALES_HACK
2388 /* Do we have a locale definition? */
2389 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2390 ((p = getenv ("LANG")) != NULL && p[0]) ||
2391 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2392 set_option (OPTLOCALES);
2396 /* Unset suspend by default if we're the session leader */
2397 if (getsid(0) == getpid())
2398 unset_option (OPTSUSPEND);
2401 mutt_init_history ();
2410 * When changing the code which looks for a configuration file,
2411 * please also change the corresponding code in muttbug.sh.in.
2421 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2422 if (access(buffer, F_OK) == -1)
2423 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2424 if (access(buffer, F_OK) == -1)
2425 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2426 if (access(buffer, F_OK) == -1)
2427 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2430 Muttrc = safe_strdup (buffer);
2434 strfcpy (buffer, Muttrc, sizeof (buffer));
2436 mutt_expand_path (buffer, sizeof (buffer));
2437 Muttrc = safe_strdup (buffer);
2440 AliasFile = safe_strdup (NONULL(Muttrc));
2442 /* Process the global rc file if it exists and the user hasn't explicity
2443 requested not to via "-n". */
2446 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2447 if (access (buffer, F_OK) == -1)
2448 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2449 if (access (buffer, F_OK) == -1)
2450 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2451 if (access (buffer, F_OK) == -1)
2452 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2453 if (access (buffer, F_OK) != -1)
2455 if (source_rc (buffer, &err) != 0)
2457 fputs (err.data, stderr);
2458 fputc ('\n', stderr);
2464 /* Read the user's initialization file. */
2465 if (access (Muttrc, F_OK) != -1)
2467 if (!option (OPTNOCURSES))
2469 if (source_rc (Muttrc, &err) != 0)
2471 fputs (err.data, stderr);
2472 fputc ('\n', stderr);
2476 else if (!default_rc)
2478 /* file specified by -F does not exist */
2479 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2480 mutt_endwin (buffer);
2484 if (mutt_execute_commands (commands) != 0)
2487 if (need_pause && !option (OPTNOCURSES))
2489 if (mutt_any_key_to_continue (NULL) == -1)
2494 set_option (OPTWEED); /* turn weeding on by default */
2498 int mutt_get_hook_type (const char *name)
2500 struct command_t *c;
2502 for (c = Commands ; c->name ; c++)
2503 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)