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);
1247 if (p->flags & R_INDEX)
1248 set_option (OPTFORCEREDRAWINDEX);
1249 if (p->flags & R_PAGER)
1250 set_option (OPTFORCEREDRAWPAGER);
1251 if (p->flags & R_RESORT_SUB)
1252 set_option (OPTSORTSUBTHREADS);
1253 if (p->flags & R_RESORT)
1254 set_option (OPTNEEDRESORT);
1255 if (p->flags & R_RESORT_INIT)
1256 set_option (OPTRESORTINIT);
1257 if (p->flags & R_TREE)
1258 set_option (OPTREDRAWTREE);
1261 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1263 int idx, query, unset, inv, reset, r = 0;
1264 char *p, scratch[_POSIX_PATH_MAX];
1266 while (MoreArgs (s))
1268 /* reset state variables */
1270 unset = data & M_SET_UNSET;
1271 inv = data & M_SET_INV;
1272 reset = data & M_SET_RESET;
1274 if (*s->dptr == '?')
1279 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1284 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1289 else if (*s->dptr == '&')
1295 /* get the variable name */
1296 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1298 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1299 !(reset && !mutt_strcmp ("all", tmp->data)))
1301 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1308 if (query || unset || inv)
1310 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1314 if (s && *s->dptr == '=')
1316 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1320 if (!mutt_strcmp ("all", tmp->data))
1322 for (idx = 0; MuttVars[idx].option; idx++)
1323 mutt_restore_default (&MuttVars[idx]);
1327 mutt_restore_default (&MuttVars[idx]);
1329 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1331 if (s && *s->dptr == '=')
1333 if (unset || inv || query)
1335 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1340 mutt_extract_token (tmp, s, 0);
1341 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1343 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1347 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1354 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1355 ? _("%s is set") : _("%s is unset"), tmp->data);
1360 unset_option (MuttVars[idx].data);
1362 toggle_option (MuttVars[idx].data);
1364 set_option (MuttVars[idx].data);
1366 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1367 DTYPE (MuttVars[idx].type) == DT_PATH ||
1368 DTYPE (MuttVars[idx].type) == DT_ADDR)
1372 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1373 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1375 FREE ((void *)MuttVars[idx].data);
1377 else if (query || *s->dptr != '=')
1382 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1385 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1389 val = *((char **) MuttVars[idx].data);
1391 /* user requested the value of this variable */
1392 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1400 /* copy the value of the string */
1401 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1402 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1404 FREE ((void *)MuttVars[idx].data);
1406 mutt_extract_token (tmp, s, 0);
1407 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1409 strfcpy (scratch, tmp->data, sizeof (scratch));
1410 mutt_expand_path (scratch, sizeof (scratch));
1411 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1413 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1415 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1416 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1417 mutt_set_charset (Charset);
1421 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1425 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1427 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1431 if (query || *s->dptr != '=')
1433 /* user requested the value of this variable */
1434 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1435 NONULL (ptr->pattern));
1439 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1441 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1448 /* copy the value of the string */
1449 mutt_extract_token (tmp, s, 0);
1451 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1455 /* $mask is case-sensitive */
1456 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1457 flags |= mutt_which_case (tmp->data);
1460 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1469 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1470 if ((e = REGCOMP (rx, p, flags)) != 0)
1472 regerror (e, rx, err->data, err->dsize);
1478 /* get here only if everything went smootly */
1481 FREE (&ptr->pattern);
1482 regfree ((regex_t *) ptr->rx);
1486 ptr->pattern = safe_strdup (tmp->data);
1490 /* $reply_regexp and $alterantes require special treatment */
1492 if (Context && Context->msgcount &&
1493 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1495 regmatch_t pmatch[1];
1498 #define CUR_ENV Context->hdrs[i]->env
1499 for (i = 0; i < Context->msgcount; i++)
1501 if (CUR_ENV && CUR_ENV->subject)
1503 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1504 CUR_ENV->subject, 1, pmatch, 0)) ?
1506 CUR_ENV->subject + pmatch[0].rm_eo;
1513 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1515 if (query || *s->dptr != '=')
1517 switch (DefaultMagic)
1535 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1541 /* copy the value of the string */
1542 mutt_extract_token (tmp, s, 0);
1543 if (mx_set_magic (tmp->data))
1545 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1550 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1552 short *ptr = (short *) MuttVars[idx].data;
1556 if (query || *s->dptr != '=')
1558 /* user requested the value of this variable */
1559 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1565 mutt_extract_token (tmp, s, 0);
1566 val = strtol (tmp->data, &t, 0);
1568 if (!*tmp->data || *t || (short) val != val)
1570 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1577 /* these ones need a sanity check */
1578 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1582 mutt_init_history ();
1584 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1590 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1594 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1596 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1597 vals [ quadoption (MuttVars[idx].data) ]);
1601 if (*s->dptr == '=')
1604 mutt_extract_token (tmp, s, 0);
1605 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1606 set_quadoption (MuttVars[idx].data, M_YES);
1607 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1608 set_quadoption (MuttVars[idx].data, M_NO);
1609 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1610 set_quadoption (MuttVars[idx].data, M_ASKYES);
1611 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1612 set_quadoption (MuttVars[idx].data, M_ASKNO);
1615 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1623 toggle_quadoption (MuttVars[idx].data);
1625 set_quadoption (MuttVars[idx].data, M_NO);
1627 set_quadoption (MuttVars[idx].data, M_YES);
1630 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1632 const struct mapping_t *map = NULL;
1634 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1637 map = SortAliasMethods;
1639 case DT_SORT_BROWSER:
1640 map = SortBrowserMethods;
1643 if ((WithCrypto & APPLICATION_PGP))
1644 map = SortKeyMethods;
1647 map = SortAuxMethods;
1656 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1661 if (query || *s->dptr != '=')
1663 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1665 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1666 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1667 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1672 mutt_extract_token (tmp, s , 0);
1674 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1682 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1687 if (MuttVars[idx].flags & R_INDEX)
1688 set_option (OPTFORCEREDRAWINDEX);
1689 if (MuttVars[idx].flags & R_PAGER)
1690 set_option (OPTFORCEREDRAWPAGER);
1691 if (MuttVars[idx].flags & R_RESORT_SUB)
1692 set_option (OPTSORTSUBTHREADS);
1693 if (MuttVars[idx].flags & R_RESORT)
1694 set_option (OPTNEEDRESORT);
1695 if (MuttVars[idx].flags & R_RESORT_INIT)
1696 set_option (OPTRESORTINIT);
1697 if (MuttVars[idx].flags & R_TREE)
1698 set_option (OPTREDRAWTREE);
1705 /* reads the specified initialization file. returns -1 if errors were found
1706 so that we can pause to let the user know... */
1707 static int source_rc (const char *rcfile, BUFFER *err)
1710 int line = 0, rc = 0, conv = 0;
1712 char *linebuf = NULL;
1713 char *currentline = NULL;
1717 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1720 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1722 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1726 memset (&token, 0, sizeof (token));
1727 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1729 conv=ConfigCharset && (*ConfigCharset) && Charset;
1732 currentline=safe_strdup(linebuf);
1733 if (!currentline) continue;
1734 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1737 currentline=linebuf;
1739 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1741 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1742 if (--rc < -MAXERRS)
1744 if (conv) FREE(¤tline);
1760 mutt_wait_filter (pid);
1763 /* the muttrc source keyword */
1764 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1765 : _("source: reading aborted due too many errors in %s"), rcfile);
1773 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1775 char path[_POSIX_PATH_MAX];
1780 if (mutt_extract_token (tmp, s, 0) != 0)
1782 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1786 strfcpy (path, tmp->data, sizeof (path));
1787 mutt_expand_path (path, sizeof (path));
1789 rc += source_rc (path, err);
1791 while (MoreArgs (s));
1793 return ((rc < 0) ? -1 : 0);
1796 /* line command to execute
1798 token scratch buffer to be used by parser. caller should free
1799 token->data when finished. the reason for this variable is
1800 to avoid having to allocate and deallocate a lot of memory
1801 if we are parsing many lines. the caller can pass in the
1802 memory to use, which avoids having to create new space for
1803 every call to this function.
1805 err where to write error messages */
1806 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1811 memset (&expn, 0, sizeof (expn));
1812 expn.data = expn.dptr = line;
1813 expn.dsize = mutt_strlen (line);
1820 if (*expn.dptr == '#')
1821 break; /* rest of line is a comment */
1822 if (*expn.dptr == ';')
1827 mutt_extract_token (token, &expn, 0);
1828 for (i = 0; Commands[i].name; i++)
1830 if (!mutt_strcmp (token->data, Commands[i].name))
1832 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1837 if (!Commands[i].name)
1839 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1851 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1852 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1853 /* initial string that starts completion. No telling how much crap
1854 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1855 char User_typed [LONG_STRING] = {0};
1857 int Num_matched = 0; /* Number of matches for completion */
1858 char Completed [STRING] = {0}; /* completed string (command or variable) */
1859 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1861 /* helper function for completion. Changes the dest buffer if
1862 necessary/possible to aid completion.
1863 dest == completion result gets here.
1864 src == candidate for completion.
1865 try == user entered data for completion.
1866 len == length of dest buffer.
1868 static void candidate (char *dest, char *try, char *src, int len)
1872 if (strstr (src, try) == src)
1874 Matches[Num_matched++] = src;
1876 strfcpy (dest, src, len);
1879 for (l = 0; src[l] && src[l] == dest[l]; l++);
1885 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1889 int spaces; /* keep track of the number of leading spaces on the line */
1892 spaces = buffer - pt;
1894 pt = buffer + pos - spaces;
1895 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1898 if (pt == buffer) /* complete cmd */
1900 /* first TAB. Collect all the matches */
1904 strfcpy (User_typed, pt, sizeof (User_typed));
1905 memset (Matches, 0, sizeof (Matches));
1906 memset (Completed, 0, sizeof (Completed));
1907 for (num = 0; Commands[num].name; num++)
1908 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1909 Matches[Num_matched++] = User_typed;
1911 /* All matches are stored. Longest non-ambiguous string is ""
1912 * i.e. dont change 'buffer'. Fake successful return this time */
1913 if (User_typed[0] == 0)
1917 if (Completed[0] == 0 && User_typed[0])
1920 /* Num_matched will _always_ be atleast 1 since the initial
1921 * user-typed string is always stored */
1922 if (numtabs == 1 && Num_matched == 2)
1923 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1924 else if (numtabs > 1 && Num_matched > 2)
1925 /* cycle thru all the matches */
1926 snprintf(Completed, sizeof(Completed), "%s",
1927 Matches[(numtabs - 2) % Num_matched]);
1929 /* return the completed command */
1930 strncpy (buffer, Completed, len - spaces);
1932 else if (!mutt_strncmp (buffer, "set", 3)
1933 || !mutt_strncmp (buffer, "unset", 5)
1934 || !mutt_strncmp (buffer, "reset", 5)
1935 || !mutt_strncmp (buffer, "toggle", 6))
1936 { /* complete variables */
1937 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1940 /* loop through all the possible prefixes (no, inv, ...) */
1941 if (!mutt_strncmp (buffer, "set", 3))
1943 for (num = 0; prefixes[num]; num++)
1945 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1947 pt += mutt_strlen (prefixes[num]);
1953 /* first TAB. Collect all the matches */
1957 strfcpy (User_typed, pt, sizeof (User_typed));
1958 memset (Matches, 0, sizeof (Matches));
1959 memset (Completed, 0, sizeof (Completed));
1960 for (num = 0; MuttVars[num].option; num++)
1961 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1962 Matches[Num_matched++] = User_typed;
1964 /* All matches are stored. Longest non-ambiguous string is ""
1965 * i.e. dont change 'buffer'. Fake successful return this time */
1966 if (User_typed[0] == 0)
1970 if (Completed[0] == 0 && User_typed[0])
1973 /* Num_matched will _always_ be atleast 1 since the initial
1974 * user-typed string is always stored */
1975 if (numtabs == 1 && Num_matched == 2)
1976 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1977 else if (numtabs > 1 && Num_matched > 2)
1978 /* cycle thru all the matches */
1979 snprintf(Completed, sizeof(Completed), "%s",
1980 Matches[(numtabs - 2) % Num_matched]);
1982 strncpy (pt, Completed, buffer + len - pt - spaces);
1984 else if (!mutt_strncmp (buffer, "exec", 4))
1986 struct binding_t *menu = km_get_table (CurrentMenu);
1988 if (!menu && CurrentMenu != MENU_PAGER)
1992 /* first TAB. Collect all the matches */
1996 strfcpy (User_typed, pt, sizeof (User_typed));
1997 memset (Matches, 0, sizeof (Matches));
1998 memset (Completed, 0, sizeof (Completed));
1999 for (num = 0; menu[num].name; num++)
2000 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2001 /* try the generic menu */
2002 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2005 for (num = 0; menu[num].name; num++)
2006 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2008 Matches[Num_matched++] = User_typed;
2010 /* All matches are stored. Longest non-ambiguous string is ""
2011 * i.e. dont change 'buffer'. Fake successful return this time */
2012 if (User_typed[0] == 0)
2016 if (Completed[0] == 0 && User_typed[0])
2019 /* Num_matched will _always_ be atleast 1 since the initial
2020 * user-typed string is always stored */
2021 if (numtabs == 1 && Num_matched == 2)
2022 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2023 else if (numtabs > 1 && Num_matched > 2)
2024 /* cycle thru all the matches */
2025 snprintf(Completed, sizeof(Completed), "%s",
2026 Matches[(numtabs - 2) % Num_matched]);
2028 strncpy (pt, Completed, buffer + len - pt - spaces);
2036 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2038 char var[STRING], *pt = buffer;
2045 spaces = buffer - pt;
2047 pt = buffer + pos - spaces;
2048 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2050 pt++; /* move past the space */
2051 if (*pt == '=') /* abort if no var before the '=' */
2054 if (mutt_strncmp (buffer, "set", 3) == 0)
2057 strfcpy (var, pt, sizeof (var));
2058 /* ignore the trailing '=' when comparing */
2059 var[mutt_strlen (var) - 1] = 0;
2060 if ((idx = mutt_option_index (var)) == -1)
2061 return 0; /* no such variable. */
2064 char tmp [LONG_STRING], tmp2[LONG_STRING];
2066 size_t dlen = buffer + len - pt - spaces;
2067 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2071 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2072 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2073 (DTYPE(MuttVars[idx].type) == DT_RX))
2075 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2076 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2077 mutt_pretty_mailbox (tmp);
2079 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2081 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2083 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2084 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2085 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2086 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2087 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2089 const struct mapping_t *map;
2092 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2095 map = SortAliasMethods;
2097 case DT_SORT_BROWSER:
2098 map = SortBrowserMethods;
2101 if ((WithCrypto & APPLICATION_PGP))
2102 map = SortKeyMethods;
2110 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2111 snprintf (tmp, sizeof (tmp), "%s%s%s",
2112 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2113 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2116 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2117 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2121 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2123 if (*s == '\\' || *s == '"')
2129 strfcpy (tmp, pt, sizeof (tmp));
2130 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2138 /* Implement the -Q command line flag */
2139 int mutt_query_variables (LIST *queries)
2143 char errbuff[STRING];
2144 char command[STRING];
2148 memset (&err, 0, sizeof (err));
2149 memset (&token, 0, sizeof (token));
2152 err.dsize = sizeof (errbuff);
2154 for (p = queries; p; p = p->next)
2156 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2157 if (mutt_parse_rc_line (command, &token, &err) == -1)
2159 fprintf (stderr, "%s\n", err.data);
2163 printf ("%s\n", err.data);
2170 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2174 for (i=0; map[i].name; i++)
2175 if (map[i].value == val)
2176 return (map[i].name);
2180 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2184 for (i = 0; map[i].name; i++)
2185 if (ascii_strcasecmp (map[i].name, name) == 0)
2186 return (map[i].value);
2191 static void start_debug (void)
2195 char buf[_POSIX_PATH_MAX];
2196 char buf2[_POSIX_PATH_MAX];
2198 /* rotate the old debug logs */
2199 for (i=3; i>=0; i--)
2201 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2202 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2205 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2208 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2209 fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2210 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2215 static int mutt_execute_commands (LIST *p)
2218 char errstr[SHORT_STRING];
2220 memset (&err, 0, sizeof (err));
2222 err.dsize = sizeof (errstr);
2223 memset (&token, 0, sizeof (token));
2224 for (; p; p = p->next)
2226 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2228 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2237 void mutt_init (int skip_sys_rc, LIST *commands)
2240 struct utsname utsname;
2241 char *p, buffer[STRING], error[STRING];
2242 int i, default_rc = 0, need_pause = 0;
2245 memset (&err, 0, sizeof (err));
2247 err.dsize = sizeof (error);
2250 * XXX - use something even more difficult to predict?
2252 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2253 "\033]9;%ld\a", (long) time (NULL));
2255 /* on one of the systems I use, getcwd() does not return the same prefix
2256 as is listed in the passwd file */
2257 if ((p = getenv ("HOME")))
2258 Homedir = safe_strdup (p);
2260 /* Get some information about the user */
2261 if ((pw = getpwuid (getuid ())))
2265 Username = safe_strdup (pw->pw_name);
2267 Homedir = safe_strdup (pw->pw_dir);
2269 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2270 Shell = safe_strdup (pw->pw_shell);
2277 fputs (_("unable to determine home directory"), stderr);
2280 if ((p = getenv ("USER")))
2281 Username = safe_strdup (p);
2285 fputs (_("unable to determine username"), stderr);
2288 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2292 /* Start up debugging mode if requested */
2297 /* And about the host... */
2299 /* some systems report the FQDN instead of just the hostname */
2300 if ((p = strchr (utsname.nodename, '.')))
2302 Hostname = mutt_substrdup (utsname.nodename, p);
2304 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2307 Hostname = safe_strdup (utsname.nodename);
2310 #define DOMAIN buffer
2311 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2312 Fqdn = safe_strdup ("@");
2317 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2318 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2321 Fqdn = safe_strdup(NONULL(Hostname));
2328 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2331 fgets (buffer, sizeof (buffer), f);
2335 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2337 NewsServer = safe_strdup (p);
2341 if ((p = getenv ("NNTPSERVER")))
2342 NewsServer = safe_strdup (p);
2345 if ((p = getenv ("MAIL")))
2346 Spoolfile = safe_strdup (p);
2347 else if ((p = getenv ("MAILDIR")))
2348 Spoolfile = safe_strdup (p);
2352 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2354 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2356 Spoolfile = safe_strdup (buffer);
2359 if ((p = getenv ("MAILCAPS")))
2360 MailcapPath = safe_strdup (p);
2363 /* Default search path from RFC1524 */
2364 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2367 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2369 p = getenv ("VISUAL");
2372 p = getenv ("EDITOR");
2376 Editor = safe_strdup (p);
2377 Visual = safe_strdup (p);
2379 if ((p = getenv ("REPLYTO")) != NULL)
2383 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2385 memset (&buf, 0, sizeof (buf));
2386 buf.data = buf.dptr = buffer;
2387 buf.dsize = mutt_strlen (buffer);
2389 memset (&token, 0, sizeof (token));
2390 parse_my_hdr (&token, &buf, 0, &err);
2394 if ((p = getenv ("EMAIL")) != NULL)
2395 From = rfc822_parse_adrlist (NULL, p);
2397 mutt_set_langinfo_charset ();
2398 mutt_set_charset (Charset);
2401 /* Set standard defaults */
2402 for (i = 0; MuttVars[i].option; i++)
2404 mutt_set_default (&MuttVars[i]);
2405 mutt_restore_default (&MuttVars[i]);
2408 CurrentMenu = MENU_MAIN;
2411 #ifndef LOCALES_HACK
2412 /* Do we have a locale definition? */
2413 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2414 ((p = getenv ("LANG")) != NULL && p[0]) ||
2415 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2416 set_option (OPTLOCALES);
2420 /* Unset suspend by default if we're the session leader */
2421 if (getsid(0) == getpid())
2422 unset_option (OPTSUSPEND);
2425 mutt_init_history ();
2434 * When changing the code which looks for a configuration file,
2435 * please also change the corresponding code in muttbug.sh.in.
2445 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2446 if (access(buffer, F_OK) == -1)
2447 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2448 if (access(buffer, F_OK) == -1)
2449 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2450 if (access(buffer, F_OK) == -1)
2451 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2454 Muttrc = safe_strdup (buffer);
2458 strfcpy (buffer, Muttrc, sizeof (buffer));
2460 mutt_expand_path (buffer, sizeof (buffer));
2461 Muttrc = safe_strdup (buffer);
2464 AliasFile = safe_strdup (NONULL(Muttrc));
2466 /* Process the global rc file if it exists and the user hasn't explicity
2467 requested not to via "-n". */
2470 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2471 if (access (buffer, F_OK) == -1)
2472 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2473 if (access (buffer, F_OK) == -1)
2474 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2475 if (access (buffer, F_OK) == -1)
2476 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2477 if (access (buffer, F_OK) != -1)
2479 if (source_rc (buffer, &err) != 0)
2481 fputs (err.data, stderr);
2482 fputc ('\n', stderr);
2488 /* Read the user's initialization file. */
2489 if (access (Muttrc, F_OK) != -1)
2491 if (!option (OPTNOCURSES))
2493 if (source_rc (Muttrc, &err) != 0)
2495 fputs (err.data, stderr);
2496 fputc ('\n', stderr);
2500 else if (!default_rc)
2502 /* file specified by -F does not exist */
2503 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2504 mutt_endwin (buffer);
2508 if (mutt_execute_commands (commands) != 0)
2511 if (need_pause && !option (OPTNOCURSES))
2513 if (mutt_any_key_to_continue (NULL) == -1)
2518 set_option (OPTWEED); /* turn weeding on by default */
2522 int mutt_get_hook_type (const char *name)
2524 struct command_t *c;
2526 for (c = Commands ; c->name ; c++)
2527 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)