2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
16 #include "mutt_curses.h"
21 #include "mutt_crypt.h"
22 #include "mutt_idna.h"
24 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
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. */
432 if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
435 FREE(&spam->template);
441 for (spam = prev->next; spam;) {
442 if (!mutt_strcmp (spam->rx->pattern, pat)) {
443 prev->next = spam->next;
445 FREE(spam->template);
458 static void remove_from_list (LIST ** l, const char *str)
460 LIST *p, *last = NULL;
462 if (mutt_strcmp ("*", str) == 0)
463 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */
468 if (ascii_strcasecmp (str, p->data) == 0) {
471 last->next = p->next;
484 static int remove_from_rx_list (list2_t** l, const char *str)
488 if (mutt_strcmp ("*", str) == 0) {
489 list_del (l, (list_del_t*) rx_free);
493 i = rx_lookup ((*l), str);
495 rx_t* r = list_pop_idx ((*l), i);
503 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
509 memset (&token, 0, sizeof (token));
510 mutt_extract_token (tmp, s, 0);
512 /* is the item defined as a variable or a function? */
513 if (!(res = (mutt_option_index (tmp->data) != -1)))
514 for (i = 0; !res && i < MENU_MAX; i++) {
515 struct binding_t *b = km_get_table (Menus[i].value);
520 for (j = 0; b[j].name; j++)
521 if (!ascii_strncasecmp (tmp->data, b[j].name, mutt_strlen (tmp->data))
522 && (mutt_strlen (b[j].name) == mutt_strlen (tmp->data))) {
527 /* check for feature_* */
532 j = mutt_strlen (tmp->data);
533 /* need at least input of 'feature_X' */
537 while (Features[i].name) {
538 if (mutt_strlen (Features[i].name) == j &&
539 ascii_strncasecmp (Features[i].name, p, j)) {
550 snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
552 snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
555 mutt_extract_token (tmp, s, M_TOKEN_SPACE);
557 if ((data && res) || (!data && !res)) {
558 if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
559 mutt_error ("Error: %s", err->data);
568 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
572 mutt_extract_token (buf, s, 0);
574 /* don't add "*" to the unignore list */
575 if (strcmp (buf->data, "*"))
576 add_to_list (&UnIgnore, buf->data);
578 remove_from_list (&Ignore, buf->data);
580 while (MoreArgs (s));
585 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
589 mutt_extract_token (buf, s, 0);
590 remove_from_list (&UnIgnore, buf->data);
591 add_to_list (&Ignore, buf->data);
593 while (MoreArgs (s));
598 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
602 mutt_extract_token (buf, s, 0);
603 add_to_list ((LIST **) data, buf->data);
605 while (MoreArgs (s));
610 static void _alternates_clean (void)
614 if (Context && Context->msgcount) {
615 for (i = 0; i < Context->msgcount; i++)
616 Context->hdrs[i]->recip_valid = 0;
620 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
623 _alternates_clean ();
625 mutt_extract_token (buf, s, 0);
626 remove_from_rx_list (&UnAlternates, buf->data);
628 if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
631 while (MoreArgs (s));
636 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
639 _alternates_clean ();
641 mutt_extract_token (buf, s, 0);
642 remove_from_rx_list (&Alternates, buf->data);
644 if (mutt_strcmp (buf->data, "*") &&
645 add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
649 while (MoreArgs (s));
654 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
659 memset (&templ, 0, sizeof (templ));
661 /* Insist on at least one parameter */
664 strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
666 strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
670 /* Extract the first token, a regexp */
671 mutt_extract_token (buf, s, 0);
673 /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
674 if (data == M_SPAM) {
675 /* If there's a second parameter, it's a template for the spam tag. */
677 mutt_extract_token (&templ, s, 0);
679 /* Add to the spam list. */
680 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
687 /* If not, try to remove from the nospam list. */
689 remove_from_rx_list (&NoSpamList, buf->data);
695 /* M_NOSPAM is for nospam commands. */
696 else if (data == M_NOSPAM) {
697 /* nospam only ever has one parameter. */
699 /* "*" is a special case. */
700 if (!mutt_strcmp (buf->data, "*")) {
701 mutt_free_spam_list (&SpamList);
702 list_del (&NoSpamList, (list_del_t*) rx_free);
706 /* If it's on the spam list, just remove it. */
707 if (remove_from_spam_list (&SpamList, buf->data) != 0)
710 /* Otherwise, add it to the nospam list. */
711 if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
717 /* This should not happen. */
718 strfcpy (err->data, "This is no good at all.", err->dsize);
722 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
726 mutt_extract_token (buf, s, 0);
728 * Check for deletion of entire list
730 if (mutt_strcmp (buf->data, "*") == 0) {
731 mutt_free_list ((LIST **) data);
734 remove_from_list ((LIST **) data, buf->data);
736 while (MoreArgs (s));
741 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
745 mutt_extract_token (buf, s, 0);
746 remove_from_rx_list (&UnMailLists, buf->data);
748 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
751 while (MoreArgs (s));
756 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
760 mutt_extract_token (buf, s, 0);
761 remove_from_rx_list (&SubscribedLists, buf->data);
762 remove_from_rx_list (&MailLists, buf->data);
764 if (mutt_strcmp (buf->data, "*") &&
765 add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
768 while (MoreArgs (s));
773 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
777 mutt_extract_token (buf, s, 0);
778 remove_from_rx_list (&UnMailLists, buf->data);
779 remove_from_rx_list (&UnSubscribedLists, buf->data);
781 if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
783 if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
786 while (MoreArgs (s));
791 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
795 mutt_extract_token (buf, s, 0);
796 remove_from_rx_list (&SubscribedLists, buf->data);
798 if (mutt_strcmp (buf->data, "*") &&
799 add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
802 while (MoreArgs (s));
807 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
810 ALIAS *tmp, *last = NULL;
813 mutt_extract_token (buf, s, 0);
815 if (mutt_strcmp ("*", buf->data) == 0) {
816 if (CurrentMenu == MENU_ALIAS) {
817 for (tmp = Aliases; tmp; tmp = tmp->next)
819 set_option (OPTFORCEREDRAWINDEX);
822 mutt_free_alias (&Aliases);
826 for (tmp = Aliases; tmp; tmp = tmp->next) {
827 if (safe_strcasecmp (buf->data, tmp->name) == 0) {
828 if (CurrentMenu == MENU_ALIAS) {
830 set_option (OPTFORCEREDRAWINDEX);
835 last->next = tmp->next;
839 mutt_free_alias (&tmp);
845 while (MoreArgs (s));
849 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
852 ALIAS *tmp = Aliases;
857 strfcpy (err->data, _("alias: no address"), err->dsize);
861 mutt_extract_token (buf, s, 0);
863 debug_print (2, ("first token is '%s'.\n", buf->data));
865 /* check to see if an alias with this name already exists */
866 for (; tmp; tmp = tmp->next) {
867 if (!safe_strcasecmp (tmp->name, buf->data))
873 /* create a new alias */
874 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
876 tmp->name = safe_strdup (buf->data);
877 /* give the main addressbook code a chance */
878 if (CurrentMenu == MENU_ALIAS)
879 set_option (OPTMENUCALLER);
882 /* override the previous value */
883 rfc822_free_address (&tmp->addr);
884 if (CurrentMenu == MENU_ALIAS)
885 set_option (OPTFORCEREDRAWINDEX);
888 mutt_extract_token (buf, s,
889 M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
890 debug_print (2, ("second token is '%s'.\n", buf->data));
891 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
896 if (mutt_addrlist_to_idna (tmp->addr, &estr)) {
897 snprintf (err->data, err->dsize,
898 _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name);
902 if (DebugLevel >= 2) {
905 for (a = tmp->addr; a; a = a->next) {
907 debug_print (2, ("%s\n", a->mailbox));
909 debug_print (2, ("group %s\n", a->mailbox));
917 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
920 LIST *tmp = UserHeader;
925 mutt_extract_token (buf, s, 0);
926 if (mutt_strcmp ("*", buf->data) == 0)
927 mutt_free_list (&UserHeader);
932 l = mutt_strlen (buf->data);
933 if (buf->data[l - 1] == ':')
937 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
938 && tmp->data[l] == ':') {
941 last->next = tmp->next;
943 UserHeader = tmp->next;
946 mutt_free_list (&ptr);
955 while (MoreArgs (s));
959 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
966 mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
967 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') {
968 strfcpy (err->data, _("invalid header field"), err->dsize);
971 keylen = p - buf->data + 1;
974 for (tmp = UserHeader;; tmp = tmp->next) {
975 /* see if there is already a field by this name */
976 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) {
977 /* replace the old value */
979 tmp->data = buf->data;
980 memset (buf, 0, sizeof (BUFFER));
986 tmp->next = mutt_new_list ();
990 tmp = mutt_new_list ();
993 tmp->data = buf->data;
994 memset (buf, 0, sizeof (BUFFER));
999 parse_sort (short *val, const char *s, const struct mapping_t *map,
1004 if (safe_strncmp ("reverse-", s, 8) == 0) {
1006 flags = SORT_REVERSE;
1009 if (safe_strncmp ("last-", s, 5) == 0) {
1014 if ((i = mutt_getvaluebyname (s, map)) == -1) {
1015 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1024 static void mutt_set_default (struct option_t *p)
1026 switch (p->type & DT_MASK) {
1028 if (!p->init && *((char **) p->data))
1029 p->init = (unsigned long) safe_strdup (*((char **) p->data));
1032 if (!p->init && *((char **) p->data)) {
1033 char *cp = safe_strdup (*((char **) p->data));
1035 /* mutt_pretty_mailbox (cp); */
1036 p->init = (unsigned long) cp;
1040 if (!p->init && *((ADDRESS **) p->data)) {
1041 char tmp[HUGE_STRING];
1044 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1045 p->init = (unsigned long) safe_strdup (tmp);
1050 rx_t* pp = (rx_t*) p->data;
1052 if (!p->init && pp->pattern)
1053 p->init = (unsigned long) safe_strdup (pp->pattern);
1059 static void mutt_restore_default (struct option_t *p)
1061 switch (p->type & DT_MASK) {
1064 str_replace ((char **) p->data, (char *) p->init);
1068 char path[_POSIX_PATH_MAX];
1070 strfcpy (path, (char *) p->init, sizeof (path));
1071 mutt_expand_path (path, sizeof (path));
1072 str_replace ((char **) p->data, path);
1077 rfc822_free_address ((ADDRESS **) p->data);
1078 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1083 set_option (p->data);
1085 unset_option (p->data);
1088 set_quadoption (p->data, p->init);
1093 *((short *) p->data) = p->init;
1097 rx_t *pp = (rx_t *) p->data;
1100 FREE (&pp->pattern);
1107 char *s = (char *) p->init;
1109 pp->rx = safe_calloc (1, sizeof (regex_t));
1110 if (mutt_strcmp (p->option, "mask") != 0)
1111 flags |= mutt_which_case ((const char *) p->init);
1112 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') {
1116 if (REGCOMP (pp->rx, s, flags) != 0) {
1118 _("mutt_restore_default(%s): error in regexp: %s\n"),
1119 p->option, pp->pattern);
1120 FREE (&pp->pattern);
1125 str_replace (&pp->pattern, (char *) p->init);
1131 if (p->flags & R_INDEX)
1132 set_option (OPTFORCEREDRAWINDEX);
1133 if (p->flags & R_PAGER)
1134 set_option (OPTFORCEREDRAWPAGER);
1135 if (p->flags & R_RESORT_SUB)
1136 set_option (OPTSORTSUBTHREADS);
1137 if (p->flags & R_RESORT)
1138 set_option (OPTNEEDRESORT);
1139 if (p->flags & R_RESORT_INIT)
1140 set_option (OPTRESORTINIT);
1141 if (p->flags & R_TREE)
1142 set_option (OPTREDRAWTREE);
1145 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1148 int idx, query, unset, inv, reset, r = 0;
1149 char *p, scratch[_POSIX_PATH_MAX];
1151 while (MoreArgs (s)) {
1152 /* reset state variables */
1154 unset = data & M_SET_UNSET;
1155 inv = data & M_SET_INV;
1156 reset = data & M_SET_RESET;
1158 if (*s->dptr == '?') {
1162 else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1166 else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1170 else if (*s->dptr == '&') {
1175 /* get the variable name */
1176 mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1178 if ((idx = mutt_option_index (tmp->data)) == -1 &&
1179 !(reset && !mutt_strcmp ("all", tmp->data))) {
1180 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1186 if (query || unset || inv) {
1187 snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1191 if (s && *s->dptr == '=') {
1192 snprintf (err->data, err->dsize, _("value is illegal with reset"));
1196 if (!mutt_strcmp ("all", tmp->data)) {
1197 for (idx = 0; MuttVars[idx].option; idx++)
1198 mutt_restore_default (&MuttVars[idx]);
1202 mutt_restore_default (&MuttVars[idx]);
1204 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) {
1205 if (s && *s->dptr == '=') {
1206 if (unset || inv || query) {
1207 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1212 mutt_extract_token (tmp, s, 0);
1213 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1215 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1218 snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1224 snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1225 ? _("%s is set") : _("%s is unset"), tmp->data);
1230 unset_option (MuttVars[idx].data);
1232 toggle_option (MuttVars[idx].data);
1234 set_option (MuttVars[idx].data);
1236 else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1237 DTYPE (MuttVars[idx].type) == DT_PATH ||
1238 DTYPE (MuttVars[idx].type) == DT_ADDR) {
1240 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1241 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1243 FREE ((void *) MuttVars[idx].data);
1245 else if (query || *s->dptr != '=') {
1249 if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1251 rfc822_write_address (_tmp, sizeof (_tmp),
1252 *((ADDRESS **) MuttVars[idx].data), 0);
1256 val = *((char **) MuttVars[idx].data);
1258 /* user requested the value of this variable */
1259 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1266 /* copy the value of the string */
1267 if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1268 rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1270 FREE ((void *) MuttVars[idx].data);
1272 mutt_extract_token (tmp, s, 0);
1273 if (DTYPE (MuttVars[idx].type) == DT_PATH) {
1274 strfcpy (scratch, tmp->data, sizeof (scratch));
1275 mutt_expand_path (scratch, sizeof (scratch));
1276 *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1278 else if (DTYPE (MuttVars[idx].type) == DT_STR) {
1279 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1280 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1281 mutt_set_charset (Charset);
1284 *((ADDRESS **) MuttVars[idx].data) =
1285 rfc822_parse_adrlist (NULL, tmp->data);
1289 else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1290 rx_t *ptr = (rx_t *) MuttVars[idx].data;
1294 if (query || *s->dptr != '=') {
1295 /* user requested the value of this variable */
1296 snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1297 NONULL (ptr->pattern));
1301 if (option (OPTATTACHMSG)
1302 && !mutt_strcmp (MuttVars[idx].option, "reply_regexp")) {
1303 snprintf (err->data, err->dsize,
1304 "Operation not permitted when in attach-message mode.");
1311 /* copy the value of the string */
1312 mutt_extract_token (tmp, s, 0);
1314 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1317 /* $mask is case-sensitive */
1318 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1319 flags |= mutt_which_case (tmp->data);
1322 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1329 rx = (regex_t *) safe_malloc (sizeof (regex_t));
1330 if ((e = REGCOMP (rx, p, flags)) != 0) {
1331 regerror (e, rx, err->data, err->dsize);
1337 /* get here only if everything went smootly */
1339 FREE (&ptr->pattern);
1340 regfree ((regex_t *) ptr->rx);
1344 ptr->pattern = safe_strdup (tmp->data);
1348 /* $reply_regexp and $alterantes require special treatment */
1350 if (Context && Context->msgcount &&
1351 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1352 regmatch_t pmatch[1];
1355 #define CUR_ENV Context->hdrs[i]->env
1356 for (i = 0; i < Context->msgcount; i++) {
1357 if (CUR_ENV && CUR_ENV->subject) {
1358 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1359 CUR_ENV->subject, 1, pmatch,
1361 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1368 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1369 if (query || *s->dptr != '=') {
1370 switch (DefaultMagic) {
1387 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1393 /* copy the value of the string */
1394 mutt_extract_token (tmp, s, 0);
1395 if (mx_set_magic (tmp->data)) {
1396 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"),
1402 else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1403 short *ptr = (short *) MuttVars[idx].data;
1407 if (query || *s->dptr != '=') {
1408 /* user requested the value of this variable */
1409 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1415 mutt_extract_token (tmp, s, 0);
1416 val = strtol (tmp->data, &t, 0);
1418 if (!*tmp->data || *t || (short) val != val) {
1419 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1426 /* these ones need a sanity check */
1427 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1430 mutt_init_history ();
1432 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1437 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1439 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1441 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1442 vals[quadoption (MuttVars[idx].data)]);
1446 if (*s->dptr == '=') {
1448 mutt_extract_token (tmp, s, 0);
1449 if (ascii_strcasecmp ("yes", tmp->data) == 0)
1450 set_quadoption (MuttVars[idx].data, M_YES);
1451 else if (ascii_strcasecmp ("no", tmp->data) == 0)
1452 set_quadoption (MuttVars[idx].data, M_NO);
1453 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1454 set_quadoption (MuttVars[idx].data, M_ASKYES);
1455 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1456 set_quadoption (MuttVars[idx].data, M_ASKNO);
1458 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1465 toggle_quadoption (MuttVars[idx].data);
1467 set_quadoption (MuttVars[idx].data, M_NO);
1469 set_quadoption (MuttVars[idx].data, M_YES);
1472 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1473 const struct mapping_t *map = NULL;
1475 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1477 map = SortAliasMethods;
1479 case DT_SORT_BROWSER:
1480 map = SortBrowserMethods;
1483 if ((WithCrypto & APPLICATION_PGP))
1484 map = SortKeyMethods;
1487 map = SortAuxMethods;
1495 snprintf (err->data, err->dsize, _("%s: Unknown type."),
1496 MuttVars[idx].option);
1501 if (query || *s->dptr != '=') {
1503 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1506 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1507 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1509 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1514 mutt_extract_token (tmp, s, 0);
1516 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1523 snprintf (err->data, err->dsize, _("%s: unknown type"),
1524 MuttVars[idx].option);
1529 if (MuttVars[idx].flags & R_INDEX)
1530 set_option (OPTFORCEREDRAWINDEX);
1531 if (MuttVars[idx].flags & R_PAGER)
1532 set_option (OPTFORCEREDRAWPAGER);
1533 if (MuttVars[idx].flags & R_RESORT_SUB)
1534 set_option (OPTSORTSUBTHREADS);
1535 if (MuttVars[idx].flags & R_RESORT)
1536 set_option (OPTNEEDRESORT);
1537 if (MuttVars[idx].flags & R_RESORT_INIT)
1538 set_option (OPTRESORTINIT);
1539 if (MuttVars[idx].flags & R_TREE)
1540 set_option (OPTREDRAWTREE);
1547 /* reads the specified initialization file. returns -1 if errors were found
1548 so that we can pause to let the user know... */
1549 static int source_rc (const char *rcfile, BUFFER * err)
1552 int line = 0, rc = 0, conv = 0;
1554 char *linebuf = NULL;
1555 char *currentline = NULL;
1559 debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1560 str_replace (&CurRCFile, rcfile);
1563 if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1564 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1568 memset (&token, 0, sizeof (token));
1569 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1571 conv = ConfigCharset && (*ConfigCharset) && Charset;
1573 currentline = safe_strdup (linebuf);
1576 mutt_convert_string (¤tline, ConfigCharset, Charset, 0);
1579 currentline = linebuf;
1581 if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1582 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1583 if (--rc < -MAXERRS) {
1585 FREE (¤tline);
1594 FREE (¤tline);
1600 mutt_wait_filter (pid);
1602 /* the muttrc source keyword */
1603 snprintf (err->data, err->dsize,
1604 rc >= -MAXERRS ? _("source: errors in %s")
1605 : _("source: reading aborted due too many errors in %s"),
1614 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1617 char path[_POSIX_PATH_MAX];
1621 if (mutt_extract_token (tmp, s, 0) != 0) {
1622 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1626 strfcpy (path, tmp->data, sizeof (path));
1627 mutt_expand_path (path, sizeof (path));
1629 rc += source_rc (path, err);
1631 while (MoreArgs (s));
1633 return ((rc < 0) ? -1 : 0);
1636 /* line command to execute
1638 token scratch buffer to be used by parser. caller should free
1639 token->data when finished. the reason for this variable is
1640 to avoid having to allocate and deallocate a lot of memory
1641 if we are parsing many lines. the caller can pass in the
1642 memory to use, which avoids having to create new space for
1643 every call to this function.
1645 err where to write error messages */
1646 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1651 memset (&expn, 0, sizeof (expn));
1652 expn.data = expn.dptr = line;
1653 expn.dsize = mutt_strlen (line);
1658 while (*expn.dptr) {
1659 if (*expn.dptr == '#')
1660 break; /* rest of line is a comment */
1661 if (*expn.dptr == ';') {
1665 mutt_extract_token (token, &expn, 0);
1666 for (i = 0; Commands[i].name; i++) {
1667 if (!mutt_strcmp (token->data, Commands[i].name)) {
1668 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1673 if (!Commands[i].name) {
1674 snprintf (err->data, err->dsize, _("%s: unknown command"),
1675 NONULL (token->data));
1687 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1688 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1689 /* initial string that starts completion. No telling how much crap
1690 * the user has typed so far. Allocate LONG_STRING just to be sure! */
1691 char User_typed[LONG_STRING] = { 0 };
1693 int Num_matched = 0; /* Number of matches for completion */
1694 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1695 char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1697 /* helper function for completion. Changes the dest buffer if
1698 necessary/possible to aid completion.
1699 dest == completion result gets here.
1700 src == candidate for completion.
1701 try == user entered data for completion.
1702 len == length of dest buffer.
1704 static void candidate (char *dest, char *try, char *src, int len)
1708 if (strstr (src, try) == src) {
1709 Matches[Num_matched++] = src;
1711 strfcpy (dest, src, len);
1713 for (l = 0; src[l] && src[l] == dest[l]; l++);
1719 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1723 int spaces; /* keep track of the number of leading spaces on the line */
1726 spaces = buffer - pt;
1728 pt = buffer + pos - spaces;
1729 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1732 if (pt == buffer) { /* complete cmd */
1733 /* first TAB. Collect all the matches */
1736 strfcpy (User_typed, pt, sizeof (User_typed));
1737 memset (Matches, 0, sizeof (Matches));
1738 memset (Completed, 0, sizeof (Completed));
1739 for (num = 0; Commands[num].name; num++)
1740 candidate (Completed, User_typed, Commands[num].name,
1741 sizeof (Completed));
1742 Matches[Num_matched++] = User_typed;
1744 /* All matches are stored. Longest non-ambiguous string is ""
1745 * i.e. dont change 'buffer'. Fake successful return this time */
1746 if (User_typed[0] == 0)
1750 if (Completed[0] == 0 && User_typed[0])
1753 /* Num_matched will _always_ be atleast 1 since the initial
1754 * user-typed string is always stored */
1755 if (numtabs == 1 && Num_matched == 2)
1756 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1757 else if (numtabs > 1 && Num_matched > 2)
1758 /* cycle thru all the matches */
1759 snprintf (Completed, sizeof (Completed), "%s",
1760 Matches[(numtabs - 2) % Num_matched]);
1762 /* return the completed command */
1763 strncpy (buffer, Completed, len - spaces);
1765 else if (!safe_strncmp (buffer, "set", 3)
1766 || !safe_strncmp (buffer, "unset", 5)
1767 || !safe_strncmp (buffer, "reset", 5)
1768 || !safe_strncmp (buffer, "toggle", 6)) { /* complete variables */
1769 char *prefixes[] = { "no", "inv", "?", "&", 0 };
1772 /* loop through all the possible prefixes (no, inv, ...) */
1773 if (!safe_strncmp (buffer, "set", 3)) {
1774 for (num = 0; prefixes[num]; num++) {
1775 if (!safe_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) {
1776 pt += mutt_strlen (prefixes[num]);
1782 /* first TAB. Collect all the matches */
1785 strfcpy (User_typed, pt, sizeof (User_typed));
1786 memset (Matches, 0, sizeof (Matches));
1787 memset (Completed, 0, sizeof (Completed));
1788 for (num = 0; MuttVars[num].option; num++)
1789 candidate (Completed, User_typed, MuttVars[num].option,
1790 sizeof (Completed));
1791 Matches[Num_matched++] = User_typed;
1793 /* All matches are stored. Longest non-ambiguous string is ""
1794 * i.e. dont change 'buffer'. Fake successful return this time */
1795 if (User_typed[0] == 0)
1799 if (Completed[0] == 0 && User_typed[0])
1802 /* Num_matched will _always_ be atleast 1 since the initial
1803 * user-typed string is always stored */
1804 if (numtabs == 1 && Num_matched == 2)
1805 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1806 else if (numtabs > 1 && Num_matched > 2)
1807 /* cycle thru all the matches */
1808 snprintf (Completed, sizeof (Completed), "%s",
1809 Matches[(numtabs - 2) % Num_matched]);
1811 strncpy (pt, Completed, buffer + len - pt - spaces);
1813 else if (!safe_strncmp (buffer, "exec", 4)) {
1814 struct binding_t *menu = km_get_table (CurrentMenu);
1816 if (!menu && CurrentMenu != MENU_PAGER)
1820 /* first TAB. Collect all the matches */
1823 strfcpy (User_typed, pt, sizeof (User_typed));
1824 memset (Matches, 0, sizeof (Matches));
1825 memset (Completed, 0, sizeof (Completed));
1826 for (num = 0; menu[num].name; num++)
1827 candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
1828 /* try the generic menu */
1829 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1831 for (num = 0; menu[num].name; num++)
1832 candidate (Completed, User_typed, menu[num].name,
1833 sizeof (Completed));
1835 Matches[Num_matched++] = User_typed;
1837 /* All matches are stored. Longest non-ambiguous string is ""
1838 * i.e. dont change 'buffer'. Fake successful return this time */
1839 if (User_typed[0] == 0)
1843 if (Completed[0] == 0 && User_typed[0])
1846 /* Num_matched will _always_ be atleast 1 since the initial
1847 * user-typed string is always stored */
1848 if (numtabs == 1 && Num_matched == 2)
1849 snprintf (Completed, sizeof (Completed), "%s", Matches[0]);
1850 else if (numtabs > 1 && Num_matched > 2)
1851 /* cycle thru all the matches */
1852 snprintf (Completed, sizeof (Completed), "%s",
1853 Matches[(numtabs - 2) % Num_matched]);
1855 strncpy (pt, Completed, buffer + len - pt - spaces);
1863 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1865 char var[STRING], *pt = buffer;
1872 spaces = buffer - pt;
1874 pt = buffer + pos - spaces;
1875 while ((pt > buffer) && !isspace ((unsigned char) *pt))
1877 pt++; /* move past the space */
1878 if (*pt == '=') /* abort if no var before the '=' */
1881 if (safe_strncmp (buffer, "set", 3) == 0) {
1884 strfcpy (var, pt, sizeof (var));
1885 /* ignore the trailing '=' when comparing */
1886 var[mutt_strlen (var) - 1] = 0;
1887 if ((idx = mutt_option_index (var)) == -1)
1888 return 0; /* no such variable. */
1890 char tmp[LONG_STRING], tmp2[LONG_STRING];
1892 size_t dlen = buffer + len - pt - spaces;
1893 char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1897 if ((DTYPE (MuttVars[idx].type) == DT_STR) ||
1898 (DTYPE (MuttVars[idx].type) == DT_PATH) ||
1899 (DTYPE (MuttVars[idx].type) == DT_RX)) {
1900 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
1901 if (DTYPE (MuttVars[idx].type) == DT_PATH)
1902 mutt_pretty_mailbox (tmp);
1904 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1905 rfc822_write_address (tmp, sizeof (tmp),
1906 *((ADDRESS **) MuttVars[idx].data), 0);
1908 else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1909 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
1910 else if (DTYPE (MuttVars[idx].type) == DT_NUM)
1911 snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
1912 else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1913 const struct mapping_t *map;
1916 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1918 map = SortAliasMethods;
1920 case DT_SORT_BROWSER:
1921 map = SortBrowserMethods;
1924 if ((WithCrypto & APPLICATION_PGP))
1925 map = SortKeyMethods;
1934 mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1936 snprintf (tmp, sizeof (tmp), "%s%s%s",
1937 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1939 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1942 else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1943 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1948 for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1949 if (*s == '\\' || *s == '"')
1955 strfcpy (tmp, pt, sizeof (tmp));
1956 snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1964 /* Implement the -Q command line flag */
1965 int mutt_query_variables (LIST * queries)
1969 char errbuff[STRING];
1970 char command[STRING];
1974 memset (&err, 0, sizeof (err));
1975 memset (&token, 0, sizeof (token));
1978 err.dsize = sizeof (errbuff);
1980 for (p = queries; p; p = p->next) {
1981 snprintf (command, sizeof (command), "set ?%s\n", p->data);
1982 if (mutt_parse_rc_line (command, &token, &err) == -1) {
1983 fprintf (stderr, "%s\n", err.data);
1987 printf ("%s\n", err.data);
1994 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1998 for (i = 0; map[i].name; i++)
1999 if (map[i].value == val)
2000 return (map[i].name);
2004 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2008 for (i = 0; map[i].name; i++)
2009 if (ascii_strcasecmp (map[i].name, name) == 0)
2010 return (map[i].value);
2014 static int mutt_execute_commands (LIST * p)
2017 char errstr[SHORT_STRING];
2019 memset (&err, 0, sizeof (err));
2021 err.dsize = sizeof (errstr);
2022 memset (&token, 0, sizeof (token));
2023 for (; p; p = p->next) {
2024 if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2025 fprintf (stderr, _("Error in command line: %s\n"), err.data);
2034 void mutt_init (int skip_sys_rc, LIST * commands)
2037 struct utsname utsname;
2038 char *p, buffer[STRING], error[STRING];
2039 int i, default_rc = 0, need_pause = 0;
2042 memset (&err, 0, sizeof (err));
2044 err.dsize = sizeof (error);
2047 * XXX - use something even more difficult to predict?
2049 snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2050 "\033]9;%ld\a", (long) time (NULL));
2052 /* on one of the systems I use, getcwd() does not return the same prefix
2053 as is listed in the passwd file */
2054 if ((p = getenv ("HOME")))
2055 Homedir = safe_strdup (p);
2057 /* Get some information about the user */
2058 if ((pw = getpwuid (getuid ()))) {
2061 Username = safe_strdup (pw->pw_name);
2063 Homedir = safe_strdup (pw->pw_dir);
2065 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2066 Shell = safe_strdup (pw->pw_shell);
2071 fputs (_("unable to determine home directory"), stderr);
2074 if ((p = getenv ("USER")))
2075 Username = safe_strdup (p);
2078 fputs (_("unable to determine username"), stderr);
2081 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2084 debug_start(Homedir);
2086 /* And about the host... */
2088 /* some systems report the FQDN instead of just the hostname */
2089 if ((p = strchr (utsname.nodename, '.'))) {
2090 Hostname = str_substrdup (utsname.nodename, p);
2092 strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2095 Hostname = safe_strdup (utsname.nodename);
2098 #define DOMAIN buffer
2099 if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2100 Fqdn = safe_strdup ("@");
2103 if (*DOMAIN != '@') {
2104 Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2105 sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2108 Fqdn = safe_strdup (NONULL (Hostname));
2115 if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2117 fgets (buffer, sizeof (buffer), f);
2118 p = (char*) &buffer;
2121 while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2125 NewsServer = safe_strdup (p);
2129 if ((p = getenv ("NNTPSERVER")))
2130 NewsServer = safe_strdup (p);
2133 if ((p = getenv ("MAIL")))
2134 Spoolfile = safe_strdup (p);
2135 else if ((p = getenv ("MAILDIR")))
2136 Spoolfile = safe_strdup (p);
2139 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2141 mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2143 Spoolfile = safe_strdup (buffer);
2146 if ((p = getenv ("MAILCAPS")))
2147 MailcapPath = safe_strdup (p);
2149 /* Default search path from RFC1524 */
2151 safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2152 "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2155 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2157 p = getenv ("VISUAL");
2159 p = getenv ("EDITOR");
2163 Editor = safe_strdup (p);
2164 Visual = safe_strdup (p);
2166 if ((p = getenv ("REPLYTO")) != NULL) {
2169 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2171 memset (&buf, 0, sizeof (buf));
2172 buf.data = buf.dptr = buffer;
2173 buf.dsize = mutt_strlen (buffer);
2175 memset (&token, 0, sizeof (token));
2176 parse_my_hdr (&token, &buf, 0, &err);
2180 if ((p = getenv ("EMAIL")) != NULL)
2181 From = rfc822_parse_adrlist (NULL, p);
2183 mutt_set_langinfo_charset ();
2184 mutt_set_charset (Charset);
2187 /* Set standard defaults */
2188 for (i = 0; MuttVars[i].option; i++) {
2189 mutt_set_default (&MuttVars[i]);
2190 mutt_restore_default (&MuttVars[i]);
2193 CurrentMenu = MENU_MAIN;
2196 #ifndef LOCALES_HACK
2197 /* Do we have a locale definition? */
2198 if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2199 ((p = getenv ("LANG")) != NULL && p[0]) ||
2200 ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2201 set_option (OPTLOCALES);
2205 /* Unset suspend by default if we're the session leader */
2206 if (getsid (0) == getpid ())
2207 unset_option (OPTSUSPEND);
2210 mutt_init_history ();
2219 * When changing the code which looks for a configuration file,
2220 * please also change the corresponding code in muttbug.sh.in.
2229 snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2231 if (access (buffer, F_OK) == -1)
2232 snprintf (buffer, sizeof (buffer), "%s/.muttngrc", NONULL (Homedir));
2233 if (access (buffer, F_OK) == -1)
2234 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s",
2235 NONULL (Homedir), MUTT_VERSION);
2236 if (access (buffer, F_OK) == -1)
2237 snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc",
2241 Muttrc = safe_strdup (buffer);
2244 strfcpy (buffer, Muttrc, sizeof (buffer));
2246 mutt_expand_path (buffer, sizeof (buffer));
2247 Muttrc = safe_strdup (buffer);
2250 AliasFile = safe_strdup (NONULL (Muttrc));
2252 /* Process the global rc file if it exists and the user hasn't explicity
2253 requested not to via "-n". */
2255 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2257 if (access (buffer, F_OK) == -1)
2258 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", SYSCONFDIR);
2259 if (access (buffer, F_OK) == -1)
2260 snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR,
2262 if (access (buffer, F_OK) == -1)
2263 snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2264 if (access (buffer, F_OK) != -1) {
2265 if (source_rc (buffer, &err) != 0) {
2266 fputs (err.data, stderr);
2267 fputc ('\n', stderr);
2273 /* Read the user's initialization file. */
2274 if (access (Muttrc, F_OK) != -1) {
2275 if (!option (OPTNOCURSES))
2277 if (source_rc (Muttrc, &err) != 0) {
2278 fputs (err.data, stderr);
2279 fputc ('\n', stderr);
2283 else if (!default_rc) {
2284 /* file specified by -F does not exist */
2285 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2286 mutt_endwin (buffer);
2290 if (mutt_execute_commands (commands) != 0)
2293 /* warn about synonym variables */
2294 if (!list_empty(Synonyms)) {
2296 fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2297 for (i = 0; i < Synonyms->length; i++)
2298 fprintf (stderr, "$%s ($%s should be used) (%s:%d)\n",
2299 MuttVars[((syn_t*) Synonyms->data[i])->o].option,
2300 MuttVars[((syn_t*) Synonyms->data[i])->n].option,
2301 NONULL(((syn_t*) Synonyms->data[i])->f),
2302 ((syn_t*) Synonyms->data[i])->l);
2303 fprintf (stderr, _("Warning: Synonym variables are scheduled for removal.\n"));
2304 list_del (&Synonyms, syn_del);
2307 /* this is not needed during runtime */
2310 if (need_pause && !option (OPTNOCURSES)) {
2311 if (mutt_any_key_to_continue (NULL) == -1)
2316 set_option (OPTWEED); /* turn weeding on by default */
2320 int mutt_get_hook_type (const char *name)
2322 struct command_t *c;
2324 for (c = Commands; c->name; c++)
2325 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2330 static int opt_cmp (const void* a, const void* b) {
2331 return (mutt_strcmp ((*(struct option_t**) a)->option,
2332 (*(struct option_t**) b)->option));
2335 /* dump out the value of all the variables we have */
2336 int mutt_dump_variables (void) {
2339 char errbuff[STRING];
2340 char command[STRING];
2341 list2_t* tmp = NULL;
2345 memset (&err, 0, sizeof (err));
2346 memset (&token, 0, sizeof (token));
2349 err.dsize = sizeof (errbuff);
2351 /* get all non-synonyms into list... */
2352 for (i = 0; MuttVars[i].option; i++) {
2353 if (MuttVars[i].type == DT_SYN)
2355 list_push_back (&tmp, &MuttVars[i]);
2357 if (!list_empty(tmp)) {
2358 /* ...and dump list sorted */
2359 qsort (tmp->data, tmp->length, sizeof (void*), opt_cmp);
2360 for (i = 0; i < tmp->length; i++) {
2361 snprintf (command, sizeof (command), "set ?%s\n",
2362 ((struct option_t*) tmp->data[i])->option);
2363 if (mutt_parse_rc_line (command, &token, &err) == -1) {
2364 fprintf (stderr, "%s\n", err.data);
2366 list_del (&tmp, NULL);
2369 printf("%s\n", err.data);
2373 list_del (&tmp, NULL);