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