2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
16 #include "mutt_curses.h"
21 #include "mutt_crypt.h"
22 #include "mutt_idna.h"
24 #if defined(USE_SSL) || defined(USE_GNUTLS)
36 #include "lib/debug.h"
42 #include <sys/utsname.h>
46 /* for synonym warning reports: synonym found during parsing */
50 int n; /* new name (index) */
51 int o; /* old name (index) */
54 /* for synonym warning reports: list of synonyms found */
56 /* for synonym warning reports: current rc file */
57 char* CurRCFile = NULL;
58 /* for synonym warning reports: current rc line */
61 /* for synonym warning reports: adds synonym to end of list */
62 static void syn_add (int n, int o) {
63 syn_t* tmp = safe_malloc (sizeof (syn_t));
64 tmp->f = safe_strdup (CurRCFile);
68 list_push_back (&Synonyms, tmp);
71 /* for synonym warning reports: free single item (for list_del()) */
72 static void syn_del (void** p) {
73 FREE(&(*(syn_t**) p)->f);
77 void toggle_quadoption (int opt)
80 int b = (opt % 4) * 2;
82 QuadOptions[n] ^= (1 << b);
85 void set_quadoption (int opt, int flag)
88 int b = (opt % 4) * 2;
90 QuadOptions[n] &= ~(0x3 << b);
91 QuadOptions[n] |= (flag & 0x3) << b;
94 int quadoption (int opt)
97 int b = (opt % 4) * 2;
99 return (QuadOptions[n] >> b) & 0x3;
102 int query_quadoption (int opt, const char *prompt)
104 int v = quadoption (opt);
112 v = mutt_yesorno (prompt, (v == M_ASKYES));
113 CLEARLINE (LINES - 1);
120 /* given the variable ``s'', return the index into the rc_vars array which
121 matches, or -1 if the variable is not found. */
122 int mutt_option_index (char *s)
126 for (i = 0; MuttVars[i].option; i++)
127 if (mutt_strcmp (s, MuttVars[i].option) == 0) {
128 if (MuttVars[i].type == DT_SYN)
129 syn_add (mutt_option_index ((char *) MuttVars[i].data), i);
130 return (MuttVars[i].type ==
131 DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i);
136 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
139 char qc = 0; /* quote char */
142 /* reset the destination pointer to the beginning of the buffer */
143 dest->dptr = dest->data;
146 while ((ch = *tok->dptr)) {
148 if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
149 (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
150 (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
151 (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
152 ((flags & M_TOKEN_PATTERN) && strchr ("~!|", ch)))
159 qc = 0; /* end of quote */
160 else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
162 else if (ch == '\\' && qc != '\'') {
164 return -1; /* premature end of token */
165 switch (ch = *tok->dptr++) {
169 return -1; /* premature end of token */
170 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
175 mutt_buffer_addch (dest, '\r');
178 mutt_buffer_addch (dest, '\n');
181 mutt_buffer_addch (dest, '\t');
184 mutt_buffer_addch (dest, '\f');
187 mutt_buffer_addch (dest, '\033');
190 if (isdigit ((unsigned char) ch) &&
191 isdigit ((unsigned char) *tok->dptr) &&
192 isdigit ((unsigned char) *(tok->dptr + 1))) {
194 mutt_buffer_addch (dest,
195 (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
200 mutt_buffer_addch (dest, ch);
203 else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
205 return -1; /* premature end of token */
208 mutt_buffer_addch (dest, ch);
210 mutt_buffer_addch (dest, '\033');
211 else if (isalpha ((unsigned char) ch))
212 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
214 mutt_buffer_addch (dest, '^');
215 mutt_buffer_addch (dest, ch);
218 else if (ch == '`' && (!qc || qc == '"')) {
228 if ((pc = strpbrk (pc, "\\`"))) {
229 /* skip any quoted chars */
233 } while (pc && *pc != '`');
235 debug_print (1, ("mismatched backtics\n"));
238 cmd = str_substrdup (tok->dptr, pc);
239 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) {
240 debug_print (1, ("unable to fork command: %s\n", cmd));
249 memset (&expn, 0, sizeof (expn));
250 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
252 mutt_wait_filter (pid);
254 /* if we got output, make a new string consiting of the shell ouptput
255 plus whatever else was left on the original line */
256 /* BUT: If this is inside a quoted string, directly add output to
258 if (expn.data && qc) {
259 mutt_buffer_addstr (dest, expn.data);
262 else if (expn.data) {
263 expnlen = mutt_strlen (expn.data);
264 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
265 ptr = safe_malloc (tok->dsize);
266 memcpy (ptr, expn.data, expnlen);
267 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */
272 tok->destroy = 1; /* mark that the caller should destroy this data */
277 else if (ch == '$' && (!qc || qc == '"')
278 && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
279 char *env = NULL, *var = NULL;
281 if (*tok->dptr == '{') {
283 if ((pc = strchr (tok->dptr, '}'))) {
284 var = str_substrdup (tok->dptr, pc);
289 for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
291 var = str_substrdup (tok->dptr, pc);
294 if (var && (env = getenv (var)))
295 mutt_buffer_addstr (dest, env);
299 mutt_buffer_addch (dest, ch);
301 mutt_buffer_addch (dest, 0); /* terminate the string */
306 static void add_to_list (LIST ** list, const char *str)
308 LIST *t, *last = NULL;
310 /* don't add a NULL or empty string to the list */
311 if (!str || *str == '\0')
314 /* check to make sure the item is not already on this list */
315 for (last = *list; last; last = last->next) {
316 if (ascii_strcasecmp (str, last->data) == 0) {
317 /* already on the list, so just ignore it */
325 if (!*list || last) {
326 t = (LIST *) safe_calloc (1, sizeof (LIST));
327 t->data = safe_strdup (str);
337 static int add_to_rx_list (list2_t** list, const char *s, int flags,
346 if (!(rx = rx_compile (s, flags))) {
347 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
351 i = rx_lookup ((*list), rx->pattern);
355 list_push_back (list, rx);
359 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
360 const char *templ, BUFFER * err)
362 SPAM_LIST *t = NULL, *last = NULL;
367 if (!pat || !*pat || !templ)
370 if (!(rx = rx_compile (pat, REG_ICASE))) {
371 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
375 /* check to make sure the item is not already on this list */
376 for (last = *list; last; last = last->next) {
377 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) {
378 /* Already on the list. Formerly we just skipped this case, but
379 * now we're supporting removals, which means we're supporting
380 * re-adds conceptually. So we probably want this to imply a
381 * removal, then do an add. We can achieve the removal by freeing
382 * the template, and leaving t pointed at the current item.
392 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
393 * update. Otherwise we want to make a new one to link at the list's end.
396 t = mutt_new_spam_list ();
404 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
405 t->template = safe_strdup (templ);
407 /* Find highest match number in template string */
409 for (p = templ; *p;) {
414 while (*p && isdigit ((int) *p))
420 t->nmatch++; /* match 0 is always the whole expr */
425 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
427 SPAM_LIST *spam, *prev;
430 /* Being first is a special case. */
434 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
437 FREE(&spam->template);
443 for (spam = prev->next; spam;) {
444 if (!mutt_strcmp (spam->rx->pattern, pat)) {
445 prev->next = spam->next;
447 FREE(spam->template);
460 static void remove_from_list (LIST ** l, const char *str)
462 LIST *p, *last = NULL;
464 if (mutt_strcmp ("*", str) == 0)
465 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
470 if (ascii_strcasecmp (str, p->data) == 0) {
473 last->next = p->next;
486 static int remove_from_rx_list (list2_t** l, const char *str)
490 if (mutt_strcmp ("*", str) == 0) {
491 list_del (l, (list_del_t*) rx_free);
495 i = rx_lookup ((*l), str);
497 rx_t* r = list_pop_idx ((*l), i);
505 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
511 memset (&token, 0, sizeof (token));
512 mutt_extract_token (tmp, s, 0);
514 /* is the item defined as a variable or a function? */
515 if (!(res = (mutt_option_index (tmp->data) != -1)))
516 for (i = 0; !res && i < MENU_MAX; i++) {
517 struct binding_t *b = km_get_table (Menus[i].value);
522 for (j = 0; b[j].name; j++)
523 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
524 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
529 /* check for feature_* */
534 j = mutt_strlen (tmp->data);
535 /* need at least input of 'feature_X' */
539 while (Features[i].name) {
540 if (mutt_strlen (Features[i].name) == j &&
541 ascii_strncasecmp (Features[i].name, p, j)) {
552 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
554 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
557 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
559 if ((data && res) || (!data && !res)) {
560 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
561 mutt_error ("Error: %s", err->data);
570 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
574 mutt_extract_token (buf, s, 0);
576 /* don't add "*" to the unignore list */
577 if (strcmp (buf->data, "*"))
578 add_to_list (&UnIgnore, buf->data);
580 remove_from_list (&Ignore, buf->data);
582 while (MoreArgs (s));
587 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
591 mutt_extract_token (buf, s, 0);
592 remove_from_list (&UnIgnore, buf->data);
593 add_to_list (&Ignore, buf->data);
595 while (MoreArgs (s));
600 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
604 mutt_extract_token (buf, s, 0);
605 add_to_list ((LIST **) data, buf->data);
607 while (MoreArgs (s));
612 static void _alternates_clean (void)
616 if (Context && Context->msgcount) {
617 for (i = 0; i < Context->msgcount; i++)
618 Context->hdrs[i]->recip_valid = 0;
622 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
625 _alternates_clean ();
627 mutt_extract_token (buf, s, 0);
628 remove_from_rx_list (&UnAlternates, buf->data);
630 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
633 while (MoreArgs (s));
638 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
641 _alternates_clean ();
643 mutt_extract_token (buf, s, 0);
644 remove_from_rx_list (&Alternates, buf->data);
646 if (mutt_strcmp (buf->data, "*") &&
647 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
651 while (MoreArgs (s));
656 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
661 memset (&templ, 0, sizeof (templ));
663 /* Insist on at least one parameter */
666 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
668 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
672 /* Extract the first token, a regexp */
673 mutt_extract_token (buf, s, 0);
675 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
676 if (data == M_SPAM) {
677 /* If there's a second parameter, it's a template for the spam tag. */
679 mutt_extract_token (&templ, s, 0);
681 /* Add to the spam list. */
682 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
689 /* If not, try to remove from the nospam list. */
691 remove_from_rx_list (&NoSpamList, buf->data);
697 /* M_NOSPAM is for nospam commands. */
698 else if (data == M_NOSPAM) {
699 /* nospam only ever has one parameter. */
701 /* "*" is a special case. */
702 if (!mutt_strcmp (buf->data, "*")) {
703 mutt_free_spam_list (&SpamList);
704 list_del (&NoSpamList, (list_del_t*) rx_free);
708 /* If it's on the spam list, just remove it. */
709 if (remove_from_spam_list (&SpamList, buf->data) != 0)
712 /* Otherwise, add it to the nospam list. */
713 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
719 /* This should not happen. */
720 strfcpy (err->data, "This is no good at all.", err->dsize);
724 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
728 mutt_extract_token (buf, s, 0);
730 * Check for deletion of entire list
732 if (mutt_strcmp (buf->data, "*") == 0) {
733 mutt_free_list ((LIST **) data);
736 remove_from_list ((LIST **) data, buf->data);
738 while (MoreArgs (s));
743 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
747 mutt_extract_token (buf, s, 0);
748 remove_from_rx_list (&UnMailLists, buf->data);
750 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
753 while (MoreArgs (s));
758 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
762 mutt_extract_token (buf, s, 0);
763 remove_from_rx_list (&SubscribedLists, buf->data);
764 remove_from_rx_list (&MailLists, buf->data);
766 if (mutt_strcmp (buf->data, "*") &&
767 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
770 while (MoreArgs (s));
775 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
779 mutt_extract_token (buf, s, 0);
780 remove_from_rx_list (&UnMailLists, buf->data);
781 remove_from_rx_list (&UnSubscribedLists, buf->data);
783 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
785 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
788 while (MoreArgs (s));
793 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
797 mutt_extract_token (buf, s, 0);
798 remove_from_rx_list (&SubscribedLists, buf->data);
800 if (mutt_strcmp (buf->data, "*") &&
801 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
804 while (MoreArgs (s));
809 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
812 ALIAS *tmp, *last = NULL;
815 mutt_extract_token (buf, s, 0);
817 if (mutt_strcmp ("*", buf->data) == 0) {
818 if (CurrentMenu == MENU_ALIAS) {
819 for (tmp = Aliases; tmp; tmp = tmp->next)
821 set_option (OPTFORCEREDRAWINDEX);
824 mutt_free_alias (&Aliases);
828 for (tmp = Aliases; tmp; tmp = tmp->next) {
829 if (safe_strcasecmp (buf->data, tmp->name) == 0) {
830 if (CurrentMenu == MENU_ALIAS) {
832 set_option (OPTFORCEREDRAWINDEX);
837 last->next = tmp->next;
841 mutt_free_alias (&tmp);
847 while (MoreArgs (s));
851 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
854 ALIAS *tmp = Aliases;
859 strfcpy (err->data, _("alias: no address"), err->dsize);
863 mutt_extract_token (buf, s, 0);
865 debug_print (2, ("first token is '%s'.\n", buf->data));
867 /* check to see if an alias with this name already exists */
868 for (; tmp; tmp = tmp->next) {
869 if (!safe_strcasecmp (tmp->name, buf->data))
875 /* create a new alias */
876 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
878 tmp->name = safe_strdup (buf->data);
879 /* give the main addressbook code a chance */
880 if (CurrentMenu == MENU_ALIAS)
881 set_option (OPTMENUCALLER);
884 /* override the previous value */
885 rfc822_free_address (&tmp->addr);
886 if (CurrentMenu == MENU_ALIAS)
887 set_option (OPTFORCEREDRAWINDEX);
890 mutt_extract_token (buf, s,
891 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
892 debug_print (2, ("second token is '%s'.\n", buf->data));
893 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
898 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
899 snprintf (err->data, err->dsize,
900 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
904 if (DebugLevel >= 2) {
907 for (a = tmp->addr; a; a = a->next) {
909 debug_print (2, ("%s\n", a->mailbox));
911 debug_print (2, ("group %s\n", a->mailbox));
919 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
922 LIST *tmp = UserHeader;
927 mutt_extract_token (buf, s, 0);
928 if (mutt_strcmp ("*", buf->data) == 0)
929 mutt_free_list (&UserHeader);
934 l = mutt_strlen (buf->data);
935 if (buf->data[l - 1] == ':')
939 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
940 && tmp->data[l] == ':') {
943 last->next = tmp->next;
945 UserHeader = tmp->next;
948 mutt_free_list (&ptr);
957 while (MoreArgs (s));
961 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
968 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
969 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
970 strfcpy (err->data, _("invalid header field"), err->dsize);
973 keylen = p - buf->data + 1;
976 for (tmp = UserHeader;; tmp = tmp->next) {
977 /* see if there is already a field by this name */
978 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
979 /* replace the old value */
981 tmp->data = buf->data;
982 memset (buf, 0, sizeof (BUFFER));
988 tmp->next = mutt_new_list ();
992 tmp = mutt_new_list ();
995 tmp->data = buf->data;
996 memset (buf, 0, sizeof (BUFFER));
1001 parse_sort (short *val, const char *s, const struct mapping_t *map,
1006 if (safe_strncmp ("reverse-", s, 8) == 0) {
1008 flags = SORT_REVERSE;
1011 if (safe_strncmp ("last-", s, 5) == 0) {
1016 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1017 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1026 static void mutt_set_default (struct option_t *p)
1028 switch (p->type & DT_MASK) {
1030 if (!p->init && *((char **) p->data))
1031 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1034 if (!p->init && *((char **) p->data)) {
1035 char *cp = safe_strdup (*((char **) p->data));
1037 /* mutt_pretty_mailbox (cp); */
1038 p->init = (unsigned long) cp;
1042 if (!p->init && *((ADDRESS **) p->data)) {
1043 char tmp[HUGE_STRING];
1046 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1047 p->init = (unsigned long) safe_strdup (tmp);
1052 rx_t* pp = (rx_t*) p->data;
1054 if (!p->init && pp->pattern)
1055 p->init = (unsigned long) safe_strdup (pp->pattern);
1061 static void mutt_restore_default (struct option_t *p)
1063 switch (p->type & DT_MASK) {
1066 str_replace ((char **) p->data, (char *) p->init);
1070 char path[_POSIX_PATH_MAX];
1072 strfcpy (path, (char *) p->init, sizeof (path));
1073 mutt_expand_path (path, sizeof (path));
1074 str_replace ((char **) p->data, path);
1079 rfc822_free_address ((ADDRESS **) p->data);
1080 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1085 set_option (p->data);
1087 unset_option (p->data);
1090 set_quadoption (p->data, p->init);
1095 *((short *) p->data) = p->init;
1099 rx_t *pp = (rx_t *) p->data;
1102 FREE (&pp->pattern);
1109 char *s = (char *) p->init;
1111 pp->rx = safe_calloc (1, sizeof (regex_t));
1112 if (mutt_strcmp (p->option, "mask") != 0)
1113 flags |= mutt_which_case ((const char *) p->init);
1114 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1118 if (REGCOMP (pp->rx, s, flags) != 0) {
1120 _("mutt_restore_default(%s): error in regexp: %s\n"),
1121 p->option, pp->pattern);
1122 FREE (&pp->pattern);
1127 str_replace (&pp->pattern, (char *) p->init);
1133 if (p->flags & R_INDEX)
1134 set_option (OPTFORCEREDRAWINDEX);
1135 if (p->flags & R_PAGER)
1136 set_option (OPTFORCEREDRAWPAGER);
1137 if (p->flags & R_RESORT_SUB)
1138 set_option (OPTSORTSUBTHREADS);
1139 if (p->flags & R_RESORT)
1140 set_option (OPTNEEDRESORT);
1141 if (p->flags & R_RESORT_INIT)
1142 set_option (OPTRESORTINIT);
1143 if (p->flags & R_TREE)
1144 set_option (OPTREDRAWTREE);
1147 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1150 int idx, query, unset, inv, reset, r = 0;
1151 char *p, scratch[_POSIX_PATH_MAX];
1153 while (MoreArgs (s)) {
1154 /* reset state variables */
1156 unset = data & M_SET_UNSET;
1157 inv = data & M_SET_INV;
1158 reset = data & M_SET_RESET;
1160 if (*s->dptr == '?') {
1164 else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1168 else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1172 else if (*s->dptr == '&') {
1177 /* get the variable name */
1178 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1180 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1181 !(reset && !mutt_strcmp ("all", tmp->data))) {
1182 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1188 if (query || unset || inv) {
1189 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1193 if (s && *s->dptr == '=') {
1194 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1198 if (!mutt_strcmp ("all", tmp->data)) {
1199 for (idx = 0; MuttVars[idx].option; idx++)
1200 mutt_restore_default (&MuttVars[idx]);
1204 mutt_restore_default (&MuttVars[idx]);
1206 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1207 if (s && *s->dptr == '=') {
1208 if (unset || inv || query) {
1209 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1214 mutt_extract_token (tmp, s, 0);
1215 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1217 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1220 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1226 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1227 ? _("%s is set") : _("%s is unset"), tmp->data);
1232 unset_option (MuttVars[idx].data);
1234 toggle_option (MuttVars[idx].data);
1236 set_option (MuttVars[idx].data);
1238 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1239 DTYPE (MuttVars[idx].type) == DT_PATH ||
1240 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1242 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1243 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1245 FREE ((void *) MuttVars[idx].data);
1247 else if (query || *s->dptr != '=') {
1251 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1253 rfc822_write_address (_tmp, sizeof (_tmp),
1254 *((ADDRESS **) MuttVars[idx].data), 0);
1258 val = *((char **) MuttVars[idx].data);
1260 /* user requested the value of this variable */
1261 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1268 /* copy the value of the string */
1269 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1270 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1272 FREE ((void *) MuttVars[idx].data);
1274 mutt_extract_token (tmp, s, 0);
1275 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1276 strfcpy (scratch, tmp->data, sizeof (scratch));
1277 mutt_expand_path (scratch, sizeof (scratch));
1278 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1280 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1281 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1282 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1283 mutt_set_charset (Charset);
1286 *((ADDRESS **) MuttVars[idx].data) =
1287 rfc822_parse_adrlist (NULL, tmp->data);
1291 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1292 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1296 if (query || *s->dptr != '=') {
1297 /* user requested the value of this variable */
1298 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1299 NONULL (ptr->pattern));
1303 if (option (OPTATTACHMSG)
1304 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1305 snprintf (err->data, err->dsize,
1306 "Operation not permitted when in attach-message mode.");
1313 /* copy the value of the string */
1314 mutt_extract_token (tmp, s, 0);
1316 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1319 /* $mask is case-sensitive */
1320 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1321 flags |= mutt_which_case (tmp->data);
1324 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1331 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1332 if ((e = REGCOMP (rx, p, flags)) != 0) {
1333 regerror (e, rx, err->data, err->dsize);
1339 /* get here only if everything went smootly */
1341 FREE (&ptr->pattern);
1342 regfree ((regex_t *) ptr->rx);
1346 ptr->pattern = safe_strdup (tmp->data);
1350 /* $reply_regexp and $alterantes require special treatment */
1352 if (Context && Context->msgcount &&
1353 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1354 regmatch_t pmatch[1];
1357 #define CUR_ENV Context->hdrs[i]->env
1358 for (i = 0; i < Context->msgcount; i++) {
1359 if (CUR_ENV && CUR_ENV->subject) {
1360 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1361 CUR_ENV->subject, 1, pmatch,
1363 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1370 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1371 if (query || *s->dptr != '=') {
1372 switch (DefaultMagic) {
1389 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1395 /* copy the value of the string */
1396 mutt_extract_token (tmp, s, 0);
1397 if (mx_set_magic (tmp->data)) {
1398 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1404 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1405 short *ptr = (short *) MuttVars[idx].data;
1409 if (query || *s->dptr != '=') {
1410 /* user requested the value of this variable */
1411 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1417 mutt_extract_token (tmp, s, 0);
1418 val = strtol (tmp->data, &t, 0);
1420 if (!*tmp->data || *t || (short) val != val) {
1421 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1428 /* these ones need a sanity check */
1429 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1432 mutt_init_history ();
1434 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1439 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1441 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1443 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1444 vals[quadoption (MuttVars[idx].data)]);
1448 if (*s->dptr == '=') {
1450 mutt_extract_token (tmp, s, 0);
1451 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1452 set_quadoption (MuttVars[idx].data, M_YES);
1453 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1454 set_quadoption (MuttVars[idx].data, M_NO);
1455 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1456 set_quadoption (MuttVars[idx].data, M_ASKYES);
1457 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1458 set_quadoption (MuttVars[idx].data, M_ASKNO);
1460 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1467 toggle_quadoption (MuttVars[idx].data);
1469 set_quadoption (MuttVars[idx].data, M_NO);
1471 set_quadoption (MuttVars[idx].data, M_YES);
1474 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1475 const struct mapping_t *map = NULL;
1477 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1479 map = SortAliasMethods;
1481 case DT_SORT_BROWSER:
1482 map = SortBrowserMethods;
1485 if ((WithCrypto & APPLICATION_PGP))
1486 map = SortKeyMethods;
1489 map = SortAuxMethods;
1497 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1498 MuttVars[idx].option);
1503 if (query || *s->dptr != '=') {
1505 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1508 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1509 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1511 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1516 mutt_extract_token (tmp, s, 0);
1518 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1525 snprintf (err->data, err->dsize, _("%s: unknown type"),
1526 MuttVars[idx].option);
1531 if (MuttVars[idx].flags & R_INDEX)
1532 set_option (OPTFORCEREDRAWINDEX);
1533 if (MuttVars[idx].flags & R_PAGER)
1534 set_option (OPTFORCEREDRAWPAGER);
1535 if (MuttVars[idx].flags & R_RESORT_SUB)
1536 set_option (OPTSORTSUBTHREADS);
1537 if (MuttVars[idx].flags & R_RESORT)
1538 set_option (OPTNEEDRESORT);
1539 if (MuttVars[idx].flags & R_RESORT_INIT)
1540 set_option (OPTRESORTINIT);
1541 if (MuttVars[idx].flags & R_TREE)
1542 set_option (OPTREDRAWTREE);
1549 /* reads the specified initialization file. returns -1 if errors were found
1550 so that we can pause to let the user know... */
1551 static int source_rc (const char *rcfile, BUFFER * err)
1554 int line = 0, rc = 0, conv = 0;
1556 char *linebuf = NULL;
1557 char *currentline = NULL;
1561 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1562 str_replace (&CurRCFile, rcfile);
1565 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1566 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1570 memset (&token, 0, sizeof (token));
1571 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1573 conv = ConfigCharset && (*ConfigCharset) && Charset;
1575 currentline = safe_strdup (linebuf);
1578 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1581 currentline = linebuf;
1583 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1584 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1585 if (--rc < -MAXERRS) {
1587 FREE (¤tline);
1596 FREE (¤tline);
1602 mutt_wait_filter (pid);
1604 /* the muttrc source keyword */
1605 snprintf (err->data, err->dsize,
1606 rc >= -MAXERRS ? _("source: errors in %s")
1607 : _("source: reading aborted due too many errors in %s"),
1616 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1619 char path[_POSIX_PATH_MAX];
1623 if (mutt_extract_token (tmp, s, 0) != 0) {
1624 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1628 strfcpy (path, tmp->data, sizeof (path));
1629 mutt_expand_path (path, sizeof (path));
1631 rc += source_rc (path, err);
1633 while (MoreArgs (s));
1635 return ((rc < 0) ? -1 : 0);
1638 /* line command to execute
1640 token scratch buffer to be used by parser. caller should free
1641 token->data when finished. the reason for this variable is
1642 to avoid having to allocate and deallocate a lot of memory
1643 if we are parsing many lines. the caller can pass in the
1644 memory to use, which avoids having to create new space for
1645 every call to this function.
1647 err where to write error messages */
1648 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1653 memset (&expn, 0, sizeof (expn));
1654 expn.data = expn.dptr = line;
1655 expn.dsize = mutt_strlen (line);
1660 while (*expn.dptr) {
1661 if (*expn.dptr == '#')
1662 break; /* rest of line is a comment */
1663 if (*expn.dptr == ';') {
1667 mutt_extract_token (token, &expn, 0);
1668 for (i = 0; Commands[i].name; i++) {
1669 if (!mutt_strcmp (token->data, Commands[i].name)) {
1670 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1675 if (!Commands[i].name) {
1676 snprintf (err->data, err->dsize, _("%s: unknown command"),
1677 NONULL (token->data));
1689 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1690 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1691 /* initial string that starts completion. No telling how much crap
1692 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1693 char User_typed[LONG_STRING] = { 0 };
1695 int Num_matched = 0; /* Number of matches for completion */
1696 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1697 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1699 /* helper function for completion. Changes the dest buffer if
1700 necessary/possible to aid completion.
1701 dest == completion result gets here.
1702 src == candidate for completion.
1703 try == user entered data for completion.
1704 len == length of dest buffer.
1706 static void candidate (char *dest, char *try, char *src, int len)
1710 if (strstr (src, try) == src) {
1711 Matches[Num_matched++] = src;
1713 strfcpy (dest, src, len);
1715 for (l = 0; src[l] && src[l] == dest[l]; l++);
1721 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1725 int spaces; /* keep track of the number of leading spaces on the line */
1728 spaces = buffer - pt;
1730 pt = buffer + pos - spaces;
1731 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1734 if (pt == buffer) { /* complete cmd */
1735 /* first TAB. Collect all the matches */
1738 strfcpy (User_typed, pt, sizeof (User_typed));
1739 memset (Matches, 0, sizeof (Matches));
1740 memset (Completed, 0, sizeof (Completed));
1741 for (num = 0; Commands[num].name; num++)
1742 candidate (Completed, User_typed, Commands[num].name,
1743 sizeof (Completed));
1744 Matches[Num_matched++] = User_typed;
1746 /* All matches are stored. Longest non-ambiguous string is ""
1747 * i.e. dont change 'buffer'. Fake successful return this time */
1748 if (User_typed[0] == 0)
1752 if (Completed[0] == 0 && User_typed[0])
1755 /* Num_matched will _always_ be atleast 1 since the initial
1756 * user-typed string is always stored */
1757 if (numtabs == 1 && Num_matched == 2)
1758 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1759 else if (numtabs > 1 && Num_matched > 2)
1760 /* cycle thru all the matches */
1761 snprintf (Completed, sizeof (Completed), "%s",
1762 Matches[(numtabs - 2) % Num_matched]);
1764 /* return the completed command */
1765 strncpy (buffer, Completed, len - spaces);
1767 else if (!safe_strncmp (buffer, "set", 3)
1768 || !safe_strncmp (buffer, "unset", 5)
1769 || !safe_strncmp (buffer, "reset", 5)
1770 || !safe_strncmp (buffer, "toggle", 6)) { /* complete variables */
1771 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1774 /* loop through all the possible prefixes (no, inv, ...) */
1775 if (!safe_strncmp (buffer, "set", 3)) {
1776 for (num = 0; prefixes[num]; num++) {
1777 if (!safe_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1778 pt += mutt_strlen (prefixes[num]);
1784 /* first TAB. Collect all the matches */
1787 strfcpy (User_typed, pt, sizeof (User_typed));
1788 memset (Matches, 0, sizeof (Matches));
1789 memset (Completed, 0, sizeof (Completed));
1790 for (num = 0; MuttVars[num].option; num++)
1791 candidate (Completed, User_typed, MuttVars[num].option,
1792 sizeof (Completed));
1793 Matches[Num_matched++] = User_typed;
1795 /* All matches are stored. Longest non-ambiguous string is ""
1796 * i.e. dont change 'buffer'. Fake successful return this time */
1797 if (User_typed[0] == 0)
1801 if (Completed[0] == 0 && User_typed[0])
1804 /* Num_matched will _always_ be atleast 1 since the initial
1805 * user-typed string is always stored */
1806 if (numtabs == 1 && Num_matched == 2)
1807 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1808 else if (numtabs > 1 && Num_matched > 2)
1809 /* cycle thru all the matches */
1810 snprintf (Completed, sizeof (Completed), "%s",
1811 Matches[(numtabs - 2) % Num_matched]);
1813 strncpy (pt, Completed, buffer + len - pt - spaces);
1815 else if (!safe_strncmp (buffer, "exec", 4)) {
1816 struct binding_t *menu = km_get_table (CurrentMenu);
1818 if (!menu && CurrentMenu != MENU_PAGER)
1822 /* first TAB. Collect all the matches */
1825 strfcpy (User_typed, pt, sizeof (User_typed));
1826 memset (Matches, 0, sizeof (Matches));
1827 memset (Completed, 0, sizeof (Completed));
1828 for (num = 0; menu[num].name; num++)
1829 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1830 /* try the generic menu */
1831 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1833 for (num = 0; menu[num].name; num++)
1834 candidate (Completed, User_typed, menu[num].name,
1835 sizeof (Completed));
1837 Matches[Num_matched++] = User_typed;
1839 /* All matches are stored. Longest non-ambiguous string is ""
1840 * i.e. dont change 'buffer'. Fake successful return this time */
1841 if (User_typed[0] == 0)
1845 if (Completed[0] == 0 && User_typed[0])
1848 /* Num_matched will _always_ be atleast 1 since the initial
1849 * user-typed string is always stored */
1850 if (numtabs == 1 && Num_matched == 2)
1851 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1852 else if (numtabs > 1 && Num_matched > 2)
1853 /* cycle thru all the matches */
1854 snprintf (Completed, sizeof (Completed), "%s",
1855 Matches[(numtabs - 2) % Num_matched]);
1857 strncpy (pt, Completed, buffer + len - pt - spaces);
1865 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1867 char var[STRING], *pt = buffer;
1874 spaces = buffer - pt;
1876 pt = buffer + pos - spaces;
1877 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1879 pt++; /* move past the space */
1880 if (*pt == '=') /* abort if no var before the '=' */
1883 if (safe_strncmp (buffer, "set", 3) == 0) {
1886 strfcpy (var, pt, sizeof (var));
1887 /* ignore the trailing '=' when comparing */
1888 var[mutt_strlen (var) - 1] = 0;
1889 if ((idx = mutt_option_index (var)) == -1)
1890 return 0; /* no such variable. */
1892 char tmp[LONG_STRING], tmp2[LONG_STRING];
1894 size_t dlen = buffer + len - pt - spaces;
1895 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1899 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1900 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1901 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1902 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1903 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1904 mutt_pretty_mailbox (tmp);
1906 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1907 rfc822_write_address (tmp, sizeof (tmp),
1908 *((ADDRESS **) MuttVars[idx].data), 0);
1910 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1911 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1912 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1913 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1914 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1915 const struct mapping_t *map;
1918 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1920 map = SortAliasMethods;
1922 case DT_SORT_BROWSER:
1923 map = SortBrowserMethods;
1926 if ((WithCrypto & APPLICATION_PGP))
1927 map = SortKeyMethods;
1936 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1938 snprintf (tmp, sizeof (tmp), "%s%s%s",
1939 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1941 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1944 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1946 switch (DefaultMagic) {
1962 strfcpy (tmp, p, sizeof (tmp));
1964 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1965 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1970 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1971 if (*s == '\\' || *s == '"')
1977 strfcpy (tmp, pt, sizeof (tmp));
1978 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1986 /* Implement the -Q command line flag */
1987 int mutt_query_variables (LIST * queries)
1991 char errbuff[STRING];
1992 char command[STRING];
1996 memset (&err, 0, sizeof (err));
1997 memset (&token, 0, sizeof (token));
2000 err.dsize = sizeof (errbuff);
2002 for (p = queries; p; p = p->next) {
2003 snprintf (command, sizeof (command), "set ?%s\n", p->data);
2004 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2005 fprintf (stderr, "%s\n", err.data);
2009 printf ("%s\n", err.data);
2016 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2020 for (i = 0; map[i].name; i++)
2021 if (map[i].value == val)
2022 return (map[i].name);
2026 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2030 for (i = 0; map[i].name; i++)
2031 if (ascii_strcasecmp (map[i].name, name) == 0)
2032 return (map[i].value);
2036 static int mutt_execute_commands (LIST * p)
2039 char errstr[SHORT_STRING];
2041 memset (&err, 0, sizeof (err));
2043 err.dsize = sizeof (errstr);
2044 memset (&token, 0, sizeof (token));
2045 for (; p; p = p->next) {
2046 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2047 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2056 void mutt_init (int skip_sys_rc, LIST * commands)
2059 struct utsname utsname;
2060 char *p, buffer[STRING], error[STRING];
2061 int i, default_rc = 0, need_pause = 0;
2064 memset (&err, 0, sizeof (err));
2066 err.dsize = sizeof (error);
2069 * XXX - use something even more difficult to predict?
2071 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2072 "\033]9;%ld\a", (long) time (NULL));
2074 /* on one of the systems I use, getcwd() does not return the same prefix
2075 as is listed in the passwd file */
2076 if ((p = getenv ("HOME")))
2077 Homedir = safe_strdup (p);
2079 /* Get some information about the user */
2080 if ((pw = getpwuid (getuid ()))) {
2083 Username = safe_strdup (pw->pw_name);
2085 Homedir = safe_strdup (pw->pw_dir);
2087 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2088 Shell = safe_strdup (pw->pw_shell);
2093 fputs (_("unable to determine home directory"), stderr);
2096 if ((p = getenv ("USER")))
2097 Username = safe_strdup (p);
2100 fputs (_("unable to determine username"), stderr);
2103 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2106 debug_start(Homedir);
2108 /* And about the host... */
2110 /* some systems report the FQDN instead of just the hostname */
2111 if ((p = strchr (utsname.nodename, '.'))) {
2112 Hostname = str_substrdup (utsname.nodename, p);
2114 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2117 Hostname = safe_strdup (utsname.nodename);
2120 #define DOMAIN buffer
2121 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2122 Fqdn = safe_strdup ("@");
2125 if (*DOMAIN != '@') {
2126 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2127 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2130 Fqdn = safe_strdup (NONULL (Hostname));
2137 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2139 fgets (buffer, sizeof (buffer), f);
2140 p = (char*) &buffer;
2143 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2147 NewsServer = safe_strdup (p);
2151 if ((p = getenv ("NNTPSERVER")))
2152 NewsServer = safe_strdup (p);
2155 if ((p = getenv ("MAIL")))
2156 Spoolfile = safe_strdup (p);
2157 else if ((p = getenv ("MAILDIR")))
2158 Spoolfile = safe_strdup (p);
2161 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2163 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2165 Spoolfile = safe_strdup (buffer);
2168 if ((p = getenv ("MAILCAPS")))
2169 MailcapPath = safe_strdup (p);
2171 /* Default search path from RFC1524 */
2173 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2174 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2177 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2179 p = getenv ("VISUAL");
2181 p = getenv ("EDITOR");
2185 Editor = safe_strdup (p);
2186 Visual = safe_strdup (p);
2188 if ((p = getenv ("REPLYTO")) != NULL) {
2191 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2193 memset (&buf, 0, sizeof (buf));
2194 buf.data = buf.dptr = buffer;
2195 buf.dsize = mutt_strlen (buffer);
2197 memset (&token, 0, sizeof (token));
2198 parse_my_hdr (&token, &buf, 0, &err);
2202 if ((p = getenv ("EMAIL")) != NULL)
2203 From = rfc822_parse_adrlist (NULL, p);
2205 mutt_set_langinfo_charset ();
2206 mutt_set_charset (Charset);
2209 /* Set standard defaults */
2210 for (i = 0; MuttVars[i].option; i++) {
2211 mutt_set_default (&MuttVars[i]);
2212 mutt_restore_default (&MuttVars[i]);
2215 CurrentMenu = MENU_MAIN;
2218 #ifndef LOCALES_HACK
2219 /* Do we have a locale definition? */
2220 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2221 ((p = getenv ("LANG")) != NULL && p[0]) ||
2222 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2223 set_option (OPTLOCALES);
2227 /* Unset suspend by default if we're the session leader */
2228 if (getsid (0) == getpid ())
2229 unset_option (OPTSUSPEND);
2232 mutt_init_history ();
2241 * When changing the code which looks for a configuration file,
2242 * please also change the corresponding code in muttbug.sh.in.
2251 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2253 if (access (buffer, F_OK) == -1)
2254 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2255 if (access (buffer, F_OK) == -1)
2256 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2257 NONULL (Homedir), MUTT_VERSION);
2258 if (access (buffer, F_OK) == -1)
2259 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2263 Muttrc = safe_strdup (buffer);
2266 strfcpy (buffer, Muttrc, sizeof (buffer));
2268 mutt_expand_path (buffer, sizeof (buffer));
2269 Muttrc = safe_strdup (buffer);
2272 AliasFile = safe_strdup (NONULL (Muttrc));
2274 /* Process the global rc file if it exists and the user hasn't explicity
2275 requested not to via "-n". */
2277 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2279 if (access (buffer, F_OK) == -1)
2280 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2281 if (access (buffer, F_OK) == -1)
2282 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2284 if (access (buffer, F_OK) == -1)
2285 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2286 if (access (buffer, F_OK) != -1) {
2287 if (source_rc (buffer, &err) != 0) {
2288 fputs (err.data, stderr);
2289 fputc ('\n', stderr);
2295 /* Read the user's initialization file. */
2296 if (access (Muttrc, F_OK) != -1) {
2297 if (!option (OPTNOCURSES))
2299 if (source_rc (Muttrc, &err) != 0) {
2300 fputs (err.data, stderr);
2301 fputc ('\n', stderr);
2305 else if (!default_rc) {
2306 /* file specified by -F does not exist */
2307 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2308 mutt_endwin (buffer);
2312 if (mutt_execute_commands (commands) != 0)
2315 /* warn about synonym variables */
2316 if (!list_empty(Synonyms)) {
2318 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2319 for (i = 0; i < Synonyms->length; i++)
2320 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2321 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2322 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2323 NONULL(((syn_t*) Synonyms->data[i])->f),
2324 ((syn_t*) Synonyms->data[i])->l);
2325 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2326 list_del (&Synonyms, syn_del);
2329 /* this is not needed during runtime */
2332 if (need_pause && !option (OPTNOCURSES)) {
2333 if (mutt_any_key_to_continue (NULL) == -1)
2338 set_option (OPTWEED); /* turn weeding on by default */
2342 int mutt_get_hook_type (const char *name)
2344 struct command_t *c;
2346 for (c = Commands; c->name; c++)
2347 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2352 static int opt_cmp (const void* a, const void* b) {
2353 return (mutt_strcmp ((*(struct option_t**) a)->option,
2354 (*(struct option_t**) b)->option));
2357 /* dump out the value of all the variables we have */
2358 int mutt_dump_variables (void) {
2361 char errbuff[STRING];
2362 char command[STRING];
2363 list2_t* tmp = NULL;
2367 memset (&err, 0, sizeof (err));
2368 memset (&token, 0, sizeof (token));
2371 err.dsize = sizeof (errbuff);
2373 /* get all non-synonyms into list... */
2374 for (i = 0; MuttVars[i].option; i++) {
2375 if (MuttVars[i].type == DT_SYN)
2377 list_push_back (&tmp, &MuttVars[i]);
2379 if (!list_empty(tmp)) {
2380 /* ...and dump list sorted */
2381 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2382 for (i = 0; i < tmp->length; i++) {
2383 snprintf (command, sizeof (command), "set ?%s\n",
2384 ((struct option_t*) tmp->data[i])->option);
2385 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2386 fprintf (stderr, "%s\n", err.data);
2388 list_del (&tmp, NULL);
2391 printf("%s\n", err.data);
2395 list_del (&tmp, NULL);