2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 #include "mutt_curses.h"
22 #include "mutt_regex.h"
27 #include "mutt_crypt.h"
28 #include "mutt_idna.h"
30 #if defined(USE_SSL) || defined(USE_NSS)
44 #include <sys/utsname.h>
46 #include <sys/types.h>
50 void toggle_quadoption (int opt)
53 int b = (opt % 4) * 2;
55 QuadOptions[n] ^= (1 << b);
58 void set_quadoption (int opt, int flag)
61 int b = (opt % 4) * 2;
63 QuadOptions[n] &= ~(0x3 << b);
64 QuadOptions[n] |= (flag & 0x3) << b;
67 int quadoption (int opt)
70 int b = (opt % 4) * 2;
72 return (QuadOptions[n] >> b) & 0x3;
75 int query_quadoption (int opt, const char *prompt)
77 int v = quadoption (opt);
86 v = mutt_yesorno (prompt, (v == M_ASKYES));
87 CLEARLINE (LINES - 1);
94 /* given the variable ``s'', return the index into the rc_vars array which
95 matches, or -1 if the variable is not found. */
96 int mutt_option_index (char *s)
100 for (i = 0; MuttVars[i].option; i++)
101 if (mutt_strcmp (s, MuttVars[i].option) == 0)
102 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
106 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
109 char qc = 0; /* quote char */
112 /* reset the destination pointer to the beginning of the buffer */
113 dest->dptr = dest->data;
116 while ((ch = *tok->dptr))
120 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
121 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
122 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
123 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
124 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
131 qc = 0; /* end of quote */
132 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
134 else if (ch == '\\' && qc != '\'')
137 return -1; /* premature end of token */
138 switch (ch = *tok->dptr++)
143 return -1; /* premature end of token */
144 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
149 mutt_buffer_addch (dest, '\r');
152 mutt_buffer_addch (dest, '\n');
155 mutt_buffer_addch (dest, '\t');
158 mutt_buffer_addch (dest, '\f');
161 mutt_buffer_addch (dest, '\033');
164 if (isdigit ((unsigned char) ch) &&
165 isdigit ((unsigned char) *tok->dptr) &&
166 isdigit ((unsigned char) *(tok->dptr + 1)))
169 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
173 mutt_buffer_addch (dest, ch);
176 else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
179 return -1; /* premature end of token */
182 mutt_buffer_addch (dest, ch);
184 mutt_buffer_addch (dest, '\033');
185 else if (isalpha ((unsigned char) ch))
186 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
189 mutt_buffer_addch (dest, '^');
190 mutt_buffer_addch (dest, ch);
193 else if (ch == '`' && (!qc || qc == '"'))
204 if ((pc = strpbrk (pc, "\\`")))
206 /* skip any quoted chars */
210 } while (pc && *pc != '`');
213 dprint (1, (debugfile, "mutt_get_token: mismatched backtics\n"));
216 cmd = mutt_substrdup (tok->dptr, pc);
217 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
219 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
228 memset (&expn, 0, sizeof (expn));
229 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
231 mutt_wait_filter (pid);
233 /* if we got output, make a new string consiting of the shell ouptput
234 plus whatever else was left on the original line */
235 /* BUT: If this is inside a quoted string, directly add output to
239 mutt_buffer_addstr (dest, expn.data);
244 expnlen = mutt_strlen (expn.data);
245 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
246 ptr = safe_malloc (tok->dsize);
247 memcpy (ptr, expn.data, expnlen);
248 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
253 tok->destroy = 1; /* mark that the caller should destroy this data */
258 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
260 char *env = NULL, *var = NULL;
262 if (*tok->dptr == '{')
265 if ((pc = strchr (tok->dptr, '}')))
267 var = mutt_substrdup (tok->dptr, pc);
273 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_'; pc++)
275 var = mutt_substrdup (tok->dptr, pc);
278 if (var && (env = getenv (var)))
279 mutt_buffer_addstr (dest, env);
283 mutt_buffer_addch (dest, ch);
285 mutt_buffer_addch (dest, 0); /* terminate the string */
290 static void add_to_list (LIST **list, const char *str)
292 LIST *t, *last = NULL;
294 /* don't add a NULL or empty string to the list */
295 if (!str || *str == '\0')
298 /* check to make sure the item is not already on this list */
299 for (last = *list; last; last = last->next)
301 if (ascii_strcasecmp (str, last->data) == 0)
303 /* already on the list, so just ignore it */
313 t = (LIST *) safe_calloc (1, sizeof (LIST));
314 t->data = safe_strdup (str);
325 static int add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
327 RX_LIST *t, *last = NULL;
333 if (!(rx = mutt_compile_regexp (s, flags)))
335 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
339 /* check to make sure the item is not already on this list */
340 for (last = *list; last; last = last->next)
342 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
344 /* already on the list, so just ignore it */
354 t = mutt_new_rx_list();
365 mutt_free_regexp (&rx);
371 static void remove_from_list (LIST **l, const char *str)
373 LIST *p, *last = NULL;
375 if (mutt_strcmp ("*", str) == 0)
376 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
383 if (ascii_strcasecmp (str, p->data) == 0)
387 last->next = p->next;
401 static void remove_from_rx_list (RX_LIST **l, const char *str)
403 RX_LIST *p, *last = NULL;
405 if (mutt_strcmp ("*", str) == 0)
406 mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */
413 if (ascii_strcasecmp (str, p->rx->pattern) == 0)
415 mutt_free_regexp (&p->rx);
417 last->next = p->next;
431 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
435 mutt_extract_token (buf, s, 0);
437 /* don't add "*" to the unignore list */
438 if (strcmp (buf->data, "*"))
439 add_to_list (&UnIgnore, buf->data);
441 remove_from_list (&Ignore, buf->data);
443 while (MoreArgs (s));
448 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
452 mutt_extract_token (buf, s, 0);
453 remove_from_list (&UnIgnore, buf->data);
454 add_to_list (&Ignore, buf->data);
456 while (MoreArgs (s));
461 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
465 mutt_extract_token (buf, s, 0);
466 add_to_list ((LIST **) data, buf->data);
468 while (MoreArgs (s));
473 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
477 mutt_extract_token (buf, s, 0);
478 if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
482 while (MoreArgs (s));
487 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
489 return _parse_rx_list (buf, s, data, err, REG_ICASE);
492 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
496 mutt_extract_token (buf, s, 0);
497 if (mutt_strcmp (buf->data, "*") == 0)
499 mutt_free_rx_list ((RX_LIST **) data);
502 remove_from_rx_list ((RX_LIST **) data, buf->data);
504 while (MoreArgs (s));
509 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
513 mutt_extract_token (buf, s, 0);
515 * Check for deletion of entire list
517 if (mutt_strcmp (buf->data, "*") == 0)
519 mutt_free_list ((LIST **) data);
522 remove_from_list ((LIST **) data, buf->data);
524 while (MoreArgs (s));
530 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
534 mutt_extract_token (buf, s, 0);
535 remove_from_rx_list (&MailLists, buf->data);
536 remove_from_rx_list (&SubscribedLists, buf->data);
538 while (MoreArgs (s));
543 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
547 mutt_extract_token (buf, s, 0);
548 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
550 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
553 while (MoreArgs (s));
558 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
560 ALIAS *tmp, *last = NULL;
564 mutt_extract_token (buf, s, 0);
566 if (mutt_strcmp ("*", buf->data) == 0)
568 if (CurrentMenu == MENU_ALIAS)
570 for (tmp = Aliases; tmp ; tmp = tmp->next)
572 set_option (OPTFORCEREDRAWINDEX);
575 mutt_free_alias (&Aliases);
579 for (tmp = Aliases; tmp; tmp = tmp->next)
581 if (mutt_strcasecmp (buf->data, tmp->name) == 0)
583 if (CurrentMenu == MENU_ALIAS)
586 set_option (OPTFORCEREDRAWINDEX);
591 last->next = tmp->next;
595 mutt_free_alias (&tmp);
601 while (MoreArgs (s));
605 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
607 ALIAS *tmp = Aliases;
613 strfcpy (err->data, _("alias: no address"), err->dsize);
617 mutt_extract_token (buf, s, 0);
619 /* check to see if an alias with this name already exists */
620 for (; tmp; tmp = tmp->next)
622 if (!mutt_strcasecmp (tmp->name, buf->data))
629 /* create a new alias */
630 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
632 tmp->name = safe_strdup (buf->data);
633 /* give the main addressbook code a chance */
634 if (CurrentMenu == MENU_ALIAS)
635 set_option (OPTMENUCALLER);
639 /* override the previous value */
640 rfc822_free_address (&tmp->addr);
641 if (CurrentMenu == MENU_ALIAS)
642 set_option (OPTFORCEREDRAWINDEX);
645 mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
646 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
651 if (mutt_addrlist_to_idna (tmp->addr, &estr))
653 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
661 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
664 LIST *tmp = UserHeader;
670 mutt_extract_token (buf, s, 0);
671 if (mutt_strcmp ("*", buf->data) == 0)
672 mutt_free_list (&UserHeader);
678 l = mutt_strlen (buf->data);
679 if (buf->data[l - 1] == ':')
684 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
688 last->next = tmp->next;
690 UserHeader = tmp->next;
693 mutt_free_list (&ptr);
703 while (MoreArgs (s));
707 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
713 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
714 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
716 strfcpy (err->data, _("invalid header field"), err->dsize);
719 keylen = p - buf->data + 1;
723 for (tmp = UserHeader; ; tmp = tmp->next)
725 /* see if there is already a field by this name */
726 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
728 /* replace the old value */
730 tmp->data = buf->data;
731 memset (buf, 0, sizeof (BUFFER));
737 tmp->next = mutt_new_list ();
742 tmp = mutt_new_list ();
745 tmp->data = buf->data;
746 memset (buf, 0, sizeof (BUFFER));
751 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
755 if (mutt_strncmp ("reverse-", s, 8) == 0)
758 flags = SORT_REVERSE;
761 if (mutt_strncmp ("last-", s, 5) == 0)
767 if ((i = mutt_getvaluebyname (s, map)) == -1)
769 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
778 static void mutt_set_default (struct option_t *p)
780 switch (p->type & DT_MASK)
783 if (!p->init && *((char **) p->data))
784 p->init = (unsigned long) safe_strdup (* ((char **) p->data));
787 if (!p->init && *((char **) p->data))
789 char *cp = safe_strdup (*((char **) p->data));
790 /* mutt_pretty_mailbox (cp); */
791 p->init = (unsigned long) cp;
795 if (!p->init && *((ADDRESS **) p->data))
797 char tmp[HUGE_STRING];
799 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
800 p->init = (unsigned long) safe_strdup (tmp);
805 REGEXP *pp = (REGEXP *) p->data;
806 if (!p->init && pp->pattern)
807 p->init = (unsigned long) safe_strdup (pp->pattern);
813 static void mutt_restore_default (struct option_t *p)
815 switch (p->type & DT_MASK)
819 mutt_str_replace ((char **) p->data, (char *) p->init);
824 char path[_POSIX_PATH_MAX];
826 strfcpy (path, (char *) p->init, sizeof (path));
827 mutt_expand_path (path, sizeof (path));
828 mutt_str_replace ((char **) p->data, path);
834 rfc822_free_address ((ADDRESS **) p->data);
835 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
840 set_option (p->data);
842 unset_option (p->data);
845 set_quadoption (p->data, p->init);
850 *((short *) p->data) = p->init;
854 REGEXP *pp = (REGEXP *) p->data;
866 char *s = (char *) p->init;
868 pp->rx = safe_calloc (1, sizeof (regex_t));
869 pp->pattern = safe_strdup ((char *) p->init);
870 if (mutt_strcmp (p->option, "alternates") == 0)
872 else if (mutt_strcmp (p->option, "mask") != 0)
873 flags |= mutt_which_case ((const char *) p->init);
874 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
879 if (REGCOMP (pp->rx, s, flags) != 0)
881 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
882 p->option, pp->pattern);
892 if (p->flags & R_INDEX)
893 set_option (OPTFORCEREDRAWINDEX);
894 if (p->flags & R_PAGER)
895 set_option (OPTFORCEREDRAWPAGER);
896 if (p->flags & R_RESORT_SUB)
897 set_option (OPTSORTSUBTHREADS);
898 if (p->flags & R_RESORT)
899 set_option (OPTNEEDRESORT);
900 if (p->flags & R_RESORT_INIT)
901 set_option (OPTRESORTINIT);
902 if (p->flags & R_TREE)
903 set_option (OPTREDRAWTREE);
906 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
908 int idx, query, unset, inv, reset, r = 0;
909 char *p, scratch[_POSIX_PATH_MAX];
913 /* reset state variables */
915 unset = data & M_SET_UNSET;
916 inv = data & M_SET_INV;
917 reset = data & M_SET_RESET;
924 else if (mutt_strncmp ("no", s->dptr, 2) == 0)
929 else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
934 else if (*s->dptr == '&')
940 /* get the variable name */
941 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
943 if ((idx = mutt_option_index (tmp->data)) == -1 &&
944 !(reset && !mutt_strcmp ("all", tmp->data)))
946 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
953 if (query || unset || inv)
955 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
959 if (s && *s->dptr == '=')
961 snprintf (err->data, err->dsize, _("value is illegal with reset"));
965 if (!mutt_strcmp ("all", tmp->data))
967 for (idx = 0; MuttVars[idx].option; idx++)
968 mutt_restore_default (&MuttVars[idx]);
972 mutt_restore_default (&MuttVars[idx]);
974 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
976 if (s && *s->dptr == '=')
978 if (unset || inv || query)
980 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
985 mutt_extract_token (tmp, s, 0);
986 if (ascii_strcasecmp ("yes", tmp->data) == 0)
988 else if (ascii_strcasecmp ("no", tmp->data) == 0)
992 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
999 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1000 ? _("%s is set") : _("%s is unset"), tmp->data);
1005 unset_option (MuttVars[idx].data);
1007 toggle_option (MuttVars[idx].data);
1009 set_option (MuttVars[idx].data);
1011 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1012 DTYPE (MuttVars[idx].type) == DT_PATH ||
1013 DTYPE (MuttVars[idx].type) == DT_ADDR)
1017 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1018 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1020 FREE (MuttVars[idx].data);
1022 else if (query || *s->dptr != '=')
1027 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1030 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1034 val = *((char **) MuttVars[idx].data);
1036 /* user requested the value of this variable */
1037 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1045 /* copy the value of the string */
1046 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1047 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1049 FREE (MuttVars[idx].data);
1051 mutt_extract_token (tmp, s, 0);
1052 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1054 strfcpy (scratch, tmp->data, sizeof (scratch));
1055 mutt_expand_path (scratch, sizeof (scratch));
1056 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1058 else if (DTYPE (MuttVars[idx].type) == DT_STR)
1060 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1061 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1062 mutt_set_charset (Charset);
1066 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1070 else if (DTYPE(MuttVars[idx].type) == DT_RX)
1072 REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1076 if (query || *s->dptr != '=')
1078 /* user requested the value of this variable */
1079 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1080 NONULL (ptr->pattern));
1084 if (option(OPTATTACHMSG) && (!mutt_strcmp(MuttVars[idx].option, "alternates")
1085 || !mutt_strcmp(MuttVars[idx].option, "reply_regexp")))
1087 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1094 /* copy the value of the string */
1095 mutt_extract_token (tmp, s, 0);
1097 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1101 /* $alternates is case-insensitive,
1102 $mask is case-sensitive */
1103 if (mutt_strcmp (MuttVars[idx].option, "alternates") == 0)
1105 else if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1106 flags |= mutt_which_case (tmp->data);
1109 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1118 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1119 if ((e = REGCOMP (rx, p, flags)) != 0)
1121 regerror (e, rx, err->data, err->dsize);
1127 /* get here only if everything went smootly */
1130 FREE (&ptr->pattern);
1131 regfree ((regex_t *) ptr->rx);
1135 ptr->pattern = safe_strdup (tmp->data);
1139 /* $reply_regexp and $alterantes require special treatment */
1141 if (Context && Context->msgcount &&
1142 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1144 regmatch_t pmatch[1];
1147 #define CUR_ENV Context->hdrs[i]->env
1148 for (i = 0; i < Context->msgcount; i++)
1150 if (CUR_ENV && CUR_ENV->subject)
1152 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1153 CUR_ENV->subject, 1, pmatch, 0)) ?
1155 CUR_ENV->subject + pmatch[0].rm_eo;
1161 if(Context && Context->msgcount &&
1162 mutt_strcmp(MuttVars[idx].option, "alternates") == 0)
1166 for(i = 0; i < Context->msgcount; i++)
1167 Context->hdrs[i]->recip_valid = 0;
1171 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1173 if (query || *s->dptr != '=')
1175 switch (DefaultMagic)
1193 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1199 /* copy the value of the string */
1200 mutt_extract_token (tmp, s, 0);
1201 if (mx_set_magic (tmp->data))
1203 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1208 else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1210 short *ptr = (short *) MuttVars[idx].data;
1214 if (query || *s->dptr != '=')
1216 /* user requested the value of this variable */
1217 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1223 mutt_extract_token (tmp, s, 0);
1224 val = strtol (tmp->data, &t, 0);
1226 if (!*tmp->data || *t || (short) val != val)
1228 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1235 /* these ones need a sanity check */
1236 if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1240 mutt_init_history ();
1242 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1248 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1252 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1254 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1255 vals [ quadoption (MuttVars[idx].data) ]);
1259 if (*s->dptr == '=')
1262 mutt_extract_token (tmp, s, 0);
1263 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1264 set_quadoption (MuttVars[idx].data, M_YES);
1265 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1266 set_quadoption (MuttVars[idx].data, M_NO);
1267 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1268 set_quadoption (MuttVars[idx].data, M_ASKYES);
1269 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1270 set_quadoption (MuttVars[idx].data, M_ASKNO);
1273 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1281 toggle_quadoption (MuttVars[idx].data);
1283 set_quadoption (MuttVars[idx].data, M_NO);
1285 set_quadoption (MuttVars[idx].data, M_YES);
1288 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1290 const struct mapping_t *map = NULL;
1292 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1295 map = SortAliasMethods;
1297 case DT_SORT_BROWSER:
1298 map = SortBrowserMethods;
1301 if ((WithCrypto & APPLICATION_PGP))
1302 map = SortKeyMethods;
1305 map = SortAuxMethods;
1314 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1319 if (query || *s->dptr != '=')
1321 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1323 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1324 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1325 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1330 mutt_extract_token (tmp, s , 0);
1332 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1340 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1345 if (MuttVars[idx].flags & R_INDEX)
1346 set_option (OPTFORCEREDRAWINDEX);
1347 if (MuttVars[idx].flags & R_PAGER)
1348 set_option (OPTFORCEREDRAWPAGER);
1349 if (MuttVars[idx].flags & R_RESORT_SUB)
1350 set_option (OPTSORTSUBTHREADS);
1351 if (MuttVars[idx].flags & R_RESORT)
1352 set_option (OPTNEEDRESORT);
1353 if (MuttVars[idx].flags & R_RESORT_INIT)
1354 set_option (OPTRESORTINIT);
1355 if (MuttVars[idx].flags & R_TREE)
1356 set_option (OPTREDRAWTREE);
1363 /* reads the specified initialization file. returns -1 if errors were found
1364 so that we can pause to let the user know... */
1365 static int source_rc (const char *rcfile, BUFFER *err)
1368 int line = 0, rc = 0;
1370 char *linebuf = NULL;
1375 if (stat (rcfile, &s) < 0)
1377 snprintf (err->data, err->dsize, _("%s: stat: %s"), rcfile, strerror (errno));
1380 if (!S_ISREG (s.st_mode))
1382 snprintf (err->data, err->dsize, _("%s: not a regular file"), rcfile);
1386 if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1388 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1392 memset (&token, 0, sizeof (token));
1393 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1395 if (mutt_parse_rc_line (linebuf, &token, err) == -1)
1397 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1398 if (--rc < -MAXERRS)
1409 mutt_wait_filter (pid);
1412 /* the muttrc source keyword */
1413 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1414 : _("source: reading aborted due too many errors in %s"), rcfile);
1422 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1424 char path[_POSIX_PATH_MAX];
1429 if (mutt_extract_token (tmp, s, 0) != 0)
1431 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1435 strfcpy (path, tmp->data, sizeof (path));
1436 mutt_expand_path (path, sizeof (path));
1438 rc += source_rc (path, err);
1440 while (MoreArgs (s));
1442 return ((rc < 0) ? -1 : 0);
1445 /* line command to execute
1447 token scratch buffer to be used by parser. caller should free
1448 token->data when finished. the reason for this variable is
1449 to avoid having to allocate and deallocate a lot of memory
1450 if we are parsing many lines. the caller can pass in the
1451 memory to use, which avoids having to create new space for
1452 every call to this function.
1454 err where to write error messages */
1455 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1460 memset (&expn, 0, sizeof (expn));
1461 expn.data = expn.dptr = line;
1462 expn.dsize = mutt_strlen (line);
1469 if (*expn.dptr == '#')
1470 break; /* rest of line is a comment */
1471 if (*expn.dptr == ';')
1476 mutt_extract_token (token, &expn, 0);
1477 for (i = 0; Commands[i].name; i++)
1479 if (!mutt_strcmp (token->data, Commands[i].name))
1481 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1486 if (!Commands[i].name)
1488 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1500 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1501 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1502 /* initial string that starts completion. No telling how much crap
1503 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1504 char User_typed [LONG_STRING] = {0};
1506 int Num_matched = 0; /* Number of matches for completion */
1507 char Completed [STRING] = {0}; /* completed string (command or variable) */
1508 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1510 /* helper function for completion. Changes the dest buffer if
1511 necessary/possible to aid completion.
1512 dest == completion result gets here.
1513 src == candidate for completion.
1514 try == user entered data for completion.
1515 len == length of dest buffer.
1517 static void candidate (char *dest, char *try, char *src, int len)
1521 if (strstr (src, try) == src)
1523 Matches[Num_matched++] = src;
1525 strfcpy (dest, src, len);
1528 for (l = 0; src[l] && src[l] == dest[l]; l++);
1534 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1538 int spaces; /* keep track of the number of leading spaces on the line */
1541 spaces = buffer - pt;
1543 pt = buffer + pos - spaces;
1544 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1547 if (pt == buffer) /* complete cmd */
1549 /* first TAB. Collect all the matches */
1553 strfcpy (User_typed, pt, sizeof (User_typed));
1554 memset (Matches, 0, sizeof (Matches));
1555 memset (Completed, 0, sizeof (Completed));
1556 for (num = 0; Commands[num].name; num++)
1557 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1558 Matches[Num_matched++] = User_typed;
1560 /* All matches are stored. Longest non-ambiguous string is ""
1561 * i.e. dont change 'buffer'. Fake successful return this time */
1562 if (User_typed[0] == 0)
1566 if (Completed[0] == 0 && User_typed[0])
1569 /* Num_matched will _always_ be atleast 1 since the initial
1570 * user-typed string is always stored */
1571 if (numtabs == 1 && Num_matched == 2)
1572 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1573 else if (numtabs > 1 && Num_matched > 2)
1574 /* cycle thru all the matches */
1575 snprintf(Completed, sizeof(Completed), "%s",
1576 Matches[(numtabs - 2) % Num_matched]);
1578 /* return the completed command */
1579 strncpy (buffer, Completed, len - spaces);
1581 else if (!mutt_strncmp (buffer, "set", 3)
1582 || !mutt_strncmp (buffer, "unset", 5)
1583 || !mutt_strncmp (buffer, "reset", 5)
1584 || !mutt_strncmp (buffer, "toggle", 6))
1585 { /* complete variables */
1586 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1589 /* loop through all the possible prefixes (no, inv, ...) */
1590 if (!mutt_strncmp (buffer, "set", 3))
1592 for (num = 0; prefixes[num]; num++)
1594 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1596 pt += mutt_strlen (prefixes[num]);
1602 /* first TAB. Collect all the matches */
1606 strfcpy (User_typed, pt, sizeof (User_typed));
1607 memset (Matches, 0, sizeof (Matches));
1608 memset (Completed, 0, sizeof (Completed));
1609 for (num = 0; MuttVars[num].option; num++)
1610 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1611 Matches[Num_matched++] = User_typed;
1613 /* All matches are stored. Longest non-ambiguous string is ""
1614 * i.e. dont change 'buffer'. Fake successful return this time */
1615 if (User_typed[0] == 0)
1619 if (Completed[0] == 0 && User_typed[0])
1622 /* Num_matched will _always_ be atleast 1 since the initial
1623 * user-typed string is always stored */
1624 if (numtabs == 1 && Num_matched == 2)
1625 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1626 else if (numtabs > 1 && Num_matched > 2)
1627 /* cycle thru all the matches */
1628 snprintf(Completed, sizeof(Completed), "%s",
1629 Matches[(numtabs - 2) % Num_matched]);
1631 strncpy (pt, Completed, buffer + len - pt - spaces);
1633 else if (!mutt_strncmp (buffer, "exec", 4))
1635 struct binding_t *menu = km_get_table (CurrentMenu);
1637 if (!menu && CurrentMenu != MENU_PAGER)
1641 /* first TAB. Collect all the matches */
1645 strfcpy (User_typed, pt, sizeof (User_typed));
1646 memset (Matches, 0, sizeof (Matches));
1647 memset (Completed, 0, sizeof (Completed));
1648 for (num = 0; menu[num].name; num++)
1649 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1650 /* try the generic menu */
1651 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
1654 for (num = 0; menu[num].name; num++)
1655 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1657 Matches[Num_matched++] = User_typed;
1659 /* All matches are stored. Longest non-ambiguous string is ""
1660 * i.e. dont change 'buffer'. Fake successful return this time */
1661 if (User_typed[0] == 0)
1665 if (Completed[0] == 0 && User_typed[0])
1668 /* Num_matched will _always_ be atleast 1 since the initial
1669 * user-typed string is always stored */
1670 if (numtabs == 1 && Num_matched == 2)
1671 snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1672 else if (numtabs > 1 && Num_matched > 2)
1673 /* cycle thru all the matches */
1674 snprintf(Completed, sizeof(Completed), "%s",
1675 Matches[(numtabs - 2) % Num_matched]);
1677 strncpy (pt, Completed, buffer + len - pt - spaces);
1685 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1687 char var[STRING], *pt = buffer;
1694 spaces = buffer - pt;
1696 pt = buffer + pos - spaces;
1697 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1699 pt++; /* move past the space */
1700 if (*pt == '=') /* abort if no var before the '=' */
1703 if (mutt_strncmp (buffer, "set", 3) == 0)
1706 strfcpy (var, pt, sizeof (var));
1707 /* ignore the trailing '=' when comparing */
1708 var[mutt_strlen (var) - 1] = 0;
1709 if ((idx = mutt_option_index (var)) == -1)
1710 return 0; /* no such variable. */
1713 char tmp [LONG_STRING], tmp2[LONG_STRING];
1715 size_t dlen = buffer + len - pt - spaces;
1716 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1720 if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
1721 (DTYPE(MuttVars[idx].type) == DT_PATH) ||
1722 (DTYPE(MuttVars[idx].type) == DT_RX))
1724 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1725 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1726 mutt_pretty_mailbox (tmp);
1728 else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1730 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1732 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1733 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1734 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1735 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1736 else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1738 const struct mapping_t *map;
1741 switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1744 map = SortAliasMethods;
1746 case DT_SORT_BROWSER:
1747 map = SortBrowserMethods;
1750 if ((WithCrypto & APPLICATION_PGP))
1751 map = SortKeyMethods;
1759 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1760 snprintf (tmp, sizeof (tmp), "%s%s%s",
1761 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1762 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1765 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1766 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
1770 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
1772 if (*s == '\\' || *s == '"')
1778 strfcpy (tmp, pt, sizeof (tmp));
1779 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1787 /* Implement the -Q command line flag */
1788 int mutt_query_variables (LIST *queries)
1792 char errbuff[STRING];
1793 char command[STRING];
1797 memset (&err, 0, sizeof (err));
1798 memset (&token, 0, sizeof (token));
1801 err.dsize = sizeof (errbuff);
1803 for (p = queries; p; p = p->next)
1805 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1806 if (mutt_parse_rc_line (command, &token, &err) == -1)
1808 fprintf (stderr, "%s\n", err.data);
1812 printf ("%s\n", err.data);
1819 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1823 for (i=0; map[i].name; i++)
1824 if (map[i].value == val)
1825 return (map[i].name);
1829 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
1833 for (i = 0; map[i].name; i++)
1834 if (ascii_strcasecmp (map[i].name, name) == 0)
1835 return (map[i].value);
1840 static void start_debug (void)
1844 char buf[_POSIX_PATH_MAX];
1845 char buf2[_POSIX_PATH_MAX];
1847 /* rotate the old debug logs */
1848 for (i=3; i>=0; i--)
1850 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
1851 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
1854 if ((debugfile = safe_fopen(buf, "w")) != NULL)
1857 setbuf (debugfile, NULL); /* don't buffer the debugging output! */
1858 fprintf (debugfile, "Mutt %s started at %s.\nDebugging at level %d.\n\n",
1859 MUTT_VERSION, asctime (localtime (&t)), debuglevel);
1864 static int mutt_execute_commands (LIST *p)
1867 char errstr[SHORT_STRING];
1869 memset (&err, 0, sizeof (err));
1871 err.dsize = sizeof (errstr);
1872 memset (&token, 0, sizeof (token));
1873 for (; p; p = p->next)
1875 if (mutt_parse_rc_line (p->data, &token, &err) != 0)
1877 fprintf (stderr, _("Error in command line: %s\n"), err.data);
1886 void mutt_init (int skip_sys_rc, LIST *commands)
1889 struct utsname utsname;
1890 char *p, buffer[STRING], error[STRING];
1891 int i, default_rc = 0, need_pause = 0;
1894 memset (&err, 0, sizeof (err));
1896 err.dsize = sizeof (error);
1899 * XXX - use something even more difficult to predict?
1901 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
1902 "\033]9;%ld\a", (long) time (NULL));
1904 /* on one of the systems I use, getcwd() does not return the same prefix
1905 as is listed in the passwd file */
1906 if ((p = getenv ("HOME")))
1907 Homedir = safe_strdup (p);
1909 /* Get some information about the user */
1910 if ((pw = getpwuid (getuid ())))
1914 Username = safe_strdup (pw->pw_name);
1916 Homedir = safe_strdup (pw->pw_dir);
1918 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
1919 Shell = safe_strdup (pw->pw_shell);
1926 fputs (_("unable to determine home directory"), stderr);
1929 if ((p = getenv ("USER")))
1930 Username = safe_strdup (p);
1934 fputs (_("unable to determine username"), stderr);
1937 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
1941 /* Start up debugging mode if requested */
1946 /* And about the host... */
1948 /* some systems report the FQDN instead of just the hostname */
1949 if ((p = strchr (utsname.nodename, '.')))
1951 Hostname = mutt_substrdup (utsname.nodename, p);
1953 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
1956 Hostname = safe_strdup (utsname.nodename);
1959 #define DOMAIN buffer
1960 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
1961 Fqdn = safe_strdup ("@");
1966 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
1967 sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
1970 Fqdn = safe_strdup(NONULL(Hostname));
1977 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
1980 fgets (buffer, sizeof (buffer), f);
1984 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
1986 NewsServer = safe_strdup (p);
1990 if ((p = getenv ("NNTPSERVER")))
1991 NewsServer = safe_strdup (p);
1994 if ((p = getenv ("MAIL")))
1995 Spoolfile = safe_strdup (p);
1996 else if ((p = getenv ("MAILDIR")))
1997 Spoolfile = safe_strdup (p);
2001 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2003 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2005 Spoolfile = safe_strdup (buffer);
2008 if ((p = getenv ("MAILCAPS")))
2009 MailcapPath = safe_strdup (p);
2012 /* Default search path from RFC1524 */
2013 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2016 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2018 p = getenv ("VISUAL");
2021 p = getenv ("EDITOR");
2025 Editor = safe_strdup (p);
2026 Visual = safe_strdup (p);
2028 if ((p = getenv ("REPLYTO")) != NULL)
2032 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2034 memset (&buf, 0, sizeof (buf));
2035 buf.data = buf.dptr = buffer;
2036 buf.dsize = mutt_strlen (buffer);
2038 memset (&token, 0, sizeof (token));
2039 parse_my_hdr (&token, &buf, 0, &err);
2043 if ((p = getenv ("EMAIL")) != NULL)
2044 From = rfc822_parse_adrlist (NULL, p);
2046 mutt_set_langinfo_charset ();
2047 mutt_set_charset (Charset);
2050 /* Set standard defaults */
2051 for (i = 0; MuttVars[i].option; i++)
2053 mutt_set_default (&MuttVars[i]);
2054 mutt_restore_default (&MuttVars[i]);
2057 CurrentMenu = MENU_MAIN;
2060 #ifndef LOCALES_HACK
2061 /* Do we have a locale definition? */
2062 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2063 ((p = getenv ("LANG")) != NULL && p[0]) ||
2064 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2065 set_option (OPTLOCALES);
2069 /* Unset suspend by default if we're the session leader */
2070 if (getsid(0) == getpid())
2071 unset_option (OPTSUSPEND);
2074 mutt_init_history ();
2083 * When changing the code which looks for a configuration file,
2084 * please also change the corresponding code in muttbug.sh.in.
2094 snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2095 if (access(buffer, F_OK) == -1)
2096 snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2097 if (access(buffer, F_OK) == -1)
2098 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2099 if (access(buffer, F_OK) == -1)
2100 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2103 Muttrc = safe_strdup (buffer);
2107 strfcpy (buffer, Muttrc, sizeof (buffer));
2109 mutt_expand_path (buffer, sizeof (buffer));
2110 Muttrc = safe_strdup (buffer);
2113 AliasFile = safe_strdup (NONULL(Muttrc));
2115 /* Process the global rc file if it exists and the user hasn't explicity
2116 requested not to via "-n". */
2119 snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2120 if (access (buffer, F_OK) == -1)
2121 snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2122 if (access (buffer, F_OK) == -1)
2123 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2124 if (access (buffer, F_OK) == -1)
2125 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2126 if (access (buffer, F_OK) != -1)
2128 if (source_rc (buffer, &err) != 0)
2130 fputs (err.data, stderr);
2131 fputc ('\n', stderr);
2137 /* Read the user's initialization file. */
2138 if (access (Muttrc, F_OK) != -1)
2140 if (!option (OPTNOCURSES))
2142 if (source_rc (Muttrc, &err) != 0)
2144 fputs (err.data, stderr);
2145 fputc ('\n', stderr);
2149 else if (!default_rc)
2151 /* file specified by -F does not exist */
2152 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2153 mutt_endwin (buffer);
2157 if (mutt_execute_commands (commands) != 0)
2160 if (need_pause && !option (OPTNOCURSES))
2162 if (mutt_any_key_to_continue (NULL) == -1)
2167 set_option (OPTWEED); /* turn weeding on by default */
2171 int mutt_get_hook_type (const char *name)
2173 struct command_t *c;
2175 for (c = Commands ; c->name ; c++)
2176 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2181 void mutt_start_slrnface(void)
2184 int pathlen, status;
2188 if (!option(OPTXFACE))
2192 * If we don't have display, there's no point. The user probably knows,
2195 if (!getenv("DISPLAY"))
2197 /* If there is no WINDOWID, complain. */
2198 if (!getenv ("WINDOWID"))
2200 mutt_error (_("Cannot run slrnface: WINDOWID not found in environment."));
2205 pathlen = strlen (Homedir) + sizeof("/.slrnfaces/")
2206 + strlen (u.nodename) + 30;
2207 fifo = safe_malloc (pathlen);
2208 sprintf (fifo, "%s/.slrnfaces", Homedir);
2209 if (mkdir (fifo, 0700))
2211 if (errno != EEXIST)
2213 mutt_error (_("Cannot run slrnface: failed to create %s: %s."),
2214 fifo, strerror(errno));
2222 /* We'll abuse fifo filename memory here. It's long enough. */
2223 sprintf (fifo, "%s/.slrnfaces/README", Homedir);
2224 if ((fp = fopen (fifo, "w")) != NULL)
2227 "This directory is used to create named pipes for communication between\n"
2228 "slrnface and its parent process. It should normally be empty because\n"
2229 "the pipe is deleted right after it has been opened by both processes.\n\n"
2230 "File names generated by slrnface have the form \"hostname.pid\". It is\n"
2231 "probably an error if they linger here longer than a fraction of a second.\n\n"
2232 "However, if the directory is mounted from an NFS server, you might see\n"
2233 "special files created by your NFS server while slrnface is running.\n"
2234 "Do not try to remove them.\n"), fp);
2239 status = snprintf (fifo, pathlen, "%s/.slrnfaces/%s.%ld", Homedir,
2240 u.nodename, (long)getpid());
2245 if (mkfifo (fifo, 0600) < 0)
2247 mutt_error (_("Cannot run slrnface, failed to create %s: %s."), fifo,
2256 case 0: execlp ("slrnface", "slrnface", fifo, (char *)0);
2257 /* This is child, exit on error. */
2260 pidst = waitpid (pid, &status, 0);
2261 } while (pidst == -1 && errno == EINTR);
2263 if (!WIFEXITED (status))
2264 mutt_error (_("Slrnface abnormaly exited, code %d."), status);
2269 switch (WEXITSTATUS (status))
2271 case 0: /* All fine, open the pipe */
2272 slrnface_fd = open (fifo, O_WRONLY, 0600);
2273 write (slrnface_fd, "start\n", sizeof "start");
2275 case 1: message = "couldn't connect to display";
2277 case 2: message = "WINDOWID not found in environment";
2279 case 3: message = "couldn't find controlling terminal";
2281 case 4: message = "terminal doesn't export width and height";
2283 case 5: message = "cannot open FIFO";
2285 case 6: message = "fork() failed";
2287 case 10: message = "executable not found";
2289 default: message = "unknown error";
2291 mutt_error (_("Slrnface failed: %s."), message);
2300 void mutt_stop_slrnface(void)
2302 if (slrnface_fd >= 0)
2306 /* FIFO has been unlinked in the startup function. */