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