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