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"
17 #include "mutt_regex.h"
22 #include "mutt_crypt.h"
23 #include "mutt_idna.h"
25 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
39 #include <sys/utsname.h>
43 void toggle_quadoption (int opt)
46 int b = (opt % 4) * 2;
48 QuadOptions[n] ^= (1 << b);
51 void set_quadoption (int opt, int flag)
54 int b = (opt % 4) * 2;
56 QuadOptions[n] &= ~(0x3 << b);
57 QuadOptions[n] |= (flag & 0x3) << b;
60 int quadoption (int opt)
63 int b = (opt % 4) * 2;
65 return (QuadOptions[n] >> b) & 0x3;
68 int query_quadoption (int opt, const char *prompt)
70 int v = quadoption (opt);
78 v = mutt_yesorno (prompt, (v == M_ASKYES));
79 CLEARLINE (LINES - 1);
86 /* given the variable ``s'', return the index into the rc_vars array which
87 matches, or -1 if the variable is not found. */
88 int mutt_option_index (char *s)
92 for (i = 0; MuttVars[i].option; i++)
93 if (mutt_strcmp (s, MuttVars[i].option) == 0)
94 return (MuttVars[i].type ==
95 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
99 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
102 char qc = 0; /* quote char */
105 /* reset the destination pointer to the beginning of the buffer */
106 dest->dptr = dest->data;
109 while ((ch = *tok->dptr)) {
111 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
112 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
113 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
114 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
115 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
122 qc = 0; /* end of quote */
123 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
125 else if (ch == '\\' && qc != '\'') {
127 return -1; /* premature end of token */
128 switch (ch = *tok->dptr++) {
132 return -1; /* premature end of token */
133 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
138 mutt_buffer_addch (dest, '\r');
141 mutt_buffer_addch (dest, '\n');
144 mutt_buffer_addch (dest, '\t');
147 mutt_buffer_addch (dest, '\f');
150 mutt_buffer_addch (dest, '\033');
153 if (isdigit ((unsigned char) ch) &&
154 isdigit ((unsigned char) *tok->dptr) &&
155 isdigit ((unsigned char) *(tok->dptr + 1))) {
157 mutt_buffer_addch (dest,
158 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
163 mutt_buffer_addch (dest, ch);
166 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
168 return -1; /* premature end of token */
171 mutt_buffer_addch (dest, ch);
173 mutt_buffer_addch (dest, '\033');
174 else if (isalpha ((unsigned char) ch))
175 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
177 mutt_buffer_addch (dest, '^');
178 mutt_buffer_addch (dest, ch);
181 else if (ch == '`' && (!qc || qc == '"')) {
191 if ((pc = strpbrk (pc, "\\`"))) {
192 /* skip any quoted chars */
196 } while (pc && *pc != '`');
198 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
201 cmd = mutt_substrdup (tok->dptr, pc);
202 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
204 (debugfile, "mutt_get_token: unable to fork command: %s",
214 memset (&expn, 0, sizeof (expn));
215 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
217 mutt_wait_filter (pid);
219 /* if we got output, make a new string consiting of the shell ouptput
220 plus whatever else was left on the original line */
221 /* BUT: If this is inside a quoted string, directly add output to
223 if (expn.data && qc) {
224 mutt_buffer_addstr (dest, expn.data);
227 else if (expn.data) {
228 expnlen = mutt_strlen (expn.data);
229 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
230 ptr = safe_malloc (tok->dsize);
231 memcpy (ptr, expn.data, expnlen);
232 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
237 tok->destroy = 1; /* mark that the caller should destroy this data */
242 else if (ch == '$' && (!qc || qc == '"')
243 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
244 char *env = NULL, *var = NULL;
246 if (*tok->dptr == '{') {
248 if ((pc = strchr (tok->dptr, '}'))) {
249 var = mutt_substrdup (tok->dptr, pc);
254 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
256 var = mutt_substrdup (tok->dptr, pc);
259 if (var && (env = getenv (var)))
260 mutt_buffer_addstr (dest, env);
264 mutt_buffer_addch (dest, ch);
266 mutt_buffer_addch (dest, 0); /* terminate the string */
271 static void add_to_list (LIST ** list, const char *str)
273 LIST *t, *last = NULL;
275 /* don't add a NULL or empty string to the list */
276 if (!str || *str == '\0')
279 /* check to make sure the item is not already on this list */
280 for (last = *list; last; last = last->next) {
281 if (ascii_strcasecmp (str, last->data) == 0) {
282 /* already on the list, so just ignore it */
290 if (!*list || last) {
291 t = (LIST *) safe_calloc (1, sizeof (LIST));
292 t->data = safe_strdup (str);
302 static int add_to_rx_list (RX_LIST ** list, const char *s, int flags,
305 RX_LIST *t, *last = NULL;
311 if (!(rx = mutt_compile_regexp (s, flags))) {
312 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
316 /* check to make sure the item is not already on this list */
317 for (last = *list; last; last = last->next) {
318 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
319 /* already on the list, so just ignore it */
327 if (!*list || last) {
328 t = mutt_new_rx_list ();
338 mutt_free_regexp (&rx);
343 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
344 const char *templ, BUFFER * err)
346 SPAM_LIST *t = NULL, *last = NULL;
351 if (!pat || !*pat || !templ)
354 if (!(rx = mutt_compile_regexp (pat, REG_ICASE))) {
355 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
359 /* check to make sure the item is not already on this list */
360 for (last = *list; last; last = last->next) {
361 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
362 /* Already on the list. Formerly we just skipped this case, but
363 * now we're supporting removals, which means we're supporting
364 * re-adds conceptually. So we probably want this to imply a
365 * removal, then do an add. We can achieve the removal by freeing
366 * the template, and leaving t pointed at the current item.
369 safe_free (&t->template);
376 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
377 * update. Otherwise we want to make a new one to link at the list's end.
380 t = mutt_new_spam_list ();
388 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
389 t->template = safe_strdup (templ);
391 /* Find highest match number in template string */
393 for (p = templ; *p;) {
398 while (*p && isdigit ((int) *p))
404 t->nmatch++; /* match 0 is always the whole expr */
409 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
411 SPAM_LIST *spam, *prev;
414 /* Being first is a special case. */
416 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
418 mutt_free_regexp (&spam->rx);
419 safe_free (&spam->template);
425 for (spam = prev->next; spam;) {
426 if (!mutt_strcmp (spam->rx->pattern, pat)) {
427 prev->next = spam->next;
428 mutt_free_regexp (&spam->rx);
429 safe_free (&spam->template);
442 static void remove_from_list (LIST ** l, const char *str)
444 LIST *p, *last = NULL;
446 if (mutt_strcmp ("*", str) == 0)
447 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
452 if (ascii_strcasecmp (str, p->data) == 0) {
455 last->next = p->next;
468 static int remove_from_rx_list (RX_LIST ** l, const char *str)
470 RX_LIST *p, *last = NULL;
473 if (mutt_strcmp ("*", str) == 0) {
474 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
481 if (ascii_strcasecmp (str, p->rx->pattern) == 0) {
482 mutt_free_regexp (&p->rx);
484 last->next = p->next;
499 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
505 memset (&token, 0, sizeof (token));
506 mutt_extract_token (tmp, s, 0);
508 /* is the item defined as a variable or a function? */
509 if (!(res = (mutt_option_index (tmp->data) != -1)))
510 for (i = 0; !res && i < MENU_MAX; i++) {
511 struct binding_t *b = km_get_table (Menus[i].value);
516 for (j = 0; b[j].name; j++)
517 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
518 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
523 /* check for feature_* */
528 j = mutt_strlen (tmp->data);
529 /* need at least input of 'feature_X' */
533 while (Features[i].name) {
534 if (mutt_strlen (Features[i].name) == j &&
535 ascii_strncasecmp (Features[i].name, p, j)) {
546 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
548 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
551 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
553 if ((data && res) || (!data && !res)) {
554 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
555 mutt_error ("Error: %s", err->data);
564 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
568 mutt_extract_token (buf, s, 0);
570 /* don't add "*" to the unignore list */
571 if (strcmp (buf->data, "*"))
572 add_to_list (&UnIgnore, buf->data);
574 remove_from_list (&Ignore, buf->data);
576 while (MoreArgs (s));
581 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
585 mutt_extract_token (buf, s, 0);
586 remove_from_list (&UnIgnore, buf->data);
587 add_to_list (&Ignore, buf->data);
589 while (MoreArgs (s));
594 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
598 mutt_extract_token (buf, s, 0);
599 add_to_list ((LIST **) data, buf->data);
601 while (MoreArgs (s));
607 static int _parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
608 BUFFER * err, int flags)
611 mutt_extract_token (buf, s, 0);
612 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
616 while (MoreArgs (s));
621 static int parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
624 return _parse_rx_list (buf, s, data, err, REG_ICASE);
627 static int parse_rx_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
631 mutt_extract_token (buf, s, 0);
632 if (mutt_strcmp (buf->data, "*") == 0) {
633 mutt_free_rx_list ((RX_LIST **) data);
636 remove_from_rx_list ((RX_LIST **) data, buf->data);
638 while (MoreArgs (s));
644 static void _alternates_clean (void)
648 if (Context && Context->msgcount) {
649 for (i = 0; i < Context->msgcount; i++)
650 Context->hdrs[i]->recip_valid = 0;
654 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
657 _alternates_clean ();
659 mutt_extract_token (buf, s, 0);
660 remove_from_rx_list (&UnAlternates, buf->data);
662 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
665 while (MoreArgs (s));
670 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
673 _alternates_clean ();
675 mutt_extract_token (buf, s, 0);
676 remove_from_rx_list (&Alternates, buf->data);
678 if (mutt_strcmp (buf->data, "*") &&
679 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
683 while (MoreArgs (s));
688 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
693 memset (&templ, 0, sizeof (templ));
695 /* Insist on at least one parameter */
698 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
700 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
704 /* Extract the first token, a regexp */
705 mutt_extract_token (buf, s, 0);
707 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
708 if (data == M_SPAM) {
709 /* If there's a second parameter, it's a template for the spam tag. */
711 mutt_extract_token (&templ, s, 0);
713 /* Add to the spam list. */
714 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
721 /* If not, try to remove from the nospam list. */
723 remove_from_rx_list (&NoSpamList, buf->data);
729 /* M_NOSPAM is for nospam commands. */
730 else if (data == M_NOSPAM) {
731 /* nospam only ever has one parameter. */
733 /* "*" is a special case. */
734 if (!mutt_strcmp (buf->data, "*")) {
735 mutt_free_spam_list (&SpamList);
736 mutt_free_rx_list (&NoSpamList);
740 /* If it's on the spam list, just remove it. */
741 if (remove_from_spam_list (&SpamList, buf->data) != 0)
744 /* Otherwise, add it to the nospam list. */
745 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
751 /* This should not happen. */
752 strfcpy (err->data, "This is no good at all.", err->dsize);
756 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
760 mutt_extract_token (buf, s, 0);
762 * Check for deletion of entire list
764 if (mutt_strcmp (buf->data, "*") == 0) {
765 mutt_free_list ((LIST **) data);
768 remove_from_list ((LIST **) data, buf->data);
770 while (MoreArgs (s));
775 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
779 mutt_extract_token (buf, s, 0);
780 remove_from_rx_list (&UnMailLists, buf->data);
782 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
785 while (MoreArgs (s));
790 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
794 mutt_extract_token (buf, s, 0);
795 remove_from_rx_list (&SubscribedLists, buf->data);
796 remove_from_rx_list (&MailLists, buf->data);
798 if (mutt_strcmp (buf->data, "*") &&
799 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
802 while (MoreArgs (s));
807 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
811 mutt_extract_token (buf, s, 0);
812 remove_from_rx_list (&UnMailLists, buf->data);
813 remove_from_rx_list (&UnSubscribedLists, buf->data);
815 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
817 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
820 while (MoreArgs (s));
825 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
829 mutt_extract_token (buf, s, 0);
830 remove_from_rx_list (&SubscribedLists, buf->data);
832 if (mutt_strcmp (buf->data, "*") &&
833 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
836 while (MoreArgs (s));
841 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
844 ALIAS *tmp, *last = NULL;
847 mutt_extract_token (buf, s, 0);
849 if (mutt_strcmp ("*", buf->data) == 0) {
850 if (CurrentMenu == MENU_ALIAS) {
851 for (tmp = Aliases; tmp; tmp = tmp->next)
853 set_option (OPTFORCEREDRAWINDEX);
856 mutt_free_alias (&Aliases);
860 for (tmp = Aliases; tmp; tmp = tmp->next) {
861 if (mutt_strcasecmp (buf->data, tmp->name) == 0) {
862 if (CurrentMenu == MENU_ALIAS) {
864 set_option (OPTFORCEREDRAWINDEX);
869 last->next = tmp->next;
873 mutt_free_alias (&tmp);
879 while (MoreArgs (s));
883 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
886 ALIAS *tmp = Aliases;
891 strfcpy (err->data, _("alias: no address"), err->dsize);
895 mutt_extract_token (buf, s, 0);
897 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
899 /* check to see if an alias with this name already exists */
900 for (; tmp; tmp = tmp->next) {
901 if (!mutt_strcasecmp (tmp->name, buf->data))
907 /* create a new alias */
908 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
910 tmp->name = safe_strdup (buf->data);
911 /* give the main addressbook code a chance */
912 if (CurrentMenu == MENU_ALIAS)
913 set_option (OPTMENUCALLER);
916 /* override the previous value */
917 rfc822_free_address (&tmp->addr);
918 if (CurrentMenu == MENU_ALIAS)
919 set_option (OPTFORCEREDRAWINDEX);
922 mutt_extract_token (buf, s,
923 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
924 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
925 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
930 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
931 snprintf (err->data, err->dsize,
932 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
936 if (debuglevel >= 2) {
939 for (a = tmp->addr; a; a = a->next) {
941 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
943 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
951 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
954 LIST *tmp = UserHeader;
959 mutt_extract_token (buf, s, 0);
960 if (mutt_strcmp ("*", buf->data) == 0)
961 mutt_free_list (&UserHeader);
966 l = mutt_strlen (buf->data);
967 if (buf->data[l - 1] == ':')
971 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
972 && tmp->data[l] == ':') {
975 last->next = tmp->next;
977 UserHeader = tmp->next;
980 mutt_free_list (&ptr);
989 while (MoreArgs (s));
993 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1000 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1001 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1002 strfcpy (err->data, _("invalid header field"), err->dsize);
1005 keylen = p - buf->data + 1;
1008 for (tmp = UserHeader;; tmp = tmp->next) {
1009 /* see if there is already a field by this name */
1010 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1011 /* replace the old value */
1013 tmp->data = buf->data;
1014 memset (buf, 0, sizeof (BUFFER));
1020 tmp->next = mutt_new_list ();
1024 tmp = mutt_new_list ();
1027 tmp->data = buf->data;
1028 memset (buf, 0, sizeof (BUFFER));
1033 parse_sort (short *val, const char *s, const struct mapping_t *map,
1038 if (mutt_strncmp ("reverse-", s, 8) == 0) {
1040 flags = SORT_REVERSE;
1043 if (mutt_strncmp ("last-", s, 5) == 0) {
1048 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1049 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1058 static void mutt_set_default (struct option_t *p)
1060 switch (p->type & DT_MASK) {
1062 if (!p->init && *((char **) p->data))
1063 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1066 if (!p->init && *((char **) p->data)) {
1067 char *cp = safe_strdup (*((char **) p->data));
1069 /* mutt_pretty_mailbox (cp); */
1070 p->init = (unsigned long) cp;
1074 if (!p->init && *((ADDRESS **) p->data)) {
1075 char tmp[HUGE_STRING];
1078 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1079 p->init = (unsigned long) safe_strdup (tmp);
1084 REGEXP *pp = (REGEXP *) p->data;
1086 if (!p->init && pp->pattern)
1087 p->init = (unsigned long) safe_strdup (pp->pattern);
1093 static void mutt_restore_default (struct option_t *p)
1095 switch (p->type & DT_MASK) {
1098 mutt_str_replace ((char **) p->data, (char *) p->init);
1102 char path[_POSIX_PATH_MAX];
1104 strfcpy (path, (char *) p->init, sizeof (path));
1105 mutt_expand_path (path, sizeof (path));
1106 mutt_str_replace ((char **) p->data, path);
1111 rfc822_free_address ((ADDRESS **) p->data);
1112 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1117 set_option (p->data);
1119 unset_option (p->data);
1122 set_quadoption (p->data, p->init);
1127 *((short *) p->data) = p->init;
1131 REGEXP *pp = (REGEXP *) p->data;
1134 FREE (&pp->pattern);
1141 char *s = (char *) p->init;
1143 pp->rx = safe_calloc (1, sizeof (regex_t));
1144 if (mutt_strcmp (p->option, "mask") != 0)
1145 flags |= mutt_which_case ((const char *) p->init);
1146 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1150 if (REGCOMP (pp->rx, s, flags) != 0) {
1152 _("mutt_restore_default(%s): error in regexp: %s\n"),
1153 p->option, pp->pattern);
1154 FREE (&pp->pattern);
1159 mutt_str_replace (&pp->pattern, (char *) p->init);
1165 if (p->flags & R_INDEX)
1166 set_option (OPTFORCEREDRAWINDEX);
1167 if (p->flags & R_PAGER)
1168 set_option (OPTFORCEREDRAWPAGER);
1169 if (p->flags & R_RESORT_SUB)
1170 set_option (OPTSORTSUBTHREADS);
1171 if (p->flags & R_RESORT)
1172 set_option (OPTNEEDRESORT);
1173 if (p->flags & R_RESORT_INIT)
1174 set_option (OPTRESORTINIT);
1175 if (p->flags & R_TREE)
1176 set_option (OPTREDRAWTREE);
1179 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1182 int idx, query, unset, inv, reset, r = 0;
1183 char *p, scratch[_POSIX_PATH_MAX];
1185 while (MoreArgs (s)) {
1186 /* reset state variables */
1188 unset = data & M_SET_UNSET;
1189 inv = data & M_SET_INV;
1190 reset = data & M_SET_RESET;
1192 if (*s->dptr == '?') {
1196 else if (mutt_strncmp ("no", s->dptr, 2) == 0) {
1200 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) {
1204 else if (*s->dptr == '&') {
1209 /* get the variable name */
1210 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1212 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1213 !(reset && !mutt_strcmp ("all", tmp->data))) {
1214 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1220 if (query || unset || inv) {
1221 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1225 if (s && *s->dptr == '=') {
1226 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1230 if (!mutt_strcmp ("all", tmp->data)) {
1231 for (idx = 0; MuttVars[idx].option; idx++)
1232 mutt_restore_default (&MuttVars[idx]);
1236 mutt_restore_default (&MuttVars[idx]);
1238 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1239 if (s && *s->dptr == '=') {
1240 if (unset || inv || query) {
1241 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1246 mutt_extract_token (tmp, s, 0);
1247 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1249 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1252 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1258 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1259 ? _("%s is set") : _("%s is unset"), tmp->data);
1264 unset_option (MuttVars[idx].data);
1266 toggle_option (MuttVars[idx].data);
1268 set_option (MuttVars[idx].data);
1270 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1271 DTYPE (MuttVars[idx].type) == DT_PATH ||
1272 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1274 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1275 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1277 FREE ((void *) MuttVars[idx].data);
1279 else if (query || *s->dptr != '=') {
1283 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1285 rfc822_write_address (_tmp, sizeof (_tmp),
1286 *((ADDRESS **) MuttVars[idx].data), 0);
1290 val = *((char **) MuttVars[idx].data);
1292 /* user requested the value of this variable */
1293 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1300 /* copy the value of the string */
1301 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1302 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1304 FREE ((void *) MuttVars[idx].data);
1306 mutt_extract_token (tmp, s, 0);
1307 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1308 strfcpy (scratch, tmp->data, sizeof (scratch));
1309 mutt_expand_path (scratch, sizeof (scratch));
1310 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1312 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1313 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1314 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1315 mutt_set_charset (Charset);
1318 *((ADDRESS **) MuttVars[idx].data) =
1319 rfc822_parse_adrlist (NULL, tmp->data);
1323 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1324 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1328 if (query || *s->dptr != '=') {
1329 /* user requested the value of this variable */
1330 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1331 NONULL (ptr->pattern));
1335 if (option (OPTATTACHMSG)
1336 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1337 snprintf (err->data, err->dsize,
1338 "Operation not permitted when in attach-message mode.");
1345 /* copy the value of the string */
1346 mutt_extract_token (tmp, s, 0);
1348 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1351 /* $mask is case-sensitive */
1352 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1353 flags |= mutt_which_case (tmp->data);
1356 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1363 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1364 if ((e = REGCOMP (rx, p, flags)) != 0) {
1365 regerror (e, rx, err->data, err->dsize);
1371 /* get here only if everything went smootly */
1373 FREE (&ptr->pattern);
1374 regfree ((regex_t *) ptr->rx);
1378 ptr->pattern = safe_strdup (tmp->data);
1382 /* $reply_regexp and $alterantes require special treatment */
1384 if (Context && Context->msgcount &&
1385 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1386 regmatch_t pmatch[1];
1389 #define CUR_ENV Context->hdrs[i]->env
1390 for (i = 0; i < Context->msgcount; i++) {
1391 if (CUR_ENV && CUR_ENV->subject) {
1392 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1393 CUR_ENV->subject, 1, pmatch,
1395 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1402 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1403 if (query || *s->dptr != '=') {
1404 switch (DefaultMagic) {
1421 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1427 /* copy the value of the string */
1428 mutt_extract_token (tmp, s, 0);
1429 if (mx_set_magic (tmp->data)) {
1430 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1436 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1437 short *ptr = (short *) MuttVars[idx].data;
1441 if (query || *s->dptr != '=') {
1442 /* user requested the value of this variable */
1443 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1449 mutt_extract_token (tmp, s, 0);
1450 val = strtol (tmp->data, &t, 0);
1452 if (!*tmp->data || *t || (short) val != val) {
1453 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1460 /* these ones need a sanity check */
1461 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1464 mutt_init_history ();
1466 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1471 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1473 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1475 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1476 vals[quadoption (MuttVars[idx].data)]);
1480 if (*s->dptr == '=') {
1482 mutt_extract_token (tmp, s, 0);
1483 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1484 set_quadoption (MuttVars[idx].data, M_YES);
1485 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1486 set_quadoption (MuttVars[idx].data, M_NO);
1487 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1488 set_quadoption (MuttVars[idx].data, M_ASKYES);
1489 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1490 set_quadoption (MuttVars[idx].data, M_ASKNO);
1492 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1499 toggle_quadoption (MuttVars[idx].data);
1501 set_quadoption (MuttVars[idx].data, M_NO);
1503 set_quadoption (MuttVars[idx].data, M_YES);
1506 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1507 const struct mapping_t *map = NULL;
1509 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1511 map = SortAliasMethods;
1513 case DT_SORT_BROWSER:
1514 map = SortBrowserMethods;
1517 if ((WithCrypto & APPLICATION_PGP))
1518 map = SortKeyMethods;
1521 map = SortAuxMethods;
1529 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1530 MuttVars[idx].option);
1535 if (query || *s->dptr != '=') {
1537 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1540 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1541 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1543 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1548 mutt_extract_token (tmp, s, 0);
1550 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1557 snprintf (err->data, err->dsize, _("%s: unknown type"),
1558 MuttVars[idx].option);
1563 if (MuttVars[idx].flags & R_INDEX)
1564 set_option (OPTFORCEREDRAWINDEX);
1565 if (MuttVars[idx].flags & R_PAGER)
1566 set_option (OPTFORCEREDRAWPAGER);
1567 if (MuttVars[idx].flags & R_RESORT_SUB)
1568 set_option (OPTSORTSUBTHREADS);
1569 if (MuttVars[idx].flags & R_RESORT)
1570 set_option (OPTNEEDRESORT);
1571 if (MuttVars[idx].flags & R_RESORT_INIT)
1572 set_option (OPTRESORTINIT);
1573 if (MuttVars[idx].flags & R_TREE)
1574 set_option (OPTREDRAWTREE);
1581 /* reads the specified initialization file. returns -1 if errors were found
1582 so that we can pause to let the user know... */
1583 static int source_rc (const char *rcfile, BUFFER * err)
1586 int line = 0, rc = 0, conv = 0;
1588 char *linebuf = NULL;
1589 char *currentline = NULL;
1593 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1595 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1596 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1600 memset (&token, 0, sizeof (token));
1601 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1602 conv = ConfigCharset && (*ConfigCharset) && Charset;
1604 currentline = safe_strdup (linebuf);
1607 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1610 currentline = linebuf;
1612 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1613 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1614 if (--rc < -MAXERRS) {
1616 FREE (¤tline);
1625 FREE (¤tline);
1631 mutt_wait_filter (pid);
1633 /* the muttrc source keyword */
1634 snprintf (err->data, err->dsize,
1635 rc >= -MAXERRS ? _("source: errors in %s")
1636 : _("source: reading aborted due too many errors in %s"),
1645 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1648 char path[_POSIX_PATH_MAX];
1652 if (mutt_extract_token (tmp, s, 0) != 0) {
1653 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1657 strfcpy (path, tmp->data, sizeof (path));
1658 mutt_expand_path (path, sizeof (path));
1660 rc += source_rc (path, err);
1662 while (MoreArgs (s));
1664 return ((rc < 0) ? -1 : 0);
1667 /* line command to execute
1669 token scratch buffer to be used by parser. caller should free
1670 token->data when finished. the reason for this variable is
1671 to avoid having to allocate and deallocate a lot of memory
1672 if we are parsing many lines. the caller can pass in the
1673 memory to use, which avoids having to create new space for
1674 every call to this function.
1676 err where to write error messages */
1677 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1682 memset (&expn, 0, sizeof (expn));
1683 expn.data = expn.dptr = line;
1684 expn.dsize = mutt_strlen (line);
1689 while (*expn.dptr) {
1690 if (*expn.dptr == '#')
1691 break; /* rest of line is a comment */
1692 if (*expn.dptr == ';') {
1696 mutt_extract_token (token, &expn, 0);
1697 for (i = 0; Commands[i].name; i++) {
1698 if (!mutt_strcmp (token->data, Commands[i].name)) {
1699 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1704 if (!Commands[i].name) {
1705 snprintf (err->data, err->dsize, _("%s: unknown command"),
1706 NONULL (token->data));
1718 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1719 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1720 /* initial string that starts completion. No telling how much crap
1721 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1722 char User_typed[LONG_STRING] = { 0 };
1724 int Num_matched = 0; /* Number of matches for completion */
1725 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1726 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1728 /* helper function for completion. Changes the dest buffer if
1729 necessary/possible to aid completion.
1730 dest == completion result gets here.
1731 src == candidate for completion.
1732 try == user entered data for completion.
1733 len == length of dest buffer.
1735 static void candidate (char *dest, char *try, char *src, int len)
1739 if (strstr (src, try) == src) {
1740 Matches[Num_matched++] = src;
1742 strfcpy (dest, src, len);
1744 for (l = 0; src[l] && src[l] == dest[l]; l++);
1750 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1754 int spaces; /* keep track of the number of leading spaces on the line */
1757 spaces = buffer - pt;
1759 pt = buffer + pos - spaces;
1760 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1763 if (pt == buffer) { /* complete cmd */
1764 /* first TAB. Collect all the matches */
1767 strfcpy (User_typed, pt, sizeof (User_typed));
1768 memset (Matches, 0, sizeof (Matches));
1769 memset (Completed, 0, sizeof (Completed));
1770 for (num = 0; Commands[num].name; num++)
1771 candidate (Completed, User_typed, Commands[num].name,
1772 sizeof (Completed));
1773 Matches[Num_matched++] = User_typed;
1775 /* All matches are stored. Longest non-ambiguous string is ""
1776 * i.e. dont change 'buffer'. Fake successful return this time */
1777 if (User_typed[0] == 0)
1781 if (Completed[0] == 0 && User_typed[0])
1784 /* Num_matched will _always_ be atleast 1 since the initial
1785 * user-typed string is always stored */
1786 if (numtabs == 1 && Num_matched == 2)
1787 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1788 else if (numtabs > 1 && Num_matched > 2)
1789 /* cycle thru all the matches */
1790 snprintf (Completed, sizeof (Completed), "%s",
1791 Matches[(numtabs - 2) % Num_matched]);
1793 /* return the completed command */
1794 strncpy (buffer, Completed, len - spaces);
1796 else if (!mutt_strncmp (buffer, "set", 3)
1797 || !mutt_strncmp (buffer, "unset", 5)
1798 || !mutt_strncmp (buffer, "reset", 5)
1799 || !mutt_strncmp (buffer, "toggle", 6)) { /* complete variables */
1800 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1803 /* loop through all the possible prefixes (no, inv, ...) */
1804 if (!mutt_strncmp (buffer, "set", 3)) {
1805 for (num = 0; prefixes[num]; num++) {
1806 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1807 pt += mutt_strlen (prefixes[num]);
1813 /* first TAB. Collect all the matches */
1816 strfcpy (User_typed, pt, sizeof (User_typed));
1817 memset (Matches, 0, sizeof (Matches));
1818 memset (Completed, 0, sizeof (Completed));
1819 for (num = 0; MuttVars[num].option; num++)
1820 candidate (Completed, User_typed, MuttVars[num].option,
1821 sizeof (Completed));
1822 Matches[Num_matched++] = User_typed;
1824 /* All matches are stored. Longest non-ambiguous string is ""
1825 * i.e. dont change 'buffer'. Fake successful return this time */
1826 if (User_typed[0] == 0)
1830 if (Completed[0] == 0 && User_typed[0])
1833 /* Num_matched will _always_ be atleast 1 since the initial
1834 * user-typed string is always stored */
1835 if (numtabs == 1 && Num_matched == 2)
1836 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1837 else if (numtabs > 1 && Num_matched > 2)
1838 /* cycle thru all the matches */
1839 snprintf (Completed, sizeof (Completed), "%s",
1840 Matches[(numtabs - 2) % Num_matched]);
1842 strncpy (pt, Completed, buffer + len - pt - spaces);
1844 else if (!mutt_strncmp (buffer, "exec", 4)) {
1845 struct binding_t *menu = km_get_table (CurrentMenu);
1847 if (!menu && CurrentMenu != MENU_PAGER)
1851 /* first TAB. Collect all the matches */
1854 strfcpy (User_typed, pt, sizeof (User_typed));
1855 memset (Matches, 0, sizeof (Matches));
1856 memset (Completed, 0, sizeof (Completed));
1857 for (num = 0; menu[num].name; num++)
1858 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1859 /* try the generic menu */
1860 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1862 for (num = 0; menu[num].name; num++)
1863 candidate (Completed, User_typed, menu[num].name,
1864 sizeof (Completed));
1866 Matches[Num_matched++] = User_typed;
1868 /* All matches are stored. Longest non-ambiguous string is ""
1869 * i.e. dont change 'buffer'. Fake successful return this time */
1870 if (User_typed[0] == 0)
1874 if (Completed[0] == 0 && User_typed[0])
1877 /* Num_matched will _always_ be atleast 1 since the initial
1878 * user-typed string is always stored */
1879 if (numtabs == 1 && Num_matched == 2)
1880 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1881 else if (numtabs > 1 && Num_matched > 2)
1882 /* cycle thru all the matches */
1883 snprintf (Completed, sizeof (Completed), "%s",
1884 Matches[(numtabs - 2) % Num_matched]);
1886 strncpy (pt, Completed, buffer + len - pt - spaces);
1894 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1896 char var[STRING], *pt = buffer;
1903 spaces = buffer - pt;
1905 pt = buffer + pos - spaces;
1906 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1908 pt++; /* move past the space */
1909 if (*pt == '=') /* abort if no var before the '=' */
1912 if (mutt_strncmp (buffer, "set", 3) == 0) {
1915 strfcpy (var, pt, sizeof (var));
1916 /* ignore the trailing '=' when comparing */
1917 var[mutt_strlen (var) - 1] = 0;
1918 if ((idx = mutt_option_index (var)) == -1)
1919 return 0; /* no such variable. */
1921 char tmp[LONG_STRING], tmp2[LONG_STRING];
1923 size_t dlen = buffer + len - pt - spaces;
1924 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1928 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1929 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1930 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1931 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1932 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1933 mutt_pretty_mailbox (tmp);
1935 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1936 rfc822_write_address (tmp, sizeof (tmp),
1937 *((ADDRESS **) MuttVars[idx].data), 0);
1939 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1940 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1941 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1942 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1943 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1944 const struct mapping_t *map;
1947 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1949 map = SortAliasMethods;
1951 case DT_SORT_BROWSER:
1952 map = SortBrowserMethods;
1955 if ((WithCrypto & APPLICATION_PGP))
1956 map = SortKeyMethods;
1965 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1967 snprintf (tmp, sizeof (tmp), "%s%s%s",
1968 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1970 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1973 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1974 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1979 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1980 if (*s == '\\' || *s == '"')
1986 strfcpy (tmp, pt, sizeof (tmp));
1987 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1995 /* Implement the -Q command line flag */
1996 int mutt_query_variables (LIST * queries)
2000 char errbuff[STRING];
2001 char command[STRING];
2005 memset (&err, 0, sizeof (err));
2006 memset (&token, 0, sizeof (token));
2009 err.dsize = sizeof (errbuff);
2011 for (p = queries; p; p = p->next) {
2012 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2013 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2014 fprintf (stderr, "%s\n", err.data);
2018 printf ("%s\n", err.data);
2025 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2029 for (i = 0; map[i].name; i++)
2030 if (map[i].value == val)
2031 return (map[i].name);
2035 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2039 for (i = 0; map[i].name; i++)
2040 if (ascii_strcasecmp (map[i].name, name) == 0)
2041 return (map[i].value);
2046 static void start_debug (void)
2050 char buf[_POSIX_PATH_MAX];
2051 char buf2[_POSIX_PATH_MAX];
2053 /* rotate the old debug logs */
2054 for (i = 3; i >= 0; i--) {
2055 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
2056 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
2060 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
2062 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2064 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2065 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2070 static int mutt_execute_commands (LIST * p)
2073 char errstr[SHORT_STRING];
2075 memset (&err, 0, sizeof (err));
2077 err.dsize = sizeof (errstr);
2078 memset (&token, 0, sizeof (token));
2079 for (; p; p = p->next) {
2080 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2081 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2090 void mutt_init (int skip_sys_rc, LIST * commands)
2093 struct utsname utsname;
2094 char *p, buffer[STRING], error[STRING];
2095 int i, default_rc = 0, need_pause = 0;
2098 memset (&err, 0, sizeof (err));
2100 err.dsize = sizeof (error);
2103 * XXX - use something even more difficult to predict?
2105 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2106 "\033]9;%ld\a", (long) time (NULL));
2108 /* on one of the systems I use, getcwd() does not return the same prefix
2109 as is listed in the passwd file */
2110 if ((p = getenv ("HOME")))
2111 Homedir = safe_strdup (p);
2113 /* Get some information about the user */
2114 if ((pw = getpwuid (getuid ()))) {
2117 Username = safe_strdup (pw->pw_name);
2119 Homedir = safe_strdup (pw->pw_dir);
2121 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2122 Shell = safe_strdup (pw->pw_shell);
2127 fputs (_("unable to determine home directory"), stderr);
2130 if ((p = getenv ("USER")))
2131 Username = safe_strdup (p);
2134 fputs (_("unable to determine username"), stderr);
2137 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2141 /* Start up debugging mode if requested */
2146 /* And about the host... */
2148 /* some systems report the FQDN instead of just the hostname */
2149 if ((p = strchr (utsname.nodename, '.'))) {
2150 Hostname = mutt_substrdup (utsname.nodename, p);
2152 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2155 Hostname = safe_strdup (utsname.nodename);
2158 #define DOMAIN buffer
2159 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2160 Fqdn = safe_strdup ("@");
2163 if (*DOMAIN != '@') {
2164 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2165 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2168 Fqdn = safe_strdup (NONULL (Hostname));
2175 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2177 fgets (buffer, sizeof (buffer), f);
2181 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2185 NewsServer = safe_strdup (p);
2189 if ((p = getenv ("NNTPSERVER")))
2190 NewsServer = safe_strdup (p);
2193 if ((p = getenv ("MAIL")))
2194 Spoolfile = safe_strdup (p);
2195 else if ((p = getenv ("MAILDIR")))
2196 Spoolfile = safe_strdup (p);
2199 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2201 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2203 Spoolfile = safe_strdup (buffer);
2206 if ((p = getenv ("MAILCAPS")))
2207 MailcapPath = safe_strdup (p);
2209 /* Default search path from RFC1524 */
2211 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2212 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2215 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2217 p = getenv ("VISUAL");
2219 p = getenv ("EDITOR");
2223 Editor = safe_strdup (p);
2224 Visual = safe_strdup (p);
2226 if ((p = getenv ("REPLYTO")) != NULL) {
2229 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2231 memset (&buf, 0, sizeof (buf));
2232 buf.data = buf.dptr = buffer;
2233 buf.dsize = mutt_strlen (buffer);
2235 memset (&token, 0, sizeof (token));
2236 parse_my_hdr (&token, &buf, 0, &err);
2240 if ((p = getenv ("EMAIL")) != NULL)
2241 From = rfc822_parse_adrlist (NULL, p);
2243 mutt_set_langinfo_charset ();
2244 mutt_set_charset (Charset);
2247 /* Set standard defaults */
2248 for (i = 0; MuttVars[i].option; i++) {
2249 mutt_set_default (&MuttVars[i]);
2250 mutt_restore_default (&MuttVars[i]);
2253 CurrentMenu = MENU_MAIN;
2256 #ifndef LOCALES_HACK
2257 /* Do we have a locale definition? */
2258 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2259 ((p = getenv ("LANG")) != NULL && p[0]) ||
2260 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2261 set_option (OPTLOCALES);
2265 /* Unset suspend by default if we're the session leader */
2266 if (getsid (0) == getpid ())
2267 unset_option (OPTSUSPEND);
2270 mutt_init_history ();
2279 * When changing the code which looks for a configuration file,
2280 * please also change the corresponding code in muttbug.sh.in.
2289 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2291 if (access (buffer, F_OK) == -1)
2292 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2293 if (access (buffer, F_OK) == -1)
2294 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2295 NONULL (Homedir), MUTT_VERSION);
2296 if (access (buffer, F_OK) == -1)
2297 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2301 Muttrc = safe_strdup (buffer);
2304 strfcpy (buffer, Muttrc, sizeof (buffer));
2306 mutt_expand_path (buffer, sizeof (buffer));
2307 Muttrc = safe_strdup (buffer);
2310 AliasFile = safe_strdup (NONULL (Muttrc));
2312 /* Process the global rc file if it exists and the user hasn't explicity
2313 requested not to via "-n". */
2315 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2317 if (access (buffer, F_OK) == -1)
2318 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2319 if (access (buffer, F_OK) == -1)
2320 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2322 if (access (buffer, F_OK) == -1)
2323 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2324 if (access (buffer, F_OK) != -1) {
2325 if (source_rc (buffer, &err) != 0) {
2326 fputs (err.data, stderr);
2327 fputc ('\n', stderr);
2333 /* Read the user's initialization file. */
2334 if (access (Muttrc, F_OK) != -1) {
2335 if (!option (OPTNOCURSES))
2337 if (source_rc (Muttrc, &err) != 0) {
2338 fputs (err.data, stderr);
2339 fputc ('\n', stderr);
2343 else if (!default_rc) {
2344 /* file specified by -F does not exist */
2345 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2346 mutt_endwin (buffer);
2350 if (mutt_execute_commands (commands) != 0)
2353 if (need_pause && !option (OPTNOCURSES)) {
2354 if (mutt_any_key_to_continue (NULL) == -1)
2359 set_option (OPTWEED); /* turn weeding on by default */
2363 int mutt_get_hook_type (const char *name)
2365 struct command_t *c;
2367 for (c = Commands; c->name; c++)
2368 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)