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