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