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