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_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
549 mutt_extract_token (buf, s, 0);
551 /* don't add "*" to the unignore list */
552 if (strcmp (buf->data, "*"))
553 add_to_list (&UnIgnore, buf->data);
555 remove_from_list (&Ignore, buf->data);
557 while (MoreArgs (s));
562 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
566 mutt_extract_token (buf, s, 0);
567 remove_from_list (&UnIgnore, buf->data);
568 add_to_list (&Ignore, buf->data);
570 while (MoreArgs (s));
575 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
579 mutt_extract_token (buf, s, 0);
580 add_to_list ((LIST **) data, buf->data);
582 while (MoreArgs (s));
587 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
591 mutt_extract_token (buf, s, 0);
592 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
596 while (MoreArgs (s));
601 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
603 return _parse_rx_list (buf, s, data, err, REG_ICASE);
606 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
610 mutt_extract_token (buf, s, 0);
611 if (mutt_strcmp (buf->data, "*") == 0)
613 mutt_free_rx_list ((RX_LIST **) data);
616 remove_from_rx_list ((RX_LIST **) data, buf->data);
618 while (MoreArgs (s));
623 static void _alternates_clean (void)
626 if (Context && Context->msgcount)
628 for (i = 0; i < Context->msgcount; i++)
629 Context->hdrs[i]->recip_valid = 0;
633 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
638 mutt_extract_token (buf, s, 0);
639 remove_from_rx_list (&UnAlternates, buf->data);
641 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
644 while (MoreArgs (s));
649 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
654 mutt_extract_token (buf, s, 0);
655 remove_from_rx_list (&Alternates, buf->data);
657 if (mutt_strcmp (buf->data, "*") &&
658 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
662 while (MoreArgs (s));
667 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
671 memset(&templ, 0, sizeof(templ));
673 /* Insist on at least one parameter */
677 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
679 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
683 /* Extract the first token, a regexp */
684 mutt_extract_token (buf, s, 0);
686 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
689 /* If there's a second parameter, it's a template for the spam tag. */
692 mutt_extract_token (&templ, s, 0);
694 /* Add to the spam list. */
695 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
702 /* If not, try to remove from the nospam list. */
705 remove_from_rx_list(&NoSpamList, buf->data);
711 /* M_NOSPAM is for nospam commands. */
712 else if (data == M_NOSPAM)
714 /* nospam only ever has one parameter. */
716 /* "*" is a special case. */
717 if (!mutt_strcmp(buf->data, "*"))
719 mutt_free_spam_list (&SpamList);
720 mutt_free_rx_list (&NoSpamList);
724 /* If it's on the spam list, just remove it. */
725 if (remove_from_spam_list(&SpamList, buf->data) != 0)
728 /* Otherwise, add it to the nospam list. */
729 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
735 /* This should not happen. */
736 strfcpy(err->data, "This is no good at all.", err->dsize);
740 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
744 mutt_extract_token (buf, s, 0);
746 * Check for deletion of entire list
748 if (mutt_strcmp (buf->data, "*") == 0)
750 mutt_free_list ((LIST **) data);
753 remove_from_list ((LIST **) data, buf->data);
755 while (MoreArgs (s));
760 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
764 mutt_extract_token (buf, s, 0);
765 remove_from_rx_list (&UnMailLists, buf->data);
767 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
770 while (MoreArgs (s));
775 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
779 mutt_extract_token (buf, s, 0);
780 remove_from_rx_list (&SubscribedLists, buf->data);
781 remove_from_rx_list (&MailLists, buf->data);
783 if (mutt_strcmp (buf->data, "*") &&
784 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
787 while (MoreArgs (s));
792 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
796 mutt_extract_token (buf, s, 0);
797 remove_from_rx_list (&UnMailLists, buf->data);
798 remove_from_rx_list (&UnSubscribedLists, buf->data);
800 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
802 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
805 while (MoreArgs (s));
810 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
814 mutt_extract_token (buf, s, 0);
815 remove_from_rx_list (&SubscribedLists, buf->data);
817 if (mutt_strcmp (buf->data, "*") &&
818 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
821 while (MoreArgs (s));
826 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
828 ALIAS *tmp, *last = NULL;
832 mutt_extract_token (buf, s, 0);
834 if (mutt_strcmp ("*", buf->data) == 0)
836 if (CurrentMenu == MENU_ALIAS)
838 for (tmp = Aliases; tmp ; tmp = tmp->next)
840 set_option (OPTFORCEREDRAWINDEX);
843 mutt_free_alias (&Aliases);
847 for (tmp = Aliases; tmp; tmp = tmp->next)
849 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
851 if (CurrentMenu == MENU_ALIAS)
854 set_option (OPTFORCEREDRAWINDEX);
859 last->next = tmp->next;
863 mutt_free_alias (&tmp);
869 while (MoreArgs (s));
873 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
875 ALIAS *tmp = Aliases;
881 strfcpy (err->data, _("alias: no address"), err->dsize);
885 mutt_extract_token (buf, s, 0);
887 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
890 /* check to see if an alias with this name already exists */
891 for (; tmp; tmp = tmp->next)
893 if (!mutt_strcasecmp (tmp->name, buf->data))
900 /* create a new alias */
901 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
903 tmp->name = safe_strdup (buf->data);
904 /* give the main addressbook code a chance */
905 if (CurrentMenu == MENU_ALIAS)
906 set_option (OPTMENUCALLER);
910 /* override the previous value */
911 rfc822_free_address (&tmp->addr);
912 if (CurrentMenu == MENU_ALIAS)
913 set_option (OPTFORCEREDRAWINDEX);
916 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
917 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
919 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
924 if (mutt_addrlist_to_idna (tmp->addr, &estr))
926 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
934 for (a = tmp->addr; a; a = a->next)
937 dprint (2, (debugfile, "parse_alias: %s\n",
940 dprint (2, (debugfile, "parse_alias: Group %s\n",
949 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
952 LIST *tmp = UserHeader;
958 mutt_extract_token (buf, s, 0);
959 if (mutt_strcmp ("*", buf->data) == 0)
960 mutt_free_list (&UserHeader);
966 l = mutt_strlen (buf->data);
967 if (buf->data[l - 1] == ':')
972 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
976 last->next = tmp->next;
978 UserHeader = tmp->next;
981 mutt_free_list (&ptr);
991 while (MoreArgs (s));
995 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1001 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1002 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1004 strfcpy (err->data, _("invalid header field"), err->dsize);
1007 keylen = p - buf->data + 1;
1011 for (tmp = UserHeader; ; tmp = tmp->next)
1013 /* see if there is already a field by this name */
1014 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1016 /* replace the old value */
1018 tmp->data = buf->data;
1019 memset (buf, 0, sizeof (BUFFER));
1025 tmp->next = mutt_new_list ();
1030 tmp = mutt_new_list ();
1033 tmp->data = buf->data;
1034 memset (buf, 0, sizeof (BUFFER));
1039 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1043 if (mutt_strncmp ("reverse-", s, 8) == 0)
1046 flags = SORT_REVERSE;
1049 if (mutt_strncmp ("last-", s, 5) == 0)
1055 if ((i = mutt_getvaluebyname (s, map)) == -1)
1057 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1066 static void mutt_set_default (struct option_t *p)
1068 switch (p->type & DT_MASK)
1071 if (!p->init && *((char **) p->data))
1072 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1075 if (!p->init && *((char **) p->data))
1077 char *cp = safe_strdup (*((char **) p->data));
1078 /* mutt_pretty_mailbox (cp); */
1079 p->init = (unsigned long) cp;
1083 if (!p->init && *((ADDRESS **) p->data))
1085 char tmp[HUGE_STRING];
1087 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1088 p->init = (unsigned long) safe_strdup (tmp);
1093 REGEXP *pp = (REGEXP *) p->data;
1094 if (!p->init && pp->pattern)
1095 p->init = (unsigned long) safe_strdup (pp->pattern);
1101 static void mutt_restore_default (struct option_t *p)
1103 switch (p->type & DT_MASK)
1107 mutt_str_replace ((char **) p->data, (char *) p->init);
1112 char path[_POSIX_PATH_MAX];
1114 strfcpy (path, (char *) p->init, sizeof (path));
1115 mutt_expand_path (path, sizeof (path));
1116 mutt_str_replace ((char **) p->data, path);
1122 rfc822_free_address ((ADDRESS **) p->data);
1123 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1128 set_option (p->data);
1130 unset_option (p->data);
1133 set_quadoption (p->data, p->init);
1138 *((short *) p->data) = p->init;
1142 REGEXP *pp = (REGEXP *) p->data;
1145 FREE (&pp->pattern);
1154 char *s = (char *) p->init;
1156 pp->rx = safe_calloc (1, sizeof (regex_t));
1157 if (mutt_strcmp (p->option, "mask") != 0)
1158 flags |= mutt_which_case ((const char *) p->init);
1159 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1164 if (REGCOMP (pp->rx, s, flags) != 0)
1166 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1167 p->option, pp->pattern);
1168 FREE (&pp->pattern);
1177 if (p->flags & R_INDEX)
1178 set_option (OPTFORCEREDRAWINDEX);
1179 if (p->flags & R_PAGER)
1180 set_option (OPTFORCEREDRAWPAGER);
1181 if (p->flags & R_RESORT_SUB)
1182 set_option (OPTSORTSUBTHREADS);
1183 if (p->flags & R_RESORT)
1184 set_option (OPTNEEDRESORT);
1185 if (p->flags & R_RESORT_INIT)
1186 set_option (OPTRESORTINIT);
1187 if (p->flags & R_TREE)
1188 set_option (OPTREDRAWTREE);
1191 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1193 int idx, query, unset, inv, reset, r = 0;
1194 char *p, scratch[_POSIX_PATH_MAX];
1196 while (MoreArgs (s))
1198 /* reset state variables */
1200 unset = data & M_SET_UNSET;
1201 inv = data & M_SET_INV;
1202 reset = data & M_SET_RESET;
1204 if (*s->dptr == '?')
1209 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1214 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1219 else if (*s->dptr == '&')
1225 /* get the variable name */
1226 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1228 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1229 !(reset && !mutt_strcmp ("all", tmp->data)))
1231 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1238 if (query || unset || inv)
1240 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1244 if (s && *s->dptr == '=')
1246 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1250 if (!mutt_strcmp ("all", tmp->data))
1252 for (idx = 0; MuttVars[idx].option; idx++)
1253 mutt_restore_default (&MuttVars[idx]);
1257 mutt_restore_default (&MuttVars[idx]);
1259 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1261 if (s && *s->dptr == '=')
1263 if (unset || inv || query)
1265 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1270 mutt_extract_token (tmp, s, 0);
1271 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1273 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1277 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1284 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1285 ? _("%s is set") : _("%s is unset"), tmp->data);
1290 unset_option (MuttVars[idx].data);
1292 toggle_option (MuttVars[idx].data);
1294 set_option (MuttVars[idx].data);
1296 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1297 DTYPE (MuttVars[idx].type) == DT_PATH ||
1298 DTYPE (MuttVars[idx].type) == DT_ADDR)
1302 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1303 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1305 FREE ((void *)MuttVars[idx].data);
1307 else if (query || *s->dptr != '=')
1312 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1315 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1319 val = *((char **) MuttVars[idx].data);
1321 /* user requested the value of this variable */
1322 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1330 /* copy the value of the string */
1331 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1332 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1334 FREE ((void *)MuttVars[idx].data);
1336 mutt_extract_token (tmp, s, 0);
1337 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1339 strfcpy (scratch, tmp->data, sizeof (scratch));
1340 mutt_expand_path (scratch, sizeof (scratch));
1341 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1343 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1345 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1346 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1347 mutt_set_charset (Charset);
1351 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1355 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1357 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1361 if (query || *s->dptr != '=')
1363 /* user requested the value of this variable */
1364 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1365 NONULL (ptr->pattern));
1369 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1371 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1378 /* copy the value of the string */
1379 mutt_extract_token (tmp, s, 0);
1381 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1385 /* $mask is case-sensitive */
1386 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1387 flags |= mutt_which_case (tmp->data);
1390 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1399 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1400 if ((e = REGCOMP (rx, p, flags)) != 0)
1402 regerror (e, rx, err->data, err->dsize);
1408 /* get here only if everything went smootly */
1411 FREE (&ptr->pattern);
1412 regfree ((regex_t *) ptr->rx);
1416 ptr->pattern = safe_strdup (tmp->data);
1420 /* $reply_regexp and $alterantes require special treatment */
1422 if (Context && Context->msgcount &&
1423 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1425 regmatch_t pmatch[1];
1428 #define CUR_ENV Context->hdrs[i]->env
1429 for (i = 0; i < Context->msgcount; i++)
1431 if (CUR_ENV && CUR_ENV->subject)
1433 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1434 CUR_ENV->subject, 1, pmatch, 0)) ?
1436 CUR_ENV->subject + pmatch[0].rm_eo;
1443 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1445 if (query || *s->dptr != '=')
1447 switch (DefaultMagic)
1465 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1471 /* copy the value of the string */
1472 mutt_extract_token (tmp, s, 0);
1473 if (mx_set_magic (tmp->data))
1475 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1480 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1482 short *ptr = (short *) MuttVars[idx].data;
1486 if (query || *s->dptr != '=')
1488 /* user requested the value of this variable */
1489 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1495 mutt_extract_token (tmp, s, 0);
1496 val = strtol (tmp->data, &t, 0);
1498 if (!*tmp->data || *t || (short) val != val)
1500 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1507 /* these ones need a sanity check */
1508 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1512 mutt_init_history ();
1514 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1520 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1524 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1526 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1527 vals [ quadoption (MuttVars[idx].data) ]);
1531 if (*s->dptr == '=')
1534 mutt_extract_token (tmp, s, 0);
1535 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1536 set_quadoption (MuttVars[idx].data, M_YES);
1537 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1538 set_quadoption (MuttVars[idx].data, M_NO);
1539 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1540 set_quadoption (MuttVars[idx].data, M_ASKYES);
1541 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1542 set_quadoption (MuttVars[idx].data, M_ASKNO);
1545 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1553 toggle_quadoption (MuttVars[idx].data);
1555 set_quadoption (MuttVars[idx].data, M_NO);
1557 set_quadoption (MuttVars[idx].data, M_YES);
1560 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1562 const struct mapping_t *map = NULL;
1564 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1567 map = SortAliasMethods;
1569 case DT_SORT_BROWSER:
1570 map = SortBrowserMethods;
1573 if ((WithCrypto & APPLICATION_PGP))
1574 map = SortKeyMethods;
1577 map = SortAuxMethods;
1586 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1591 if (query || *s->dptr != '=')
1593 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1595 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1596 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1597 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1602 mutt_extract_token (tmp, s , 0);
1604 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1612 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1617 if (MuttVars[idx].flags & R_INDEX)
1618 set_option (OPTFORCEREDRAWINDEX);
1619 if (MuttVars[idx].flags & R_PAGER)
1620 set_option (OPTFORCEREDRAWPAGER);
1621 if (MuttVars[idx].flags & R_RESORT_SUB)
1622 set_option (OPTSORTSUBTHREADS);
1623 if (MuttVars[idx].flags & R_RESORT)
1624 set_option (OPTNEEDRESORT);
1625 if (MuttVars[idx].flags & R_RESORT_INIT)
1626 set_option (OPTRESORTINIT);
1627 if (MuttVars[idx].flags & R_TREE)
1628 set_option (OPTREDRAWTREE);
1635 /* reads the specified initialization file. returns -1 if errors were found
1636 so that we can pause to let the user know... */
1637 static int source_rc (const char *rcfile, BUFFER *err)
1640 int line = 0, rc = 0, conv = 0;
1642 char *linebuf = NULL;
1643 char *currentline = NULL;
1647 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1650 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1652 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1656 memset (&token, 0, sizeof (token));
1657 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1659 conv=ConfigCharset && (*ConfigCharset) && Charset;
1662 currentline=safe_strdup(linebuf);
1663 if (!currentline) continue;
1664 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1667 currentline=linebuf;
1669 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1671 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1672 if (--rc < -MAXERRS)
1674 if (conv) FREE(¤tline);
1690 mutt_wait_filter (pid);
1693 /* the muttrc source keyword */
1694 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1695 : _("source: reading aborted due too many errors in %s"), rcfile);
1703 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1705 char path[_POSIX_PATH_MAX];
1710 if (mutt_extract_token (tmp, s, 0) != 0)
1712 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1716 strfcpy (path, tmp->data, sizeof (path));
1717 mutt_expand_path (path, sizeof (path));
1719 rc += source_rc (path, err);
1721 while (MoreArgs (s));
1723 return ((rc < 0) ? -1 : 0);
1726 /* line command to execute
1728 token scratch buffer to be used by parser. caller should free
1729 token->data when finished. the reason for this variable is
1730 to avoid having to allocate and deallocate a lot of memory
1731 if we are parsing many lines. the caller can pass in the
1732 memory to use, which avoids having to create new space for
1733 every call to this function.
1735 err where to write error messages */
1736 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1741 memset (&expn, 0, sizeof (expn));
1742 expn.data = expn.dptr = line;
1743 expn.dsize = mutt_strlen (line);
1750 if (*expn.dptr == '#')
1751 break; /* rest of line is a comment */
1752 if (*expn.dptr == ';')
1757 mutt_extract_token (token, &expn, 0);
1758 for (i = 0; Commands[i].name; i++)
1760 if (!mutt_strcmp (token->data, Commands[i].name))
1762 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1767 if (!Commands[i].name)
1769 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1781 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1782 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1783 /* initial string that starts completion. No telling how much crap
1784 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1785 char User_typed [LONG_STRING] = {0};
1787 int Num_matched = 0; /* Number of matches for completion */
1788 char Completed [STRING] = {0}; /* completed string (command or variable) */
1789 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1791 /* helper function for completion. Changes the dest buffer if
1792 necessary/possible to aid completion.
1793 dest == completion result gets here.
1794 src == candidate for completion.
1795 try == user entered data for completion.
1796 len == length of dest buffer.
1798 static void candidate (char *dest, char *try, char *src, int len)
1802 if (strstr (src, try) == src)
1804 Matches[Num_matched++] = src;
1806 strfcpy (dest, src, len);
1809 for (l = 0; src[l] && src[l] == dest[l]; l++);
1815 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1819 int spaces; /* keep track of the number of leading spaces on the line */
1822 spaces = buffer - pt;
1824 pt = buffer + pos - spaces;
1825 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1828 if (pt == buffer) /* complete cmd */
1830 /* first TAB. Collect all the matches */
1834 strfcpy (User_typed, pt, sizeof (User_typed));
1835 memset (Matches, 0, sizeof (Matches));
1836 memset (Completed, 0, sizeof (Completed));
1837 for (num = 0; Commands[num].name; num++)
1838 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1839 Matches[Num_matched++] = User_typed;
1841 /* All matches are stored. Longest non-ambiguous string is ""
1842 * i.e. dont change 'buffer'. Fake successful return this time */
1843 if (User_typed[0] == 0)
1847 if (Completed[0] == 0 && User_typed[0])
1850 /* Num_matched will _always_ be atleast 1 since the initial
1851 * user-typed string is always stored */
1852 if (numtabs == 1 && Num_matched == 2)
1853 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1854 else if (numtabs > 1 && Num_matched > 2)
1855 /* cycle thru all the matches */
1856 snprintf(Completed, sizeof(Completed), "%s",
1857 Matches[(numtabs - 2) % Num_matched]);
1859 /* return the completed command */
1860 strncpy (buffer, Completed, len - spaces);
1862 else if (!mutt_strncmp (buffer, "set", 3)
1863 || !mutt_strncmp (buffer, "unset", 5)
1864 || !mutt_strncmp (buffer, "reset", 5)
1865 || !mutt_strncmp (buffer, "toggle", 6))
1866 { /* complete variables */
1867 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1870 /* loop through all the possible prefixes (no, inv, ...) */
1871 if (!mutt_strncmp (buffer, "set", 3))
1873 for (num = 0; prefixes[num]; num++)
1875 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1877 pt += mutt_strlen (prefixes[num]);
1883 /* first TAB. Collect all the matches */
1887 strfcpy (User_typed, pt, sizeof (User_typed));
1888 memset (Matches, 0, sizeof (Matches));
1889 memset (Completed, 0, sizeof (Completed));
1890 for (num = 0; MuttVars[num].option; num++)
1891 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1892 Matches[Num_matched++] = User_typed;
1894 /* All matches are stored. Longest non-ambiguous string is ""
1895 * i.e. dont change 'buffer'. Fake successful return this time */
1896 if (User_typed[0] == 0)
1900 if (Completed[0] == 0 && User_typed[0])
1903 /* Num_matched will _always_ be atleast 1 since the initial
1904 * user-typed string is always stored */
1905 if (numtabs == 1 && Num_matched == 2)
1906 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1907 else if (numtabs > 1 && Num_matched > 2)
1908 /* cycle thru all the matches */
1909 snprintf(Completed, sizeof(Completed), "%s",
1910 Matches[(numtabs - 2) % Num_matched]);
1912 strncpy (pt, Completed, buffer + len - pt - spaces);
1914 else if (!mutt_strncmp (buffer, "exec", 4))
1916 struct binding_t *menu = km_get_table (CurrentMenu);
1918 if (!menu && CurrentMenu != MENU_PAGER)
1922 /* first TAB. Collect all the matches */
1926 strfcpy (User_typed, pt, sizeof (User_typed));
1927 memset (Matches, 0, sizeof (Matches));
1928 memset (Completed, 0, sizeof (Completed));
1929 for (num = 0; menu[num].name; num++)
1930 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1931 /* try the generic menu */
1932 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
1935 for (num = 0; menu[num].name; num++)
1936 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1938 Matches[Num_matched++] = User_typed;
1940 /* All matches are stored. Longest non-ambiguous string is ""
1941 * i.e. dont change 'buffer'. Fake successful return this time */
1942 if (User_typed[0] == 0)
1946 if (Completed[0] == 0 && User_typed[0])
1949 /* Num_matched will _always_ be atleast 1 since the initial
1950 * user-typed string is always stored */
1951 if (numtabs == 1 && Num_matched == 2)
1952 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1953 else if (numtabs > 1 && Num_matched > 2)
1954 /* cycle thru all the matches */
1955 snprintf(Completed, sizeof(Completed), "%s",
1956 Matches[(numtabs - 2) % Num_matched]);
1958 strncpy (pt, Completed, buffer + len - pt - spaces);
1966 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1968 char var[STRING], *pt = buffer;
1975 spaces = buffer - pt;
1977 pt = buffer + pos - spaces;
1978 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1980 pt++; /* move past the space */
1981 if (*pt == '=') /* abort if no var before the '=' */
1984 if (mutt_strncmp (buffer, "set", 3) == 0)
1987 strfcpy (var, pt, sizeof (var));
1988 /* ignore the trailing '=' when comparing */
1989 var[mutt_strlen (var) - 1] = 0;
1990 if ((idx = mutt_option_index (var)) == -1)
1991 return 0; /* no such variable. */
1994 char tmp [LONG_STRING], tmp2[LONG_STRING];
1996 size_t dlen = buffer + len - pt - spaces;
1997 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2001 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2002 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2003 (DTYPE(MuttVars[idx].type) == DT_RX))
2005 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2006 if (DTYPE (MuttVars[idx].type) == DT_PATH)
2007 mutt_pretty_mailbox (tmp);
2009 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2011 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2013 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2014 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2015 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2016 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2017 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2019 const struct mapping_t *map;
2022 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2025 map = SortAliasMethods;
2027 case DT_SORT_BROWSER:
2028 map = SortBrowserMethods;
2031 if ((WithCrypto & APPLICATION_PGP))
2032 map = SortKeyMethods;
2040 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2041 snprintf (tmp, sizeof (tmp), "%s%s%s",
2042 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2043 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2046 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2047 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2051 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2053 if (*s == '\\' || *s == '"')
2059 strfcpy (tmp, pt, sizeof (tmp));
2060 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2068 /* Implement the -Q command line flag */
2069 int mutt_query_variables (LIST *queries)
2073 char errbuff[STRING];
2074 char command[STRING];
2078 memset (&err, 0, sizeof (err));
2079 memset (&token, 0, sizeof (token));
2082 err.dsize = sizeof (errbuff);
2084 for (p = queries; p; p = p->next)
2086 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2087 if (mutt_parse_rc_line (command, &token, &err) == -1)
2089 fprintf (stderr, "%s\n", err.data);
2093 printf ("%s\n", err.data);
2100 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2104 for (i=0; map[i].name; i++)
2105 if (map[i].value == val)
2106 return (map[i].name);
2110 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2114 for (i = 0; map[i].name; i++)
2115 if (ascii_strcasecmp (map[i].name, name) == 0)
2116 return (map[i].value);
2121 static void start_debug (void)
2125 char buf[_POSIX_PATH_MAX];
2126 char buf2[_POSIX_PATH_MAX];
2128 /* rotate the old debug logs */
2129 for (i=3; i>=0; i--)
2131 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2132 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2135 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2138 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2139 fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2140 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2145 static int mutt_execute_commands (LIST *p)
2148 char errstr[SHORT_STRING];
2150 memset (&err, 0, sizeof (err));
2152 err.dsize = sizeof (errstr);
2153 memset (&token, 0, sizeof (token));
2154 for (; p; p = p->next)
2156 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2158 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2167 void mutt_init (int skip_sys_rc, LIST *commands)
2170 struct utsname utsname;
2171 char *p, buffer[STRING], error[STRING];
2172 int i, default_rc = 0, need_pause = 0;
2175 memset (&err, 0, sizeof (err));
2177 err.dsize = sizeof (error);
2180 * XXX - use something even more difficult to predict?
2182 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2183 "\033]9;%ld\a", (long) time (NULL));
2185 /* on one of the systems I use, getcwd() does not return the same prefix
2186 as is listed in the passwd file */
2187 if ((p = getenv ("HOME")))
2188 Homedir = safe_strdup (p);
2190 /* Get some information about the user */
2191 if ((pw = getpwuid (getuid ())))
2195 Username = safe_strdup (pw->pw_name);
2197 Homedir = safe_strdup (pw->pw_dir);
2199 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2200 Shell = safe_strdup (pw->pw_shell);
2207 fputs (_("unable to determine home directory"), stderr);
2210 if ((p = getenv ("USER")))
2211 Username = safe_strdup (p);
2215 fputs (_("unable to determine username"), stderr);
2218 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2222 /* Start up debugging mode if requested */
2227 /* And about the host... */
2229 /* some systems report the FQDN instead of just the hostname */
2230 if ((p = strchr (utsname.nodename, '.')))
2232 Hostname = mutt_substrdup (utsname.nodename, p);
2234 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2237 Hostname = safe_strdup (utsname.nodename);
2240 #define DOMAIN buffer
2241 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2242 Fqdn = safe_strdup ("@");
2247 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2248 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2251 Fqdn = safe_strdup(NONULL(Hostname));
2258 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2261 fgets (buffer, sizeof (buffer), f);
2265 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2267 NewsServer = safe_strdup (p);
2271 if ((p = getenv ("NNTPSERVER")))
2272 NewsServer = safe_strdup (p);
2275 if ((p = getenv ("MAIL")))
2276 Spoolfile = safe_strdup (p);
2277 else if ((p = getenv ("MAILDIR")))
2278 Spoolfile = safe_strdup (p);
2282 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2284 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2286 Spoolfile = safe_strdup (buffer);
2289 if ((p = getenv ("MAILCAPS")))
2290 MailcapPath = safe_strdup (p);
2293 /* Default search path from RFC1524 */
2294 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2297 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2299 p = getenv ("VISUAL");
2302 p = getenv ("EDITOR");
2306 Editor = safe_strdup (p);
2307 Visual = safe_strdup (p);
2309 if ((p = getenv ("REPLYTO")) != NULL)
2313 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2315 memset (&buf, 0, sizeof (buf));
2316 buf.data = buf.dptr = buffer;
2317 buf.dsize = mutt_strlen (buffer);
2319 memset (&token, 0, sizeof (token));
2320 parse_my_hdr (&token, &buf, 0, &err);
2324 if ((p = getenv ("EMAIL")) != NULL)
2325 From = rfc822_parse_adrlist (NULL, p);
2327 mutt_set_langinfo_charset ();
2328 mutt_set_charset (Charset);
2331 /* Set standard defaults */
2332 for (i = 0; MuttVars[i].option; i++)
2334 mutt_set_default (&MuttVars[i]);
2335 mutt_restore_default (&MuttVars[i]);
2338 CurrentMenu = MENU_MAIN;
2341 #ifndef LOCALES_HACK
2342 /* Do we have a locale definition? */
2343 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2344 ((p = getenv ("LANG")) != NULL && p[0]) ||
2345 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2346 set_option (OPTLOCALES);
2350 /* Unset suspend by default if we're the session leader */
2351 if (getsid(0) == getpid())
2352 unset_option (OPTSUSPEND);
2355 mutt_init_history ();
2364 * When changing the code which looks for a configuration file,
2365 * please also change the corresponding code in muttbug.sh.in.
2375 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2376 if (access(buffer, F_OK) == -1)
2377 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2378 if (access(buffer, F_OK) == -1)
2379 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2380 if (access(buffer, F_OK) == -1)
2381 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2384 Muttrc = safe_strdup (buffer);
2388 strfcpy (buffer, Muttrc, sizeof (buffer));
2390 mutt_expand_path (buffer, sizeof (buffer));
2391 Muttrc = safe_strdup (buffer);
2394 AliasFile = safe_strdup (NONULL(Muttrc));
2396 /* Process the global rc file if it exists and the user hasn't explicity
2397 requested not to via "-n". */
2400 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2401 if (access (buffer, F_OK) == -1)
2402 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2403 if (access (buffer, F_OK) == -1)
2404 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2405 if (access (buffer, F_OK) == -1)
2406 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2407 if (access (buffer, F_OK) != -1)
2409 if (source_rc (buffer, &err) != 0)
2411 fputs (err.data, stderr);
2412 fputc ('\n', stderr);
2418 /* Read the user's initialization file. */
2419 if (access (Muttrc, F_OK) != -1)
2421 if (!option (OPTNOCURSES))
2423 if (source_rc (Muttrc, &err) != 0)
2425 fputs (err.data, stderr);
2426 fputc ('\n', stderr);
2430 else if (!default_rc)
2432 /* file specified by -F does not exist */
2433 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2434 mutt_endwin (buffer);
2438 if (mutt_execute_commands (commands) != 0)
2441 if (need_pause && !option (OPTNOCURSES))
2443 if (mutt_any_key_to_continue (NULL) == -1)
2448 set_option (OPTWEED); /* turn weeding on by default */
2452 int mutt_get_hook_type (const char *name)
2454 struct command_t *c;
2456 for (c = Commands ; c->name ; c++)
2457 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)