2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
16 #include "mutt_curses.h"
21 #include "mutt_crypt.h"
22 #include "mutt_idna.h"
24 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
41 #include <sys/utsname.h>
45 void toggle_quadoption (int opt)
48 int b = (opt % 4) * 2;
50 QuadOptions[n] ^= (1 << b);
53 void set_quadoption (int opt, int flag)
56 int b = (opt % 4) * 2;
58 QuadOptions[n] &= ~(0x3 << b);
59 QuadOptions[n] |= (flag & 0x3) << b;
62 int quadoption (int opt)
65 int b = (opt % 4) * 2;
67 return (QuadOptions[n] >> b) & 0x3;
70 int query_quadoption (int opt, const char *prompt)
72 int v = quadoption (opt);
80 v = mutt_yesorno (prompt, (v == M_ASKYES));
81 CLEARLINE (LINES - 1);
88 /* given the variable ``s'', return the index into the rc_vars array which
89 matches, or -1 if the variable is not found. */
90 int mutt_option_index (char *s)
94 for (i = 0; MuttVars[i].option; i++)
95 if (mutt_strcmp (s, MuttVars[i].option) == 0)
96 return (MuttVars[i].type ==
97 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
101 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
104 char qc = 0; /* quote char */
107 /* reset the destination pointer to the beginning of the buffer */
108 dest->dptr = dest->data;
111 while ((ch = *tok->dptr)) {
113 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
114 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
115 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
116 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
117 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
124 qc = 0; /* end of quote */
125 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
127 else if (ch == '\\' && qc != '\'') {
129 return -1; /* premature end of token */
130 switch (ch = *tok->dptr++) {
134 return -1; /* premature end of token */
135 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
140 mutt_buffer_addch (dest, '\r');
143 mutt_buffer_addch (dest, '\n');
146 mutt_buffer_addch (dest, '\t');
149 mutt_buffer_addch (dest, '\f');
152 mutt_buffer_addch (dest, '\033');
155 if (isdigit ((unsigned char) ch) &&
156 isdigit ((unsigned char) *tok->dptr) &&
157 isdigit ((unsigned char) *(tok->dptr + 1))) {
159 mutt_buffer_addch (dest,
160 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
165 mutt_buffer_addch (dest, ch);
168 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
170 return -1; /* premature end of token */
173 mutt_buffer_addch (dest, ch);
175 mutt_buffer_addch (dest, '\033');
176 else if (isalpha ((unsigned char) ch))
177 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
179 mutt_buffer_addch (dest, '^');
180 mutt_buffer_addch (dest, ch);
183 else if (ch == '`' && (!qc || qc == '"')) {
193 if ((pc = strpbrk (pc, "\\`"))) {
194 /* skip any quoted chars */
198 } while (pc && *pc != '`');
200 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
203 cmd = mutt_substrdup (tok->dptr, pc);
204 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
206 (debugfile, "mutt_get_token: unable to fork command: %s",
216 memset (&expn, 0, sizeof (expn));
217 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
219 mutt_wait_filter (pid);
221 /* if we got output, make a new string consiting of the shell ouptput
222 plus whatever else was left on the original line */
223 /* BUT: If this is inside a quoted string, directly add output to
225 if (expn.data && qc) {
226 mutt_buffer_addstr (dest, expn.data);
229 else if (expn.data) {
230 expnlen = mutt_strlen (expn.data);
231 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
232 ptr = safe_malloc (tok->dsize);
233 memcpy (ptr, expn.data, expnlen);
234 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
239 tok->destroy = 1; /* mark that the caller should destroy this data */
244 else if (ch == '$' && (!qc || qc == '"')
245 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
246 char *env = NULL, *var = NULL;
248 if (*tok->dptr == '{') {
250 if ((pc = strchr (tok->dptr, '}'))) {
251 var = mutt_substrdup (tok->dptr, pc);
256 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
258 var = mutt_substrdup (tok->dptr, pc);
261 if (var && (env = getenv (var)))
262 mutt_buffer_addstr (dest, env);
266 mutt_buffer_addch (dest, ch);
268 mutt_buffer_addch (dest, 0); /* terminate the string */
273 static void add_to_list (LIST ** list, const char *str)
275 LIST *t, *last = NULL;
277 /* don't add a NULL or empty string to the list */
278 if (!str || *str == '\0')
281 /* check to make sure the item is not already on this list */
282 for (last = *list; last; last = last->next) {
283 if (ascii_strcasecmp (str, last->data) == 0) {
284 /* already on the list, so just ignore it */
292 if (!*list || last) {
293 t = (LIST *) safe_calloc (1, sizeof (LIST));
294 t->data = safe_strdup (str);
304 static int add_to_rx_list (list2_t** list, const char *s, int flags,
313 if (!(rx = rx_compile (s, flags))) {
314 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
318 i = rx_lookup ((*list), rx->pattern);
322 list_push_back (list, rx);
326 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
327 const char *templ, BUFFER * err)
329 SPAM_LIST *t = NULL, *last = NULL;
334 if (!pat || !*pat || !templ)
337 if (!(rx = rx_compile (pat, REG_ICASE))) {
338 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
342 /* check to make sure the item is not already on this list */
343 for (last = *list; last; last = last->next) {
344 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
345 /* Already on the list. Formerly we just skipped this case, but
346 * now we're supporting removals, which means we're supporting
347 * re-adds conceptually. So we probably want this to imply a
348 * removal, then do an add. We can achieve the removal by freeing
349 * the template, and leaving t pointed at the current item.
359 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
360 * update. Otherwise we want to make a new one to link at the list's end.
363 t = mutt_new_spam_list ();
371 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
372 t->template = safe_strdup (templ);
374 /* Find highest match number in template string */
376 for (p = templ; *p;) {
381 while (*p && isdigit ((int) *p))
387 t->nmatch++; /* match 0 is always the whole expr */
392 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
394 SPAM_LIST *spam, *prev;
397 /* Being first is a special case. */
399 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
402 FREE(&spam->template);
408 for (spam = prev->next; spam;) {
409 if (!mutt_strcmp (spam->rx->pattern, pat)) {
410 prev->next = spam->next;
412 FREE(spam->template);
425 static void remove_from_list (LIST ** l, const char *str)
427 LIST *p, *last = NULL;
429 if (mutt_strcmp ("*", str) == 0)
430 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
435 if (ascii_strcasecmp (str, p->data) == 0) {
438 last->next = p->next;
451 static int remove_from_rx_list (list2_t** l, const char *str)
455 if (mutt_strcmp ("*", str) == 0) {
456 list_del (l, rx_free);
460 i = rx_lookup ((*l), str);
462 rx_t* r = list_pop_idx ((*l), i);
470 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
476 memset (&token, 0, sizeof (token));
477 mutt_extract_token (tmp, s, 0);
479 /* is the item defined as a variable or a function? */
480 if (!(res = (mutt_option_index (tmp->data) != -1)))
481 for (i = 0; !res && i < MENU_MAX; i++) {
482 struct binding_t *b = km_get_table (Menus[i].value);
487 for (j = 0; b[j].name; j++)
488 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
489 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
494 /* check for feature_* */
499 j = mutt_strlen (tmp->data);
500 /* need at least input of 'feature_X' */
504 while (Features[i].name) {
505 if (mutt_strlen (Features[i].name) == j &&
506 ascii_strncasecmp (Features[i].name, p, j)) {
517 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
519 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
522 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
524 if ((data && res) || (!data && !res)) {
525 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
526 mutt_error ("Error: %s", err->data);
535 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
539 mutt_extract_token (buf, s, 0);
541 /* don't add "*" to the unignore list */
542 if (strcmp (buf->data, "*"))
543 add_to_list (&UnIgnore, buf->data);
545 remove_from_list (&Ignore, buf->data);
547 while (MoreArgs (s));
552 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
556 mutt_extract_token (buf, s, 0);
557 remove_from_list (&UnIgnore, buf->data);
558 add_to_list (&Ignore, buf->data);
560 while (MoreArgs (s));
565 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
569 mutt_extract_token (buf, s, 0);
570 add_to_list ((LIST **) data, buf->data);
572 while (MoreArgs (s));
577 static void _alternates_clean (void)
581 if (Context && Context->msgcount) {
582 for (i = 0; i < Context->msgcount; i++)
583 Context->hdrs[i]->recip_valid = 0;
587 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
590 _alternates_clean ();
592 mutt_extract_token (buf, s, 0);
593 remove_from_rx_list (&UnAlternates, buf->data);
595 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
598 while (MoreArgs (s));
603 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
606 _alternates_clean ();
608 mutt_extract_token (buf, s, 0);
609 remove_from_rx_list (&Alternates, buf->data);
611 if (mutt_strcmp (buf->data, "*") &&
612 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
616 while (MoreArgs (s));
621 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
626 memset (&templ, 0, sizeof (templ));
628 /* Insist on at least one parameter */
631 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
633 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
637 /* Extract the first token, a regexp */
638 mutt_extract_token (buf, s, 0);
640 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
641 if (data == M_SPAM) {
642 /* If there's a second parameter, it's a template for the spam tag. */
644 mutt_extract_token (&templ, s, 0);
646 /* Add to the spam list. */
647 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
654 /* If not, try to remove from the nospam list. */
656 remove_from_rx_list (&NoSpamList, buf->data);
662 /* M_NOSPAM is for nospam commands. */
663 else if (data == M_NOSPAM) {
664 /* nospam only ever has one parameter. */
666 /* "*" is a special case. */
667 if (!mutt_strcmp (buf->data, "*")) {
668 mutt_free_spam_list (&SpamList);
669 list_del (&NoSpamList, rx_free);
673 /* If it's on the spam list, just remove it. */
674 if (remove_from_spam_list (&SpamList, buf->data) != 0)
677 /* Otherwise, add it to the nospam list. */
678 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
684 /* This should not happen. */
685 strfcpy (err->data, "This is no good at all.", err->dsize);
689 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
693 mutt_extract_token (buf, s, 0);
695 * Check for deletion of entire list
697 if (mutt_strcmp (buf->data, "*") == 0) {
698 mutt_free_list ((LIST **) data);
701 remove_from_list ((LIST **) data, buf->data);
703 while (MoreArgs (s));
708 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
712 mutt_extract_token (buf, s, 0);
713 remove_from_rx_list (&UnMailLists, buf->data);
715 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
718 while (MoreArgs (s));
723 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
727 mutt_extract_token (buf, s, 0);
728 remove_from_rx_list (&SubscribedLists, buf->data);
729 remove_from_rx_list (&MailLists, buf->data);
731 if (mutt_strcmp (buf->data, "*") &&
732 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
735 while (MoreArgs (s));
740 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
744 mutt_extract_token (buf, s, 0);
745 remove_from_rx_list (&UnMailLists, buf->data);
746 remove_from_rx_list (&UnSubscribedLists, buf->data);
748 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
750 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
753 while (MoreArgs (s));
758 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
762 mutt_extract_token (buf, s, 0);
763 remove_from_rx_list (&SubscribedLists, buf->data);
765 if (mutt_strcmp (buf->data, "*") &&
766 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
769 while (MoreArgs (s));
774 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
777 ALIAS *tmp, *last = NULL;
780 mutt_extract_token (buf, s, 0);
782 if (mutt_strcmp ("*", buf->data) == 0) {
783 if (CurrentMenu == MENU_ALIAS) {
784 for (tmp = Aliases; tmp; tmp = tmp->next)
786 set_option (OPTFORCEREDRAWINDEX);
789 mutt_free_alias (&Aliases);
793 for (tmp = Aliases; tmp; tmp = tmp->next) {
794 if (mutt_strcasecmp (buf->data, tmp->name) == 0) {
795 if (CurrentMenu == MENU_ALIAS) {
797 set_option (OPTFORCEREDRAWINDEX);
802 last->next = tmp->next;
806 mutt_free_alias (&tmp);
812 while (MoreArgs (s));
816 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
819 ALIAS *tmp = Aliases;
824 strfcpy (err->data, _("alias: no address"), err->dsize);
828 mutt_extract_token (buf, s, 0);
830 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
832 /* check to see if an alias with this name already exists */
833 for (; tmp; tmp = tmp->next) {
834 if (!mutt_strcasecmp (tmp->name, buf->data))
840 /* create a new alias */
841 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
843 tmp->name = safe_strdup (buf->data);
844 /* give the main addressbook code a chance */
845 if (CurrentMenu == MENU_ALIAS)
846 set_option (OPTMENUCALLER);
849 /* override the previous value */
850 rfc822_free_address (&tmp->addr);
851 if (CurrentMenu == MENU_ALIAS)
852 set_option (OPTFORCEREDRAWINDEX);
855 mutt_extract_token (buf, s,
856 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
857 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
858 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
863 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
864 snprintf (err->data, err->dsize,
865 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
869 if (debuglevel >= 2) {
872 for (a = tmp->addr; a; a = a->next) {
874 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
876 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
884 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
887 LIST *tmp = UserHeader;
892 mutt_extract_token (buf, s, 0);
893 if (mutt_strcmp ("*", buf->data) == 0)
894 mutt_free_list (&UserHeader);
899 l = mutt_strlen (buf->data);
900 if (buf->data[l - 1] == ':')
904 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
905 && tmp->data[l] == ':') {
908 last->next = tmp->next;
910 UserHeader = tmp->next;
913 mutt_free_list (&ptr);
922 while (MoreArgs (s));
926 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
933 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
934 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
935 strfcpy (err->data, _("invalid header field"), err->dsize);
938 keylen = p - buf->data + 1;
941 for (tmp = UserHeader;; tmp = tmp->next) {
942 /* see if there is already a field by this name */
943 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
944 /* replace the old value */
946 tmp->data = buf->data;
947 memset (buf, 0, sizeof (BUFFER));
953 tmp->next = mutt_new_list ();
957 tmp = mutt_new_list ();
960 tmp->data = buf->data;
961 memset (buf, 0, sizeof (BUFFER));
966 parse_sort (short *val, const char *s, const struct mapping_t *map,
971 if (mutt_strncmp ("reverse-", s, 8) == 0) {
973 flags = SORT_REVERSE;
976 if (mutt_strncmp ("last-", s, 5) == 0) {
981 if ((i = mutt_getvaluebyname (s, map)) == -1) {
982 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
991 static void mutt_set_default (struct option_t *p)
993 switch (p->type & DT_MASK) {
995 if (!p->init && *((char **) p->data))
996 p->init = (unsigned long) safe_strdup (*((char **) p->data));
999 if (!p->init && *((char **) p->data)) {
1000 char *cp = safe_strdup (*((char **) p->data));
1002 /* mutt_pretty_mailbox (cp); */
1003 p->init = (unsigned long) cp;
1007 if (!p->init && *((ADDRESS **) p->data)) {
1008 char tmp[HUGE_STRING];
1011 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1012 p->init = (unsigned long) safe_strdup (tmp);
1017 rx_t* pp = (rx_t*) p->data;
1019 if (!p->init && pp->pattern)
1020 p->init = (unsigned long) safe_strdup (pp->pattern);
1026 static void mutt_restore_default (struct option_t *p)
1028 switch (p->type & DT_MASK) {
1031 mutt_str_replace ((char **) p->data, (char *) p->init);
1035 char path[_POSIX_PATH_MAX];
1037 strfcpy (path, (char *) p->init, sizeof (path));
1038 mutt_expand_path (path, sizeof (path));
1039 mutt_str_replace ((char **) p->data, path);
1044 rfc822_free_address ((ADDRESS **) p->data);
1045 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1050 set_option (p->data);
1052 unset_option (p->data);
1055 set_quadoption (p->data, p->init);
1060 *((short *) p->data) = p->init;
1064 rx_t *pp = (rx_t *) p->data;
1067 FREE (&pp->pattern);
1074 char *s = (char *) p->init;
1076 pp->rx = safe_calloc (1, sizeof (regex_t));
1077 if (mutt_strcmp (p->option, "mask") != 0)
1078 flags |= mutt_which_case ((const char *) p->init);
1079 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1083 if (REGCOMP (pp->rx, s, flags) != 0) {
1085 _("mutt_restore_default(%s): error in regexp: %s\n"),
1086 p->option, pp->pattern);
1087 FREE (&pp->pattern);
1092 mutt_str_replace (&pp->pattern, (char *) p->init);
1098 if (p->flags & R_INDEX)
1099 set_option (OPTFORCEREDRAWINDEX);
1100 if (p->flags & R_PAGER)
1101 set_option (OPTFORCEREDRAWPAGER);
1102 if (p->flags & R_RESORT_SUB)
1103 set_option (OPTSORTSUBTHREADS);
1104 if (p->flags & R_RESORT)
1105 set_option (OPTNEEDRESORT);
1106 if (p->flags & R_RESORT_INIT)
1107 set_option (OPTRESORTINIT);
1108 if (p->flags & R_TREE)
1109 set_option (OPTREDRAWTREE);
1112 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1115 int idx, query, unset, inv, reset, r = 0;
1116 char *p, scratch[_POSIX_PATH_MAX];
1118 while (MoreArgs (s)) {
1119 /* reset state variables */
1121 unset = data & M_SET_UNSET;
1122 inv = data & M_SET_INV;
1123 reset = data & M_SET_RESET;
1125 if (*s->dptr == '?') {
1129 else if (mutt_strncmp ("no", s->dptr, 2) == 0) {
1133 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) {
1137 else if (*s->dptr == '&') {
1142 /* get the variable name */
1143 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1145 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1146 !(reset && !mutt_strcmp ("all", tmp->data))) {
1147 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1153 if (query || unset || inv) {
1154 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1158 if (s && *s->dptr == '=') {
1159 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1163 if (!mutt_strcmp ("all", tmp->data)) {
1164 for (idx = 0; MuttVars[idx].option; idx++)
1165 mutt_restore_default (&MuttVars[idx]);
1169 mutt_restore_default (&MuttVars[idx]);
1171 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1172 if (s && *s->dptr == '=') {
1173 if (unset || inv || query) {
1174 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1179 mutt_extract_token (tmp, s, 0);
1180 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1182 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1185 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1191 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1192 ? _("%s is set") : _("%s is unset"), tmp->data);
1197 unset_option (MuttVars[idx].data);
1199 toggle_option (MuttVars[idx].data);
1201 set_option (MuttVars[idx].data);
1203 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1204 DTYPE (MuttVars[idx].type) == DT_PATH ||
1205 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1207 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1208 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1210 FREE ((void *) MuttVars[idx].data);
1212 else if (query || *s->dptr != '=') {
1216 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1218 rfc822_write_address (_tmp, sizeof (_tmp),
1219 *((ADDRESS **) MuttVars[idx].data), 0);
1223 val = *((char **) MuttVars[idx].data);
1225 /* user requested the value of this variable */
1226 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1233 /* copy the value of the string */
1234 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1235 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1237 FREE ((void *) MuttVars[idx].data);
1239 mutt_extract_token (tmp, s, 0);
1240 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1241 strfcpy (scratch, tmp->data, sizeof (scratch));
1242 mutt_expand_path (scratch, sizeof (scratch));
1243 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1245 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1246 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1247 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1248 mutt_set_charset (Charset);
1251 *((ADDRESS **) MuttVars[idx].data) =
1252 rfc822_parse_adrlist (NULL, tmp->data);
1256 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1257 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1261 if (query || *s->dptr != '=') {
1262 /* user requested the value of this variable */
1263 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1264 NONULL (ptr->pattern));
1268 if (option (OPTATTACHMSG)
1269 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1270 snprintf (err->data, err->dsize,
1271 "Operation not permitted when in attach-message mode.");
1278 /* copy the value of the string */
1279 mutt_extract_token (tmp, s, 0);
1281 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1284 /* $mask is case-sensitive */
1285 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1286 flags |= mutt_which_case (tmp->data);
1289 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1296 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1297 if ((e = REGCOMP (rx, p, flags)) != 0) {
1298 regerror (e, rx, err->data, err->dsize);
1304 /* get here only if everything went smootly */
1306 FREE (&ptr->pattern);
1307 regfree ((regex_t *) ptr->rx);
1311 ptr->pattern = safe_strdup (tmp->data);
1315 /* $reply_regexp and $alterantes require special treatment */
1317 if (Context && Context->msgcount &&
1318 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1319 regmatch_t pmatch[1];
1322 #define CUR_ENV Context->hdrs[i]->env
1323 for (i = 0; i < Context->msgcount; i++) {
1324 if (CUR_ENV && CUR_ENV->subject) {
1325 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1326 CUR_ENV->subject, 1, pmatch,
1328 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1335 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1336 if (query || *s->dptr != '=') {
1337 switch (DefaultMagic) {
1354 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1360 /* copy the value of the string */
1361 mutt_extract_token (tmp, s, 0);
1362 if (mx_set_magic (tmp->data)) {
1363 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1369 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1370 short *ptr = (short *) MuttVars[idx].data;
1374 if (query || *s->dptr != '=') {
1375 /* user requested the value of this variable */
1376 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1382 mutt_extract_token (tmp, s, 0);
1383 val = strtol (tmp->data, &t, 0);
1385 if (!*tmp->data || *t || (short) val != val) {
1386 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1393 /* these ones need a sanity check */
1394 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1397 mutt_init_history ();
1399 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1404 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1406 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1408 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1409 vals[quadoption (MuttVars[idx].data)]);
1413 if (*s->dptr == '=') {
1415 mutt_extract_token (tmp, s, 0);
1416 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1417 set_quadoption (MuttVars[idx].data, M_YES);
1418 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1419 set_quadoption (MuttVars[idx].data, M_NO);
1420 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1421 set_quadoption (MuttVars[idx].data, M_ASKYES);
1422 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1423 set_quadoption (MuttVars[idx].data, M_ASKNO);
1425 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1432 toggle_quadoption (MuttVars[idx].data);
1434 set_quadoption (MuttVars[idx].data, M_NO);
1436 set_quadoption (MuttVars[idx].data, M_YES);
1439 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1440 const struct mapping_t *map = NULL;
1442 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1444 map = SortAliasMethods;
1446 case DT_SORT_BROWSER:
1447 map = SortBrowserMethods;
1450 if ((WithCrypto & APPLICATION_PGP))
1451 map = SortKeyMethods;
1454 map = SortAuxMethods;
1462 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1463 MuttVars[idx].option);
1468 if (query || *s->dptr != '=') {
1470 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1473 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1474 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1476 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1481 mutt_extract_token (tmp, s, 0);
1483 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1490 snprintf (err->data, err->dsize, _("%s: unknown type"),
1491 MuttVars[idx].option);
1496 if (MuttVars[idx].flags & R_INDEX)
1497 set_option (OPTFORCEREDRAWINDEX);
1498 if (MuttVars[idx].flags & R_PAGER)
1499 set_option (OPTFORCEREDRAWPAGER);
1500 if (MuttVars[idx].flags & R_RESORT_SUB)
1501 set_option (OPTSORTSUBTHREADS);
1502 if (MuttVars[idx].flags & R_RESORT)
1503 set_option (OPTNEEDRESORT);
1504 if (MuttVars[idx].flags & R_RESORT_INIT)
1505 set_option (OPTRESORTINIT);
1506 if (MuttVars[idx].flags & R_TREE)
1507 set_option (OPTREDRAWTREE);
1514 /* reads the specified initialization file. returns -1 if errors were found
1515 so that we can pause to let the user know... */
1516 static int source_rc (const char *rcfile, BUFFER * err)
1519 int line = 0, rc = 0, conv = 0;
1521 char *linebuf = NULL;
1522 char *currentline = NULL;
1526 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1528 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1529 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1533 memset (&token, 0, sizeof (token));
1534 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1535 conv = ConfigCharset && (*ConfigCharset) && Charset;
1537 currentline = safe_strdup (linebuf);
1540 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1543 currentline = linebuf;
1545 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1546 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1547 if (--rc < -MAXERRS) {
1549 FREE (¤tline);
1558 FREE (¤tline);
1564 mutt_wait_filter (pid);
1566 /* the muttrc source keyword */
1567 snprintf (err->data, err->dsize,
1568 rc >= -MAXERRS ? _("source: errors in %s")
1569 : _("source: reading aborted due too many errors in %s"),
1578 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1581 char path[_POSIX_PATH_MAX];
1585 if (mutt_extract_token (tmp, s, 0) != 0) {
1586 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1590 strfcpy (path, tmp->data, sizeof (path));
1591 mutt_expand_path (path, sizeof (path));
1593 rc += source_rc (path, err);
1595 while (MoreArgs (s));
1597 return ((rc < 0) ? -1 : 0);
1600 /* line command to execute
1602 token scratch buffer to be used by parser. caller should free
1603 token->data when finished. the reason for this variable is
1604 to avoid having to allocate and deallocate a lot of memory
1605 if we are parsing many lines. the caller can pass in the
1606 memory to use, which avoids having to create new space for
1607 every call to this function.
1609 err where to write error messages */
1610 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1615 memset (&expn, 0, sizeof (expn));
1616 expn.data = expn.dptr = line;
1617 expn.dsize = mutt_strlen (line);
1622 while (*expn.dptr) {
1623 if (*expn.dptr == '#')
1624 break; /* rest of line is a comment */
1625 if (*expn.dptr == ';') {
1629 mutt_extract_token (token, &expn, 0);
1630 for (i = 0; Commands[i].name; i++) {
1631 if (!mutt_strcmp (token->data, Commands[i].name)) {
1632 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1637 if (!Commands[i].name) {
1638 snprintf (err->data, err->dsize, _("%s: unknown command"),
1639 NONULL (token->data));
1651 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1652 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1653 /* initial string that starts completion. No telling how much crap
1654 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1655 char User_typed[LONG_STRING] = { 0 };
1657 int Num_matched = 0; /* Number of matches for completion */
1658 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1659 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1661 /* helper function for completion. Changes the dest buffer if
1662 necessary/possible to aid completion.
1663 dest == completion result gets here.
1664 src == candidate for completion.
1665 try == user entered data for completion.
1666 len == length of dest buffer.
1668 static void candidate (char *dest, char *try, char *src, int len)
1672 if (strstr (src, try) == src) {
1673 Matches[Num_matched++] = src;
1675 strfcpy (dest, src, len);
1677 for (l = 0; src[l] && src[l] == dest[l]; l++);
1683 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1687 int spaces; /* keep track of the number of leading spaces on the line */
1690 spaces = buffer - pt;
1692 pt = buffer + pos - spaces;
1693 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1696 if (pt == buffer) { /* complete cmd */
1697 /* first TAB. Collect all the matches */
1700 strfcpy (User_typed, pt, sizeof (User_typed));
1701 memset (Matches, 0, sizeof (Matches));
1702 memset (Completed, 0, sizeof (Completed));
1703 for (num = 0; Commands[num].name; num++)
1704 candidate (Completed, User_typed, Commands[num].name,
1705 sizeof (Completed));
1706 Matches[Num_matched++] = User_typed;
1708 /* All matches are stored. Longest non-ambiguous string is ""
1709 * i.e. dont change 'buffer'. Fake successful return this time */
1710 if (User_typed[0] == 0)
1714 if (Completed[0] == 0 && User_typed[0])
1717 /* Num_matched will _always_ be atleast 1 since the initial
1718 * user-typed string is always stored */
1719 if (numtabs == 1 && Num_matched == 2)
1720 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1721 else if (numtabs > 1 && Num_matched > 2)
1722 /* cycle thru all the matches */
1723 snprintf (Completed, sizeof (Completed), "%s",
1724 Matches[(numtabs - 2) % Num_matched]);
1726 /* return the completed command */
1727 strncpy (buffer, Completed, len - spaces);
1729 else if (!mutt_strncmp (buffer, "set", 3)
1730 || !mutt_strncmp (buffer, "unset", 5)
1731 || !mutt_strncmp (buffer, "reset", 5)
1732 || !mutt_strncmp (buffer, "toggle", 6)) { /* complete variables */
1733 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1736 /* loop through all the possible prefixes (no, inv, ...) */
1737 if (!mutt_strncmp (buffer, "set", 3)) {
1738 for (num = 0; prefixes[num]; num++) {
1739 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1740 pt += mutt_strlen (prefixes[num]);
1746 /* first TAB. Collect all the matches */
1749 strfcpy (User_typed, pt, sizeof (User_typed));
1750 memset (Matches, 0, sizeof (Matches));
1751 memset (Completed, 0, sizeof (Completed));
1752 for (num = 0; MuttVars[num].option; num++)
1753 candidate (Completed, User_typed, MuttVars[num].option,
1754 sizeof (Completed));
1755 Matches[Num_matched++] = User_typed;
1757 /* All matches are stored. Longest non-ambiguous string is ""
1758 * i.e. dont change 'buffer'. Fake successful return this time */
1759 if (User_typed[0] == 0)
1763 if (Completed[0] == 0 && User_typed[0])
1766 /* Num_matched will _always_ be atleast 1 since the initial
1767 * user-typed string is always stored */
1768 if (numtabs == 1 && Num_matched == 2)
1769 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1770 else if (numtabs > 1 && Num_matched > 2)
1771 /* cycle thru all the matches */
1772 snprintf (Completed, sizeof (Completed), "%s",
1773 Matches[(numtabs - 2) % Num_matched]);
1775 strncpy (pt, Completed, buffer + len - pt - spaces);
1777 else if (!mutt_strncmp (buffer, "exec", 4)) {
1778 struct binding_t *menu = km_get_table (CurrentMenu);
1780 if (!menu && CurrentMenu != MENU_PAGER)
1784 /* first TAB. Collect all the matches */
1787 strfcpy (User_typed, pt, sizeof (User_typed));
1788 memset (Matches, 0, sizeof (Matches));
1789 memset (Completed, 0, sizeof (Completed));
1790 for (num = 0; menu[num].name; num++)
1791 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1792 /* try the generic menu */
1793 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1795 for (num = 0; menu[num].name; num++)
1796 candidate (Completed, User_typed, menu[num].name,
1797 sizeof (Completed));
1799 Matches[Num_matched++] = User_typed;
1801 /* All matches are stored. Longest non-ambiguous string is ""
1802 * i.e. dont change 'buffer'. Fake successful return this time */
1803 if (User_typed[0] == 0)
1807 if (Completed[0] == 0 && User_typed[0])
1810 /* Num_matched will _always_ be atleast 1 since the initial
1811 * user-typed string is always stored */
1812 if (numtabs == 1 && Num_matched == 2)
1813 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1814 else if (numtabs > 1 && Num_matched > 2)
1815 /* cycle thru all the matches */
1816 snprintf (Completed, sizeof (Completed), "%s",
1817 Matches[(numtabs - 2) % Num_matched]);
1819 strncpy (pt, Completed, buffer + len - pt - spaces);
1827 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1829 char var[STRING], *pt = buffer;
1836 spaces = buffer - pt;
1838 pt = buffer + pos - spaces;
1839 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1841 pt++; /* move past the space */
1842 if (*pt == '=') /* abort if no var before the '=' */
1845 if (mutt_strncmp (buffer, "set", 3) == 0) {
1848 strfcpy (var, pt, sizeof (var));
1849 /* ignore the trailing '=' when comparing */
1850 var[mutt_strlen (var) - 1] = 0;
1851 if ((idx = mutt_option_index (var)) == -1)
1852 return 0; /* no such variable. */
1854 char tmp[LONG_STRING], tmp2[LONG_STRING];
1856 size_t dlen = buffer + len - pt - spaces;
1857 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1861 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1862 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1863 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1864 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1865 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1866 mutt_pretty_mailbox (tmp);
1868 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1869 rfc822_write_address (tmp, sizeof (tmp),
1870 *((ADDRESS **) MuttVars[idx].data), 0);
1872 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1873 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1874 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1875 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1876 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1877 const struct mapping_t *map;
1880 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1882 map = SortAliasMethods;
1884 case DT_SORT_BROWSER:
1885 map = SortBrowserMethods;
1888 if ((WithCrypto & APPLICATION_PGP))
1889 map = SortKeyMethods;
1898 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1900 snprintf (tmp, sizeof (tmp), "%s%s%s",
1901 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1903 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1906 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1907 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1912 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1913 if (*s == '\\' || *s == '"')
1919 strfcpy (tmp, pt, sizeof (tmp));
1920 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1928 /* Implement the -Q command line flag */
1929 int mutt_query_variables (LIST * queries)
1933 char errbuff[STRING];
1934 char command[STRING];
1938 memset (&err, 0, sizeof (err));
1939 memset (&token, 0, sizeof (token));
1942 err.dsize = sizeof (errbuff);
1944 for (p = queries; p; p = p->next) {
1945 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1946 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1947 fprintf (stderr, "%s\n", err.data);
1951 printf ("%s\n", err.data);
1958 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1962 for (i = 0; map[i].name; i++)
1963 if (map[i].value == val)
1964 return (map[i].name);
1968 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1972 for (i = 0; map[i].name; i++)
1973 if (ascii_strcasecmp (map[i].name, name) == 0)
1974 return (map[i].value);
1979 static void start_debug (void)
1983 char buf[_POSIX_PATH_MAX];
1984 char buf2[_POSIX_PATH_MAX];
1986 /* rotate the old debug logs */
1987 for (i = 3; i >= 0; i--) {
1988 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
1989 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
1993 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
1995 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
1997 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
1998 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2003 static int mutt_execute_commands (LIST * p)
2006 char errstr[SHORT_STRING];
2008 memset (&err, 0, sizeof (err));
2010 err.dsize = sizeof (errstr);
2011 memset (&token, 0, sizeof (token));
2012 for (; p; p = p->next) {
2013 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2014 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2023 void mutt_init (int skip_sys_rc, LIST * commands)
2026 struct utsname utsname;
2027 char *p, buffer[STRING], error[STRING];
2028 int i, default_rc = 0, need_pause = 0;
2031 memset (&err, 0, sizeof (err));
2033 err.dsize = sizeof (error);
2036 * XXX - use something even more difficult to predict?
2038 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2039 "\033]9;%ld\a", (long) time (NULL));
2041 /* on one of the systems I use, getcwd() does not return the same prefix
2042 as is listed in the passwd file */
2043 if ((p = getenv ("HOME")))
2044 Homedir = safe_strdup (p);
2046 /* Get some information about the user */
2047 if ((pw = getpwuid (getuid ()))) {
2050 Username = safe_strdup (pw->pw_name);
2052 Homedir = safe_strdup (pw->pw_dir);
2054 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2055 Shell = safe_strdup (pw->pw_shell);
2060 fputs (_("unable to determine home directory"), stderr);
2063 if ((p = getenv ("USER")))
2064 Username = safe_strdup (p);
2067 fputs (_("unable to determine username"), stderr);
2070 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2074 /* Start up debugging mode if requested */
2079 /* And about the host... */
2081 /* some systems report the FQDN instead of just the hostname */
2082 if ((p = strchr (utsname.nodename, '.'))) {
2083 Hostname = mutt_substrdup (utsname.nodename, p);
2085 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2088 Hostname = safe_strdup (utsname.nodename);
2091 #define DOMAIN buffer
2092 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2093 Fqdn = safe_strdup ("@");
2096 if (*DOMAIN != '@') {
2097 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2098 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2101 Fqdn = safe_strdup (NONULL (Hostname));
2108 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2110 fgets (buffer, sizeof (buffer), f);
2111 p = (char*) &buffer;
2114 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2118 NewsServer = safe_strdup (p);
2122 if ((p = getenv ("NNTPSERVER")))
2123 NewsServer = safe_strdup (p);
2126 if ((p = getenv ("MAIL")))
2127 Spoolfile = safe_strdup (p);
2128 else if ((p = getenv ("MAILDIR")))
2129 Spoolfile = safe_strdup (p);
2132 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2134 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2136 Spoolfile = safe_strdup (buffer);
2139 if ((p = getenv ("MAILCAPS")))
2140 MailcapPath = safe_strdup (p);
2142 /* Default search path from RFC1524 */
2144 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2145 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2148 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2150 p = getenv ("VISUAL");
2152 p = getenv ("EDITOR");
2156 Editor = safe_strdup (p);
2157 Visual = safe_strdup (p);
2159 if ((p = getenv ("REPLYTO")) != NULL) {
2162 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2164 memset (&buf, 0, sizeof (buf));
2165 buf.data = buf.dptr = buffer;
2166 buf.dsize = mutt_strlen (buffer);
2168 memset (&token, 0, sizeof (token));
2169 parse_my_hdr (&token, &buf, 0, &err);
2173 if ((p = getenv ("EMAIL")) != NULL)
2174 From = rfc822_parse_adrlist (NULL, p);
2176 mutt_set_langinfo_charset ();
2177 mutt_set_charset (Charset);
2180 /* Set standard defaults */
2181 for (i = 0; MuttVars[i].option; i++) {
2182 mutt_set_default (&MuttVars[i]);
2183 mutt_restore_default (&MuttVars[i]);
2186 CurrentMenu = MENU_MAIN;
2189 #ifndef LOCALES_HACK
2190 /* Do we have a locale definition? */
2191 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2192 ((p = getenv ("LANG")) != NULL && p[0]) ||
2193 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2194 set_option (OPTLOCALES);
2198 /* Unset suspend by default if we're the session leader */
2199 if (getsid (0) == getpid ())
2200 unset_option (OPTSUSPEND);
2203 mutt_init_history ();
2212 * When changing the code which looks for a configuration file,
2213 * please also change the corresponding code in muttbug.sh.in.
2222 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2224 if (access (buffer, F_OK) == -1)
2225 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2226 if (access (buffer, F_OK) == -1)
2227 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2228 NONULL (Homedir), MUTT_VERSION);
2229 if (access (buffer, F_OK) == -1)
2230 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2234 Muttrc = safe_strdup (buffer);
2237 strfcpy (buffer, Muttrc, sizeof (buffer));
2239 mutt_expand_path (buffer, sizeof (buffer));
2240 Muttrc = safe_strdup (buffer);
2243 AliasFile = safe_strdup (NONULL (Muttrc));
2245 /* Process the global rc file if it exists and the user hasn't explicity
2246 requested not to via "-n". */
2248 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2250 if (access (buffer, F_OK) == -1)
2251 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2252 if (access (buffer, F_OK) == -1)
2253 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2255 if (access (buffer, F_OK) == -1)
2256 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2257 if (access (buffer, F_OK) != -1) {
2258 if (source_rc (buffer, &err) != 0) {
2259 fputs (err.data, stderr);
2260 fputc ('\n', stderr);
2266 /* Read the user's initialization file. */
2267 if (access (Muttrc, F_OK) != -1) {
2268 if (!option (OPTNOCURSES))
2270 if (source_rc (Muttrc, &err) != 0) {
2271 fputs (err.data, stderr);
2272 fputc ('\n', stderr);
2276 else if (!default_rc) {
2277 /* file specified by -F does not exist */
2278 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2279 mutt_endwin (buffer);
2283 if (mutt_execute_commands (commands) != 0)
2286 if (need_pause && !option (OPTNOCURSES)) {
2287 if (mutt_any_key_to_continue (NULL) == -1)
2292 set_option (OPTWEED); /* turn weeding on by default */
2296 int mutt_get_hook_type (const char *name)
2298 struct command_t *c;
2300 for (c = Commands; c->name; c++)
2301 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)