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>
46 void toggle_quadoption (int opt)
49 int b = (opt % 4) * 2;
51 QuadOptions[n] ^= (1 << b);
54 void set_quadoption (int opt, int flag)
57 int b = (opt % 4) * 2;
59 QuadOptions[n] &= ~(0x3 << b);
60 QuadOptions[n] |= (flag & 0x3) << b;
63 int quadoption (int opt)
66 int b = (opt % 4) * 2;
68 return (QuadOptions[n] >> b) & 0x3;
71 int query_quadoption (int opt, const char *prompt)
73 int v = quadoption (opt);
81 v = mutt_yesorno (prompt, (v == M_ASKYES));
82 CLEARLINE (LINES - 1);
89 /* given the variable ``s'', return the index into the rc_vars array which
90 matches, or -1 if the variable is not found. */
91 int mutt_option_index (char *s)
95 for (i = 0; MuttVars[i].option; i++)
96 if (safe_strcmp (s, MuttVars[i].option) == 0) {
97 if (MuttVars[i].type == DT_SYN)
98 list_push_back (&Synonyms, &MuttVars[i]);
99 return (MuttVars[i].type ==
100 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
105 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
108 char qc = 0; /* quote char */
111 /* reset the destination pointer to the beginning of the buffer */
112 dest->dptr = dest->data;
115 while ((ch = *tok->dptr)) {
117 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
118 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
119 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
120 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
121 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
128 qc = 0; /* end of quote */
129 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
131 else if (ch == '\\' && qc != '\'') {
133 return -1; /* premature end of token */
134 switch (ch = *tok->dptr++) {
138 return -1; /* premature end of token */
139 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
144 mutt_buffer_addch (dest, '\r');
147 mutt_buffer_addch (dest, '\n');
150 mutt_buffer_addch (dest, '\t');
153 mutt_buffer_addch (dest, '\f');
156 mutt_buffer_addch (dest, '\033');
159 if (isdigit ((unsigned char) ch) &&
160 isdigit ((unsigned char) *tok->dptr) &&
161 isdigit ((unsigned char) *(tok->dptr + 1))) {
163 mutt_buffer_addch (dest,
164 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
169 mutt_buffer_addch (dest, ch);
172 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
174 return -1; /* premature end of token */
177 mutt_buffer_addch (dest, ch);
179 mutt_buffer_addch (dest, '\033');
180 else if (isalpha ((unsigned char) ch))
181 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
183 mutt_buffer_addch (dest, '^');
184 mutt_buffer_addch (dest, ch);
187 else if (ch == '`' && (!qc || qc == '"')) {
197 if ((pc = strpbrk (pc, "\\`"))) {
198 /* skip any quoted chars */
202 } while (pc && *pc != '`');
204 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
207 cmd = str_substrdup (tok->dptr, pc);
208 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
210 (debugfile, "mutt_get_token: unable to fork command: %s",
220 memset (&expn, 0, sizeof (expn));
221 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
223 mutt_wait_filter (pid);
225 /* if we got output, make a new string consiting of the shell ouptput
226 plus whatever else was left on the original line */
227 /* BUT: If this is inside a quoted string, directly add output to
229 if (expn.data && qc) {
230 mutt_buffer_addstr (dest, expn.data);
233 else if (expn.data) {
234 expnlen = safe_strlen (expn.data);
235 tok->dsize = expnlen + safe_strlen (tok->dptr) + 1;
236 ptr = safe_malloc (tok->dsize);
237 memcpy (ptr, expn.data, expnlen);
238 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
243 tok->destroy = 1; /* mark that the caller should destroy this data */
248 else if (ch == '$' && (!qc || qc == '"')
249 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
250 char *env = NULL, *var = NULL;
252 if (*tok->dptr == '{') {
254 if ((pc = strchr (tok->dptr, '}'))) {
255 var = str_substrdup (tok->dptr, pc);
260 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
262 var = str_substrdup (tok->dptr, pc);
265 if (var && (env = getenv (var)))
266 mutt_buffer_addstr (dest, env);
270 mutt_buffer_addch (dest, ch);
272 mutt_buffer_addch (dest, 0); /* terminate the string */
277 static void add_to_list (LIST ** list, const char *str)
279 LIST *t, *last = NULL;
281 /* don't add a NULL or empty string to the list */
282 if (!str || *str == '\0')
285 /* check to make sure the item is not already on this list */
286 for (last = *list; last; last = last->next) {
287 if (ascii_strcasecmp (str, last->data) == 0) {
288 /* already on the list, so just ignore it */
296 if (!*list || last) {
297 t = (LIST *) safe_calloc (1, sizeof (LIST));
298 t->data = safe_strdup (str);
308 static int add_to_rx_list (list2_t** list, const char *s, int flags,
317 if (!(rx = rx_compile (s, flags))) {
318 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
322 i = rx_lookup ((*list), rx->pattern);
326 list_push_back (list, rx);
330 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
331 const char *templ, BUFFER * err)
333 SPAM_LIST *t = NULL, *last = NULL;
338 if (!pat || !*pat || !templ)
341 if (!(rx = rx_compile (pat, REG_ICASE))) {
342 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
346 /* check to make sure the item is not already on this list */
347 for (last = *list; last; last = last->next) {
348 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
349 /* Already on the list. Formerly we just skipped this case, but
350 * now we're supporting removals, which means we're supporting
351 * re-adds conceptually. So we probably want this to imply a
352 * removal, then do an add. We can achieve the removal by freeing
353 * the template, and leaving t pointed at the current item.
363 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
364 * update. Otherwise we want to make a new one to link at the list's end.
367 t = mutt_new_spam_list ();
375 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
376 t->template = safe_strdup (templ);
378 /* Find highest match number in template string */
380 for (p = templ; *p;) {
385 while (*p && isdigit ((int) *p))
391 t->nmatch++; /* match 0 is always the whole expr */
396 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
398 SPAM_LIST *spam, *prev;
401 /* Being first is a special case. */
403 if (spam->rx && !safe_strcmp (spam->rx->pattern, pat)) {
406 FREE(&spam->template);
412 for (spam = prev->next; spam;) {
413 if (!safe_strcmp (spam->rx->pattern, pat)) {
414 prev->next = spam->next;
416 FREE(spam->template);
429 static void remove_from_list (LIST ** l, const char *str)
431 LIST *p, *last = NULL;
433 if (safe_strcmp ("*", str) == 0)
434 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
439 if (ascii_strcasecmp (str, p->data) == 0) {
442 last->next = p->next;
455 static int remove_from_rx_list (list2_t** l, const char *str)
459 if (safe_strcmp ("*", str) == 0) {
460 list_del (l, rx_free);
464 i = rx_lookup ((*l), str);
466 rx_t* r = list_pop_idx ((*l), i);
474 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
480 memset (&token, 0, sizeof (token));
481 mutt_extract_token (tmp, s, 0);
483 /* is the item defined as a variable or a function? */
484 if (!(res = (mutt_option_index (tmp->data) != -1)))
485 for (i = 0; !res && i < MENU_MAX; i++) {
486 struct binding_t *b = km_get_table (Menus[i].value);
491 for (j = 0; b[j].name; j++)
492 if (!ascii_strncasecmp (tmp->data, b[j].name, safe_strlen (tmp->data))
493 && (safe_strlen (b[j].name) == safe_strlen (tmp->data))) {
498 /* check for feature_* */
503 j = safe_strlen (tmp->data);
504 /* need at least input of 'feature_X' */
508 while (Features[i].name) {
509 if (safe_strlen (Features[i].name) == j &&
510 ascii_strncasecmp (Features[i].name, p, j)) {
521 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
523 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
526 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
528 if ((data && res) || (!data && !res)) {
529 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
530 mutt_error ("Error: %s", err->data);
539 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
543 mutt_extract_token (buf, s, 0);
545 /* don't add "*" to the unignore list */
546 if (strcmp (buf->data, "*"))
547 add_to_list (&UnIgnore, buf->data);
549 remove_from_list (&Ignore, buf->data);
551 while (MoreArgs (s));
556 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
560 mutt_extract_token (buf, s, 0);
561 remove_from_list (&UnIgnore, buf->data);
562 add_to_list (&Ignore, buf->data);
564 while (MoreArgs (s));
569 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
573 mutt_extract_token (buf, s, 0);
574 add_to_list ((LIST **) data, buf->data);
576 while (MoreArgs (s));
581 static void _alternates_clean (void)
585 if (Context && Context->msgcount) {
586 for (i = 0; i < Context->msgcount; i++)
587 Context->hdrs[i]->recip_valid = 0;
591 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
594 _alternates_clean ();
596 mutt_extract_token (buf, s, 0);
597 remove_from_rx_list (&UnAlternates, buf->data);
599 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
602 while (MoreArgs (s));
607 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
610 _alternates_clean ();
612 mutt_extract_token (buf, s, 0);
613 remove_from_rx_list (&Alternates, buf->data);
615 if (safe_strcmp (buf->data, "*") &&
616 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
620 while (MoreArgs (s));
625 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
630 memset (&templ, 0, sizeof (templ));
632 /* Insist on at least one parameter */
635 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
637 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
641 /* Extract the first token, a regexp */
642 mutt_extract_token (buf, s, 0);
644 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
645 if (data == M_SPAM) {
646 /* If there's a second parameter, it's a template for the spam tag. */
648 mutt_extract_token (&templ, s, 0);
650 /* Add to the spam list. */
651 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
658 /* If not, try to remove from the nospam list. */
660 remove_from_rx_list (&NoSpamList, buf->data);
666 /* M_NOSPAM is for nospam commands. */
667 else if (data == M_NOSPAM) {
668 /* nospam only ever has one parameter. */
670 /* "*" is a special case. */
671 if (!safe_strcmp (buf->data, "*")) {
672 mutt_free_spam_list (&SpamList);
673 list_del (&NoSpamList, rx_free);
677 /* If it's on the spam list, just remove it. */
678 if (remove_from_spam_list (&SpamList, buf->data) != 0)
681 /* Otherwise, add it to the nospam list. */
682 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
688 /* This should not happen. */
689 strfcpy (err->data, "This is no good at all.", err->dsize);
693 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
697 mutt_extract_token (buf, s, 0);
699 * Check for deletion of entire list
701 if (safe_strcmp (buf->data, "*") == 0) {
702 mutt_free_list ((LIST **) data);
705 remove_from_list ((LIST **) data, buf->data);
707 while (MoreArgs (s));
712 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
716 mutt_extract_token (buf, s, 0);
717 remove_from_rx_list (&UnMailLists, buf->data);
719 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
722 while (MoreArgs (s));
727 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
731 mutt_extract_token (buf, s, 0);
732 remove_from_rx_list (&SubscribedLists, buf->data);
733 remove_from_rx_list (&MailLists, buf->data);
735 if (safe_strcmp (buf->data, "*") &&
736 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
739 while (MoreArgs (s));
744 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
748 mutt_extract_token (buf, s, 0);
749 remove_from_rx_list (&UnMailLists, buf->data);
750 remove_from_rx_list (&UnSubscribedLists, buf->data);
752 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
754 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
757 while (MoreArgs (s));
762 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
766 mutt_extract_token (buf, s, 0);
767 remove_from_rx_list (&SubscribedLists, buf->data);
769 if (safe_strcmp (buf->data, "*") &&
770 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
773 while (MoreArgs (s));
778 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
781 ALIAS *tmp, *last = NULL;
784 mutt_extract_token (buf, s, 0);
786 if (safe_strcmp ("*", buf->data) == 0) {
787 if (CurrentMenu == MENU_ALIAS) {
788 for (tmp = Aliases; tmp; tmp = tmp->next)
790 set_option (OPTFORCEREDRAWINDEX);
793 mutt_free_alias (&Aliases);
797 for (tmp = Aliases; tmp; tmp = tmp->next) {
798 if (safe_strcasecmp (buf->data, tmp->name) == 0) {
799 if (CurrentMenu == MENU_ALIAS) {
801 set_option (OPTFORCEREDRAWINDEX);
806 last->next = tmp->next;
810 mutt_free_alias (&tmp);
816 while (MoreArgs (s));
820 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
823 ALIAS *tmp = Aliases;
828 strfcpy (err->data, _("alias: no address"), err->dsize);
832 mutt_extract_token (buf, s, 0);
834 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
836 /* check to see if an alias with this name already exists */
837 for (; tmp; tmp = tmp->next) {
838 if (!safe_strcasecmp (tmp->name, buf->data))
844 /* create a new alias */
845 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
847 tmp->name = safe_strdup (buf->data);
848 /* give the main addressbook code a chance */
849 if (CurrentMenu == MENU_ALIAS)
850 set_option (OPTMENUCALLER);
853 /* override the previous value */
854 rfc822_free_address (&tmp->addr);
855 if (CurrentMenu == MENU_ALIAS)
856 set_option (OPTFORCEREDRAWINDEX);
859 mutt_extract_token (buf, s,
860 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
861 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
862 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
867 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
868 snprintf (err->data, err->dsize,
869 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
873 if (debuglevel >= 2) {
876 for (a = tmp->addr; a; a = a->next) {
878 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
880 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
888 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
891 LIST *tmp = UserHeader;
896 mutt_extract_token (buf, s, 0);
897 if (safe_strcmp ("*", buf->data) == 0)
898 mutt_free_list (&UserHeader);
903 l = safe_strlen (buf->data);
904 if (buf->data[l - 1] == ':')
908 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
909 && tmp->data[l] == ':') {
912 last->next = tmp->next;
914 UserHeader = tmp->next;
917 mutt_free_list (&ptr);
926 while (MoreArgs (s));
930 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
937 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
938 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
939 strfcpy (err->data, _("invalid header field"), err->dsize);
942 keylen = p - buf->data + 1;
945 for (tmp = UserHeader;; tmp = tmp->next) {
946 /* see if there is already a field by this name */
947 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
948 /* replace the old value */
950 tmp->data = buf->data;
951 memset (buf, 0, sizeof (BUFFER));
957 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,
975 if (safe_strncmp ("reverse-", s, 8) == 0) {
977 flags = SORT_REVERSE;
980 if (safe_strncmp ("last-", s, 5) == 0) {
985 if ((i = mutt_getvaluebyname (s, map)) == -1) {
986 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
995 static void mutt_set_default (struct option_t *p)
997 switch (p->type & DT_MASK) {
999 if (!p->init && *((char **) p->data))
1000 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1003 if (!p->init && *((char **) p->data)) {
1004 char *cp = safe_strdup (*((char **) p->data));
1006 /* mutt_pretty_mailbox (cp); */
1007 p->init = (unsigned long) cp;
1011 if (!p->init && *((ADDRESS **) p->data)) {
1012 char tmp[HUGE_STRING];
1015 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1016 p->init = (unsigned long) safe_strdup (tmp);
1021 rx_t* pp = (rx_t*) p->data;
1023 if (!p->init && pp->pattern)
1024 p->init = (unsigned long) safe_strdup (pp->pattern);
1030 static void mutt_restore_default (struct option_t *p)
1032 switch (p->type & DT_MASK) {
1035 str_replace ((char **) p->data, (char *) p->init);
1039 char path[_POSIX_PATH_MAX];
1041 strfcpy (path, (char *) p->init, sizeof (path));
1042 mutt_expand_path (path, sizeof (path));
1043 str_replace ((char **) p->data, path);
1048 rfc822_free_address ((ADDRESS **) p->data);
1049 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1054 set_option (p->data);
1056 unset_option (p->data);
1059 set_quadoption (p->data, p->init);
1064 *((short *) p->data) = p->init;
1068 rx_t *pp = (rx_t *) p->data;
1071 FREE (&pp->pattern);
1078 char *s = (char *) p->init;
1080 pp->rx = safe_calloc (1, sizeof (regex_t));
1081 if (safe_strcmp (p->option, "mask") != 0)
1082 flags |= mutt_which_case ((const char *) p->init);
1083 if (safe_strcmp (p->option, "mask") == 0 && *s == '!') {
1087 if (REGCOMP (pp->rx, s, flags) != 0) {
1089 _("mutt_restore_default(%s): error in regexp: %s\n"),
1090 p->option, pp->pattern);
1091 FREE (&pp->pattern);
1096 str_replace (&pp->pattern, (char *) p->init);
1102 if (p->flags & R_INDEX)
1103 set_option (OPTFORCEREDRAWINDEX);
1104 if (p->flags & R_PAGER)
1105 set_option (OPTFORCEREDRAWPAGER);
1106 if (p->flags & R_RESORT_SUB)
1107 set_option (OPTSORTSUBTHREADS);
1108 if (p->flags & R_RESORT)
1109 set_option (OPTNEEDRESORT);
1110 if (p->flags & R_RESORT_INIT)
1111 set_option (OPTRESORTINIT);
1112 if (p->flags & R_TREE)
1113 set_option (OPTREDRAWTREE);
1116 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1119 int idx, query, unset, inv, reset, r = 0;
1120 char *p, scratch[_POSIX_PATH_MAX];
1122 while (MoreArgs (s)) {
1123 /* reset state variables */
1125 unset = data & M_SET_UNSET;
1126 inv = data & M_SET_INV;
1127 reset = data & M_SET_RESET;
1129 if (*s->dptr == '?') {
1133 else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1137 else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1141 else if (*s->dptr == '&') {
1146 /* get the variable name */
1147 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1149 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1150 !(reset && !safe_strcmp ("all", tmp->data))) {
1151 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1157 if (query || unset || inv) {
1158 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1162 if (s && *s->dptr == '=') {
1163 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1167 if (!safe_strcmp ("all", tmp->data)) {
1168 for (idx = 0; MuttVars[idx].option; idx++)
1169 mutt_restore_default (&MuttVars[idx]);
1173 mutt_restore_default (&MuttVars[idx]);
1175 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1176 if (s && *s->dptr == '=') {
1177 if (unset || inv || query) {
1178 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1183 mutt_extract_token (tmp, s, 0);
1184 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1186 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1189 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1195 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1196 ? _("%s is set") : _("%s is unset"), tmp->data);
1201 unset_option (MuttVars[idx].data);
1203 toggle_option (MuttVars[idx].data);
1205 set_option (MuttVars[idx].data);
1207 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1208 DTYPE (MuttVars[idx].type) == DT_PATH ||
1209 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1211 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1212 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1214 FREE ((void *) MuttVars[idx].data);
1216 else if (query || *s->dptr != '=') {
1220 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1222 rfc822_write_address (_tmp, sizeof (_tmp),
1223 *((ADDRESS **) MuttVars[idx].data), 0);
1227 val = *((char **) MuttVars[idx].data);
1229 /* user requested the value of this variable */
1230 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1237 /* copy the value of the string */
1238 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1239 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1241 FREE ((void *) MuttVars[idx].data);
1243 mutt_extract_token (tmp, s, 0);
1244 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1245 strfcpy (scratch, tmp->data, sizeof (scratch));
1246 mutt_expand_path (scratch, sizeof (scratch));
1247 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1249 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1250 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1251 if (safe_strcmp (MuttVars[idx].option, "charset") == 0)
1252 mutt_set_charset (Charset);
1255 *((ADDRESS **) MuttVars[idx].data) =
1256 rfc822_parse_adrlist (NULL, tmp->data);
1260 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1261 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1265 if (query || *s->dptr != '=') {
1266 /* user requested the value of this variable */
1267 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1268 NONULL (ptr->pattern));
1272 if (option (OPTATTACHMSG)
1273 && !safe_strcmp (MuttVars[idx].option, "reply_regexp")) {
1274 snprintf (err->data, err->dsize,
1275 "Operation not permitted when in attach-message mode.");
1282 /* copy the value of the string */
1283 mutt_extract_token (tmp, s, 0);
1285 if (!ptr->pattern || safe_strcmp (ptr->pattern, tmp->data) != 0) {
1288 /* $mask is case-sensitive */
1289 if (safe_strcmp (MuttVars[idx].option, "mask") != 0)
1290 flags |= mutt_which_case (tmp->data);
1293 if (safe_strcmp (MuttVars[idx].option, "mask") == 0) {
1300 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1301 if ((e = REGCOMP (rx, p, flags)) != 0) {
1302 regerror (e, rx, err->data, err->dsize);
1308 /* get here only if everything went smootly */
1310 FREE (&ptr->pattern);
1311 regfree ((regex_t *) ptr->rx);
1315 ptr->pattern = safe_strdup (tmp->data);
1319 /* $reply_regexp and $alterantes require special treatment */
1321 if (Context && Context->msgcount &&
1322 safe_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1323 regmatch_t pmatch[1];
1326 #define CUR_ENV Context->hdrs[i]->env
1327 for (i = 0; i < Context->msgcount; i++) {
1328 if (CUR_ENV && CUR_ENV->subject) {
1329 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1330 CUR_ENV->subject, 1, pmatch,
1332 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1339 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1340 if (query || *s->dptr != '=') {
1341 switch (DefaultMagic) {
1358 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1364 /* copy the value of the string */
1365 mutt_extract_token (tmp, s, 0);
1366 if (mx_set_magic (tmp->data)) {
1367 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1373 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1374 short *ptr = (short *) MuttVars[idx].data;
1378 if (query || *s->dptr != '=') {
1379 /* user requested the value of this variable */
1380 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1386 mutt_extract_token (tmp, s, 0);
1387 val = strtol (tmp->data, &t, 0);
1389 if (!*tmp->data || *t || (short) val != val) {
1390 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1397 /* these ones need a sanity check */
1398 if (safe_strcmp (MuttVars[idx].option, "history") == 0) {
1401 mutt_init_history ();
1403 else if (safe_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1408 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1410 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1412 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1413 vals[quadoption (MuttVars[idx].data)]);
1417 if (*s->dptr == '=') {
1419 mutt_extract_token (tmp, s, 0);
1420 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1421 set_quadoption (MuttVars[idx].data, M_YES);
1422 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1423 set_quadoption (MuttVars[idx].data, M_NO);
1424 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1425 set_quadoption (MuttVars[idx].data, M_ASKYES);
1426 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1427 set_quadoption (MuttVars[idx].data, M_ASKNO);
1429 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1436 toggle_quadoption (MuttVars[idx].data);
1438 set_quadoption (MuttVars[idx].data, M_NO);
1440 set_quadoption (MuttVars[idx].data, M_YES);
1443 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1444 const struct mapping_t *map = NULL;
1446 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1448 map = SortAliasMethods;
1450 case DT_SORT_BROWSER:
1451 map = SortBrowserMethods;
1454 if ((WithCrypto & APPLICATION_PGP))
1455 map = SortKeyMethods;
1458 map = SortAuxMethods;
1466 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1467 MuttVars[idx].option);
1472 if (query || *s->dptr != '=') {
1474 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1477 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1478 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1480 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1485 mutt_extract_token (tmp, s, 0);
1487 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1494 snprintf (err->data, err->dsize, _("%s: unknown type"),
1495 MuttVars[idx].option);
1500 if (MuttVars[idx].flags & R_INDEX)
1501 set_option (OPTFORCEREDRAWINDEX);
1502 if (MuttVars[idx].flags & R_PAGER)
1503 set_option (OPTFORCEREDRAWPAGER);
1504 if (MuttVars[idx].flags & R_RESORT_SUB)
1505 set_option (OPTSORTSUBTHREADS);
1506 if (MuttVars[idx].flags & R_RESORT)
1507 set_option (OPTNEEDRESORT);
1508 if (MuttVars[idx].flags & R_RESORT_INIT)
1509 set_option (OPTRESORTINIT);
1510 if (MuttVars[idx].flags & R_TREE)
1511 set_option (OPTREDRAWTREE);
1518 /* reads the specified initialization file. returns -1 if errors were found
1519 so that we can pause to let the user know... */
1520 static int source_rc (const char *rcfile, BUFFER * err)
1523 int line = 0, rc = 0, conv = 0;
1525 char *linebuf = NULL;
1526 char *currentline = NULL;
1530 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1532 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1533 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1537 memset (&token, 0, sizeof (token));
1538 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1539 conv = ConfigCharset && (*ConfigCharset) && Charset;
1541 currentline = safe_strdup (linebuf);
1544 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1547 currentline = linebuf;
1549 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1550 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1551 if (--rc < -MAXERRS) {
1553 FREE (¤tline);
1562 FREE (¤tline);
1568 mutt_wait_filter (pid);
1570 /* the muttrc source keyword */
1571 snprintf (err->data, err->dsize,
1572 rc >= -MAXERRS ? _("source: errors in %s")
1573 : _("source: reading aborted due too many errors in %s"),
1582 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1585 char path[_POSIX_PATH_MAX];
1589 if (mutt_extract_token (tmp, s, 0) != 0) {
1590 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1594 strfcpy (path, tmp->data, sizeof (path));
1595 mutt_expand_path (path, sizeof (path));
1597 rc += source_rc (path, err);
1599 while (MoreArgs (s));
1601 return ((rc < 0) ? -1 : 0);
1604 /* line command to execute
1606 token scratch buffer to be used by parser. caller should free
1607 token->data when finished. the reason for this variable is
1608 to avoid having to allocate and deallocate a lot of memory
1609 if we are parsing many lines. the caller can pass in the
1610 memory to use, which avoids having to create new space for
1611 every call to this function.
1613 err where to write error messages */
1614 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1619 memset (&expn, 0, sizeof (expn));
1620 expn.data = expn.dptr = line;
1621 expn.dsize = safe_strlen (line);
1626 while (*expn.dptr) {
1627 if (*expn.dptr == '#')
1628 break; /* rest of line is a comment */
1629 if (*expn.dptr == ';') {
1633 mutt_extract_token (token, &expn, 0);
1634 for (i = 0; Commands[i].name; i++) {
1635 if (!safe_strcmp (token->data, Commands[i].name)) {
1636 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1641 if (!Commands[i].name) {
1642 snprintf (err->data, err->dsize, _("%s: unknown command"),
1643 NONULL (token->data));
1655 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1656 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1657 /* initial string that starts completion. No telling how much crap
1658 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1659 char User_typed[LONG_STRING] = { 0 };
1661 int Num_matched = 0; /* Number of matches for completion */
1662 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1663 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1665 /* helper function for completion. Changes the dest buffer if
1666 necessary/possible to aid completion.
1667 dest == completion result gets here.
1668 src == candidate for completion.
1669 try == user entered data for completion.
1670 len == length of dest buffer.
1672 static void candidate (char *dest, char *try, char *src, int len)
1676 if (strstr (src, try) == src) {
1677 Matches[Num_matched++] = src;
1679 strfcpy (dest, src, len);
1681 for (l = 0; src[l] && src[l] == dest[l]; l++);
1687 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1691 int spaces; /* keep track of the number of leading spaces on the line */
1694 spaces = buffer - pt;
1696 pt = buffer + pos - spaces;
1697 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1700 if (pt == buffer) { /* complete cmd */
1701 /* first TAB. Collect all the matches */
1704 strfcpy (User_typed, pt, sizeof (User_typed));
1705 memset (Matches, 0, sizeof (Matches));
1706 memset (Completed, 0, sizeof (Completed));
1707 for (num = 0; Commands[num].name; num++)
1708 candidate (Completed, User_typed, Commands[num].name,
1709 sizeof (Completed));
1710 Matches[Num_matched++] = User_typed;
1712 /* All matches are stored. Longest non-ambiguous string is ""
1713 * i.e. dont change 'buffer'. Fake successful return this time */
1714 if (User_typed[0] == 0)
1718 if (Completed[0] == 0 && User_typed[0])
1721 /* Num_matched will _always_ be atleast 1 since the initial
1722 * user-typed string is always stored */
1723 if (numtabs == 1 && Num_matched == 2)
1724 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1725 else if (numtabs > 1 && Num_matched > 2)
1726 /* cycle thru all the matches */
1727 snprintf (Completed, sizeof (Completed), "%s",
1728 Matches[(numtabs - 2) % Num_matched]);
1730 /* return the completed command */
1731 strncpy (buffer, Completed, len - spaces);
1733 else if (!safe_strncmp (buffer, "set", 3)
1734 || !safe_strncmp (buffer, "unset", 5)
1735 || !safe_strncmp (buffer, "reset", 5)
1736 || !safe_strncmp (buffer, "toggle", 6)) { /* complete variables */
1737 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1740 /* loop through all the possible prefixes (no, inv, ...) */
1741 if (!safe_strncmp (buffer, "set", 3)) {
1742 for (num = 0; prefixes[num]; num++) {
1743 if (!safe_strncmp (pt, prefixes[num], safe_strlen (prefixes[num]))) {
1744 pt += safe_strlen (prefixes[num]);
1750 /* first TAB. Collect all the matches */
1753 strfcpy (User_typed, pt, sizeof (User_typed));
1754 memset (Matches, 0, sizeof (Matches));
1755 memset (Completed, 0, sizeof (Completed));
1756 for (num = 0; MuttVars[num].option; num++)
1757 candidate (Completed, User_typed, MuttVars[num].option,
1758 sizeof (Completed));
1759 Matches[Num_matched++] = User_typed;
1761 /* All matches are stored. Longest non-ambiguous string is ""
1762 * i.e. dont change 'buffer'. Fake successful return this time */
1763 if (User_typed[0] == 0)
1767 if (Completed[0] == 0 && User_typed[0])
1770 /* Num_matched will _always_ be atleast 1 since the initial
1771 * user-typed string is always stored */
1772 if (numtabs == 1 && Num_matched == 2)
1773 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1774 else if (numtabs > 1 && Num_matched > 2)
1775 /* cycle thru all the matches */
1776 snprintf (Completed, sizeof (Completed), "%s",
1777 Matches[(numtabs - 2) % Num_matched]);
1779 strncpy (pt, Completed, buffer + len - pt - spaces);
1781 else if (!safe_strncmp (buffer, "exec", 4)) {
1782 struct binding_t *menu = km_get_table (CurrentMenu);
1784 if (!menu && CurrentMenu != MENU_PAGER)
1788 /* first TAB. Collect all the matches */
1791 strfcpy (User_typed, pt, sizeof (User_typed));
1792 memset (Matches, 0, sizeof (Matches));
1793 memset (Completed, 0, sizeof (Completed));
1794 for (num = 0; menu[num].name; num++)
1795 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1796 /* try the generic menu */
1797 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1799 for (num = 0; menu[num].name; num++)
1800 candidate (Completed, User_typed, menu[num].name,
1801 sizeof (Completed));
1803 Matches[Num_matched++] = User_typed;
1805 /* All matches are stored. Longest non-ambiguous string is ""
1806 * i.e. dont change 'buffer'. Fake successful return this time */
1807 if (User_typed[0] == 0)
1811 if (Completed[0] == 0 && User_typed[0])
1814 /* Num_matched will _always_ be atleast 1 since the initial
1815 * user-typed string is always stored */
1816 if (numtabs == 1 && Num_matched == 2)
1817 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1818 else if (numtabs > 1 && Num_matched > 2)
1819 /* cycle thru all the matches */
1820 snprintf (Completed, sizeof (Completed), "%s",
1821 Matches[(numtabs - 2) % Num_matched]);
1823 strncpy (pt, Completed, buffer + len - pt - spaces);
1831 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1833 char var[STRING], *pt = buffer;
1840 spaces = buffer - pt;
1842 pt = buffer + pos - spaces;
1843 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1845 pt++; /* move past the space */
1846 if (*pt == '=') /* abort if no var before the '=' */
1849 if (safe_strncmp (buffer, "set", 3) == 0) {
1852 strfcpy (var, pt, sizeof (var));
1853 /* ignore the trailing '=' when comparing */
1854 var[safe_strlen (var) - 1] = 0;
1855 if ((idx = mutt_option_index (var)) == -1)
1856 return 0; /* no such variable. */
1858 char tmp[LONG_STRING], tmp2[LONG_STRING];
1860 size_t dlen = buffer + len - pt - spaces;
1861 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1865 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1866 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1867 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1868 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1869 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1870 mutt_pretty_mailbox (tmp);
1872 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1873 rfc822_write_address (tmp, sizeof (tmp),
1874 *((ADDRESS **) MuttVars[idx].data), 0);
1876 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1877 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1878 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1879 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1880 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1881 const struct mapping_t *map;
1884 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1886 map = SortAliasMethods;
1888 case DT_SORT_BROWSER:
1889 map = SortBrowserMethods;
1892 if ((WithCrypto & APPLICATION_PGP))
1893 map = SortKeyMethods;
1902 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1904 snprintf (tmp, sizeof (tmp), "%s%s%s",
1905 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1907 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1910 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1911 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1916 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1917 if (*s == '\\' || *s == '"')
1923 strfcpy (tmp, pt, sizeof (tmp));
1924 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1932 /* Implement the -Q command line flag */
1933 int mutt_query_variables (LIST * queries)
1937 char errbuff[STRING];
1938 char command[STRING];
1942 memset (&err, 0, sizeof (err));
1943 memset (&token, 0, sizeof (token));
1946 err.dsize = sizeof (errbuff);
1948 for (p = queries; p; p = p->next) {
1949 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1950 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1951 fprintf (stderr, "%s\n", err.data);
1955 printf ("%s\n", err.data);
1962 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1966 for (i = 0; map[i].name; i++)
1967 if (map[i].value == val)
1968 return (map[i].name);
1972 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1976 for (i = 0; map[i].name; i++)
1977 if (ascii_strcasecmp (map[i].name, name) == 0)
1978 return (map[i].value);
1983 static void start_debug (void)
1987 char buf[_POSIX_PATH_MAX];
1988 char buf2[_POSIX_PATH_MAX];
1990 /* rotate the old debug logs */
1991 for (i = 3; i >= 0; i--) {
1992 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
1993 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
1997 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
1999 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2001 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2002 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2007 static int mutt_execute_commands (LIST * p)
2010 char errstr[SHORT_STRING];
2012 memset (&err, 0, sizeof (err));
2014 err.dsize = sizeof (errstr);
2015 memset (&token, 0, sizeof (token));
2016 for (; p; p = p->next) {
2017 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2018 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2027 void mutt_init (int skip_sys_rc, LIST * commands)
2030 struct utsname utsname;
2031 char *p, buffer[STRING], error[STRING];
2032 int i, default_rc = 0, need_pause = 0;
2035 memset (&err, 0, sizeof (err));
2037 err.dsize = sizeof (error);
2040 * XXX - use something even more difficult to predict?
2042 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2043 "\033]9;%ld\a", (long) time (NULL));
2045 /* on one of the systems I use, getcwd() does not return the same prefix
2046 as is listed in the passwd file */
2047 if ((p = getenv ("HOME")))
2048 Homedir = safe_strdup (p);
2050 /* Get some information about the user */
2051 if ((pw = getpwuid (getuid ()))) {
2054 Username = safe_strdup (pw->pw_name);
2056 Homedir = safe_strdup (pw->pw_dir);
2058 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2059 Shell = safe_strdup (pw->pw_shell);
2064 fputs (_("unable to determine home directory"), stderr);
2067 if ((p = getenv ("USER")))
2068 Username = safe_strdup (p);
2071 fputs (_("unable to determine username"), stderr);
2074 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2078 /* Start up debugging mode if requested */
2083 /* And about the host... */
2085 /* some systems report the FQDN instead of just the hostname */
2086 if ((p = strchr (utsname.nodename, '.'))) {
2087 Hostname = str_substrdup (utsname.nodename, p);
2089 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2092 Hostname = safe_strdup (utsname.nodename);
2095 #define DOMAIN buffer
2096 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2097 Fqdn = safe_strdup ("@");
2100 if (*DOMAIN != '@') {
2101 Fqdn = safe_malloc (safe_strlen (DOMAIN) + safe_strlen (Hostname) + 2);
2102 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2105 Fqdn = safe_strdup (NONULL (Hostname));
2112 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2114 fgets (buffer, sizeof (buffer), f);
2115 p = (char*) &buffer;
2118 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2122 NewsServer = safe_strdup (p);
2126 if ((p = getenv ("NNTPSERVER")))
2127 NewsServer = safe_strdup (p);
2130 if ((p = getenv ("MAIL")))
2131 Spoolfile = safe_strdup (p);
2132 else if ((p = getenv ("MAILDIR")))
2133 Spoolfile = safe_strdup (p);
2136 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2138 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2140 Spoolfile = safe_strdup (buffer);
2143 if ((p = getenv ("MAILCAPS")))
2144 MailcapPath = safe_strdup (p);
2146 /* Default search path from RFC1524 */
2148 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2149 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2152 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2154 p = getenv ("VISUAL");
2156 p = getenv ("EDITOR");
2160 Editor = safe_strdup (p);
2161 Visual = safe_strdup (p);
2163 if ((p = getenv ("REPLYTO")) != NULL) {
2166 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2168 memset (&buf, 0, sizeof (buf));
2169 buf.data = buf.dptr = buffer;
2170 buf.dsize = safe_strlen (buffer);
2172 memset (&token, 0, sizeof (token));
2173 parse_my_hdr (&token, &buf, 0, &err);
2177 if ((p = getenv ("EMAIL")) != NULL)
2178 From = rfc822_parse_adrlist (NULL, p);
2180 mutt_set_langinfo_charset ();
2181 mutt_set_charset (Charset);
2184 /* Set standard defaults */
2185 for (i = 0; MuttVars[i].option; i++) {
2186 mutt_set_default (&MuttVars[i]);
2187 mutt_restore_default (&MuttVars[i]);
2190 CurrentMenu = MENU_MAIN;
2193 #ifndef LOCALES_HACK
2194 /* Do we have a locale definition? */
2195 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2196 ((p = getenv ("LANG")) != NULL && p[0]) ||
2197 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2198 set_option (OPTLOCALES);
2202 /* Unset suspend by default if we're the session leader */
2203 if (getsid (0) == getpid ())
2204 unset_option (OPTSUSPEND);
2207 mutt_init_history ();
2216 * When changing the code which looks for a configuration file,
2217 * please also change the corresponding code in muttbug.sh.in.
2226 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2228 if (access (buffer, F_OK) == -1)
2229 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2230 if (access (buffer, F_OK) == -1)
2231 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2232 NONULL (Homedir), MUTT_VERSION);
2233 if (access (buffer, F_OK) == -1)
2234 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2238 Muttrc = safe_strdup (buffer);
2241 strfcpy (buffer, Muttrc, sizeof (buffer));
2243 mutt_expand_path (buffer, sizeof (buffer));
2244 Muttrc = safe_strdup (buffer);
2247 AliasFile = safe_strdup (NONULL (Muttrc));
2249 /* Process the global rc file if it exists and the user hasn't explicity
2250 requested not to via "-n". */
2252 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2254 if (access (buffer, F_OK) == -1)
2255 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2256 if (access (buffer, F_OK) == -1)
2257 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2259 if (access (buffer, F_OK) == -1)
2260 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2261 if (access (buffer, F_OK) != -1) {
2262 if (source_rc (buffer, &err) != 0) {
2263 fputs (err.data, stderr);
2264 fputc ('\n', stderr);
2270 /* Read the user's initialization file. */
2271 if (access (Muttrc, F_OK) != -1) {
2272 if (!option (OPTNOCURSES))
2274 if (source_rc (Muttrc, &err) != 0) {
2275 fputs (err.data, stderr);
2276 fputc ('\n', stderr);
2280 else if (!default_rc) {
2281 /* file specified by -F does not exist */
2282 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2283 mutt_endwin (buffer);
2287 if (mutt_execute_commands (commands) != 0)
2290 /* warn about synonym variables */
2291 if (!list_empty(Synonyms)) {
2293 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2294 for (i = 0; i < Synonyms->length; i++)
2295 fprintf (stderr, "$%s (for $%s)\n",
2296 ((struct option_t*) Synonyms->data[i])->option,
2297 (char*) ((struct option_t*) Synonyms->data[i])->data);
2298 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2299 list_del (&Synonyms, NULL);
2303 if (need_pause && !option (OPTNOCURSES)) {
2304 if (mutt_any_key_to_continue (NULL) == -1)
2309 set_option (OPTWEED); /* turn weeding on by default */
2313 int mutt_get_hook_type (const char *name)
2315 struct command_t *c;
2317 for (c = Commands; c->name; c++)
2318 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)