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           } else
1242             mutt_str_replace (&pp->pattern, (char*) p->init);
1243         }
1244       }
1245       break;
1246   }
1247
1248   if (p->flags & R_INDEX)
1249     set_option (OPTFORCEREDRAWINDEX);
1250   if (p->flags & R_PAGER)
1251     set_option (OPTFORCEREDRAWPAGER);
1252   if (p->flags & R_RESORT_SUB)
1253     set_option (OPTSORTSUBTHREADS);
1254   if (p->flags & R_RESORT)
1255     set_option (OPTNEEDRESORT);
1256   if (p->flags & R_RESORT_INIT)
1257     set_option (OPTRESORTINIT);
1258   if (p->flags & R_TREE)
1259     set_option (OPTREDRAWTREE);
1260 }
1261
1262 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1263 {
1264   int idx, query, unset, inv, reset, r = 0;
1265   char *p, scratch[_POSIX_PATH_MAX];
1266
1267   while (MoreArgs (s))
1268   {
1269     /* reset state variables */
1270     query = 0;
1271     unset = data & M_SET_UNSET;
1272     inv = data & M_SET_INV;
1273     reset = data & M_SET_RESET;
1274
1275     if (*s->dptr == '?')
1276     {
1277       query = 1;
1278       s->dptr++;
1279     }
1280     else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1281     {
1282       s->dptr += 2;
1283       unset = !unset;
1284     }
1285     else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1286     {
1287       s->dptr += 3;
1288       inv = !inv;
1289     }
1290     else if (*s->dptr == '&')
1291     {
1292       reset = 1;
1293       s->dptr++;
1294     }
1295
1296     /* get the variable name */
1297     mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1298
1299     if ((idx = mutt_option_index (tmp->data)) == -1 &&
1300         !(reset && !mutt_strcmp ("all", tmp->data)))
1301     {
1302       snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1303       return (-1);
1304     }
1305     SKIPWS (s->dptr);
1306
1307     if (reset)
1308     {
1309       if (query || unset || inv)
1310       {
1311         snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1312         return (-1);
1313       }
1314
1315       if (s && *s->dptr == '=')
1316       {
1317         snprintf (err->data, err->dsize, _("value is illegal with reset"));
1318         return (-1);
1319       }
1320      
1321       if (!mutt_strcmp ("all", tmp->data))
1322       {
1323         for (idx = 0; MuttVars[idx].option; idx++)
1324           mutt_restore_default (&MuttVars[idx]);
1325         return 0;
1326       }
1327       else
1328         mutt_restore_default (&MuttVars[idx]);
1329     } 
1330     else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
1331     { 
1332       if (s && *s->dptr == '=')
1333       {
1334         if (unset || inv || query)
1335         {
1336           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1337           return (-1);
1338         }
1339
1340         s->dptr++;
1341         mutt_extract_token (tmp, s, 0);
1342         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1343           unset = inv = 0;
1344         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1345           unset = 1;
1346         else
1347         {
1348           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1349           return (-1);
1350         }
1351       }
1352
1353       if (query)
1354       {
1355         snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1356                         ? _("%s is set") : _("%s is unset"), tmp->data);
1357         return 0;
1358       }
1359
1360       if (unset)
1361         unset_option (MuttVars[idx].data);
1362       else if (inv)
1363         toggle_option (MuttVars[idx].data);
1364       else
1365         set_option (MuttVars[idx].data);
1366     }
1367     else if (DTYPE (MuttVars[idx].type) == DT_STR ||
1368              DTYPE (MuttVars[idx].type) == DT_PATH ||
1369              DTYPE (MuttVars[idx].type) == DT_ADDR)
1370     {
1371       if (unset)
1372       {
1373         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1374           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1375         else
1376           FREE ((void *)MuttVars[idx].data);
1377       }
1378       else if (query || *s->dptr != '=')
1379       {
1380         char _tmp[STRING];
1381         char *val = NULL;
1382         
1383         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1384         {
1385           _tmp[0] = '\0';
1386           rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1387           val = _tmp;
1388         }
1389         else
1390           val = *((char **) MuttVars[idx].data);
1391         
1392         /* user requested the value of this variable */
1393         snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1394                   NONULL (val));
1395         break;
1396       }
1397       else
1398       {
1399         s->dptr++;
1400
1401         /* copy the value of the string */
1402         if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1403           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1404         else
1405           FREE ((void *)MuttVars[idx].data);
1406
1407         mutt_extract_token (tmp, s, 0);
1408         if (DTYPE (MuttVars[idx].type) == DT_PATH)
1409         {
1410           strfcpy (scratch, tmp->data, sizeof (scratch));
1411           mutt_expand_path (scratch, sizeof (scratch));
1412           *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1413         }
1414         else if (DTYPE (MuttVars[idx].type) == DT_STR)
1415         {
1416           *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1417           if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1418             mutt_set_charset (Charset);
1419         }
1420         else
1421         {
1422           *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1423         }
1424       }
1425     }
1426     else if (DTYPE(MuttVars[idx].type) == DT_RX)
1427     {
1428       REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1429       regex_t *rx;
1430       int e, flags = 0;
1431
1432       if (query || *s->dptr != '=')
1433       {
1434         /* user requested the value of this variable */
1435         snprintf (err->data, err->dsize, "%s=\"%s\"", MuttVars[idx].option,
1436                   NONULL (ptr->pattern));
1437         break;
1438       }
1439
1440       if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1441       {
1442         snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1443         r = -1;
1444         break;
1445       }
1446       
1447       s->dptr++;
1448
1449       /* copy the value of the string */
1450       mutt_extract_token (tmp, s, 0);
1451
1452       if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1453       {
1454         int not = 0;
1455
1456         /* $mask is case-sensitive */
1457         if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1458           flags |= mutt_which_case (tmp->data);
1459
1460         p = tmp->data;
1461         if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1462         {
1463           if (*p == '!')
1464           {
1465             not = 1;
1466             p++;
1467           }
1468         }
1469           
1470         rx = (regex_t *) safe_malloc (sizeof (regex_t));
1471         if ((e = REGCOMP (rx, p, flags)) != 0)
1472         {
1473           regerror (e, rx, err->data, err->dsize);
1474           regfree (rx);
1475           FREE (&rx);
1476           break;
1477         }
1478
1479         /* get here only if everything went smootly */
1480         if (ptr->pattern)
1481         {
1482           FREE (&ptr->pattern);
1483           regfree ((regex_t *) ptr->rx);
1484           FREE (&ptr->rx);
1485         }
1486
1487         ptr->pattern = safe_strdup (tmp->data);
1488         ptr->rx = rx;
1489         ptr->not = not;
1490
1491         /* $reply_regexp and $alterantes require special treatment */
1492         
1493         if (Context && Context->msgcount &&
1494             mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1495         {
1496           regmatch_t pmatch[1];
1497           int i;
1498           
1499 #define CUR_ENV Context->hdrs[i]->env
1500           for (i = 0; i < Context->msgcount; i++)
1501           {
1502             if (CUR_ENV && CUR_ENV->subject)
1503             {
1504               CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
1505                                     CUR_ENV->subject, 1, pmatch, 0)) ?
1506                                     CUR_ENV->subject : 
1507                                     CUR_ENV->subject + pmatch[0].rm_eo;
1508             }
1509           }
1510 #undef CUR_ENV
1511         }
1512       }
1513     }
1514     else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
1515     {
1516       if (query || *s->dptr != '=')
1517       {
1518         switch (DefaultMagic)
1519         {
1520           case M_MBOX:
1521             p = "mbox";
1522             break;
1523           case M_MMDF:
1524             p = "MMDF";
1525             break;
1526           case M_MH:
1527             p = "MH";
1528             break;
1529           case M_MAILDIR:
1530             p = "Maildir";
1531             break;
1532           default:
1533             p = "unknown";
1534             break;
1535         }
1536         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
1537         break;
1538       }
1539
1540       s->dptr++;
1541
1542       /* copy the value of the string */
1543       mutt_extract_token (tmp, s, 0);
1544       if (mx_set_magic (tmp->data))
1545       {
1546         snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
1547         r = -1;
1548         break;
1549       }
1550     }
1551     else if (DTYPE(MuttVars[idx].type) == DT_NUM)
1552     {
1553       short *ptr = (short *) MuttVars[idx].data;
1554       int val;
1555       char *t;
1556
1557       if (query || *s->dptr != '=')
1558       {
1559         /* user requested the value of this variable */
1560         snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, *ptr);
1561         break;
1562       }
1563
1564       s->dptr++;
1565
1566       mutt_extract_token (tmp, s, 0);
1567       val = strtol (tmp->data, &t, 0);
1568
1569       if (!*tmp->data || *t || (short) val != val)
1570       {
1571         snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1572         r = -1;
1573         break;
1574       }
1575       else
1576         *ptr = (short) val;
1577
1578       /* these ones need a sanity check */
1579       if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
1580       {
1581         if (*ptr < 0)
1582           *ptr = 0;
1583         mutt_init_history ();
1584       }
1585       else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
1586       {
1587         if (*ptr < 0)
1588           *ptr = 0;
1589       }
1590     }
1591     else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
1592     {
1593       if (query)
1594       {
1595         char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
1596
1597         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
1598                   vals [ quadoption (MuttVars[idx].data) ]);
1599         break;
1600       }
1601
1602       if (*s->dptr == '=')
1603       {
1604         s->dptr++;
1605         mutt_extract_token (tmp, s, 0);
1606         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1607           set_quadoption (MuttVars[idx].data, M_YES);
1608         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1609           set_quadoption (MuttVars[idx].data, M_NO);
1610         else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1611           set_quadoption (MuttVars[idx].data, M_ASKYES);
1612         else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1613           set_quadoption (MuttVars[idx].data, M_ASKNO);
1614         else
1615         {
1616           snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
1617           r = -1;
1618           break;
1619         }
1620       }
1621       else
1622       {
1623         if (inv)
1624           toggle_quadoption (MuttVars[idx].data);
1625         else if (unset)
1626           set_quadoption (MuttVars[idx].data, M_NO);
1627         else
1628           set_quadoption (MuttVars[idx].data, M_YES);
1629       }
1630     }
1631     else if (DTYPE (MuttVars[idx].type) == DT_SORT)
1632     {
1633       const struct mapping_t *map = NULL;
1634
1635       switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
1636       {
1637         case DT_SORT_ALIAS:
1638           map = SortAliasMethods;
1639           break;
1640         case DT_SORT_BROWSER:
1641           map = SortBrowserMethods;
1642           break;
1643         case DT_SORT_KEYS:
1644           if ((WithCrypto & APPLICATION_PGP))
1645             map = SortKeyMethods;
1646           break;
1647         case DT_SORT_AUX:
1648           map = SortAuxMethods;
1649           break;
1650         default:
1651           map = SortMethods;
1652           break;
1653       }
1654
1655       if (!map)
1656       {
1657         snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
1658         r = -1;
1659         break;
1660       }
1661       
1662       if (query || *s->dptr != '=')
1663       {
1664         p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
1665
1666         snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
1667                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
1668                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
1669                   p);
1670         return 0;
1671       }
1672       s->dptr++;
1673       mutt_extract_token (tmp, s , 0);
1674
1675       if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
1676       {
1677         r = -1;
1678         break;
1679       }
1680     }
1681     else
1682     {
1683       snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
1684       r = -1;
1685       break;
1686     }
1687
1688     if (MuttVars[idx].flags & R_INDEX)
1689       set_option (OPTFORCEREDRAWINDEX);
1690     if (MuttVars[idx].flags & R_PAGER)
1691       set_option (OPTFORCEREDRAWPAGER);
1692     if (MuttVars[idx].flags & R_RESORT_SUB)
1693       set_option (OPTSORTSUBTHREADS);
1694     if (MuttVars[idx].flags & R_RESORT)
1695       set_option (OPTNEEDRESORT);
1696     if (MuttVars[idx].flags & R_RESORT_INIT)
1697       set_option (OPTRESORTINIT);
1698     if (MuttVars[idx].flags & R_TREE)
1699       set_option (OPTREDRAWTREE);
1700   }
1701   return (r);
1702 }
1703
1704 #define MAXERRS 128
1705
1706 /* reads the specified initialization file.  returns -1 if errors were found
1707    so that we can pause to let the user know...  */
1708 static int source_rc (const char *rcfile, BUFFER *err)
1709 {
1710   FILE *f;
1711   int line = 0, rc = 0, conv = 0;
1712   BUFFER token;
1713   char *linebuf = NULL;
1714   char *currentline = NULL;
1715   size_t buflen;
1716   pid_t pid;
1717
1718   dprint (2, (debugfile, "Reading configuration file '%s'.\n",
1719           rcfile));
1720   
1721   if ((f = mutt_open_read (rcfile, &pid)) == NULL)
1722   {
1723     snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1724     return (-1);
1725   }
1726
1727   memset (&token, 0, sizeof (token));
1728   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line)) != NULL)
1729   {
1730     conv=ConfigCharset && (*ConfigCharset) && Charset;
1731     if (conv) 
1732     {
1733       currentline=safe_strdup(linebuf);
1734       if (!currentline) continue;
1735       mutt_convert_string(&currentline, ConfigCharset, Charset, 0);
1736     } 
1737     else 
1738       currentline=linebuf;
1739
1740     if (mutt_parse_rc_line (currentline, &token, err) == -1)
1741     {
1742       mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1743       if (--rc < -MAXERRS) 
1744       {
1745         if (conv) FREE(&currentline);
1746         break;
1747       }
1748     }
1749     else
1750     {
1751       if (rc < 0)
1752         rc = -1;
1753     }
1754     if (conv)
1755       FREE(&currentline);
1756   }
1757   FREE (&token.data);
1758   FREE (&linebuf);
1759   fclose (f);
1760   if (pid != -1)
1761     mutt_wait_filter (pid);
1762   if (rc)
1763   {
1764     /* the muttrc source keyword */
1765     snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
1766       : _("source: reading aborted due too many errors in %s"), rcfile);
1767     rc = -1;
1768   }
1769   return (rc);
1770 }
1771
1772 #undef MAXERRS
1773
1774 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1775 {
1776   char path[_POSIX_PATH_MAX];
1777   int rc = 0;
1778
1779   do
1780   {
1781     if (mutt_extract_token (tmp, s, 0) != 0)
1782     {
1783       snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1784       return (-1);
1785     }
1786
1787     strfcpy (path, tmp->data, sizeof (path));
1788     mutt_expand_path (path, sizeof (path));
1789
1790     rc += source_rc (path, err);
1791   }
1792   while (MoreArgs (s));
1793
1794   return ((rc < 0) ? -1 : 0);
1795 }
1796
1797 /* line         command to execute
1798
1799    token        scratch buffer to be used by parser.  caller should free
1800                 token->data when finished.  the reason for this variable is
1801                 to avoid having to allocate and deallocate a lot of memory
1802                 if we are parsing many lines.  the caller can pass in the
1803                 memory to use, which avoids having to create new space for
1804                 every call to this function.
1805
1806    err          where to write error messages */
1807 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
1808 {
1809   int i, r = -1;
1810   BUFFER expn;
1811
1812   memset (&expn, 0, sizeof (expn));
1813   expn.data = expn.dptr = line;
1814   expn.dsize = mutt_strlen (line);
1815
1816   *err->data = 0;
1817
1818   SKIPWS (expn.dptr);
1819   while (*expn.dptr)
1820   {
1821     if (*expn.dptr == '#')
1822       break; /* rest of line is a comment */
1823     if (*expn.dptr == ';')
1824     {
1825       expn.dptr++;
1826       continue;
1827     }
1828     mutt_extract_token (token, &expn, 0);
1829     for (i = 0; Commands[i].name; i++)
1830     {
1831       if (!mutt_strcmp (token->data, Commands[i].name))
1832       {
1833         if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1834           goto finish;
1835         break;
1836       }
1837     }
1838     if (!Commands[i].name)
1839     {
1840       snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
1841       goto finish;
1842     }
1843   }
1844   r = 0;
1845 finish:
1846   if (expn.destroy)
1847     FREE (&expn.data);
1848   return (r);
1849 }
1850
1851
1852 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
1853 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
1854 /* initial string that starts completion. No telling how much crap 
1855  * the user has typed so far. Allocate LONG_STRING just to be sure! */
1856 char User_typed [LONG_STRING] = {0}; 
1857
1858 int  Num_matched = 0; /* Number of matches for completion */
1859 char Completed [STRING] = {0}; /* completed string (command or variable) */
1860 char *Matches[MAX(NUMVARS,NUMCOMMANDS) + 1]; /* all the matches + User_typed */
1861
1862 /* helper function for completion.  Changes the dest buffer if
1863    necessary/possible to aid completion.
1864         dest == completion result gets here.
1865         src == candidate for completion.
1866         try == user entered data for completion.
1867         len == length of dest buffer.
1868 */
1869 static void candidate (char *dest, char *try, char *src, int len)
1870 {
1871   int l;
1872
1873   if (strstr (src, try) == src)
1874   {
1875     Matches[Num_matched++] = src;
1876     if (dest[0] == 0)
1877       strfcpy (dest, src, len);
1878     else
1879     {
1880       for (l = 0; src[l] && src[l] == dest[l]; l++);
1881       dest[l] = 0;
1882     }
1883   }
1884 }
1885
1886 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
1887 {
1888   char *pt = buffer;
1889   int num;
1890   int spaces; /* keep track of the number of leading spaces on the line */
1891
1892   SKIPWS (buffer);
1893   spaces = buffer - pt;
1894
1895   pt = buffer + pos - spaces;
1896   while ((pt > buffer) && !isspace ((unsigned char) *pt))
1897     pt--;
1898
1899   if (pt == buffer) /* complete cmd */
1900   {
1901     /* first TAB. Collect all the matches */
1902     if (numtabs == 1)
1903     {
1904       Num_matched = 0;
1905       strfcpy (User_typed, pt, sizeof (User_typed));
1906       memset (Matches, 0, sizeof (Matches));
1907       memset (Completed, 0, sizeof (Completed));
1908       for (num = 0; Commands[num].name; num++)
1909         candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
1910       Matches[Num_matched++] = User_typed;
1911
1912       /* All matches are stored. Longest non-ambiguous string is ""
1913        * i.e. dont change 'buffer'. Fake successful return this time */
1914       if (User_typed[0] == 0)
1915         return 1;
1916     }
1917
1918     if (Completed[0] == 0 && User_typed[0])
1919       return 0;
1920
1921      /* Num_matched will _always_ be atleast 1 since the initial
1922       * user-typed string is always stored */
1923     if (numtabs == 1 && Num_matched == 2)
1924       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1925     else if (numtabs > 1 && Num_matched > 2)
1926       /* cycle thru all the matches */
1927       snprintf(Completed, sizeof(Completed), "%s", 
1928                Matches[(numtabs - 2) % Num_matched]);
1929
1930     /* return the completed command */
1931     strncpy (buffer, Completed, len - spaces);
1932   }
1933   else if (!mutt_strncmp (buffer, "set", 3)
1934            || !mutt_strncmp (buffer, "unset", 5)
1935            || !mutt_strncmp (buffer, "reset", 5)
1936            || !mutt_strncmp (buffer, "toggle", 6))
1937   {             /* complete variables */
1938     char *prefixes[] = { "no", "inv", "?", "&", 0 };
1939     
1940     pt++;
1941     /* loop through all the possible prefixes (no, inv, ...) */
1942     if (!mutt_strncmp (buffer, "set", 3))
1943     {
1944       for (num = 0; prefixes[num]; num++)
1945       {
1946         if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
1947         {
1948           pt += mutt_strlen (prefixes[num]);
1949           break;
1950         }
1951       }
1952     }
1953     
1954     /* first TAB. Collect all the matches */
1955     if (numtabs == 1)
1956     {
1957       Num_matched = 0;
1958       strfcpy (User_typed, pt, sizeof (User_typed));
1959       memset (Matches, 0, sizeof (Matches));
1960       memset (Completed, 0, sizeof (Completed));
1961       for (num = 0; MuttVars[num].option; num++)
1962         candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
1963       Matches[Num_matched++] = User_typed;
1964
1965       /* All matches are stored. Longest non-ambiguous string is ""
1966        * i.e. dont change 'buffer'. Fake successful return this time */
1967       if (User_typed[0] == 0)
1968         return 1;
1969     }
1970
1971     if (Completed[0] == 0 && User_typed[0])
1972       return 0;
1973
1974     /* Num_matched will _always_ be atleast 1 since the initial
1975      * user-typed string is always stored */
1976     if (numtabs == 1 && Num_matched == 2)
1977       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
1978     else if (numtabs > 1 && Num_matched > 2)
1979     /* cycle thru all the matches */
1980       snprintf(Completed, sizeof(Completed), "%s", 
1981                Matches[(numtabs - 2) % Num_matched]);
1982
1983     strncpy (pt, Completed, buffer + len - pt - spaces);
1984   }
1985   else if (!mutt_strncmp (buffer, "exec", 4))
1986   {
1987     struct binding_t *menu = km_get_table (CurrentMenu);
1988
1989     if (!menu && CurrentMenu != MENU_PAGER)
1990       menu = OpGeneric;
1991     
1992     pt++;
1993     /* first TAB. Collect all the matches */
1994     if (numtabs == 1)
1995     {
1996       Num_matched = 0;
1997       strfcpy (User_typed, pt, sizeof (User_typed));
1998       memset (Matches, 0, sizeof (Matches));
1999       memset (Completed, 0, sizeof (Completed));
2000       for (num = 0; menu[num].name; num++)
2001         candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2002       /* try the generic menu */
2003       if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) 
2004       {
2005         menu = OpGeneric;
2006         for (num = 0; menu[num].name; num++)
2007           candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2008       }
2009       Matches[Num_matched++] = User_typed;
2010
2011       /* All matches are stored. Longest non-ambiguous string is ""
2012        * i.e. dont change 'buffer'. Fake successful return this time */
2013       if (User_typed[0] == 0)
2014         return 1;
2015     }
2016
2017     if (Completed[0] == 0 && User_typed[0])
2018       return 0;
2019
2020     /* Num_matched will _always_ be atleast 1 since the initial
2021      * user-typed string is always stored */
2022     if (numtabs == 1 && Num_matched == 2)
2023       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2024     else if (numtabs > 1 && Num_matched > 2)
2025     /* cycle thru all the matches */
2026       snprintf(Completed, sizeof(Completed), "%s", 
2027                Matches[(numtabs - 2) % Num_matched]);
2028
2029     strncpy (pt, Completed, buffer + len - pt - spaces);
2030   }
2031   else
2032     return 0;
2033
2034   return 1;
2035 }
2036
2037 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2038 {
2039   char var[STRING], *pt = buffer;
2040   int spaces;
2041   
2042   if (buffer[0] == 0)
2043     return 0;
2044
2045   SKIPWS (buffer);
2046   spaces = buffer - pt;
2047
2048   pt = buffer + pos - spaces;
2049   while ((pt > buffer) && !isspace ((unsigned char) *pt))
2050     pt--;
2051   pt++; /* move past the space */
2052   if (*pt == '=') /* abort if no var before the '=' */
2053     return 0;
2054
2055   if (mutt_strncmp (buffer, "set", 3) == 0)
2056   {
2057     int idx;
2058     strfcpy (var, pt, sizeof (var));
2059     /* ignore the trailing '=' when comparing */
2060     var[mutt_strlen (var) - 1] = 0;
2061     if ((idx = mutt_option_index (var)) == -1) 
2062       return 0; /* no such variable. */
2063     else
2064     {
2065       char tmp [LONG_STRING], tmp2[LONG_STRING];
2066       char *s, *d;
2067       size_t dlen = buffer + len - pt - spaces;
2068       char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2069
2070       tmp[0] = '\0';
2071
2072       if ((DTYPE(MuttVars[idx].type) == DT_STR) || 
2073           (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2074           (DTYPE(MuttVars[idx].type) == DT_RX))
2075       {
2076         strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2077         if (DTYPE (MuttVars[idx].type) == DT_PATH)
2078           mutt_pretty_mailbox (tmp);
2079       }
2080       else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2081       {
2082         rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2083       }
2084       else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2085         strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2086       else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2087         snprintf (tmp, sizeof (tmp), "%d", (*((short *) MuttVars[idx].data)));
2088       else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2089       {
2090         const struct mapping_t *map;
2091         char *p;
2092
2093         switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2094         {
2095           case DT_SORT_ALIAS:
2096             map = SortAliasMethods;
2097             break;
2098           case DT_SORT_BROWSER:
2099             map = SortBrowserMethods;
2100             break;
2101           case DT_SORT_KEYS:
2102             if ((WithCrypto & APPLICATION_PGP))
2103               map = SortKeyMethods;
2104             else
2105               map = SortMethods;
2106             break;
2107           default:
2108             map = SortMethods;
2109             break;
2110         }
2111         p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2112         snprintf (tmp, sizeof (tmp), "%s%s%s",
2113                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2114                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2115                   p);
2116       }
2117       else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2118         strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2119       else
2120         return 0;
2121       
2122       for (s = tmp, d = tmp2; *s && (d - tmp2) < sizeof (tmp2) - 2;)
2123       {
2124         if (*s == '\\' || *s == '"')
2125           *d++ = '\\';
2126         *d++ = *s++;
2127       }
2128       *d = '\0';
2129       
2130       strfcpy (tmp, pt, sizeof (tmp));
2131       snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2132           
2133       return 1;
2134     }
2135   }
2136   return 0;
2137 }
2138
2139 /* Implement the -Q command line flag */
2140 int mutt_query_variables (LIST *queries)
2141 {
2142   LIST *p;
2143   
2144   char errbuff[STRING];
2145   char command[STRING];
2146   
2147   BUFFER err, token;
2148   
2149   memset (&err, 0, sizeof (err));
2150   memset (&token, 0, sizeof (token));
2151   
2152   err.data = errbuff;
2153   err.dsize = sizeof (errbuff);
2154   
2155   for (p = queries; p; p = p->next)
2156   {
2157     snprintf (command, sizeof (command), "set ?%s\n", p->data);
2158     if (mutt_parse_rc_line (command, &token, &err) == -1)
2159     {
2160       fprintf (stderr, "%s\n", err.data);
2161       FREE (&token.data);
2162       return 1;
2163     }
2164     printf ("%s\n", err.data);
2165   }
2166   
2167   FREE (&token.data);
2168   return 0;
2169 }
2170
2171 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2172 {
2173   int i;
2174
2175   for (i=0; map[i].name; i++)
2176     if (map[i].value == val)
2177       return (map[i].name);
2178   return NULL;
2179 }
2180
2181 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2182 {
2183   int i;
2184
2185   for (i = 0; map[i].name; i++)
2186     if (ascii_strcasecmp (map[i].name, name) == 0)
2187       return (map[i].value);
2188   return (-1);
2189 }
2190
2191 #ifdef DEBUG
2192 static void start_debug (void)
2193 {
2194   time_t t;
2195   int i;
2196   char buf[_POSIX_PATH_MAX];
2197   char buf2[_POSIX_PATH_MAX];
2198
2199   /* rotate the old debug logs */
2200   for (i=3; i>=0; i--)
2201   {
2202     snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2203     snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2204     rename (buf, buf2);
2205   }
2206   if ((debugfile = safe_fopen(buf, "w")) != NULL)
2207   {
2208     t = time (0);
2209     setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2210     fprintf (debugfile, "Mutt-ng %s started at %s.\nDebugging at level %d.\n\n",
2211              MUTT_VERSION, asctime (localtime (&t)), debuglevel);
2212   }
2213 }
2214 #endif
2215
2216 static int mutt_execute_commands (LIST *p)
2217 {
2218   BUFFER err, token;
2219   char errstr[SHORT_STRING];
2220
2221   memset (&err, 0, sizeof (err));
2222   err.data = errstr;
2223   err.dsize = sizeof (errstr);
2224   memset (&token, 0, sizeof (token));
2225   for (; p; p = p->next)
2226   {
2227     if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2228     {
2229       fprintf (stderr, _("Error in command line: %s\n"), err.data);
2230       FREE (&token.data);
2231       return (-1);
2232     }
2233   }
2234   FREE (&token.data);
2235   return 0;
2236 }
2237
2238 void mutt_init (int skip_sys_rc, LIST *commands)
2239 {
2240   struct passwd *pw;
2241   struct utsname utsname;
2242   char *p, buffer[STRING], error[STRING];
2243   int i, default_rc = 0, need_pause = 0;
2244   BUFFER err;
2245
2246   memset (&err, 0, sizeof (err));
2247   err.data = error;
2248   err.dsize = sizeof (error);
2249
2250   /* 
2251    * XXX - use something even more difficult to predict?
2252    */
2253   snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2254             "\033]9;%ld\a", (long) time (NULL));
2255   
2256   /* on one of the systems I use, getcwd() does not return the same prefix
2257      as is listed in the passwd file */
2258   if ((p = getenv ("HOME")))
2259     Homedir = safe_strdup (p);
2260
2261   /* Get some information about the user */
2262   if ((pw = getpwuid (getuid ())))
2263   {
2264     char rnbuf[STRING];
2265
2266     Username = safe_strdup (pw->pw_name);
2267     if (!Homedir)
2268       Homedir = safe_strdup (pw->pw_dir);
2269
2270     Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2271     Shell = safe_strdup (pw->pw_shell);
2272   }
2273   else 
2274   {
2275     if (!Homedir)
2276     {
2277       mutt_endwin (NULL);
2278       fputs (_("unable to determine home directory"), stderr);
2279       exit (1);
2280     }
2281     if ((p = getenv ("USER")))
2282       Username = safe_strdup (p);
2283     else
2284     {
2285       mutt_endwin (NULL);
2286       fputs (_("unable to determine username"), stderr);
2287       exit (1);
2288     }
2289     Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2290   }
2291
2292 #ifdef DEBUG
2293   /* Start up debugging mode if requested */
2294   if (debuglevel > 0)
2295     start_debug ();
2296 #endif
2297
2298   /* And about the host... */
2299   uname (&utsname);
2300   /* some systems report the FQDN instead of just the hostname */
2301   if ((p = strchr (utsname.nodename, '.')))
2302   {
2303     Hostname = mutt_substrdup (utsname.nodename, p);
2304     p++;
2305     strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2306   }
2307   else
2308     Hostname = safe_strdup (utsname.nodename);
2309
2310 #ifndef DOMAIN
2311 #define DOMAIN buffer
2312   if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2313     Fqdn = safe_strdup ("@");
2314   else
2315 #endif /* DOMAIN */
2316     if (*DOMAIN != '@')
2317   {
2318     Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2319     sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN);  /* __SPRINTF_CHECKED__ */
2320   }
2321   else
2322     Fqdn = safe_strdup(NONULL(Hostname));
2323
2324 #ifdef USE_NNTP
2325   {
2326     FILE *f;
2327     char *i;
2328
2329     if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
2330     {
2331       buffer[0] = '\0';
2332       fgets (buffer, sizeof (buffer), f);
2333       p = &buffer;
2334       SKIPWS (p);
2335       i = p;
2336       while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
2337       *i = '\0';
2338       NewsServer = safe_strdup (p);
2339       fclose (f);
2340     }
2341   }
2342   if ((p = getenv ("NNTPSERVER")))
2343     NewsServer = safe_strdup (p);
2344 #endif
2345
2346   if ((p = getenv ("MAIL")))
2347     Spoolfile = safe_strdup (p);
2348   else if ((p = getenv ("MAILDIR")))
2349     Spoolfile = safe_strdup (p);
2350   else
2351   {
2352 #ifdef HOMESPOOL
2353     mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2354 #else
2355     mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2356 #endif
2357     Spoolfile = safe_strdup (buffer);
2358   }
2359
2360   if ((p = getenv ("MAILCAPS")))
2361     MailcapPath = safe_strdup (p);
2362   else
2363   {
2364     /* Default search path from RFC1524 */
2365     MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2366   }
2367
2368   Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2369
2370   p = getenv ("VISUAL");
2371   if (!p)
2372   {
2373     p = getenv ("EDITOR");
2374     if (!p)
2375       p = "vi";
2376   }
2377   Editor = safe_strdup (p);
2378   Visual = safe_strdup (p);
2379
2380   if ((p = getenv ("REPLYTO")) != NULL)
2381   {
2382     BUFFER buf, token;
2383
2384     snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
2385
2386     memset (&buf, 0, sizeof (buf));
2387     buf.data = buf.dptr = buffer;
2388     buf.dsize = mutt_strlen (buffer);
2389
2390     memset (&token, 0, sizeof (token));
2391     parse_my_hdr (&token, &buf, 0, &err);
2392     FREE (&token.data);
2393   }
2394
2395   if ((p = getenv ("EMAIL")) != NULL)
2396     From = rfc822_parse_adrlist (NULL, p);
2397   
2398   mutt_set_langinfo_charset ();
2399   mutt_set_charset (Charset);
2400   
2401   
2402   /* Set standard defaults */
2403   for (i = 0; MuttVars[i].option; i++)
2404   {
2405     mutt_set_default (&MuttVars[i]);
2406     mutt_restore_default (&MuttVars[i]);
2407   }
2408
2409   CurrentMenu = MENU_MAIN;
2410
2411
2412 #ifndef LOCALES_HACK
2413   /* Do we have a locale definition? */
2414   if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
2415       ((p = getenv ("LANG")) != NULL && p[0]) ||
2416       ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
2417     set_option (OPTLOCALES);
2418 #endif
2419
2420 #ifdef HAVE_GETSID
2421   /* Unset suspend by default if we're the session leader */
2422   if (getsid(0) == getpid())
2423     unset_option (OPTSUSPEND);
2424 #endif
2425
2426   mutt_init_history ();
2427
2428   
2429   
2430   
2431   /*
2432    * 
2433    *                       BIG FAT WARNING
2434    * 
2435    * When changing the code which looks for a configuration file,
2436    * please also change the corresponding code in muttbug.sh.in.
2437    * 
2438    * 
2439    */
2440   
2441   
2442   
2443   
2444   if (!Muttrc)
2445   {
2446     snprintf (buffer, sizeof(buffer), "%s/.muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2447     if (access(buffer, F_OK) == -1)
2448       snprintf (buffer, sizeof(buffer), "%s/.muttngrc", NONULL(Homedir));
2449     if (access(buffer, F_OK) == -1)
2450       snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc-%s", NONULL(Homedir), MUTT_VERSION);
2451     if (access(buffer, F_OK) == -1)
2452       snprintf (buffer, sizeof (buffer), "%s/.muttng/muttngrc", NONULL(Homedir));
2453     
2454     default_rc = 1;
2455     Muttrc = safe_strdup (buffer);
2456   }
2457   else
2458   {
2459     strfcpy (buffer, Muttrc, sizeof (buffer));
2460     FREE (&Muttrc);
2461     mutt_expand_path (buffer, sizeof (buffer));
2462     Muttrc = safe_strdup (buffer);
2463   }
2464   FREE (&AliasFile);
2465   AliasFile = safe_strdup (NONULL(Muttrc));
2466
2467   /* Process the global rc file if it exists and the user hasn't explicity
2468      requested not to via "-n".  */
2469   if (!skip_sys_rc)
2470   {
2471     snprintf (buffer, sizeof(buffer), "%s/Muttngrc-%s", SYSCONFDIR, MUTT_VERSION);
2472     if (access (buffer, F_OK) == -1)
2473       snprintf (buffer, sizeof(buffer), "%s/Muttngrc", SYSCONFDIR);
2474     if (access (buffer, F_OK) == -1)
2475       snprintf (buffer, sizeof (buffer), "%s/Muttngrc-%s", PKGDATADIR, MUTT_VERSION);
2476     if (access (buffer, F_OK) == -1)
2477       snprintf (buffer, sizeof (buffer), "%s/Muttngrc", PKGDATADIR);
2478     if (access (buffer, F_OK) != -1)
2479     {
2480       if (source_rc (buffer, &err) != 0)
2481       {
2482         fputs (err.data, stderr);
2483         fputc ('\n', stderr);
2484         need_pause = 1;
2485       }
2486     }
2487   }
2488
2489   /* Read the user's initialization file.  */
2490   if (access (Muttrc, F_OK) != -1)
2491   {
2492     if (!option (OPTNOCURSES))
2493       endwin ();
2494     if (source_rc (Muttrc, &err) != 0)
2495     {
2496       fputs (err.data, stderr);
2497       fputc ('\n', stderr);
2498       need_pause = 1;
2499     }
2500   }
2501   else if (!default_rc)
2502   {
2503     /* file specified by -F does not exist */
2504     snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
2505     mutt_endwin (buffer);
2506     exit (1);
2507   }
2508
2509   if (mutt_execute_commands (commands) != 0)
2510     need_pause = 1;
2511
2512   if (need_pause && !option (OPTNOCURSES))
2513   {
2514     if (mutt_any_key_to_continue (NULL) == -1)
2515       mutt_exit(1);
2516   }
2517
2518 #if 0
2519   set_option (OPTWEED); /* turn weeding on by default */
2520 #endif
2521 }
2522
2523 int mutt_get_hook_type (const char *name)
2524 {
2525   struct command_t *c;
2526
2527   for (c = Commands ; c->name ; c++)
2528     if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2529       return c->data;
2530   return 0;
2531 }