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