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)))
570 /* check for feature_* */
574 j = mutt_strlen (tmp->data);
575 /* need at least input of 'feature_X' */
579 while (Features[i].name) {
580 if (mutt_strlen (Features[i].name) == j &&
581 ascii_strncasecmp (Features[i].name, p, j)) {
593 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
595 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
598 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
600 if ((data && res) || (!data && !res))
602 if (mutt_parse_rc_line (tmp->data, &token, err) == -1)
604 mutt_error ("Error: %s", err->data);
613 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
617 mutt_extract_token (buf, s, 0);
619 /* don't add "*" to the unignore list */
620 if (strcmp (buf->data, "*"))
621 add_to_list (&UnIgnore, buf->data);
623 remove_from_list (&Ignore, buf->data);
625 while (MoreArgs (s));
630 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
634 mutt_extract_token (buf, s, 0);
635 remove_from_list (&UnIgnore, buf->data);
636 add_to_list (&Ignore, buf->data);
638 while (MoreArgs (s));
643 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
647 mutt_extract_token (buf, s, 0);
648 add_to_list ((LIST **) data, buf->data);
650 while (MoreArgs (s));
656 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
660 mutt_extract_token (buf, s, 0);
661 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
665 while (MoreArgs (s));
670 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
672 return _parse_rx_list (buf, s, data, err, REG_ICASE);
675 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
679 mutt_extract_token (buf, s, 0);
680 if (mutt_strcmp (buf->data, "*") == 0)
682 mutt_free_rx_list ((RX_LIST **) data);
685 remove_from_rx_list ((RX_LIST **) data, buf->data);
687 while (MoreArgs (s));
693 static void _alternates_clean (void)
696 if (Context && Context->msgcount)
698 for (i = 0; i < Context->msgcount; i++)
699 Context->hdrs[i]->recip_valid = 0;
703 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
708 mutt_extract_token (buf, s, 0);
709 remove_from_rx_list (&UnAlternates, buf->data);
711 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
714 while (MoreArgs (s));
719 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
724 mutt_extract_token (buf, s, 0);
725 remove_from_rx_list (&Alternates, buf->data);
727 if (mutt_strcmp (buf->data, "*") &&
728 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
732 while (MoreArgs (s));
737 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
741 memset(&templ, 0, sizeof(templ));
743 /* Insist on at least one parameter */
747 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
749 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
753 /* Extract the first token, a regexp */
754 mutt_extract_token (buf, s, 0);
756 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
759 /* If there's a second parameter, it's a template for the spam tag. */
762 mutt_extract_token (&templ, s, 0);
764 /* Add to the spam list. */
765 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
772 /* If not, try to remove from the nospam list. */
775 remove_from_rx_list(&NoSpamList, buf->data);
781 /* M_NOSPAM is for nospam commands. */
782 else if (data == M_NOSPAM)
784 /* nospam only ever has one parameter. */
786 /* "*" is a special case. */
787 if (!mutt_strcmp(buf->data, "*"))
789 mutt_free_spam_list (&SpamList);
790 mutt_free_rx_list (&NoSpamList);
794 /* If it's on the spam list, just remove it. */
795 if (remove_from_spam_list(&SpamList, buf->data) != 0)
798 /* Otherwise, add it to the nospam list. */
799 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
805 /* This should not happen. */
806 strfcpy(err->data, "This is no good at all.", err->dsize);
810 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
814 mutt_extract_token (buf, s, 0);
816 * Check for deletion of entire list
818 if (mutt_strcmp (buf->data, "*") == 0)
820 mutt_free_list ((LIST **) data);
823 remove_from_list ((LIST **) data, buf->data);
825 while (MoreArgs (s));
830 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
834 mutt_extract_token (buf, s, 0);
835 remove_from_rx_list (&UnMailLists, buf->data);
837 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
840 while (MoreArgs (s));
845 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
849 mutt_extract_token (buf, s, 0);
850 remove_from_rx_list (&SubscribedLists, buf->data);
851 remove_from_rx_list (&MailLists, buf->data);
853 if (mutt_strcmp (buf->data, "*") &&
854 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
857 while (MoreArgs (s));
862 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
866 mutt_extract_token (buf, s, 0);
867 remove_from_rx_list (&UnMailLists, buf->data);
868 remove_from_rx_list (&UnSubscribedLists, buf->data);
870 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
872 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
875 while (MoreArgs (s));
880 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
884 mutt_extract_token (buf, s, 0);
885 remove_from_rx_list (&SubscribedLists, buf->data);
887 if (mutt_strcmp (buf->data, "*") &&
888 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
891 while (MoreArgs (s));
896 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
898 ALIAS *tmp, *last = NULL;
902 mutt_extract_token (buf, s, 0);
904 if (mutt_strcmp ("*", buf->data) == 0)
906 if (CurrentMenu == MENU_ALIAS)
908 for (tmp = Aliases; tmp ; tmp = tmp->next)
910 set_option (OPTFORCEREDRAWINDEX);
913 mutt_free_alias (&Aliases);
917 for (tmp = Aliases; tmp; tmp = tmp->next)
919 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
921 if (CurrentMenu == MENU_ALIAS)
924 set_option (OPTFORCEREDRAWINDEX);
929 last->next = tmp->next;
933 mutt_free_alias (&tmp);
939 while (MoreArgs (s));
943 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
945 ALIAS *tmp = Aliases;
951 strfcpy (err->data, _("alias: no address"), err->dsize);
955 mutt_extract_token (buf, s, 0);
957 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
960 /* check to see if an alias with this name already exists */
961 for (; tmp; tmp = tmp->next)
963 if (!mutt_strcasecmp (tmp->name, buf->data))
970 /* create a new alias */
971 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
973 tmp->name = safe_strdup (buf->data);
974 /* give the main addressbook code a chance */
975 if (CurrentMenu == MENU_ALIAS)
976 set_option (OPTMENUCALLER);
980 /* override the previous value */
981 rfc822_free_address (&tmp->addr);
982 if (CurrentMenu == MENU_ALIAS)
983 set_option (OPTFORCEREDRAWINDEX);
986 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
987 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
989 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
994 if (mutt_addrlist_to_idna (tmp->addr, &estr))
996 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
1001 if (debuglevel >= 2)
1004 for (a = tmp->addr; a; a = a->next)
1007 dprint (2, (debugfile, "parse_alias: %s\n",
1010 dprint (2, (debugfile, "parse_alias: Group %s\n",
1019 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1022 LIST *tmp = UserHeader;
1028 mutt_extract_token (buf, s, 0);
1029 if (mutt_strcmp ("*", buf->data) == 0)
1030 mutt_free_list (&UserHeader);
1036 l = mutt_strlen (buf->data);
1037 if (buf->data[l - 1] == ':')
1042 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1046 last->next = tmp->next;
1048 UserHeader = tmp->next;
1051 mutt_free_list (&ptr);
1061 while (MoreArgs (s));
1065 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1071 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1072 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1074 strfcpy (err->data, _("invalid header field"), err->dsize);
1077 keylen = p - buf->data + 1;
1081 for (tmp = UserHeader; ; tmp = tmp->next)
1083 /* see if there is already a field by this name */
1084 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1086 /* replace the old value */
1088 tmp->data = buf->data;
1089 memset (buf, 0, sizeof (BUFFER));
1095 tmp->next = mutt_new_list ();
1100 tmp = mutt_new_list ();
1103 tmp->data = buf->data;
1104 memset (buf, 0, sizeof (BUFFER));
1109 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1113 if (mutt_strncmp ("reverse-", s, 8) == 0)
1116 flags = SORT_REVERSE;
1119 if (mutt_strncmp ("last-", s, 5) == 0)
1125 if ((i = mutt_getvaluebyname (s, map)) == -1)
1127 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1136 static void mutt_set_default (struct option_t *p)
1138 switch (p->type & DT_MASK)
1141 if (!p->init && *((char **) p->data))
1142 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1145 if (!p->init && *((char **) p->data))
1147 char *cp = safe_strdup (*((char **) p->data));
1148 /* mutt_pretty_mailbox (cp); */
1149 p->init = (unsigned long) cp;
1153 if (!p->init && *((ADDRESS **) p->data))
1155 char tmp[HUGE_STRING];
1157 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1158 p->init = (unsigned long) safe_strdup (tmp);
1163 REGEXP *pp = (REGEXP *) p->data;
1164 if (!p->init && pp->pattern)
1165 p->init = (unsigned long) safe_strdup (pp->pattern);
1171 static void mutt_restore_default (struct option_t *p)
1173 switch (p->type & DT_MASK)
1177 mutt_str_replace ((char **) p->data, (char *) p->init);
1182 char path[_POSIX_PATH_MAX];
1184 strfcpy (path, (char *) p->init, sizeof (path));
1185 mutt_expand_path (path, sizeof (path));
1186 mutt_str_replace ((char **) p->data, path);
1192 rfc822_free_address ((ADDRESS **) p->data);
1193 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1198 set_option (p->data);
1200 unset_option (p->data);
1203 set_quadoption (p->data, p->init);
1208 *((short *) p->data) = p->init;
1212 REGEXP *pp = (REGEXP *) p->data;
1215 FREE (&pp->pattern);
1224 char *s = (char *) p->init;
1226 pp->rx = safe_calloc (1, sizeof (regex_t));
1227 if (mutt_strcmp (p->option, "mask") != 0)
1228 flags |= mutt_which_case ((const char *) p->init);
1229 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1234 if (REGCOMP (pp->rx, s, flags) != 0)
1236 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1237 p->option, pp->pattern);
1238 FREE (&pp->pattern);
1242 mutt_str_replace (&pp->pattern, (char*) p->init);
1248 if (p->flags & R_INDEX)
1249 set_option (OPTFORCEREDRAWINDEX);
1250 if (p->flags & R_PAGER)
1251 set_option (OPTFORCEREDRAWPAGER);
1252 if (p->flags & R_RESORT_SUB)
1253 set_option (OPTSORTSUBTHREADS);
1254 if (p->flags & R_RESORT)
1255 set_option (OPTNEEDRESORT);
1256 if (p->flags & R_RESORT_INIT)
1257 set_option (OPTRESORTINIT);
1258 if (p->flags & R_TREE)
1259 set_option (OPTREDRAWTREE);
1262 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1264 int idx, query, unset, inv, reset, r = 0;
1265 char *p, scratch[_POSIX_PATH_MAX];
1267 while (MoreArgs (s))
1269 /* reset state variables */
1271 unset = data & M_SET_UNSET;
1272 inv = data & M_SET_INV;
1273 reset = data & M_SET_RESET;
1275 if (*s->dptr == '?')
1280 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1285 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1290 else if (*s->dptr == '&')
1296 /* get the variable name */
1297 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1299 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1300 !(reset && !mutt_strcmp ("all", tmp->data)))
1302 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1309 if (query || unset || inv)
1311 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1315 if (s && *s->dptr == '=')
1317 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1321 if (!mutt_strcmp ("all", tmp->data))
1323 for (idx = 0; MuttVars[idx].option; idx++)
1324 mutt_restore_default (&MuttVars[idx]);
1328 mutt_restore_default (&MuttVars[idx]);
1330 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1332 if (s && *s->dptr == '=')
1334 if (unset || inv || query)
1336 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1341 mutt_extract_token (tmp, s, 0);
1342 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1344 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1348 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1355 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1356 ? _("%s is set") : _("%s is unset"), tmp->data);
1361 unset_option (MuttVars[idx].data);
1363 toggle_option (MuttVars[idx].data);
1365 set_option (MuttVars[idx].data);
1367 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1368 DTYPE (MuttVars[idx].type) == DT_PATH ||
1369 DTYPE (MuttVars[idx].type) == DT_ADDR)
1373 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1374 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1376 FREE ((void *)MuttVars[idx].data);
1378 else if (query || *s->dptr != '=')
1383 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1386 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1390 val = *((char **) MuttVars[idx].data);
1392 /* user requested the value of this variable */
1393 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1401 /* copy the value of the string */
1402 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1403 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1405 FREE ((void *)MuttVars[idx].data);
1407 mutt_extract_token (tmp, s, 0);
1408 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1410 strfcpy (scratch, tmp->data, sizeof (scratch));
1411 mutt_expand_path (scratch, sizeof (scratch));
1412 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1414 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1416 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1417 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1418 mutt_set_charset (Charset);
1422 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1426 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1428 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1432 if (query || *s->dptr != '=')
1434 /* user requested the value of this variable */
1435 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1436 NONULL (ptr->pattern));
1440 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1442 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1449 /* copy the value of the string */
1450 mutt_extract_token (tmp, s, 0);
1452 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1456 /* $mask is case-sensitive */
1457 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1458 flags |= mutt_which_case (tmp->data);
1461 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1470 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1471 if ((e = REGCOMP (rx, p, flags)) != 0)
1473 regerror (e, rx, err->data, err->dsize);
1479 /* get here only if everything went smootly */
1482 FREE (&ptr->pattern);
1483 regfree ((regex_t *) ptr->rx);
1487 ptr->pattern = safe_strdup (tmp->data);
1491 /* $reply_regexp and $alterantes require special treatment */
1493 if (Context && Context->msgcount &&
1494 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1496 regmatch_t pmatch[1];
1499 #define CUR_ENV Context->hdrs[i]->env
1500 for (i = 0; i < Context->msgcount; i++)
1502 if (CUR_ENV && CUR_ENV->subject)
1504 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1505 CUR_ENV->subject, 1, pmatch, 0)) ?
1507 CUR_ENV->subject + pmatch[0].rm_eo;
1514 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1516 if (query || *s->dptr != '=')
1518 switch (DefaultMagic)
1536 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1542 /* copy the value of the string */
1543 mutt_extract_token (tmp, s, 0);
1544 if (mx_set_magic (tmp->data))
1546 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1551 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1553 short *ptr = (short *) MuttVars[idx].data;
1557 if (query || *s->dptr != '=')
1559 /* user requested the value of this variable */
1560 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1566 mutt_extract_token (tmp, s, 0);
1567 val = strtol (tmp->data, &t, 0);
1569 if (!*tmp->data || *t || (short) val != val)
1571 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1578 /* these ones need a sanity check */
1579 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1583 mutt_init_history ();
1585 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1591 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1595 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1597 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1598 vals [ quadoption (MuttVars[idx].data) ]);
1602 if (*s->dptr == '=')
1605 mutt_extract_token (tmp, s, 0);
1606 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1607 set_quadoption (MuttVars[idx].data, M_YES);
1608 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1609 set_quadoption (MuttVars[idx].data, M_NO);
1610 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1611 set_quadoption (MuttVars[idx].data, M_ASKYES);
1612 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1613 set_quadoption (MuttVars[idx].data, M_ASKNO);
1616 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1624 toggle_quadoption (MuttVars[idx].data);
1626 set_quadoption (MuttVars[idx].data, M_NO);
1628 set_quadoption (MuttVars[idx].data, M_YES);
1631 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1633 const struct mapping_t *map = NULL;
1635 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1638 map = SortAliasMethods;
1640 case DT_SORT_BROWSER:
1641 map = SortBrowserMethods;
1644 if ((WithCrypto & APPLICATION_PGP))
1645 map = SortKeyMethods;
1648 map = SortAuxMethods;
1657 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1662 if (query || *s->dptr != '=')
1664 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1666 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1667 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1668 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1673 mutt_extract_token (tmp, s , 0);
1675 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1683 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1688 if (MuttVars[idx].flags & R_INDEX)
1689 set_option (OPTFORCEREDRAWINDEX);
1690 if (MuttVars[idx].flags & R_PAGER)
1691 set_option (OPTFORCEREDRAWPAGER);
1692 if (MuttVars[idx].flags & R_RESORT_SUB)
1693 set_option (OPTSORTSUBTHREADS);
1694 if (MuttVars[idx].flags & R_RESORT)
1695 set_option (OPTNEEDRESORT);
1696 if (MuttVars[idx].flags & R_RESORT_INIT)
1697 set_option (OPTRESORTINIT);
1698 if (MuttVars[idx].flags & R_TREE)
1699 set_option (OPTREDRAWTREE);
1706 /* reads the specified initialization file. returns -1 if errors were found
1707 so that we can pause to let the user know... */
1708 static int source_rc (const char *rcfile, BUFFER *err)
1711 int line = 0, rc = 0, conv = 0;
1713 char *linebuf = NULL;
1714 char *currentline = NULL;
1718 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1721 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1723 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1727 memset (&token, 0, sizeof (token));
1728 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1730 conv=ConfigCharset && (*ConfigCharset) && Charset;
1733 currentline=safe_strdup(linebuf);
1734 if (!currentline) continue;
1735 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1738 currentline=linebuf;
1740 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1742 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1743 if (--rc < -MAXERRS)
1745 if (conv) FREE(¤tline);
1761 mutt_wait_filter (pid);
1764 /* the muttrc source keyword */
1765 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1766 : _("source: reading aborted due too many errors in %s"), rcfile);
1774 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1776 char path[_POSIX_PATH_MAX];
1781 if (mutt_extract_token (tmp, s, 0) != 0)
1783 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1787 strfcpy (path, tmp->data, sizeof (path));
1788 mutt_expand_path (path, sizeof (path));
1790 rc += source_rc (path, err);
1792 while (MoreArgs (s));
1794 return ((rc < 0) ? -1 : 0);
1797 /* line command to execute
1799 token scratch buffer to be used by parser. caller should free
1800 token->data when finished. the reason for this variable is
1801 to avoid having to allocate and deallocate a lot of memory
1802 if we are parsing many lines. the caller can pass in the
1803 memory to use, which avoids having to create new space for
1804 every call to this function.
1806 err where to write error messages */
1807 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1812 memset (&expn, 0, sizeof (expn));
1813 expn.data = expn.dptr = line;
1814 expn.dsize = mutt_strlen (line);
1821 if (*expn.dptr == '#')
1822 break; /* rest of line is a comment */
1823 if (*expn.dptr == ';')
1828 mutt_extract_token (token, &expn, 0);
1829 for (i = 0; Commands[i].name; i++)
1831 if (!mutt_strcmp (token->data, Commands[i].name))
1833 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1838 if (!Commands[i].name)
1840 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1852 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1853 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1854 /* initial string that starts completion. No telling how much crap
1855 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1856 char User_typed [LONG_STRING] = {0};
1858 int Num_matched = 0; /* Number of matches for completion */
1859 char Completed [STRING] = {0}; /* completed string (command or variable) */
1860 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1862 /* helper function for completion. Changes the dest buffer if
1863 necessary/possible to aid completion.
1864 dest == completion result gets here.
1865 src == candidate for completion.
1866 try == user entered data for completion.
1867 len == length of dest buffer.
1869 static void candidate (char *dest, char *try, char *src, int len)
1873 if (strstr (src, try) == src)
1875 Matches[Num_matched++] = src;
1877 strfcpy (dest, src, len);
1880 for (l = 0; src[l] && src[l] == dest[l]; l++);
1886 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1890 int spaces; /* keep track of the number of leading spaces on the line */
1893 spaces = buffer - pt;
1895 pt = buffer + pos - spaces;
1896 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1899 if (pt == buffer) /* complete cmd */
1901 /* 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; Commands[num].name; num++)
1909 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1910 Matches[Num_matched++] = User_typed;
1912 /* All matches are stored. Longest non-ambiguous string is ""
1913 * i.e. dont change 'buffer'. Fake successful return this time */
1914 if (User_typed[0] == 0)
1918 if (Completed[0] == 0 && User_typed[0])
1921 /* Num_matched will _always_ be atleast 1 since the initial
1922 * user-typed string is always stored */
1923 if (numtabs == 1 && Num_matched == 2)
1924 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1925 else if (numtabs > 1 && Num_matched > 2)
1926 /* cycle thru all the matches */
1927 snprintf(Completed, sizeof(Completed), "%s",
1928 Matches[(numtabs - 2) % Num_matched]);
1930 /* return the completed command */
1931 strncpy (buffer, Completed, len - spaces);
1933 else if (!mutt_strncmp (buffer, "set", 3)
1934 || !mutt_strncmp (buffer, "unset", 5)
1935 || !mutt_strncmp (buffer, "reset", 5)
1936 || !mutt_strncmp (buffer, "toggle", 6))
1937 { /* complete variables */
1938 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1941 /* loop through all the possible prefixes (no, inv, ...) */
1942 if (!mutt_strncmp (buffer, "set", 3))
1944 for (num = 0; prefixes[num]; num++)
1946 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1948 pt += mutt_strlen (prefixes[num]);
1954 /* first TAB. Collect all the matches */
1958 strfcpy (User_typed, pt, sizeof (User_typed));
1959 memset (Matches, 0, sizeof (Matches));
1960 memset (Completed, 0, sizeof (Completed));
1961 for (num = 0; MuttVars[num].option; num++)
1962 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1963 Matches[Num_matched++] = User_typed;
1965 /* All matches are stored. Longest non-ambiguous string is ""
1966 * i.e. dont change 'buffer'. Fake successful return this time */
1967 if (User_typed[0] == 0)
1971 if (Completed[0] == 0 && User_typed[0])
1974 /* Num_matched will _always_ be atleast 1 since the initial
1975 * user-typed string is always stored */
1976 if (numtabs == 1 && Num_matched == 2)
1977 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1978 else if (numtabs > 1 && Num_matched > 2)
1979 /* cycle thru all the matches */
1980 snprintf(Completed, sizeof(Completed), "%s",
1981 Matches[(numtabs - 2) % Num_matched]);
1983 strncpy (pt, Completed, buffer + len - pt - spaces);
1985 else if (!mutt_strncmp (buffer, "exec", 4))
1987 struct binding_t *menu = km_get_table (CurrentMenu);
1989 if (!menu && CurrentMenu != MENU_PAGER)
1993 /* first TAB. Collect all the matches */
1997 strfcpy (User_typed, pt, sizeof (User_typed));
1998 memset (Matches, 0, sizeof (Matches));
1999 memset (Completed, 0, sizeof (Completed));
2000 for (num = 0; menu[num].name; num++)
2001 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2002 /* try the generic menu */
2003 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2006 for (num = 0; menu[num].name; num++)
2007 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2009 Matches[Num_matched++] = User_typed;
2011 /* All matches are stored. Longest non-ambiguous string is ""
2012 * i.e. dont change 'buffer'. Fake successful return this time */
2013 if (User_typed[0] == 0)
2017 if (Completed[0] == 0 && User_typed[0])
2020 /* Num_matched will _always_ be atleast 1 since the initial
2021 * user-typed string is always stored */
2022 if (numtabs == 1 && Num_matched == 2)
2023 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2024 else if (numtabs > 1 && Num_matched > 2)
2025 /* cycle thru all the matches */
2026 snprintf(Completed, sizeof(Completed), "%s",
2027 Matches[(numtabs - 2) % Num_matched]);
2029 strncpy (pt, Completed, buffer + len - pt - spaces);
2037 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2039 char var[STRING], *pt = buffer;
2046 spaces = buffer - pt;
2048 pt = buffer + pos - spaces;
2049 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2051 pt++; /* move past the space */
2052 if (*pt == '=') /* abort if no var before the '=' */
2055 if (mutt_strncmp (buffer, "set", 3) == 0)
2058 strfcpy (var, pt, sizeof (var));
2059 /* ignore the trailing '=' when comparing */
2060 var[mutt_strlen (var) - 1] = 0;
2061 if ((idx = mutt_option_index (var)) == -1)
2062 return 0; /* no such variable. */
2065 char tmp [LONG_STRING], tmp2[LONG_STRING];
2067 size_t dlen = buffer + len - pt - spaces;
2068 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2072 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2073 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2074 (DTYPE(MuttVars[idx].type) == DT_RX))
2076 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2077 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2078 mutt_pretty_mailbox (tmp);
2080 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2082 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2084 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2085 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2086 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2087 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2088 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2090 const struct mapping_t *map;
2093 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2096 map = SortAliasMethods;
2098 case DT_SORT_BROWSER:
2099 map = SortBrowserMethods;
2102 if ((WithCrypto & APPLICATION_PGP))
2103 map = SortKeyMethods;
2111 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2112 snprintf (tmp, sizeof (tmp), "%s%s%s",
2113 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2114 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2117 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2118 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2122 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2124 if (*s == '\\' || *s == '"')
2130 strfcpy (tmp, pt, sizeof (tmp));
2131 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2139 /* Implement the -Q command line flag */
2140 int mutt_query_variables (LIST *queries)
2144 char errbuff[STRING];
2145 char command[STRING];
2149 memset (&err, 0, sizeof (err));
2150 memset (&token, 0, sizeof (token));
2153 err.dsize = sizeof (errbuff);
2155 for (p = queries; p; p = p->next)
2157 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2158 if (mutt_parse_rc_line (command, &token, &err) == -1)
2160 fprintf (stderr, "%s\n", err.data);
2164 printf ("%s\n", err.data);
2171 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2175 for (i=0; map[i].name; i++)
2176 if (map[i].value == val)
2177 return (map[i].name);
2181 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2185 for (i = 0; map[i].name; i++)
2186 if (ascii_strcasecmp (map[i].name, name) == 0)
2187 return (map[i].value);
2192 static void start_debug (void)
2196 char buf[_POSIX_PATH_MAX];
2197 char buf2[_POSIX_PATH_MAX];
2199 /* rotate the old debug logs */
2200 for (i=3; i>=0; i--)
2202 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2203 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2206 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2209 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2210 fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2211 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2216 static int mutt_execute_commands (LIST *p)
2219 char errstr[SHORT_STRING];
2221 memset (&err, 0, sizeof (err));
2223 err.dsize = sizeof (errstr);
2224 memset (&token, 0, sizeof (token));
2225 for (; p; p = p->next)
2227 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2229 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2238 void mutt_init (int skip_sys_rc, LIST *commands)
2241 struct utsname utsname;
2242 char *p, buffer[STRING], error[STRING];
2243 int i, default_rc = 0, need_pause = 0;
2246 memset (&err, 0, sizeof (err));
2248 err.dsize = sizeof (error);
2251 * XXX - use something even more difficult to predict?
2253 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2254 "\033]9;%ld\a", (long) time (NULL));
2256 /* on one of the systems I use, getcwd() does not return the same prefix
2257 as is listed in the passwd file */
2258 if ((p = getenv ("HOME")))
2259 Homedir = safe_strdup (p);
2261 /* Get some information about the user */
2262 if ((pw = getpwuid (getuid ())))
2266 Username = safe_strdup (pw->pw_name);
2268 Homedir = safe_strdup (pw->pw_dir);
2270 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2271 Shell = safe_strdup (pw->pw_shell);
2278 fputs (_("unable to determine home directory"), stderr);
2281 if ((p = getenv ("USER")))
2282 Username = safe_strdup (p);
2286 fputs (_("unable to determine username"), stderr);
2289 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2293 /* Start up debugging mode if requested */
2298 /* And about the host... */
2300 /* some systems report the FQDN instead of just the hostname */
2301 if ((p = strchr (utsname.nodename, '.')))
2303 Hostname = mutt_substrdup (utsname.nodename, p);
2305 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2308 Hostname = safe_strdup (utsname.nodename);
2311 #define DOMAIN buffer
2312 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2313 Fqdn = safe_strdup ("@");
2318 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2319 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2322 Fqdn = safe_strdup(NONULL(Hostname));
2329 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2332 fgets (buffer, sizeof (buffer), f);
2336 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2338 NewsServer = safe_strdup (p);
2342 if ((p = getenv ("NNTPSERVER")))
2343 NewsServer = safe_strdup (p);
2346 if ((p = getenv ("MAIL")))
2347 Spoolfile = safe_strdup (p);
2348 else if ((p = getenv ("MAILDIR")))
2349 Spoolfile = safe_strdup (p);
2353 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2355 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2357 Spoolfile = safe_strdup (buffer);
2360 if ((p = getenv ("MAILCAPS")))
2361 MailcapPath = safe_strdup (p);
2364 /* Default search path from RFC1524 */
2365 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2368 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2370 p = getenv ("VISUAL");
2373 p = getenv ("EDITOR");
2377 Editor = safe_strdup (p);
2378 Visual = safe_strdup (p);
2380 if ((p = getenv ("REPLYTO")) != NULL)
2384 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2386 memset (&buf, 0, sizeof (buf));
2387 buf.data = buf.dptr = buffer;
2388 buf.dsize = mutt_strlen (buffer);
2390 memset (&token, 0, sizeof (token));
2391 parse_my_hdr (&token, &buf, 0, &err);
2395 if ((p = getenv ("EMAIL")) != NULL)
2396 From = rfc822_parse_adrlist (NULL, p);
2398 mutt_set_langinfo_charset ();
2399 mutt_set_charset (Charset);
2402 /* Set standard defaults */
2403 for (i = 0; MuttVars[i].option; i++)
2405 mutt_set_default (&MuttVars[i]);
2406 mutt_restore_default (&MuttVars[i]);
2409 CurrentMenu = MENU_MAIN;
2412 #ifndef LOCALES_HACK
2413 /* Do we have a locale definition? */
2414 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2415 ((p = getenv ("LANG")) != NULL && p[0]) ||
2416 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2417 set_option (OPTLOCALES);
2421 /* Unset suspend by default if we're the session leader */
2422 if (getsid(0) == getpid())
2423 unset_option (OPTSUSPEND);
2426 mutt_init_history ();
2435 * When changing the code which looks for a configuration file,
2436 * please also change the corresponding code in muttbug.sh.in.
2446 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2447 if (access(buffer, F_OK) == -1)
2448 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2449 if (access(buffer, F_OK) == -1)
2450 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2451 if (access(buffer, F_OK) == -1)
2452 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2455 Muttrc = safe_strdup (buffer);
2459 strfcpy (buffer, Muttrc, sizeof (buffer));
2461 mutt_expand_path (buffer, sizeof (buffer));
2462 Muttrc = safe_strdup (buffer);
2465 AliasFile = safe_strdup (NONULL(Muttrc));
2467 /* Process the global rc file if it exists and the user hasn't explicity
2468 requested not to via "-n". */
2471 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2472 if (access (buffer, F_OK) == -1)
2473 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2474 if (access (buffer, F_OK) == -1)
2475 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2476 if (access (buffer, F_OK) == -1)
2477 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2478 if (access (buffer, F_OK) != -1)
2480 if (source_rc (buffer, &err) != 0)
2482 fputs (err.data, stderr);
2483 fputc ('\n', stderr);
2489 /* Read the user's initialization file. */
2490 if (access (Muttrc, F_OK) != -1)
2492 if (!option (OPTNOCURSES))
2494 if (source_rc (Muttrc, &err) != 0)
2496 fputs (err.data, stderr);
2497 fputc ('\n', stderr);
2501 else if (!default_rc)
2503 /* file specified by -F does not exist */
2504 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2505 mutt_endwin (buffer);
2509 if (mutt_execute_commands (commands) != 0)
2512 if (need_pause && !option (OPTNOCURSES))
2514 if (mutt_any_key_to_continue (NULL) == -1)
2519 set_option (OPTWEED); /* turn weeding on by default */
2523 int mutt_get_hook_type (const char *name)
2525 struct command_t *c;
2527 for (c = Commands ; c->name ; c++)
2528 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)