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