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