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