Rocco Rutte:
[apps/madmutt.git] / init.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
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.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #include "mapping.h"
16 #include "mutt_curses.h"
17 #include "history.h"
18 #include "keymap.h"
19 #include "mbyte.h"
20 #include "charset.h"
21 #include "mutt_crypt.h"
22 #include "mutt_idna.h"
23
24 #if defined(USE_SSL) || defined(USE_NSS) || defined(USE_GNUTLS)
25 #include "mutt_ssl.h"
26 #endif
27
28 #include "mx.h"
29 #include "init.h"
30
31 #include "lib/mem.h"
32 #include "lib/intl.h"
33 #include "lib/str.h"
34 #include "lib/rx.h"
35 #include "lib/list.h"
36 #include "lib/debug.h"
37
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <sys/utsname.h>
43 #include <errno.h>
44 #include <sys/wait.h>
45
46 /* for synonym warning reports: synonym found during parsing */
47 typedef struct {
48   char* f;      /* file */
49   int l;        /* line */
50   int n;        /* new name (index) */
51   int o;        /* old name (index) */
52 } syn_t;
53
54 /* for synonym warning reports: list of synonyms found */
55 list2_t* Synonyms;
56 /* for synonym warning reports: current rc file */
57 char* CurRCFile = NULL;
58 /* for synonym warning reports: current rc line */
59 int CurRCLine = 0;
60
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);
65   tmp->l = CurRCLine;
66   tmp->n = n;
67   tmp->o = o;
68   list_push_back (&Synonyms, tmp);
69 }
70
71 /* for synonym warning reports: free single item (for list_del()) */
72 static void syn_del (void** p) {
73   FREE(&(*(syn_t**) p)->f);
74   FREE(p);
75 }
76
77 void toggle_quadoption (int opt)
78 {
79   int n = opt / 4;
80   int b = (opt % 4) * 2;
81
82   QuadOptions[n] ^= (1 << b);
83 }
84
85 void set_quadoption (int opt, int flag)
86 {
87   int n = opt / 4;
88   int b = (opt % 4) * 2;
89
90   QuadOptions[n] &= ~(0x3 << b);
91   QuadOptions[n] |= (flag & 0x3) << b;
92 }
93
94 int quadoption (int opt)
95 {
96   int n = opt / 4;
97   int b = (opt % 4) * 2;
98
99   return (QuadOptions[n] >> b) & 0x3;
100 }
101
102 int query_quadoption (int opt, const char *prompt)
103 {
104   int v = quadoption (opt);
105
106   switch (v) {
107   case M_YES:
108   case M_NO:
109     return (v);
110
111   default:
112     v = mutt_yesorno (prompt, (v == M_ASKYES));
113     CLEARLINE (LINES - 1);
114     return (v);
115   }
116
117   /* not reached */
118 }
119
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)
123 {
124   int i;
125
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);
132     }
133   return (-1);
134 }
135
136 int mutt_extract_token (BUFFER * dest, BUFFER * tok, int flags)
137 {
138   char ch;
139   char qc = 0;                  /* quote char */
140   char *pc;
141
142   /* reset the destination pointer to the beginning of the buffer */
143   dest->dptr = dest->data;
144
145   SKIPWS (tok->dptr);
146   while ((ch = *tok->dptr)) {
147     if (!qc) {
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)))
153         break;
154     }
155
156     tok->dptr++;
157
158     if (ch == qc)
159       qc = 0;                   /* end of quote */
160     else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
161       qc = ch;
162     else if (ch == '\\' && qc != '\'') {
163       if (!*tok->dptr)
164         return -1;              /* premature end of token */
165       switch (ch = *tok->dptr++) {
166       case 'c':
167       case 'C':
168         if (!*tok->dptr)
169           return -1;            /* premature end of token */
170         mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
171                                   - '@') & 0x7f);
172         tok->dptr++;
173         break;
174       case 'r':
175         mutt_buffer_addch (dest, '\r');
176         break;
177       case 'n':
178         mutt_buffer_addch (dest, '\n');
179         break;
180       case 't':
181         mutt_buffer_addch (dest, '\t');
182         break;
183       case 'f':
184         mutt_buffer_addch (dest, '\f');
185         break;
186       case 'e':
187         mutt_buffer_addch (dest, '\033');
188         break;
189       default:
190         if (isdigit ((unsigned char) ch) &&
191             isdigit ((unsigned char) *tok->dptr) &&
192             isdigit ((unsigned char) *(tok->dptr + 1))) {
193
194           mutt_buffer_addch (dest,
195                              (ch << 6) + (*tok->dptr << 3) + *(tok->dptr +
196                                                                1) - 3504);
197           tok->dptr += 2;
198         }
199         else
200           mutt_buffer_addch (dest, ch);
201       }
202     }
203     else if (ch == '^' && (flags & M_TOKEN_CONDENSE)) {
204       if (!*tok->dptr)
205         return -1;              /* premature end of token */
206       ch = *tok->dptr++;
207       if (ch == '^')
208         mutt_buffer_addch (dest, ch);
209       else if (ch == '[')
210         mutt_buffer_addch (dest, '\033');
211       else if (isalpha ((unsigned char) ch))
212         mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
213       else {
214         mutt_buffer_addch (dest, '^');
215         mutt_buffer_addch (dest, ch);
216       }
217     }
218     else if (ch == '`' && (!qc || qc == '"')) {
219       FILE *fp;
220       pid_t pid;
221       char *cmd, *ptr;
222       size_t expnlen;
223       BUFFER expn;
224       int line = 0;
225
226       pc = tok->dptr;
227       do {
228         if ((pc = strpbrk (pc, "\\`"))) {
229           /* skip any quoted chars */
230           if (*pc == '\\')
231             pc += 2;
232         }
233       } while (pc && *pc != '`');
234       if (!pc) {
235         debug_print (1, ("mismatched backtics\n"));
236         return (-1);
237       }
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));
241         FREE (&cmd);
242         return (-1);
243       }
244       FREE (&cmd);
245
246       tok->dptr = pc + 1;
247
248       /* read line */
249       memset (&expn, 0, sizeof (expn));
250       expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line);
251       fclose (fp);
252       mutt_wait_filter (pid);
253
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 
257        * the token */
258       if (expn.data && qc) {
259         mutt_buffer_addstr (dest, expn.data);
260         FREE (&expn.data);
261       }
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__ */
268         if (tok->destroy)
269           FREE (&tok->data);
270         tok->data = ptr;
271         tok->dptr = ptr;
272         tok->destroy = 1;       /* mark that the caller should destroy this data */
273         ptr = NULL;
274         FREE (&expn.data);
275       }
276     }
277     else if (ch == '$' && (!qc || qc == '"')
278              && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) {
279       char *env = NULL, *var = NULL;
280
281       if (*tok->dptr == '{') {
282         tok->dptr++;
283         if ((pc = strchr (tok->dptr, '}'))) {
284           var = str_substrdup (tok->dptr, pc);
285           tok->dptr = pc + 1;
286         }
287       }
288       else {
289         for (pc = tok->dptr; isalpha ((unsigned char) *pc) || *pc == '_';
290              pc++);
291         var = str_substrdup (tok->dptr, pc);
292         tok->dptr = pc;
293       }
294       if (var && (env = getenv (var)))
295         mutt_buffer_addstr (dest, env);
296       FREE (&var);
297     }
298     else
299       mutt_buffer_addch (dest, ch);
300   }
301   mutt_buffer_addch (dest, 0);  /* terminate the string */
302   SKIPWS (tok->dptr);
303   return 0;
304 }
305
306 static void add_to_list (LIST ** list, const char *str)
307 {
308   LIST *t, *last = NULL;
309
310   /* don't add a NULL or empty string to the list */
311   if (!str || *str == '\0')
312     return;
313
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 */
318       last = NULL;
319       break;
320     }
321     if (!last->next)
322       break;
323   }
324
325   if (!*list || last) {
326     t = (LIST *) safe_calloc (1, sizeof (LIST));
327     t->data = safe_strdup (str);
328     if (last) {
329       last->next = t;
330       last = last->next;
331     }
332     else
333       *list = last = t;
334   }
335 }
336
337 static int add_to_rx_list (list2_t** list, const char *s, int flags,
338                            BUFFER * err)
339 {
340   rx_t* rx;
341   int i = 0;
342
343   if (!s || !*s)
344     return 0;
345
346   if (!(rx = rx_compile (s, flags))) {
347     snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
348     return -1;
349   }
350
351   i = rx_lookup ((*list), rx->pattern);
352   if (i >= 0)
353     rx_free (&rx);
354   else
355     list_push_back (list, rx);
356   return 0;
357 }
358
359 static int add_to_spam_list (SPAM_LIST ** list, const char *pat,
360                              const char *templ, BUFFER * err)
361 {
362   SPAM_LIST *t = NULL, *last = NULL;
363   rx_t* rx;
364   int n;
365   const char *p;
366
367   if (!pat || !*pat || !templ)
368     return 0;
369
370   if (!(rx = rx_compile (pat, REG_ICASE))) {
371     snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
372     return -1;
373   }
374
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.
383        */
384       t = last;
385       FREE(t->template);
386       break;
387     }
388     if (!last->next)
389       break;
390   }
391
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.
394    */
395   if (!t) {
396     t = mutt_new_spam_list ();
397     t->rx = rx;
398     if (last)
399       last->next = t;
400     else
401       *list = t;
402   }
403
404   /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
405   t->template = safe_strdup (templ);
406
407   /* Find highest match number in template string */
408   t->nmatch = 0;
409   for (p = templ; *p;) {
410     if (*p == '%') {
411       n = atoi (++p);
412       if (n > t->nmatch)
413         t->nmatch = n;
414       while (*p && isdigit ((int) *p))
415         ++p;
416     }
417     else
418       ++p;
419   }
420   t->nmatch++;                  /* match 0 is always the whole expr */
421
422   return 0;
423 }
424
425 static int remove_from_spam_list (SPAM_LIST ** list, const char *pat)
426 {
427   SPAM_LIST *spam, *prev;
428   int nremoved = 0;
429
430   /* Being first is a special case. */
431   spam = *list;
432   if (spam->rx && !mutt_strcmp (spam->rx->pattern, pat)) {
433     *list = spam->next;
434     rx_free (&spam->rx);
435     FREE(&spam->template);
436     FREE(&spam);
437     return 1;
438   }
439
440   prev = spam;
441   for (spam = prev->next; spam;) {
442     if (!mutt_strcmp (spam->rx->pattern, pat)) {
443       prev->next = spam->next;
444       rx_free (&spam->rx);
445       FREE(spam->template);
446       FREE(spam);
447       spam = prev->next;
448       ++nremoved;
449     }
450     else
451       spam = spam->next;
452   }
453
454   return nremoved;
455 }
456
457
458 static void remove_from_list (LIST ** l, const char *str)
459 {
460   LIST *p, *last = NULL;
461
462   if (mutt_strcmp ("*", str) == 0)
463     mutt_free_list (l);         /* ``unCMD *'' means delete all current entries */
464   else {
465     p = *l;
466     last = NULL;
467     while (p) {
468       if (ascii_strcasecmp (str, p->data) == 0) {
469         FREE (&p->data);
470         if (last)
471           last->next = p->next;
472         else
473           (*l) = p->next;
474         FREE (&p);
475       }
476       else {
477         last = p;
478         p = p->next;
479       }
480     }
481   }
482 }
483
484 static int remove_from_rx_list (list2_t** l, const char *str)
485 {
486   int i = 0;
487
488   if (mutt_strcmp ("*", str) == 0) {
489     list_del (l, (list_del_t*) rx_free);
490     return (0);
491   }
492   else {
493     i = rx_lookup ((*l), str);
494     if (i >= 0) {
495       rx_t* r = list_pop_idx ((*l), i);
496       rx_free (&r);
497       return (0);
498     }
499   }
500   return (-1);
501 }
502
503 static int parse_ifdef (BUFFER * tmp, BUFFER * s, unsigned long data,
504                         BUFFER * err)
505 {
506   int i, j, res = 0;
507   BUFFER token;
508
509   memset (&token, 0, sizeof (token));
510   mutt_extract_token (tmp, s, 0);
511
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);
516
517       if (!b)
518         continue;
519
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))) {
523           res = 1;
524           break;
525         }
526     }
527   /* check for feature_* */
528   if (!res) {
529     char *p = NULL;
530
531     i = 0;
532     j = mutt_strlen (tmp->data);
533     /* need at least input of 'feature_X' */
534     if (j >= 7) {
535       p = tmp->data + 7;
536       j -= 7;
537       while (Features[i].name) {
538         if (mutt_strlen (Features[i].name) == j &&
539             ascii_strncasecmp (Features[i].name, p, j)) {
540           res = 1;
541           break;
542         }
543         i++;
544       }
545     }
546   }
547
548   if (!MoreArgs (s)) {
549     if (data)
550       snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
551     else
552       snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
553     return (-1);
554   }
555   mutt_extract_token (tmp, s, M_TOKEN_SPACE);
556
557   if ((data && res) || (!data && !res)) {
558     if (mutt_parse_rc_line (tmp->data, &token, err) == -1) {
559       mutt_error ("Error: %s", err->data);
560       FREE (&token.data);
561       return (-1);
562     }
563     FREE (&token.data);
564   }
565   return 0;
566 }
567
568 static int parse_unignore (BUFFER * buf, BUFFER * s, unsigned long data,
569                            BUFFER * err)
570 {
571   do {
572     mutt_extract_token (buf, s, 0);
573
574     /* don't add "*" to the unignore list */
575     if (strcmp (buf->data, "*"))
576       add_to_list (&UnIgnore, buf->data);
577
578     remove_from_list (&Ignore, buf->data);
579   }
580   while (MoreArgs (s));
581
582   return 0;
583 }
584
585 static int parse_ignore (BUFFER * buf, BUFFER * s, unsigned long data,
586                          BUFFER * err)
587 {
588   do {
589     mutt_extract_token (buf, s, 0);
590     remove_from_list (&UnIgnore, buf->data);
591     add_to_list (&Ignore, buf->data);
592   }
593   while (MoreArgs (s));
594
595   return 0;
596 }
597
598 static int parse_list (BUFFER * buf, BUFFER * s, unsigned long data,
599                        BUFFER * err)
600 {
601   do {
602     mutt_extract_token (buf, s, 0);
603     add_to_list ((LIST **) data, buf->data);
604   }
605   while (MoreArgs (s));
606
607   return 0;
608 }
609
610 static void _alternates_clean (void)
611 {
612   int i;
613
614   if (Context && Context->msgcount) {
615     for (i = 0; i < Context->msgcount; i++)
616       Context->hdrs[i]->recip_valid = 0;
617   }
618 }
619
620 static int parse_alternates (BUFFER * buf, BUFFER * s, unsigned long data,
621                              BUFFER * err)
622 {
623   _alternates_clean ();
624   do {
625     mutt_extract_token (buf, s, 0);
626     remove_from_rx_list (&UnAlternates, buf->data);
627
628     if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
629       return -1;
630   }
631   while (MoreArgs (s));
632
633   return 0;
634 }
635
636 static int parse_unalternates (BUFFER * buf, BUFFER * s, unsigned long data,
637                                BUFFER * err)
638 {
639   _alternates_clean ();
640   do {
641     mutt_extract_token (buf, s, 0);
642     remove_from_rx_list (&Alternates, buf->data);
643
644     if (mutt_strcmp (buf->data, "*") &&
645         add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
646       return -1;
647
648   }
649   while (MoreArgs (s));
650
651   return 0;
652 }
653
654 static int parse_spam_list (BUFFER * buf, BUFFER * s, unsigned long data,
655                             BUFFER * err)
656 {
657   BUFFER templ;
658
659   memset (&templ, 0, sizeof (templ));
660
661   /* Insist on at least one parameter */
662   if (!MoreArgs (s)) {
663     if (data == M_SPAM)
664       strfcpy (err->data, _("spam: no matching pattern"), err->dsize);
665     else
666       strfcpy (err->data, _("nospam: no matching pattern"), err->dsize);
667     return -1;
668   }
669
670   /* Extract the first token, a regexp */
671   mutt_extract_token (buf, s, 0);
672
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. */
676     if (MoreArgs (s)) {
677       mutt_extract_token (&templ, s, 0);
678
679       /* Add to the spam list. */
680       if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
681         FREE (&templ.data);
682         return -1;
683       }
684       FREE (&templ.data);
685     }
686
687     /* If not, try to remove from the nospam list. */
688     else {
689       remove_from_rx_list (&NoSpamList, buf->data);
690     }
691
692     return 0;
693   }
694
695   /* M_NOSPAM is for nospam commands. */
696   else if (data == M_NOSPAM) {
697     /* nospam only ever has one parameter. */
698
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);
703       return 0;
704     }
705
706     /* If it's on the spam list, just remove it. */
707     if (remove_from_spam_list (&SpamList, buf->data) != 0)
708       return 0;
709
710     /* Otherwise, add it to the nospam list. */
711     if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
712       return -1;
713
714     return 0;
715   }
716
717   /* This should not happen. */
718   strfcpy (err->data, "This is no good at all.", err->dsize);
719   return -1;
720 }
721
722 static int parse_unlist (BUFFER * buf, BUFFER * s, unsigned long data,
723                          BUFFER * err)
724 {
725   do {
726     mutt_extract_token (buf, s, 0);
727     /*
728      * Check for deletion of entire list
729      */
730     if (mutt_strcmp (buf->data, "*") == 0) {
731       mutt_free_list ((LIST **) data);
732       break;
733     }
734     remove_from_list ((LIST **) data, buf->data);
735   }
736   while (MoreArgs (s));
737
738   return 0;
739 }
740
741 static int parse_lists (BUFFER * buf, BUFFER * s, unsigned long data,
742                         BUFFER * err)
743 {
744   do {
745     mutt_extract_token (buf, s, 0);
746     remove_from_rx_list (&UnMailLists, buf->data);
747
748     if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
749       return -1;
750   }
751   while (MoreArgs (s));
752
753   return 0;
754 }
755
756 static int parse_unlists (BUFFER * buf, BUFFER * s, unsigned long data,
757                           BUFFER * err)
758 {
759   do {
760     mutt_extract_token (buf, s, 0);
761     remove_from_rx_list (&SubscribedLists, buf->data);
762     remove_from_rx_list (&MailLists, buf->data);
763
764     if (mutt_strcmp (buf->data, "*") &&
765         add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
766       return -1;
767   }
768   while (MoreArgs (s));
769
770   return 0;
771 }
772
773 static int parse_subscribe (BUFFER * buf, BUFFER * s, unsigned long data,
774                             BUFFER * err)
775 {
776   do {
777     mutt_extract_token (buf, s, 0);
778     remove_from_rx_list (&UnMailLists, buf->data);
779     remove_from_rx_list (&UnSubscribedLists, buf->data);
780
781     if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
782       return -1;
783     if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
784       return -1;
785   }
786   while (MoreArgs (s));
787
788   return 0;
789 }
790
791 static int parse_unsubscribe (BUFFER * buf, BUFFER * s, unsigned long data,
792                               BUFFER * err)
793 {
794   do {
795     mutt_extract_token (buf, s, 0);
796     remove_from_rx_list (&SubscribedLists, buf->data);
797
798     if (mutt_strcmp (buf->data, "*") &&
799         add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
800       return -1;
801   }
802   while (MoreArgs (s));
803
804   return 0;
805 }
806
807 static int parse_unalias (BUFFER * buf, BUFFER * s, unsigned long data,
808                           BUFFER * err)
809 {
810   ALIAS *tmp, *last = NULL;
811
812   do {
813     mutt_extract_token (buf, s, 0);
814
815     if (mutt_strcmp ("*", buf->data) == 0) {
816       if (CurrentMenu == MENU_ALIAS) {
817         for (tmp = Aliases; tmp; tmp = tmp->next)
818           tmp->del = 1;
819         set_option (OPTFORCEREDRAWINDEX);
820       }
821       else
822         mutt_free_alias (&Aliases);
823       break;
824     }
825     else
826       for (tmp = Aliases; tmp; tmp = tmp->next) {
827         if (safe_strcasecmp (buf->data, tmp->name) == 0) {
828           if (CurrentMenu == MENU_ALIAS) {
829             tmp->del = 1;
830             set_option (OPTFORCEREDRAWINDEX);
831             break;
832           }
833
834           if (last)
835             last->next = tmp->next;
836           else
837             Aliases = tmp->next;
838           tmp->next = NULL;
839           mutt_free_alias (&tmp);
840           break;
841         }
842         last = tmp;
843       }
844   }
845   while (MoreArgs (s));
846   return 0;
847 }
848
849 static int parse_alias (BUFFER * buf, BUFFER * s, unsigned long data,
850                         BUFFER * err)
851 {
852   ALIAS *tmp = Aliases;
853   ALIAS *last = NULL;
854   char *estr = NULL;
855
856   if (!MoreArgs (s)) {
857     strfcpy (err->data, _("alias: no address"), err->dsize);
858     return (-1);
859   }
860
861   mutt_extract_token (buf, s, 0);
862
863   debug_print (2, ("first token is '%s'.\n", buf->data));
864
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))
868       break;
869     last = tmp;
870   }
871
872   if (!tmp) {
873     /* create a new alias */
874     tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
875     tmp->self = tmp;
876     tmp->name = safe_strdup (buf->data);
877     /* give the main addressbook code a chance */
878     if (CurrentMenu == MENU_ALIAS)
879       set_option (OPTMENUCALLER);
880   }
881   else {
882     /* override the previous value */
883     rfc822_free_address (&tmp->addr);
884     if (CurrentMenu == MENU_ALIAS)
885       set_option (OPTFORCEREDRAWINDEX);
886   }
887
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);
892   if (last)
893     last->next = tmp;
894   else
895     Aliases = tmp;
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);
899     return -1;
900   }
901 #ifdef DEBUG
902   if (DebugLevel >= 2) {
903     ADDRESS *a;
904
905     for (a = tmp->addr; a; a = a->next) {
906       if (!a->group)
907         debug_print (2, ("%s\n", a->mailbox));
908       else
909         debug_print (2, ("group %s\n", a->mailbox));
910     }
911   }
912 #endif
913   return 0;
914 }
915
916 static int
917 parse_unmy_hdr (BUFFER * buf, BUFFER * s, unsigned long data, BUFFER * err)
918 {
919   LIST *last = NULL;
920   LIST *tmp = UserHeader;
921   LIST *ptr;
922   size_t l;
923
924   do {
925     mutt_extract_token (buf, s, 0);
926     if (mutt_strcmp ("*", buf->data) == 0)
927       mutt_free_list (&UserHeader);
928     else {
929       tmp = UserHeader;
930       last = NULL;
931
932       l = mutt_strlen (buf->data);
933       if (buf->data[l - 1] == ':')
934         l--;
935
936       while (tmp) {
937         if (ascii_strncasecmp (buf->data, tmp->data, l) == 0
938             && tmp->data[l] == ':') {
939           ptr = tmp;
940           if (last)
941             last->next = tmp->next;
942           else
943             UserHeader = tmp->next;
944           tmp = tmp->next;
945           ptr->next = NULL;
946           mutt_free_list (&ptr);
947         }
948         else {
949           last = tmp;
950           tmp = tmp->next;
951         }
952       }
953     }
954   }
955   while (MoreArgs (s));
956   return 0;
957 }
958
959 static int parse_my_hdr (BUFFER * buf, BUFFER * s, unsigned long data,
960                          BUFFER * err)
961 {
962   LIST *tmp;
963   size_t keylen;
964   char *p;
965
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);
969     return (-1);
970   }
971   keylen = p - buf->data + 1;
972
973   if (UserHeader) {
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 */
978         FREE (&tmp->data);
979         tmp->data = buf->data;
980         memset (buf, 0, sizeof (BUFFER));
981         return 0;
982       }
983       if (!tmp->next)
984         break;
985     }
986     tmp->next = mutt_new_list ();
987     tmp = tmp->next;
988   }
989   else {
990     tmp = mutt_new_list ();
991     UserHeader = tmp;
992   }
993   tmp->data = buf->data;
994   memset (buf, 0, sizeof (BUFFER));
995   return 0;
996 }
997
998 static int
999 parse_sort (short *val, const char *s, const struct mapping_t *map,
1000             BUFFER * err)
1001 {
1002   int i, flags = 0;
1003
1004   if (safe_strncmp ("reverse-", s, 8) == 0) {
1005     s += 8;
1006     flags = SORT_REVERSE;
1007   }
1008
1009   if (safe_strncmp ("last-", s, 5) == 0) {
1010     s += 5;
1011     flags |= SORT_LAST;
1012   }
1013
1014   if ((i = mutt_getvaluebyname (s, map)) == -1) {
1015     snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1016     return (-1);
1017   }
1018
1019   *val = i | flags;
1020
1021   return 0;
1022 }
1023
1024 static void mutt_set_default (struct option_t *p)
1025 {
1026   switch (p->type & DT_MASK) {
1027   case DT_STR:
1028     if (!p->init && *((char **) p->data))
1029       p->init = (unsigned long) safe_strdup (*((char **) p->data));
1030     break;
1031   case DT_PATH:
1032     if (!p->init && *((char **) p->data)) {
1033       char *cp = safe_strdup (*((char **) p->data));
1034
1035       /* mutt_pretty_mailbox (cp); */
1036       p->init = (unsigned long) cp;
1037     }
1038     break;
1039   case DT_ADDR:
1040     if (!p->init && *((ADDRESS **) p->data)) {
1041       char tmp[HUGE_STRING];
1042
1043       *tmp = '\0';
1044       rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1045       p->init = (unsigned long) safe_strdup (tmp);
1046     }
1047     break;
1048   case DT_RX:
1049     {
1050       rx_t* pp = (rx_t*) p->data;
1051
1052       if (!p->init && pp->pattern)
1053         p->init = (unsigned long) safe_strdup (pp->pattern);
1054       break;
1055     }
1056   }
1057 }
1058
1059 static void mutt_restore_default (struct option_t *p)
1060 {
1061   switch (p->type & DT_MASK) {
1062   case DT_STR:
1063     if (p->init)
1064       str_replace ((char **) p->data, (char *) p->init);
1065     break;
1066   case DT_PATH:
1067     if (p->init) {
1068       char path[_POSIX_PATH_MAX];
1069
1070       strfcpy (path, (char *) p->init, sizeof (path));
1071       mutt_expand_path (path, sizeof (path));
1072       str_replace ((char **) p->data, path);
1073     }
1074     break;
1075   case DT_ADDR:
1076     if (p->init) {
1077       rfc822_free_address ((ADDRESS **) p->data);
1078       *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1079     }
1080     break;
1081   case DT_BOOL:
1082     if (p->init)
1083       set_option (p->data);
1084     else
1085       unset_option (p->data);
1086     break;
1087   case DT_QUAD:
1088     set_quadoption (p->data, p->init);
1089     break;
1090   case DT_NUM:
1091   case DT_SORT:
1092   case DT_MAGIC:
1093     *((short *) p->data) = p->init;
1094     break;
1095   case DT_RX:
1096     {
1097       rx_t *pp = (rx_t *) p->data;
1098       int flags = 0;
1099
1100       FREE (&pp->pattern);
1101       if (pp->rx) {
1102         regfree (pp->rx);
1103         FREE (&pp->rx);
1104       }
1105
1106       if (p->init) {
1107         char *s = (char *) p->init;
1108
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 == '!') {
1113           s++;
1114           pp->not = 1;
1115         }
1116         if (REGCOMP (pp->rx, s, flags) != 0) {
1117           fprintf (stderr,
1118                    _("mutt_restore_default(%s): error in regexp: %s\n"),
1119                    p->option, pp->pattern);
1120           FREE (&pp->pattern);
1121           regfree (pp->rx);
1122           FREE (&pp->rx);
1123         }
1124         else
1125           str_replace (&pp->pattern, (char *) p->init);
1126       }
1127     }
1128     break;
1129   }
1130
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);
1143 }
1144
1145 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1146                       BUFFER * err)
1147 {
1148   int idx, query, unset, inv, reset, r = 0;
1149   char *p, scratch[_POSIX_PATH_MAX];
1150
1151   while (MoreArgs (s)) {
1152     /* reset state variables */
1153     query = 0;
1154     unset = data & M_SET_UNSET;
1155     inv = data & M_SET_INV;
1156     reset = data & M_SET_RESET;
1157
1158     if (*s->dptr == '?') {
1159       query = 1;
1160       s->dptr++;
1161     }
1162     else if (safe_strncmp ("no", s->dptr, 2) == 0) {
1163       s->dptr += 2;
1164       unset = !unset;
1165     }
1166     else if (safe_strncmp ("inv", s->dptr, 3) == 0) {
1167       s->dptr += 3;
1168       inv = !inv;
1169     }
1170     else if (*s->dptr == '&') {
1171       reset = 1;
1172       s->dptr++;
1173     }
1174
1175     /* get the variable name */
1176     mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1177
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);
1181       return (-1);
1182     }
1183     SKIPWS (s->dptr);
1184
1185     if (reset) {
1186       if (query || unset || inv) {
1187         snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1188         return (-1);
1189       }
1190
1191       if (s && *s->dptr == '=') {
1192         snprintf (err->data, err->dsize, _("value is illegal with reset"));
1193         return (-1);
1194       }
1195
1196       if (!mutt_strcmp ("all", tmp->data)) {
1197         for (idx = 0; MuttVars[idx].option; idx++)
1198           mutt_restore_default (&MuttVars[idx]);
1199         return 0;
1200       }
1201       else
1202         mutt_restore_default (&MuttVars[idx]);
1203     }
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");
1208           return (-1);
1209         }
1210
1211         s->dptr++;
1212         mutt_extract_token (tmp, s, 0);
1213         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1214           unset = inv = 0;
1215         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1216           unset = 1;
1217         else {
1218           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1219           return (-1);
1220         }
1221       }
1222
1223       if (query) {
1224         snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1225                   ? _("%s is set") : _("%s is unset"), tmp->data);
1226         return 0;
1227       }
1228
1229       if (unset)
1230         unset_option (MuttVars[idx].data);
1231       else if (inv)
1232         toggle_option (MuttVars[idx].data);
1233       else
1234         set_option (MuttVars[idx].data);
1235     }
1236     else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1237              DTYPE (MuttVars[idx].type) == DT_PATH ||
1238              DTYPE (MuttVars[idx].type) == DT_ADDR) {
1239       if (unset) {
1240         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1241           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1242         else
1243           FREE ((void *) MuttVars[idx].data);
1244       }
1245       else if (query || *s->dptr != '=') {
1246         char _tmp[STRING];
1247         char *val = NULL;
1248
1249         if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1250           _tmp[0] = '\0';
1251           rfc822_write_address (_tmp, sizeof (_tmp),
1252                                 *((ADDRESS **) MuttVars[idx].data), 0);
1253           val = _tmp;
1254         }
1255         else
1256           val = *((char **) MuttVars[idx].data);
1257
1258         /* user requested the value of this variable */
1259         snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1260                   NONULL (val));
1261         break;
1262       }
1263       else {
1264         s->dptr++;
1265
1266         /* copy the value of the string */
1267         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1268           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1269         else
1270           FREE ((void *) MuttVars[idx].data);
1271
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);
1277         }
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);
1282         }
1283         else {
1284           *((ADDRESS **) MuttVars[idx].data) =
1285             rfc822_parse_adrlist (NULL, tmp->data);
1286         }
1287       }
1288     }
1289     else if (DTYPE (MuttVars[idx].type) == DT_RX) {
1290       rx_t *ptr = (rx_t *) MuttVars[idx].data;
1291       regex_t *rx;
1292       int e, flags = 0;
1293
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));
1298         break;
1299       }
1300
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.");
1305         r = -1;
1306         break;
1307       }
1308
1309       s->dptr++;
1310
1311       /* copy the value of the string */
1312       mutt_extract_token (tmp, s, 0);
1313
1314       if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) {
1315         int not = 0;
1316
1317         /* $mask is case-sensitive */
1318         if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1319           flags |= mutt_which_case (tmp->data);
1320
1321         p = tmp->data;
1322         if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) {
1323           if (*p == '!') {
1324             not = 1;
1325             p++;
1326           }
1327         }
1328
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);
1332           regfree (rx);
1333           FREE (&rx);
1334           break;
1335         }
1336
1337         /* get here only if everything went smootly */
1338         if (ptr->pattern) {
1339           FREE (&ptr->pattern);
1340           regfree ((regex_t *) ptr->rx);
1341           FREE (&ptr->rx);
1342         }
1343
1344         ptr->pattern = safe_strdup (tmp->data);
1345         ptr->rx = rx;
1346         ptr->not = not;
1347
1348         /* $reply_regexp and $alterantes require special treatment */
1349
1350         if (Context && Context->msgcount &&
1351             mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) {
1352           regmatch_t pmatch[1];
1353           int i;
1354
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,
1360                                              0)) ? CUR_ENV->
1361                 subject : CUR_ENV->subject + pmatch[0].rm_eo;
1362             }
1363           }
1364 #undef CUR_ENV
1365         }
1366       }
1367     }
1368     else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) {
1369       if (query || *s->dptr != '=') {
1370         switch (DefaultMagic) {
1371         case M_MBOX:
1372           p = "mbox";
1373           break;
1374         case M_MMDF:
1375           p = "MMDF";
1376           break;
1377         case M_MH:
1378           p = "MH";
1379           break;
1380         case M_MAILDIR:
1381           p = "Maildir";
1382           break;
1383         default:
1384           p = "unknown";
1385           break;
1386         }
1387         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1388         break;
1389       }
1390
1391       s->dptr++;
1392
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"),
1397                   tmp->data);
1398         r = -1;
1399         break;
1400       }
1401     }
1402     else if (DTYPE (MuttVars[idx].type) == DT_NUM) {
1403       short *ptr = (short *) MuttVars[idx].data;
1404       int val;
1405       char *t;
1406
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);
1410         break;
1411       }
1412
1413       s->dptr++;
1414
1415       mutt_extract_token (tmp, s, 0);
1416       val = strtol (tmp->data, &t, 0);
1417
1418       if (!*tmp->data || *t || (short) val != val) {
1419         snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1420         r = -1;
1421         break;
1422       }
1423       else
1424         *ptr = (short) val;
1425
1426       /* these ones need a sanity check */
1427       if (mutt_strcmp (MuttVars[idx].option, "history") == 0) {
1428         if (*ptr < 0)
1429           *ptr = 0;
1430         mutt_init_history ();
1431       }
1432       else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) {
1433         if (*ptr < 0)
1434           *ptr = 0;
1435       }
1436     }
1437     else if (DTYPE (MuttVars[idx].type) == DT_QUAD) {
1438       if (query) {
1439         char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1440
1441         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1442                   vals[quadoption (MuttVars[idx].data)]);
1443         break;
1444       }
1445
1446       if (*s->dptr == '=') {
1447         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);
1457         else {
1458           snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1459           r = -1;
1460           break;
1461         }
1462       }
1463       else {
1464         if (inv)
1465           toggle_quadoption (MuttVars[idx].data);
1466         else if (unset)
1467           set_quadoption (MuttVars[idx].data, M_NO);
1468         else
1469           set_quadoption (MuttVars[idx].data, M_YES);
1470       }
1471     }
1472     else if (DTYPE (MuttVars[idx].type) == DT_SORT) {
1473       const struct mapping_t *map = NULL;
1474
1475       switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1476       case DT_SORT_ALIAS:
1477         map = SortAliasMethods;
1478         break;
1479       case DT_SORT_BROWSER:
1480         map = SortBrowserMethods;
1481         break;
1482       case DT_SORT_KEYS:
1483         if ((WithCrypto & APPLICATION_PGP))
1484           map = SortKeyMethods;
1485         break;
1486       case DT_SORT_AUX:
1487         map = SortAuxMethods;
1488         break;
1489       default:
1490         map = SortMethods;
1491         break;
1492       }
1493
1494       if (!map) {
1495         snprintf (err->data, err->dsize, _("%s: Unknown type."),
1496                   MuttVars[idx].option);
1497         r = -1;
1498         break;
1499       }
1500
1501       if (query || *s->dptr != '=') {
1502         p =
1503           mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1504                                map);
1505
1506         snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1507                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1508                   "reverse-" : "",
1509                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1510                   "", p);
1511         return 0;
1512       }
1513       s->dptr++;
1514       mutt_extract_token (tmp, s, 0);
1515
1516       if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) ==
1517           -1) {
1518         r = -1;
1519         break;
1520       }
1521     }
1522     else {
1523       snprintf (err->data, err->dsize, _("%s: unknown type"),
1524                 MuttVars[idx].option);
1525       r = -1;
1526       break;
1527     }
1528
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);
1541   }
1542   return (r);
1543 }
1544
1545 #define MAXERRS 128
1546
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)
1550 {
1551   FILE *f;
1552   int line = 0, rc = 0, conv = 0;
1553   BUFFER token;
1554   char *linebuf = NULL;
1555   char *currentline = NULL;
1556   size_t buflen;
1557   pid_t pid;
1558
1559   debug_print (2, ("reading configuration file '%s'.\n", rcfile));
1560   str_replace (&CurRCFile, rcfile);
1561   CurRCLine = 0;
1562
1563   if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1564     snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1565     return (-1);
1566   }
1567
1568   memset (&token, 0, sizeof (token));
1569   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL) {
1570     CurRCLine++;
1571     conv = ConfigCharset && (*ConfigCharset) && Charset;
1572     if (conv) {
1573       currentline = safe_strdup (linebuf);
1574       if (!currentline)
1575         continue;
1576       mutt_convert_string (&currentline, ConfigCharset, Charset, 0);
1577     }
1578     else
1579       currentline = linebuf;
1580
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) {
1584         if (conv)
1585           FREE (&currentline);
1586         break;
1587       }
1588     }
1589     else {
1590       if (rc < 0)
1591         rc = -1;
1592     }
1593     if (conv)
1594       FREE (&currentline);
1595   }
1596   FREE (&token.data);
1597   FREE (&linebuf);
1598   fclose (f);
1599   if (pid != -1)
1600     mutt_wait_filter (pid);
1601   if (rc) {
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"),
1606               rcfile);
1607     rc = -1;
1608   }
1609   return (rc);
1610 }
1611
1612 #undef MAXERRS
1613
1614 static int parse_source (BUFFER * tmp, BUFFER * s, unsigned long data,
1615                          BUFFER * err)
1616 {
1617   char path[_POSIX_PATH_MAX];
1618   int rc = 0;
1619
1620   do {
1621     if (mutt_extract_token (tmp, s, 0) != 0) {
1622       snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1623       return (-1);
1624     }
1625
1626     strfcpy (path, tmp->data, sizeof (path));
1627     mutt_expand_path (path, sizeof (path));
1628
1629     rc += source_rc (path, err);
1630   }
1631   while (MoreArgs (s));
1632
1633   return ((rc < 0) ? -1 : 0);
1634 }
1635
1636 /* line         command to execute
1637
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.
1644
1645    err          where to write error messages */
1646 int mutt_parse_rc_line ( /* const */ char *line, BUFFER * token, BUFFER * err)
1647 {
1648   int i, r = -1;
1649   BUFFER expn;
1650
1651   memset (&expn, 0, sizeof (expn));
1652   expn.data = expn.dptr = line;
1653   expn.dsize = mutt_strlen (line);
1654
1655   *err->data = 0;
1656
1657   SKIPWS (expn.dptr);
1658   while (*expn.dptr) {
1659     if (*expn.dptr == '#')
1660       break;                    /* rest of line is a comment */
1661     if (*expn.dptr == ';') {
1662       expn.dptr++;
1663       continue;
1664     }
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)
1669           goto finish;
1670         break;
1671       }
1672     }
1673     if (!Commands[i].name) {
1674       snprintf (err->data, err->dsize, _("%s: unknown command"),
1675                 NONULL (token->data));
1676       goto finish;
1677     }
1678   }
1679   r = 0;
1680 finish:
1681   if (expn.destroy)
1682     FREE (&expn.data);
1683   return (r);
1684 }
1685
1686
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 };
1692
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 */
1696
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.
1703 */
1704 static void candidate (char *dest, char *try, char *src, int len)
1705 {
1706   int l;
1707
1708   if (strstr (src, try) == src) {
1709     Matches[Num_matched++] = src;
1710     if (dest[0] == 0)
1711       strfcpy (dest, src, len);
1712     else {
1713       for (l = 0; src[l] && src[l] == dest[l]; l++);
1714       dest[l] = 0;
1715     }
1716   }
1717 }
1718
1719 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1720 {
1721   char *pt = buffer;
1722   int num;
1723   int spaces;                   /* keep track of the number of leading spaces on the line */
1724
1725   SKIPWS (buffer);
1726   spaces = buffer - pt;
1727
1728   pt = buffer + pos - spaces;
1729   while ((pt > buffer) && !isspace ((unsigned char) *pt))
1730     pt--;
1731
1732   if (pt == buffer) {           /* complete cmd */
1733     /* first TAB. Collect all the matches */
1734     if (numtabs == 1) {
1735       Num_matched = 0;
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;
1743
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)
1747         return 1;
1748     }
1749
1750     if (Completed[0] == 0 && User_typed[0])
1751       return 0;
1752
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]);
1761
1762     /* return the completed command */
1763     strncpy (buffer, Completed, len - spaces);
1764   }
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 };
1770
1771     pt++;
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]);
1777           break;
1778         }
1779       }
1780     }
1781
1782     /* first TAB. Collect all the matches */
1783     if (numtabs == 1) {
1784       Num_matched = 0;
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;
1792
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)
1796         return 1;
1797     }
1798
1799     if (Completed[0] == 0 && User_typed[0])
1800       return 0;
1801
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]);
1810
1811     strncpy (pt, Completed, buffer + len - pt - spaces);
1812   }
1813   else if (!safe_strncmp (buffer, "exec", 4)) {
1814     struct binding_t *menu = km_get_table (CurrentMenu);
1815
1816     if (!menu && CurrentMenu != MENU_PAGER)
1817       menu = OpGeneric;
1818
1819     pt++;
1820     /* first TAB. Collect all the matches */
1821     if (numtabs == 1) {
1822       Num_matched = 0;
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) {
1830         menu = OpGeneric;
1831         for (num = 0; menu[num].name; num++)
1832           candidate (Completed, User_typed, menu[num].name,
1833                      sizeof (Completed));
1834       }
1835       Matches[Num_matched++] = User_typed;
1836
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)
1840         return 1;
1841     }
1842
1843     if (Completed[0] == 0 && User_typed[0])
1844       return 0;
1845
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]);
1854
1855     strncpy (pt, Completed, buffer + len - pt - spaces);
1856   }
1857   else
1858     return 0;
1859
1860   return 1;
1861 }
1862
1863 int mutt_var_value_complete (char *buffer, size_t len, int pos)
1864 {
1865   char var[STRING], *pt = buffer;
1866   int spaces;
1867
1868   if (buffer[0] == 0)
1869     return 0;
1870
1871   SKIPWS (buffer);
1872   spaces = buffer - pt;
1873
1874   pt = buffer + pos - spaces;
1875   while ((pt > buffer) && !isspace ((unsigned char) *pt))
1876     pt--;
1877   pt++;                         /* move past the space */
1878   if (*pt == '=')               /* abort if no var before the '=' */
1879     return 0;
1880
1881   if (safe_strncmp (buffer, "set", 3) == 0) {
1882     int idx;
1883
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. */
1889     else {
1890       char tmp[LONG_STRING], tmp2[LONG_STRING];
1891       char *s, *d;
1892       size_t dlen = buffer + len - pt - spaces;
1893       char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1894
1895       tmp[0] = '\0';
1896
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);
1903       }
1904       else if (DTYPE (MuttVars[idx].type) == DT_ADDR) {
1905         rfc822_write_address (tmp, sizeof (tmp),
1906                               *((ADDRESS **) MuttVars[idx].data), 0);
1907       }
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;
1914         char *p;
1915
1916         switch (MuttVars[idx].type & DT_SUBTYPE_MASK) {
1917         case DT_SORT_ALIAS:
1918           map = SortAliasMethods;
1919           break;
1920         case DT_SORT_BROWSER:
1921           map = SortBrowserMethods;
1922           break;
1923         case DT_SORT_KEYS:
1924           if ((WithCrypto & APPLICATION_PGP))
1925             map = SortKeyMethods;
1926           else
1927             map = SortMethods;
1928           break;
1929         default:
1930           map = SortMethods;
1931           break;
1932         }
1933         p =
1934           mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK,
1935                                map);
1936         snprintf (tmp, sizeof (tmp), "%s%s%s",
1937                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ?
1938                   "reverse-" : "",
1939                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" :
1940                   "", p);
1941       }
1942       else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1943         strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no",
1944                  sizeof (tmp));
1945       else
1946         return 0;
1947
1948       for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;) {
1949         if (*s == '\\' || *s == '"')
1950           *d++ = '\\';
1951         *d++ = *s++;
1952       }
1953       *d = '\0';
1954
1955       strfcpy (tmp, pt, sizeof (tmp));
1956       snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
1957
1958       return 1;
1959     }
1960   }
1961   return 0;
1962 }
1963
1964 /* Implement the -Q command line flag */
1965 int mutt_query_variables (LIST * queries)
1966 {
1967   LIST *p;
1968
1969   char errbuff[STRING];
1970   char command[STRING];
1971
1972   BUFFER err, token;
1973
1974   memset (&err, 0, sizeof (err));
1975   memset (&token, 0, sizeof (token));
1976
1977   err.data = errbuff;
1978   err.dsize = sizeof (errbuff);
1979
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);
1984       FREE (&token.data);
1985       return 1;
1986     }
1987     printf ("%s\n", err.data);
1988   }
1989
1990   FREE (&token.data);
1991   return 0;
1992 }
1993
1994 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
1995 {
1996   int i;
1997
1998   for (i = 0; map[i].name; i++)
1999     if (map[i].value == val)
2000       return (map[i].name);
2001   return NULL;
2002 }
2003
2004 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2005 {
2006   int i;
2007
2008   for (i = 0; map[i].name; i++)
2009     if (ascii_strcasecmp (map[i].name, name) == 0)
2010       return (map[i].value);
2011   return (-1);
2012 }
2013
2014 static int mutt_execute_commands (LIST * p)
2015 {
2016   BUFFER err, token;
2017   char errstr[SHORT_STRING];
2018
2019   memset (&err, 0, sizeof (err));
2020   err.data = errstr;
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);
2026       FREE (&token.data);
2027       return (-1);
2028     }
2029   }
2030   FREE (&token.data);
2031   return 0;
2032 }
2033
2034 void mutt_init (int skip_sys_rc, LIST * commands)
2035 {
2036   struct passwd *pw;
2037   struct utsname utsname;
2038   char *p, buffer[STRING], error[STRING];
2039   int i, default_rc = 0, need_pause = 0;
2040   BUFFER err;
2041
2042   memset (&err, 0, sizeof (err));
2043   err.data = error;
2044   err.dsize = sizeof (error);
2045
2046   /* 
2047    * XXX - use something even more difficult to predict?
2048    */
2049   snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2050             "\033]9;%ld\a", (long) time (NULL));
2051
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);
2056
2057   /* Get some information about the user */
2058   if ((pw = getpwuid (getuid ()))) {
2059     char rnbuf[STRING];
2060
2061     Username = safe_strdup (pw->pw_name);
2062     if (!Homedir)
2063       Homedir = safe_strdup (pw->pw_dir);
2064
2065     Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2066     Shell = safe_strdup (pw->pw_shell);
2067   }
2068   else {
2069     if (!Homedir) {
2070       mutt_endwin (NULL);
2071       fputs (_("unable to determine home directory"), stderr);
2072       exit (1);
2073     }
2074     if ((p = getenv ("USER")))
2075       Username = safe_strdup (p);
2076     else {
2077       mutt_endwin (NULL);
2078       fputs (_("unable to determine username"), stderr);
2079       exit (1);
2080     }
2081     Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2082   }
2083
2084   debug_start(Homedir);
2085
2086   /* And about the host... */
2087   uname (&utsname);
2088   /* some systems report the FQDN instead of just the hostname */
2089   if ((p = strchr (utsname.nodename, '.'))) {
2090     Hostname = str_substrdup (utsname.nodename, p);
2091     p++;
2092     strfcpy (buffer, p, sizeof (buffer));       /* save the domain for below */
2093   }
2094   else
2095     Hostname = safe_strdup (utsname.nodename);
2096
2097 #ifndef DOMAIN
2098 #define DOMAIN buffer
2099   if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2100     Fqdn = safe_strdup ("@");
2101   else
2102 #endif /* DOMAIN */
2103   if (*DOMAIN != '@') {
2104     Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2105     sprintf (Fqdn, "%s.%s", NONULL (Hostname), DOMAIN); /* __SPRINTF_CHECKED__ */
2106   }
2107   else
2108     Fqdn = safe_strdup (NONULL (Hostname));
2109
2110 #ifdef USE_NNTP
2111   {
2112     FILE *f;
2113     char *i;
2114
2115     if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2116       buffer[0] = '\0';
2117       fgets (buffer, sizeof (buffer), f);
2118       p = (char*) &buffer;
2119       SKIPWS (p);
2120       i = p;
2121       while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r')
2122              && (*i != '\n'))
2123         i++;
2124       *i = '\0';
2125       NewsServer = safe_strdup (p);
2126       fclose (f);
2127     }
2128   }
2129   if ((p = getenv ("NNTPSERVER")))
2130     NewsServer = safe_strdup (p);
2131 #endif
2132
2133   if ((p = getenv ("MAIL")))
2134     Spoolfile = safe_strdup (p);
2135   else if ((p = getenv ("MAILDIR")))
2136     Spoolfile = safe_strdup (p);
2137   else {
2138 #ifdef HOMESPOOL
2139     mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2140 #else
2141     mutt_concat_path (buffer, MAILPATH, NONULL (Username), sizeof (buffer));
2142 #endif
2143     Spoolfile = safe_strdup (buffer);
2144   }
2145
2146   if ((p = getenv ("MAILCAPS")))
2147     MailcapPath = safe_strdup (p);
2148   else {
2149     /* Default search path from RFC1524 */
2150     MailcapPath =
2151       safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2152                    "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2153   }
2154
2155   Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2156
2157   p = getenv ("VISUAL");
2158   if (!p) {
2159     p = getenv ("EDITOR");
2160     if (!p)
2161       p = "vi";
2162   }
2163   Editor = safe_strdup (p);
2164   Visual = safe_strdup (p);
2165
2166   if ((p = getenv ("REPLYTO")) != NULL) {
2167     BUFFER buf, token;
2168
2169     snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2170
2171     memset (&buf, 0, sizeof (buf));
2172     buf.data = buf.dptr = buffer;
2173     buf.dsize = mutt_strlen (buffer);
2174
2175     memset (&token, 0, sizeof (token));
2176     parse_my_hdr (&token, &buf, 0, &err);
2177     FREE (&token.data);
2178   }
2179
2180   if ((p = getenv ("EMAIL")) != NULL)
2181     From = rfc822_parse_adrlist (NULL, p);
2182
2183   mutt_set_langinfo_charset ();
2184   mutt_set_charset (Charset);
2185
2186
2187   /* Set standard defaults */
2188   for (i = 0; MuttVars[i].option; i++) {
2189     mutt_set_default (&MuttVars[i]);
2190     mutt_restore_default (&MuttVars[i]);
2191   }
2192
2193   CurrentMenu = MENU_MAIN;
2194
2195
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);
2202 #endif
2203
2204 #ifdef HAVE_GETSID
2205   /* Unset suspend by default if we're the session leader */
2206   if (getsid (0) == getpid ())
2207     unset_option (OPTSUSPEND);
2208 #endif
2209
2210   mutt_init_history ();
2211
2212
2213
2214
2215   /*
2216    * 
2217    *                       BIG FAT WARNING
2218    * 
2219    * When changing the code which looks for a configuration file,
2220    * please also change the corresponding code in muttbug.sh.in.
2221    * 
2222    * 
2223    */
2224
2225
2226
2227
2228   if (!Muttrc) {
2229     snprintf (buffer, sizeof (buffer), "%s/.muttngrc-%s", NONULL (Homedir),
2230               MUTT_VERSION);
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",
2238                 NONULL (Homedir));
2239
2240     default_rc = 1;
2241     Muttrc = safe_strdup (buffer);
2242   }
2243   else {
2244     strfcpy (buffer, Muttrc, sizeof (buffer));
2245     FREE (&Muttrc);
2246     mutt_expand_path (buffer, sizeof (buffer));
2247     Muttrc = safe_strdup (buffer);
2248   }
2249   FREE (&AliasFile);
2250   AliasFile = safe_strdup (NONULL (Muttrc));
2251
2252   /* Process the global rc file if it exists and the user hasn't explicity
2253      requested not to via "-n".  */
2254   if (!skip_sys_rc) {
2255     snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", SYSCONFDIR,
2256               MUTT_VERSION);
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,
2261                 MUTT_VERSION);
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);
2268         need_pause = 1;
2269       }
2270     }
2271   }
2272
2273   /* Read the user's initialization file.  */
2274   if (access (Muttrc, F_OK) != -1) {
2275     if (!option (OPTNOCURSES))
2276       endwin ();
2277     if (source_rc (Muttrc, &err) != 0) {
2278       fputs (err.data, stderr);
2279       fputc ('\n', stderr);
2280       need_pause = 1;
2281     }
2282   }
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);
2287     exit (1);
2288   }
2289
2290   if (mutt_execute_commands (commands) != 0)
2291     need_pause = 1;
2292
2293   /* warn about synonym variables */
2294   if (!list_empty(Synonyms)) {
2295     int i = 0;
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);
2305     need_pause = 1;
2306   }
2307   /* this is not needed during runtime */
2308   FREE(&CurRCFile);
2309
2310   if (need_pause && !option (OPTNOCURSES)) {
2311     if (mutt_any_key_to_continue (NULL) == -1)
2312       mutt_exit (1);
2313   }
2314
2315 #if 0
2316   set_option (OPTWEED);         /* turn weeding on by default */
2317 #endif
2318 }
2319
2320 int mutt_get_hook_type (const char *name)
2321 {
2322   struct command_t *c;
2323
2324   for (c = Commands; c->name; c++)
2325     if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2326       return c->data;
2327   return 0;
2328 }
2329
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));
2333 }
2334
2335 /* dump out the value of all the variables we have */
2336 int mutt_dump_variables (void) {
2337   int i;
2338
2339   char errbuff[STRING];
2340   char command[STRING];
2341   list2_t* tmp = NULL;
2342
2343   BUFFER err, token;
2344
2345   memset (&err, 0, sizeof (err));
2346   memset (&token, 0, sizeof (token));
2347
2348   err.data = errbuff;
2349   err.dsize = sizeof (errbuff);
2350
2351   /* get all non-synonyms into list... */
2352   for (i = 0; MuttVars[i].option; i++) {
2353     if (MuttVars[i].type == DT_SYN)
2354       continue;
2355     list_push_back (&tmp, &MuttVars[i]);
2356   }
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);
2365         FREE (&token.data);
2366         list_del (&tmp, NULL);
2367         return 1;
2368       }
2369       printf("%s\n", err.data);
2370     }
2371   }
2372   FREE (&token.data);
2373   list_del (&tmp, NULL);
2374   return 0;
2375 }