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)
40 #include <sys/utsname.h>
44 void toggle_quadoption (int opt)
47 int b = (opt % 4) * 2;
49 QuadOptions[n] ^= (1 << b);
52 void set_quadoption (int opt, int flag)
55 int b = (opt % 4) * 2;
57 QuadOptions[n] &= ~(0x3 << b);
58 QuadOptions[n] |= (flag & 0x3) << b;
61 int quadoption (int opt)
64 int b = (opt % 4) * 2;
66 return (QuadOptions[n] >> b) & 0x3;
69 int query_quadoption (int opt, const char *prompt)
71 int v = quadoption (opt);
79 v = mutt_yesorno (prompt, (v == M_ASKYES));
80 CLEARLINE (LINES - 1);
87 /* given the variable ``s'', return the index into the rc_vars array which
88 matches, or -1 if the variable is not found. */
89 int mutt_option_index (char *s)
93 for (i = 0; MuttVars[i].option; i++)
94 if (safe_strcmp (s, MuttVars[i].option) == 0)
95 return (MuttVars[i].type ==
96 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
100 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
103 char qc = 0; /* quote char */
106 /* reset the destination pointer to the beginning of the buffer */
107 dest->dptr = dest->data;
110 while ((ch = *tok->dptr)) {
112 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
113 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
114 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
115 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
116 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
123 qc = 0; /* end of quote */
124 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
126 else if (ch == '\\' && qc != '\'') {
128 return -1; /* premature end of token */
129 switch (ch = *tok->dptr++) {
133 return -1; /* premature end of token */
134 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
139 mutt_buffer_addch (dest, '\r');
142 mutt_buffer_addch (dest, '\n');
145 mutt_buffer_addch (dest, '\t');
148 mutt_buffer_addch (dest, '\f');
151 mutt_buffer_addch (dest, '\033');
154 if (isdigit ((unsigned char) ch) &&
155 isdigit ((unsigned char) *tok->dptr) &&
156 isdigit ((unsigned char) *(tok->dptr + 1))) {
158 mutt_buffer_addch (dest,
159 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
164 mutt_buffer_addch (dest, ch);
167 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
169 return -1; /* premature end of token */
172 mutt_buffer_addch (dest, ch);
174 mutt_buffer_addch (dest, '\033');
175 else if (isalpha ((unsigned char) ch))
176 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
178 mutt_buffer_addch (dest, '^');
179 mutt_buffer_addch (dest, ch);
182 else if (ch == '`' && (!qc || qc == '"')) {
192 if ((pc = strpbrk (pc, "\\`"))) {
193 /* skip any quoted chars */
197 } while (pc && *pc != '`');
199 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
202 cmd = str_substrdup (tok->dptr, pc);
203 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
205 (debugfile, "mutt_get_token: unable to fork command: %s",
215 memset (&expn, 0, sizeof (expn));
216 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
218 mutt_wait_filter (pid);
220 /* if we got output, make a new string consiting of the shell ouptput
221 plus whatever else was left on the original line */
222 /* BUT: If this is inside a quoted string, directly add output to
224 if (expn.data && qc) {
225 mutt_buffer_addstr (dest, expn.data);
228 else if (expn.data) {
229 expnlen = safe_strlen (expn.data);
230 tok->dsize = expnlen + safe_strlen (tok->dptr) + 1;
231 ptr = safe_malloc (tok->dsize);
232 memcpy (ptr, expn.data, expnlen);
233 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
238 tok->destroy = 1; /* mark that the caller should destroy this data */
243 else if (ch == '$' && (!qc || qc == '"')
244 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
245 char *env = NULL, *var = NULL;
247 if (*tok->dptr == '{') {
249 if ((pc = strchr (tok->dptr, '}'))) {
250 var = str_substrdup (tok->dptr, pc);
255 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
257 var = str_substrdup (tok->dptr, pc);
260 if (var && (env = getenv (var)))
261 mutt_buffer_addstr (dest, env);
265 mutt_buffer_addch (dest, ch);
267 mutt_buffer_addch (dest, 0); /* terminate the string */
272 static void add_to_list (LIST ** list, const char *str)
274 LIST *t, *last = NULL;
276 /* don't add a NULL or empty string to the list */
277 if (!str || *str == '\0')
280 /* check to make sure the item is not already on this list */
281 for (last = *list; last; last = last->next) {
282 if (ascii_strcasecmp (str, last->data) == 0) {
283 /* already on the list, so just ignore it */
291 if (!*list || last) {
292 t = (LIST *) safe_calloc (1, sizeof (LIST));
293 t->data = safe_strdup (str);
303 static int add_to_rx_list (list2_t** list, const char *s, int flags,
312 if (!(rx = rx_compile (s, flags))) {
313 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
317 i = rx_lookup ((*list), rx->pattern);
321 list_push_back (list, rx);
325 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
326 const char *templ, BUFFER * err)
328 SPAM_LIST *t = NULL, *last = NULL;
333 if (!pat || !*pat || !templ)
336 if (!(rx = rx_compile (pat, REG_ICASE))) {
337 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
341 /* check to make sure the item is not already on this list */
342 for (last = *list; last; last = last->next) {
343 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
344 /* Already on the list. Formerly we just skipped this case, but
345 * now we're supporting removals, which means we're supporting
346 * re-adds conceptually. So we probably want this to imply a
347 * removal, then do an add. We can achieve the removal by freeing
348 * the template, and leaving t pointed at the current item.
358 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
359 * update. Otherwise we want to make a new one to link at the list's end.
362 t = mutt_new_spam_list ();
370 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
371 t->template = safe_strdup (templ);
373 /* Find highest match number in template string */
375 for (p = templ; *p;) {
380 while (*p && isdigit ((int) *p))
386 t->nmatch++; /* match 0 is always the whole expr */
391 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
393 SPAM_LIST *spam, *prev;
396 /* Being first is a special case. */
398 if (spam->rx && !safe_strcmp (spam->rx->pattern, pat)) {
401 FREE(&spam->template);
407 for (spam = prev->next; spam;) {
408 if (!safe_strcmp (spam->rx->pattern, pat)) {
409 prev->next = spam->next;
411 FREE(spam->template);
424 static void remove_from_list (LIST ** l, const char *str)
426 LIST *p, *last = NULL;
428 if (safe_strcmp ("*", str) == 0)
429 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
434 if (ascii_strcasecmp (str, p->data) == 0) {
437 last->next = p->next;
450 static int remove_from_rx_list (list2_t** l, const char *str)
454 if (safe_strcmp ("*", str) == 0) {
455 list_del (l, rx_free);
459 i = rx_lookup ((*l), str);
461 rx_t* r = list_pop_idx ((*l), i);
469 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
475 memset (&token, 0, sizeof (token));
476 mutt_extract_token (tmp, s, 0);
478 /* is the item defined as a variable or a function? */
479 if (!(res = (mutt_option_index (tmp->data) != -1)))
480 for (i = 0; !res && i < MENU_MAX; i++) {
481 struct binding_t *b = km_get_table (Menus[i].value);
486 for (j = 0; b[j].name; j++)
487 if (!ascii_strncasecmp (tmp->data, b[j].name, safe_strlen (tmp->data))
488 && (safe_strlen (b[j].name) == safe_strlen (tmp->data))) {
493 /* check for feature_* */
498 j = safe_strlen (tmp->data);
499 /* need at least input of 'feature_X' */
503 while (Features[i].name) {
504 if (safe_strlen (Features[i].name) == j &&
505 ascii_strncasecmp (Features[i].name, p, j)) {
516 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
518 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
521 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
523 if ((data && res) || (!data && !res)) {
524 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
525 mutt_error ("Error: %s", err->data);
534 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
538 mutt_extract_token (buf, s, 0);
540 /* don't add "*" to the unignore list */
541 if (strcmp (buf->data, "*"))
542 add_to_list (&UnIgnore, buf->data);
544 remove_from_list (&Ignore, buf->data);
546 while (MoreArgs (s));
551 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
555 mutt_extract_token (buf, s, 0);
556 remove_from_list (&UnIgnore, buf->data);
557 add_to_list (&Ignore, buf->data);
559 while (MoreArgs (s));
564 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
568 mutt_extract_token (buf, s, 0);
569 add_to_list ((LIST **) data, buf->data);
571 while (MoreArgs (s));
576 static void _alternates_clean (void)
580 if (Context && Context->msgcount) {
581 for (i = 0; i < Context->msgcount; i++)
582 Context->hdrs[i]->recip_valid = 0;
586 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
589 _alternates_clean ();
591 mutt_extract_token (buf, s, 0);
592 remove_from_rx_list (&UnAlternates, buf->data);
594 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
597 while (MoreArgs (s));
602 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
605 _alternates_clean ();
607 mutt_extract_token (buf, s, 0);
608 remove_from_rx_list (&Alternates, buf->data);
610 if (safe_strcmp (buf->data, "*") &&
611 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
615 while (MoreArgs (s));
620 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
625 memset (&templ, 0, sizeof (templ));
627 /* Insist on at least one parameter */
630 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
632 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
636 /* Extract the first token, a regexp */
637 mutt_extract_token (buf, s, 0);
639 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
640 if (data == M_SPAM) {
641 /* If there's a second parameter, it's a template for the spam tag. */
643 mutt_extract_token (&templ, s, 0);
645 /* Add to the spam list. */
646 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
653 /* If not, try to remove from the nospam list. */
655 remove_from_rx_list (&NoSpamList, buf->data);
661 /* M_NOSPAM is for nospam commands. */
662 else if (data == M_NOSPAM) {
663 /* nospam only ever has one parameter. */
665 /* "*" is a special case. */
666 if (!safe_strcmp (buf->data, "*")) {
667 mutt_free_spam_list (&SpamList);
668 list_del (&NoSpamList, rx_free);
672 /* If it's on the spam list, just remove it. */
673 if (remove_from_spam_list (&SpamList, buf->data) != 0)
676 /* Otherwise, add it to the nospam list. */
677 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
683 /* This should not happen. */
684 strfcpy (err->data, "This is no good at all.", err->dsize);
688 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
692 mutt_extract_token (buf, s, 0);
694 * Check for deletion of entire list
696 if (safe_strcmp (buf->data, "*") == 0) {
697 mutt_free_list ((LIST **) data);
700 remove_from_list ((LIST **) data, buf->data);
702 while (MoreArgs (s));
707 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
711 mutt_extract_token (buf, s, 0);
712 remove_from_rx_list (&UnMailLists, buf->data);
714 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
717 while (MoreArgs (s));
722 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
726 mutt_extract_token (buf, s, 0);
727 remove_from_rx_list (&SubscribedLists, buf->data);
728 remove_from_rx_list (&MailLists, buf->data);
730 if (safe_strcmp (buf->data, "*") &&
731 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
734 while (MoreArgs (s));
739 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
743 mutt_extract_token (buf, s, 0);
744 remove_from_rx_list (&UnMailLists, buf->data);
745 remove_from_rx_list (&UnSubscribedLists, buf->data);
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_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
761 mutt_extract_token (buf, s, 0);
762 remove_from_rx_list (&SubscribedLists, buf->data);
764 if (safe_strcmp (buf->data, "*") &&
765 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
768 while (MoreArgs (s));
773 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
776 ALIAS *tmp, *last = NULL;
779 mutt_extract_token (buf, s, 0);
781 if (safe_strcmp ("*", buf->data) == 0) {
782 if (CurrentMenu == MENU_ALIAS) {
783 for (tmp = Aliases; tmp; tmp = tmp->next)
785 set_option (OPTFORCEREDRAWINDEX);
788 mutt_free_alias (&Aliases);
792 for (tmp = Aliases; tmp; tmp = tmp->next) {
793 if (safe_strcasecmp (buf->data, tmp->name) == 0) {
794 if (CurrentMenu == MENU_ALIAS) {
796 set_option (OPTFORCEREDRAWINDEX);
801 last->next = tmp->next;
805 mutt_free_alias (&tmp);
811 while (MoreArgs (s));
815 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
818 ALIAS *tmp = Aliases;
823 strfcpy (err->data, _("alias: no address"), err->dsize);
827 mutt_extract_token (buf, s, 0);
829 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
831 /* check to see if an alias with this name already exists */
832 for (; tmp; tmp = tmp->next) {
833 if (!safe_strcasecmp (tmp->name, buf->data))
839 /* create a new alias */
840 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
842 tmp->name = safe_strdup (buf->data);
843 /* give the main addressbook code a chance */
844 if (CurrentMenu == MENU_ALIAS)
845 set_option (OPTMENUCALLER);
848 /* override the previous value */
849 rfc822_free_address (&tmp->addr);
850 if (CurrentMenu == MENU_ALIAS)
851 set_option (OPTFORCEREDRAWINDEX);
854 mutt_extract_token (buf, s,
855 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
856 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
857 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
862 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
863 snprintf (err->data, err->dsize,
864 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
868 if (debuglevel >= 2) {
871 for (a = tmp->addr; a; a = a->next) {
873 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
875 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
883 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
886 LIST *tmp = UserHeader;
891 mutt_extract_token (buf, s, 0);
892 if (safe_strcmp ("*", buf->data) == 0)
893 mutt_free_list (&UserHeader);
898 l = safe_strlen (buf->data);
899 if (buf->data[l - 1] == ':')
903 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
904 && tmp->data[l] == ':') {
907 last->next = tmp->next;
909 UserHeader = tmp->next;
912 mutt_free_list (&ptr);
921 while (MoreArgs (s));
925 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
932 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
933 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
934 strfcpy (err->data, _("invalid header field"), err->dsize);
937 keylen = p - buf->data + 1;
940 for (tmp = UserHeader;; tmp = tmp->next) {
941 /* see if there is already a field by this name */
942 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
943 /* replace the old value */
945 tmp->data = buf->data;
946 memset (buf, 0, sizeof (BUFFER));
952 tmp->next = mutt_new_list ();
956 tmp = mutt_new_list ();
959 tmp->data = buf->data;
960 memset (buf, 0, sizeof (BUFFER));
965 parse_sort (short *val, const char *s, const struct mapping_t *map,
970 if (safe_strncmp ("reverse-", s, 8) == 0) {
972 flags = SORT_REVERSE;
975 if (safe_strncmp ("last-", s, 5) == 0) {
980 if ((i = mutt_getvaluebyname (s, map)) == -1) {
981 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
990 static void mutt_set_default (struct option_t *p)
992 switch (p->type & DT_MASK) {
994 if (!p->init && *((char **) p->data))
995 p->init = (unsigned long) safe_strdup (*((char **) p->data));
998 if (!p->init && *((char **) p->data)) {
999 char *cp = safe_strdup (*((char **) p->data));
1001 /* mutt_pretty_mailbox (cp); */
1002 p->init = (unsigned long) cp;
1006 if (!p->init && *((ADDRESS **) p->data)) {
1007 char tmp[HUGE_STRING];
1010 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1011 p->init = (unsigned long) safe_strdup (tmp);
1016 rx_t* pp = (rx_t*) p->data;
1018 if (!p->init && pp->pattern)
1019 p->init = (unsigned long) safe_strdup (pp->pattern);
1025 static void mutt_restore_default (struct option_t *p)
1027 switch (p->type & DT_MASK) {
1030 str_replace ((char **) p->data, (char *) p->init);
1034 char path[_POSIX_PATH_MAX];
1036 strfcpy (path, (char *) p->init, sizeof (path));
1037 mutt_expand_path (path, sizeof (path));
1038 str_replace ((char **) p->data, path);
1043 rfc822_free_address ((ADDRESS **) p->data);
1044 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1049 set_option (p->data);
1051 unset_option (p->data);
1054 set_quadoption (p->data, p->init);
1059 *((short *) p->data) = p->init;
1063 rx_t *pp = (rx_t *) p->data;
1066 FREE (&pp->pattern);
1073 char *s = (char *) p->init;
1075 pp->rx = safe_calloc (1, sizeof (regex_t));
1076 if (safe_strcmp (p->option, "mask") != 0)
1077 flags |= mutt_which_case ((const char *) p->init);
1078 if (safe_strcmp (p->option, "mask") == 0 && *s == '!') {
1082 if (REGCOMP (pp->rx, s, flags) != 0) {
1084 _("mutt_restore_default(%s): error in regexp: %s\n"),
1085 p->option, pp->pattern);
1086 FREE (&pp->pattern);
1091 str_replace (&pp->pattern, (char *) p->init);
1097 if (p->flags & R_INDEX)
1098 set_option (OPTFORCEREDRAWINDEX);
1099 if (p->flags & R_PAGER)
1100 set_option (OPTFORCEREDRAWPAGER);
1101 if (p->flags & R_RESORT_SUB)
1102 set_option (OPTSORTSUBTHREADS);
1103 if (p->flags & R_RESORT)
1104 set_option (OPTNEEDRESORT);
1105 if (p->flags & R_RESORT_INIT)
1106 set_option (OPTRESORTINIT);
1107 if (p->flags & R_TREE)
1108 set_option (OPTREDRAWTREE);
1111 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1114 int idx, query, unset, inv, reset, r = 0;
1115 char *p, scratch[_POSIX_PATH_MAX];
1117 while (MoreArgs (s)) {
1118 /* reset state variables */
1120 unset = data & M_SET_UNSET;
1121 inv = data & M_SET_INV;
1122 reset = data & M_SET_RESET;
1124 if (*s->dptr == '?') {
1128 else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1132 else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1136 else if (*s->dptr == '&') {
1141 /* get the variable name */
1142 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1144 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1145 !(reset && !safe_strcmp ("all", tmp->data))) {
1146 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1152 if (query || unset || inv) {
1153 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1157 if (s && *s->dptr == '=') {
1158 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1162 if (!safe_strcmp ("all", tmp->data)) {
1163 for (idx = 0; MuttVars[idx].option; idx++)
1164 mutt_restore_default (&MuttVars[idx]);
1168 mutt_restore_default (&MuttVars[idx]);
1170 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1171 if (s && *s->dptr == '=') {
1172 if (unset || inv || query) {
1173 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1178 mutt_extract_token (tmp, s, 0);
1179 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1181 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1184 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1190 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1191 ? _("%s is set") : _("%s is unset"), tmp->data);
1196 unset_option (MuttVars[idx].data);
1198 toggle_option (MuttVars[idx].data);
1200 set_option (MuttVars[idx].data);
1202 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1203 DTYPE (MuttVars[idx].type) == DT_PATH ||
1204 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1206 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1207 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1209 FREE ((void *) MuttVars[idx].data);
1211 else if (query || *s->dptr != '=') {
1215 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1217 rfc822_write_address (_tmp, sizeof (_tmp),
1218 *((ADDRESS **) MuttVars[idx].data), 0);
1222 val = *((char **) MuttVars[idx].data);
1224 /* user requested the value of this variable */
1225 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1232 /* copy the value of the string */
1233 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1234 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1236 FREE ((void *) MuttVars[idx].data);
1238 mutt_extract_token (tmp, s, 0);
1239 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1240 strfcpy (scratch, tmp->data, sizeof (scratch));
1241 mutt_expand_path (scratch, sizeof (scratch));
1242 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1244 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1245 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1246 if (safe_strcmp (MuttVars[idx].option, "charset") == 0)
1247 mutt_set_charset (Charset);
1250 *((ADDRESS **) MuttVars[idx].data) =
1251 rfc822_parse_adrlist (NULL, tmp->data);
1255 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1256 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1260 if (query || *s->dptr != '=') {
1261 /* user requested the value of this variable */
1262 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1263 NONULL (ptr->pattern));
1267 if (option (OPTATTACHMSG)
1268 && !safe_strcmp (MuttVars[idx].option, "reply_regexp")) {
1269 snprintf (err->data, err->dsize,
1270 "Operation not permitted when in attach-message mode.");
1277 /* copy the value of the string */
1278 mutt_extract_token (tmp, s, 0);
1280 if (!ptr->pattern || safe_strcmp (ptr->pattern, tmp->data) != 0) {
1283 /* $mask is case-sensitive */
1284 if (safe_strcmp (MuttVars[idx].option, "mask") != 0)
1285 flags |= mutt_which_case (tmp->data);
1288 if (safe_strcmp (MuttVars[idx].option, "mask") == 0) {
1295 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1296 if ((e = REGCOMP (rx, p, flags)) != 0) {
1297 regerror (e, rx, err->data, err->dsize);
1303 /* get here only if everything went smootly */
1305 FREE (&ptr->pattern);
1306 regfree ((regex_t *) ptr->rx);
1310 ptr->pattern = safe_strdup (tmp->data);
1314 /* $reply_regexp and $alterantes require special treatment */
1316 if (Context && Context->msgcount &&
1317 safe_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1318 regmatch_t pmatch[1];
1321 #define CUR_ENV Context->hdrs[i]->env
1322 for (i = 0; i < Context->msgcount; i++) {
1323 if (CUR_ENV && CUR_ENV->subject) {
1324 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1325 CUR_ENV->subject, 1, pmatch,
1327 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1334 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1335 if (query || *s->dptr != '=') {
1336 switch (DefaultMagic) {
1353 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1359 /* copy the value of the string */
1360 mutt_extract_token (tmp, s, 0);
1361 if (mx_set_magic (tmp->data)) {
1362 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1368 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1369 short *ptr = (short *) MuttVars[idx].data;
1373 if (query || *s->dptr != '=') {
1374 /* user requested the value of this variable */
1375 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1381 mutt_extract_token (tmp, s, 0);
1382 val = strtol (tmp->data, &t, 0);
1384 if (!*tmp->data || *t || (short) val != val) {
1385 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1392 /* these ones need a sanity check */
1393 if (safe_strcmp (MuttVars[idx].option, "history") == 0) {
1396 mutt_init_history ();
1398 else if (safe_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1403 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1405 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1407 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1408 vals[quadoption (MuttVars[idx].data)]);
1412 if (*s->dptr == '=') {
1414 mutt_extract_token (tmp, s, 0);
1415 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1416 set_quadoption (MuttVars[idx].data, M_YES);
1417 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1418 set_quadoption (MuttVars[idx].data, M_NO);
1419 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1420 set_quadoption (MuttVars[idx].data, M_ASKYES);
1421 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1422 set_quadoption (MuttVars[idx].data, M_ASKNO);
1424 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1431 toggle_quadoption (MuttVars[idx].data);
1433 set_quadoption (MuttVars[idx].data, M_NO);
1435 set_quadoption (MuttVars[idx].data, M_YES);
1438 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1439 const struct mapping_t *map = NULL;
1441 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1443 map = SortAliasMethods;
1445 case DT_SORT_BROWSER:
1446 map = SortBrowserMethods;
1449 if ((WithCrypto & APPLICATION_PGP))
1450 map = SortKeyMethods;
1453 map = SortAuxMethods;
1461 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1462 MuttVars[idx].option);
1467 if (query || *s->dptr != '=') {
1469 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1472 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1473 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1475 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1480 mutt_extract_token (tmp, s, 0);
1482 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1489 snprintf (err->data, err->dsize, _("%s: unknown type"),
1490 MuttVars[idx].option);
1495 if (MuttVars[idx].flags & R_INDEX)
1496 set_option (OPTFORCEREDRAWINDEX);
1497 if (MuttVars[idx].flags & R_PAGER)
1498 set_option (OPTFORCEREDRAWPAGER);
1499 if (MuttVars[idx].flags & R_RESORT_SUB)
1500 set_option (OPTSORTSUBTHREADS);
1501 if (MuttVars[idx].flags & R_RESORT)
1502 set_option (OPTNEEDRESORT);
1503 if (MuttVars[idx].flags & R_RESORT_INIT)
1504 set_option (OPTRESORTINIT);
1505 if (MuttVars[idx].flags & R_TREE)
1506 set_option (OPTREDRAWTREE);
1513 /* reads the specified initialization file. returns -1 if errors were found
1514 so that we can pause to let the user know... */
1515 static int source_rc (const char *rcfile, BUFFER * err)
1518 int line = 0, rc = 0, conv = 0;
1520 char *linebuf = NULL;
1521 char *currentline = NULL;
1525 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1527 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1528 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1532 memset (&token, 0, sizeof (token));
1533 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1534 conv = ConfigCharset && (*ConfigCharset) && Charset;
1536 currentline = safe_strdup (linebuf);
1539 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1542 currentline = linebuf;
1544 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1545 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1546 if (--rc < -MAXERRS) {
1548 FREE (¤tline);
1557 FREE (¤tline);
1563 mutt_wait_filter (pid);
1565 /* the muttrc source keyword */
1566 snprintf (err->data, err->dsize,
1567 rc >= -MAXERRS ? _("source: errors in %s")
1568 : _("source: reading aborted due too many errors in %s"),
1577 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1580 char path[_POSIX_PATH_MAX];
1584 if (mutt_extract_token (tmp, s, 0) != 0) {
1585 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1589 strfcpy (path, tmp->data, sizeof (path));
1590 mutt_expand_path (path, sizeof (path));
1592 rc += source_rc (path, err);
1594 while (MoreArgs (s));
1596 return ((rc < 0) ? -1 : 0);
1599 /* line command to execute
1601 token scratch buffer to be used by parser. caller should free
1602 token->data when finished. the reason for this variable is
1603 to avoid having to allocate and deallocate a lot of memory
1604 if we are parsing many lines. the caller can pass in the
1605 memory to use, which avoids having to create new space for
1606 every call to this function.
1608 err where to write error messages */
1609 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1614 memset (&expn, 0, sizeof (expn));
1615 expn.data = expn.dptr = line;
1616 expn.dsize = safe_strlen (line);
1621 while (*expn.dptr) {
1622 if (*expn.dptr == '#')
1623 break; /* rest of line is a comment */
1624 if (*expn.dptr == ';') {
1628 mutt_extract_token (token, &expn, 0);
1629 for (i = 0; Commands[i].name; i++) {
1630 if (!safe_strcmp (token->data, Commands[i].name)) {
1631 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1636 if (!Commands[i].name) {
1637 snprintf (err->data, err->dsize, _("%s: unknown command"),
1638 NONULL (token->data));
1650 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1651 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1652 /* initial string that starts completion. No telling how much crap
1653 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1654 char User_typed[LONG_STRING] = { 0 };
1656 int Num_matched = 0; /* Number of matches for completion */
1657 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1658 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1660 /* helper function for completion. Changes the dest buffer if
1661 necessary/possible to aid completion.
1662 dest == completion result gets here.
1663 src == candidate for completion.
1664 try == user entered data for completion.
1665 len == length of dest buffer.
1667 static void candidate (char *dest, char *try, char *src, int len)
1671 if (strstr (src, try) == src) {
1672 Matches[Num_matched++] = src;
1674 strfcpy (dest, src, len);
1676 for (l = 0; src[l] && src[l] == dest[l]; l++);
1682 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1686 int spaces; /* keep track of the number of leading spaces on the line */
1689 spaces = buffer - pt;
1691 pt = buffer + pos - spaces;
1692 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1695 if (pt == buffer) { /* complete cmd */
1696 /* first TAB. Collect all the matches */
1699 strfcpy (User_typed, pt, sizeof (User_typed));
1700 memset (Matches, 0, sizeof (Matches));
1701 memset (Completed, 0, sizeof (Completed));
1702 for (num = 0; Commands[num].name; num++)
1703 candidate (Completed, User_typed, Commands[num].name,
1704 sizeof (Completed));
1705 Matches[Num_matched++] = User_typed;
1707 /* All matches are stored. Longest non-ambiguous string is ""
1708 * i.e. dont change 'buffer'. Fake successful return this time */
1709 if (User_typed[0] == 0)
1713 if (Completed[0] == 0 && User_typed[0])
1716 /* Num_matched will _always_ be atleast 1 since the initial
1717 * user-typed string is always stored */
1718 if (numtabs == 1 && Num_matched == 2)
1719 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1720 else if (numtabs > 1 && Num_matched > 2)
1721 /* cycle thru all the matches */
1722 snprintf (Completed, sizeof (Completed), "%s",
1723 Matches[(numtabs - 2) % Num_matched]);
1725 /* return the completed command */
1726 strncpy (buffer, Completed, len - spaces);
1728 else if (!safe_strncmp (buffer, "set", 3)
1729 || !safe_strncmp (buffer, "unset", 5)
1730 || !safe_strncmp (buffer, "reset", 5)
1731 || !safe_strncmp (buffer, "toggle", 6)) { /* complete variables */
1732 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1735 /* loop through all the possible prefixes (no, inv, ...) */
1736 if (!safe_strncmp (buffer, "set", 3)) {
1737 for (num = 0; prefixes[num]; num++) {
1738 if (!safe_strncmp (pt, prefixes[num], safe_strlen (prefixes[num]))) {
1739 pt += safe_strlen (prefixes[num]);
1745 /* first TAB. Collect all the matches */
1748 strfcpy (User_typed, pt, sizeof (User_typed));
1749 memset (Matches, 0, sizeof (Matches));
1750 memset (Completed, 0, sizeof (Completed));
1751 for (num = 0; MuttVars[num].option; num++)
1752 candidate (Completed, User_typed, MuttVars[num].option,
1753 sizeof (Completed));
1754 Matches[Num_matched++] = User_typed;
1756 /* All matches are stored. Longest non-ambiguous string is ""
1757 * i.e. dont change 'buffer'. Fake successful return this time */
1758 if (User_typed[0] == 0)
1762 if (Completed[0] == 0 && User_typed[0])
1765 /* Num_matched will _always_ be atleast 1 since the initial
1766 * user-typed string is always stored */
1767 if (numtabs == 1 && Num_matched == 2)
1768 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1769 else if (numtabs > 1 && Num_matched > 2)
1770 /* cycle thru all the matches */
1771 snprintf (Completed, sizeof (Completed), "%s",
1772 Matches[(numtabs - 2) % Num_matched]);
1774 strncpy (pt, Completed, buffer + len - pt - spaces);
1776 else if (!safe_strncmp (buffer, "exec", 4)) {
1777 struct binding_t *menu = km_get_table (CurrentMenu);
1779 if (!menu && CurrentMenu != MENU_PAGER)
1783 /* first TAB. Collect all the matches */
1786 strfcpy (User_typed, pt, sizeof (User_typed));
1787 memset (Matches, 0, sizeof (Matches));
1788 memset (Completed, 0, sizeof (Completed));
1789 for (num = 0; menu[num].name; num++)
1790 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1791 /* try the generic menu */
1792 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1794 for (num = 0; menu[num].name; num++)
1795 candidate (Completed, User_typed, menu[num].name,
1796 sizeof (Completed));
1798 Matches[Num_matched++] = User_typed;
1800 /* All matches are stored. Longest non-ambiguous string is ""
1801 * i.e. dont change 'buffer'. Fake successful return this time */
1802 if (User_typed[0] == 0)
1806 if (Completed[0] == 0 && User_typed[0])
1809 /* Num_matched will _always_ be atleast 1 since the initial
1810 * user-typed string is always stored */
1811 if (numtabs == 1 && Num_matched == 2)
1812 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1813 else if (numtabs > 1 && Num_matched > 2)
1814 /* cycle thru all the matches */
1815 snprintf (Completed, sizeof (Completed), "%s",
1816 Matches[(numtabs - 2) % Num_matched]);
1818 strncpy (pt, Completed, buffer + len - pt - spaces);
1826 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1828 char var[STRING], *pt = buffer;
1835 spaces = buffer - pt;
1837 pt = buffer + pos - spaces;
1838 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1840 pt++; /* move past the space */
1841 if (*pt == '=') /* abort if no var before the '=' */
1844 if (safe_strncmp (buffer, "set", 3) == 0) {
1847 strfcpy (var, pt, sizeof (var));
1848 /* ignore the trailing '=' when comparing */
1849 var[safe_strlen (var) - 1] = 0;
1850 if ((idx = mutt_option_index (var)) == -1)
1851 return 0; /* no such variable. */
1853 char tmp[LONG_STRING], tmp2[LONG_STRING];
1855 size_t dlen = buffer + len - pt - spaces;
1856 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1860 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1861 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1862 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1863 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1864 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1865 mutt_pretty_mailbox (tmp);
1867 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1868 rfc822_write_address (tmp, sizeof (tmp),
1869 *((ADDRESS **) MuttVars[idx].data), 0);
1871 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1872 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1873 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1874 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1875 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1876 const struct mapping_t *map;
1879 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1881 map = SortAliasMethods;
1883 case DT_SORT_BROWSER:
1884 map = SortBrowserMethods;
1887 if ((WithCrypto & APPLICATION_PGP))
1888 map = SortKeyMethods;
1897 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1899 snprintf (tmp, sizeof (tmp), "%s%s%s",
1900 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1902 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1905 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1906 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1911 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1912 if (*s == '\\' || *s == '"')
1918 strfcpy (tmp, pt, sizeof (tmp));
1919 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1927 /* Implement the -Q command line flag */
1928 int mutt_query_variables (LIST * queries)
1932 char errbuff[STRING];
1933 char command[STRING];
1937 memset (&err, 0, sizeof (err));
1938 memset (&token, 0, sizeof (token));
1941 err.dsize = sizeof (errbuff);
1943 for (p = queries; p; p = p->next) {
1944 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1945 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1946 fprintf (stderr, "%s\n", err.data);
1950 printf ("%s\n", err.data);
1957 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1961 for (i = 0; map[i].name; i++)
1962 if (map[i].value == val)
1963 return (map[i].name);
1967 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1971 for (i = 0; map[i].name; i++)
1972 if (ascii_strcasecmp (map[i].name, name) == 0)
1973 return (map[i].value);
1978 static void start_debug (void)
1982 char buf[_POSIX_PATH_MAX];
1983 char buf2[_POSIX_PATH_MAX];
1985 /* rotate the old debug logs */
1986 for (i = 3; i >= 0; i--) {
1987 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
1988 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
1992 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
1994 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
1996 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
1997 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2002 static int mutt_execute_commands (LIST * p)
2005 char errstr[SHORT_STRING];
2007 memset (&err, 0, sizeof (err));
2009 err.dsize = sizeof (errstr);
2010 memset (&token, 0, sizeof (token));
2011 for (; p; p = p->next) {
2012 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2013 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2022 void mutt_init (int skip_sys_rc, LIST * commands)
2025 struct utsname utsname;
2026 char *p, buffer[STRING], error[STRING];
2027 int i, default_rc = 0, need_pause = 0;
2030 memset (&err, 0, sizeof (err));
2032 err.dsize = sizeof (error);
2035 * XXX - use something even more difficult to predict?
2037 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2038 "\033]9;%ld\a", (long) time (NULL));
2040 /* on one of the systems I use, getcwd() does not return the same prefix
2041 as is listed in the passwd file */
2042 if ((p = getenv ("HOME")))
2043 Homedir = safe_strdup (p);
2045 /* Get some information about the user */
2046 if ((pw = getpwuid (getuid ()))) {
2049 Username = safe_strdup (pw->pw_name);
2051 Homedir = safe_strdup (pw->pw_dir);
2053 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2054 Shell = safe_strdup (pw->pw_shell);
2059 fputs (_("unable to determine home directory"), stderr);
2062 if ((p = getenv ("USER")))
2063 Username = safe_strdup (p);
2066 fputs (_("unable to determine username"), stderr);
2069 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2073 /* Start up debugging mode if requested */
2078 /* And about the host... */
2080 /* some systems report the FQDN instead of just the hostname */
2081 if ((p = strchr (utsname.nodename, '.'))) {
2082 Hostname = str_substrdup (utsname.nodename, p);
2084 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2087 Hostname = safe_strdup (utsname.nodename);
2090 #define DOMAIN buffer
2091 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2092 Fqdn = safe_strdup ("@");
2095 if (*DOMAIN != '@') {
2096 Fqdn = safe_malloc (safe_strlen (DOMAIN) + safe_strlen (Hostname) + 2);
2097 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2100 Fqdn = safe_strdup (NONULL (Hostname));
2107 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2109 fgets (buffer, sizeof (buffer), f);
2110 p = (char*) &buffer;
2113 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2117 NewsServer = safe_strdup (p);
2121 if ((p = getenv ("NNTPSERVER")))
2122 NewsServer = safe_strdup (p);
2125 if ((p = getenv ("MAIL")))
2126 Spoolfile = safe_strdup (p);
2127 else if ((p = getenv ("MAILDIR")))
2128 Spoolfile = safe_strdup (p);
2131 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2133 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2135 Spoolfile = safe_strdup (buffer);
2138 if ((p = getenv ("MAILCAPS")))
2139 MailcapPath = safe_strdup (p);
2141 /* Default search path from RFC1524 */
2143 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2144 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2147 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2149 p = getenv ("VISUAL");
2151 p = getenv ("EDITOR");
2155 Editor = safe_strdup (p);
2156 Visual = safe_strdup (p);
2158 if ((p = getenv ("REPLYTO")) != NULL) {
2161 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2163 memset (&buf, 0, sizeof (buf));
2164 buf.data = buf.dptr = buffer;
2165 buf.dsize = safe_strlen (buffer);
2167 memset (&token, 0, sizeof (token));
2168 parse_my_hdr (&token, &buf, 0, &err);
2172 if ((p = getenv ("EMAIL")) != NULL)
2173 From = rfc822_parse_adrlist (NULL, p);
2175 mutt_set_langinfo_charset ();
2176 mutt_set_charset (Charset);
2179 /* Set standard defaults */
2180 for (i = 0; MuttVars[i].option; i++) {
2181 mutt_set_default (&MuttVars[i]);
2182 mutt_restore_default (&MuttVars[i]);
2185 CurrentMenu = MENU_MAIN;
2188 #ifndef LOCALES_HACK
2189 /* Do we have a locale definition? */
2190 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2191 ((p = getenv ("LANG")) != NULL && p[0]) ||
2192 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2193 set_option (OPTLOCALES);
2197 /* Unset suspend by default if we're the session leader */
2198 if (getsid (0) == getpid ())
2199 unset_option (OPTSUSPEND);
2202 mutt_init_history ();
2211 * When changing the code which looks for a configuration file,
2212 * please also change the corresponding code in muttbug.sh.in.
2221 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2223 if (access (buffer, F_OK) == -1)
2224 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2225 if (access (buffer, F_OK) == -1)
2226 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2227 NONULL (Homedir), MUTT_VERSION);
2228 if (access (buffer, F_OK) == -1)
2229 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2233 Muttrc = safe_strdup (buffer);
2236 strfcpy (buffer, Muttrc, sizeof (buffer));
2238 mutt_expand_path (buffer, sizeof (buffer));
2239 Muttrc = safe_strdup (buffer);
2242 AliasFile = safe_strdup (NONULL (Muttrc));
2244 /* Process the global rc file if it exists and the user hasn't explicity
2245 requested not to via "-n". */
2247 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2249 if (access (buffer, F_OK) == -1)
2250 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2251 if (access (buffer, F_OK) == -1)
2252 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2254 if (access (buffer, F_OK) == -1)
2255 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2256 if (access (buffer, F_OK) != -1) {
2257 if (source_rc (buffer, &err) != 0) {
2258 fputs (err.data, stderr);
2259 fputc ('\n', stderr);
2265 /* Read the user's initialization file. */
2266 if (access (Muttrc, F_OK) != -1) {
2267 if (!option (OPTNOCURSES))
2269 if (source_rc (Muttrc, &err) != 0) {
2270 fputs (err.data, stderr);
2271 fputc ('\n', stderr);
2275 else if (!default_rc) {
2276 /* file specified by -F does not exist */
2277 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2278 mutt_endwin (buffer);
2282 if (mutt_execute_commands (commands) != 0)
2285 if (need_pause && !option (OPTNOCURSES)) {
2286 if (mutt_any_key_to_continue (NULL) == -1)
2291 set_option (OPTWEED); /* turn weeding on by default */
2295 int mutt_get_hook_type (const char *name)
2297 struct command_t *c;
2299 for (c = Commands; c->name; c++)
2300 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)