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.
21 #include "mutt_curses.h"
22 #include "mutt_regex.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
44 #include <sys/utsname.h>
48 void toggle_quadoption (int opt)
51 int b = (opt % 4) * 2;
53 QuadOptions[n] ^= (1 << b);
56 void set_quadoption (int opt, int flag)
59 int b = (opt % 4) * 2;
61 QuadOptions[n] &= ~(0x3 << b);
62 QuadOptions[n] |= (flag & 0x3) << b;
65 int quadoption (int opt)
68 int b = (opt % 4) * 2;
70 return (QuadOptions[n] >> b) & 0x3;
73 int query_quadoption (int opt, const char *prompt)
75 int v = quadoption (opt);
84 v = mutt_yesorno (prompt, (v == M_ASKYES));
85 CLEARLINE (LINES - 1);
92 /* given the variable ``s'', return the index into the rc_vars array which
93 matches, or -1 if the variable is not found. */
94 int mutt_option_index (char *s)
98 for (i = 0; MuttVars[i].option; i++)
99 if (mutt_strcmp (s, MuttVars[i].option) == 0)
100 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
104 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
107 char qc = 0; /* quote char */
110 /* reset the destination pointer to the beginning of the buffer */
111 dest->dptr = dest->data;
114 while ((ch = *tok->dptr))
118 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
119 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
120 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
121 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
122 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
129 qc = 0; /* end of quote */
130 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
132 else if (ch == '\\' && qc != '\'')
135 return -1; /* premature end of token */
136 switch (ch = *tok->dptr++)
141 return -1; /* premature end of token */
142 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
147 mutt_buffer_addch (dest, '\r');
150 mutt_buffer_addch (dest, '\n');
153 mutt_buffer_addch (dest, '\t');
156 mutt_buffer_addch (dest, '\f');
159 mutt_buffer_addch (dest, '\033');
162 if (isdigit ((unsigned char) ch) &&
163 isdigit ((unsigned char) *tok->dptr) &&
164 isdigit ((unsigned char) *(tok->dptr + 1)))
167 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
171 mutt_buffer_addch (dest, ch);
174 else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
177 return -1; /* premature end of token */
180 mutt_buffer_addch (dest, ch);
182 mutt_buffer_addch (dest, '\033');
183 else if (isalpha ((unsigned char) ch))
184 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
187 mutt_buffer_addch (dest, '^');
188 mutt_buffer_addch (dest, ch);
191 else if (ch == '`' && (!qc || qc == '"'))
202 if ((pc = strpbrk (pc, "\\`")))
204 /* skip any quoted chars */
208 } while (pc && *pc != '`');
211 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
214 cmd = mutt_substrdup (tok->dptr, pc);
215 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
217 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
226 memset (&expn, 0, sizeof (expn));
227 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
229 mutt_wait_filter (pid);
231 /* if we got output, make a new string consiting of the shell ouptput
232 plus whatever else was left on the original line */
233 /* BUT: If this is inside a quoted string, directly add output to
237 mutt_buffer_addstr (dest, expn.data);
242 expnlen = mutt_strlen (expn.data);
243 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
244 ptr = safe_malloc (tok->dsize);
245 memcpy (ptr, expn.data, expnlen);
246 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
251 tok->destroy = 1; /* mark that the caller should destroy this data */
256 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
258 char *env = NULL, *var = NULL;
260 if (*tok->dptr == '{')
263 if ((pc = strchr (tok->dptr, '}')))
265 var = mutt_substrdup (tok->dptr, pc);
271 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_'; pc++)
273 var = mutt_substrdup (tok->dptr, pc);
276 if (var && (env = getenv (var)))
277 mutt_buffer_addstr (dest, env);
281 mutt_buffer_addch (dest, ch);
283 mutt_buffer_addch (dest, 0); /* terminate the string */
288 static void add_to_list (LIST **list, const char *str)
290 LIST *t, *last = NULL;
292 /* don't add a NULL or empty string to the list */
293 if (!str || *str == '\0')
296 /* check to make sure the item is not already on this list */
297 for (last = *list; last; last = last->next)
299 if (ascii_strcasecmp (str, last->data) == 0)
301 /* already on the list, so just ignore it */
311 t = (LIST *) safe_calloc (1, sizeof (LIST));
312 t->data = safe_strdup (str);
323 static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
325 RX_LIST *t, *last = NULL;
331 if (!(rx = mutt_compile_regexp (s, flags)))
333 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
337 /* check to make sure the item is not already on this list */
338 for (last = *list; last; last = last->next)
340 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
342 /* already on the list, so just ignore it */
352 t = mutt_new_rx_list();
363 mutt_free_regexp (&rx);
368 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
370 SPAM_LIST *t = NULL, *last = NULL;
375 if (!pat || !*pat || !templ)
378 if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
380 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
384 /* check to make sure the item is not already on this list */
385 for (last = *list; last; last = last->next)
387 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
389 /* Already on the list. Formerly we just skipped this case, but
390 * now we're supporting removals, which means we're supporting
391 * re-adds conceptually. So we probably want this to imply a
392 * removal, then do an add. We can achieve the removal by freeing
393 * the template, and leaving t pointed at the current item.
396 safe_free(&t->template);
403 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
404 * update. Otherwise we want to make a new one to link at the list's end.
408 t = mutt_new_spam_list();
416 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
417 t->template = safe_strdup(templ);
419 /* Find highest match number in template string */
428 while (*p && isdigit((int)*p))
434 t->nmatch++; /* match 0 is always the whole expr */
439 static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
441 SPAM_LIST *spam, *prev;
444 /* Being first is a special case. */
446 if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
449 mutt_free_regexp(&spam->rx);
450 safe_free(&spam->template);
456 for (spam = prev->next; spam;)
458 if (!mutt_strcmp(spam->rx->pattern, pat))
460 prev->next = spam->next;
461 mutt_free_regexp(&spam->rx);
462 safe_free(&spam->template);
475 static void remove_from_list (LIST **l, const char *str)
477 LIST *p, *last = NULL;
479 if (mutt_strcmp ("*", str) == 0)
480 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
487 if (ascii_strcasecmp (str, p->data) == 0)
491 last->next = p->next;
505 static void remove_from_rx_list (RX_LIST **l, const char *str)
507 RX_LIST *p, *last = NULL;
509 if (mutt_strcmp ("*", str) == 0)
510 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
517 if (ascii_strcasecmp (str, p->rx->pattern) == 0)
519 mutt_free_regexp (&p->rx);
521 last->next = p->next;
535 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
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, BUFFER *err)
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, BUFFER *err)
569 mutt_extract_token (buf, s, 0);
570 add_to_list ((LIST **) data, buf->data);
572 while (MoreArgs (s));
577 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
581 mutt_extract_token (buf, s, 0);
582 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
586 while (MoreArgs (s));
591 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
593 return _parse_rx_list (buf, s, data, err, REG_ICASE);
596 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
600 mutt_extract_token (buf, s, 0);
601 if (mutt_strcmp (buf->data, "*") == 0)
603 mutt_free_rx_list ((RX_LIST **) data);
606 remove_from_rx_list ((RX_LIST **) data, buf->data);
608 while (MoreArgs (s));
613 static void _alternates_clean (void)
616 if (Context && Context->msgcount)
618 for (i = 0; i < Context->msgcount; i++)
619 Context->hdrs[i]->recip_valid = 0;
623 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
626 return parse_rx_list (buf, s, data, err);
629 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
632 return parse_rx_unlist (buf, s, data, err);
635 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
639 memset(&templ, 0, sizeof(templ));
641 /* Insist on at least one parameter */
645 strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
647 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
651 /* Extract the first token, a regexp */
652 mutt_extract_token (buf, s, 0);
654 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
657 /* If there's a second parameter, it's a template for the spam tag. */
660 mutt_extract_token (&templ, s, 0);
662 /* Add to the spam list. */
663 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
670 /* If not, try to remove from the nospam list. */
673 remove_from_rx_list(&NoSpamList, buf->data);
679 /* M_NOSPAM is for nospam commands. */
680 else if (data == M_NOSPAM)
682 /* nospam only ever has one parameter. */
684 /* "*" is a special case. */
685 if (!mutt_strcmp(buf->data, "*"))
687 mutt_free_spam_list (&SpamList);
688 mutt_free_rx_list (&NoSpamList);
692 /* If it's on the spam list, just remove it. */
693 if (remove_from_spam_list(&SpamList, buf->data) != 0)
696 /* Otherwise, add it to the nospam list. */
697 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
703 /* This should not happen. */
704 strfcpy(err->data, "This is no good at all.", err->dsize);
708 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
712 mutt_extract_token (buf, s, 0);
714 * Check for deletion of entire list
716 if (mutt_strcmp (buf->data, "*") == 0)
718 mutt_free_list ((LIST **) data);
721 remove_from_list ((LIST **) data, buf->data);
723 while (MoreArgs (s));
729 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
733 mutt_extract_token (buf, s, 0);
734 remove_from_rx_list (&MailLists, buf->data);
735 remove_from_rx_list (&SubscribedLists, buf->data);
737 while (MoreArgs (s));
742 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
746 mutt_extract_token (buf, s, 0);
747 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
749 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
752 while (MoreArgs (s));
757 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
759 ALIAS *tmp, *last = NULL;
763 mutt_extract_token (buf, s, 0);
765 if (mutt_strcmp ("*", buf->data) == 0)
767 if (CurrentMenu == MENU_ALIAS)
769 for (tmp = Aliases; tmp ; tmp = tmp->next)
771 set_option (OPTFORCEREDRAWINDEX);
774 mutt_free_alias (&Aliases);
778 for (tmp = Aliases; tmp; tmp = tmp->next)
780 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
782 if (CurrentMenu == MENU_ALIAS)
785 set_option (OPTFORCEREDRAWINDEX);
790 last->next = tmp->next;
794 mutt_free_alias (&tmp);
800 while (MoreArgs (s));
804 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
806 ALIAS *tmp = Aliases;
812 strfcpy (err->data, _("alias: no address"), err->dsize);
816 mutt_extract_token (buf, s, 0);
818 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
821 /* check to see if an alias with this name already exists */
822 for (; tmp; tmp = tmp->next)
824 if (!mutt_strcasecmp (tmp->name, buf->data))
831 /* create a new alias */
832 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
834 tmp->name = safe_strdup (buf->data);
835 /* give the main addressbook code a chance */
836 if (CurrentMenu == MENU_ALIAS)
837 set_option (OPTMENUCALLER);
841 /* override the previous value */
842 rfc822_free_address (&tmp->addr);
843 if (CurrentMenu == MENU_ALIAS)
844 set_option (OPTFORCEREDRAWINDEX);
847 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
848 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
850 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
855 if (mutt_addrlist_to_idna (tmp->addr, &estr))
857 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
865 for (a = tmp->addr; a; a = a->next)
868 dprint (2, (debugfile, "parse_alias: %s\n",
871 dprint (2, (debugfile, "parse_alias: Group %s\n",
880 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
883 LIST *tmp = UserHeader;
889 mutt_extract_token (buf, s, 0);
890 if (mutt_strcmp ("*", buf->data) == 0)
891 mutt_free_list (&UserHeader);
897 l = mutt_strlen (buf->data);
898 if (buf->data[l - 1] == ':')
903 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
907 last->next = tmp->next;
909 UserHeader = tmp->next;
912 mutt_free_list (&ptr);
922 while (MoreArgs (s));
926 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
932 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
933 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
935 strfcpy (err->data, _("invalid header field"), err->dsize);
938 keylen = p - buf->data + 1;
942 for (tmp = UserHeader; ; tmp = tmp->next)
944 /* see if there is already a field by this name */
945 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
947 /* replace the old value */
949 tmp->data = buf->data;
950 memset (buf, 0, sizeof (BUFFER));
956 tmp->next = mutt_new_list ();
961 tmp = mutt_new_list ();
964 tmp->data = buf->data;
965 memset (buf, 0, sizeof (BUFFER));
970 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
974 if (mutt_strncmp ("reverse-", s, 8) == 0)
977 flags = SORT_REVERSE;
980 if (mutt_strncmp ("last-", s, 5) == 0)
986 if ((i = mutt_getvaluebyname (s, map)) == -1)
988 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
997 static void mutt_set_default (struct option_t *p)
999 switch (p->type & DT_MASK)
1002 if (!p->init && *((char **) p->data))
1003 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1006 if (!p->init && *((char **) p->data))
1008 char *cp = safe_strdup (*((char **) p->data));
1009 /* mutt_pretty_mailbox (cp); */
1010 p->init = (unsigned long) cp;
1014 if (!p->init && *((ADDRESS **) p->data))
1016 char tmp[HUGE_STRING];
1018 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1019 p->init = (unsigned long) safe_strdup (tmp);
1024 REGEXP *pp = (REGEXP *) p->data;
1025 if (!p->init && pp->pattern)
1026 p->init = (unsigned long) safe_strdup (pp->pattern);
1032 static void mutt_restore_default (struct option_t *p)
1034 switch (p->type & DT_MASK)
1038 mutt_str_replace ((char **) p->data, (char *) p->init);
1043 char path[_POSIX_PATH_MAX];
1045 strfcpy (path, (char *) p->init, sizeof (path));
1046 mutt_expand_path (path, sizeof (path));
1047 mutt_str_replace ((char **) p->data, path);
1053 rfc822_free_address ((ADDRESS **) p->data);
1054 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1059 set_option (p->data);
1061 unset_option (p->data);
1064 set_quadoption (p->data, p->init);
1069 *((short *) p->data) = p->init;
1073 REGEXP *pp = (REGEXP *) p->data;
1076 FREE (&pp->pattern);
1085 char *s = (char *) p->init;
1087 pp->rx = safe_calloc (1, sizeof (regex_t));
1088 if (mutt_strcmp (p->option, "mask") != 0)
1089 flags |= mutt_which_case ((const char *) p->init);
1090 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1095 if (REGCOMP (pp->rx, s, flags) != 0)
1097 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1098 p->option, pp->pattern);
1099 FREE (&pp->pattern);
1108 if (p->flags & R_INDEX)
1109 set_option (OPTFORCEREDRAWINDEX);
1110 if (p->flags & R_PAGER)
1111 set_option (OPTFORCEREDRAWPAGER);
1112 if (p->flags & R_RESORT_SUB)
1113 set_option (OPTSORTSUBTHREADS);
1114 if (p->flags & R_RESORT)
1115 set_option (OPTNEEDRESORT);
1116 if (p->flags & R_RESORT_INIT)
1117 set_option (OPTRESORTINIT);
1118 if (p->flags & R_TREE)
1119 set_option (OPTREDRAWTREE);
1122 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1124 int idx, query, unset, inv, reset, r = 0;
1125 char *p, scratch[_POSIX_PATH_MAX];
1127 while (MoreArgs (s))
1129 /* reset state variables */
1131 unset = data & M_SET_UNSET;
1132 inv = data & M_SET_INV;
1133 reset = data & M_SET_RESET;
1135 if (*s->dptr == '?')
1140 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1145 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1150 else if (*s->dptr == '&')
1156 /* get the variable name */
1157 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1159 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1160 !(reset && !mutt_strcmp ("all", tmp->data)))
1162 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1169 if (query || unset || inv)
1171 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1175 if (s && *s->dptr == '=')
1177 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1181 if (!mutt_strcmp ("all", tmp->data))
1183 for (idx = 0; MuttVars[idx].option; idx++)
1184 mutt_restore_default (&MuttVars[idx]);
1188 mutt_restore_default (&MuttVars[idx]);
1190 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1192 if (s && *s->dptr == '=')
1194 if (unset || inv || query)
1196 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1201 mutt_extract_token (tmp, s, 0);
1202 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1204 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1208 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1215 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1216 ? _("%s is set") : _("%s is unset"), tmp->data);
1221 unset_option (MuttVars[idx].data);
1223 toggle_option (MuttVars[idx].data);
1225 set_option (MuttVars[idx].data);
1227 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1228 DTYPE (MuttVars[idx].type) == DT_PATH ||
1229 DTYPE (MuttVars[idx].type) == DT_ADDR)
1233 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1234 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1236 FREE ((void *)MuttVars[idx].data);
1238 else if (query || *s->dptr != '=')
1243 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1246 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1250 val = *((char **) MuttVars[idx].data);
1252 /* user requested the value of this variable */
1253 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1261 /* copy the value of the string */
1262 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1263 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1265 FREE ((void *)MuttVars[idx].data);
1267 mutt_extract_token (tmp, s, 0);
1268 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1270 strfcpy (scratch, tmp->data, sizeof (scratch));
1271 mutt_expand_path (scratch, sizeof (scratch));
1272 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1274 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1276 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1277 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1278 mutt_set_charset (Charset);
1282 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1286 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1288 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1292 if (query || *s->dptr != '=')
1294 /* user requested the value of this variable */
1295 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1296 NONULL (ptr->pattern));
1300 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1302 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1309 /* copy the value of the string */
1310 mutt_extract_token (tmp, s, 0);
1312 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1316 /* $mask is case-sensitive */
1317 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1318 flags |= mutt_which_case (tmp->data);
1321 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1330 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1331 if ((e = REGCOMP (rx, p, flags)) != 0)
1333 regerror (e, rx, err->data, err->dsize);
1339 /* get here only if everything went smootly */
1342 FREE (&ptr->pattern);
1343 regfree ((regex_t *) ptr->rx);
1347 ptr->pattern = safe_strdup (tmp->data);
1351 /* $reply_regexp and $alterantes require special treatment */
1353 if (Context && Context->msgcount &&
1354 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1356 regmatch_t pmatch[1];
1359 #define CUR_ENV Context->hdrs[i]->env
1360 for (i = 0; i < Context->msgcount; i++)
1362 if (CUR_ENV && CUR_ENV->subject)
1364 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1365 CUR_ENV->subject, 1, pmatch, 0)) ?
1367 CUR_ENV->subject + pmatch[0].rm_eo;
1374 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1376 if (query || *s->dptr != '=')
1378 switch (DefaultMagic)
1396 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1402 /* copy the value of the string */
1403 mutt_extract_token (tmp, s, 0);
1404 if (mx_set_magic (tmp->data))
1406 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1411 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1413 short *ptr = (short *) MuttVars[idx].data;
1417 if (query || *s->dptr != '=')
1419 /* user requested the value of this variable */
1420 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1426 mutt_extract_token (tmp, s, 0);
1427 val = strtol (tmp->data, &t, 0);
1429 if (!*tmp->data || *t || (short) val != val)
1431 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1438 /* these ones need a sanity check */
1439 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1443 mutt_init_history ();
1445 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1451 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1455 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1457 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1458 vals [ quadoption (MuttVars[idx].data) ]);
1462 if (*s->dptr == '=')
1465 mutt_extract_token (tmp, s, 0);
1466 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1467 set_quadoption (MuttVars[idx].data, M_YES);
1468 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1469 set_quadoption (MuttVars[idx].data, M_NO);
1470 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1471 set_quadoption (MuttVars[idx].data, M_ASKYES);
1472 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1473 set_quadoption (MuttVars[idx].data, M_ASKNO);
1476 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1484 toggle_quadoption (MuttVars[idx].data);
1486 set_quadoption (MuttVars[idx].data, M_NO);
1488 set_quadoption (MuttVars[idx].data, M_YES);
1491 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1493 const struct mapping_t *map = NULL;
1495 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1498 map = SortAliasMethods;
1500 case DT_SORT_BROWSER:
1501 map = SortBrowserMethods;
1504 if ((WithCrypto & APPLICATION_PGP))
1505 map = SortKeyMethods;
1508 map = SortAuxMethods;
1517 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1522 if (query || *s->dptr != '=')
1524 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1526 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1527 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1528 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1533 mutt_extract_token (tmp, s , 0);
1535 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1543 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1548 if (MuttVars[idx].flags & R_INDEX)
1549 set_option (OPTFORCEREDRAWINDEX);
1550 if (MuttVars[idx].flags & R_PAGER)
1551 set_option (OPTFORCEREDRAWPAGER);
1552 if (MuttVars[idx].flags & R_RESORT_SUB)
1553 set_option (OPTSORTSUBTHREADS);
1554 if (MuttVars[idx].flags & R_RESORT)
1555 set_option (OPTNEEDRESORT);
1556 if (MuttVars[idx].flags & R_RESORT_INIT)
1557 set_option (OPTRESORTINIT);
1558 if (MuttVars[idx].flags & R_TREE)
1559 set_option (OPTREDRAWTREE);
1566 /* reads the specified initialization file. returns -1 if errors were found
1567 so that we can pause to let the user know... */
1568 static int source_rc (const char *rcfile, BUFFER *err)
1571 int line = 0, rc = 0, conv = 0;
1573 char *linebuf = NULL;
1574 char *currentline = NULL;
1578 dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1581 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1583 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1587 memset (&token, 0, sizeof (token));
1588 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1590 conv=ConfigCharset && (*ConfigCharset) && Charset;
1593 currentline=safe_strdup(linebuf);
1594 if (!currentline) continue;
1595 mutt_convert_string(¤tline, ConfigCharset, Charset, 0);
1598 currentline=linebuf;
1600 if (mutt_parse_rc_line (currentline, &token, err) == -1)
1602 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1603 if (--rc < -MAXERRS)
1605 if (conv) FREE(¤tline);
1621 mutt_wait_filter (pid);
1624 /* the muttrc source keyword */
1625 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1626 : _("source: reading aborted due too many errors in %s"), rcfile);
1634 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1636 char path[_POSIX_PATH_MAX];
1641 if (mutt_extract_token (tmp, s, 0) != 0)
1643 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1647 strfcpy (path, tmp->data, sizeof (path));
1648 mutt_expand_path (path, sizeof (path));
1650 rc += source_rc (path, err);
1652 while (MoreArgs (s));
1654 return ((rc < 0) ? -1 : 0);
1657 /* line command to execute
1659 token scratch buffer to be used by parser. caller should free
1660 token->data when finished. the reason for this variable is
1661 to avoid having to allocate and deallocate a lot of memory
1662 if we are parsing many lines. the caller can pass in the
1663 memory to use, which avoids having to create new space for
1664 every call to this function.
1666 err where to write error messages */
1667 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1672 memset (&expn, 0, sizeof (expn));
1673 expn.data = expn.dptr = line;
1674 expn.dsize = mutt_strlen (line);
1681 if (*expn.dptr == '#')
1682 break; /* rest of line is a comment */
1683 if (*expn.dptr == ';')
1688 mutt_extract_token (token, &expn, 0);
1689 for (i = 0; Commands[i].name; i++)
1691 if (!mutt_strcmp (token->data, Commands[i].name))
1693 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1698 if (!Commands[i].name)
1700 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1712 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1713 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1714 /* initial string that starts completion. No telling how much crap
1715 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1716 char User_typed [LONG_STRING] = {0};
1718 int Num_matched = 0; /* Number of matches for completion */
1719 char Completed [STRING] = {0}; /* completed string (command or variable) */
1720 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1722 /* helper function for completion. Changes the dest buffer if
1723 necessary/possible to aid completion.
1724 dest == completion result gets here.
1725 src == candidate for completion.
1726 try == user entered data for completion.
1727 len == length of dest buffer.
1729 static void candidate (char *dest, char *try, char *src, int len)
1733 if (strstr (src, try) == src)
1735 Matches[Num_matched++] = src;
1737 strfcpy (dest, src, len);
1740 for (l = 0; src[l] && src[l] == dest[l]; l++);
1746 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1750 int spaces; /* keep track of the number of leading spaces on the line */
1753 spaces = buffer - pt;
1755 pt = buffer + pos - spaces;
1756 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1759 if (pt == buffer) /* complete cmd */
1761 /* first TAB. Collect all the matches */
1765 strfcpy (User_typed, pt, sizeof (User_typed));
1766 memset (Matches, 0, sizeof (Matches));
1767 memset (Completed, 0, sizeof (Completed));
1768 for (num = 0; Commands[num].name; num++)
1769 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1770 Matches[Num_matched++] = User_typed;
1772 /* All matches are stored. Longest non-ambiguous string is ""
1773 * i.e. dont change 'buffer'. Fake successful return this time */
1774 if (User_typed[0] == 0)
1778 if (Completed[0] == 0 && User_typed[0])
1781 /* Num_matched will _always_ be atleast 1 since the initial
1782 * user-typed string is always stored */
1783 if (numtabs == 1 && Num_matched == 2)
1784 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1785 else if (numtabs > 1 && Num_matched > 2)
1786 /* cycle thru all the matches */
1787 snprintf(Completed, sizeof(Completed), "%s",
1788 Matches[(numtabs - 2) % Num_matched]);
1790 /* return the completed command */
1791 strncpy (buffer, Completed, len - spaces);
1793 else if (!mutt_strncmp (buffer, "set", 3)
1794 || !mutt_strncmp (buffer, "unset", 5)
1795 || !mutt_strncmp (buffer, "reset", 5)
1796 || !mutt_strncmp (buffer, "toggle", 6))
1797 { /* complete variables */
1798 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1801 /* loop through all the possible prefixes (no, inv, ...) */
1802 if (!mutt_strncmp (buffer, "set", 3))
1804 for (num = 0; prefixes[num]; num++)
1806 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1808 pt += mutt_strlen (prefixes[num]);
1814 /* first TAB. Collect all the matches */
1818 strfcpy (User_typed, pt, sizeof (User_typed));
1819 memset (Matches, 0, sizeof (Matches));
1820 memset (Completed, 0, sizeof (Completed));
1821 for (num = 0; MuttVars[num].option; num++)
1822 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1823 Matches[Num_matched++] = User_typed;
1825 /* All matches are stored. Longest non-ambiguous string is ""
1826 * i.e. dont change 'buffer'. Fake successful return this time */
1827 if (User_typed[0] == 0)
1831 if (Completed[0] == 0 && User_typed[0])
1834 /* Num_matched will _always_ be atleast 1 since the initial
1835 * user-typed string is always stored */
1836 if (numtabs == 1 && Num_matched == 2)
1837 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1838 else if (numtabs > 1 && Num_matched > 2)
1839 /* cycle thru all the matches */
1840 snprintf(Completed, sizeof(Completed), "%s",
1841 Matches[(numtabs - 2) % Num_matched]);
1843 strncpy (pt, Completed, buffer + len - pt - spaces);
1845 else if (!mutt_strncmp (buffer, "exec", 4))
1847 struct binding_t *menu = km_get_table (CurrentMenu);
1849 if (!menu && CurrentMenu != MENU_PAGER)
1853 /* first TAB. Collect all the matches */
1857 strfcpy (User_typed, pt, sizeof (User_typed));
1858 memset (Matches, 0, sizeof (Matches));
1859 memset (Completed, 0, sizeof (Completed));
1860 for (num = 0; menu[num].name; num++)
1861 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1862 /* try the generic menu */
1863 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
1866 for (num = 0; menu[num].name; num++)
1867 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1869 Matches[Num_matched++] = User_typed;
1871 /* All matches are stored. Longest non-ambiguous string is ""
1872 * i.e. dont change 'buffer'. Fake successful return this time */
1873 if (User_typed[0] == 0)
1877 if (Completed[0] == 0 && User_typed[0])
1880 /* Num_matched will _always_ be atleast 1 since the initial
1881 * user-typed string is always stored */
1882 if (numtabs == 1 && Num_matched == 2)
1883 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1884 else if (numtabs > 1 && Num_matched > 2)
1885 /* cycle thru all the matches */
1886 snprintf(Completed, sizeof(Completed), "%s",
1887 Matches[(numtabs - 2) % Num_matched]);
1889 strncpy (pt, Completed, buffer + len - pt - spaces);
1897 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1899 char var[STRING], *pt = buffer;
1906 spaces = buffer - pt;
1908 pt = buffer + pos - spaces;
1909 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1911 pt++; /* move past the space */
1912 if (*pt == '=') /* abort if no var before the '=' */
1915 if (mutt_strncmp (buffer, "set", 3) == 0)
1918 strfcpy (var, pt, sizeof (var));
1919 /* ignore the trailing '=' when comparing */
1920 var[mutt_strlen (var) - 1] = 0;
1921 if ((idx = mutt_option_index (var)) == -1)
1922 return 0; /* no such variable. */
1925 char tmp [LONG_STRING], tmp2[LONG_STRING];
1927 size_t dlen = buffer + len - pt - spaces;
1928 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1932 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
1933 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
1934 (DTYPE(MuttVars[idx].type) == DT_RX))
1936 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1937 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1938 mutt_pretty_mailbox (tmp);
1940 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1942 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1944 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1945 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1946 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1947 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1948 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1950 const struct mapping_t *map;
1953 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1956 map = SortAliasMethods;
1958 case DT_SORT_BROWSER:
1959 map = SortBrowserMethods;
1962 if ((WithCrypto & APPLICATION_PGP))
1963 map = SortKeyMethods;
1971 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1972 snprintf (tmp, sizeof (tmp), "%s%s%s",
1973 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1974 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1977 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1978 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
1982 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
1984 if (*s == '\\' || *s == '"')
1990 strfcpy (tmp, pt, sizeof (tmp));
1991 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1999 /* Implement the -Q command line flag */
2000 int mutt_query_variables (LIST *queries)
2004 char errbuff[STRING];
2005 char command[STRING];
2009 memset (&err, 0, sizeof (err));
2010 memset (&token, 0, sizeof (token));
2013 err.dsize = sizeof (errbuff);
2015 for (p = queries; p; p = p->next)
2017 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2018 if (mutt_parse_rc_line (command, &token, &err) == -1)
2020 fprintf (stderr, "%s\n", err.data);
2024 printf ("%s\n", err.data);
2031 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2035 for (i=0; map[i].name; i++)
2036 if (map[i].value == val)
2037 return (map[i].name);
2041 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2045 for (i = 0; map[i].name; i++)
2046 if (ascii_strcasecmp (map[i].name, name) == 0)
2047 return (map[i].value);
2052 static void start_debug (void)
2056 char buf[_POSIX_PATH_MAX];
2057 char buf2[_POSIX_PATH_MAX];
2059 /* rotate the old debug logs */
2060 for (i=3; i>=0; i--)
2062 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2063 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2066 if ((debugfile = safe_fopen(buf, "w")) != NULL)
2069 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2070 fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
2071 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2076 static int mutt_execute_commands (LIST *p)
2079 char errstr[SHORT_STRING];
2081 memset (&err, 0, sizeof (err));
2083 err.dsize = sizeof (errstr);
2084 memset (&token, 0, sizeof (token));
2085 for (; p; p = p->next)
2087 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2089 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2098 void mutt_init (int skip_sys_rc, LIST *commands)
2101 struct utsname utsname;
2102 char *p, buffer[STRING], error[STRING];
2103 int i, default_rc = 0, need_pause = 0;
2106 memset (&err, 0, sizeof (err));
2108 err.dsize = sizeof (error);
2111 * XXX - use something even more difficult to predict?
2113 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2114 "\033]9;%ld\a", (long) time (NULL));
2116 /* on one of the systems I use, getcwd() does not return the same prefix
2117 as is listed in the passwd file */
2118 if ((p = getenv ("HOME")))
2119 Homedir = safe_strdup (p);
2121 /* Get some information about the user */
2122 if ((pw = getpwuid (getuid ())))
2126 Username = safe_strdup (pw->pw_name);
2128 Homedir = safe_strdup (pw->pw_dir);
2130 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2131 Shell = safe_strdup (pw->pw_shell);
2138 fputs (_("unable to determine home directory"), stderr);
2141 if ((p = getenv ("USER")))
2142 Username = safe_strdup (p);
2146 fputs (_("unable to determine username"), stderr);
2149 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2153 /* Start up debugging mode if requested */
2158 /* And about the host... */
2160 /* some systems report the FQDN instead of just the hostname */
2161 if ((p = strchr (utsname.nodename, '.')))
2163 Hostname = mutt_substrdup (utsname.nodename, p);
2165 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2168 Hostname = safe_strdup (utsname.nodename);
2171 #define DOMAIN buffer
2172 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2173 Fqdn = safe_strdup ("@");
2178 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2179 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2182 Fqdn = safe_strdup(NONULL(Hostname));
2189 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2192 fgets (buffer, sizeof (buffer), f);
2196 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2198 NewsServer = safe_strdup (p);
2202 if ((p = getenv ("NNTPSERVER")))
2203 NewsServer = safe_strdup (p);
2206 if ((p = getenv ("MAIL")))
2207 Spoolfile = safe_strdup (p);
2208 else if ((p = getenv ("MAILDIR")))
2209 Spoolfile = safe_strdup (p);
2213 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2215 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2217 Spoolfile = safe_strdup (buffer);
2220 if ((p = getenv ("MAILCAPS")))
2221 MailcapPath = safe_strdup (p);
2224 /* Default search path from RFC1524 */
2225 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2228 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2230 p = getenv ("VISUAL");
2233 p = getenv ("EDITOR");
2237 Editor = safe_strdup (p);
2238 Visual = safe_strdup (p);
2240 if ((p = getenv ("REPLYTO")) != NULL)
2244 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2246 memset (&buf, 0, sizeof (buf));
2247 buf.data = buf.dptr = buffer;
2248 buf.dsize = mutt_strlen (buffer);
2250 memset (&token, 0, sizeof (token));
2251 parse_my_hdr (&token, &buf, 0, &err);
2255 if ((p = getenv ("EMAIL")) != NULL)
2256 From = rfc822_parse_adrlist (NULL, p);
2258 mutt_set_langinfo_charset ();
2259 mutt_set_charset (Charset);
2262 /* Set standard defaults */
2263 for (i = 0; MuttVars[i].option; i++)
2265 mutt_set_default (&MuttVars[i]);
2266 mutt_restore_default (&MuttVars[i]);
2269 CurrentMenu = MENU_MAIN;
2272 #ifndef LOCALES_HACK
2273 /* Do we have a locale definition? */
2274 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2275 ((p = getenv ("LANG")) != NULL && p[0]) ||
2276 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2277 set_option (OPTLOCALES);
2281 /* Unset suspend by default if we're the session leader */
2282 if (getsid(0) == getpid())
2283 unset_option (OPTSUSPEND);
2286 mutt_init_history ();
2295 * When changing the code which looks for a configuration file,
2296 * please also change the corresponding code in muttbug.sh.in.
2306 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2307 if (access(buffer, F_OK) == -1)
2308 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2309 if (access(buffer, F_OK) == -1)
2310 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2311 if (access(buffer, F_OK) == -1)
2312 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2315 Muttrc = safe_strdup (buffer);
2319 strfcpy (buffer, Muttrc, sizeof (buffer));
2321 mutt_expand_path (buffer, sizeof (buffer));
2322 Muttrc = safe_strdup (buffer);
2325 AliasFile = safe_strdup (NONULL(Muttrc));
2327 /* Process the global rc file if it exists and the user hasn't explicity
2328 requested not to via "-n". */
2331 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2332 if (access (buffer, F_OK) == -1)
2333 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2334 if (access (buffer, F_OK) == -1)
2335 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2336 if (access (buffer, F_OK) == -1)
2337 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2338 if (access (buffer, F_OK) != -1)
2340 if (source_rc (buffer, &err) != 0)
2342 fputs (err.data, stderr);
2343 fputc ('\n', stderr);
2349 /* Read the user's initialization file. */
2350 if (access (Muttrc, F_OK) != -1)
2352 if (!option (OPTNOCURSES))
2354 if (source_rc (Muttrc, &err) != 0)
2356 fputs (err.data, stderr);
2357 fputc ('\n', stderr);
2361 else if (!default_rc)
2363 /* file specified by -F does not exist */
2364 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2365 mutt_endwin (buffer);
2369 if (mutt_execute_commands (commands) != 0)
2372 if (need_pause && !option (OPTNOCURSES))
2374 if (mutt_any_key_to_continue (NULL) == -1)
2379 set_option (OPTWEED); /* turn weeding on by default */
2383 int mutt_get_hook_type (const char *name)
2385 struct command_t *c;
2387 for (c = Commands ; c->name ; c++)
2388 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)