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   /* check for feature_* */
571   if (!res) {
572     char* p = NULL;
573     i = 0;
574     j = mutt_strlen (tmp->data);
575     /* need at least input of 'feature_X' */
576     if (j >= 7) {
577       p = tmp->data + 7;
578       j -= 7;
579       while (Features[i].name) {
580         if (mutt_strlen (Features[i].name) == j &&
581             ascii_strncasecmp (Features[i].name, p, j)) {
582           res = 1;
583           break;
584         }
585         i++;
586       }
587     }
588   }
589
590   if (!MoreArgs (s))
591   {
592     if (data)
593       snprintf (err->data, err->dsize, _("ifdef: too few arguments"));
594     else
595       snprintf (err->data, err->dsize, _("ifndef: too few arguments"));
596     return (-1);
597   }
598   mutt_extract_token (tmp, s, M_TOKEN_SPACE);
599
600   if ((data && res) || (!data && !res))
601   {
602     if (mutt_parse_rc_line (tmp->data, &token, err) == -1)
603     {
604       mutt_error ("Error: %s", err->data);
605       FREE (&token.data);
606       return (-1);
607     }
608     FREE (&token.data);
609   }
610   return 0;
611 }
612
613 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
614 {
615   do
616   {
617     mutt_extract_token (buf, s, 0);
618
619     /* don't add "*" to the unignore list */
620     if (strcmp (buf->data, "*")) 
621       add_to_list (&UnIgnore, buf->data);
622
623     remove_from_list (&Ignore, buf->data);
624   }
625   while (MoreArgs (s));
626
627   return 0;
628 }
629
630 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
631 {
632   do
633   {
634     mutt_extract_token (buf, s, 0);
635     remove_from_list (&UnIgnore, buf->data);
636     add_to_list (&Ignore, buf->data);
637   }
638   while (MoreArgs (s));
639
640   return 0;
641 }
642
643 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
644 {
645   do
646   {
647     mutt_extract_token (buf, s, 0);
648     add_to_list ((LIST **) data, buf->data);
649   }
650   while (MoreArgs (s));
651
652   return 0;
653 }
654
655 #if 0
656 static int _parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, int flags)
657 {
658   do 
659   {
660     mutt_extract_token (buf, s, 0);
661     if (add_to_rx_list ((RX_LIST **) data, buf->data, flags, err) != 0)
662       return -1;
663         
664   }
665   while (MoreArgs (s));
666   
667   return 0;
668 }
669
670 static int parse_rx_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
671 {
672   return _parse_rx_list (buf, s, data, err, REG_ICASE);
673 }
674
675 static int parse_rx_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
676 {
677   do
678   {
679     mutt_extract_token (buf, s, 0);
680     if (mutt_strcmp (buf->data, "*") == 0)
681     {
682       mutt_free_rx_list ((RX_LIST **) data);
683       break;
684     }
685     remove_from_rx_list ((RX_LIST **) data, buf->data);
686   }
687   while (MoreArgs (s));
688   
689   return 0;
690 }
691 #endif
692
693 static void _alternates_clean (void)
694 {
695   int i;
696   if (Context && Context->msgcount) 
697   {
698     for (i = 0; i < Context->msgcount; i++)
699       Context->hdrs[i]->recip_valid = 0;
700   }
701 }
702
703 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
704 {
705   _alternates_clean();
706   do
707   {
708     mutt_extract_token (buf, s, 0);
709     remove_from_rx_list (&UnAlternates, buf->data);
710
711     if (add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
712       return -1;
713   }
714   while (MoreArgs (s));
715
716   return 0;
717 }
718
719 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
720 {
721   _alternates_clean();
722   do
723   {
724     mutt_extract_token (buf, s, 0);
725     remove_from_rx_list (&Alternates, buf->data);
726
727     if (mutt_strcmp (buf->data, "*") &&
728         add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
729       return -1;
730
731   }
732   while (MoreArgs (s));
733
734   return 0;
735 }
736
737 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
738 {
739   BUFFER templ;
740
741   memset(&templ, 0, sizeof(templ));
742
743   /* Insist on at least one parameter */
744   if (!MoreArgs(s))
745   {
746     if (data == M_SPAM)
747       strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
748     else
749       strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
750     return -1;
751   }
752
753   /* Extract the first token, a regexp */
754   mutt_extract_token (buf, s, 0);
755
756   /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
757   if (data == M_SPAM)
758   {
759     /* If there's a second parameter, it's a template for the spam tag. */
760     if (MoreArgs(s))
761     {
762       mutt_extract_token (&templ, s, 0);
763
764       /* Add to the spam list. */
765       if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
766           FREE(&templ.data);
767           return -1;
768       }
769       FREE(&templ.data);
770     }
771
772     /* If not, try to remove from the nospam list. */
773     else
774     {
775       remove_from_rx_list(&NoSpamList, buf->data);
776     }
777
778     return 0;
779   }
780
781   /* M_NOSPAM is for nospam commands. */
782   else if (data == M_NOSPAM)
783   {
784     /* nospam only ever has one parameter. */
785
786     /* "*" is a special case. */
787     if (!mutt_strcmp(buf->data, "*"))
788     {
789       mutt_free_spam_list (&SpamList);
790       mutt_free_rx_list (&NoSpamList);
791       return 0;
792     }
793
794     /* If it's on the spam list, just remove it. */
795     if (remove_from_spam_list(&SpamList, buf->data) != 0)
796       return 0;
797
798     /* Otherwise, add it to the nospam list. */
799     if (add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
800       return -1;
801
802     return 0;
803   }
804
805   /* This should not happen. */
806   strfcpy(err->data, "This is no good at all.", err->dsize);
807   return -1;
808 }
809
810 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
811 {
812   do
813   {
814     mutt_extract_token (buf, s, 0);
815     /*
816      * Check for deletion of entire list
817      */
818     if (mutt_strcmp (buf->data, "*") == 0)
819     {
820       mutt_free_list ((LIST **) data);
821       break;
822     }
823     remove_from_list ((LIST **) data, buf->data);
824   }
825   while (MoreArgs (s));
826
827   return 0;
828 }
829
830 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
831 {
832   do
833   {
834     mutt_extract_token (buf, s, 0);
835     remove_from_rx_list (&UnMailLists, buf->data);
836     
837     if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
838       return -1;
839   }
840   while (MoreArgs (s));
841
842   return 0;
843 }
844
845 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
846 {
847   do
848   {
849     mutt_extract_token (buf, s, 0);
850     remove_from_rx_list (&SubscribedLists, buf->data);
851     remove_from_rx_list (&MailLists, buf->data);
852     
853     if (mutt_strcmp (buf->data, "*") && 
854         add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
855       return -1;
856   }
857   while (MoreArgs (s));
858
859   return 0;
860 }
861
862 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
863 {
864   do
865   {
866     mutt_extract_token (buf, s, 0);
867     remove_from_rx_list (&UnMailLists, buf->data);
868     remove_from_rx_list (&UnSubscribedLists, buf->data);
869
870     if (add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
871       return -1;
872     if (add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
873       return -1;
874   }
875   while (MoreArgs (s));
876
877   return 0;
878 }
879
880 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
881 {
882   do
883   {
884     mutt_extract_token (buf, s, 0);
885     remove_from_rx_list (&SubscribedLists, buf->data);
886     
887     if (mutt_strcmp (buf->data, "*") &&
888         add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
889       return -1;
890   }
891   while (MoreArgs (s));
892
893   return 0;
894 }
895   
896 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
897 {
898   ALIAS *tmp, *last = NULL;
899
900   do
901   {
902     mutt_extract_token (buf, s, 0);
903
904     if (mutt_strcmp ("*", buf->data) == 0)
905     {
906       if (CurrentMenu == MENU_ALIAS)
907       {
908         for (tmp = Aliases; tmp ; tmp = tmp->next)
909           tmp->del = 1;
910         set_option (OPTFORCEREDRAWINDEX);
911       }
912       else
913         mutt_free_alias (&Aliases);
914       break;
915     }
916     else
917       for (tmp = Aliases; tmp; tmp = tmp->next)
918       {
919         if (mutt_strcasecmp (buf->data, tmp->name) == 0)
920         {
921           if (CurrentMenu == MENU_ALIAS)
922           {
923             tmp->del = 1;
924             set_option (OPTFORCEREDRAWINDEX);
925             break;
926           }
927
928           if (last)
929             last->next = tmp->next;
930           else
931             Aliases = tmp->next;
932           tmp->next = NULL;
933           mutt_free_alias (&tmp);
934           break;
935         }
936         last = tmp;
937       }
938   }
939   while (MoreArgs (s));
940   return 0;
941 }
942
943 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
944 {
945   ALIAS *tmp = Aliases;
946   ALIAS *last = NULL;
947   char *estr = NULL;
948   
949   if (!MoreArgs (s))
950   {
951     strfcpy (err->data, _("alias: no address"), err->dsize);
952     return (-1);
953   }
954
955   mutt_extract_token (buf, s, 0);
956
957   dprint (2, (debugfile, "parse_alias: First token is '%s'.\n",
958               buf->data));
959
960   /* check to see if an alias with this name already exists */
961   for (; tmp; tmp = tmp->next)
962   {
963     if (!mutt_strcasecmp (tmp->name, buf->data))
964       break;
965     last = tmp;
966   }
967
968   if (!tmp)
969   {
970     /* create a new alias */
971     tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
972     tmp->self = tmp;
973     tmp->name = safe_strdup (buf->data);
974     /* give the main addressbook code a chance */
975     if (CurrentMenu == MENU_ALIAS)
976       set_option (OPTMENUCALLER);
977   }
978   else
979   {
980     /* override the previous value */
981     rfc822_free_address (&tmp->addr);
982     if (CurrentMenu == MENU_ALIAS)
983       set_option (OPTFORCEREDRAWINDEX);
984   }
985
986   mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
987   dprint (2, (debugfile, "parse_alias: Second token is '%s'.\n",
988               buf->data));
989   tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
990   if (last)
991     last->next = tmp;
992   else
993     Aliases = tmp;
994   if (mutt_addrlist_to_idna (tmp->addr, &estr))
995   {
996     snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
997               estr, tmp->name);
998     return -1;
999   }
1000 #ifdef DEBUG
1001   if (debuglevel >= 2) 
1002   {
1003     ADDRESS *a;
1004     for (a = tmp->addr; a; a = a->next)
1005     {
1006       if (!a->group)
1007         dprint (2, (debugfile, "parse_alias:   %s\n",
1008                     a->mailbox));
1009       else
1010         dprint (2, (debugfile, "parse_alias:   Group %s\n",
1011                     a->mailbox));
1012     }
1013   }
1014 #endif
1015   return 0;
1016 }
1017
1018 static int
1019 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1020 {
1021   LIST *last = NULL;
1022   LIST *tmp = UserHeader;
1023   LIST *ptr;
1024   size_t l;
1025
1026   do
1027   {
1028     mutt_extract_token (buf, s, 0);
1029     if (mutt_strcmp ("*", buf->data) == 0)
1030       mutt_free_list (&UserHeader);
1031     else
1032     {
1033       tmp = UserHeader;
1034       last = NULL;
1035
1036       l = mutt_strlen (buf->data);
1037       if (buf->data[l - 1] == ':')
1038         l--;
1039
1040       while (tmp)
1041       {
1042         if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1043         {
1044           ptr = tmp;
1045           if (last)
1046             last->next = tmp->next;
1047           else
1048             UserHeader = tmp->next;
1049           tmp = tmp->next;
1050           ptr->next = NULL;
1051           mutt_free_list (&ptr);
1052         }
1053         else
1054         {
1055           last = tmp;
1056           tmp = tmp->next;
1057         }
1058       }
1059     }
1060   }
1061   while (MoreArgs (s));
1062   return 0;
1063 }
1064
1065 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1066 {
1067   LIST *tmp;
1068   size_t keylen;
1069   char *p;
1070
1071   mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1072   if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1073   {
1074     strfcpy (err->data, _("invalid header field"), err->dsize);
1075     return (-1);
1076   }
1077   keylen = p - buf->data + 1;
1078
1079   if (UserHeader)
1080   {
1081     for (tmp = UserHeader; ; tmp = tmp->next)
1082     {
1083       /* see if there is already a field by this name */
1084       if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1085       {
1086         /* replace the old value */
1087         FREE (&tmp->data);
1088         tmp->data = buf->data;
1089         memset (buf, 0, sizeof (BUFFER));
1090         return 0;
1091       }
1092       if (!tmp->next)
1093         break;
1094     }
1095     tmp->next = mutt_new_list ();
1096     tmp = tmp->next;
1097   }
1098   else
1099   {
1100     tmp = mutt_new_list ();
1101     UserHeader = tmp;
1102   }
1103   tmp->data = buf->data;
1104   memset (buf, 0, sizeof (BUFFER));
1105   return 0;
1106 }
1107
1108 static int
1109 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1110 {
1111   int i, flags = 0;
1112
1113   if (mutt_strncmp ("reverse-", s, 8) == 0)
1114   {
1115     s += 8;
1116     flags = SORT_REVERSE;
1117   }
1118   
1119   if (mutt_strncmp ("last-", s, 5) == 0)
1120   {
1121     s += 5;
1122     flags |= SORT_LAST;
1123   }
1124
1125   if ((i = mutt_getvaluebyname (s, map)) == -1)
1126   {
1127     snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1128     return (-1);
1129   }
1130
1131   *val = i | flags;
1132
1133   return 0;
1134 }
1135
1136 static void mutt_set_default (struct option_t *p)
1137 {
1138   switch (p->type & DT_MASK)
1139   {
1140     case DT_STR:
1141       if (!p->init && *((char **) p->data))
1142         p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1143       break;
1144     case DT_PATH:
1145       if (!p->init && *((char **) p->data))
1146       {
1147         char *cp = safe_strdup (*((char **) p->data));
1148         /* mutt_pretty_mailbox (cp); */
1149         p->init = (unsigned long) cp;
1150       }
1151       break;
1152     case DT_ADDR:
1153       if (!p->init && *((ADDRESS **) p->data))
1154       {
1155         char tmp[HUGE_STRING];
1156         *tmp = '\0';
1157         rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1158         p->init = (unsigned long) safe_strdup (tmp);
1159       }
1160       break;
1161     case DT_RX:
1162     {
1163       REGEXP *pp = (REGEXP *) p->data;
1164       if (!p->init && pp->pattern)
1165         p->init = (unsigned long) safe_strdup (pp->pattern);
1166       break;
1167     }
1168   }
1169 }
1170
1171 static void mutt_restore_default (struct option_t *p)
1172 {
1173   switch (p->type & DT_MASK)
1174   {
1175     case DT_STR:
1176       if (p->init)
1177         mutt_str_replace ((char **) p->data, (char *) p->init); 
1178       break;
1179     case DT_PATH:
1180       if (p->init)
1181       {
1182         char path[_POSIX_PATH_MAX];
1183
1184         strfcpy (path, (char *) p->init, sizeof (path));
1185         mutt_expand_path (path, sizeof (path));
1186         mutt_str_replace ((char **) p->data, path);
1187       }
1188       break;
1189     case DT_ADDR:
1190       if (p->init)
1191       {
1192         rfc822_free_address ((ADDRESS **) p->data);
1193         *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1194       }
1195       break;
1196     case DT_BOOL:
1197       if (p->init)
1198         set_option (p->data);
1199       else
1200         unset_option (p->data);
1201       break;
1202     case DT_QUAD:
1203       set_quadoption (p->data, p->init);
1204       break;
1205     case DT_NUM:
1206     case DT_SORT:
1207     case DT_MAGIC:
1208       *((short *) p->data) = p->init;
1209       break;
1210     case DT_RX:
1211       {
1212         REGEXP *pp = (REGEXP *) p->data;
1213         int flags = 0;
1214
1215         FREE (&pp->pattern);
1216         if (pp->rx)
1217         {
1218           regfree (pp->rx);
1219           FREE (&pp->rx);
1220         }
1221
1222         if (p->init)
1223         {
1224           char *s = (char *) p->init;
1225
1226           pp->rx = safe_calloc (1, sizeof (regex_t));
1227           if (mutt_strcmp (p->option, "mask") != 0)
1228             flags |= mutt_which_case ((const char *) p->init);
1229           if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1230           {
1231             s++;
1232             pp->not = 1;
1233           }
1234           if (REGCOMP (pp->rx, s, flags) != 0)
1235           {
1236             fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1237                      p->option, pp->pattern);
1238             FREE (&pp->pattern);
1239             regfree (pp->rx);
1240             FREE (&pp->rx);
1241           }
1242         }
1243       }
1244       break;
1245   }
1246
1247   if (p->flags & R_INDEX)
1248     set_option (OPTFORCEREDRAWINDEX);
1249   if (p->flags & R_PAGER)
1250     set_option (OPTFORCEREDRAWPAGER);
1251   if (p->flags & R_RESORT_SUB)
1252     set_option (OPTSORTSUBTHREADS);
1253   if (p->flags & R_RESORT)
1254     set_option (OPTNEEDRESORT);
1255   if (p->flags & R_RESORT_INIT)
1256     set_option (OPTRESORTINIT);
1257   if (p->flags & R_TREE)
1258     set_option (OPTREDRAWTREE);
1259 }
1260
1261 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1262 {
1263   int idx, query, unset, inv, reset, r = 0;
1264   char *p, scratch[_POSIX_PATH_MAX];
1265
1266   while (MoreArgs (s))
1267   {
1268     /* reset state variables */
1269     query = 0;
1270     unset = data & M_SET_UNSET;
1271     inv = data & M_SET_INV;
1272     reset = data & M_SET_RESET;
1273
1274     if (*s->dptr == '?')
1275     {
1276       query = 1;
1277       s->dptr++;
1278     }
1279     else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1280     {
1281       s->dptr += 2;
1282       unset = !unset;
1283     }
1284     else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1285     {
1286       s->dptr += 3;
1287       inv = !inv;
1288     }
1289     else if (*s->dptr == '&')
1290     {
1291       reset = 1;
1292       s->dptr++;
1293     }
1294
1295     /* get the variable name */
1296     mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1297
1298     if ((idx = mutt_option_index (tmp->data)) == -1 &&
1299         !(reset && !mutt_strcmp ("all", tmp->data)))
1300     {
1301       snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1302       return (-1);
1303     }
1304     SKIPWS (s->dptr);
1305
1306     if (reset)
1307     {
1308       if (query || unset || inv)
1309       {
1310         snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1311         return (-1);
1312       }
1313
1314       if (s && *s->dptr == '=')
1315       {
1316         snprintf (err->data, err->dsize, _("value is illegal with reset"));
1317         return (-1);
1318       }
1319      
1320       if (!mutt_strcmp ("all", tmp->data))
1321       {
1322         for (idx = 0; MuttVars[idx].option; idx++)
1323           mutt_restore_default (&MuttVars[idx]);
1324         return 0;
1325       }
1326       else
1327         mutt_restore_default (&MuttVars[idx]);
1328     } 
1329     else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1330     { 
1331       if (s && *s->dptr == '=')
1332       {
1333         if (unset || inv || query)
1334         {
1335           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1336           return (-1);
1337         }
1338
1339         s->dptr++;
1340         mutt_extract_token (tmp, s, 0);
1341         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1342           unset = inv = 0;
1343         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1344           unset = 1;
1345         else
1346         {
1347           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1348           return (-1);
1349         }
1350       }
1351
1352       if (query)
1353       {
1354         snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1355                         ? _("%s is set") : _("%s is unset"), tmp->data);
1356         return 0;
1357       }
1358
1359       if (unset)
1360         unset_option (MuttVars[idx].data);
1361       else if (inv)
1362         toggle_option (MuttVars[idx].data);
1363       else
1364         set_option (MuttVars[idx].data);
1365     }
1366     else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1367              DTYPE (MuttVars[idx].type) == DT_PATH ||
1368              DTYPE (MuttVars[idx].type) == DT_ADDR)
1369     {
1370       if (unset)
1371       {
1372         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1373           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1374         else
1375           FREE ((void *)MuttVars[idx].data);
1376       }
1377       else if (query || *s->dptr != '=')
1378       {
1379         char _tmp[STRING];
1380         char *val = NULL;
1381         
1382         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1383         {
1384           _tmp[0] = '\0';
1385           rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1386           val = _tmp;
1387         }
1388         else
1389           val = *((char **) MuttVars[idx].data);
1390         
1391         /* user requested the value of this variable */
1392         snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1393                   NONULL (val));
1394         break;
1395       }
1396       else
1397       {
1398         s->dptr++;
1399
1400         /* copy the value of the string */
1401         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1402           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1403         else
1404           FREE ((void *)MuttVars[idx].data);
1405
1406         mutt_extract_token (tmp, s, 0);
1407         if (DTYPE (MuttVars[idx].type) == DT_PATH)
1408         {
1409           strfcpy (scratch, tmp->data, sizeof (scratch));
1410           mutt_expand_path (scratch, sizeof (scratch));
1411           *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1412         }
1413         else if (DTYPE (MuttVars[idx].type) == DT_STR)
1414         {
1415           *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1416           if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1417             mutt_set_charset (Charset);
1418         }
1419         else
1420         {
1421           *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1422         }
1423       }
1424     }
1425     else if (DTYPE(MuttVars[idx].type) == DT_RX)
1426     {
1427       REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1428       regex_t *rx;
1429       int e, flags = 0;
1430
1431       if (query || *s->dptr != '=')
1432       {
1433         /* user requested the value of this variable */
1434         snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1435                   NONULL (ptr->pattern));
1436         break;
1437       }
1438
1439       if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1440       {
1441         snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1442         r = -1;
1443         break;
1444       }
1445       
1446       s->dptr++;
1447
1448       /* copy the value of the string */
1449       mutt_extract_token (tmp, s, 0);
1450
1451       if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1452       {
1453         int not = 0;
1454
1455         /* $mask is case-sensitive */
1456         if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1457           flags |= mutt_which_case (tmp->data);
1458
1459         p = tmp->data;
1460         if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1461         {
1462           if (*p == '!')
1463           {
1464             not = 1;
1465             p++;
1466           }
1467         }
1468           
1469         rx = (regex_t *) safe_malloc (sizeof (regex_t));
1470         if ((e = REGCOMP (rx, p, flags)) != 0)
1471         {
1472           regerror (e, rx, err->data, err->dsize);
1473           regfree (rx);
1474           FREE (&rx);
1475           break;
1476         }
1477
1478         /* get here only if everything went smootly */
1479         if (ptr->pattern)
1480         {
1481           FREE (&ptr->pattern);
1482           regfree ((regex_t *) ptr->rx);
1483           FREE (&ptr->rx);
1484         }
1485
1486         ptr->pattern = safe_strdup (tmp->data);
1487         ptr->rx = rx;
1488         ptr->not = not;
1489
1490         /* $reply_regexp and $alterantes require special treatment */
1491         
1492         if (Context && Context->msgcount &&
1493             mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1494         {
1495           regmatch_t pmatch[1];
1496           int i;
1497           
1498 #define CUR_ENV Context->hdrs[i]->env
1499           for (i = 0; i < Context->msgcount; i++)
1500           {
1501             if (CUR_ENV && CUR_ENV->subject)
1502             {
1503               CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1504                                     CUR_ENV->subject, 1, pmatch, 0)) ?
1505                                     CUR_ENV->subject : 
1506                                     CUR_ENV->subject + pmatch[0].rm_eo;
1507             }
1508           }
1509 #undef CUR_ENV
1510         }
1511       }
1512     }
1513     else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1514     {
1515       if (query || *s->dptr != '=')
1516       {
1517         switch (DefaultMagic)
1518         {
1519           case M_MBOX:
1520             p = "mbox";
1521             break;
1522           case M_MMDF:
1523             p = "MMDF";
1524             break;
1525           case M_MH:
1526             p = "MH";
1527             break;
1528           case M_MAILDIR:
1529             p = "Maildir";
1530             break;
1531           default:
1532             p = "unknown";
1533             break;
1534         }
1535         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1536         break;
1537       }
1538
1539       s->dptr++;
1540
1541       /* copy the value of the string */
1542       mutt_extract_token (tmp, s, 0);
1543       if (mx_set_magic (tmp->data))
1544       {
1545         snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1546         r = -1;
1547         break;
1548       }
1549     }
1550     else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1551     {
1552       short *ptr = (short *) MuttVars[idx].data;
1553       int val;
1554       char *t;
1555
1556       if (query || *s->dptr != '=')
1557       {
1558         /* user requested the value of this variable */
1559         snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1560         break;
1561       }
1562
1563       s->dptr++;
1564
1565       mutt_extract_token (tmp, s, 0);
1566       val = strtol (tmp->data, &t, 0);
1567
1568       if (!*tmp->data || *t || (short) val != val)
1569       {
1570         snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1571         r = -1;
1572         break;
1573       }
1574       else
1575         *ptr = (short) val;
1576
1577       /* these ones need a sanity check */
1578       if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1579       {
1580         if (*ptr < 0)
1581           *ptr = 0;
1582         mutt_init_history ();
1583       }
1584       else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1585       {
1586         if (*ptr < 0)
1587           *ptr = 0;
1588       }
1589     }
1590     else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1591     {
1592       if (query)
1593       {
1594         char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1595
1596         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1597                   vals [ quadoption (MuttVars[idx].data) ]);
1598         break;
1599       }
1600
1601       if (*s->dptr == '=')
1602       {
1603         s->dptr++;
1604         mutt_extract_token (tmp, s, 0);
1605         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1606           set_quadoption (MuttVars[idx].data, M_YES);
1607         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1608           set_quadoption (MuttVars[idx].data, M_NO);
1609         else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1610           set_quadoption (MuttVars[idx].data, M_ASKYES);
1611         else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1612           set_quadoption (MuttVars[idx].data, M_ASKNO);
1613         else
1614         {
1615           snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1616           r = -1;
1617           break;
1618         }
1619       }
1620       else
1621       {
1622         if (inv)
1623           toggle_quadoption (MuttVars[idx].data);
1624         else if (unset)
1625           set_quadoption (MuttVars[idx].data, M_NO);
1626         else
1627           set_quadoption (MuttVars[idx].data, M_YES);
1628       }
1629     }
1630     else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1631     {
1632       const struct mapping_t *map = NULL;
1633
1634       switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1635       {
1636         case DT_SORT_ALIAS:
1637           map = SortAliasMethods;
1638           break;
1639         case DT_SORT_BROWSER:
1640           map = SortBrowserMethods;
1641           break;
1642         case DT_SORT_KEYS:
1643           if ((WithCrypto & APPLICATION_PGP))
1644             map = SortKeyMethods;
1645           break;
1646         case DT_SORT_AUX:
1647           map = SortAuxMethods;
1648           break;
1649         default:
1650           map = SortMethods;
1651           break;
1652       }
1653
1654       if (!map)
1655       {
1656         snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1657         r = -1;
1658         break;
1659       }
1660       
1661       if (query || *s->dptr != '=')
1662       {
1663         p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1664
1665         snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1666                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1667                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1668                   p);
1669         return 0;
1670       }
1671       s->dptr++;
1672       mutt_extract_token (tmp, s , 0);
1673
1674       if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1675       {
1676         r = -1;
1677         break;
1678       }
1679     }
1680     else
1681     {
1682       snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1683       r = -1;
1684       break;
1685     }
1686
1687     if (MuttVars[idx].flags & R_INDEX)
1688       set_option (OPTFORCEREDRAWINDEX);
1689     if (MuttVars[idx].flags & R_PAGER)
1690       set_option (OPTFORCEREDRAWPAGER);
1691     if (MuttVars[idx].flags & R_RESORT_SUB)
1692       set_option (OPTSORTSUBTHREADS);
1693     if (MuttVars[idx].flags & R_RESORT)
1694       set_option (OPTNEEDRESORT);
1695     if (MuttVars[idx].flags & R_RESORT_INIT)
1696       set_option (OPTRESORTINIT);
1697     if (MuttVars[idx].flags & R_TREE)
1698       set_option (OPTREDRAWTREE);
1699   }
1700   return (r);
1701 }
1702
1703 #define MAXERRS 128
1704
1705 /* reads the specified initialization file.  returns -1 if errors were found
1706    so that we can pause to let the user know...  */
1707 static int source_rc (const char *rcfile, BUFFER *err)
1708 {
1709   FILE *f;
1710   int line = 0, rc = 0, conv = 0;
1711   BUFFER token;
1712   char *linebuf = NULL;
1713   char *currentline = NULL;
1714   size_t buflen;
1715   pid_t pid;
1716
1717   dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1718           rcfile));
1719   
1720   if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1721   {
1722     snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1723     return (-1);
1724   }
1725
1726   memset (&token, 0, sizeof (token));
1727   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1728   {
1729     conv=ConfigCharset && (*ConfigCharset) && Charset;
1730     if (conv) 
1731     {
1732       currentline=safe_strdup(linebuf);
1733       if (!currentline) continue;
1734       mutt_convert_string(&currentline, ConfigCharset, Charset, 0);
1735     } 
1736     else 
1737       currentline=linebuf;
1738
1739     if (mutt_parse_rc_line (currentline, &token, err) == -1)
1740     {
1741       mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1742       if (--rc < -MAXERRS) 
1743       {
1744         if (conv) FREE(&currentline);
1745         break;
1746       }
1747     }
1748     else
1749     {
1750       if (rc < 0)
1751         rc = -1;
1752     }
1753     if (conv)
1754       FREE(&currentline);
1755   }
1756   FREE (&token.data);
1757   FREE (&linebuf);
1758   fclose (f);
1759   if (pid != -1)
1760     mutt_wait_filter (pid);
1761   if (rc)
1762   {
1763     /* the muttrc source keyword */
1764     snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1765       : _("source: reading aborted due too many errors in %s"), rcfile);
1766     rc = -1;
1767   }
1768   return (rc);
1769 }
1770
1771 #undef MAXERRS
1772
1773 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1774 {
1775   char path[_POSIX_PATH_MAX];
1776   int rc = 0;
1777
1778   do
1779   {
1780     if (mutt_extract_token (tmp, s, 0) != 0)
1781     {
1782       snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1783       return (-1);
1784     }
1785
1786     strfcpy (path, tmp->data, sizeof (path));
1787     mutt_expand_path (path, sizeof (path));
1788
1789     rc += source_rc (path, err);
1790   }
1791   while (MoreArgs (s));
1792
1793   return ((rc < 0) ? -1 : 0);
1794 }
1795
1796 /* line         command to execute
1797
1798    token        scratch buffer to be used by parser.  caller should free
1799                 token->data when finished.  the reason for this variable is
1800                 to avoid having to allocate and deallocate a lot of memory
1801                 if we are parsing many lines.  the caller can pass in the
1802                 memory to use, which avoids having to create new space for
1803                 every call to this function.
1804
1805    err          where to write error messages */
1806 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1807 {
1808   int i, r = -1;
1809   BUFFER expn;
1810
1811   memset (&expn, 0, sizeof (expn));
1812   expn.data = expn.dptr = line;
1813   expn.dsize = mutt_strlen (line);
1814
1815   *err->data = 0;
1816
1817   SKIPWS (expn.dptr);
1818   while (*expn.dptr)
1819   {
1820     if (*expn.dptr == '#')
1821       break; /* rest of line is a comment */
1822     if (*expn.dptr == ';')
1823     {
1824       expn.dptr++;
1825       continue;
1826     }
1827     mutt_extract_token (token, &expn, 0);
1828     for (i = 0; Commands[i].name; i++)
1829     {
1830       if (!mutt_strcmp (token->data, Commands[i].name))
1831       {
1832         if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1833           goto finish;
1834         break;
1835       }
1836     }
1837     if (!Commands[i].name)
1838     {
1839       snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1840       goto finish;
1841     }
1842   }
1843   r = 0;
1844 finish:
1845   if (expn.destroy)
1846     FREE (&expn.data);
1847   return (r);
1848 }
1849
1850
1851 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1852 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1853 /* initial string that starts completion. No telling how much crap 
1854  * the user has typed so far. Allocate LONG_STRING just to be sure! */
1855 char User_typed [LONG_STRING] = {0}; 
1856
1857 int  Num_matched = 0; /* Number of matches for completion */
1858 char Completed [STRING] = {0}; /* completed string (command or variable) */
1859 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1860
1861 /* helper function for completion.  Changes the dest buffer if
1862    necessary/possible to aid completion.
1863         dest == completion result gets here.
1864         src == candidate for completion.
1865         try == user entered data for completion.
1866         len == length of dest buffer.
1867 */
1868 static void candidate (char *dest, char *try, char *src, int len)
1869 {
1870   int l;
1871
1872   if (strstr (src, try) == src)
1873   {
1874     Matches[Num_matched++] = src;
1875     if (dest[0] == 0)
1876       strfcpy (dest, src, len);
1877     else
1878     {
1879       for (l = 0; src[l] && src[l] == dest[l]; l++);
1880       dest[l] = 0;
1881     }
1882   }
1883 }
1884
1885 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1886 {
1887   char *pt = buffer;
1888   int num;
1889   int spaces; /* keep track of the number of leading spaces on the line */
1890
1891   SKIPWS (buffer);
1892   spaces = buffer - pt;
1893
1894   pt = buffer + pos - spaces;
1895   while ((pt > buffer) && !isspace ((unsigned char) *pt))
1896     pt--;
1897
1898   if (pt == buffer) /* complete cmd */
1899   {
1900     /* first TAB. Collect all the matches */
1901     if (numtabs == 1)
1902     {
1903       Num_matched = 0;
1904       strfcpy (User_typed, pt, sizeof (User_typed));
1905       memset (Matches, 0, sizeof (Matches));
1906       memset (Completed, 0, sizeof (Completed));
1907       for (num = 0; Commands[num].name; num++)
1908         candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1909       Matches[Num_matched++] = User_typed;
1910
1911       /* All matches are stored. Longest non-ambiguous string is ""
1912        * i.e. dont change 'buffer'. Fake successful return this time */
1913       if (User_typed[0] == 0)
1914         return 1;
1915     }
1916
1917     if (Completed[0] == 0 && User_typed[0])
1918       return 0;
1919
1920      /* Num_matched will _always_ be atleast 1 since the initial
1921       * user-typed string is always stored */
1922     if (numtabs == 1 && Num_matched == 2)
1923       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1924     else if (numtabs > 1 && Num_matched > 2)
1925       /* cycle thru all the matches */
1926       snprintf(Completed, sizeof(Completed), "%s", 
1927                Matches[(numtabs - 2) % Num_matched]);
1928
1929     /* return the completed command */
1930     strncpy (buffer, Completed, len - spaces);
1931   }
1932   else if (!mutt_strncmp (buffer, "set", 3)
1933            || !mutt_strncmp (buffer, "unset", 5)
1934            || !mutt_strncmp (buffer, "reset", 5)
1935            || !mutt_strncmp (buffer, "toggle", 6))
1936   {             /* complete variables */
1937     char *prefixes[] = { "no", "inv", "?", "&", 0 };
1938     
1939     pt++;
1940     /* loop through all the possible prefixes (no, inv, ...) */
1941     if (!mutt_strncmp (buffer, "set", 3))
1942     {
1943       for (num = 0; prefixes[num]; num++)
1944       {
1945         if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1946         {
1947           pt += mutt_strlen (prefixes[num]);
1948           break;
1949         }
1950       }
1951     }
1952     
1953     /* first TAB. Collect all the matches */
1954     if (numtabs == 1)
1955     {
1956       Num_matched = 0;
1957       strfcpy (User_typed, pt, sizeof (User_typed));
1958       memset (Matches, 0, sizeof (Matches));
1959       memset (Completed, 0, sizeof (Completed));
1960       for (num = 0; MuttVars[num].option; num++)
1961         candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1962       Matches[Num_matched++] = User_typed;
1963
1964       /* All matches are stored. Longest non-ambiguous string is ""
1965        * i.e. dont change 'buffer'. Fake successful return this time */
1966       if (User_typed[0] == 0)
1967         return 1;
1968     }
1969
1970     if (Completed[0] == 0 && User_typed[0])
1971       return 0;
1972
1973     /* Num_matched will _always_ be atleast 1 since the initial
1974      * user-typed string is always stored */
1975     if (numtabs == 1 && Num_matched == 2)
1976       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1977     else if (numtabs > 1 && Num_matched > 2)
1978     /* cycle thru all the matches */
1979       snprintf(Completed, sizeof(Completed), "%s", 
1980                Matches[(numtabs - 2) % Num_matched]);
1981
1982     strncpy (pt, Completed, buffer + len - pt - spaces);
1983   }
1984   else if (!mutt_strncmp (buffer, "exec", 4))
1985   {
1986     struct binding_t *menu = km_get_table (CurrentMenu);
1987
1988     if (!menu && CurrentMenu != MENU_PAGER)
1989       menu = OpGeneric;
1990     
1991     pt++;
1992     /* first TAB. Collect all the matches */
1993     if (numtabs == 1)
1994     {
1995       Num_matched = 0;
1996       strfcpy (User_typed, pt, sizeof (User_typed));
1997       memset (Matches, 0, sizeof (Matches));
1998       memset (Completed, 0, sizeof (Completed));
1999       for (num = 0; menu[num].name; num++)
2000         candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2001       /* try the generic menu */
2002       if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) 
2003       {
2004         menu = OpGeneric;
2005         for (num = 0; menu[num].name; num++)
2006           candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2007       }
2008       Matches[Num_matched++] = User_typed;
2009
2010       /* All matches are stored. Longest non-ambiguous string is ""
2011        * i.e. dont change 'buffer'. Fake successful return this time */
2012       if (User_typed[0] == 0)
2013         return 1;
2014     }
2015
2016     if (Completed[0] == 0 && User_typed[0])
2017       return 0;
2018
2019     /* Num_matched will _always_ be atleast 1 since the initial
2020      * user-typed string is always stored */
2021     if (numtabs == 1 && Num_matched == 2)
2022       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2023     else if (numtabs > 1 && Num_matched > 2)
2024     /* cycle thru all the matches */
2025       snprintf(Completed, sizeof(Completed), "%s", 
2026                Matches[(numtabs - 2) % Num_matched]);
2027
2028     strncpy (pt, Completed, buffer + len - pt - spaces);
2029   }
2030   else
2031     return 0;
2032
2033   return 1;
2034 }
2035
2036 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2037 {
2038   char var[STRING], *pt = buffer;
2039   int spaces;
2040   
2041   if (buffer[0] == 0)
2042     return 0;
2043
2044   SKIPWS (buffer);
2045   spaces = buffer - pt;
2046
2047   pt = buffer + pos - spaces;
2048   while ((pt > buffer) && !isspace ((unsigned char) *pt))
2049     pt--;
2050   pt++; /* move past the space */
2051   if (*pt == '=') /* abort if no var before the '=' */
2052     return 0;
2053
2054   if (mutt_strncmp (buffer, "set", 3) == 0)
2055   {
2056     int idx;
2057     strfcpy (var, pt, sizeof (var));
2058     /* ignore the trailing '=' when comparing */
2059     var[mutt_strlen (var) - 1] = 0;
2060     if ((idx = mutt_option_index (var)) == -1) 
2061       return 0; /* no such variable. */
2062     else
2063     {
2064       char tmp [LONG_STRING], tmp2[LONG_STRING];
2065       char *s, *d;
2066       size_t dlen = buffer + len - pt - spaces;
2067       char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2068
2069       tmp[0] = '\0';
2070
2071       if ((DTYPE(MuttVars[idx].type) == DT_STR) || 
2072           (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2073           (DTYPE(MuttVars[idx].type) == DT_RX))
2074       {
2075         strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2076         if (DTYPE (MuttVars[idx].type) == DT_PATH)
2077           mutt_pretty_mailbox (tmp);
2078       }
2079       else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2080       {
2081         rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2082       }
2083       else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2084         strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2085       else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2086         snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2087       else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2088       {
2089         const struct mapping_t *map;
2090         char *p;
2091
2092         switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2093         {
2094           case DT_SORT_ALIAS:
2095             map = SortAliasMethods;
2096             break;
2097           case DT_SORT_BROWSER:
2098             map = SortBrowserMethods;
2099             break;
2100           case DT_SORT_KEYS:
2101             if ((WithCrypto & APPLICATION_PGP))
2102               map = SortKeyMethods;
2103             else
2104               map = SortMethods;
2105             break;
2106           default:
2107             map = SortMethods;
2108             break;
2109         }
2110         p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2111         snprintf (tmp, sizeof (tmp), "%s%s%s",
2112                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2113                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2114                   p);
2115       }
2116       else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2117         strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2118       else
2119         return 0;
2120       
2121       for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2122       {
2123         if (*s == '\\' || *s == '"')
2124           *d++ = '\\';
2125         *d++ = *s++;
2126       }
2127       *d = '\0';
2128       
2129       strfcpy (tmp, pt, sizeof (tmp));
2130       snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2131           
2132       return 1;
2133     }
2134   }
2135   return 0;
2136 }
2137
2138 /* Implement the -Q command line flag */
2139 int mutt_query_variables (LIST *queries)
2140 {
2141   LIST *p;
2142   
2143   char errbuff[STRING];
2144   char command[STRING];
2145   
2146   BUFFER err, token;
2147   
2148   memset (&err, 0, sizeof (err));
2149   memset (&token, 0, sizeof (token));
2150   
2151   err.data = errbuff;
2152   err.dsize = sizeof (errbuff);
2153   
2154   for (p = queries; p; p = p->next)
2155   {
2156     snprintf (command, sizeof (command), "set ?%s\n", p->data);
2157     if (mutt_parse_rc_line (command, &token, &err) == -1)
2158     {
2159       fprintf (stderr, "%s\n", err.data);
2160       FREE (&token.data);
2161       return 1;
2162     }
2163     printf ("%s\n", err.data);
2164   }
2165   
2166   FREE (&token.data);
2167   return 0;
2168 }
2169
2170 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2171 {
2172   int i;
2173
2174   for (i=0; map[i].name; i++)
2175     if (map[i].value == val)
2176       return (map[i].name);
2177   return NULL;
2178 }
2179
2180 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2181 {
2182   int i;
2183
2184   for (i = 0; map[i].name; i++)
2185     if (ascii_strcasecmp (map[i].name, name) == 0)
2186       return (map[i].value);
2187   return (-1);
2188 }
2189
2190 #ifdef DEBUG
2191 static void start_debug (void)
2192 {
2193   time_t t;
2194   int i;
2195   char buf[_POSIX_PATH_MAX];
2196   char buf2[_POSIX_PATH_MAX];
2197
2198   /* rotate the old debug logs */
2199   for (i=3; i>=0; i--)
2200   {
2201     snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2202     snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2203     rename (buf, buf2);
2204   }
2205   if ((debugfile = safe_fopen(buf, "w")) != NULL)
2206   {
2207     t = time (0);
2208     setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2209     fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2210              MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2211   }
2212 }
2213 #endif
2214
2215 static int mutt_execute_commands (LIST *p)
2216 {
2217   BUFFER err, token;
2218   char errstr[SHORT_STRING];
2219
2220   memset (&err, 0, sizeof (err));
2221   err.data = errstr;
2222   err.dsize = sizeof (errstr);
2223   memset (&token, 0, sizeof (token));
2224   for (; p; p = p->next)
2225   {
2226     if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2227     {
2228       fprintf (stderr, _("Error in command line: %s\n"), err.data);
2229       FREE (&token.data);
2230       return (-1);
2231     }
2232   }
2233   FREE (&token.data);
2234   return 0;
2235 }
2236
2237 void mutt_init (int skip_sys_rc, LIST *commands)
2238 {
2239   struct passwd *pw;
2240   struct utsname utsname;
2241   char *p, buffer[STRING], error[STRING];
2242   int i, default_rc = 0, need_pause = 0;
2243   BUFFER err;
2244
2245   memset (&err, 0, sizeof (err));
2246   err.data = error;
2247   err.dsize = sizeof (error);
2248
2249   /* 
2250    * XXX - use something even more difficult to predict?
2251    */
2252   snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2253             "\033]9;%ld\a", (long) time (NULL));
2254   
2255   /* on one of the systems I use, getcwd() does not return the same prefix
2256      as is listed in the passwd file */
2257   if ((p = getenv ("HOME")))
2258     Homedir = safe_strdup (p);
2259
2260   /* Get some information about the user */
2261   if ((pw = getpwuid (getuid ())))
2262   {
2263     char rnbuf[STRING];
2264
2265     Username = safe_strdup (pw->pw_name);
2266     if (!Homedir)
2267       Homedir = safe_strdup (pw->pw_dir);
2268
2269     Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2270     Shell = safe_strdup (pw->pw_shell);
2271   }
2272   else 
2273   {
2274     if (!Homedir)
2275     {
2276       mutt_endwin (NULL);
2277       fputs (_("unable to determine home directory"), stderr);
2278       exit (1);
2279     }
2280     if ((p = getenv ("USER")))
2281       Username = safe_strdup (p);
2282     else
2283     {
2284       mutt_endwin (NULL);
2285       fputs (_("unable to determine username"), stderr);
2286       exit (1);
2287     }
2288     Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2289   }
2290
2291 #ifdef DEBUG
2292   /* Start up debugging mode if requested */
2293   if (debuglevel > 0)
2294     start_debug ();
2295 #endif
2296
2297   /* And about the host... */
2298   uname (&utsname);
2299   /* some systems report the FQDN instead of just the hostname */
2300   if ((p = strchr (utsname.nodename, '.')))
2301   {
2302     Hostname = mutt_substrdup (utsname.nodename, p);
2303     p++;
2304     strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2305   }
2306   else
2307     Hostname = safe_strdup (utsname.nodename);
2308
2309 #ifndef DOMAIN
2310 #define DOMAIN buffer
2311   if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2312     Fqdn = safe_strdup ("@");
2313   else
2314 #endif /* DOMAIN */
2315     if (*DOMAIN != '@')
2316   {
2317     Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2318     sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN);  /* __SPRINTF_CHECKED__ */
2319   }
2320   else
2321     Fqdn = safe_strdup(NONULL(Hostname));
2322
2323 #ifdef USE_NNTP
2324   {
2325     FILE *f;
2326     char *i;
2327
2328     if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2329     {
2330       buffer[0] = '\0';
2331       fgets (buffer, sizeof (buffer), f);
2332       p = &buffer;
2333       SKIPWS (p);
2334       i = p;
2335       while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2336       *i = '\0';
2337       NewsServer = safe_strdup (p);
2338       fclose (f);
2339     }
2340   }
2341   if ((p = getenv ("NNTPSERVER")))
2342     NewsServer = safe_strdup (p);
2343 #endif
2344
2345   if ((p = getenv ("MAIL")))
2346     Spoolfile = safe_strdup (p);
2347   else if ((p = getenv ("MAILDIR")))
2348     Spoolfile = safe_strdup (p);
2349   else
2350   {
2351 #ifdef HOMESPOOL
2352     mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2353 #else
2354     mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2355 #endif
2356     Spoolfile = safe_strdup (buffer);
2357   }
2358
2359   if ((p = getenv ("MAILCAPS")))
2360     MailcapPath = safe_strdup (p);
2361   else
2362   {
2363     /* Default search path from RFC1524 */
2364     MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2365   }
2366
2367   Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2368
2369   p = getenv ("VISUAL");
2370   if (!p)
2371   {
2372     p = getenv ("EDITOR");
2373     if (!p)
2374       p = "vi";
2375   }
2376   Editor = safe_strdup (p);
2377   Visual = safe_strdup (p);
2378
2379   if ((p = getenv ("REPLYTO")) != NULL)
2380   {
2381     BUFFER buf, token;
2382
2383     snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2384
2385     memset (&buf, 0, sizeof (buf));
2386     buf.data = buf.dptr = buffer;
2387     buf.dsize = mutt_strlen (buffer);
2388
2389     memset (&token, 0, sizeof (token));
2390     parse_my_hdr (&token, &buf, 0, &err);
2391     FREE (&token.data);
2392   }
2393
2394   if ((p = getenv ("EMAIL")) != NULL)
2395     From = rfc822_parse_adrlist (NULL, p);
2396   
2397   mutt_set_langinfo_charset ();
2398   mutt_set_charset (Charset);
2399   
2400   
2401   /* Set standard defaults */
2402   for (i = 0; MuttVars[i].option; i++)
2403   {
2404     mutt_set_default (&MuttVars[i]);
2405     mutt_restore_default (&MuttVars[i]);
2406   }
2407
2408   CurrentMenu = MENU_MAIN;
2409
2410
2411 #ifndef LOCALES_HACK
2412   /* Do we have a locale definition? */
2413   if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2414       ((p = getenv ("LANG")) != NULL && p[0]) ||
2415       ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2416     set_option (OPTLOCALES);
2417 #endif
2418
2419 #ifdef HAVE_GETSID
2420   /* Unset suspend by default if we're the session leader */
2421   if (getsid(0) == getpid())
2422     unset_option (OPTSUSPEND);
2423 #endif
2424
2425   mutt_init_history ();
2426
2427   
2428   
2429   
2430   /*
2431    * 
2432    *                       BIG FAT WARNING
2433    * 
2434    * When changing the code which looks for a configuration file,
2435    * please also change the corresponding code in muttbug.sh.in.
2436    * 
2437    * 
2438    */
2439   
2440   
2441   
2442   
2443   if (!Muttrc)
2444   {
2445     snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2446     if (access(buffer, F_OK) == -1)
2447       snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2448     if (access(buffer, F_OK) == -1)
2449       snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2450     if (access(buffer, F_OK) == -1)
2451       snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2452     
2453     default_rc = 1;
2454     Muttrc = safe_strdup (buffer);
2455   }
2456   else
2457   {
2458     strfcpy (buffer, Muttrc, sizeof (buffer));
2459     FREE (&Muttrc);
2460     mutt_expand_path (buffer, sizeof (buffer));
2461     Muttrc = safe_strdup (buffer);
2462   }
2463   FREE (&AliasFile);
2464   AliasFile = safe_strdup (NONULL(Muttrc));
2465
2466   /* Process the global rc file if it exists and the user hasn't explicity
2467      requested not to via "-n".  */
2468   if (!skip_sys_rc)
2469   {
2470     snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2471     if (access (buffer, F_OK) == -1)
2472       snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2473     if (access (buffer, F_OK) == -1)
2474       snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2475     if (access (buffer, F_OK) == -1)
2476       snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2477     if (access (buffer, F_OK) != -1)
2478     {
2479       if (source_rc (buffer, &err) != 0)
2480       {
2481         fputs (err.data, stderr);
2482         fputc ('\n', stderr);
2483         need_pause = 1;
2484       }
2485     }
2486   }
2487
2488   /* Read the user's initialization file.  */
2489   if (access (Muttrc, F_OK) != -1)
2490   {
2491     if (!option (OPTNOCURSES))
2492       endwin ();
2493     if (source_rc (Muttrc, &err) != 0)
2494     {
2495       fputs (err.data, stderr);
2496       fputc ('\n', stderr);
2497       need_pause = 1;
2498     }
2499   }
2500   else if (!default_rc)
2501   {
2502     /* file specified by -F does not exist */
2503     snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2504     mutt_endwin (buffer);
2505     exit (1);
2506   }
2507
2508   if (mutt_execute_commands (commands) != 0)
2509     need_pause = 1;
2510
2511   if (need_pause && !option (OPTNOCURSES))
2512   {
2513     if (mutt_any_key_to_continue (NULL) == -1)
2514       mutt_exit(1);
2515   }
2516
2517 #if 0
2518   set_option (OPTWEED); /* turn weeding on by default */
2519 #endif
2520 }
2521
2522 int mutt_get_hook_type (const char *name)
2523 {
2524   struct command_t *c;
2525
2526   for (c = Commands ; c->name ; c++)
2527     if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2528       return c->data;
2529   return 0;
2530 }