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