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));
655 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
659 mutt_extract_token (buf, s, 0);
660 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
664 while (MoreArgs (s));
669 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
671 return _parse_rx_list (buf, s, data, err, REG_ICASE);
674 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
678 mutt_extract_token (buf, s, 0);
679 if (mutt_strcmp (buf->data, "*") == 0)
681 mutt_free_rx_list ((RX_LIST **) data);
684 remove_from_rx_list ((RX_LIST **) data, buf->data);
686 while (MoreArgs (s));
691 static void _alternates_clean (void)
694 if (Context && Context->msgcount)
696 for (i = 0; i < Context->msgcount; i++)
697 Context->hdrs[i]->recip_valid = 0;
701 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
706 mutt_extract_token (buf, s, 0);
707 remove_from_rx_list (&UnAlternates, buf->data);
709 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
712 while (MoreArgs (s));
717 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
722 mutt_extract_token (buf, s, 0);
723 remove_from_rx_list (&Alternates, buf->data);
725 if (mutt_strcmp (buf->data, "*") &&
726 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
730 while (MoreArgs (s));
735 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
739 memset(&templ, 0, sizeof(templ));
741 /* Insist on at least one parameter */
745 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
747 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
751 /* Extract the first token, a regexp */
752 mutt_extract_token (buf, s, 0);
754 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
757 /* If there's a second parameter, it's a template for the spam tag. */
760 mutt_extract_token (&templ, s, 0);
762 /* Add to the spam list. */
763 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
770 /* If not, try to remove from the nospam list. */
773 remove_from_rx_list(&NoSpamList, buf->data);
779 /* M_NOSPAM is for nospam commands. */
780 else if (data == M_NOSPAM)
782 /* nospam only ever has one parameter. */
784 /* "*" is a special case. */
785 if (!mutt_strcmp(buf->data, "*"))
787 mutt_free_spam_list (&SpamList);
788 mutt_free_rx_list (&NoSpamList);
792 /* If it's on the spam list, just remove it. */
793 if (remove_from_spam_list(&SpamList, buf->data) != 0)
796 /* Otherwise, add it to the nospam list. */
797 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
803 /* This should not happen. */
804 strfcpy(err->data, "This is no good at all.", err->dsize);
808 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
812 mutt_extract_token (buf, s, 0);
814 * Check for deletion of entire list
816 if (mutt_strcmp (buf->data, "*") == 0)
818 mutt_free_list ((LIST **) data);
821 remove_from_list ((LIST **) data, buf->data);
823 while (MoreArgs (s));
828 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
832 mutt_extract_token (buf, s, 0);
833 remove_from_rx_list (&UnMailLists, buf->data);
835 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
838 while (MoreArgs (s));
843 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
847 mutt_extract_token (buf, s, 0);
848 remove_from_rx_list (&SubscribedLists, buf->data);
849 remove_from_rx_list (&MailLists, buf->data);
851 if (mutt_strcmp (buf->data, "*") &&
852 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
855 while (MoreArgs (s));
860 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
864 mutt_extract_token (buf, s, 0);
865 remove_from_rx_list (&UnMailLists, buf->data);
866 remove_from_rx_list (&UnSubscribedLists, buf->data);
868 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
870 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
873 while (MoreArgs (s));
878 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
882 mutt_extract_token (buf, s, 0);
883 remove_from_rx_list (&SubscribedLists, buf->data);
885 if (mutt_strcmp (buf->data, "*") &&
886 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
889 while (MoreArgs (s));
894 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
896 ALIAS *tmp, *last = NULL;
900 mutt_extract_token (buf, s, 0);
902 if (mutt_strcmp ("*", buf->data) == 0)
904 if (CurrentMenu == MENU_ALIAS)
906 for (tmp = Aliases; tmp ; tmp = tmp->next)
908 set_option (OPTFORCEREDRAWINDEX);
911 mutt_free_alias (&Aliases);
915 for (tmp = Aliases; tmp; tmp = tmp->next)
917 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
919 if (CurrentMenu == MENU_ALIAS)
922 set_option (OPTFORCEREDRAWINDEX);
927 last->next = tmp->next;
931 mutt_free_alias (&tmp);
937 while (MoreArgs (s));
941 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
943 ALIAS *tmp = Aliases;
949 strfcpy (err->data, _("alias: no address"), err->dsize);
953 mutt_extract_token (buf, s, 0);
955 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
958 /* check to see if an alias with this name already exists */
959 for (; tmp; tmp = tmp->next)
961 if (!mutt_strcasecmp (tmp->name, buf->data))
968 /* create a new alias */
969 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
971 tmp->name = safe_strdup (buf->data);
972 /* give the main addressbook code a chance */
973 if (CurrentMenu == MENU_ALIAS)
974 set_option (OPTMENUCALLER);
978 /* override the previous value */
979 rfc822_free_address (&tmp->addr);
980 if (CurrentMenu == MENU_ALIAS)
981 set_option (OPTFORCEREDRAWINDEX);
984 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
985 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
987 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
992 if (mutt_addrlist_to_idna (tmp->addr, &estr))
994 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
1002 for (a = tmp->addr; a; a = a->next)
1005 dprint (2, (debugfile, "parse_alias: %s\n",
1008 dprint (2, (debugfile, "parse_alias: Group %s\n",
1017 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1020 LIST *tmp = UserHeader;
1026 mutt_extract_token (buf, s, 0);
1027 if (mutt_strcmp ("*", buf->data) == 0)
1028 mutt_free_list (&UserHeader);
1034 l = mutt_strlen (buf->data);
1035 if (buf->data[l - 1] == ':')
1040 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1044 last->next = tmp->next;
1046 UserHeader = tmp->next;
1049 mutt_free_list (&ptr);
1059 while (MoreArgs (s));
1063 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1069 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1070 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1072 strfcpy (err->data, _("invalid header field"), err->dsize);
1075 keylen = p - buf->data + 1;
1079 for (tmp = UserHeader; ; tmp = tmp->next)
1081 /* see if there is already a field by this name */
1082 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1084 /* replace the old value */
1086 tmp->data = buf->data;
1087 memset (buf, 0, sizeof (BUFFER));
1093 tmp->next = mutt_new_list ();
1098 tmp = mutt_new_list ();
1101 tmp->data = buf->data;
1102 memset (buf, 0, sizeof (BUFFER));
1107 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1111 if (mutt_strncmp ("reverse-", s, 8) == 0)
1114 flags = SORT_REVERSE;
1117 if (mutt_strncmp ("last-", s, 5) == 0)
1123 if ((i = mutt_getvaluebyname (s, map)) == -1)
1125 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1134 static void mutt_set_default (struct option_t *p)
1136 switch (p->type & DT_MASK)
1139 if (!p->init && *((char **) p->data))
1140 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1143 if (!p->init && *((char **) p->data))
1145 char *cp = safe_strdup (*((char **) p->data));
1146 /* mutt_pretty_mailbox (cp); */
1147 p->init = (unsigned long) cp;
1151 if (!p->init && *((ADDRESS **) p->data))
1153 char tmp[HUGE_STRING];
1155 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1156 p->init = (unsigned long) safe_strdup (tmp);
1161 REGEXP *pp = (REGEXP *) p->data;
1162 if (!p->init && pp->pattern)
1163 p->init = (unsigned long) safe_strdup (pp->pattern);
1169 static void mutt_restore_default (struct option_t *p)
1171 switch (p->type & DT_MASK)
1175 mutt_str_replace ((char **) p->data, (char *) p->init);
1180 char path[_POSIX_PATH_MAX];
1182 strfcpy (path, (char *) p->init, sizeof (path));
1183 mutt_expand_path (path, sizeof (path));
1184 mutt_str_replace ((char **) p->data, path);
1190 rfc822_free_address ((ADDRESS **) p->data);
1191 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1196 set_option (p->data);
1198 unset_option (p->data);
1201 set_quadoption (p->data, p->init);
1206 *((short *) p->data) = p->init;
1210 REGEXP *pp = (REGEXP *) p->data;
1213 FREE (&pp->pattern);
1222 char *s = (char *) p->init;
1224 pp->rx = safe_calloc (1, sizeof (regex_t));
1225 if (mutt_strcmp (p->option, "mask") != 0)
1226 flags |= mutt_which_case ((const char *) p->init);
1227 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1232 if (REGCOMP (pp->rx, s, flags) != 0)
1234 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1235 p->option, pp->pattern);
1236 FREE (&pp->pattern);
1245 if (p->flags & R_INDEX)
1246 set_option (OPTFORCEREDRAWINDEX);
1247 if (p->flags & R_PAGER)
1248 set_option (OPTFORCEREDRAWPAGER);
1249 if (p->flags & R_RESORT_SUB)
1250 set_option (OPTSORTSUBTHREADS);
1251 if (p->flags & R_RESORT)
1252 set_option (OPTNEEDRESORT);
1253 if (p->flags & R_RESORT_INIT)
1254 set_option (OPTRESORTINIT);
1255 if (p->flags & R_TREE)
1256 set_option (OPTREDRAWTREE);
1259 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1261 int idx, query, unset, inv, reset, r = 0;
1262 char *p, scratch[_POSIX_PATH_MAX];
1264 while (MoreArgs (s))
1266 /* reset state variables */
1268 unset = data & M_SET_UNSET;
1269 inv = data & M_SET_INV;
1270 reset = data & M_SET_RESET;
1272 if (*s->dptr == '?')
1277 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1282 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1287 else if (*s->dptr == '&')
1293 /* get the variable name */
1294 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1296 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1297 !(reset && !mutt_strcmp ("all", tmp->data)))
1299 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1306 if (query || unset || inv)
1308 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1312 if (s && *s->dptr == '=')
1314 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1318 if (!mutt_strcmp ("all", tmp->data))
1320 for (idx = 0; MuttVars[idx].option; idx++)
1321 mutt_restore_default (&MuttVars[idx]);
1325 mutt_restore_default (&MuttVars[idx]);
1327 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1329 if (s && *s->dptr == '=')
1331 if (unset || inv || query)
1333 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1338 mutt_extract_token (tmp, s, 0);
1339 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1341 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1345 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1352 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1353 ? _("%s is set") : _("%s is unset"), tmp->data);
1358 unset_option (MuttVars[idx].data);
1360 toggle_option (MuttVars[idx].data);
1362 set_option (MuttVars[idx].data);
1364 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1365 DTYPE (MuttVars[idx].type) == DT_PATH ||
1366 DTYPE (MuttVars[idx].type) == DT_ADDR)
1370 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1371 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1373 FREE ((void *)MuttVars[idx].data);
1375 else if (query || *s->dptr != '=')
1380 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1383 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1387 val = *((char **) MuttVars[idx].data);
1389 /* user requested the value of this variable */
1390 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1398 /* copy the value of the string */
1399 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1400 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1402 FREE ((void *)MuttVars[idx].data);
1404 mutt_extract_token (tmp, s, 0);
1405 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1407 strfcpy (scratch, tmp->data, sizeof (scratch));
1408 mutt_expand_path (scratch, sizeof (scratch));
1409 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1411 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1413 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1414 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1415 mutt_set_charset (Charset);
1419 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1423 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1425 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1429 if (query || *s->dptr != '=')
1431 /* user requested the value of this variable */
1432 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1433 NONULL (ptr->pattern));
1437 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1439 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1446 /* copy the value of the string */
1447 mutt_extract_token (tmp, s, 0);
1449 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1453 /* $mask is case-sensitive */
1454 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1455 flags |= mutt_which_case (tmp->data);
1458 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1467 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1468 if ((e = REGCOMP (rx, p, flags)) != 0)
1470 regerror (e, rx, err->data, err->dsize);
1476 /* get here only if everything went smootly */
1479 FREE (&ptr->pattern);
1480 regfree ((regex_t *) ptr->rx);
1484 ptr->pattern = safe_strdup (tmp->data);
1488 /* $reply_regexp and $alterantes require special treatment */
1490 if (Context && Context->msgcount &&
1491 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1493 regmatch_t pmatch[1];
1496 #define CUR_ENV Context->hdrs[i]->env
1497 for (i = 0; i < Context->msgcount; i++)
1499 if (CUR_ENV && CUR_ENV->subject)
1501 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1502 CUR_ENV->subject, 1, pmatch, 0)) ?
1504 CUR_ENV->subject + pmatch[0].rm_eo;
1511 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1513 if (query || *s->dptr != '=')
1515 switch (DefaultMagic)
1533 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1539 /* copy the value of the string */
1540 mutt_extract_token (tmp, s, 0);
1541 if (mx_set_magic (tmp->data))
1543 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1548 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1550 short *ptr = (short *) MuttVars[idx].data;
1554 if (query || *s->dptr != '=')
1556 /* user requested the value of this variable */
1557 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1563 mutt_extract_token (tmp, s, 0);
1564 val = strtol (tmp->data, &t, 0);
1566 if (!*tmp->data || *t || (short) val != val)
1568 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1575 /* these ones need a sanity check */
1576 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1580 mutt_init_history ();
1582 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1588 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1592 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1594 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1595 vals [ quadoption (MuttVars[idx].data) ]);
1599 if (*s->dptr == '=')
1602 mutt_extract_token (tmp, s, 0);
1603 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1604 set_quadoption (MuttVars[idx].data, M_YES);
1605 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1606 set_quadoption (MuttVars[idx].data, M_NO);
1607 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1608 set_quadoption (MuttVars[idx].data, M_ASKYES);
1609 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1610 set_quadoption (MuttVars[idx].data, M_ASKNO);
1613 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1621 toggle_quadoption (MuttVars[idx].data);
1623 set_quadoption (MuttVars[idx].data, M_NO);
1625 set_quadoption (MuttVars[idx].data, M_YES);
1628 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1630 const struct mapping_t *map = NULL;
1632 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1635 map = SortAliasMethods;
1637 case DT_SORT_BROWSER:
1638 map = SortBrowserMethods;
1641 if ((WithCrypto & APPLICATION_PGP))
1642 map = SortKeyMethods;
1645 map = SortAuxMethods;
1654 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1659 if (query || *s->dptr != '=')
1661 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1663 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1664 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1665 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1670 mutt_extract_token (tmp, s , 0);
1672 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1680 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1685 if (MuttVars[idx].flags & R_INDEX)
1686 set_option (OPTFORCEREDRAWINDEX);
1687 if (MuttVars[idx].flags & R_PAGER)
1688 set_option (OPTFORCEREDRAWPAGER);
1689 if (MuttVars[idx].flags & R_RESORT_SUB)
1690 set_option (OPTSORTSUBTHREADS);
1691 if (MuttVars[idx].flags & R_RESORT)
1692 set_option (OPTNEEDRESORT);
1693 if (MuttVars[idx].flags & R_RESORT_INIT)
1694 set_option (OPTRESORTINIT);
1695 if (MuttVars[idx].flags & R_TREE)
1696 set_option (OPTREDRAWTREE);
1703 /* reads the specified initialization file. returns -1 if errors were found
1704 so that we can pause to let the user know... */
1705 static int source_rc (const char *rcfile, BUFFER *err)
1708 int line = 0, rc = 0, conv = 0;
1710 char *linebuf = NULL;
1711 char *currentline = NULL;
1715 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1718 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1720 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1724 memset (&token, 0, sizeof (token));
1725 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1727 conv=ConfigCharset && (*ConfigCharset) && Charset;
1730 currentline=safe_strdup(linebuf);
1731 if (!currentline) continue;
1732 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1735 currentline=linebuf;
1737 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1739 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1740 if (--rc < -MAXERRS)
1742 if (conv) FREE(¤tline);
1758 mutt_wait_filter (pid);
1761 /* the muttrc source keyword */
1762 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1763 : _("source: reading aborted due too many errors in %s"), rcfile);
1771 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1773 char path[_POSIX_PATH_MAX];
1778 if (mutt_extract_token (tmp, s, 0) != 0)
1780 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1784 strfcpy (path, tmp->data, sizeof (path));
1785 mutt_expand_path (path, sizeof (path));
1787 rc += source_rc (path, err);
1789 while (MoreArgs (s));
1791 return ((rc < 0) ? -1 : 0);
1794 /* line command to execute
1796 token scratch buffer to be used by parser. caller should free
1797 token->data when finished. the reason for this variable is
1798 to avoid having to allocate and deallocate a lot of memory
1799 if we are parsing many lines. the caller can pass in the
1800 memory to use, which avoids having to create new space for
1801 every call to this function.
1803 err where to write error messages */
1804 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1809 memset (&expn, 0, sizeof (expn));
1810 expn.data = expn.dptr = line;
1811 expn.dsize = mutt_strlen (line);
1818 if (*expn.dptr == '#')
1819 break; /* rest of line is a comment */
1820 if (*expn.dptr == ';')
1825 mutt_extract_token (token, &expn, 0);
1826 for (i = 0; Commands[i].name; i++)
1828 if (!mutt_strcmp (token->data, Commands[i].name))
1830 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1835 if (!Commands[i].name)
1837 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1849 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1850 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1851 /* initial string that starts completion. No telling how much crap
1852 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1853 char User_typed [LONG_STRING] = {0};
1855 int Num_matched = 0; /* Number of matches for completion */
1856 char Completed [STRING] = {0}; /* completed string (command or variable) */
1857 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1859 /* helper function for completion. Changes the dest buffer if
1860 necessary/possible to aid completion.
1861 dest == completion result gets here.
1862 src == candidate for completion.
1863 try == user entered data for completion.
1864 len == length of dest buffer.
1866 static void candidate (char *dest, char *try, char *src, int len)
1870 if (strstr (src, try) == src)
1872 Matches[Num_matched++] = src;
1874 strfcpy (dest, src, len);
1877 for (l = 0; src[l] && src[l] == dest[l]; l++);
1883 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1887 int spaces; /* keep track of the number of leading spaces on the line */
1890 spaces = buffer - pt;
1892 pt = buffer + pos - spaces;
1893 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1896 if (pt == buffer) /* complete cmd */
1898 /* first TAB. Collect all the matches */
1902 strfcpy (User_typed, pt, sizeof (User_typed));
1903 memset (Matches, 0, sizeof (Matches));
1904 memset (Completed, 0, sizeof (Completed));
1905 for (num = 0; Commands[num].name; num++)
1906 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1907 Matches[Num_matched++] = User_typed;
1909 /* All matches are stored. Longest non-ambiguous string is ""
1910 * i.e. dont change 'buffer'. Fake successful return this time */
1911 if (User_typed[0] == 0)
1915 if (Completed[0] == 0 && User_typed[0])
1918 /* Num_matched will _always_ be atleast 1 since the initial
1919 * user-typed string is always stored */
1920 if (numtabs == 1 && Num_matched == 2)
1921 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1922 else if (numtabs > 1 && Num_matched > 2)
1923 /* cycle thru all the matches */
1924 snprintf(Completed, sizeof(Completed), "%s",
1925 Matches[(numtabs - 2) % Num_matched]);
1927 /* return the completed command */
1928 strncpy (buffer, Completed, len - spaces);
1930 else if (!mutt_strncmp (buffer, "set", 3)
1931 || !mutt_strncmp (buffer, "unset", 5)
1932 || !mutt_strncmp (buffer, "reset", 5)
1933 || !mutt_strncmp (buffer, "toggle", 6))
1934 { /* complete variables */
1935 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1938 /* loop through all the possible prefixes (no, inv, ...) */
1939 if (!mutt_strncmp (buffer, "set", 3))
1941 for (num = 0; prefixes[num]; num++)
1943 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1945 pt += mutt_strlen (prefixes[num]);
1951 /* first TAB. Collect all the matches */
1955 strfcpy (User_typed, pt, sizeof (User_typed));
1956 memset (Matches, 0, sizeof (Matches));
1957 memset (Completed, 0, sizeof (Completed));
1958 for (num = 0; MuttVars[num].option; num++)
1959 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1960 Matches[Num_matched++] = User_typed;
1962 /* All matches are stored. Longest non-ambiguous string is ""
1963 * i.e. dont change 'buffer'. Fake successful return this time */
1964 if (User_typed[0] == 0)
1968 if (Completed[0] == 0 && User_typed[0])
1971 /* Num_matched will _always_ be atleast 1 since the initial
1972 * user-typed string is always stored */
1973 if (numtabs == 1 && Num_matched == 2)
1974 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1975 else if (numtabs > 1 && Num_matched > 2)
1976 /* cycle thru all the matches */
1977 snprintf(Completed, sizeof(Completed), "%s",
1978 Matches[(numtabs - 2) % Num_matched]);
1980 strncpy (pt, Completed, buffer + len - pt - spaces);
1982 else if (!mutt_strncmp (buffer, "exec", 4))
1984 struct binding_t *menu = km_get_table (CurrentMenu);
1986 if (!menu && CurrentMenu != MENU_PAGER)
1990 /* first TAB. Collect all the matches */
1994 strfcpy (User_typed, pt, sizeof (User_typed));
1995 memset (Matches, 0, sizeof (Matches));
1996 memset (Completed, 0, sizeof (Completed));
1997 for (num = 0; menu[num].name; num++)
1998 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1999 /* try the generic menu */
2000 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
2003 for (num = 0; menu[num].name; num++)
2004 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2006 Matches[Num_matched++] = User_typed;
2008 /* All matches are stored. Longest non-ambiguous string is ""
2009 * i.e. dont change 'buffer'. Fake successful return this time */
2010 if (User_typed[0] == 0)
2014 if (Completed[0] == 0 && User_typed[0])
2017 /* Num_matched will _always_ be atleast 1 since the initial
2018 * user-typed string is always stored */
2019 if (numtabs == 1 && Num_matched == 2)
2020 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2021 else if (numtabs > 1 && Num_matched > 2)
2022 /* cycle thru all the matches */
2023 snprintf(Completed, sizeof(Completed), "%s",
2024 Matches[(numtabs - 2) % Num_matched]);
2026 strncpy (pt, Completed, buffer + len - pt - spaces);
2034 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2036 char var[STRING], *pt = buffer;
2043 spaces = buffer - pt;
2045 pt = buffer + pos - spaces;
2046 while ((pt > buffer) && !isspace ((unsigned char) *pt))
2048 pt++; /* move past the space */
2049 if (*pt == '=') /* abort if no var before the '=' */
2052 if (mutt_strncmp (buffer, "set", 3) == 0)
2055 strfcpy (var, pt, sizeof (var));
2056 /* ignore the trailing '=' when comparing */
2057 var[mutt_strlen (var) - 1] = 0;
2058 if ((idx = mutt_option_index (var)) == -1)
2059 return 0; /* no such variable. */
2062 char tmp [LONG_STRING], tmp2[LONG_STRING];
2064 size_t dlen = buffer + len - pt - spaces;
2065 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2069 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2070 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2071 (DTYPE(MuttVars[idx].type) == DT_RX))
2073 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2074 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2075 mutt_pretty_mailbox (tmp);
2077 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2079 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2081 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2082 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2083 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2084 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2085 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2087 const struct mapping_t *map;
2090 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2093 map = SortAliasMethods;
2095 case DT_SORT_BROWSER:
2096 map = SortBrowserMethods;
2099 if ((WithCrypto & APPLICATION_PGP))
2100 map = SortKeyMethods;
2108 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2109 snprintf (tmp, sizeof (tmp), "%s%s%s",
2110 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2111 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2114 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2115 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2119 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2121 if (*s == '\\' || *s == '"')
2127 strfcpy (tmp, pt, sizeof (tmp));
2128 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2136 /* Implement the -Q command line flag */
2137 int mutt_query_variables (LIST *queries)
2141 char errbuff[STRING];
2142 char command[STRING];
2146 memset (&err, 0, sizeof (err));
2147 memset (&token, 0, sizeof (token));
2150 err.dsize = sizeof (errbuff);
2152 for (p = queries; p; p = p->next)
2154 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2155 if (mutt_parse_rc_line (command, &token, &err) == -1)
2157 fprintf (stderr, "%s\n", err.data);
2161 printf ("%s\n", err.data);
2168 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2172 for (i=0; map[i].name; i++)
2173 if (map[i].value == val)
2174 return (map[i].name);
2178 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2182 for (i = 0; map[i].name; i++)
2183 if (ascii_strcasecmp (map[i].name, name) == 0)
2184 return (map[i].value);
2189 static void start_debug (void)
2193 char buf[_POSIX_PATH_MAX];
2194 char buf2[_POSIX_PATH_MAX];
2196 /* rotate the old debug logs */
2197 for (i=3; i>=0; i--)
2199 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2200 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2203 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2206 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2207 fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2208 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2213 static int mutt_execute_commands (LIST *p)
2216 char errstr[SHORT_STRING];
2218 memset (&err, 0, sizeof (err));
2220 err.dsize = sizeof (errstr);
2221 memset (&token, 0, sizeof (token));
2222 for (; p; p = p->next)
2224 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2226 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2235 void mutt_init (int skip_sys_rc, LIST *commands)
2238 struct utsname utsname;
2239 char *p, buffer[STRING], error[STRING];
2240 int i, default_rc = 0, need_pause = 0;
2243 memset (&err, 0, sizeof (err));
2245 err.dsize = sizeof (error);
2248 * XXX - use something even more difficult to predict?
2250 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2251 "\033]9;%ld\a", (long) time (NULL));
2253 /* on one of the systems I use, getcwd() does not return the same prefix
2254 as is listed in the passwd file */
2255 if ((p = getenv ("HOME")))
2256 Homedir = safe_strdup (p);
2258 /* Get some information about the user */
2259 if ((pw = getpwuid (getuid ())))
2263 Username = safe_strdup (pw->pw_name);
2265 Homedir = safe_strdup (pw->pw_dir);
2267 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2268 Shell = safe_strdup (pw->pw_shell);
2275 fputs (_("unable to determine home directory"), stderr);
2278 if ((p = getenv ("USER")))
2279 Username = safe_strdup (p);
2283 fputs (_("unable to determine username"), stderr);
2286 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2290 /* Start up debugging mode if requested */
2295 /* And about the host... */
2297 /* some systems report the FQDN instead of just the hostname */
2298 if ((p = strchr (utsname.nodename, '.')))
2300 Hostname = mutt_substrdup (utsname.nodename, p);
2302 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2305 Hostname = safe_strdup (utsname.nodename);
2308 #define DOMAIN buffer
2309 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2310 Fqdn = safe_strdup ("@");
2315 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2316 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2319 Fqdn = safe_strdup(NONULL(Hostname));
2326 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2329 fgets (buffer, sizeof (buffer), f);
2333 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2335 NewsServer = safe_strdup (p);
2339 if ((p = getenv ("NNTPSERVER")))
2340 NewsServer = safe_strdup (p);
2343 if ((p = getenv ("MAIL")))
2344 Spoolfile = safe_strdup (p);
2345 else if ((p = getenv ("MAILDIR")))
2346 Spoolfile = safe_strdup (p);
2350 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2352 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2354 Spoolfile = safe_strdup (buffer);
2357 if ((p = getenv ("MAILCAPS")))
2358 MailcapPath = safe_strdup (p);
2361 /* Default search path from RFC1524 */
2362 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2365 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2367 p = getenv ("VISUAL");
2370 p = getenv ("EDITOR");
2374 Editor = safe_strdup (p);
2375 Visual = safe_strdup (p);
2377 if ((p = getenv ("REPLYTO")) != NULL)
2381 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2383 memset (&buf, 0, sizeof (buf));
2384 buf.data = buf.dptr = buffer;
2385 buf.dsize = mutt_strlen (buffer);
2387 memset (&token, 0, sizeof (token));
2388 parse_my_hdr (&token, &buf, 0, &err);
2392 if ((p = getenv ("EMAIL")) != NULL)
2393 From = rfc822_parse_adrlist (NULL, p);
2395 mutt_set_langinfo_charset ();
2396 mutt_set_charset (Charset);
2399 /* Set standard defaults */
2400 for (i = 0; MuttVars[i].option; i++)
2402 mutt_set_default (&MuttVars[i]);
2403 mutt_restore_default (&MuttVars[i]);
2406 CurrentMenu = MENU_MAIN;
2409 #ifndef LOCALES_HACK
2410 /* Do we have a locale definition? */
2411 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2412 ((p = getenv ("LANG")) != NULL && p[0]) ||
2413 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2414 set_option (OPTLOCALES);
2418 /* Unset suspend by default if we're the session leader */
2419 if (getsid(0) == getpid())
2420 unset_option (OPTSUSPEND);
2423 mutt_init_history ();
2432 * When changing the code which looks for a configuration file,
2433 * please also change the corresponding code in muttbug.sh.in.
2443 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2444 if (access(buffer, F_OK) == -1)
2445 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2446 if (access(buffer, F_OK) == -1)
2447 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2448 if (access(buffer, F_OK) == -1)
2449 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2452 Muttrc = safe_strdup (buffer);
2456 strfcpy (buffer, Muttrc, sizeof (buffer));
2458 mutt_expand_path (buffer, sizeof (buffer));
2459 Muttrc = safe_strdup (buffer);
2462 AliasFile = safe_strdup (NONULL(Muttrc));
2464 /* Process the global rc file if it exists and the user hasn't explicity
2465 requested not to via "-n". */
2468 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2469 if (access (buffer, F_OK) == -1)
2470 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2471 if (access (buffer, F_OK) == -1)
2472 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2473 if (access (buffer, F_OK) == -1)
2474 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2475 if (access (buffer, F_OK) != -1)
2477 if (source_rc (buffer, &err) != 0)
2479 fputs (err.data, stderr);
2480 fputc ('\n', stderr);
2486 /* Read the user's initialization file. */
2487 if (access (Muttrc, F_OK) != -1)
2489 if (!option (OPTNOCURSES))
2491 if (source_rc (Muttrc, &err) != 0)
2493 fputs (err.data, stderr);
2494 fputc ('\n', stderr);
2498 else if (!default_rc)
2500 /* file specified by -F does not exist */
2501 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2502 mutt_endwin (buffer);
2506 if (mutt_execute_commands (commands) != 0)
2509 if (need_pause && !option (OPTNOCURSES))
2511 if (mutt_any_key_to_continue (NULL) == -1)
2516 set_option (OPTWEED); /* turn weeding on by default */
2520 int mutt_get_hook_type (const char *name)
2522 struct command_t *c;
2524 for (c = Commands ; c->name ; c++)
2525 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)