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