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