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