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