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)
41 #include <sys/utsname.h>
45 void toggle_quadoption (int opt)
48 int b = (opt % 4) * 2;
50 QuadOptions[n] ^= (1 << b);
53 void set_quadoption (int opt, int flag)
56 int b = (opt % 4) * 2;
58 QuadOptions[n] &= ~(0x3 << b);
59 QuadOptions[n] |= (flag & 0x3) << b;
62 int quadoption (int opt)
65 int b = (opt % 4) * 2;
67 return (QuadOptions[n] >> b) & 0x3;
70 int query_quadoption (int opt, const char *prompt)
72 int v = quadoption (opt);
80 v = mutt_yesorno (prompt, (v == M_ASKYES));
81 CLEARLINE (LINES - 1);
88 /* given the variable ``s'', return the index into the rc_vars array which
89 matches, or -1 if the variable is not found. */
90 int mutt_option_index (char *s)
94 for (i = 0; MuttVars[i].option; i++)
95 if (mutt_strcmp (s, MuttVars[i].option) == 0)
96 return (MuttVars[i].type ==
97 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
101 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
104 char qc = 0; /* quote char */
107 /* reset the destination pointer to the beginning of the buffer */
108 dest->dptr = dest->data;
111 while ((ch = *tok->dptr)) {
113 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
114 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
115 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
116 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
117 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
124 qc = 0; /* end of quote */
125 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
127 else if (ch == '\\' && qc != '\'') {
129 return -1; /* premature end of token */
130 switch (ch = *tok->dptr++) {
134 return -1; /* premature end of token */
135 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
140 mutt_buffer_addch (dest, '\r');
143 mutt_buffer_addch (dest, '\n');
146 mutt_buffer_addch (dest, '\t');
149 mutt_buffer_addch (dest, '\f');
152 mutt_buffer_addch (dest, '\033');
155 if (isdigit ((unsigned char) ch) &&
156 isdigit ((unsigned char) *tok->dptr) &&
157 isdigit ((unsigned char) *(tok->dptr + 1))) {
159 mutt_buffer_addch (dest,
160 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
165 mutt_buffer_addch (dest, ch);
168 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
170 return -1; /* premature end of token */
173 mutt_buffer_addch (dest, ch);
175 mutt_buffer_addch (dest, '\033');
176 else if (isalpha ((unsigned char) ch))
177 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
179 mutt_buffer_addch (dest, '^');
180 mutt_buffer_addch (dest, ch);
183 else if (ch == '`' && (!qc || qc == '"')) {
193 if ((pc = strpbrk (pc, "\\`"))) {
194 /* skip any quoted chars */
198 } while (pc && *pc != '`');
200 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
203 cmd = mutt_substrdup (tok->dptr, pc);
204 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
206 (debugfile, "mutt_get_token: unable to fork command: %s",
216 memset (&expn, 0, sizeof (expn));
217 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
219 mutt_wait_filter (pid);
221 /* if we got output, make a new string consiting of the shell ouptput
222 plus whatever else was left on the original line */
223 /* BUT: If this is inside a quoted string, directly add output to
225 if (expn.data && qc) {
226 mutt_buffer_addstr (dest, expn.data);
229 else if (expn.data) {
230 expnlen = mutt_strlen (expn.data);
231 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
232 ptr = safe_malloc (tok->dsize);
233 memcpy (ptr, expn.data, expnlen);
234 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
239 tok->destroy = 1; /* mark that the caller should destroy this data */
244 else if (ch == '$' && (!qc || qc == '"')
245 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
246 char *env = NULL, *var = NULL;
248 if (*tok->dptr == '{') {
250 if ((pc = strchr (tok->dptr, '}'))) {
251 var = mutt_substrdup (tok->dptr, pc);
256 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
258 var = mutt_substrdup (tok->dptr, pc);
261 if (var && (env = getenv (var)))
262 mutt_buffer_addstr (dest, env);
266 mutt_buffer_addch (dest, ch);
268 mutt_buffer_addch (dest, 0); /* terminate the string */
273 static void add_to_list (LIST ** list, const char *str)
275 LIST *t, *last = NULL;
277 /* don't add a NULL or empty string to the list */
278 if (!str || *str == '\0')
281 /* check to make sure the item is not already on this list */
282 for (last = *list; last; last = last->next) {
283 if (ascii_strcasecmp (str, last->data) == 0) {
284 /* already on the list, so just ignore it */
292 if (!*list || last) {
293 t = (LIST *) safe_calloc (1, sizeof (LIST));
294 t->data = safe_strdup (str);
304 static int add_to_rx_list (RX_LIST ** list, const char *s, int flags,
307 RX_LIST *t, *last = NULL;
313 if (!(rx = mutt_compile_regexp (s, flags))) {
314 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
318 /* check to make sure the item is not already on this list */
319 for (last = *list; last; last = last->next) {
320 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
321 /* already on the list, so just ignore it */
329 if (!*list || last) {
330 t = mutt_new_rx_list ();
340 mutt_free_regexp (&rx);
345 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
346 const char *templ, BUFFER * err)
348 SPAM_LIST *t = NULL, *last = NULL;
353 if (!pat || !*pat || !templ)
356 if (!(rx = mutt_compile_regexp (pat, REG_ICASE))) {
357 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
361 /* check to make sure the item is not already on this list */
362 for (last = *list; last; last = last->next) {
363 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
364 /* Already on the list. Formerly we just skipped this case, but
365 * now we're supporting removals, which means we're supporting
366 * re-adds conceptually. So we probably want this to imply a
367 * removal, then do an add. We can achieve the removal by freeing
368 * the template, and leaving t pointed at the current item.
378 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
379 * update. Otherwise we want to make a new one to link at the list's end.
382 t = mutt_new_spam_list ();
390 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
391 t->template = safe_strdup (templ);
393 /* Find highest match number in template string */
395 for (p = templ; *p;) {
400 while (*p && isdigit ((int) *p))
406 t->nmatch++; /* match 0 is always the whole expr */
411 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
413 SPAM_LIST *spam, *prev;
416 /* Being first is a special case. */
418 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
420 mutt_free_regexp (&spam->rx);
421 FREE(&spam->template);
427 for (spam = prev->next; spam;) {
428 if (!mutt_strcmp (spam->rx->pattern, pat)) {
429 prev->next = spam->next;
430 mutt_free_regexp (&spam->rx);
431 FREE(spam->template);
444 static void remove_from_list (LIST ** l, const char *str)
446 LIST *p, *last = NULL;
448 if (mutt_strcmp ("*", str) == 0)
449 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
454 if (ascii_strcasecmp (str, p->data) == 0) {
457 last->next = p->next;
470 static int remove_from_rx_list (RX_LIST ** l, const char *str)
472 RX_LIST *p, *last = NULL;
475 if (mutt_strcmp ("*", str) == 0) {
476 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
483 if (ascii_strcasecmp (str, p->rx->pattern) == 0) {
484 mutt_free_regexp (&p->rx);
486 last->next = p->next;
501 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
507 memset (&token, 0, sizeof (token));
508 mutt_extract_token (tmp, s, 0);
510 /* is the item defined as a variable or a function? */
511 if (!(res = (mutt_option_index (tmp->data) != -1)))
512 for (i = 0; !res && i < MENU_MAX; i++) {
513 struct binding_t *b = km_get_table (Menus[i].value);
518 for (j = 0; b[j].name; j++)
519 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
520 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
525 /* check for feature_* */
530 j = mutt_strlen (tmp->data);
531 /* need at least input of 'feature_X' */
535 while (Features[i].name) {
536 if (mutt_strlen (Features[i].name) == j &&
537 ascii_strncasecmp (Features[i].name, p, j)) {
548 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
550 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
553 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
555 if ((data && res) || (!data && !res)) {
556 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
557 mutt_error ("Error: %s", err->data);
566 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
570 mutt_extract_token (buf, s, 0);
572 /* don't add "*" to the unignore list */
573 if (strcmp (buf->data, "*"))
574 add_to_list (&UnIgnore, buf->data);
576 remove_from_list (&Ignore, buf->data);
578 while (MoreArgs (s));
583 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
587 mutt_extract_token (buf, s, 0);
588 remove_from_list (&UnIgnore, buf->data);
589 add_to_list (&Ignore, buf->data);
591 while (MoreArgs (s));
596 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
600 mutt_extract_token (buf, s, 0);
601 add_to_list ((LIST **) data, buf->data);
603 while (MoreArgs (s));
609 static int _parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
610 BUFFER * err, int flags)
613 mutt_extract_token (buf, s, 0);
614 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
618 while (MoreArgs (s));
623 static int parse_rx_list (BUFFER * buf, BUFFER * s, unsigned long data,
626 return _parse_rx_list (buf, s, data, err, REG_ICASE);
629 static int parse_rx_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
633 mutt_extract_token (buf, s, 0);
634 if (mutt_strcmp (buf->data, "*") == 0) {
635 mutt_free_rx_list ((RX_LIST **) data);
638 remove_from_rx_list ((RX_LIST **) data, buf->data);
640 while (MoreArgs (s));
646 static void _alternates_clean (void)
650 if (Context && Context->msgcount) {
651 for (i = 0; i < Context->msgcount; i++)
652 Context->hdrs[i]->recip_valid = 0;
656 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
659 _alternates_clean ();
661 mutt_extract_token (buf, s, 0);
662 remove_from_rx_list (&UnAlternates, buf->data);
664 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
667 while (MoreArgs (s));
672 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
675 _alternates_clean ();
677 mutt_extract_token (buf, s, 0);
678 remove_from_rx_list (&Alternates, buf->data);
680 if (mutt_strcmp (buf->data, "*") &&
681 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
685 while (MoreArgs (s));
690 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
695 memset (&templ, 0, sizeof (templ));
697 /* Insist on at least one parameter */
700 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
702 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
706 /* Extract the first token, a regexp */
707 mutt_extract_token (buf, s, 0);
709 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
710 if (data == M_SPAM) {
711 /* If there's a second parameter, it's a template for the spam tag. */
713 mutt_extract_token (&templ, s, 0);
715 /* Add to the spam list. */
716 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
723 /* If not, try to remove from the nospam list. */
725 remove_from_rx_list (&NoSpamList, buf->data);
731 /* M_NOSPAM is for nospam commands. */
732 else if (data == M_NOSPAM) {
733 /* nospam only ever has one parameter. */
735 /* "*" is a special case. */
736 if (!mutt_strcmp (buf->data, "*")) {
737 mutt_free_spam_list (&SpamList);
738 mutt_free_rx_list (&NoSpamList);
742 /* If it's on the spam list, just remove it. */
743 if (remove_from_spam_list (&SpamList, buf->data) != 0)
746 /* Otherwise, add it to the nospam list. */
747 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
753 /* This should not happen. */
754 strfcpy (err->data, "This is no good at all.", err->dsize);
758 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
762 mutt_extract_token (buf, s, 0);
764 * Check for deletion of entire list
766 if (mutt_strcmp (buf->data, "*") == 0) {
767 mutt_free_list ((LIST **) data);
770 remove_from_list ((LIST **) data, buf->data);
772 while (MoreArgs (s));
777 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
781 mutt_extract_token (buf, s, 0);
782 remove_from_rx_list (&UnMailLists, buf->data);
784 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
787 while (MoreArgs (s));
792 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
796 mutt_extract_token (buf, s, 0);
797 remove_from_rx_list (&SubscribedLists, buf->data);
798 remove_from_rx_list (&MailLists, buf->data);
800 if (mutt_strcmp (buf->data, "*") &&
801 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
804 while (MoreArgs (s));
809 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
813 mutt_extract_token (buf, s, 0);
814 remove_from_rx_list (&UnMailLists, buf->data);
815 remove_from_rx_list (&UnSubscribedLists, buf->data);
817 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
819 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
822 while (MoreArgs (s));
827 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
831 mutt_extract_token (buf, s, 0);
832 remove_from_rx_list (&SubscribedLists, buf->data);
834 if (mutt_strcmp (buf->data, "*") &&
835 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
838 while (MoreArgs (s));
843 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
846 ALIAS *tmp, *last = NULL;
849 mutt_extract_token (buf, s, 0);
851 if (mutt_strcmp ("*", buf->data) == 0) {
852 if (CurrentMenu == MENU_ALIAS) {
853 for (tmp = Aliases; tmp; tmp = tmp->next)
855 set_option (OPTFORCEREDRAWINDEX);
858 mutt_free_alias (&Aliases);
862 for (tmp = Aliases; tmp; tmp = tmp->next) {
863 if (mutt_strcasecmp (buf->data, tmp->name) == 0) {
864 if (CurrentMenu == MENU_ALIAS) {
866 set_option (OPTFORCEREDRAWINDEX);
871 last->next = tmp->next;
875 mutt_free_alias (&tmp);
881 while (MoreArgs (s));
885 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
888 ALIAS *tmp = Aliases;
893 strfcpy (err->data, _("alias: no address"), err->dsize);
897 mutt_extract_token (buf, s, 0);
899 dprint (2, (debugfile, "parse_alias: First token is '%s'.\n", buf->data));
901 /* check to see if an alias with this name already exists */
902 for (; tmp; tmp = tmp->next) {
903 if (!mutt_strcasecmp (tmp->name, buf->data))
909 /* create a new alias */
910 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
912 tmp->name = safe_strdup (buf->data);
913 /* give the main addressbook code a chance */
914 if (CurrentMenu == MENU_ALIAS)
915 set_option (OPTMENUCALLER);
918 /* override the previous value */
919 rfc822_free_address (&tmp->addr);
920 if (CurrentMenu == MENU_ALIAS)
921 set_option (OPTFORCEREDRAWINDEX);
924 mutt_extract_token (buf, s,
925 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
926 dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n", buf->data));
927 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
932 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
933 snprintf (err->data, err->dsize,
934 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
938 if (debuglevel >= 2) {
941 for (a = tmp->addr; a; a = a->next) {
943 dprint (2, (debugfile, "parse_alias: %s\n", a->mailbox));
945 dprint (2, (debugfile, "parse_alias: Group %s\n", a->mailbox));
953 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
956 LIST *tmp = UserHeader;
961 mutt_extract_token (buf, s, 0);
962 if (mutt_strcmp ("*", buf->data) == 0)
963 mutt_free_list (&UserHeader);
968 l = mutt_strlen (buf->data);
969 if (buf->data[l - 1] == ':')
973 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
974 && tmp->data[l] == ':') {
977 last->next = tmp->next;
979 UserHeader = tmp->next;
982 mutt_free_list (&ptr);
991 while (MoreArgs (s));
995 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
1002 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1003 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
1004 strfcpy (err->data, _("invalid header field"), err->dsize);
1007 keylen = p - buf->data + 1;
1010 for (tmp = UserHeader;; tmp = tmp->next) {
1011 /* see if there is already a field by this name */
1012 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
1013 /* replace the old value */
1015 tmp->data = buf->data;
1016 memset (buf, 0, sizeof (BUFFER));
1022 tmp->next = mutt_new_list ();
1026 tmp = mutt_new_list ();
1029 tmp->data = buf->data;
1030 memset (buf, 0, sizeof (BUFFER));
1035 parse_sort (short *val, const char *s, const struct mapping_t *map,
1040 if (mutt_strncmp ("reverse-", s, 8) == 0) {
1042 flags = SORT_REVERSE;
1045 if (mutt_strncmp ("last-", s, 5) == 0) {
1050 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1051 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1060 static void mutt_set_default (struct option_t *p)
1062 switch (p->type & DT_MASK) {
1064 if (!p->init && *((char **) p->data))
1065 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1068 if (!p->init && *((char **) p->data)) {
1069 char *cp = safe_strdup (*((char **) p->data));
1071 /* mutt_pretty_mailbox (cp); */
1072 p->init = (unsigned long) cp;
1076 if (!p->init && *((ADDRESS **) p->data)) {
1077 char tmp[HUGE_STRING];
1080 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1081 p->init = (unsigned long) safe_strdup (tmp);
1086 REGEXP *pp = (REGEXP *) p->data;
1088 if (!p->init && pp->pattern)
1089 p->init = (unsigned long) safe_strdup (pp->pattern);
1095 static void mutt_restore_default (struct option_t *p)
1097 switch (p->type & DT_MASK) {
1100 mutt_str_replace ((char **) p->data, (char *) p->init);
1104 char path[_POSIX_PATH_MAX];
1106 strfcpy (path, (char *) p->init, sizeof (path));
1107 mutt_expand_path (path, sizeof (path));
1108 mutt_str_replace ((char **) p->data, path);
1113 rfc822_free_address ((ADDRESS **) p->data);
1114 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1119 set_option (p->data);
1121 unset_option (p->data);
1124 set_quadoption (p->data, p->init);
1129 *((short *) p->data) = p->init;
1133 REGEXP *pp = (REGEXP *) p->data;
1136 FREE (&pp->pattern);
1143 char *s = (char *) p->init;
1145 pp->rx = safe_calloc (1, sizeof (regex_t));
1146 if (mutt_strcmp (p->option, "mask") != 0)
1147 flags |= mutt_which_case ((const char *) p->init);
1148 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1152 if (REGCOMP (pp->rx, s, flags) != 0) {
1154 _("mutt_restore_default(%s): error in regexp: %s\n"),
1155 p->option, pp->pattern);
1156 FREE (&pp->pattern);
1161 mutt_str_replace (&pp->pattern, (char *) p->init);
1167 if (p->flags & R_INDEX)
1168 set_option (OPTFORCEREDRAWINDEX);
1169 if (p->flags & R_PAGER)
1170 set_option (OPTFORCEREDRAWPAGER);
1171 if (p->flags & R_RESORT_SUB)
1172 set_option (OPTSORTSUBTHREADS);
1173 if (p->flags & R_RESORT)
1174 set_option (OPTNEEDRESORT);
1175 if (p->flags & R_RESORT_INIT)
1176 set_option (OPTRESORTINIT);
1177 if (p->flags & R_TREE)
1178 set_option (OPTREDRAWTREE);
1181 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1184 int idx, query, unset, inv, reset, r = 0;
1185 char *p, scratch[_POSIX_PATH_MAX];
1187 while (MoreArgs (s)) {
1188 /* reset state variables */
1190 unset = data & M_SET_UNSET;
1191 inv = data & M_SET_INV;
1192 reset = data & M_SET_RESET;
1194 if (*s->dptr == '?') {
1198 else if (mutt_strncmp ("no", s->dptr, 2) == 0) {
1202 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) {
1206 else if (*s->dptr == '&') {
1211 /* get the variable name */
1212 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1214 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1215 !(reset && !mutt_strcmp ("all", tmp->data))) {
1216 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1222 if (query || unset || inv) {
1223 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1227 if (s && *s->dptr == '=') {
1228 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1232 if (!mutt_strcmp ("all", tmp->data)) {
1233 for (idx = 0; MuttVars[idx].option; idx++)
1234 mutt_restore_default (&MuttVars[idx]);
1238 mutt_restore_default (&MuttVars[idx]);
1240 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1241 if (s && *s->dptr == '=') {
1242 if (unset || inv || query) {
1243 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1248 mutt_extract_token (tmp, s, 0);
1249 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1251 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1254 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1260 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1261 ? _("%s is set") : _("%s is unset"), tmp->data);
1266 unset_option (MuttVars[idx].data);
1268 toggle_option (MuttVars[idx].data);
1270 set_option (MuttVars[idx].data);
1272 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1273 DTYPE (MuttVars[idx].type) == DT_PATH ||
1274 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1276 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1277 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1279 FREE ((void *) MuttVars[idx].data);
1281 else if (query || *s->dptr != '=') {
1285 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1287 rfc822_write_address (_tmp, sizeof (_tmp),
1288 *((ADDRESS **) MuttVars[idx].data), 0);
1292 val = *((char **) MuttVars[idx].data);
1294 /* user requested the value of this variable */
1295 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1302 /* copy the value of the string */
1303 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1304 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1306 FREE ((void *) MuttVars[idx].data);
1308 mutt_extract_token (tmp, s, 0);
1309 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1310 strfcpy (scratch, tmp->data, sizeof (scratch));
1311 mutt_expand_path (scratch, sizeof (scratch));
1312 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1314 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1315 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1316 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1317 mutt_set_charset (Charset);
1320 *((ADDRESS **) MuttVars[idx].data) =
1321 rfc822_parse_adrlist (NULL, tmp->data);
1325 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1326 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1330 if (query || *s->dptr != '=') {
1331 /* user requested the value of this variable */
1332 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1333 NONULL (ptr->pattern));
1337 if (option (OPTATTACHMSG)
1338 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1339 snprintf (err->data, err->dsize,
1340 "Operation not permitted when in attach-message mode.");
1347 /* copy the value of the string */
1348 mutt_extract_token (tmp, s, 0);
1350 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1353 /* $mask is case-sensitive */
1354 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1355 flags |= mutt_which_case (tmp->data);
1358 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1365 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1366 if ((e = REGCOMP (rx, p, flags)) != 0) {
1367 regerror (e, rx, err->data, err->dsize);
1373 /* get here only if everything went smootly */
1375 FREE (&ptr->pattern);
1376 regfree ((regex_t *) ptr->rx);
1380 ptr->pattern = safe_strdup (tmp->data);
1384 /* $reply_regexp and $alterantes require special treatment */
1386 if (Context && Context->msgcount &&
1387 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1388 regmatch_t pmatch[1];
1391 #define CUR_ENV Context->hdrs[i]->env
1392 for (i = 0; i < Context->msgcount; i++) {
1393 if (CUR_ENV && CUR_ENV->subject) {
1394 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1395 CUR_ENV->subject, 1, pmatch,
1397 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1404 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1405 if (query || *s->dptr != '=') {
1406 switch (DefaultMagic) {
1423 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1429 /* copy the value of the string */
1430 mutt_extract_token (tmp, s, 0);
1431 if (mx_set_magic (tmp->data)) {
1432 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1438 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1439 short *ptr = (short *) MuttVars[idx].data;
1443 if (query || *s->dptr != '=') {
1444 /* user requested the value of this variable */
1445 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1451 mutt_extract_token (tmp, s, 0);
1452 val = strtol (tmp->data, &t, 0);
1454 if (!*tmp->data || *t || (short) val != val) {
1455 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1462 /* these ones need a sanity check */
1463 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1466 mutt_init_history ();
1468 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1473 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1475 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1477 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1478 vals[quadoption (MuttVars[idx].data)]);
1482 if (*s->dptr == '=') {
1484 mutt_extract_token (tmp, s, 0);
1485 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1486 set_quadoption (MuttVars[idx].data, M_YES);
1487 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1488 set_quadoption (MuttVars[idx].data, M_NO);
1489 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1490 set_quadoption (MuttVars[idx].data, M_ASKYES);
1491 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1492 set_quadoption (MuttVars[idx].data, M_ASKNO);
1494 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1501 toggle_quadoption (MuttVars[idx].data);
1503 set_quadoption (MuttVars[idx].data, M_NO);
1505 set_quadoption (MuttVars[idx].data, M_YES);
1508 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1509 const struct mapping_t *map = NULL;
1511 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1513 map = SortAliasMethods;
1515 case DT_SORT_BROWSER:
1516 map = SortBrowserMethods;
1519 if ((WithCrypto & APPLICATION_PGP))
1520 map = SortKeyMethods;
1523 map = SortAuxMethods;
1531 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1532 MuttVars[idx].option);
1537 if (query || *s->dptr != '=') {
1539 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1542 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1543 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1545 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1550 mutt_extract_token (tmp, s, 0);
1552 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1559 snprintf (err->data, err->dsize, _("%s: unknown type"),
1560 MuttVars[idx].option);
1565 if (MuttVars[idx].flags & R_INDEX)
1566 set_option (OPTFORCEREDRAWINDEX);
1567 if (MuttVars[idx].flags & R_PAGER)
1568 set_option (OPTFORCEREDRAWPAGER);
1569 if (MuttVars[idx].flags & R_RESORT_SUB)
1570 set_option (OPTSORTSUBTHREADS);
1571 if (MuttVars[idx].flags & R_RESORT)
1572 set_option (OPTNEEDRESORT);
1573 if (MuttVars[idx].flags & R_RESORT_INIT)
1574 set_option (OPTRESORTINIT);
1575 if (MuttVars[idx].flags & R_TREE)
1576 set_option (OPTREDRAWTREE);
1583 /* reads the specified initialization file. returns -1 if errors were found
1584 so that we can pause to let the user know... */
1585 static int source_rc (const char *rcfile, BUFFER * err)
1588 int line = 0, rc = 0, conv = 0;
1590 char *linebuf = NULL;
1591 char *currentline = NULL;
1595 dprint (2, (debugfile, "Reading configuration file '%s'.\n", rcfile));
1597 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1598 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1602 memset (&token, 0, sizeof (token));
1603 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1604 conv = ConfigCharset && (*ConfigCharset) && Charset;
1606 currentline = safe_strdup (linebuf);
1609 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1612 currentline = linebuf;
1614 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1615 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1616 if (--rc < -MAXERRS) {
1618 FREE (¤tline);
1627 FREE (¤tline);
1633 mutt_wait_filter (pid);
1635 /* the muttrc source keyword */
1636 snprintf (err->data, err->dsize,
1637 rc >= -MAXERRS ? _("source: errors in %s")
1638 : _("source: reading aborted due too many errors in %s"),
1647 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1650 char path[_POSIX_PATH_MAX];
1654 if (mutt_extract_token (tmp, s, 0) != 0) {
1655 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1659 strfcpy (path, tmp->data, sizeof (path));
1660 mutt_expand_path (path, sizeof (path));
1662 rc += source_rc (path, err);
1664 while (MoreArgs (s));
1666 return ((rc < 0) ? -1 : 0);
1669 /* line command to execute
1671 token scratch buffer to be used by parser. caller should free
1672 token->data when finished. the reason for this variable is
1673 to avoid having to allocate and deallocate a lot of memory
1674 if we are parsing many lines. the caller can pass in the
1675 memory to use, which avoids having to create new space for
1676 every call to this function.
1678 err where to write error messages */
1679 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1684 memset (&expn, 0, sizeof (expn));
1685 expn.data = expn.dptr = line;
1686 expn.dsize = mutt_strlen (line);
1691 while (*expn.dptr) {
1692 if (*expn.dptr == '#')
1693 break; /* rest of line is a comment */
1694 if (*expn.dptr == ';') {
1698 mutt_extract_token (token, &expn, 0);
1699 for (i = 0; Commands[i].name; i++) {
1700 if (!mutt_strcmp (token->data, Commands[i].name)) {
1701 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1706 if (!Commands[i].name) {
1707 snprintf (err->data, err->dsize, _("%s: unknown command"),
1708 NONULL (token->data));
1720 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1721 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1722 /* initial string that starts completion. No telling how much crap
1723 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1724 char User_typed[LONG_STRING] = { 0 };
1726 int Num_matched = 0; /* Number of matches for completion */
1727 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1728 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1730 /* helper function for completion. Changes the dest buffer if
1731 necessary/possible to aid completion.
1732 dest == completion result gets here.
1733 src == candidate for completion.
1734 try == user entered data for completion.
1735 len == length of dest buffer.
1737 static void candidate (char *dest, char *try, char *src, int len)
1741 if (strstr (src, try) == src) {
1742 Matches[Num_matched++] = src;
1744 strfcpy (dest, src, len);
1746 for (l = 0; src[l] && src[l] == dest[l]; l++);
1752 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1756 int spaces; /* keep track of the number of leading spaces on the line */
1759 spaces = buffer - pt;
1761 pt = buffer + pos - spaces;
1762 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1765 if (pt == buffer) { /* complete cmd */
1766 /* first TAB. Collect all the matches */
1769 strfcpy (User_typed, pt, sizeof (User_typed));
1770 memset (Matches, 0, sizeof (Matches));
1771 memset (Completed, 0, sizeof (Completed));
1772 for (num = 0; Commands[num].name; num++)
1773 candidate (Completed, User_typed, Commands[num].name,
1774 sizeof (Completed));
1775 Matches[Num_matched++] = User_typed;
1777 /* All matches are stored. Longest non-ambiguous string is ""
1778 * i.e. dont change 'buffer'. Fake successful return this time */
1779 if (User_typed[0] == 0)
1783 if (Completed[0] == 0 && User_typed[0])
1786 /* Num_matched will _always_ be atleast 1 since the initial
1787 * user-typed string is always stored */
1788 if (numtabs == 1 && Num_matched == 2)
1789 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1790 else if (numtabs > 1 && Num_matched > 2)
1791 /* cycle thru all the matches */
1792 snprintf (Completed, sizeof (Completed), "%s",
1793 Matches[(numtabs - 2) % Num_matched]);
1795 /* return the completed command */
1796 strncpy (buffer, Completed, len - spaces);
1798 else if (!mutt_strncmp (buffer, "set", 3)
1799 || !mutt_strncmp (buffer, "unset", 5)
1800 || !mutt_strncmp (buffer, "reset", 5)
1801 || !mutt_strncmp (buffer, "toggle", 6)) { /* complete variables */
1802 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1805 /* loop through all the possible prefixes (no, inv, ...) */
1806 if (!mutt_strncmp (buffer, "set", 3)) {
1807 for (num = 0; prefixes[num]; num++) {
1808 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1809 pt += mutt_strlen (prefixes[num]);
1815 /* first TAB. Collect all the matches */
1818 strfcpy (User_typed, pt, sizeof (User_typed));
1819 memset (Matches, 0, sizeof (Matches));
1820 memset (Completed, 0, sizeof (Completed));
1821 for (num = 0; MuttVars[num].option; num++)
1822 candidate (Completed, User_typed, MuttVars[num].option,
1823 sizeof (Completed));
1824 Matches[Num_matched++] = User_typed;
1826 /* All matches are stored. Longest non-ambiguous string is ""
1827 * i.e. dont change 'buffer'. Fake successful return this time */
1828 if (User_typed[0] == 0)
1832 if (Completed[0] == 0 && User_typed[0])
1835 /* Num_matched will _always_ be atleast 1 since the initial
1836 * user-typed string is always stored */
1837 if (numtabs == 1 && Num_matched == 2)
1838 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1839 else if (numtabs > 1 && Num_matched > 2)
1840 /* cycle thru all the matches */
1841 snprintf (Completed, sizeof (Completed), "%s",
1842 Matches[(numtabs - 2) % Num_matched]);
1844 strncpy (pt, Completed, buffer + len - pt - spaces);
1846 else if (!mutt_strncmp (buffer, "exec", 4)) {
1847 struct binding_t *menu = km_get_table (CurrentMenu);
1849 if (!menu && CurrentMenu != MENU_PAGER)
1853 /* first TAB. Collect all the matches */
1856 strfcpy (User_typed, pt, sizeof (User_typed));
1857 memset (Matches, 0, sizeof (Matches));
1858 memset (Completed, 0, sizeof (Completed));
1859 for (num = 0; menu[num].name; num++)
1860 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1861 /* try the generic menu */
1862 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1864 for (num = 0; menu[num].name; num++)
1865 candidate (Completed, User_typed, menu[num].name,
1866 sizeof (Completed));
1868 Matches[Num_matched++] = User_typed;
1870 /* All matches are stored. Longest non-ambiguous string is ""
1871 * i.e. dont change 'buffer'. Fake successful return this time */
1872 if (User_typed[0] == 0)
1876 if (Completed[0] == 0 && User_typed[0])
1879 /* Num_matched will _always_ be atleast 1 since the initial
1880 * user-typed string is always stored */
1881 if (numtabs == 1 && Num_matched == 2)
1882 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1883 else if (numtabs > 1 && Num_matched > 2)
1884 /* cycle thru all the matches */
1885 snprintf (Completed, sizeof (Completed), "%s",
1886 Matches[(numtabs - 2) % Num_matched]);
1888 strncpy (pt, Completed, buffer + len - pt - spaces);
1896 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1898 char var[STRING], *pt = buffer;
1905 spaces = buffer - pt;
1907 pt = buffer + pos - spaces;
1908 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1910 pt++; /* move past the space */
1911 if (*pt == '=') /* abort if no var before the '=' */
1914 if (mutt_strncmp (buffer, "set", 3) == 0) {
1917 strfcpy (var, pt, sizeof (var));
1918 /* ignore the trailing '=' when comparing */
1919 var[mutt_strlen (var) - 1] = 0;
1920 if ((idx = mutt_option_index (var)) == -1)
1921 return 0; /* no such variable. */
1923 char tmp[LONG_STRING], tmp2[LONG_STRING];
1925 size_t dlen = buffer + len - pt - spaces;
1926 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1930 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1931 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1932 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1933 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1934 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1935 mutt_pretty_mailbox (tmp);
1937 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1938 rfc822_write_address (tmp, sizeof (tmp),
1939 *((ADDRESS **) MuttVars[idx].data), 0);
1941 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1942 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1943 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1944 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1945 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1946 const struct mapping_t *map;
1949 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1951 map = SortAliasMethods;
1953 case DT_SORT_BROWSER:
1954 map = SortBrowserMethods;
1957 if ((WithCrypto & APPLICATION_PGP))
1958 map = SortKeyMethods;
1967 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1969 snprintf (tmp, sizeof (tmp), "%s%s%s",
1970 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1972 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1975 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1976 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1981 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1982 if (*s == '\\' || *s == '"')
1988 strfcpy (tmp, pt, sizeof (tmp));
1989 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1997 /* Implement the -Q command line flag */
1998 int mutt_query_variables (LIST * queries)
2002 char errbuff[STRING];
2003 char command[STRING];
2007 memset (&err, 0, sizeof (err));
2008 memset (&token, 0, sizeof (token));
2011 err.dsize = sizeof (errbuff);
2013 for (p = queries; p; p = p->next) {
2014 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2015 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2016 fprintf (stderr, "%s\n", err.data);
2020 printf ("%s\n", err.data);
2027 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2031 for (i = 0; map[i].name; i++)
2032 if (map[i].value == val)
2033 return (map[i].name);
2037 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2041 for (i = 0; map[i].name; i++)
2042 if (ascii_strcasecmp (map[i].name, name) == 0)
2043 return (map[i].value);
2048 static void start_debug (void)
2052 char buf[_POSIX_PATH_MAX];
2053 char buf2[_POSIX_PATH_MAX];
2055 /* rotate the old debug logs */
2056 for (i = 3; i >= 0; i--) {
2057 snprintf (buf, sizeof (buf), "%s/.muttdebug%d", NONULL (Homedir), i);
2058 snprintf (buf2, sizeof (buf2), "%s/.muttdebug%d", NONULL (Homedir),
2062 if ((debugfile = safe_fopen (buf, "w")) != NULL) {
2064 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2066 "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2067 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2072 static int mutt_execute_commands (LIST * p)
2075 char errstr[SHORT_STRING];
2077 memset (&err, 0, sizeof (err));
2079 err.dsize = sizeof (errstr);
2080 memset (&token, 0, sizeof (token));
2081 for (; p; p = p->next) {
2082 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2083 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2092 void mutt_init (int skip_sys_rc, LIST * commands)
2095 struct utsname utsname;
2096 char *p, buffer[STRING], error[STRING];
2097 int i, default_rc = 0, need_pause = 0;
2100 memset (&err, 0, sizeof (err));
2102 err.dsize = sizeof (error);
2105 * XXX - use something even more difficult to predict?
2107 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2108 "\033]9;%ld\a", (long) time (NULL));
2110 /* on one of the systems I use, getcwd() does not return the same prefix
2111 as is listed in the passwd file */
2112 if ((p = getenv ("HOME")))
2113 Homedir = safe_strdup (p);
2115 /* Get some information about the user */
2116 if ((pw = getpwuid (getuid ()))) {
2119 Username = safe_strdup (pw->pw_name);
2121 Homedir = safe_strdup (pw->pw_dir);
2123 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2124 Shell = safe_strdup (pw->pw_shell);
2129 fputs (_("unable to determine home directory"), stderr);
2132 if ((p = getenv ("USER")))
2133 Username = safe_strdup (p);
2136 fputs (_("unable to determine username"), stderr);
2139 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2143 /* Start up debugging mode if requested */
2148 /* And about the host... */
2150 /* some systems report the FQDN instead of just the hostname */
2151 if ((p = strchr (utsname.nodename, '.'))) {
2152 Hostname = mutt_substrdup (utsname.nodename, p);
2154 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2157 Hostname = safe_strdup (utsname.nodename);
2160 #define DOMAIN buffer
2161 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2162 Fqdn = safe_strdup ("@");
2165 if (*DOMAIN != '@') {
2166 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2167 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2170 Fqdn = safe_strdup (NONULL (Hostname));
2177 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2179 fgets (buffer, sizeof (buffer), f);
2180 p = (char*) &buffer;
2183 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2187 NewsServer = safe_strdup (p);
2191 if ((p = getenv ("NNTPSERVER")))
2192 NewsServer = safe_strdup (p);
2195 if ((p = getenv ("MAIL")))
2196 Spoolfile = safe_strdup (p);
2197 else if ((p = getenv ("MAILDIR")))
2198 Spoolfile = safe_strdup (p);
2201 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2203 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2205 Spoolfile = safe_strdup (buffer);
2208 if ((p = getenv ("MAILCAPS")))
2209 MailcapPath = safe_strdup (p);
2211 /* Default search path from RFC1524 */
2213 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2214 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2217 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2219 p = getenv ("VISUAL");
2221 p = getenv ("EDITOR");
2225 Editor = safe_strdup (p);
2226 Visual = safe_strdup (p);
2228 if ((p = getenv ("REPLYTO")) != NULL) {
2231 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2233 memset (&buf, 0, sizeof (buf));
2234 buf.data = buf.dptr = buffer;
2235 buf.dsize = mutt_strlen (buffer);
2237 memset (&token, 0, sizeof (token));
2238 parse_my_hdr (&token, &buf, 0, &err);
2242 if ((p = getenv ("EMAIL")) != NULL)
2243 From = rfc822_parse_adrlist (NULL, p);
2245 mutt_set_langinfo_charset ();
2246 mutt_set_charset (Charset);
2249 /* Set standard defaults */
2250 for (i = 0; MuttVars[i].option; i++) {
2251 mutt_set_default (&MuttVars[i]);
2252 mutt_restore_default (&MuttVars[i]);
2255 CurrentMenu = MENU_MAIN;
2258 #ifndef LOCALES_HACK
2259 /* Do we have a locale definition? */
2260 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2261 ((p = getenv ("LANG")) != NULL && p[0]) ||
2262 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2263 set_option (OPTLOCALES);
2267 /* Unset suspend by default if we're the session leader */
2268 if (getsid (0) == getpid ())
2269 unset_option (OPTSUSPEND);
2272 mutt_init_history ();
2281 * When changing the code which looks for a configuration file,
2282 * please also change the corresponding code in muttbug.sh.in.
2291 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2293 if (access (buffer, F_OK) == -1)
2294 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2295 if (access (buffer, F_OK) == -1)
2296 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2297 NONULL (Homedir), MUTT_VERSION);
2298 if (access (buffer, F_OK) == -1)
2299 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2303 Muttrc = safe_strdup (buffer);
2306 strfcpy (buffer, Muttrc, sizeof (buffer));
2308 mutt_expand_path (buffer, sizeof (buffer));
2309 Muttrc = safe_strdup (buffer);
2312 AliasFile = safe_strdup (NONULL (Muttrc));
2314 /* Process the global rc file if it exists and the user hasn't explicity
2315 requested not to via "-n". */
2317 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2319 if (access (buffer, F_OK) == -1)
2320 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2321 if (access (buffer, F_OK) == -1)
2322 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2324 if (access (buffer, F_OK) == -1)
2325 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2326 if (access (buffer, F_OK) != -1) {
2327 if (source_rc (buffer, &err) != 0) {
2328 fputs (err.data, stderr);
2329 fputc ('\n', stderr);
2335 /* Read the user's initialization file. */
2336 if (access (Muttrc, F_OK) != -1) {
2337 if (!option (OPTNOCURSES))
2339 if (source_rc (Muttrc, &err) != 0) {
2340 fputs (err.data, stderr);
2341 fputc ('\n', stderr);
2345 else if (!default_rc) {
2346 /* file specified by -F does not exist */
2347 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2348 mutt_endwin (buffer);
2352 if (mutt_execute_commands (commands) != 0)
2355 if (need_pause && !option (OPTNOCURSES)) {
2356 if (mutt_any_key_to_continue (NULL) == -1)
2361 set_option (OPTWEED); /* turn weeding on by default */
2365 int mutt_get_hook_type (const char *name)
2367 struct command_t *c;
2369 for (c = Commands; c->name; c++)
2370 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)