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)
35 #include "lib/debug.h"
41 #include <sys/utsname.h>
45 /* for synonym warning reports: synonym found during parsing */
49 int n; /* new name (index) */
50 int o; /* old name (index) */
53 /* for synonym warning reports: list of synonyms found */
55 /* for synonym warning reports: current rc file */
56 char* CurRCFile = NULL;
57 /* for synonym warning reports: current rc line */
60 /* for synonym warning reports: adds synonym to end of list */
61 static void syn_add (int n, int o) {
62 syn_t* tmp = safe_malloc (sizeof (syn_t));
63 tmp->f = safe_strdup (CurRCFile);
67 list_push_back (&Synonyms, tmp);
70 /* for synonym warning reports: free single item (for list_del()) */
71 static void syn_del (void** p) {
72 FREE(&(*(syn_t**) p)->f);
76 void toggle_quadoption (int opt)
79 int b = (opt % 4) * 2;
81 QuadOptions[n] ^= (1 << b);
84 void set_quadoption (int opt, int flag)
87 int b = (opt % 4) * 2;
89 QuadOptions[n] &= ~(0x3 << b);
90 QuadOptions[n] |= (flag & 0x3) << b;
93 int quadoption (int opt)
96 int b = (opt % 4) * 2;
98 return (QuadOptions[n] >> b) & 0x3;
101 int query_quadoption (int opt, const char *prompt)
103 int v = quadoption (opt);
111 v = mutt_yesorno (prompt, (v == M_ASKYES));
112 CLEARLINE (LINES - 1);
119 /* given the variable ``s'', return the index into the rc_vars array which
120 matches, or -1 if the variable is not found. */
121 int mutt_option_index (char *s)
125 for (i = 0; MuttVars[i].option; i++)
126 if (safe_strcmp (s, MuttVars[i].option) == 0) {
127 if (MuttVars[i].type == DT_SYN)
128 syn_add (mutt_option_index ((char *) MuttVars[i].data), i);
129 return (MuttVars[i].type ==
130 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
135 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
138 char qc = 0; /* quote char */
141 /* reset the destination pointer to the beginning of the buffer */
142 dest->dptr = dest->data;
145 while ((ch = *tok->dptr)) {
147 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
148 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
149 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
150 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
151 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
158 qc = 0; /* end of quote */
159 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
161 else if (ch == '\\' && qc != '\'') {
163 return -1; /* premature end of token */
164 switch (ch = *tok->dptr++) {
168 return -1; /* premature end of token */
169 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
174 mutt_buffer_addch (dest, '\r');
177 mutt_buffer_addch (dest, '\n');
180 mutt_buffer_addch (dest, '\t');
183 mutt_buffer_addch (dest, '\f');
186 mutt_buffer_addch (dest, '\033');
189 if (isdigit ((unsigned char) ch) &&
190 isdigit ((unsigned char) *tok->dptr) &&
191 isdigit ((unsigned char) *(tok->dptr + 1))) {
193 mutt_buffer_addch (dest,
194 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
199 mutt_buffer_addch (dest, ch);
202 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
204 return -1; /* premature end of token */
207 mutt_buffer_addch (dest, ch);
209 mutt_buffer_addch (dest, '\033');
210 else if (isalpha ((unsigned char) ch))
211 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
213 mutt_buffer_addch (dest, '^');
214 mutt_buffer_addch (dest, ch);
217 else if (ch == '`' && (!qc || qc == '"')) {
227 if ((pc = strpbrk (pc, "\\`"))) {
228 /* skip any quoted chars */
232 } while (pc && *pc != '`');
234 debug_print (1, ("mismatched backtics\n"));
237 cmd = str_substrdup (tok->dptr, pc);
238 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
239 debug_print (1, ("unable to fork command: %s\n", cmd));
248 memset (&expn, 0, sizeof (expn));
249 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
251 mutt_wait_filter (pid);
253 /* if we got output, make a new string consiting of the shell ouptput
254 plus whatever else was left on the original line */
255 /* BUT: If this is inside a quoted string, directly add output to
257 if (expn.data && qc) {
258 mutt_buffer_addstr (dest, expn.data);
261 else if (expn.data) {
262 expnlen = safe_strlen (expn.data);
263 tok->dsize = expnlen + safe_strlen (tok->dptr) + 1;
264 ptr = safe_malloc (tok->dsize);
265 memcpy (ptr, expn.data, expnlen);
266 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
271 tok->destroy = 1; /* mark that the caller should destroy this data */
276 else if (ch == '$' && (!qc || qc == '"')
277 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
278 char *env = NULL, *var = NULL;
280 if (*tok->dptr == '{') {
282 if ((pc = strchr (tok->dptr, '}'))) {
283 var = str_substrdup (tok->dptr, pc);
288 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
290 var = str_substrdup (tok->dptr, pc);
293 if (var && (env = getenv (var)))
294 mutt_buffer_addstr (dest, env);
298 mutt_buffer_addch (dest, ch);
300 mutt_buffer_addch (dest, 0); /* terminate the string */
305 static void add_to_list (LIST ** list, const char *str)
307 LIST *t, *last = NULL;
309 /* don't add a NULL or empty string to the list */
310 if (!str || *str == '\0')
313 /* check to make sure the item is not already on this list */
314 for (last = *list; last; last = last->next) {
315 if (ascii_strcasecmp (str, last->data) == 0) {
316 /* already on the list, so just ignore it */
324 if (!*list || last) {
325 t = (LIST *) safe_calloc (1, sizeof (LIST));
326 t->data = safe_strdup (str);
336 static int add_to_rx_list (list2_t** list, const char *s, int flags,
345 if (!(rx = rx_compile (s, flags))) {
346 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
350 i = rx_lookup ((*list), rx->pattern);
354 list_push_back (list, rx);
358 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
359 const char *templ, BUFFER * err)
361 SPAM_LIST *t = NULL, *last = NULL;
366 if (!pat || !*pat || !templ)
369 if (!(rx = rx_compile (pat, REG_ICASE))) {
370 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
374 /* check to make sure the item is not already on this list */
375 for (last = *list; last; last = last->next) {
376 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
377 /* Already on the list. Formerly we just skipped this case, but
378 * now we're supporting removals, which means we're supporting
379 * re-adds conceptually. So we probably want this to imply a
380 * removal, then do an add. We can achieve the removal by freeing
381 * the template, and leaving t pointed at the current item.
391 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
392 * update. Otherwise we want to make a new one to link at the list's end.
395 t = mutt_new_spam_list ();
403 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
404 t->template = safe_strdup (templ);
406 /* Find highest match number in template string */
408 for (p = templ; *p;) {
413 while (*p && isdigit ((int) *p))
419 t->nmatch++; /* match 0 is always the whole expr */
424 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
426 SPAM_LIST *spam, *prev;
429 /* Being first is a special case. */
431 if (spam->rx && !safe_strcmp (spam->rx->pattern, pat)) {
434 FREE(&spam->template);
440 for (spam = prev->next; spam;) {
441 if (!safe_strcmp (spam->rx->pattern, pat)) {
442 prev->next = spam->next;
444 FREE(spam->template);
457 static void remove_from_list (LIST ** l, const char *str)
459 LIST *p, *last = NULL;
461 if (safe_strcmp ("*", str) == 0)
462 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
467 if (ascii_strcasecmp (str, p->data) == 0) {
470 last->next = p->next;
483 static int remove_from_rx_list (list2_t** l, const char *str)
487 if (safe_strcmp ("*", str) == 0) {
488 list_del (l, rx_free);
492 i = rx_lookup ((*l), str);
494 rx_t* r = list_pop_idx ((*l), i);
502 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
508 memset (&token, 0, sizeof (token));
509 mutt_extract_token (tmp, s, 0);
511 /* is the item defined as a variable or a function? */
512 if (!(res = (mutt_option_index (tmp->data) != -1)))
513 for (i = 0; !res && i < MENU_MAX; i++) {
514 struct binding_t *b = km_get_table (Menus[i].value);
519 for (j = 0; b[j].name; j++)
520 if (!ascii_strncasecmp (tmp->data, b[j].name, safe_strlen (tmp->data))
521 && (safe_strlen (b[j].name) == safe_strlen (tmp->data))) {
526 /* check for feature_* */
531 j = safe_strlen (tmp->data);
532 /* need at least input of 'feature_X' */
536 while (Features[i].name) {
537 if (safe_strlen (Features[i].name) == j &&
538 ascii_strncasecmp (Features[i].name, p, j)) {
549 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
551 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
554 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
556 if ((data && res) || (!data && !res)) {
557 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
558 mutt_error ("Error: %s", err->data);
567 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
571 mutt_extract_token (buf, s, 0);
573 /* don't add "*" to the unignore list */
574 if (strcmp (buf->data, "*"))
575 add_to_list (&UnIgnore, buf->data);
577 remove_from_list (&Ignore, buf->data);
579 while (MoreArgs (s));
584 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
588 mutt_extract_token (buf, s, 0);
589 remove_from_list (&UnIgnore, buf->data);
590 add_to_list (&Ignore, buf->data);
592 while (MoreArgs (s));
597 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
601 mutt_extract_token (buf, s, 0);
602 add_to_list ((LIST **) data, buf->data);
604 while (MoreArgs (s));
609 static void _alternates_clean (void)
613 if (Context && Context->msgcount) {
614 for (i = 0; i < Context->msgcount; i++)
615 Context->hdrs[i]->recip_valid = 0;
619 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
622 _alternates_clean ();
624 mutt_extract_token (buf, s, 0);
625 remove_from_rx_list (&UnAlternates, buf->data);
627 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
630 while (MoreArgs (s));
635 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
638 _alternates_clean ();
640 mutt_extract_token (buf, s, 0);
641 remove_from_rx_list (&Alternates, buf->data);
643 if (safe_strcmp (buf->data, "*") &&
644 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
648 while (MoreArgs (s));
653 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
658 memset (&templ, 0, sizeof (templ));
660 /* Insist on at least one parameter */
663 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
665 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
669 /* Extract the first token, a regexp */
670 mutt_extract_token (buf, s, 0);
672 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
673 if (data == M_SPAM) {
674 /* If there's a second parameter, it's a template for the spam tag. */
676 mutt_extract_token (&templ, s, 0);
678 /* Add to the spam list. */
679 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
686 /* If not, try to remove from the nospam list. */
688 remove_from_rx_list (&NoSpamList, buf->data);
694 /* M_NOSPAM is for nospam commands. */
695 else if (data == M_NOSPAM) {
696 /* nospam only ever has one parameter. */
698 /* "*" is a special case. */
699 if (!safe_strcmp (buf->data, "*")) {
700 mutt_free_spam_list (&SpamList);
701 list_del (&NoSpamList, rx_free);
705 /* If it's on the spam list, just remove it. */
706 if (remove_from_spam_list (&SpamList, buf->data) != 0)
709 /* Otherwise, add it to the nospam list. */
710 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
716 /* This should not happen. */
717 strfcpy (err->data, "This is no good at all.", err->dsize);
721 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
725 mutt_extract_token (buf, s, 0);
727 * Check for deletion of entire list
729 if (safe_strcmp (buf->data, "*") == 0) {
730 mutt_free_list ((LIST **) data);
733 remove_from_list ((LIST **) data, buf->data);
735 while (MoreArgs (s));
740 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
744 mutt_extract_token (buf, s, 0);
745 remove_from_rx_list (&UnMailLists, buf->data);
747 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
750 while (MoreArgs (s));
755 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
759 mutt_extract_token (buf, s, 0);
760 remove_from_rx_list (&SubscribedLists, buf->data);
761 remove_from_rx_list (&MailLists, buf->data);
763 if (safe_strcmp (buf->data, "*") &&
764 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
767 while (MoreArgs (s));
772 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
776 mutt_extract_token (buf, s, 0);
777 remove_from_rx_list (&UnMailLists, buf->data);
778 remove_from_rx_list (&UnSubscribedLists, buf->data);
780 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
782 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
785 while (MoreArgs (s));
790 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
794 mutt_extract_token (buf, s, 0);
795 remove_from_rx_list (&SubscribedLists, buf->data);
797 if (safe_strcmp (buf->data, "*") &&
798 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
801 while (MoreArgs (s));
806 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
809 ALIAS *tmp, *last = NULL;
812 mutt_extract_token (buf, s, 0);
814 if (safe_strcmp ("*", buf->data) == 0) {
815 if (CurrentMenu == MENU_ALIAS) {
816 for (tmp = Aliases; tmp; tmp = tmp->next)
818 set_option (OPTFORCEREDRAWINDEX);
821 mutt_free_alias (&Aliases);
825 for (tmp = Aliases; tmp; tmp = tmp->next) {
826 if (safe_strcasecmp (buf->data, tmp->name) == 0) {
827 if (CurrentMenu == MENU_ALIAS) {
829 set_option (OPTFORCEREDRAWINDEX);
834 last->next = tmp->next;
838 mutt_free_alias (&tmp);
844 while (MoreArgs (s));
848 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
851 ALIAS *tmp = Aliases;
856 strfcpy (err->data, _("alias: no address"), err->dsize);
860 mutt_extract_token (buf, s, 0);
862 debug_print (2, ("first token is '%s'.\n", buf->data));
864 /* check to see if an alias with this name already exists */
865 for (; tmp; tmp = tmp->next) {
866 if (!safe_strcasecmp (tmp->name, buf->data))
872 /* create a new alias */
873 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
875 tmp->name = safe_strdup (buf->data);
876 /* give the main addressbook code a chance */
877 if (CurrentMenu == MENU_ALIAS)
878 set_option (OPTMENUCALLER);
881 /* override the previous value */
882 rfc822_free_address (&tmp->addr);
883 if (CurrentMenu == MENU_ALIAS)
884 set_option (OPTFORCEREDRAWINDEX);
887 mutt_extract_token (buf, s,
888 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
889 debug_print (2, ("second token is '%s'.\n", buf->data));
890 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
895 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
896 snprintf (err->data, err->dsize,
897 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
901 if (DebugLevel >= 2) {
904 for (a = tmp->addr; a; a = a->next) {
906 debug_print (2, ("%s\n", a->mailbox));
908 debug_print (2, ("group %s\n", a->mailbox));
916 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
919 LIST *tmp = UserHeader;
924 mutt_extract_token (buf, s, 0);
925 if (safe_strcmp ("*", buf->data) == 0)
926 mutt_free_list (&UserHeader);
931 l = safe_strlen (buf->data);
932 if (buf->data[l - 1] == ':')
936 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
937 && tmp->data[l] == ':') {
940 last->next = tmp->next;
942 UserHeader = tmp->next;
945 mutt_free_list (&ptr);
954 while (MoreArgs (s));
958 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
965 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
966 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
967 strfcpy (err->data, _("invalid header field"), err->dsize);
970 keylen = p - buf->data + 1;
973 for (tmp = UserHeader;; tmp = tmp->next) {
974 /* see if there is already a field by this name */
975 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
976 /* replace the old value */
978 tmp->data = buf->data;
979 memset (buf, 0, sizeof (BUFFER));
985 tmp->next = mutt_new_list ();
989 tmp = mutt_new_list ();
992 tmp->data = buf->data;
993 memset (buf, 0, sizeof (BUFFER));
998 parse_sort (short *val, const char *s, const struct mapping_t *map,
1003 if (safe_strncmp ("reverse-", s, 8) == 0) {
1005 flags = SORT_REVERSE;
1008 if (safe_strncmp ("last-", s, 5) == 0) {
1013 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1014 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1023 static void mutt_set_default (struct option_t *p)
1025 switch (p->type & DT_MASK) {
1027 if (!p->init && *((char **) p->data))
1028 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1031 if (!p->init && *((char **) p->data)) {
1032 char *cp = safe_strdup (*((char **) p->data));
1034 /* mutt_pretty_mailbox (cp); */
1035 p->init = (unsigned long) cp;
1039 if (!p->init && *((ADDRESS **) p->data)) {
1040 char tmp[HUGE_STRING];
1043 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1044 p->init = (unsigned long) safe_strdup (tmp);
1049 rx_t* pp = (rx_t*) p->data;
1051 if (!p->init && pp->pattern)
1052 p->init = (unsigned long) safe_strdup (pp->pattern);
1058 static void mutt_restore_default (struct option_t *p)
1060 switch (p->type & DT_MASK) {
1063 str_replace ((char **) p->data, (char *) p->init);
1067 char path[_POSIX_PATH_MAX];
1069 strfcpy (path, (char *) p->init, sizeof (path));
1070 mutt_expand_path (path, sizeof (path));
1071 str_replace ((char **) p->data, path);
1076 rfc822_free_address ((ADDRESS **) p->data);
1077 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1082 set_option (p->data);
1084 unset_option (p->data);
1087 set_quadoption (p->data, p->init);
1092 *((short *) p->data) = p->init;
1096 rx_t *pp = (rx_t *) p->data;
1099 FREE (&pp->pattern);
1106 char *s = (char *) p->init;
1108 pp->rx = safe_calloc (1, sizeof (regex_t));
1109 if (safe_strcmp (p->option, "mask") != 0)
1110 flags |= mutt_which_case ((const char *) p->init);
1111 if (safe_strcmp (p->option, "mask") == 0 && *s == '!') {
1115 if (REGCOMP (pp->rx, s, flags) != 0) {
1117 _("mutt_restore_default(%s): error in regexp: %s\n"),
1118 p->option, pp->pattern);
1119 FREE (&pp->pattern);
1124 str_replace (&pp->pattern, (char *) p->init);
1130 if (p->flags & R_INDEX)
1131 set_option (OPTFORCEREDRAWINDEX);
1132 if (p->flags & R_PAGER)
1133 set_option (OPTFORCEREDRAWPAGER);
1134 if (p->flags & R_RESORT_SUB)
1135 set_option (OPTSORTSUBTHREADS);
1136 if (p->flags & R_RESORT)
1137 set_option (OPTNEEDRESORT);
1138 if (p->flags & R_RESORT_INIT)
1139 set_option (OPTRESORTINIT);
1140 if (p->flags & R_TREE)
1141 set_option (OPTREDRAWTREE);
1144 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1147 int idx, query, unset, inv, reset, r = 0;
1148 char *p, scratch[_POSIX_PATH_MAX];
1150 while (MoreArgs (s)) {
1151 /* reset state variables */
1153 unset = data & M_SET_UNSET;
1154 inv = data & M_SET_INV;
1155 reset = data & M_SET_RESET;
1157 if (*s->dptr == '?') {
1161 else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1165 else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1169 else if (*s->dptr == '&') {
1174 /* get the variable name */
1175 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1177 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1178 !(reset && !safe_strcmp ("all", tmp->data))) {
1179 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1185 if (query || unset || inv) {
1186 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1190 if (s && *s->dptr == '=') {
1191 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1195 if (!safe_strcmp ("all", tmp->data)) {
1196 for (idx = 0; MuttVars[idx].option; idx++)
1197 mutt_restore_default (&MuttVars[idx]);
1201 mutt_restore_default (&MuttVars[idx]);
1203 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1204 if (s && *s->dptr == '=') {
1205 if (unset || inv || query) {
1206 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1211 mutt_extract_token (tmp, s, 0);
1212 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1214 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1217 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1223 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1224 ? _("%s is set") : _("%s is unset"), tmp->data);
1229 unset_option (MuttVars[idx].data);
1231 toggle_option (MuttVars[idx].data);
1233 set_option (MuttVars[idx].data);
1235 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1236 DTYPE (MuttVars[idx].type) == DT_PATH ||
1237 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1239 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1240 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1242 FREE ((void *) MuttVars[idx].data);
1244 else if (query || *s->dptr != '=') {
1248 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1250 rfc822_write_address (_tmp, sizeof (_tmp),
1251 *((ADDRESS **) MuttVars[idx].data), 0);
1255 val = *((char **) MuttVars[idx].data);
1257 /* user requested the value of this variable */
1258 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1265 /* copy the value of the string */
1266 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1267 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1269 FREE ((void *) MuttVars[idx].data);
1271 mutt_extract_token (tmp, s, 0);
1272 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1273 strfcpy (scratch, tmp->data, sizeof (scratch));
1274 mutt_expand_path (scratch, sizeof (scratch));
1275 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1277 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1278 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1279 if (safe_strcmp (MuttVars[idx].option, "charset") == 0)
1280 mutt_set_charset (Charset);
1283 *((ADDRESS **) MuttVars[idx].data) =
1284 rfc822_parse_adrlist (NULL, tmp->data);
1288 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1289 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1293 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)
1301 && !safe_strcmp (MuttVars[idx].option, "reply_regexp")) {
1302 snprintf (err->data, err->dsize,
1303 "Operation not permitted when in attach-message mode.");
1310 /* copy the value of the string */
1311 mutt_extract_token (tmp, s, 0);
1313 if (!ptr->pattern || safe_strcmp (ptr->pattern, tmp->data) != 0) {
1316 /* $mask is case-sensitive */
1317 if (safe_strcmp (MuttVars[idx].option, "mask") != 0)
1318 flags |= mutt_which_case (tmp->data);
1321 if (safe_strcmp (MuttVars[idx].option, "mask") == 0) {
1328 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1329 if ((e = REGCOMP (rx, p, flags)) != 0) {
1330 regerror (e, rx, err->data, err->dsize);
1336 /* get here only if everything went smootly */
1338 FREE (&ptr->pattern);
1339 regfree ((regex_t *) ptr->rx);
1343 ptr->pattern = safe_strdup (tmp->data);
1347 /* $reply_regexp and $alterantes require special treatment */
1349 if (Context && Context->msgcount &&
1350 safe_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1351 regmatch_t pmatch[1];
1354 #define CUR_ENV Context->hdrs[i]->env
1355 for (i = 0; i < Context->msgcount; i++) {
1356 if (CUR_ENV && CUR_ENV->subject) {
1357 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1358 CUR_ENV->subject, 1, pmatch,
1360 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1367 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1368 if (query || *s->dptr != '=') {
1369 switch (DefaultMagic) {
1386 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1392 /* copy the value of the string */
1393 mutt_extract_token (tmp, s, 0);
1394 if (mx_set_magic (tmp->data)) {
1395 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1401 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1402 short *ptr = (short *) MuttVars[idx].data;
1406 if (query || *s->dptr != '=') {
1407 /* user requested the value of this variable */
1408 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1414 mutt_extract_token (tmp, s, 0);
1415 val = strtol (tmp->data, &t, 0);
1417 if (!*tmp->data || *t || (short) val != val) {
1418 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1425 /* these ones need a sanity check */
1426 if (safe_strcmp (MuttVars[idx].option, "history") == 0) {
1429 mutt_init_history ();
1431 else if (safe_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1436 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1438 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1440 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1441 vals[quadoption (MuttVars[idx].data)]);
1445 if (*s->dptr == '=') {
1447 mutt_extract_token (tmp, s, 0);
1448 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1449 set_quadoption (MuttVars[idx].data, M_YES);
1450 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1451 set_quadoption (MuttVars[idx].data, M_NO);
1452 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1453 set_quadoption (MuttVars[idx].data, M_ASKYES);
1454 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1455 set_quadoption (MuttVars[idx].data, M_ASKNO);
1457 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1464 toggle_quadoption (MuttVars[idx].data);
1466 set_quadoption (MuttVars[idx].data, M_NO);
1468 set_quadoption (MuttVars[idx].data, M_YES);
1471 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1472 const struct mapping_t *map = NULL;
1474 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1476 map = SortAliasMethods;
1478 case DT_SORT_BROWSER:
1479 map = SortBrowserMethods;
1482 if ((WithCrypto & APPLICATION_PGP))
1483 map = SortKeyMethods;
1486 map = SortAuxMethods;
1494 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1495 MuttVars[idx].option);
1500 if (query || *s->dptr != '=') {
1502 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1505 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1506 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1508 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1513 mutt_extract_token (tmp, s, 0);
1515 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1522 snprintf (err->data, err->dsize, _("%s: unknown type"),
1523 MuttVars[idx].option);
1528 if (MuttVars[idx].flags & R_INDEX)
1529 set_option (OPTFORCEREDRAWINDEX);
1530 if (MuttVars[idx].flags & R_PAGER)
1531 set_option (OPTFORCEREDRAWPAGER);
1532 if (MuttVars[idx].flags & R_RESORT_SUB)
1533 set_option (OPTSORTSUBTHREADS);
1534 if (MuttVars[idx].flags & R_RESORT)
1535 set_option (OPTNEEDRESORT);
1536 if (MuttVars[idx].flags & R_RESORT_INIT)
1537 set_option (OPTRESORTINIT);
1538 if (MuttVars[idx].flags & R_TREE)
1539 set_option (OPTREDRAWTREE);
1546 /* reads the specified initialization file. returns -1 if errors were found
1547 so that we can pause to let the user know... */
1548 static int source_rc (const char *rcfile, BUFFER * err)
1551 int line = 0, rc = 0, conv = 0;
1553 char *linebuf = NULL;
1554 char *currentline = NULL;
1558 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1559 str_replace (&CurRCFile, rcfile);
1562 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1563 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1567 memset (&token, 0, sizeof (token));
1568 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1570 conv = ConfigCharset && (*ConfigCharset) && Charset;
1572 currentline = safe_strdup (linebuf);
1575 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1578 currentline = linebuf;
1580 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1581 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1582 if (--rc < -MAXERRS) {
1584 FREE (¤tline);
1593 FREE (¤tline);
1599 mutt_wait_filter (pid);
1601 /* the muttrc source keyword */
1602 snprintf (err->data, err->dsize,
1603 rc >= -MAXERRS ? _("source: errors in %s")
1604 : _("source: reading aborted due too many errors in %s"),
1613 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1616 char path[_POSIX_PATH_MAX];
1620 if (mutt_extract_token (tmp, s, 0) != 0) {
1621 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1625 strfcpy (path, tmp->data, sizeof (path));
1626 mutt_expand_path (path, sizeof (path));
1628 rc += source_rc (path, err);
1630 while (MoreArgs (s));
1632 return ((rc < 0) ? -1 : 0);
1635 /* line command to execute
1637 token scratch buffer to be used by parser. caller should free
1638 token->data when finished. the reason for this variable is
1639 to avoid having to allocate and deallocate a lot of memory
1640 if we are parsing many lines. the caller can pass in the
1641 memory to use, which avoids having to create new space for
1642 every call to this function.
1644 err where to write error messages */
1645 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1650 memset (&expn, 0, sizeof (expn));
1651 expn.data = expn.dptr = line;
1652 expn.dsize = safe_strlen (line);
1657 while (*expn.dptr) {
1658 if (*expn.dptr == '#')
1659 break; /* rest of line is a comment */
1660 if (*expn.dptr == ';') {
1664 mutt_extract_token (token, &expn, 0);
1665 for (i = 0; Commands[i].name; i++) {
1666 if (!safe_strcmp (token->data, Commands[i].name)) {
1667 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1672 if (!Commands[i].name) {
1673 snprintf (err->data, err->dsize, _("%s: unknown command"),
1674 NONULL (token->data));
1686 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1687 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1688 /* initial string that starts completion. No telling how much crap
1689 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1690 char User_typed[LONG_STRING] = { 0 };
1692 int Num_matched = 0; /* Number of matches for completion */
1693 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1694 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1696 /* helper function for completion. Changes the dest buffer if
1697 necessary/possible to aid completion.
1698 dest == completion result gets here.
1699 src == candidate for completion.
1700 try == user entered data for completion.
1701 len == length of dest buffer.
1703 static void candidate (char *dest, char *try, char *src, int len)
1707 if (strstr (src, try) == src) {
1708 Matches[Num_matched++] = src;
1710 strfcpy (dest, src, len);
1712 for (l = 0; src[l] && src[l] == dest[l]; l++);
1718 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1722 int spaces; /* keep track of the number of leading spaces on the line */
1725 spaces = buffer - pt;
1727 pt = buffer + pos - spaces;
1728 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1731 if (pt == buffer) { /* complete cmd */
1732 /* first TAB. Collect all the matches */
1735 strfcpy (User_typed, pt, sizeof (User_typed));
1736 memset (Matches, 0, sizeof (Matches));
1737 memset (Completed, 0, sizeof (Completed));
1738 for (num = 0; Commands[num].name; num++)
1739 candidate (Completed, User_typed, Commands[num].name,
1740 sizeof (Completed));
1741 Matches[Num_matched++] = User_typed;
1743 /* All matches are stored. Longest non-ambiguous string is ""
1744 * i.e. dont change 'buffer'. Fake successful return this time */
1745 if (User_typed[0] == 0)
1749 if (Completed[0] == 0 && User_typed[0])
1752 /* Num_matched will _always_ be atleast 1 since the initial
1753 * user-typed string is always stored */
1754 if (numtabs == 1 && Num_matched == 2)
1755 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1756 else if (numtabs > 1 && Num_matched > 2)
1757 /* cycle thru all the matches */
1758 snprintf (Completed, sizeof (Completed), "%s",
1759 Matches[(numtabs - 2) % Num_matched]);
1761 /* return the completed command */
1762 strncpy (buffer, Completed, len - spaces);
1764 else if (!safe_strncmp (buffer, "set", 3)
1765 || !safe_strncmp (buffer, "unset", 5)
1766 || !safe_strncmp (buffer, "reset", 5)
1767 || !safe_strncmp (buffer, "toggle", 6)) { /* complete variables */
1768 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1771 /* loop through all the possible prefixes (no, inv, ...) */
1772 if (!safe_strncmp (buffer, "set", 3)) {
1773 for (num = 0; prefixes[num]; num++) {
1774 if (!safe_strncmp (pt, prefixes[num], safe_strlen (prefixes[num]))) {
1775 pt += safe_strlen (prefixes[num]);
1781 /* first TAB. Collect all the matches */
1784 strfcpy (User_typed, pt, sizeof (User_typed));
1785 memset (Matches, 0, sizeof (Matches));
1786 memset (Completed, 0, sizeof (Completed));
1787 for (num = 0; MuttVars[num].option; num++)
1788 candidate (Completed, User_typed, MuttVars[num].option,
1789 sizeof (Completed));
1790 Matches[Num_matched++] = User_typed;
1792 /* All matches are stored. Longest non-ambiguous string is ""
1793 * i.e. dont change 'buffer'. Fake successful return this time */
1794 if (User_typed[0] == 0)
1798 if (Completed[0] == 0 && User_typed[0])
1801 /* Num_matched will _always_ be atleast 1 since the initial
1802 * user-typed string is always stored */
1803 if (numtabs == 1 && Num_matched == 2)
1804 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1805 else if (numtabs > 1 && Num_matched > 2)
1806 /* cycle thru all the matches */
1807 snprintf (Completed, sizeof (Completed), "%s",
1808 Matches[(numtabs - 2) % Num_matched]);
1810 strncpy (pt, Completed, buffer + len - pt - spaces);
1812 else if (!safe_strncmp (buffer, "exec", 4)) {
1813 struct binding_t *menu = km_get_table (CurrentMenu);
1815 if (!menu && CurrentMenu != MENU_PAGER)
1819 /* first TAB. Collect all the matches */
1822 strfcpy (User_typed, pt, sizeof (User_typed));
1823 memset (Matches, 0, sizeof (Matches));
1824 memset (Completed, 0, sizeof (Completed));
1825 for (num = 0; menu[num].name; num++)
1826 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1827 /* try the generic menu */
1828 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1830 for (num = 0; menu[num].name; num++)
1831 candidate (Completed, User_typed, menu[num].name,
1832 sizeof (Completed));
1834 Matches[Num_matched++] = User_typed;
1836 /* All matches are stored. Longest non-ambiguous string is ""
1837 * i.e. dont change 'buffer'. Fake successful return this time */
1838 if (User_typed[0] == 0)
1842 if (Completed[0] == 0 && User_typed[0])
1845 /* Num_matched will _always_ be atleast 1 since the initial
1846 * user-typed string is always stored */
1847 if (numtabs == 1 && Num_matched == 2)
1848 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1849 else if (numtabs > 1 && Num_matched > 2)
1850 /* cycle thru all the matches */
1851 snprintf (Completed, sizeof (Completed), "%s",
1852 Matches[(numtabs - 2) % Num_matched]);
1854 strncpy (pt, Completed, buffer + len - pt - spaces);
1862 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1864 char var[STRING], *pt = buffer;
1871 spaces = buffer - pt;
1873 pt = buffer + pos - spaces;
1874 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1876 pt++; /* move past the space */
1877 if (*pt == '=') /* abort if no var before the '=' */
1880 if (safe_strncmp (buffer, "set", 3) == 0) {
1883 strfcpy (var, pt, sizeof (var));
1884 /* ignore the trailing '=' when comparing */
1885 var[safe_strlen (var) - 1] = 0;
1886 if ((idx = mutt_option_index (var)) == -1)
1887 return 0; /* no such variable. */
1889 char tmp[LONG_STRING], tmp2[LONG_STRING];
1891 size_t dlen = buffer + len - pt - spaces;
1892 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1896 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1897 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1898 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1899 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1900 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1901 mutt_pretty_mailbox (tmp);
1903 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1904 rfc822_write_address (tmp, sizeof (tmp),
1905 *((ADDRESS **) MuttVars[idx].data), 0);
1907 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1908 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1909 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1910 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1911 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1912 const struct mapping_t *map;
1915 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1917 map = SortAliasMethods;
1919 case DT_SORT_BROWSER:
1920 map = SortBrowserMethods;
1923 if ((WithCrypto & APPLICATION_PGP))
1924 map = SortKeyMethods;
1933 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1935 snprintf (tmp, sizeof (tmp), "%s%s%s",
1936 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1938 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1941 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1942 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1947 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1948 if (*s == '\\' || *s == '"')
1954 strfcpy (tmp, pt, sizeof (tmp));
1955 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1963 /* Implement the -Q command line flag */
1964 int mutt_query_variables (LIST * queries)
1968 char errbuff[STRING];
1969 char command[STRING];
1973 memset (&err, 0, sizeof (err));
1974 memset (&token, 0, sizeof (token));
1977 err.dsize = sizeof (errbuff);
1979 for (p = queries; p; p = p->next) {
1980 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1981 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1982 fprintf (stderr, "%s\n", err.data);
1986 printf ("%s\n", err.data);
1993 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1997 for (i = 0; map[i].name; i++)
1998 if (map[i].value == val)
1999 return (map[i].name);
2003 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2007 for (i = 0; map[i].name; i++)
2008 if (ascii_strcasecmp (map[i].name, name) == 0)
2009 return (map[i].value);
2013 static int mutt_execute_commands (LIST * p)
2016 char errstr[SHORT_STRING];
2018 memset (&err, 0, sizeof (err));
2020 err.dsize = sizeof (errstr);
2021 memset (&token, 0, sizeof (token));
2022 for (; p; p = p->next) {
2023 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2024 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2033 void mutt_init (int skip_sys_rc, LIST * commands)
2036 struct utsname utsname;
2037 char *p, buffer[STRING], error[STRING];
2038 int i, default_rc = 0, need_pause = 0;
2041 memset (&err, 0, sizeof (err));
2043 err.dsize = sizeof (error);
2046 * XXX - use something even more difficult to predict?
2048 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2049 "\033]9;%ld\a", (long) time (NULL));
2051 /* on one of the systems I use, getcwd() does not return the same prefix
2052 as is listed in the passwd file */
2053 if ((p = getenv ("HOME")))
2054 Homedir = safe_strdup (p);
2056 /* Get some information about the user */
2057 if ((pw = getpwuid (getuid ()))) {
2060 Username = safe_strdup (pw->pw_name);
2062 Homedir = safe_strdup (pw->pw_dir);
2064 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2065 Shell = safe_strdup (pw->pw_shell);
2070 fputs (_("unable to determine home directory"), stderr);
2073 if ((p = getenv ("USER")))
2074 Username = safe_strdup (p);
2077 fputs (_("unable to determine username"), stderr);
2080 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2083 debug_start(Homedir);
2085 /* And about the host... */
2087 /* some systems report the FQDN instead of just the hostname */
2088 if ((p = strchr (utsname.nodename, '.'))) {
2089 Hostname = str_substrdup (utsname.nodename, p);
2091 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2094 Hostname = safe_strdup (utsname.nodename);
2097 #define DOMAIN buffer
2098 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2099 Fqdn = safe_strdup ("@");
2102 if (*DOMAIN != '@') {
2103 Fqdn = safe_malloc (safe_strlen (DOMAIN) + safe_strlen (Hostname) + 2);
2104 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2107 Fqdn = safe_strdup (NONULL (Hostname));
2114 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2116 fgets (buffer, sizeof (buffer), f);
2117 p = (char*) &buffer;
2120 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2124 NewsServer = safe_strdup (p);
2128 if ((p = getenv ("NNTPSERVER")))
2129 NewsServer = safe_strdup (p);
2132 if ((p = getenv ("MAIL")))
2133 Spoolfile = safe_strdup (p);
2134 else if ((p = getenv ("MAILDIR")))
2135 Spoolfile = safe_strdup (p);
2138 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2140 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2142 Spoolfile = safe_strdup (buffer);
2145 if ((p = getenv ("MAILCAPS")))
2146 MailcapPath = safe_strdup (p);
2148 /* Default search path from RFC1524 */
2150 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2151 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2154 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2156 p = getenv ("VISUAL");
2158 p = getenv ("EDITOR");
2162 Editor = safe_strdup (p);
2163 Visual = safe_strdup (p);
2165 if ((p = getenv ("REPLYTO")) != NULL) {
2168 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2170 memset (&buf, 0, sizeof (buf));
2171 buf.data = buf.dptr = buffer;
2172 buf.dsize = safe_strlen (buffer);
2174 memset (&token, 0, sizeof (token));
2175 parse_my_hdr (&token, &buf, 0, &err);
2179 if ((p = getenv ("EMAIL")) != NULL)
2180 From = rfc822_parse_adrlist (NULL, p);
2182 mutt_set_langinfo_charset ();
2183 mutt_set_charset (Charset);
2186 /* Set standard defaults */
2187 for (i = 0; MuttVars[i].option; i++) {
2188 mutt_set_default (&MuttVars[i]);
2189 mutt_restore_default (&MuttVars[i]);
2192 CurrentMenu = MENU_MAIN;
2195 #ifndef LOCALES_HACK
2196 /* Do we have a locale definition? */
2197 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2198 ((p = getenv ("LANG")) != NULL && p[0]) ||
2199 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2200 set_option (OPTLOCALES);
2204 /* Unset suspend by default if we're the session leader */
2205 if (getsid (0) == getpid ())
2206 unset_option (OPTSUSPEND);
2209 mutt_init_history ();
2218 * When changing the code which looks for a configuration file,
2219 * please also change the corresponding code in muttbug.sh.in.
2228 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2230 if (access (buffer, F_OK) == -1)
2231 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2232 if (access (buffer, F_OK) == -1)
2233 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2234 NONULL (Homedir), MUTT_VERSION);
2235 if (access (buffer, F_OK) == -1)
2236 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2240 Muttrc = safe_strdup (buffer);
2243 strfcpy (buffer, Muttrc, sizeof (buffer));
2245 mutt_expand_path (buffer, sizeof (buffer));
2246 Muttrc = safe_strdup (buffer);
2249 AliasFile = safe_strdup (NONULL (Muttrc));
2251 /* Process the global rc file if it exists and the user hasn't explicity
2252 requested not to via "-n". */
2254 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2256 if (access (buffer, F_OK) == -1)
2257 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2258 if (access (buffer, F_OK) == -1)
2259 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2261 if (access (buffer, F_OK) == -1)
2262 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2263 if (access (buffer, F_OK) != -1) {
2264 if (source_rc (buffer, &err) != 0) {
2265 fputs (err.data, stderr);
2266 fputc ('\n', stderr);
2272 /* Read the user's initialization file. */
2273 if (access (Muttrc, F_OK) != -1) {
2274 if (!option (OPTNOCURSES))
2276 if (source_rc (Muttrc, &err) != 0) {
2277 fputs (err.data, stderr);
2278 fputc ('\n', stderr);
2282 else if (!default_rc) {
2283 /* file specified by -F does not exist */
2284 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2285 mutt_endwin (buffer);
2289 if (mutt_execute_commands (commands) != 0)
2292 /* warn about synonym variables */
2293 if (!list_empty(Synonyms)) {
2295 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2296 for (i = 0; i < Synonyms->length; i++)
2297 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2298 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2299 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2300 NONULL(((syn_t*) Synonyms->data[i])->f),
2301 ((syn_t*) Synonyms->data[i])->l);
2302 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2303 list_del (&Synonyms, syn_del);
2306 /* this is not needed during runtime */
2309 if (need_pause && !option (OPTNOCURSES)) {
2310 if (mutt_any_key_to_continue (NULL) == -1)
2315 set_option (OPTWEED); /* turn weeding on by default */
2319 int mutt_get_hook_type (const char *name)
2321 struct command_t *c;
2323 for (c = Commands; c->name; c++)
2324 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)