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