]> git.llucax.com Git - software/mutt-debian.git/blob - init.c
Make mutt-nntp depend on mutt >= 1.5.21-5
[software/mutt-debian.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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_menu.h"
27 #include "mutt_regex.h"
28 #include "history.h"
29 #include "keymap.h"
30 #include "mbyte.h"
31 #include "charset.h"
32 #include "mutt_crypt.h"
33 #include "mutt_idna.h"
34 #include "group.h"
35
36 #if defined(USE_SSL)
37 #include "mutt_ssl.h"
38 #endif
39
40
41
42 #include "mx.h"
43 #include "init.h"
44 #include "mailbox.h"
45
46 #include <ctype.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <sys/utsname.h>
51 #include <errno.h>
52 #include <sys/wait.h>
53 #include <sys/time.h>
54
55 #define CHECK_PAGER \
56   if ((CurrentMenu == MENU_PAGER) && (idx >= 0) &&      \
57             (MuttVars[idx].flags & R_RESORT)) \
58         { \
59           snprintf (err->data, err->dsize, \
60             _("Not available in this menu.")); \
61           return (-1); \
62         }
63
64 typedef struct myvar
65 {
66   char *name;
67   char *value;
68   struct myvar* next;
69 } myvar_t;
70
71 static myvar_t* MyVars;
72
73 static int var_to_string (int idx, char* val, size_t len);
74
75 static void myvar_set (const char* var, const char* val);
76 static const char* myvar_get (const char* var);
77 static void myvar_del (const char* var);
78
79 static void toggle_quadoption (int opt)
80 {
81   int n = opt/4;
82   int b = (opt % 4) * 2;
83
84   QuadOptions[n] ^= (1 << b);
85 }
86
87 void set_quadoption (int opt, int flag)
88 {
89   int n = opt/4;
90   int b = (opt % 4) * 2;
91
92   QuadOptions[n] &= ~(0x3 << b);
93   QuadOptions[n] |= (flag & 0x3) << b;
94 }
95
96 int quadoption (int opt)
97 {
98   int n = opt/4;
99   int b = (opt % 4) * 2;
100
101   return (QuadOptions[n] >> b) & 0x3;
102 }
103
104 int query_quadoption (int opt, const char *prompt)
105 {
106   int v = quadoption (opt);
107
108   switch (v)
109   {
110     case M_YES:
111     case M_NO:
112       return (v);
113
114     default:
115       v = mutt_yesorno (prompt, (v == M_ASKYES));
116       CLEARLINE (LINES - 1);
117       return (v);
118   }
119
120   /* not reached */
121 }
122
123 /* given the variable ``s'', return the index into the rc_vars array which
124    matches, or -1 if the variable is not found.  */
125 static int mutt_option_index (char *s)
126 {
127   int i;
128
129   for (i = 0; MuttVars[i].option; i++)
130     if (mutt_strcmp (s, MuttVars[i].option) == 0)
131       return (MuttVars[i].type == DT_SYN ?  mutt_option_index ((char *) MuttVars[i].data) : i);
132   return (-1);
133 }
134
135 int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags)
136 {
137   char          ch;
138   char          qc = 0; /* quote char */
139   char          *pc;
140
141   /* reset the destination pointer to the beginning of the buffer */
142   dest->dptr = dest->data;
143
144   SKIPWS (tok->dptr);
145   while ((ch = *tok->dptr))
146   {
147     if (!qc)
148     {
149       if ((ISSPACE (ch) && !(flags & M_TOKEN_SPACE)) ||
150           (ch == '#' && !(flags & M_TOKEN_COMMENT)) ||
151           (ch == '=' && (flags & M_TOKEN_EQUAL)) ||
152           (ch == ';' && !(flags & M_TOKEN_SEMICOLON)) ||
153           ((flags & M_TOKEN_PATTERN) && strchr ("~%=!|", ch)))
154         break;
155     }
156
157     tok->dptr++;
158
159     if (ch == qc)
160       qc = 0; /* end of quote */
161     else if (!qc && (ch == '\'' || ch == '"') && !(flags & M_TOKEN_QUOTE))
162       qc = ch;
163     else if (ch == '\\' && qc != '\'')
164     {
165         if (!*tok->dptr)
166             return -1; /* premature end of token */
167       switch (ch = *tok->dptr++)
168       {
169         case 'c':
170         case 'C':
171             if (!*tok->dptr)
172                 return -1; /* premature end of token */
173           mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr)
174                                     - '@') & 0x7f);
175           tok->dptr++;
176           break;
177         case 'r':
178           mutt_buffer_addch (dest, '\r');
179           break;
180         case 'n':
181           mutt_buffer_addch (dest, '\n');
182           break;
183         case 't':
184           mutt_buffer_addch (dest, '\t');
185           break;
186         case 'f':
187           mutt_buffer_addch (dest, '\f');
188           break;
189         case 'e':
190           mutt_buffer_addch (dest, '\033');
191           break;
192         default:
193           if (isdigit ((unsigned char) ch) &&
194               isdigit ((unsigned char) *tok->dptr) &&
195               isdigit ((unsigned char) *(tok->dptr + 1)))
196           {
197
198             mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504);
199             tok->dptr += 2;
200           }
201           else
202             mutt_buffer_addch (dest, ch);
203       }
204     }
205     else if (ch == '^' && (flags & M_TOKEN_CONDENSE))
206     {
207         if (!*tok->dptr)
208             return -1; /* premature end of token */
209       ch = *tok->dptr++;
210       if (ch == '^')
211         mutt_buffer_addch (dest, ch);
212       else if (ch == '[')
213         mutt_buffer_addch (dest, '\033');
214       else if (isalpha ((unsigned char) ch))
215         mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@');
216       else
217       {
218         mutt_buffer_addch (dest, '^');
219         mutt_buffer_addch (dest, ch);
220       }
221     }
222     else if (ch == '`' && (!qc || qc == '"'))
223     {
224       FILE      *fp;
225       pid_t     pid;
226       char      *cmd, *ptr;
227       size_t    expnlen;
228       BUFFER    expn;
229       int       line = 0;
230
231       pc = tok->dptr;
232       do {
233         if ((pc = strpbrk (pc, "\\`")))
234         {
235           /* skip any quoted chars */
236           if (*pc == '\\')
237             pc += 2;
238         }
239       } while (pc && *pc != '`');
240       if (!pc)
241       {
242         dprint (1, (debugfile, "mutt_get_token: mismatched backticks\n"));
243         return (-1);
244       }
245       cmd = mutt_substrdup (tok->dptr, pc);
246       if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0)
247       {
248         dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd));
249         FREE (&cmd);
250         return (-1);
251       }
252       FREE (&cmd);
253
254       tok->dptr = pc + 1;
255
256       /* read line */
257       memset (&expn, 0, sizeof (expn));
258       expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line, 0);
259       safe_fclose (&fp);
260       mutt_wait_filter (pid);
261
262       /* if we got output, make a new string consiting of the shell ouptput
263          plus whatever else was left on the original line */
264       /* BUT: If this is inside a quoted string, directly add output to 
265        * the token */
266       if (expn.data && qc)
267       {
268         mutt_buffer_addstr (dest, expn.data);
269         FREE (&expn.data);
270       }
271       else if (expn.data)
272       {
273         expnlen = mutt_strlen (expn.data);
274         tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1;
275         ptr = safe_malloc (tok->dsize);
276         memcpy (ptr, expn.data, expnlen);
277         strcpy (ptr + expnlen, tok->dptr);      /* __STRCPY_CHECKED__ */
278         if (tok->destroy)
279           FREE (&tok->data);
280         tok->data = ptr;
281         tok->dptr = ptr;
282         tok->destroy = 1; /* mark that the caller should destroy this data */
283         ptr = NULL;
284         FREE (&expn.data);
285       }
286     }
287     else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr)))
288     {
289       const char *env = NULL;
290       char *var = NULL;
291       int idx;
292
293       if (*tok->dptr == '{')
294       {
295         tok->dptr++;
296         if ((pc = strchr (tok->dptr, '}')))
297         {
298           var = mutt_substrdup (tok->dptr, pc);
299           tok->dptr = pc + 1;
300         }
301       }
302       else
303       {
304         for (pc = tok->dptr; isalnum ((unsigned char) *pc) || *pc == '_'; pc++)
305           ;
306         var = mutt_substrdup (tok->dptr, pc);
307         tok->dptr = pc;
308       }
309       if (var)
310       {
311         if ((env = getenv (var)) || (env = myvar_get (var)))
312           mutt_buffer_addstr (dest, env);
313         else if ((idx = mutt_option_index (var)) != -1)
314         {
315           /* expand settable mutt variables */
316           char val[LONG_STRING];
317
318           if (var_to_string (idx, val, sizeof (val)))
319             mutt_buffer_addstr (dest, val);
320         }
321         FREE (&var);
322       }
323     }
324     else
325       mutt_buffer_addch (dest, ch);
326   }
327   mutt_buffer_addch (dest, 0); /* terminate the string */
328   SKIPWS (tok->dptr);
329   return 0;
330 }
331
332 static void mutt_free_opt (struct option_t* p)
333 {
334   REGEXP* pp;
335
336   switch (p->type & DT_MASK)
337   {
338   case DT_ADDR:
339     rfc822_free_address ((ADDRESS**)p->data);
340     break;
341   case DT_RX:
342     pp = (REGEXP*)p->data;
343     FREE (&pp->pattern);
344     if (pp->rx)
345     {
346       regfree (pp->rx);
347       FREE (&pp->rx);
348     }
349     break;
350   case DT_PATH:
351   case DT_STR:
352     FREE ((char**)p->data);             /* __FREE_CHECKED__ */
353     break;
354   }
355 }
356
357 /* clean up before quitting */
358 void mutt_free_opts (void)
359 {
360   int i;
361
362   for (i = 0; MuttVars[i].option; i++)
363     mutt_free_opt (MuttVars + i);
364
365   mutt_free_rx_list (&Alternates);
366   mutt_free_rx_list (&UnAlternates);
367   mutt_free_rx_list (&MailLists);
368   mutt_free_rx_list (&UnMailLists);
369   mutt_free_rx_list (&SubscribedLists);
370   mutt_free_rx_list (&UnSubscribedLists);
371   mutt_free_rx_list (&NoSpamList);
372 }
373
374 static void add_to_list (LIST **list, const char *str)
375 {
376   LIST *t, *last = NULL;
377
378   /* don't add a NULL or empty string to the list */
379   if (!str || *str == '\0')
380     return;
381
382   /* check to make sure the item is not already on this list */
383   for (last = *list; last; last = last->next)
384   {
385     if (ascii_strcasecmp (str, last->data) == 0)
386     {
387       /* already on the list, so just ignore it */
388       last = NULL;
389       break;
390     }
391     if (!last->next)
392       break;
393   }
394
395   if (!*list || last)
396   {
397     t = (LIST *) safe_calloc (1, sizeof (LIST));
398     t->data = safe_strdup (str);
399     if (last)
400     {
401       last->next = t;
402       last = last->next;
403     }
404     else
405       *list = last = t;
406   }
407 }
408
409 int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err)
410 {
411   RX_LIST *t, *last = NULL;
412   REGEXP *rx;
413
414   if (!s || !*s)
415     return 0;
416
417   if (!(rx = mutt_compile_regexp (s, flags)))
418   {
419     snprintf (err->data, err->dsize, "Bad regexp: %s\n", s);
420     return -1;
421   }
422
423   /* check to make sure the item is not already on this list */
424   for (last = *list; last; last = last->next)
425   {
426     if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
427     {
428       /* already on the list, so just ignore it */
429       last = NULL;
430       break;
431     }
432     if (!last->next)
433       break;
434   }
435
436   if (!*list || last)
437   {
438     t = mutt_new_rx_list();
439     t->rx = rx;
440     if (last)
441     {
442       last->next = t;
443       last = last->next;
444     }
445     else
446       *list = last = t;
447   }
448   else /* duplicate */
449     mutt_free_regexp (&rx);
450
451   return 0;
452 }
453
454 static int remove_from_spam_list (SPAM_LIST **list, const char *pat);
455
456 static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err)
457 {
458   SPAM_LIST *t = NULL, *last = NULL;
459   REGEXP *rx;
460   int n;
461   const char *p;
462
463   if (!pat || !*pat || !templ)
464     return 0;
465
466   if (!(rx = mutt_compile_regexp (pat, REG_ICASE)))
467   {
468     snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat);
469     return -1;
470   }
471
472   /* check to make sure the item is not already on this list */
473   for (last = *list; last; last = last->next)
474   {
475     if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0)
476     {
477       /* Already on the list. Formerly we just skipped this case, but
478        * now we're supporting removals, which means we're supporting
479        * re-adds conceptually. So we probably want this to imply a
480        * removal, then do an add. We can achieve the removal by freeing
481        * the template, and leaving t pointed at the current item.
482        */
483       t = last;
484       FREE(&t->template);
485       break;
486     }
487     if (!last->next)
488       break;
489   }
490
491   /* If t is set, it's pointing into an extant SPAM_LIST* that we want to
492    * update. Otherwise we want to make a new one to link at the list's end.
493    */
494   if (!t)
495   {
496     t = mutt_new_spam_list();
497     t->rx = rx;
498     if (last)
499       last->next = t;
500     else
501       *list = t;
502   }
503
504   /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */
505   t->template = safe_strdup(templ);
506
507   /* Find highest match number in template string */
508   t->nmatch = 0;
509   for (p = templ; *p;)
510   {
511     if (*p == '%')
512     {
513         n = atoi(++p);
514         if (n > t->nmatch)
515           t->nmatch = n;
516         while (*p && isdigit((int)*p))
517           ++p;
518     }
519     else
520         ++p;
521   }
522
523   if (t->nmatch > t->rx->rx->re_nsub)
524   {
525     snprintf (err->data, err->dsize, _("Not enough subexpressions for spam "
526                                        "template"));
527     remove_from_spam_list(list, pat);
528     return -1;
529   }
530
531   t->nmatch++;         /* match 0 is always the whole expr */
532
533   return 0;
534 }
535
536 static int remove_from_spam_list (SPAM_LIST **list, const char *pat)
537 {
538   SPAM_LIST *spam, *prev;
539   int nremoved = 0;
540
541   /* Being first is a special case. */
542   spam = *list;
543   if (!spam)
544     return 0;
545   if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat))
546   {
547     *list = spam->next;
548     mutt_free_regexp(&spam->rx);
549     FREE(&spam->template);
550     FREE(&spam);
551     return 1;
552   }
553
554   prev = spam;
555   for (spam = prev->next; spam;)
556   {
557     if (!mutt_strcmp(spam->rx->pattern, pat))
558     {
559       prev->next = spam->next;
560       mutt_free_regexp(&spam->rx);
561       FREE(&spam->template);
562       FREE(&spam);
563       spam = prev->next;
564       ++nremoved;
565     }
566     else
567       spam = spam->next;
568   }
569
570   return nremoved;
571 }
572
573
574 static void remove_from_list (LIST **l, const char *str)
575 {
576   LIST *p, *last = NULL;
577
578   if (mutt_strcmp ("*", str) == 0)
579     mutt_free_list (l);    /* ``unCMD *'' means delete all current entries */
580   else
581   {
582     p = *l;
583     last = NULL;
584     while (p)
585     {
586       if (ascii_strcasecmp (str, p->data) == 0)
587       {
588         FREE (&p->data);
589         if (last)
590           last->next = p->next;
591         else
592           (*l) = p->next;
593         FREE (&p);
594       }
595       else
596       {
597         last = p;
598         p = p->next;
599       }
600     }
601   }
602 }
603
604 static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
605 {
606   do
607   {
608     mutt_extract_token (buf, s, 0);
609
610     /* don't add "*" to the unignore list */
611     if (strcmp (buf->data, "*")) 
612       add_to_list (&UnIgnore, buf->data);
613
614     remove_from_list (&Ignore, buf->data);
615   }
616   while (MoreArgs (s));
617
618   return 0;
619 }
620
621 static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
622 {
623   do
624   {
625     mutt_extract_token (buf, s, 0);
626     remove_from_list (&UnIgnore, buf->data);
627     add_to_list (&Ignore, buf->data);
628   }
629   while (MoreArgs (s));
630
631   return 0;
632 }
633
634 static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
635 {
636   do
637   {
638     mutt_extract_token (buf, s, 0);
639     add_to_list ((LIST **) data, buf->data);
640   }
641   while (MoreArgs (s));
642
643   return 0;
644 }
645
646 static void _alternates_clean (void)
647 {
648   int i;
649   if (Context && Context->msgcount) 
650   {
651     for (i = 0; i < Context->msgcount; i++)
652       Context->hdrs[i]->recip_valid = 0;
653   }
654 }
655
656 static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
657 {
658   group_context_t *gc = NULL;
659   
660   _alternates_clean();
661
662   do
663   {
664     mutt_extract_token (buf, s, 0);
665
666     if (parse_group_context (&gc, buf, s, data, err) == -1)
667       goto bail;
668
669     mutt_remove_from_rx_list (&UnAlternates, buf->data);
670
671     if (mutt_add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0)
672       goto bail;
673
674     if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
675       goto bail;
676   }
677   while (MoreArgs (s));
678   
679   mutt_group_context_destroy (&gc);
680   return 0;
681   
682  bail:
683   mutt_group_context_destroy (&gc);
684   return -1;
685 }
686
687 static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
688 {
689   _alternates_clean();
690   do
691   {
692     mutt_extract_token (buf, s, 0);
693     mutt_remove_from_rx_list (&Alternates, buf->data);
694
695     if (mutt_strcmp (buf->data, "*") &&
696         mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
697       return -1;
698
699   }
700   while (MoreArgs (s));
701
702   return 0;
703 }
704
705 static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
706 {
707   BUFFER templ;
708
709   memset(&templ, 0, sizeof(templ));
710
711   /* Insist on at least one parameter */
712   if (!MoreArgs(s))
713   {
714     if (data == M_SPAM)
715       strfcpy(err->data, _("spam: no matching pattern"), err->dsize);
716     else
717       strfcpy(err->data, _("nospam: no matching pattern"), err->dsize);
718     return -1;
719   }
720
721   /* Extract the first token, a regexp */
722   mutt_extract_token (buf, s, 0);
723
724   /* data should be either M_SPAM or M_NOSPAM. M_SPAM is for spam commands. */
725   if (data == M_SPAM)
726   {
727     /* If there's a second parameter, it's a template for the spam tag. */
728     if (MoreArgs(s))
729     {
730       mutt_extract_token (&templ, s, 0);
731
732       /* Add to the spam list. */
733       if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) {
734           FREE(&templ.data);
735           return -1;
736       }
737       FREE(&templ.data);
738     }
739
740     /* If not, try to remove from the nospam list. */
741     else
742     {
743       mutt_remove_from_rx_list(&NoSpamList, buf->data);
744     }
745
746     return 0;
747   }
748
749   /* M_NOSPAM is for nospam commands. */
750   else if (data == M_NOSPAM)
751   {
752     /* nospam only ever has one parameter. */
753
754     /* "*" is a special case. */
755     if (!mutt_strcmp(buf->data, "*"))
756     {
757       mutt_free_spam_list (&SpamList);
758       mutt_free_rx_list (&NoSpamList);
759       return 0;
760     }
761
762     /* If it's on the spam list, just remove it. */
763     if (remove_from_spam_list(&SpamList, buf->data) != 0)
764       return 0;
765
766     /* Otherwise, add it to the nospam list. */
767     if (mutt_add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0)
768       return -1;
769
770     return 0;
771   }
772
773   /* This should not happen. */
774   strfcpy(err->data, "This is no good at all.", err->dsize);
775   return -1;
776 }
777
778
779 static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
780 {
781   do
782   {
783     mutt_extract_token (buf, s, 0);
784     /*
785      * Check for deletion of entire list
786      */
787     if (mutt_strcmp (buf->data, "*") == 0)
788     {
789       mutt_free_list ((LIST **) data);
790       break;
791     }
792     remove_from_list ((LIST **) data, buf->data);
793   }
794   while (MoreArgs (s));
795
796   return 0;
797 }
798
799 static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
800 {
801   group_context_t *gc = NULL;
802
803   do
804   {
805     mutt_extract_token (buf, s, 0);
806     
807     if (parse_group_context (&gc, buf, s, data, err) == -1)
808       goto bail;
809     
810     mutt_remove_from_rx_list (&UnMailLists, buf->data);
811     
812     if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
813       goto bail;
814     
815     if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
816       goto bail;
817   }
818   while (MoreArgs (s));
819
820   mutt_group_context_destroy (&gc);
821   return 0;
822   
823  bail:
824   mutt_group_context_destroy (&gc);
825   return -1;
826 }
827
828 typedef enum group_state_t {
829   NONE, RX, ADDR
830 } group_state_t;
831
832 static int parse_group (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
833 {
834   group_context_t *gc = NULL;
835   group_state_t state = NONE;
836   ADDRESS *addr = NULL;
837   char *estr = NULL;
838
839   do
840   {
841     mutt_extract_token (buf, s, 0);
842     if (parse_group_context (&gc, buf, s, data, err) == -1)
843       goto bail;
844
845     if (data == M_UNGROUP && !mutt_strcasecmp (buf->data, "*"))
846     {
847       if (mutt_group_context_clear (&gc) < 0)
848         goto bail;
849       goto out;
850     }
851
852     if (!mutt_strcasecmp (buf->data, "-rx"))
853       state = RX;
854     else if (!mutt_strcasecmp (buf->data, "-addr"))
855       state = ADDR;
856     else
857     {
858       switch (state)
859       {
860         case NONE:
861           snprintf (err->data, err->dsize, _("%sgroup: missing -rx or -addr."),
862                    data == M_UNGROUP ? "un" : "");
863           goto bail;
864
865         case RX:
866           if (data == M_GROUP &&
867               mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
868             goto bail;
869           else if (data == M_UNGROUP &&
870                    mutt_group_context_remove_rx (gc, buf->data) < 0)
871             goto bail;
872           break;
873
874         case ADDR:
875           if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL)
876             goto bail;
877           if (mutt_addrlist_to_idna (addr, &estr))
878           { 
879             snprintf (err->data, err->dsize, _("%sgroup: warning: bad IDN '%s'.\n"),
880                       data == 1 ? "un" : "", estr);
881             goto bail;
882           }
883           if (data == M_GROUP)
884             mutt_group_context_add_adrlist (gc, addr);
885           else if (data == M_UNGROUP)
886             mutt_group_context_remove_adrlist (gc, addr);
887           rfc822_free_address (&addr);
888           break;
889       }
890     }
891   } while (MoreArgs (s));
892
893 out:
894   mutt_group_context_destroy (&gc);
895   return 0;
896
897 bail:
898   mutt_group_context_destroy (&gc);
899   return -1;
900 }
901
902 /* always wise to do what someone else did before */
903 static void _attachments_clean (void)
904 {
905   int i;
906   if (Context && Context->msgcount) 
907   {
908     for (i = 0; i < Context->msgcount; i++)
909       Context->hdrs[i]->attach_valid = 0;
910   }
911 }
912
913 static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
914 {
915   ATTACH_MATCH *a;
916   LIST *listp, *lastp;
917   char *p;
918   char *tmpminor;
919   int len;
920   int ret;
921
922   /* Find the last item in the list that data points to. */
923   lastp = NULL;
924   dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n",
925               (void *)ldata, (void *)*ldata));
926   for (listp = *ldata; listp; listp = listp->next)
927   {
928     a = (ATTACH_MATCH *)listp->data;
929     dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n",
930                 a->major, a->minor));
931     lastp = listp;
932   }
933
934   do
935   {
936     mutt_extract_token (buf, s, 0);
937
938     if (!buf->data || *buf->data == '\0')
939       continue;
940    
941     a = safe_malloc(sizeof(ATTACH_MATCH));
942
943     /* some cheap hacks that I expect to remove */
944     if (!ascii_strcasecmp(buf->data, "any"))
945       a->major = safe_strdup("*/.*");
946     else if (!ascii_strcasecmp(buf->data, "none"))
947       a->major = safe_strdup("cheap_hack/this_should_never_match");
948     else
949       a->major = safe_strdup(buf->data);
950
951     if ((p = strchr(a->major, '/')))
952     {
953       *p = '\0';
954       ++p;
955       a->minor = p;
956     }
957     else
958     {
959       a->minor = "unknown";
960     }
961
962     len = strlen(a->minor);
963     tmpminor = safe_malloc(len+3);
964     strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
965     tmpminor[0] = '^';
966     tmpminor[len+1] = '$';
967     tmpminor[len+2] = '\0';
968
969     a->major_int = mutt_check_mime_type(a->major);
970     ret = REGCOMP(&a->minor_rx, tmpminor, REG_ICASE);
971
972     FREE(&tmpminor);
973
974     if (ret)
975     {
976       regerror(ret, &a->minor_rx, err->data, err->dsize);
977       FREE(&a->major);
978       FREE(&a);
979       return -1;
980     }
981
982     dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n",
983                 a->major, a->minor, a->major_int));
984
985     listp = safe_malloc(sizeof(LIST));
986     listp->data = (char *)a;
987     listp->next = NULL;
988     if (lastp)
989     {
990       lastp->next = listp;
991     }
992     else
993     {
994       *ldata = listp;
995     }
996     lastp = listp;
997   }
998   while (MoreArgs (s));
999    
1000   _attachments_clean();
1001   return 0;
1002 }
1003
1004 static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
1005 {
1006   ATTACH_MATCH *a;
1007   LIST *lp, *lastp, *newlp;
1008   char *tmp;
1009   int major;
1010   char *minor;
1011
1012   do
1013   {
1014     mutt_extract_token (buf, s, 0);
1015
1016     if (!ascii_strcasecmp(buf->data, "any"))
1017       tmp = safe_strdup("*/.*");
1018     else if (!ascii_strcasecmp(buf->data, "none"))
1019       tmp = safe_strdup("cheap_hack/this_should_never_match");
1020     else
1021       tmp = safe_strdup(buf->data);
1022
1023     if ((minor = strchr(tmp, '/')))
1024     {
1025       *minor = '\0';
1026       ++minor;
1027     }
1028     else
1029     {
1030       minor = "unknown";
1031     }
1032     major = mutt_check_mime_type(tmp);
1033
1034     /* We must do our own walk here because remove_from_list() will only
1035      * remove the LIST->data, not anything pointed to by the LIST->data. */
1036     lastp = NULL;
1037     for(lp = *ldata; lp; )
1038     {
1039       a = (ATTACH_MATCH *)lp->data;
1040       dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n",
1041                   a->major, a->minor, a->major_int, tmp, minor, major));
1042       if (a->major_int == major && !mutt_strcasecmp(minor, a->minor))
1043       {
1044         dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n",
1045                     a->major, a->minor, a->major_int));
1046         regfree(&a->minor_rx);
1047         FREE(&a->major);
1048
1049         /* Relink backward */
1050         if (lastp)
1051           lastp->next = lp->next;
1052         else
1053           *ldata = lp->next;
1054
1055         newlp = lp->next;
1056         FREE(&lp->data);        /* same as a */
1057         FREE(&lp);
1058         lp = newlp;
1059         continue;
1060       }
1061
1062       lastp = lp;
1063       lp = lp->next;
1064     }
1065
1066   }
1067   while (MoreArgs (s));
1068    
1069   FREE(&tmp);
1070   _attachments_clean();
1071   return 0;
1072 }
1073
1074 static int print_attach_list (LIST *lp, char op, char *name)
1075 {
1076   while (lp) {
1077     printf("attachments %c%s %s/%s\n", op, name,
1078            ((ATTACH_MATCH *)lp->data)->major,
1079            ((ATTACH_MATCH *)lp->data)->minor);
1080     lp = lp->next;
1081   }
1082
1083   return 0;
1084 }
1085
1086
1087 static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1088 {
1089   char op, *category;
1090   LIST **listp;
1091
1092   mutt_extract_token(buf, s, 0);
1093   if (!buf->data || *buf->data == '\0') {
1094     strfcpy(err->data, _("attachments: no disposition"), err->dsize);
1095     return -1;
1096   }
1097
1098   category = buf->data;
1099   op = *category++;
1100
1101   if (op == '?') {
1102     mutt_endwin (NULL);
1103     fflush (stdout);
1104     printf("\nCurrent attachments settings:\n\n");
1105     print_attach_list(AttachAllow,   '+', "A");
1106     print_attach_list(AttachExclude, '-', "A");
1107     print_attach_list(InlineAllow,   '+', "I");
1108     print_attach_list(InlineExclude, '-', "I");
1109     set_option (OPTFORCEREDRAWINDEX);
1110     set_option (OPTFORCEREDRAWPAGER);
1111     mutt_any_key_to_continue (NULL);
1112     return 0;
1113   }
1114
1115   if (op != '+' && op != '-') {
1116     op = '+';
1117     category--;
1118   }
1119   if (!ascii_strncasecmp(category, "attachment", strlen(category))) {
1120     if (op == '+')
1121       listp = &AttachAllow;
1122     else
1123       listp = &AttachExclude;
1124   }
1125   else if (!ascii_strncasecmp(category, "inline", strlen(category))) {
1126     if (op == '+')
1127       listp = &InlineAllow;
1128     else
1129       listp = &InlineExclude;
1130   }
1131   else {
1132     strfcpy(err->data, _("attachments: invalid disposition"), err->dsize);
1133     return -1;
1134   }
1135
1136   return parse_attach_list(buf, s, listp, err);
1137 }
1138
1139 static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1140 {
1141   char op, *p;
1142   LIST **listp;
1143
1144   mutt_extract_token(buf, s, 0);
1145   if (!buf->data || *buf->data == '\0') {
1146     strfcpy(err->data, _("unattachments: no disposition"), err->dsize);
1147     return -1;
1148   }
1149
1150   p = buf->data;
1151   op = *p++;
1152   if (op != '+' && op != '-') {
1153     op = '+';
1154     p--;
1155   }
1156   if (!ascii_strncasecmp(p, "attachment", strlen(p))) {
1157     if (op == '+')
1158       listp = &AttachAllow;
1159     else
1160       listp = &AttachExclude;
1161   }
1162   else if (!ascii_strncasecmp(p, "inline", strlen(p))) {
1163     if (op == '+')
1164       listp = &InlineAllow;
1165     else
1166       listp = &InlineExclude;
1167   }
1168   else {
1169     strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize);
1170     return -1;
1171   }
1172
1173   return parse_unattach_list(buf, s, listp, err);
1174 }
1175
1176 static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1177 {
1178   do
1179   {
1180     mutt_extract_token (buf, s, 0);
1181     mutt_remove_from_rx_list (&SubscribedLists, buf->data);
1182     mutt_remove_from_rx_list (&MailLists, buf->data);
1183     
1184     if (mutt_strcmp (buf->data, "*") && 
1185         mutt_add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0)
1186       return -1;
1187   }
1188   while (MoreArgs (s));
1189
1190   return 0;
1191 }
1192
1193 static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1194 {
1195   group_context_t *gc = NULL;
1196   
1197   do
1198   {
1199     mutt_extract_token (buf, s, 0);
1200
1201     if (parse_group_context (&gc, buf, s, data, err) == -1)
1202       goto bail;
1203     
1204     mutt_remove_from_rx_list (&UnMailLists, buf->data);
1205     mutt_remove_from_rx_list (&UnSubscribedLists, buf->data);
1206
1207     if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0)
1208       goto bail;
1209     if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0)
1210       goto bail;
1211     if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0)
1212       goto bail;
1213   }
1214   while (MoreArgs (s));
1215   
1216   mutt_group_context_destroy (&gc);
1217   return 0;
1218   
1219  bail:
1220   mutt_group_context_destroy (&gc);
1221   return -1;
1222 }
1223
1224 static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1225 {
1226   do
1227   {
1228     mutt_extract_token (buf, s, 0);
1229     mutt_remove_from_rx_list (&SubscribedLists, buf->data);
1230     
1231     if (mutt_strcmp (buf->data, "*") &&
1232         mutt_add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0)
1233       return -1;
1234   }
1235   while (MoreArgs (s));
1236
1237   return 0;
1238 }
1239   
1240 static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1241 {
1242   ALIAS *tmp, *last = NULL;
1243
1244   do
1245   {
1246     mutt_extract_token (buf, s, 0);
1247
1248     if (mutt_strcmp ("*", buf->data) == 0)
1249     {
1250       if (CurrentMenu == MENU_ALIAS)
1251       {
1252         for (tmp = Aliases; tmp ; tmp = tmp->next) 
1253           tmp->del = 1;
1254         set_option (OPTFORCEREDRAWINDEX);
1255       }
1256       else
1257         mutt_free_alias (&Aliases);
1258       break;
1259     }
1260     else
1261       for (tmp = Aliases; tmp; tmp = tmp->next)
1262       {
1263         if (mutt_strcasecmp (buf->data, tmp->name) == 0)
1264         {
1265           if (CurrentMenu == MENU_ALIAS)
1266           {
1267             tmp->del = 1;
1268             set_option (OPTFORCEREDRAWINDEX);
1269             break;
1270           }
1271
1272           if (last)
1273             last->next = tmp->next;
1274           else
1275             Aliases = tmp->next;
1276           tmp->next = NULL;
1277           mutt_free_alias (&tmp);
1278           break;
1279         }
1280         last = tmp;
1281       }
1282   }
1283   while (MoreArgs (s));
1284   return 0;
1285 }
1286
1287 static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1288 {
1289   ALIAS *tmp = Aliases;
1290   ALIAS *last = NULL;
1291   char *estr = NULL;
1292   group_context_t *gc = NULL;
1293   
1294   if (!MoreArgs (s))
1295   {
1296     strfcpy (err->data, _("alias: no address"), err->dsize);
1297     return (-1);
1298   }
1299
1300   mutt_extract_token (buf, s, 0);
1301
1302   if (parse_group_context (&gc, buf, s, data, err) == -1)
1303     return -1;
1304   
1305   /* check to see if an alias with this name already exists */
1306   for (; tmp; tmp = tmp->next)
1307   {
1308     if (!mutt_strcasecmp (tmp->name, buf->data))
1309       break;
1310     last = tmp;
1311   }
1312
1313   if (!tmp)
1314   {
1315     /* create a new alias */
1316     tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS));
1317     tmp->self = tmp;
1318     tmp->name = safe_strdup (buf->data);
1319     /* give the main addressbook code a chance */
1320     if (CurrentMenu == MENU_ALIAS)
1321       set_option (OPTMENUCALLER);
1322   }
1323   else
1324   {
1325     mutt_alias_delete_reverse (tmp);
1326     /* override the previous value */
1327     rfc822_free_address (&tmp->addr);
1328     if (CurrentMenu == MENU_ALIAS)
1329       set_option (OPTFORCEREDRAWINDEX);
1330   }
1331
1332   mutt_extract_token (buf, s, M_TOKEN_QUOTE | M_TOKEN_SPACE | M_TOKEN_SEMICOLON);
1333   dprint (3, (debugfile, "parse_alias: Second token is '%s'.\n",
1334               buf->data));
1335
1336   tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data);
1337
1338   if (last)
1339     last->next = tmp;
1340   else
1341     Aliases = tmp;
1342   if (mutt_addrlist_to_idna (tmp->addr, &estr))
1343   {
1344     snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"),
1345               estr, tmp->name);
1346     goto bail;
1347   }
1348
1349   mutt_group_context_add_adrlist (gc, tmp->addr);
1350   mutt_alias_add_reverse (tmp);
1351
1352 #ifdef DEBUG
1353   if (debuglevel >= 2) 
1354   {
1355     ADDRESS *a;
1356     /* A group is terminated with an empty address, so check a->mailbox */
1357     for (a = tmp->addr; a && a->mailbox; a = a->next)
1358     {
1359       if (!a->group)
1360         dprint (3, (debugfile, "parse_alias:   %s\n",
1361                     a->mailbox));
1362       else
1363         dprint (3, (debugfile, "parse_alias:   Group %s\n",
1364                     a->mailbox));
1365     }
1366   }
1367 #endif
1368   mutt_group_context_destroy (&gc);
1369   return 0;
1370   
1371   bail:
1372   mutt_group_context_destroy (&gc);
1373   return -1;
1374 }
1375
1376 static int
1377 parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1378 {
1379   LIST *last = NULL;
1380   LIST *tmp = UserHeader;
1381   LIST *ptr;
1382   size_t l;
1383
1384   do
1385   {
1386     mutt_extract_token (buf, s, 0);
1387     if (mutt_strcmp ("*", buf->data) == 0)
1388       mutt_free_list (&UserHeader);
1389     else
1390     {
1391       tmp = UserHeader;
1392       last = NULL;
1393
1394       l = mutt_strlen (buf->data);
1395       if (buf->data[l - 1] == ':')
1396         l--;
1397
1398       while (tmp)
1399       {
1400         if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':')
1401         {
1402           ptr = tmp;
1403           if (last)
1404             last->next = tmp->next;
1405           else
1406             UserHeader = tmp->next;
1407           tmp = tmp->next;
1408           ptr->next = NULL;
1409           mutt_free_list (&ptr);
1410         }
1411         else
1412         {
1413           last = tmp;
1414           tmp = tmp->next;
1415         }
1416       }
1417     }
1418   }
1419   while (MoreArgs (s));
1420   return 0;
1421 }
1422
1423 static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
1424 {
1425   LIST *tmp;
1426   size_t keylen;
1427   char *p;
1428
1429   mutt_extract_token (buf, s, M_TOKEN_SPACE | M_TOKEN_QUOTE);
1430   if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':')
1431   {
1432     strfcpy (err->data, _("invalid header field"), err->dsize);
1433     return (-1);
1434   }
1435   keylen = p - buf->data + 1;
1436
1437   if (UserHeader)
1438   {
1439     for (tmp = UserHeader; ; tmp = tmp->next)
1440     {
1441       /* see if there is already a field by this name */
1442       if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0)
1443       {
1444         /* replace the old value */
1445         FREE (&tmp->data);
1446         tmp->data = buf->data;
1447         memset (buf, 0, sizeof (BUFFER));
1448         return 0;
1449       }
1450       if (!tmp->next)
1451         break;
1452     }
1453     tmp->next = mutt_new_list ();
1454     tmp = tmp->next;
1455   }
1456   else
1457   {
1458     tmp = mutt_new_list ();
1459     UserHeader = tmp;
1460   }
1461   tmp->data = buf->data;
1462   memset (buf, 0, sizeof (BUFFER));
1463   return 0;
1464 }
1465
1466 static int
1467 parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err)
1468 {
1469   int i, flags = 0;
1470
1471   if (mutt_strncmp ("reverse-", s, 8) == 0)
1472   {
1473     s += 8;
1474     flags = SORT_REVERSE;
1475   }
1476   
1477   if (mutt_strncmp ("last-", s, 5) == 0)
1478   {
1479     s += 5;
1480     flags |= SORT_LAST;
1481   }
1482
1483   if ((i = mutt_getvaluebyname (s, map)) == -1)
1484   {
1485     snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s);
1486     return (-1);
1487   }
1488
1489   *val = i | flags;
1490
1491   return 0;
1492 }
1493
1494 static void mutt_set_default (struct option_t *p)
1495 {
1496   switch (p->type & DT_MASK)
1497   {
1498     case DT_STR:
1499       if (!p->init && *((char **) p->data))
1500         p->init = (unsigned long) safe_strdup (* ((char **) p->data));
1501       break;
1502     case DT_PATH:
1503       if (!p->init && *((char **) p->data))
1504       {
1505         char *cp = safe_strdup (*((char **) p->data));
1506         /* mutt_pretty_mailbox (cp); */
1507         p->init = (unsigned long) cp;
1508       }
1509       break;
1510     case DT_ADDR:
1511       if (!p->init && *((ADDRESS **) p->data))
1512       {
1513         char tmp[HUGE_STRING];
1514         *tmp = '\0';
1515         rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0);
1516         p->init = (unsigned long) safe_strdup (tmp);
1517       }
1518       break;
1519     case DT_RX:
1520     {
1521       REGEXP *pp = (REGEXP *) p->data;
1522       if (!p->init && pp->pattern)
1523         p->init = (unsigned long) safe_strdup (pp->pattern);
1524       break;
1525     }
1526   }
1527 }
1528
1529 static void mutt_restore_default (struct option_t *p)
1530 {
1531   switch (p->type & DT_MASK)
1532   {
1533     case DT_STR:
1534       mutt_str_replace ((char **) p->data, (char *) p->init); 
1535       break;
1536     case DT_PATH:
1537       FREE((char **) p->data);          /* __FREE_CHECKED__ */
1538       if (p->init)
1539       {
1540         char path[_POSIX_PATH_MAX];
1541         strfcpy (path, (char *) p->init, sizeof (path));
1542         mutt_expand_path (path, sizeof (path));
1543         *((char **) p->data) = safe_strdup (path);
1544       }
1545       break;
1546     case DT_ADDR:
1547       rfc822_free_address ((ADDRESS **) p->data);
1548       if (p->init)
1549         *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init);
1550       break;
1551     case DT_BOOL:
1552       if (p->init)
1553         set_option (p->data);
1554       else
1555         unset_option (p->data);
1556       break;
1557     case DT_QUAD:
1558       set_quadoption (p->data, p->init);
1559       break;
1560     case DT_NUM:
1561     case DT_SORT:
1562     case DT_MAGIC:
1563       *((short *) p->data) = p->init;
1564       break;
1565     case DT_RX:
1566       {
1567         REGEXP *pp = (REGEXP *) p->data;
1568         int flags = 0;
1569
1570         FREE (&pp->pattern);
1571         if (pp->rx)
1572         {
1573           regfree (pp->rx);
1574           FREE (&pp->rx);
1575         }
1576
1577         if (p->init)
1578         {
1579           char *s = (char *) p->init;
1580
1581           pp->rx = safe_calloc (1, sizeof (regex_t));
1582           pp->pattern = safe_strdup ((char *) p->init);
1583           if (mutt_strcmp (p->option, "mask") != 0)
1584             flags |= mutt_which_case ((const char *) p->init);
1585           if (mutt_strcmp (p->option, "mask") == 0 && *s == '!')
1586           {
1587             s++;
1588             pp->not = 1;
1589           }
1590           if (REGCOMP (pp->rx, s, flags) != 0)
1591           {
1592             fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"),
1593                      p->option, pp->pattern);
1594             FREE (&pp->pattern);
1595             FREE (&pp->rx);
1596           }
1597         }
1598       }
1599       break;
1600   }
1601
1602   if (p->flags & R_INDEX)
1603     set_option (OPTFORCEREDRAWINDEX);
1604   if (p->flags & R_PAGER)
1605     set_option (OPTFORCEREDRAWPAGER);
1606   if (p->flags & R_RESORT_SUB)
1607     set_option (OPTSORTSUBTHREADS);
1608   if (p->flags & R_RESORT)
1609     set_option (OPTNEEDRESORT);
1610   if (p->flags & R_RESORT_INIT)
1611     set_option (OPTRESORTINIT);
1612   if (p->flags & R_TREE)
1613     set_option (OPTREDRAWTREE);
1614 }
1615
1616 static size_t escape_string (char *dst, size_t len, const char* src)
1617 {
1618   char* p = dst;
1619
1620   if (!len)
1621     return 0;
1622   len--; /* save room for \0 */
1623 #define ESC_CHAR(C)     do { *p++ = '\\'; if (p - dst < len) *p++ = C; } while(0)
1624   while (p - dst < len && src && *src)
1625   {
1626     switch (*src)
1627     {
1628     case '\n':
1629       ESC_CHAR('n');
1630       break;
1631     case '\r':
1632       ESC_CHAR('r');
1633       break;
1634     case '\t':
1635       ESC_CHAR('t');
1636       break;
1637     default:
1638       if ((*src == '\\' || *src == '"') && p - dst < len - 1)
1639         *p++ = '\\';
1640       *p++ = *src;
1641     }
1642     src++;
1643   }
1644 #undef ESC_CHAR
1645   *p = '\0';
1646   return p - dst;
1647 }
1648
1649 static void pretty_var (char *dst, size_t len, const char *option, const char *val)
1650 {
1651   char *p;
1652
1653   if (!len)
1654     return;
1655
1656   strfcpy (dst, option, len);
1657   len--; /* save room for \0 */
1658   p = dst + mutt_strlen (dst);
1659
1660   if (p - dst < len)
1661     *p++ = '=';
1662   if (p - dst < len)
1663     *p++ = '"';
1664   p += escape_string (p, len - (p - dst) + 1, val);     /* \0 terminate it */
1665   if (p - dst < len)
1666     *p++ = '"';
1667   *p = 0;
1668 }
1669
1670 static int check_charset (struct option_t *opt, const char *val)
1671 {
1672   char *p, *q = NULL, *s = safe_strdup (val);
1673   int rc = 0, strict = strcmp (opt->option, "send_charset") == 0;
1674
1675   for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q))
1676   {
1677     if (!*p)
1678       continue;
1679     if (mutt_check_charset (p, strict) < 0)
1680     {
1681       rc = -1;
1682       break;
1683     }
1684   }
1685
1686   FREE(&s);
1687   return rc;
1688 }
1689
1690 static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
1691 {
1692   int query, unset, inv, reset, r = 0;
1693   int idx = -1;
1694   char *p, scratch[_POSIX_PATH_MAX];
1695   char* myvar;
1696
1697   while (MoreArgs (s))
1698   {
1699     /* reset state variables */
1700     query = 0;
1701     unset = data & M_SET_UNSET;
1702     inv = data & M_SET_INV;
1703     reset = data & M_SET_RESET;
1704     myvar = NULL;
1705
1706     if (*s->dptr == '?')
1707     {
1708       query = 1;
1709       s->dptr++;
1710     }
1711     else if (mutt_strncmp ("no", s->dptr, 2) == 0)
1712     {
1713       s->dptr += 2;
1714       unset = !unset;
1715     }
1716     else if (mutt_strncmp ("inv", s->dptr, 3) == 0)
1717     {
1718       s->dptr += 3;
1719       inv = !inv;
1720     }
1721     else if (*s->dptr == '&')
1722     {
1723       reset = 1;
1724       s->dptr++;
1725     }
1726
1727     /* get the variable name */
1728     mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1729
1730     if (!mutt_strncmp ("my_", tmp->data, 3))
1731       myvar = tmp->data;
1732     else if ((idx = mutt_option_index (tmp->data)) == -1 &&
1733         !(reset && !mutt_strcmp ("all", tmp->data)))
1734     {
1735       snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1736       return (-1);
1737     }
1738     SKIPWS (s->dptr);
1739
1740     if (reset)
1741     {
1742       if (query || unset || inv)
1743       {
1744         snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1745         return (-1);
1746       }
1747
1748       if (s && *s->dptr == '=')
1749       {
1750         snprintf (err->data, err->dsize, _("value is illegal with reset"));
1751         return (-1);
1752       }
1753      
1754       if (!mutt_strcmp ("all", tmp->data))
1755       {
1756         if (CurrentMenu == MENU_PAGER)
1757         {
1758           snprintf (err->data, err->dsize, _("Not available in this menu."));
1759           return (-1);
1760         }
1761         for (idx = 0; MuttVars[idx].option; idx++)
1762           mutt_restore_default (&MuttVars[idx]);
1763         set_option (OPTFORCEREDRAWINDEX);
1764         set_option (OPTFORCEREDRAWPAGER);
1765         set_option (OPTSORTSUBTHREADS);
1766         set_option (OPTNEEDRESORT);
1767         set_option (OPTRESORTINIT);
1768         set_option (OPTREDRAWTREE);
1769         return 0;
1770       }
1771       else
1772       {
1773         CHECK_PAGER;
1774         if (myvar)
1775           myvar_del (myvar);
1776         else
1777           mutt_restore_default (&MuttVars[idx]);
1778       }
1779     } 
1780     else if (!myvar && DTYPE (MuttVars[idx].type) == DT_BOOL)
1781     { 
1782       if (s && *s->dptr == '=')
1783       {
1784         if (unset || inv || query)
1785         {
1786           snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1787           return (-1);
1788         }
1789
1790         s->dptr++;
1791         mutt_extract_token (tmp, s, 0);
1792         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1793           unset = inv = 0;
1794         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1795           unset = 1;
1796         else
1797         {
1798           snprintf (err->data, err->dsize, _("Usage: set variable=yes|no"));
1799           return (-1);
1800         }
1801       }
1802
1803       if (query)
1804       {
1805         snprintf (err->data, err->dsize, option (MuttVars[idx].data)
1806                         ? _("%s is set") : _("%s is unset"), tmp->data);
1807         return 0;
1808       }
1809
1810       CHECK_PAGER;
1811       if (unset)
1812         unset_option (MuttVars[idx].data);
1813       else if (inv)
1814         toggle_option (MuttVars[idx].data);
1815       else
1816         set_option (MuttVars[idx].data);
1817     }
1818     else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR ||
1819              DTYPE (MuttVars[idx].type) == DT_PATH ||
1820              DTYPE (MuttVars[idx].type) == DT_ADDR)
1821     {
1822       if (unset)
1823       {
1824         CHECK_PAGER;
1825         if (myvar)
1826           myvar_del (myvar);
1827         else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1828           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1829         else
1830           /* MuttVars[idx].data is already 'char**' (or some 'void**') or... 
1831            * so cast to 'void*' is okay */
1832           FREE ((void *) MuttVars[idx].data);           /* __FREE_CHECKED__ */
1833       }
1834       else if (query || *s->dptr != '=')
1835       {
1836         char _tmp[LONG_STRING];
1837         const char *val = NULL;
1838
1839         if (myvar)
1840         {
1841           if ((val = myvar_get (myvar)))
1842           {
1843             pretty_var (err->data, err->dsize, myvar, val);
1844             break;
1845           }
1846           else
1847           {
1848             snprintf (err->data, err->dsize, _("%s: unknown variable"), myvar);
1849             return (-1);
1850           }
1851         }
1852         else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
1853         {
1854           _tmp[0] = '\0';
1855           rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0);
1856           val = _tmp;
1857         }
1858         else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1859         {
1860           _tmp[0] = '\0';
1861           strfcpy (_tmp, NONULL(*((char **) MuttVars[idx].data)), sizeof (_tmp));
1862           mutt_pretty_mailbox (_tmp, sizeof (_tmp));
1863           val = _tmp;
1864         }
1865         else
1866           val = *((char **) MuttVars[idx].data);
1867         
1868         /* user requested the value of this variable */
1869         pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(val));
1870         break;
1871       }
1872       else
1873       {
1874         CHECK_PAGER;
1875         s->dptr++;
1876
1877         if (myvar)
1878         {
1879           /* myvar is a pointer to tmp and will be lost on extract_token */
1880           myvar = safe_strdup (myvar);
1881           myvar_del (myvar);
1882         }
1883
1884         mutt_extract_token (tmp, s, 0);
1885
1886         if (myvar)
1887         {
1888           myvar_set (myvar, tmp->data);
1889           FREE (&myvar);
1890           myvar="don't resort";
1891         }
1892         else if (DTYPE (MuttVars[idx].type) == DT_PATH)
1893         {
1894           /* MuttVars[idx].data is already 'char**' (or some 'void**') or... 
1895            * so cast to 'void*' is okay */
1896           FREE ((void *) MuttVars[idx].data);           /* __FREE_CHECKED__ */
1897
1898           strfcpy (scratch, tmp->data, sizeof (scratch));
1899           mutt_expand_path (scratch, sizeof (scratch));
1900           *((char **) MuttVars[idx].data) = safe_strdup (scratch);
1901         }
1902         else if (DTYPE (MuttVars[idx].type) == DT_STR)
1903         {
1904           if ((strstr (MuttVars[idx].option, "charset") &&
1905                check_charset (&MuttVars[idx], tmp->data) < 0) |
1906               /* $charset can't be empty, others can */
1907               (strcmp(MuttVars[idx].option, "charset") == 0 && ! *tmp->data))
1908           {
1909             snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""),
1910                       MuttVars[idx].option, tmp->data);
1911             return (-1);
1912           }
1913
1914           FREE ((void *) MuttVars[idx].data);           /* __FREE_CHECKED__ */
1915           *((char **) MuttVars[idx].data) = safe_strdup (tmp->data);
1916           if (mutt_strcmp (MuttVars[idx].option, "charset") == 0)
1917             mutt_set_charset (Charset);
1918         }
1919         else
1920         {
1921           rfc822_free_address ((ADDRESS **) MuttVars[idx].data);
1922           *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data);
1923         }
1924       }
1925     }
1926     else if (DTYPE(MuttVars[idx].type) == DT_RX)
1927     {
1928       REGEXP *ptr = (REGEXP *) MuttVars[idx].data;
1929       regex_t *rx;
1930       int e, flags = 0;
1931
1932       if (query || *s->dptr != '=')
1933       {
1934         /* user requested the value of this variable */
1935         pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(ptr->pattern));
1936         break;
1937       }
1938
1939       if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp"))
1940       {
1941         snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode.");
1942         r = -1;
1943         break;
1944       }
1945       
1946       CHECK_PAGER;
1947       s->dptr++;
1948
1949       /* copy the value of the string */
1950       mutt_extract_token (tmp, s, 0);
1951
1952       if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0)
1953       {
1954         int not = 0;
1955
1956         /* $mask is case-sensitive */
1957         if (mutt_strcmp (MuttVars[idx].option, "mask") != 0)
1958           flags |= mutt_which_case (tmp->data);
1959
1960         p = tmp->data;
1961         if (mutt_strcmp (MuttVars[idx].option, "mask") == 0)
1962         {
1963           if (*p == '!')
1964           {
1965             not = 1;
1966             p++;
1967           }
1968         }
1969           
1970         rx = (regex_t *) safe_malloc (sizeof (regex_t));
1971         if ((e = REGCOMP (rx, p, flags)) != 0)
1972         {
1973           regerror (e, rx, err->data, err->dsize);
1974           FREE (&rx);
1975           break;
1976         }
1977
1978         /* get here only if everything went smootly */
1979         if (ptr->pattern)
1980         {
1981           FREE (&ptr->pattern);
1982           regfree ((regex_t *) ptr->rx);
1983           FREE (&ptr->rx);
1984         }
1985
1986         ptr->pattern = safe_strdup (tmp->data);
1987         ptr->rx = rx;
1988         ptr->not = not;
1989
1990         /* $reply_regexp and $alterantes require special treatment */
1991         
1992         if (Context && Context->msgcount &&
1993             mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0)
1994         {
1995           regmatch_t pmatch[1];
1996           int i;
1997           
1998 #define CUR_ENV Context->hdrs[i]->env
1999           for (i = 0; i < Context->msgcount; i++)
2000           {
2001             if (CUR_ENV && CUR_ENV->subject)
2002             {
2003               CUR_ENV->real_subj = (regexec (ReplyRegexp.rx,
2004                                     CUR_ENV->subject, 1, pmatch, 0)) ?
2005                                     CUR_ENV->subject : 
2006                                     CUR_ENV->subject + pmatch[0].rm_eo;
2007             }
2008           }
2009 #undef CUR_ENV
2010         }
2011       }
2012     }
2013     else if (DTYPE(MuttVars[idx].type) == DT_MAGIC)
2014     {
2015       if (query || *s->dptr != '=')
2016       {
2017         switch (DefaultMagic)
2018         {
2019           case M_MBOX:
2020             p = "mbox";
2021             break;
2022           case M_MMDF:
2023             p = "MMDF";
2024             break;
2025           case M_MH:
2026             p = "MH";
2027             break;
2028           case M_MAILDIR:
2029             p = "Maildir";
2030             break;
2031           default:
2032             p = "unknown";
2033             break;
2034         }
2035         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p);
2036         break;
2037       }
2038
2039       CHECK_PAGER;
2040       s->dptr++;
2041
2042       /* copy the value of the string */
2043       mutt_extract_token (tmp, s, 0);
2044       if (mx_set_magic (tmp->data))
2045       {
2046         snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data);
2047         r = -1;
2048         break;
2049       }
2050     }
2051     else if (DTYPE(MuttVars[idx].type) == DT_NUM)
2052     {
2053       short *ptr = (short *) MuttVars[idx].data;
2054       short val;
2055       int rc;
2056
2057       if (query || *s->dptr != '=')
2058       {
2059         val = *ptr;
2060         /* compatibility alias */
2061         if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2062           val = *ptr < 0 ? -*ptr : 0;
2063
2064         /* user requested the value of this variable */
2065         snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, val);
2066         break;
2067       }
2068
2069       CHECK_PAGER;
2070       s->dptr++;
2071
2072       mutt_extract_token (tmp, s, 0);
2073       rc = mutt_atos (tmp->data, (short *) &val);
2074
2075       if (rc < 0 || !*tmp->data)
2076       {
2077         snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data,
2078                   rc == -1 ? _("format error") : _("number overflow"));
2079         r = -1;
2080         break;
2081       }
2082       else
2083         *ptr = val;
2084
2085       /* these ones need a sanity check */
2086       if (mutt_strcmp (MuttVars[idx].option, "history") == 0)
2087       {
2088         if (*ptr < 0)
2089           *ptr = 0;
2090         mutt_init_history ();
2091       }
2092       else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0)
2093       {
2094         if (*ptr < 0)
2095           *ptr = 0;
2096       }
2097       else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2098       {
2099         if (*ptr < 0)
2100           *ptr = 0;
2101         else
2102           *ptr = -*ptr;
2103       }
2104 #ifdef USE_IMAP
2105       else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0)
2106       {
2107         if (*ptr < 0)
2108           *ptr = 0;
2109       }
2110 #endif
2111     }
2112     else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2113     {
2114       if (query)
2115       {
2116         char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2117
2118         snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option,
2119                   vals [ quadoption (MuttVars[idx].data) ]);
2120         break;
2121       }
2122
2123       CHECK_PAGER;
2124       if (*s->dptr == '=')
2125       {
2126         s->dptr++;
2127         mutt_extract_token (tmp, s, 0);
2128         if (ascii_strcasecmp ("yes", tmp->data) == 0)
2129           set_quadoption (MuttVars[idx].data, M_YES);
2130         else if (ascii_strcasecmp ("no", tmp->data) == 0)
2131           set_quadoption (MuttVars[idx].data, M_NO);
2132         else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
2133           set_quadoption (MuttVars[idx].data, M_ASKYES);
2134         else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
2135           set_quadoption (MuttVars[idx].data, M_ASKNO);
2136         else
2137         {
2138           snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data);
2139           r = -1;
2140           break;
2141         }
2142       }
2143       else
2144       {
2145         if (inv)
2146           toggle_quadoption (MuttVars[idx].data);
2147         else if (unset)
2148           set_quadoption (MuttVars[idx].data, M_NO);
2149         else
2150           set_quadoption (MuttVars[idx].data, M_YES);
2151       }
2152     }
2153     else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2154     {
2155       const struct mapping_t *map = NULL;
2156
2157       switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2158       {
2159         case DT_SORT_ALIAS:
2160           map = SortAliasMethods;
2161           break;
2162         case DT_SORT_BROWSER:
2163           map = SortBrowserMethods;
2164           break;
2165         case DT_SORT_KEYS:
2166           if ((WithCrypto & APPLICATION_PGP))
2167             map = SortKeyMethods;
2168           break;
2169         case DT_SORT_AUX:
2170           map = SortAuxMethods;
2171           break;
2172         default:
2173           map = SortMethods;
2174           break;
2175       }
2176
2177       if (!map)
2178       {
2179         snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option);
2180         r = -1;
2181         break;
2182       }
2183       
2184       if (query || *s->dptr != '=')
2185       {
2186         p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2187
2188         snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option,
2189                   (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2190                   (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2191                   p);
2192         return 0;
2193       }
2194       CHECK_PAGER;
2195       s->dptr++;
2196       mutt_extract_token (tmp, s , 0);
2197
2198       if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1)
2199       {
2200         r = -1;
2201         break;
2202       }
2203     }
2204     else
2205     {
2206       snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option);
2207       r = -1;
2208       break;
2209     }
2210
2211     if (!myvar)
2212     {
2213       if (MuttVars[idx].flags & R_INDEX)
2214         set_option (OPTFORCEREDRAWINDEX);
2215       if (MuttVars[idx].flags & R_PAGER)
2216         set_option (OPTFORCEREDRAWPAGER);
2217       if (MuttVars[idx].flags & R_RESORT_SUB)
2218         set_option (OPTSORTSUBTHREADS);
2219       if (MuttVars[idx].flags & R_RESORT)
2220         set_option (OPTNEEDRESORT);
2221       if (MuttVars[idx].flags & R_RESORT_INIT)
2222         set_option (OPTRESORTINIT);
2223       if (MuttVars[idx].flags & R_TREE)
2224         set_option (OPTREDRAWTREE);
2225     }
2226   }
2227   return (r);
2228 }
2229
2230 #define MAXERRS 128
2231
2232 /* reads the specified initialization file.  returns -1 if errors were found
2233    so that we can pause to let the user know...  */
2234 static int source_rc (const char *rcfile, BUFFER *err)
2235 {
2236   FILE *f;
2237   int line = 0, rc = 0, conv = 0;
2238   BUFFER token;
2239   char *linebuf = NULL;
2240   char *currentline = NULL;
2241   size_t buflen;
2242   pid_t pid;
2243
2244   dprint (2, (debugfile, "Reading configuration file '%s'.\n",
2245           rcfile));
2246   
2247   if ((f = mutt_open_read (rcfile, &pid)) == NULL)
2248   {
2249     snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
2250     return (-1);
2251   }
2252
2253   memset (&token, 0, sizeof (token));
2254   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, M_CONT)) != NULL)
2255   {
2256     conv=ConfigCharset && (*ConfigCharset) && Charset;
2257     if (conv) 
2258     {
2259       currentline=safe_strdup(linebuf);
2260       if (!currentline) continue;
2261       mutt_convert_string(&currentline, ConfigCharset, Charset, 0);
2262     } 
2263     else 
2264       currentline=linebuf;
2265
2266     if (mutt_parse_rc_line (currentline, &token, err) == -1)
2267     {
2268       mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
2269       if (--rc < -MAXERRS) 
2270       {
2271         if (conv) FREE(&currentline);
2272         break;
2273       }
2274     }
2275     else
2276     {
2277       if (rc < 0)
2278         rc = -1;
2279     }
2280     if (conv) 
2281       FREE(&currentline);
2282   }
2283   FREE (&token.data);
2284   FREE (&linebuf);
2285   safe_fclose (&f);
2286   if (pid != -1)
2287     mutt_wait_filter (pid);
2288   if (rc)
2289   {
2290     /* the muttrc source keyword */
2291     snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s")
2292       : _("source: reading aborted due too many errors in %s"), rcfile);
2293     rc = -1;
2294   }
2295   return (rc);
2296 }
2297
2298 #undef MAXERRS
2299
2300 static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
2301 {
2302   char path[_POSIX_PATH_MAX];
2303
2304   if (mutt_extract_token (tmp, s, 0) != 0)
2305   {
2306     snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
2307     return (-1);
2308   }
2309   if (MoreArgs (s))
2310   {
2311     strfcpy (err->data, _("source: too many arguments"), err->dsize);
2312     return (-1);
2313   }
2314   strfcpy (path, tmp->data, sizeof (path));
2315   mutt_expand_path (path, sizeof (path));
2316   return (source_rc (path, err));
2317 }
2318
2319 /* line         command to execute
2320
2321    token        scratch buffer to be used by parser.  caller should free
2322                 token->data when finished.  the reason for this variable is
2323                 to avoid having to allocate and deallocate a lot of memory
2324                 if we are parsing many lines.  the caller can pass in the
2325                 memory to use, which avoids having to create new space for
2326                 every call to this function.
2327
2328    err          where to write error messages */
2329 int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
2330 {
2331   int i, r = -1;
2332   BUFFER expn;
2333
2334   if (!line || !*line)
2335     return 0;
2336
2337   memset (&expn, 0, sizeof (expn));
2338   expn.data = expn.dptr = line;
2339   expn.dsize = mutt_strlen (line);
2340
2341   *err->data = 0;
2342
2343   SKIPWS (expn.dptr);
2344   while (*expn.dptr)
2345   {
2346     if (*expn.dptr == '#')
2347       break; /* rest of line is a comment */
2348     if (*expn.dptr == ';')
2349     {
2350       expn.dptr++;
2351       continue;
2352     }
2353     mutt_extract_token (token, &expn, 0);
2354     for (i = 0; Commands[i].name; i++)
2355     {
2356       if (!mutt_strcmp (token->data, Commands[i].name))
2357       {
2358         if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
2359           goto finish;
2360         break;
2361       }
2362     }
2363     if (!Commands[i].name)
2364     {
2365       snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
2366       goto finish;
2367     }
2368   }
2369   r = 0;
2370 finish:
2371   if (expn.destroy)
2372     FREE (&expn.data);
2373   return (r);
2374 }
2375
2376
2377 #define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0]))
2378 #define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0]))
2379 /* initial string that starts completion. No telling how much crap 
2380  * the user has typed so far. Allocate LONG_STRING just to be sure! */
2381 static char User_typed [LONG_STRING] = {0}; 
2382
2383 static int  Num_matched = 0; /* Number of matches for completion */
2384 static char Completed [STRING] = {0}; /* completed string (command or variable) */
2385 static const char **Matches;
2386 /* this is a lie until mutt_init runs: */
2387 static int  Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10;
2388
2389 static void matches_ensure_morespace(int current)
2390 {
2391   int base_space, extra_space, space;
2392
2393   if (current > Matches_listsize - 2)
2394   {
2395     base_space = MAX(NUMVARS,NUMCOMMANDS) + 1; 
2396     extra_space = Matches_listsize - base_space;
2397     extra_space *= 2;
2398     space = base_space + extra_space;
2399     safe_realloc (&Matches, space * sizeof (char *));
2400     memset (&Matches[current + 1], 0, space - current);
2401     Matches_listsize = space;
2402   }
2403 }
2404
2405 /* helper function for completion.  Changes the dest buffer if
2406    necessary/possible to aid completion.
2407         dest == completion result gets here.
2408         src == candidate for completion.
2409         try == user entered data for completion.
2410         len == length of dest buffer.
2411 */
2412 static void candidate (char *dest, char *try, const char *src, int len)
2413 {
2414   int l;
2415
2416   if (strstr (src, try) == src)
2417   {
2418     matches_ensure_morespace (Num_matched);
2419     Matches[Num_matched++] = src;
2420     if (dest[0] == 0)
2421       strfcpy (dest, src, len);
2422     else
2423     {
2424       for (l = 0; src[l] && src[l] == dest[l]; l++);
2425       dest[l] = 0;
2426     }
2427   }
2428 }
2429
2430 int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
2431 {
2432   char *pt = buffer;
2433   int num;
2434   int spaces; /* keep track of the number of leading spaces on the line */
2435   myvar_t *myv;
2436
2437   SKIPWS (buffer);
2438   spaces = buffer - pt;
2439
2440   pt = buffer + pos - spaces;
2441   while ((pt > buffer) && !isspace ((unsigned char) *pt))
2442     pt--;
2443
2444   if (pt == buffer) /* complete cmd */
2445   {
2446     /* first TAB. Collect all the matches */
2447     if (numtabs == 1)
2448     {
2449       Num_matched = 0;
2450       strfcpy (User_typed, pt, sizeof (User_typed));
2451       memset (Matches, 0, Matches_listsize);
2452       memset (Completed, 0, sizeof (Completed));
2453       for (num = 0; Commands[num].name; num++)
2454         candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
2455       matches_ensure_morespace (Num_matched);
2456       Matches[Num_matched++] = User_typed;
2457
2458       /* All matches are stored. Longest non-ambiguous string is ""
2459        * i.e. dont change 'buffer'. Fake successful return this time */
2460       if (User_typed[0] == 0)
2461         return 1;
2462     }
2463
2464     if (Completed[0] == 0 && User_typed[0])
2465       return 0;
2466
2467      /* Num_matched will _always_ be atleast 1 since the initial
2468       * user-typed string is always stored */
2469     if (numtabs == 1 && Num_matched == 2)
2470       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2471     else if (numtabs > 1 && Num_matched > 2)
2472       /* cycle thru all the matches */
2473       snprintf(Completed, sizeof(Completed), "%s", 
2474                Matches[(numtabs - 2) % Num_matched]);
2475
2476     /* return the completed command */
2477     strncpy (buffer, Completed, len - spaces);
2478   }
2479   else if (!mutt_strncmp (buffer, "set", 3)
2480            || !mutt_strncmp (buffer, "unset", 5)
2481            || !mutt_strncmp (buffer, "reset", 5)
2482            || !mutt_strncmp (buffer, "toggle", 6))
2483   {             /* complete variables */
2484     char *prefixes[] = { "no", "inv", "?", "&", 0 };
2485     
2486     pt++;
2487     /* loop through all the possible prefixes (no, inv, ...) */
2488     if (!mutt_strncmp (buffer, "set", 3))
2489     {
2490       for (num = 0; prefixes[num]; num++)
2491       {
2492         if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
2493         {
2494           pt += mutt_strlen (prefixes[num]);
2495           break;
2496         }
2497       }
2498     }
2499     
2500     /* first TAB. Collect all the matches */
2501     if (numtabs == 1)
2502     {
2503       Num_matched = 0;
2504       strfcpy (User_typed, pt, sizeof (User_typed));
2505       memset (Matches, 0, Matches_listsize);
2506       memset (Completed, 0, sizeof (Completed));
2507       for (num = 0; MuttVars[num].option; num++)
2508         candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
2509       for (myv = MyVars; myv; myv = myv->next)
2510         candidate (Completed, User_typed, myv->name, sizeof (Completed));
2511       matches_ensure_morespace (Num_matched);
2512       Matches[Num_matched++] = User_typed;
2513
2514       /* All matches are stored. Longest non-ambiguous string is ""
2515        * i.e. dont change 'buffer'. Fake successful return this time */
2516       if (User_typed[0] == 0)
2517         return 1;
2518     }
2519
2520     if (Completed[0] == 0 && User_typed[0])
2521       return 0;
2522
2523     /* Num_matched will _always_ be atleast 1 since the initial
2524      * user-typed string is always stored */
2525     if (numtabs == 1 && Num_matched == 2)
2526       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2527     else if (numtabs > 1 && Num_matched > 2)
2528     /* cycle thru all the matches */
2529       snprintf(Completed, sizeof(Completed), "%s", 
2530                Matches[(numtabs - 2) % Num_matched]);
2531
2532     strncpy (pt, Completed, buffer + len - pt - spaces);
2533   }
2534   else if (!mutt_strncmp (buffer, "exec", 4))
2535   {
2536     struct binding_t *menu = km_get_table (CurrentMenu);
2537
2538     if (!menu && CurrentMenu != MENU_PAGER)
2539       menu = OpGeneric;
2540     
2541     pt++;
2542     /* first TAB. Collect all the matches */
2543     if (numtabs == 1)
2544     {
2545       Num_matched = 0;
2546       strfcpy (User_typed, pt, sizeof (User_typed));
2547       memset (Matches, 0, Matches_listsize);
2548       memset (Completed, 0, sizeof (Completed));
2549       for (num = 0; menu[num].name; num++)
2550         candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2551       /* try the generic menu */
2552       if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) 
2553       {
2554         menu = OpGeneric;
2555         for (num = 0; menu[num].name; num++)
2556           candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
2557       }
2558       matches_ensure_morespace (Num_matched);
2559       Matches[Num_matched++] = User_typed;
2560
2561       /* All matches are stored. Longest non-ambiguous string is ""
2562        * i.e. dont change 'buffer'. Fake successful return this time */
2563       if (User_typed[0] == 0)
2564         return 1;
2565     }
2566
2567     if (Completed[0] == 0 && User_typed[0])
2568       return 0;
2569
2570     /* Num_matched will _always_ be atleast 1 since the initial
2571      * user-typed string is always stored */
2572     if (numtabs == 1 && Num_matched == 2)
2573       snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
2574     else if (numtabs > 1 && Num_matched > 2)
2575     /* cycle thru all the matches */
2576       snprintf(Completed, sizeof(Completed), "%s", 
2577                Matches[(numtabs - 2) % Num_matched]);
2578
2579     strncpy (pt, Completed, buffer + len - pt - spaces);
2580   }
2581   else
2582     return 0;
2583
2584   return 1;
2585 }
2586
2587 int mutt_var_value_complete (char *buffer, size_t len, int pos)
2588 {
2589   char var[STRING], *pt = buffer;
2590   int spaces;
2591   
2592   if (buffer[0] == 0)
2593     return 0;
2594
2595   SKIPWS (buffer);
2596   spaces = buffer - pt;
2597
2598   pt = buffer + pos - spaces;
2599   while ((pt > buffer) && !isspace ((unsigned char) *pt))
2600     pt--;
2601   pt++; /* move past the space */
2602   if (*pt == '=') /* abort if no var before the '=' */
2603     return 0;
2604
2605   if (mutt_strncmp (buffer, "set", 3) == 0)
2606   {
2607     int idx;
2608     char val[LONG_STRING];
2609     const char *myvarval;
2610
2611     strfcpy (var, pt, sizeof (var));
2612     /* ignore the trailing '=' when comparing */
2613     var[mutt_strlen (var) - 1] = 0;
2614     if ((idx = mutt_option_index (var)) == -1)
2615     {
2616       if ((myvarval = myvar_get(var)) != NULL)
2617       {
2618         pretty_var (pt, len - (pt - buffer), var, myvarval);
2619         return 1;
2620       }
2621       return 0; /* no such variable. */
2622     }
2623     else if (var_to_string (idx, val, sizeof (val)))
2624     {
2625       snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val);
2626       return 1;
2627     }
2628   }
2629   return 0;
2630 }
2631
2632 static int var_to_string (int idx, char* val, size_t len)
2633 {
2634   char tmp[LONG_STRING];
2635   char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2636
2637   tmp[0] = '\0';
2638
2639   if ((DTYPE(MuttVars[idx].type) == DT_STR) ||
2640       (DTYPE(MuttVars[idx].type) == DT_PATH) ||
2641       (DTYPE(MuttVars[idx].type) == DT_RX))
2642   {
2643     strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp));
2644     if (DTYPE (MuttVars[idx].type) == DT_PATH)
2645       mutt_pretty_mailbox (tmp, sizeof (tmp));
2646   }
2647   else if (DTYPE (MuttVars[idx].type) == DT_ADDR)
2648   {
2649     rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0);
2650   }
2651   else if (DTYPE (MuttVars[idx].type) == DT_QUAD)
2652     strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp));
2653   else if (DTYPE (MuttVars[idx].type) == DT_NUM)
2654   {
2655     short sval = *((short *) MuttVars[idx].data);
2656
2657     /* avert your eyes, gentle reader */
2658     if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0)
2659       sval = sval > 0 ? 0 : -sval;
2660
2661     snprintf (tmp, sizeof (tmp), "%d", sval);
2662   }
2663   else if (DTYPE (MuttVars[idx].type) == DT_SORT)
2664   {
2665     const struct mapping_t *map;
2666     char *p;
2667
2668     switch (MuttVars[idx].type & DT_SUBTYPE_MASK)
2669     {
2670       case DT_SORT_ALIAS:
2671         map = SortAliasMethods;
2672         break;
2673       case DT_SORT_BROWSER:
2674         map = SortBrowserMethods;
2675         break;
2676       case DT_SORT_KEYS:
2677         if ((WithCrypto & APPLICATION_PGP))
2678           map = SortKeyMethods;
2679         else
2680           map = SortMethods;
2681         break;
2682       default:
2683         map = SortMethods;
2684         break;
2685     }
2686     p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map);
2687     snprintf (tmp, sizeof (tmp), "%s%s%s",
2688               (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "",
2689               (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "",
2690               p);
2691   }
2692   else if (DTYPE (MuttVars[idx].type) == DT_MAGIC)
2693   {
2694     char *p;
2695
2696     switch (DefaultMagic)
2697     {
2698       case M_MBOX:
2699         p = "mbox";
2700         break;
2701       case M_MMDF:
2702         p = "MMDF";
2703         break;
2704       case M_MH:
2705         p = "MH";
2706         break;
2707       case M_MAILDIR:
2708         p = "Maildir";
2709         break;
2710       default:
2711         p = "unknown";
2712     }
2713     strfcpy (tmp, p, sizeof (tmp));
2714   }
2715   else if (DTYPE (MuttVars[idx].type) == DT_BOOL)
2716     strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp));
2717   else
2718     return 0;
2719
2720   escape_string (val, len - 1, tmp);
2721
2722   return 1;
2723 }
2724
2725 /* Implement the -Q command line flag */
2726 int mutt_query_variables (LIST *queries)
2727 {
2728   LIST *p;
2729   
2730   char errbuff[LONG_STRING];
2731   char command[STRING];
2732   
2733   BUFFER err, token;
2734   
2735   memset (&err, 0, sizeof (err));
2736   memset (&token, 0, sizeof (token));
2737   
2738   err.data = errbuff;
2739   err.dsize = sizeof (errbuff);
2740   
2741   for (p = queries; p; p = p->next)
2742   {
2743     snprintf (command, sizeof (command), "set ?%s\n", p->data);
2744     if (mutt_parse_rc_line (command, &token, &err) == -1)
2745     {
2746       fprintf (stderr, "%s\n", err.data);
2747       FREE (&token.data);
2748       return 1;
2749     }
2750     printf ("%s\n", err.data);
2751   }
2752   
2753   FREE (&token.data);
2754   return 0;
2755 }
2756
2757 /* dump out the value of all the variables we have */
2758 int mutt_dump_variables (void)
2759 {
2760   int i;
2761   
2762   char errbuff[LONG_STRING];
2763   char command[STRING];
2764   
2765   BUFFER err, token;
2766   
2767   memset (&err, 0, sizeof (err));
2768   memset (&token, 0, sizeof (token));
2769   
2770   err.data = errbuff;
2771   err.dsize = sizeof (errbuff);
2772   
2773   for (i = 0; MuttVars[i].option; i++)
2774   {
2775     if (MuttVars[i].type == DT_SYN)
2776       continue;
2777
2778     snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option);
2779     if (mutt_parse_rc_line (command, &token, &err) == -1)
2780     {
2781       fprintf (stderr, "%s\n", err.data);
2782       FREE (&token.data);
2783       return 1;
2784     }
2785     printf("%s\n", err.data);
2786   }
2787   
2788   FREE (&token.data);
2789   return 0;
2790 }
2791
2792 char *mutt_getnamebyvalue (int val, const struct mapping_t *map)
2793 {
2794   int i;
2795
2796   for (i=0; map[i].name; i++)
2797     if (map[i].value == val)
2798       return (map[i].name);
2799   return NULL;
2800 }
2801
2802 int mutt_getvaluebyname (const char *name, const struct mapping_t *map)
2803 {
2804   int i;
2805
2806   for (i = 0; map[i].name; i++)
2807     if (ascii_strcasecmp (map[i].name, name) == 0)
2808       return (map[i].value);
2809   return (-1);
2810 }
2811
2812 #ifdef DEBUG
2813 static void start_debug (void)
2814 {
2815   time_t t;
2816   int i;
2817   char buf[_POSIX_PATH_MAX];
2818   char buf2[_POSIX_PATH_MAX];
2819
2820   /* rotate the old debug logs */
2821   for (i=3; i>=0; i--)
2822   {
2823     snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i);
2824     snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1);
2825     rename (buf, buf2);
2826   }
2827   if ((debugfile = safe_fopen(buf, "w")) != NULL)
2828   {
2829     t = time (0);
2830     setbuf (debugfile, NULL); /* don't buffer the debugging output! */
2831     dprint(1,(debugfile,"Mutt/%s (%s) debugging at level %d\n",
2832               MUTT_VERSION, ReleaseDate, debuglevel));
2833   }
2834 }
2835 #endif
2836
2837 static int mutt_execute_commands (LIST *p)
2838 {
2839   BUFFER err, token;
2840   char errstr[SHORT_STRING];
2841
2842   memset (&err, 0, sizeof (err));
2843   err.data = errstr;
2844   err.dsize = sizeof (errstr);
2845   memset (&token, 0, sizeof (token));
2846   for (; p; p = p->next)
2847   {
2848     if (mutt_parse_rc_line (p->data, &token, &err) != 0)
2849     {
2850       fprintf (stderr, _("Error in command line: %s\n"), err.data);
2851       FREE (&token.data);
2852       return (-1);
2853     }
2854   }
2855   FREE (&token.data);
2856   return 0;
2857 }
2858
2859 static void mutt_srandom (void)
2860 {
2861   struct timeval tv;
2862   unsigned seed;
2863
2864   gettimeofday(&tv, NULL);
2865   /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width.
2866    * Use as many of the lower order bits from the current time of day as the seed.
2867    * If the upper bound is truncated, that is fine.
2868    *
2869    * tv_sec is integral of type integer or float.  Cast to 'long long' before
2870    * bitshift in case it is a float.
2871    */
2872   seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec;
2873   srandom(seed);
2874 }
2875
2876 void mutt_init (int skip_sys_rc, LIST *commands)
2877 {
2878   struct passwd *pw;
2879   struct utsname utsname;
2880   char *p, buffer[STRING], error[STRING];
2881   int i, default_rc = 0, need_pause = 0;
2882   BUFFER err;
2883
2884   memset (&err, 0, sizeof (err));
2885   err.data = error;
2886   err.dsize = sizeof (error);
2887
2888   Groups = hash_create (1031, 0);
2889   ReverseAlias = hash_create (1031, 1);
2890   
2891   mutt_menu_init ();
2892   mutt_srandom ();
2893
2894   /* 
2895    * XXX - use something even more difficult to predict?
2896    */
2897   snprintf (AttachmentMarker, sizeof (AttachmentMarker),
2898             "\033]9;%ld\a", (long) time (NULL));
2899   
2900   /* on one of the systems I use, getcwd() does not return the same prefix
2901      as is listed in the passwd file */
2902   if ((p = getenv ("HOME")))
2903     Homedir = safe_strdup (p);
2904
2905   /* Get some information about the user */
2906   if ((pw = getpwuid (getuid ())))
2907   {
2908     char rnbuf[STRING];
2909
2910     Username = safe_strdup (pw->pw_name);
2911     if (!Homedir)
2912       Homedir = safe_strdup (pw->pw_dir);
2913
2914     Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw));
2915     Shell = safe_strdup (pw->pw_shell);
2916     endpwent ();
2917   }
2918   else 
2919   {
2920     if (!Homedir)
2921     {
2922       mutt_endwin (NULL);
2923       fputs (_("unable to determine home directory"), stderr);
2924       exit (1);
2925     }
2926     if ((p = getenv ("USER")))
2927       Username = safe_strdup (p);
2928     else
2929     {
2930       mutt_endwin (NULL);
2931       fputs (_("unable to determine username"), stderr);
2932       exit (1);
2933     }
2934     Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh");
2935   }
2936
2937 #ifdef DEBUG
2938   /* Start up debugging mode if requested */
2939   if (debuglevel > 0)
2940     start_debug ();
2941 #endif
2942
2943   /* And about the host... */
2944   uname (&utsname);
2945   /* some systems report the FQDN instead of just the hostname */
2946   if ((p = strchr (utsname.nodename, '.')))
2947   {
2948     Hostname = mutt_substrdup (utsname.nodename, p);
2949     p++;
2950     strfcpy (buffer, p, sizeof (buffer)); /* save the domain for below */
2951   }
2952   else
2953     Hostname = safe_strdup (utsname.nodename);
2954
2955 #ifndef DOMAIN
2956 #define DOMAIN buffer
2957   if (!p && getdnsdomainname (buffer, sizeof (buffer)) == -1)
2958     Fqdn = safe_strdup ("@");
2959   else
2960 #endif /* DOMAIN */
2961     if (*DOMAIN != '@')
2962   {
2963     Fqdn = safe_malloc (mutt_strlen (DOMAIN) + mutt_strlen (Hostname) + 2);
2964     sprintf (Fqdn, "%s.%s", NONULL(Hostname), DOMAIN);  /* __SPRINTF_CHECKED__ */
2965   }
2966   else
2967     Fqdn = safe_strdup(NONULL(Hostname));
2968
2969   if ((p = getenv ("MAIL")))
2970     Spoolfile = safe_strdup (p);
2971   else if ((p = getenv ("MAILDIR")))
2972     Spoolfile = safe_strdup (p);
2973   else
2974   {
2975 #ifdef HOMESPOOL
2976     mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer));
2977 #else
2978     mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer));
2979 #endif
2980     Spoolfile = safe_strdup (buffer);
2981   }
2982
2983   if ((p = getenv ("MAILCAPS")))
2984     MailcapPath = safe_strdup (p);
2985   else
2986   {
2987     /* Default search path from RFC1524 */
2988     MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2989   }
2990
2991   Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp");
2992
2993   p = getenv ("VISUAL");
2994   if (!p)
2995   {
2996     p = getenv ("EDITOR");
2997     if (!p)
2998       p = "vi";
2999   }
3000   Editor = safe_strdup (p);
3001   Visual = safe_strdup (p);
3002
3003   if ((p = getenv ("REPLYTO")) != NULL)
3004   {
3005     BUFFER buf, token;
3006
3007     snprintf (buffer, sizeof (buffer), "Reply-To: %s", p);
3008
3009     memset (&buf, 0, sizeof (buf));
3010     buf.data = buf.dptr = buffer;
3011     buf.dsize = mutt_strlen (buffer);
3012
3013     memset (&token, 0, sizeof (token));
3014     parse_my_hdr (&token, &buf, 0, &err);
3015     FREE (&token.data);
3016   }
3017
3018   if ((p = getenv ("EMAIL")) != NULL)
3019     From = rfc822_parse_adrlist (NULL, p);
3020   
3021   mutt_set_langinfo_charset ();
3022   mutt_set_charset (Charset);
3023   
3024   Matches = safe_calloc (Matches_listsize, sizeof (char *));
3025   
3026   /* Set standard defaults */
3027   for (i = 0; MuttVars[i].option; i++)
3028   {
3029     mutt_set_default (&MuttVars[i]);
3030     mutt_restore_default (&MuttVars[i]);
3031   }
3032
3033   CurrentMenu = MENU_MAIN;
3034
3035
3036 #ifndef LOCALES_HACK
3037   /* Do we have a locale definition? */
3038   if (((p = getenv ("LC_ALL")) != NULL && p[0]) ||
3039       ((p = getenv ("LANG")) != NULL && p[0]) ||
3040       ((p = getenv ("LC_CTYPE")) != NULL && p[0]))
3041     set_option (OPTLOCALES);
3042 #endif
3043
3044 #ifdef HAVE_GETSID
3045   /* Unset suspend by default if we're the session leader */
3046   if (getsid(0) == getpid())
3047     unset_option (OPTSUSPEND);
3048 #endif
3049
3050   mutt_init_history ();
3051
3052   
3053   
3054   
3055   /*
3056    * 
3057    *                       BIG FAT WARNING
3058    * 
3059    * When changing the code which looks for a configuration file,
3060    * please also change the corresponding code in muttbug.sh.in.
3061    * 
3062    * 
3063    */
3064   
3065   
3066   
3067   
3068   if (!Muttrc)
3069   {
3070     snprintf (buffer, sizeof(buffer), "%s/.muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3071     if (access(buffer, F_OK) == -1)
3072       snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3073     if (access(buffer, F_OK) == -1)
3074       snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc-%s", NONULL(Homedir), MUTT_VERSION);
3075     if (access(buffer, F_OK) == -1)
3076       snprintf (buffer, sizeof (buffer), "%s/.mutt/muttrc", NONULL(Homedir));
3077     if (access(buffer, F_OK) == -1) /* default to .muttrc for alias_file */
3078       snprintf (buffer, sizeof(buffer), "%s/.muttrc", NONULL(Homedir));
3079
3080     default_rc = 1;
3081     Muttrc = safe_strdup (buffer);
3082   }
3083   else
3084   {
3085     strfcpy (buffer, Muttrc, sizeof (buffer));
3086     FREE (&Muttrc);
3087     mutt_expand_path (buffer, sizeof (buffer));
3088     Muttrc = safe_strdup (buffer);
3089   }
3090   FREE (&AliasFile);
3091   AliasFile = safe_strdup (NONULL(Muttrc));
3092
3093   /* Process the global rc file if it exists and the user hasn't explicity
3094      requested not to via "-n".  */
3095   if (!skip_sys_rc)
3096   {
3097     snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION);
3098     if (access (buffer, F_OK) == -1)
3099       snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR);
3100     if (access (buffer, F_OK) == -1)
3101       snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION);
3102     if (access (buffer, F_OK) == -1)
3103       snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR);
3104     if (access (buffer, F_OK) != -1)
3105     {
3106       if (source_rc (buffer, &err) != 0)
3107       {
3108         fputs (err.data, stderr);
3109         fputc ('\n', stderr);
3110         need_pause = 1;
3111       }
3112     }
3113   }
3114
3115   /* Read the user's initialization file.  */
3116   if (access (Muttrc, F_OK) != -1)
3117   {
3118     if (!option (OPTNOCURSES))
3119       endwin ();
3120     if (source_rc (Muttrc, &err) != 0)
3121     {
3122       fputs (err.data, stderr);
3123       fputc ('\n', stderr);
3124       need_pause = 1;
3125     }
3126   }
3127   else if (!default_rc)
3128   {
3129     /* file specified by -F does not exist */
3130     snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno));
3131     mutt_endwin (buffer);
3132     exit (1);
3133   }
3134
3135   if (mutt_execute_commands (commands) != 0)
3136     need_pause = 1;
3137
3138   if (need_pause && !option (OPTNOCURSES))
3139   {
3140     if (mutt_any_key_to_continue (NULL) == -1)
3141       mutt_exit(1);
3142   }
3143
3144   mutt_read_histfile ();
3145
3146 #if 0
3147   set_option (OPTWEED); /* turn weeding on by default */
3148 #endif
3149 }
3150
3151 int mutt_get_hook_type (const char *name)
3152 {
3153   struct command_t *c;
3154
3155   for (c = Commands ; c->name ; c++)
3156     if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
3157       return c->data;
3158   return 0;
3159 }
3160
3161 static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
3162 {
3163   while (!mutt_strcasecmp (buf->data, "-group"))
3164   {
3165     if (!MoreArgs (s)) 
3166     {
3167       strfcpy (err->data, _("-group: no group name"), err->dsize);
3168       goto bail;
3169     }
3170     
3171     mutt_extract_token (buf, s, 0);
3172
3173     mutt_group_context_add (ctx, mutt_pattern_group (buf->data));
3174     
3175     if (!MoreArgs (s))
3176     {
3177       strfcpy (err->data, _("out of arguments"), err->dsize);
3178       goto bail;
3179     }
3180     
3181     mutt_extract_token (buf, s, 0);
3182   }
3183   
3184   return 0;
3185   
3186   bail:
3187   mutt_group_context_destroy (ctx);
3188   return -1;
3189 }
3190
3191 static void myvar_set (const char* var, const char* val)
3192 {
3193   myvar_t** cur;
3194
3195   for (cur = &MyVars; *cur; cur = &((*cur)->next))
3196     if (!mutt_strcmp ((*cur)->name, var))
3197       break;
3198
3199   if (!*cur)
3200     *cur = safe_calloc (1, sizeof (myvar_t));
3201   
3202   if (!(*cur)->name)
3203     (*cur)->name = safe_strdup (var);
3204   
3205   mutt_str_replace (&(*cur)->value, val);
3206 }
3207
3208 static void myvar_del (const char* var)
3209 {
3210   myvar_t **cur;
3211   myvar_t *tmp;
3212   
3213
3214   for (cur = &MyVars; *cur; cur = &((*cur)->next))
3215     if (!mutt_strcmp ((*cur)->name, var))
3216       break;
3217   
3218   if (*cur) 
3219   {
3220     tmp = (*cur)->next;
3221     FREE (&(*cur)->name);
3222     FREE (&(*cur)->value);
3223     FREE (cur);         /* __FREE_CHECKED__ */
3224     *cur = tmp;
3225   }
3226 }
3227
3228 static const char* myvar_get (const char* var)
3229 {
3230   myvar_t* cur;
3231
3232   for (cur = MyVars; cur; cur = cur->next)
3233     if (!mutt_strcmp (cur->name, var))
3234       return NONULL(cur->value);
3235
3236   return NULL;
3237 }