various cleanups.
[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 int init_expand (char** dst, struct option_t* src) {
1309   BUFFER token, in;
1310   ssize_t len = 0;
1311
1312   p_delete(dst);
1313
1314   if (DTYPE(src->type) == DT_STR ||
1315       DTYPE(src->type) == DT_PATH) {
1316     /* only expand for string as it's the only place where
1317      * we want to expand vars right now */
1318     if (src->init && *src->init) {
1319       p_clear(&token, 1);
1320       p_clear(&in, 1);
1321       len = m_strlen(src->init) + 2;
1322       in.data = p_new(char, len + 1);
1323       snprintf (in.data, len, "\"%s\"", src->init);
1324       in.dptr = in.data;
1325       in.dsize = len;
1326       mutt_extract_token (&token, &in, 0);
1327       if (token.data && *token.data)
1328         *dst = m_strdup(token.data);
1329       else
1330         *dst = m_strdup("");
1331       p_delete(&in.data);
1332       p_delete(&token.data);
1333     } else
1334       *dst = m_strdup("");
1335   } else
1336     /* for non-string: take value as is */
1337     *dst = m_strdup(src->init);
1338   return (1);
1339 }
1340
1341 /* if additional data more == 1, we want to resolve synonyms */
1342 static void mutt_restore_default (const char* name __attribute__ ((unused)),
1343                                   void* p, unsigned long more) {
1344   char errbuf[STRING];
1345   struct option_t* ptr = (struct option_t*) p;
1346   char* init = NULL;
1347
1348   if (DTYPE (ptr->type) == DT_SYN) {
1349     if (!more)
1350       return;
1351     ptr = hash_find (ConfigOptions, (char*) ptr->data);
1352   }
1353   if (!ptr)
1354     return;
1355   if (FuncTable[DTYPE (ptr->type)].opt_fromstr) {
1356     init_expand (&init, ptr);
1357     if (!FuncTable[DTYPE (ptr->type)].opt_fromstr (ptr, init, errbuf,
1358                                                        sizeof(errbuf))) {
1359       if (!option (OPTNOCURSES))
1360         mutt_endwin (NULL);
1361       fprintf (stderr, _("Invalid default setting for $%s found: \"%s\".\n"
1362                          "Please report this error: \"%s\"\n"),
1363                ptr->option, NONULL (init), errbuf);
1364       exit (1);
1365     }
1366     p_delete(&init);
1367   }
1368
1369   if (ptr->flags & R_INDEX)
1370     set_option (OPTFORCEREDRAWINDEX);
1371   if (ptr->flags & R_PAGER)
1372     set_option (OPTFORCEREDRAWPAGER);
1373   if (ptr->flags & R_RESORT_SUB)
1374     set_option (OPTSORTSUBTHREADS);
1375   if (ptr->flags & R_RESORT)
1376     set_option (OPTNEEDRESORT);
1377   if (ptr->flags & R_RESORT_INIT)
1378     set_option (OPTRESORTINIT);
1379   if (ptr->flags & R_TREE)
1380     set_option (OPTREDRAWTREE);
1381 }
1382
1383 /* check whether value for $dsn_return would be valid */
1384 static int check_dsn_return (const char* option __attribute__ ((unused)), unsigned long p,
1385                              char* errbuf, ssize_t errlen) {
1386   char* val = (char*) p;
1387   if (val && *val && m_strncmp(val, "hdrs", 4) != 0 &&
1388       m_strncmp(val, "full", 4) != 0) {
1389     if (errbuf)
1390       snprintf (errbuf, errlen, _("'%s' is invalid for $%s"), val, "dsn_return");
1391     return (0);
1392   }
1393   return (1);
1394 }
1395
1396 /* check whether value for $dsn_notify would be valid */
1397 static int
1398 check_dsn_notify (const char* option __attribute__ ((unused)),
1399                   unsigned long val, char* errbuf, ssize_t errlen)
1400 {
1401     const char *p = (const char*)val;
1402
1403     while (p && *p) {
1404         const char *q = m_strchrnul(p, ',');
1405         int len = q - p;
1406
1407         if (!m_strncmp(p, "never", len)   && !m_strncmp(p, "delay", len)
1408         &&  !m_strncmp(p, "failure", len) && !m_strncmp(p, "success", len))
1409         {
1410             if (errbuf)
1411                 snprintf(errbuf, errlen, _("'%.*s' is invalid for $%s"),
1412                          len, p, "dsn_notify");
1413             return 0;
1414         }
1415
1416         p = q + 1;
1417     }
1418
1419     return 1;
1420 }
1421
1422 static int check_num (const char* option, unsigned long p,
1423                       char* errbuf, ssize_t errlen) {
1424   if ((int) p < 0) {
1425     if (errbuf)
1426       snprintf (errbuf, errlen, _("'%d' is invalid for $%s"), (int) p, option);
1427     return (0);
1428   }
1429   return (1);
1430 }
1431
1432 static int check_history (const char* option __attribute__ ((unused)), unsigned long p,
1433                           char* errbuf, ssize_t errlen) {
1434   if (!check_num ("history", p, errbuf, errlen))
1435     return (0);
1436   mutt_init_history ();
1437   return (1);
1438 }
1439
1440 static int check_special (const char* name, unsigned long val,
1441                           char* errbuf, ssize_t errlen) {
1442   int i = 0;
1443
1444   for (i = 0; SpecialVars[i].name; i++) {
1445     if (m_strcmp(SpecialVars[i].name, name) == 0) {
1446       return (SpecialVars[i].check (SpecialVars[i].name,
1447                                     val, errbuf, errlen));
1448     }
1449   }
1450   return (1);
1451 }
1452
1453 static const struct mapping_t* get_sortmap (struct option_t* option) {
1454   const struct mapping_t* map = NULL;
1455
1456   switch (option->type & DT_SUBTYPE_MASK) {
1457   case DT_SORT_ALIAS:
1458     map = SortAliasMethods;
1459     break;
1460   case DT_SORT_BROWSER:
1461     map = SortBrowserMethods;
1462     break;
1463   case DT_SORT_KEYS:
1464     map = SortKeyMethods;
1465     break;
1466   case DT_SORT_AUX:
1467     map = SortAuxMethods;
1468     break;
1469   default:
1470     map = SortMethods;
1471     break;
1472   }
1473   return (map);
1474 }
1475
1476 #define CHECK_PAGER \
1477   if ((CurrentMenu == MENU_PAGER) && \
1478       (!option || (option->flags & R_RESORT))) \
1479   { \
1480     snprintf (err->data, err->dsize, \
1481               _("Not available in this menu.")); \
1482     return (-1); \
1483   }
1484
1485 static int parse_set (BUFFER * tmp, BUFFER * s, unsigned long data,
1486                       BUFFER * err)
1487 {
1488   int query, unset, inv, reset, r = 0;
1489   struct option_t* option = NULL;
1490
1491   while (MoreArgs (s)) {
1492     /* reset state variables */
1493     query = 0;
1494     unset = data & M_SET_UNSET;
1495     inv = data & M_SET_INV;
1496     reset = data & M_SET_RESET;
1497
1498     if (*s->dptr == '?') {
1499       query = 1;
1500       s->dptr++;
1501     }
1502     else if (m_strncmp("no", s->dptr, 2) == 0) {
1503       s->dptr += 2;
1504       unset = !unset;
1505     }
1506     else if (m_strncmp("inv", s->dptr, 3) == 0) {
1507       s->dptr += 3;
1508       inv = !inv;
1509     }
1510     else if (*s->dptr == '&') {
1511       reset = 1;
1512       s->dptr++;
1513     }
1514
1515     /* get the variable name */
1516     mutt_extract_token (tmp, s, M_TOKEN_EQUAL);
1517
1518     /* resolve synonyms */
1519     if ((option = hash_find (ConfigOptions, tmp->data)) != NULL &&
1520         DTYPE (option->type == DT_SYN))
1521     {
1522       struct option_t* newopt = hash_find (ConfigOptions, (char*) option->data);
1523       syn_t* syn = syn_new();
1524       syn->f = m_strdup(CurRCFile);
1525       syn->l = CurRCLine;
1526       syn->n = newopt;
1527       syn->o = option;
1528       syn_list_push(&Synonyms, syn);
1529       option = newopt;
1530     }
1531
1532     if (!option && !(reset && m_strcmp("all", tmp->data) == 0)) {
1533       snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data);
1534       return (-1);
1535     }
1536     s->dptr = vskipspaces(s->dptr);
1537
1538     if (reset) {
1539       if (query || unset || inv) {
1540         snprintf (err->data, err->dsize, _("prefix is illegal with reset"));
1541         return (-1);
1542       }
1543
1544       if (s && *s->dptr == '=') {
1545         snprintf (err->data, err->dsize, _("value is illegal with reset"));
1546         return (-1);
1547       }
1548
1549       if (!m_strcmp("all", tmp->data)) {
1550         if (CurrentMenu == MENU_PAGER) {
1551           snprintf (err->data, err->dsize, _("Not available in this menu."));
1552           return (-1);
1553         }
1554         hash_map (ConfigOptions, mutt_restore_default, 1);
1555         set_option (OPTFORCEREDRAWINDEX);
1556         set_option (OPTFORCEREDRAWPAGER);
1557         set_option (OPTSORTSUBTHREADS);
1558         set_option (OPTNEEDRESORT);
1559         set_option (OPTRESORTINIT);
1560         set_option (OPTREDRAWTREE);
1561         return (0);
1562       } else {
1563         CHECK_PAGER;
1564         mutt_restore_default (NULL, option, 1);
1565       }
1566     }
1567     else if (DTYPE (option->type) == DT_BOOL) {
1568       /* XXX this currently ignores the function table
1569        * as we don't get invert and stuff into it */
1570       if (s && *s->dptr == '=') {
1571         if (unset || inv || query) {
1572           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1573           return (-1);
1574         }
1575
1576         s->dptr++;
1577         mutt_extract_token (tmp, s, 0);
1578         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1579           unset = inv = 0;
1580         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1581           unset = 1;
1582         else {
1583           snprintf (err->data, err->dsize, "Usage: set variable=yes|no");
1584           return (-1);
1585         }
1586       }
1587
1588       if (query) {
1589         bool_to_string (err->data, err->dsize, option);
1590         return 0;
1591       }
1592
1593       CHECK_PAGER;
1594       if (unset)
1595         unset_option (option->data);
1596       else if (inv)
1597         toggle_option (option->data);
1598       else
1599         set_option (option->data);
1600     }
1601     else if (DTYPE (option->type) == DT_STR ||
1602              DTYPE (option->type) == DT_PATH ||
1603              DTYPE (option->type) == DT_ADDR ||
1604              DTYPE (option->type) == DT_MAGIC ||
1605              DTYPE (option->type) == DT_NUM ||
1606              DTYPE (option->type) == DT_SORT ||
1607              DTYPE (option->type) == DT_RX)
1608     {
1609       /* XXX maybe we need to get unset into handlers? */
1610       if (DTYPE (option->type) == DT_STR ||
1611           DTYPE (option->type) == DT_PATH ||
1612           DTYPE (option->type) == DT_ADDR)
1613       {
1614         if (unset) {
1615           CHECK_PAGER;
1616           if (DTYPE (option->type) == DT_ADDR)
1617             address_list_wipe((address_t **) option->data);
1618           else
1619             p_delete((void **)(void *)&option->data);
1620           break;
1621         }
1622       }
1623
1624       if (query || *s->dptr != '=') {
1625         FuncTable[DTYPE (option->type)].opt_tostr
1626           (err->data, err->dsize, option);
1627         break;
1628       }
1629
1630       CHECK_PAGER;
1631       s->dptr++;
1632       mutt_extract_token (tmp, s, 0);
1633       if (!FuncTable[DTYPE (option->type)].opt_fromstr
1634           (option, tmp->data, err->data, err->dsize))
1635         r = -1;
1636     }
1637     else if (DTYPE (option->type) == DT_QUAD) {
1638
1639       if (query) {
1640         quad_to_string (err->data, err->dsize, option);
1641         break;
1642       }
1643
1644       if (*s->dptr == '=') {
1645         CHECK_PAGER;
1646         s->dptr++;
1647         mutt_extract_token (tmp, s, 0);
1648         if (ascii_strcasecmp ("yes", tmp->data) == 0)
1649           set_quadoption (option->data, M_YES);
1650         else if (ascii_strcasecmp ("no", tmp->data) == 0)
1651           set_quadoption (option->data, M_NO);
1652         else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0)
1653           set_quadoption (option->data, M_ASKYES);
1654         else if (ascii_strcasecmp ("ask-no", tmp->data) == 0)
1655           set_quadoption (option->data, M_ASKNO);
1656         else {
1657           snprintf (err->data, err->dsize, _("'%s' is invalid for $%s\n"),
1658                     tmp->data, option->option);
1659           r = -1;
1660           break;
1661         }
1662       }
1663       else {
1664         if (inv)
1665           toggle_quadoption (option->data);
1666         else if (unset)
1667           set_quadoption (option->data, M_NO);
1668         else
1669           set_quadoption (option->data, M_YES);
1670       }
1671     }
1672     else {
1673       snprintf (err->data, err->dsize, _("%s: unknown type"),
1674                 option->option);
1675       r = -1;
1676       break;
1677     }
1678
1679     if (option->flags & R_INDEX)
1680       set_option (OPTFORCEREDRAWINDEX);
1681     if (option->flags & R_PAGER)
1682       set_option (OPTFORCEREDRAWPAGER);
1683     if (option->flags & R_RESORT_SUB)
1684       set_option (OPTSORTSUBTHREADS);
1685     if (option->flags & R_RESORT)
1686       set_option (OPTNEEDRESORT);
1687     if (option->flags & R_RESORT_INIT)
1688       set_option (OPTRESORTINIT);
1689     if (option->flags & R_TREE)
1690       set_option (OPTREDRAWTREE);
1691   }
1692   return (r);
1693 }
1694
1695 #define MAXERRS 128
1696
1697 /* reads the specified initialization file.  returns -1 if errors were found
1698    so that we can pause to let the user know...  */
1699 static int source_rc (const char *rcfile, BUFFER * err)
1700 {
1701   FILE *f;
1702   int line = 0, rc = 0, conv = 0;
1703   BUFFER token;
1704   char *linebuf = NULL;
1705   char *currentline = NULL;
1706   ssize_t buflen;
1707   pid_t pid;
1708
1709   if ((f = mutt_open_read (rcfile, &pid)) == NULL) {
1710     snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno));
1711     return (-1);
1712   }
1713
1714   p_clear(&token, 1);
1715   while ((linebuf = mutt_read_line(linebuf, &buflen, f, &line)) != NULL) {
1716     conv = ConfigCharset && (*ConfigCharset) && Charset;
1717     if (conv) {
1718       currentline = m_strdup(linebuf);
1719       if (!currentline)
1720         continue;
1721       mutt_convert_string (&currentline, ConfigCharset, Charset, 0);
1722     }
1723     else
1724       currentline = linebuf;
1725
1726     CurRCLine = line;
1727     CurRCFile = rcfile;
1728
1729     if (mutt_parse_rc_line (currentline, &token, err) == -1) {
1730       mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data);
1731       if (--rc < -MAXERRS) {
1732         if (conv)
1733           p_delete(&currentline);
1734         break;
1735       }
1736     }
1737     else {
1738       if (rc < 0)
1739         rc = -1;
1740     }
1741     if (conv)
1742       p_delete(&currentline);
1743   }
1744   p_delete(&token.data);
1745   p_delete(&linebuf);
1746   m_fclose(&f);
1747   if (pid != -1)
1748     mutt_wait_filter (pid);
1749   if (rc) {
1750     /* the muttrc source keyword */
1751     snprintf (err->data, err->dsize,
1752               rc >= -MAXERRS ? _("source: errors in %s")
1753               : _("source: reading aborted due too many errors in %s"),
1754               rcfile);
1755     rc = -1;
1756   }
1757   return (rc);
1758 }
1759
1760 #undef MAXERRS
1761
1762 static int parse_source (BUFFER * tmp, BUFFER * s,
1763                          unsigned long data __attribute__ ((unused)),
1764                          BUFFER * err)
1765 {
1766   char path[_POSIX_PATH_MAX];
1767   int rc = 0;
1768
1769   do {
1770     if (mutt_extract_token (tmp, s, 0) != 0) {
1771       snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
1772       return (-1);
1773     }
1774
1775     m_strcpy(path, sizeof(path), tmp->data);
1776     mutt_expand_path (path, sizeof(path));
1777
1778     rc += source_rc (path, err);
1779   }
1780   while (MoreArgs (s));
1781
1782   return ((rc < 0) ? -1 : 0);
1783 }
1784
1785 /* line         command to execute
1786
1787    token        scratch buffer to be used by parser.  caller should free
1788                 token->data when finished.  the reason for this variable is
1789                 to avoid having to allocate and deallocate a lot of memory
1790                 if we are parsing many lines.  the caller can pass in the
1791                 memory to use, which avoids having to create new space for
1792                 every call to this function.
1793
1794    err          where to write error messages */
1795 int mutt_parse_rc_line (const char *line, BUFFER * token, BUFFER * err)
1796 {
1797   int i, r = -1;
1798   BUFFER expn;
1799
1800   p_clear(&expn, 1);
1801   expn.data = expn.dptr = line;
1802   expn.dsize = m_strlen(line);
1803
1804   *err->data = 0;
1805
1806   expn.dptr = vskipspaces(expn.dptr);
1807   while (*expn.dptr) {
1808     if (*expn.dptr == '#')
1809       break;                    /* rest of line is a comment */
1810     if (*expn.dptr == ';') {
1811       expn.dptr++;
1812       continue;
1813     }
1814     mutt_extract_token (token, &expn, 0);
1815     for (i = 0; Commands[i].name; i++) {
1816       if (!m_strcmp(token->data, Commands[i].name)) {
1817         if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
1818           goto finish;
1819         break;
1820       }
1821     }
1822     if (!Commands[i].name) {
1823       snprintf (err->data, err->dsize, _("%s: unknown command"),
1824                 NONULL (token->data));
1825       goto finish;
1826     }
1827   }
1828   r = 0;
1829 finish:
1830   if (expn.destroy)
1831     p_delete(&expn.data);
1832   return (r);
1833 }
1834
1835
1836 #define NUMVARS (sizeof(MuttVars)/sizeof(MuttVars[0]))
1837 #define NUMCOMMANDS (sizeof(Commands)/sizeof(Commands[0]))
1838 /* initial string that starts completion. No telling how much crap
1839  * the user has typed so far. Allocate LONG_STRING just to be sure! */
1840 char User_typed[LONG_STRING] = { 0 };
1841
1842 int Num_matched = 0;            /* Number of matches for completion */
1843 char Completed[STRING] = { 0 }; /* completed string (command or variable) */
1844 const char *Matches[MAX (NUMVARS, NUMCOMMANDS) + 1];  /* all the matches + User_typed */
1845
1846 /* helper function for completion.  Changes the dest buffer if
1847    necessary/possible to aid completion.
1848         dest == completion result gets here.
1849         src == candidate for completion.
1850         try == user entered data for completion.
1851         len == length of dest buffer.
1852 */
1853 static void candidate (char *dest, char *try, const char *src, int len)
1854 {
1855   int l;
1856
1857   if (strstr (src, try) == src) {
1858     Matches[Num_matched++] = src;
1859     if (dest[0] == 0)
1860       m_strcpy(dest, len, src);
1861     else {
1862       for (l = 0; src[l] && src[l] == dest[l]; l++);
1863       dest[l] = 0;
1864     }
1865   }
1866 }
1867
1868 int mutt_command_complete (char *buffer, ssize_t len, int pos, int numtabs)
1869 {
1870   char *pt = buffer;
1871   int num;
1872   int spaces;                   /* keep track of the number of leading spaces on the line */
1873
1874   buffer = vskipspaces(buffer);
1875   spaces = buffer - pt;
1876
1877   pt = buffer + pos - spaces;
1878   while ((pt > buffer) && !isspace ((unsigned char) *pt))
1879     pt--;
1880
1881   if (pt == buffer) {           /* complete cmd */
1882     /* first TAB. Collect all the matches */
1883     if (numtabs == 1) {
1884       Num_matched = 0;
1885       m_strcpy(User_typed, sizeof(User_typed), pt);
1886       p_clear(Matches, countof(Matches));
1887       p_clear(Completed, countof(Completed));
1888       for (num = 0; Commands[num].name; num++)
1889         candidate (Completed, User_typed, Commands[num].name,
1890                    sizeof(Completed));
1891       Matches[Num_matched++] = User_typed;
1892
1893       /* All matches are stored. Longest non-ambiguous string is ""
1894        * i.e. dont change 'buffer'. Fake successful return this time */
1895       if (User_typed[0] == 0)
1896         return 1;
1897     }
1898
1899     if (Completed[0] == 0 && User_typed[0])
1900       return 0;
1901
1902     /* Num_matched will _always_ be atleast 1 since the initial
1903      * user-typed string is always stored */
1904     if (numtabs == 1 && Num_matched == 2)
1905       snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1906     else if (numtabs > 1 && Num_matched > 2)
1907       /* cycle thru all the matches */
1908       snprintf (Completed, sizeof(Completed), "%s",
1909                 Matches[(numtabs - 2) % Num_matched]);
1910
1911     /* return the completed command */
1912     m_strcpy(buffer, len - spaces, Completed);
1913   }
1914   else if (!m_strncmp(buffer, "set", 3)
1915            || !m_strncmp(buffer, "unset", 5)
1916            || !m_strncmp(buffer, "reset", 5)
1917            || !m_strncmp(buffer, "toggle", 6)) {    /* complete variables */
1918     const char *prefixes[] = { "no", "inv", "?", "&", NULL };
1919
1920     pt++;
1921     /* loop through all the possible prefixes (no, inv, ...) */
1922     if (!m_strncmp(buffer, "set", 3)) {
1923       for (num = 0; prefixes[num]; num++) {
1924         if (!m_strncmp(pt, prefixes[num], m_strlen(prefixes[num]))) {
1925           pt += m_strlen(prefixes[num]);
1926           break;
1927         }
1928       }
1929     }
1930
1931     /* first TAB. Collect all the matches */
1932     if (numtabs == 1) {
1933       Num_matched = 0;
1934       m_strcpy(User_typed, sizeof(User_typed), pt);
1935       p_clear(Matches, countof(Matches));
1936       p_clear(Completed, countof(Completed));
1937       for (num = 0; MuttVars[num].option; num++)
1938         candidate(Completed, User_typed, MuttVars[num].option,
1939                   sizeof(Completed));
1940       Matches[Num_matched++] = User_typed;
1941
1942       /* All matches are stored. Longest non-ambiguous string is ""
1943        * i.e. dont change 'buffer'. Fake successful return this time */
1944       if (User_typed[0] == 0)
1945         return 1;
1946     }
1947
1948     if (Completed[0] == 0 && User_typed[0])
1949       return 0;
1950
1951     /* Num_matched will _always_ be atleast 1 since the initial
1952      * user-typed string is always stored */
1953     if (numtabs == 1 && Num_matched == 2)
1954       snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1955     else if (numtabs > 1 && Num_matched > 2)
1956       /* cycle thru all the matches */
1957       snprintf (Completed, sizeof(Completed), "%s",
1958                 Matches[(numtabs - 2) % Num_matched]);
1959
1960     m_strcpy(pt, buffer + len - pt - spaces, Completed);
1961   }
1962   else if (!m_strncmp(buffer, "exec", 4)) {
1963     struct binding_t *menu = km_get_table (CurrentMenu);
1964
1965     if (!menu && CurrentMenu != MENU_PAGER)
1966       menu = OpGeneric;
1967
1968     pt++;
1969     /* first TAB. Collect all the matches */
1970     if (numtabs == 1) {
1971       Num_matched = 0;
1972       m_strcpy(User_typed, sizeof(User_typed), pt);
1973       p_clear(Matches, countof(Matches));
1974       p_clear(Completed, countof(Completed));
1975       for (num = 0; menu[num].name; num++)
1976         candidate (Completed, User_typed, menu[num].name, sizeof(Completed));
1977       /* try the generic menu */
1978       if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) {
1979         menu = OpGeneric;
1980         for (num = 0; menu[num].name; num++)
1981           candidate (Completed, User_typed, menu[num].name,
1982                      sizeof(Completed));
1983       }
1984       Matches[Num_matched++] = User_typed;
1985
1986       /* All matches are stored. Longest non-ambiguous string is ""
1987        * i.e. dont change 'buffer'. Fake successful return this time */
1988       if (User_typed[0] == 0)
1989         return 1;
1990     }
1991
1992     if (Completed[0] == 0 && User_typed[0])
1993       return 0;
1994
1995     /* Num_matched will _always_ be atleast 1 since the initial
1996      * user-typed string is always stored */
1997     if (numtabs == 1 && Num_matched == 2)
1998       snprintf (Completed, sizeof(Completed), "%s", Matches[0]);
1999     else if (numtabs > 1 && Num_matched > 2)
2000       /* cycle thru all the matches */
2001       snprintf (Completed, sizeof(Completed), "%s",
2002                 Matches[(numtabs - 2) % Num_matched]);
2003
2004     m_strcpy(pt, buffer + len - pt - spaces, Completed);
2005   }
2006   else
2007     return 0;
2008
2009   return 1;
2010 }
2011
2012 int mutt_var_value_complete (char *buffer, ssize_t len, int pos)
2013 {
2014   char var[STRING], *pt = buffer;
2015   int spaces;
2016   struct option_t* option = NULL;
2017
2018   if (buffer[0] == 0)
2019     return 0;
2020
2021   buffer = vskipspaces(buffer);
2022   spaces = buffer - pt;
2023
2024   pt = buffer + pos - spaces;
2025   while ((pt > buffer) && !isspace ((unsigned char) *pt))
2026     pt--;
2027   pt++;                         /* move past the space */
2028   if (*pt == '=')               /* abort if no var before the '=' */
2029     return 0;
2030
2031   if (m_strncmp(buffer, "set", 3) == 0) {
2032     m_strcpy(var, sizeof(var), pt);
2033     /* ignore the trailing '=' when comparing */
2034     var[m_strlen(var) - 1] = 0;
2035     if (!(option = hash_find (ConfigOptions, var)))
2036       return 0;                 /* no such variable. */
2037     else {
2038       char tmp[LONG_STRING], tmp2[LONG_STRING];
2039       char *s, *d;
2040       ssize_t dlen = buffer + len - pt - spaces;
2041       const char *vals[] = { "no", "yes", "ask-no", "ask-yes" };
2042
2043       tmp[0] = '\0';
2044
2045       if ((DTYPE (option->type) == DT_STR) ||
2046           (DTYPE (option->type) == DT_PATH) ||
2047           (DTYPE (option->type) == DT_RX)) {
2048         m_strcpy(tmp, sizeof(tmp), NONULL(*((char **)option->data)));
2049         if (DTYPE (option->type) == DT_PATH)
2050           mutt_pretty_mailbox (tmp);
2051       }
2052       else if (DTYPE (option->type) == DT_ADDR) {
2053         rfc822_addrcat(tmp, sizeof(tmp), *((address_t **) option->data), 0);
2054       }
2055       else if (DTYPE (option->type) == DT_QUAD)
2056         m_strcpy(tmp, sizeof(tmp), vals[quadoption(option->data)]);
2057       else if (DTYPE (option->type) == DT_NUM)
2058         snprintf (tmp, sizeof(tmp), "%d", (*((short *) option->data)));
2059       else if (DTYPE (option->type) == DT_SORT) {
2060         const struct mapping_t *map;
2061         const char *p;
2062
2063         switch (option->type & DT_SUBTYPE_MASK) {
2064         case DT_SORT_ALIAS:
2065           map = SortAliasMethods;
2066           break;
2067         case DT_SORT_BROWSER:
2068           map = SortBrowserMethods;
2069           break;
2070         case DT_SORT_KEYS:
2071           map = SortKeyMethods;
2072           break;
2073         default:
2074           map = SortMethods;
2075           break;
2076         }
2077         p = mutt_getnamebyvalue(*((short *) option->data) & SORT_MASK, map);
2078         snprintf(tmp, sizeof(tmp), "%s%s%s",
2079                  (*((short *)option->data) & SORT_REVERSE) ? "reverse-" : "",
2080                  (*((short *)option->data) & SORT_LAST) ? "last-" : "", p);
2081       }
2082       else if (DTYPE (option->type) == DT_MAGIC) {
2083         const char *p;
2084         switch (DefaultMagic) {
2085           case M_MBOX:
2086             p = "mbox";
2087             break;
2088           case M_MMDF:
2089             p = "MMDF";
2090             break;
2091           case M_MH:
2092             p = "MH";
2093           break;
2094           case M_MAILDIR:
2095             p = "Maildir";
2096             break;
2097           default:
2098             p = "unknown";
2099         }
2100         m_strcpy(tmp, sizeof(tmp), p);
2101       }
2102       else if (DTYPE (option->type) == DT_BOOL)
2103         m_strcpy(tmp, sizeof(tmp), option(option->data) ? "yes" : "no");
2104       else
2105         return 0;
2106
2107       for (s = tmp, d = tmp2; *s && (d - tmp2) < ssizeof(tmp2) - 2;) {
2108         if (*s == '\\' || *s == '"')
2109           *d++ = '\\';
2110         *d++ = *s++;
2111       }
2112       *d = '\0';
2113
2114       m_strcpy(tmp, sizeof(tmp), pt);
2115       snprintf (pt, dlen, "%s\"%s\"", tmp, tmp2);
2116
2117       return 1;
2118     }
2119   }
2120   return 0;
2121 }
2122
2123 /* Implement the -Q command line flag */
2124 int mutt_query_variables (string_list_t * queries)
2125 {
2126   string_list_t *p;
2127
2128   char errbuff[STRING];
2129   char command[STRING];
2130
2131   BUFFER err, token;
2132
2133   p_clear(&err, 1);
2134   p_clear(&token, 1);
2135
2136   err.data = errbuff;
2137   err.dsize = sizeof(errbuff);
2138
2139   for (p = queries; p; p = p->next) {
2140     snprintf (command, sizeof(command), "set ?%s\n", p->data);
2141     if (mutt_parse_rc_line (command, &token, &err) == -1) {
2142       fprintf (stderr, "%s\n", err.data);
2143       p_delete(&token.data);
2144       return 1;
2145     }
2146     printf ("%s\n", err.data);
2147   }
2148
2149   p_delete(&token.data);
2150   return 0;
2151 }
2152
2153 static int mutt_execute_commands (string_list_t * p)
2154 {
2155   BUFFER err, token;
2156   char errstr[STRING];
2157
2158   p_clear(&err, 1);
2159   err.data = errstr;
2160   err.dsize = sizeof(errstr);
2161   p_clear(&token, 1);
2162   for (; p; p = p->next) {
2163     if (mutt_parse_rc_line (p->data, &token, &err) != 0) {
2164       fprintf (stderr, _("Error in command line: %s\n"), err.data);
2165       p_delete(&token.data);
2166       return (-1);
2167     }
2168   }
2169   p_delete(&token.data);
2170   return 0;
2171 }
2172
2173 void mutt_init (int skip_sys_rc, string_list_t * commands)
2174 {
2175   struct passwd *pw;
2176   struct utsname utsname;
2177   const char *p;
2178   char buffer[STRING], error[STRING];
2179   int default_rc = 0, need_pause = 0;
2180   int i;
2181   BUFFER err;
2182
2183   p_clear(&err, 1);
2184   err.data = error;
2185   err.dsize = sizeof(error);
2186
2187   ConfigOptions = hash_create (sizeof(MuttVars) * 2);
2188   for (i = 0; MuttVars[i].option; i++) {
2189     hash_insert (ConfigOptions, MuttVars[i].option, &MuttVars[i], 0);
2190   }
2191
2192   /*
2193    * XXX - use something even more difficult to predict?
2194    */
2195   snprintf (AttachmentMarker, sizeof(AttachmentMarker),
2196             "\033]9;%ld\a", (long) time (NULL));
2197
2198   /* on one of the systems I use, getcwd() does not return the same prefix
2199      as is listed in the passwd file */
2200   if ((p = getenv ("HOME")))
2201     Homedir = m_strdup(p);
2202
2203   /* Get some information about the user */
2204   if ((pw = getpwuid (getuid ()))) {
2205     char rnbuf[STRING];
2206
2207     Username = m_strdup(pw->pw_name);
2208     if (!Homedir)
2209       Homedir = m_strdup(pw->pw_dir);
2210
2211     mutt_gecos_name(rnbuf, sizeof(rnbuf), pw, GecosMask.rx);
2212     Realname = m_strdup(rnbuf);
2213     endpwent ();
2214   }
2215   else {
2216     if (!Homedir) {
2217       mutt_endwin (NULL);
2218       fputs (_("unable to determine home directory"), stderr);
2219       exit (1);
2220     }
2221     if ((p = getenv ("USER")))
2222       Username = m_strdup(p);
2223     else {
2224       mutt_endwin (NULL);
2225       fputs (_("unable to determine username"), stderr);
2226       exit (1);
2227     }
2228   }
2229
2230   /* And about the host... */
2231   uname (&utsname);
2232   /* some systems report the FQDN instead of just the hostname */
2233   if ((p = strchr (utsname.nodename, '.'))) {
2234     Hostname = p_dupstr(utsname.nodename, p - utsname.nodename);
2235     p++;
2236     m_strcpy(buffer, sizeof(buffer), p);       /* save the domain for below */
2237   }
2238   else
2239     Hostname = m_strdup(utsname.nodename);
2240
2241   if (!p && getdnsdomainname(buffer, sizeof(buffer)) == -1)
2242     Fqdn = m_strdup("@");
2243   else
2244   if (*buffer != '@') {
2245     Fqdn = p_new(char, m_strlen(buffer) + m_strlen(Hostname) + 2);
2246     sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer);
2247   }
2248   else
2249     Fqdn = m_strdup(NONULL (Hostname));
2250
2251 #ifdef USE_NNTP
2252   {
2253     FILE *f;
2254     char *q;
2255
2256     if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r"))) {
2257       buffer[0] = '\0';
2258       fgets (buffer, sizeof(buffer), f);
2259       p = vskipspaces(buffer);
2260       q = (char*)p;
2261       while (*q && !isspace(*q))
2262         q++;
2263       *q = '\0';
2264       NewsServer = m_strdup(p);
2265       m_fclose(&f);
2266     }
2267   }
2268   if ((p = getenv ("NNTPSERVER")))
2269     NewsServer = m_strdup(p);
2270 #endif
2271
2272   if ((p = getenv ("MAIL")))
2273     Spoolfile = m_strdup(p);
2274   else if ((p = getenv ("MAILDIR")))
2275     Spoolfile = m_strdup(p);
2276   else {
2277 #ifdef HOMESPOOL
2278     mutt_concat_path(buffer, sizeof(buffer), NONULL(Homedir), MAILPATH);
2279 #else
2280     mutt_concat_path(buffer, sizeof(buffer), MAILPATH, NONULL(Username));
2281 #endif
2282     Spoolfile = m_strdup(buffer);
2283   }
2284
2285   if ((p = getenv ("MAILCAPS")))
2286     MailcapPath = m_strdup(p);
2287   else {
2288     /* Default search path from RFC1524 */
2289     MailcapPath =
2290       m_strdup("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR
2291                    "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap");
2292   }
2293
2294   Tempdir = m_strdup((p = getenv ("TMPDIR")) ? p : "/tmp");
2295
2296   p = getenv ("VISUAL");
2297   if (!p) {
2298     p = getenv ("EDITOR");
2299     if (!p)
2300       p = "vi";
2301   }
2302   Editor = m_strdup(p);
2303
2304   if ((p = getenv ("REPLYTO")) != NULL) {
2305     BUFFER buf, token;
2306
2307     snprintf (buffer, sizeof(buffer), "Reply-To: %s", p);
2308
2309     p_clear(&buf, 1);
2310     buf.data = buf.dptr = buffer;
2311     buf.dsize = m_strlen(buffer);
2312
2313     p_clear(&token, 1);
2314     parse_my_hdr (&token, &buf, 0, &err);
2315     p_delete(&token.data);
2316   }
2317
2318   if ((p = getenv ("EMAIL")) != NULL)
2319     From = rfc822_parse_adrlist (NULL, p);
2320
2321   charset_initialize();
2322   mlua_initialize();
2323
2324   /* Set standard defaults */
2325   hash_map (ConfigOptions, mutt_set_default, 0);
2326   hash_map (ConfigOptions, mutt_restore_default, 0);
2327
2328   CurrentMenu = MENU_MAIN;
2329
2330 #ifdef HAVE_GETSID
2331   /* Unset suspend by default if we're the session leader */
2332   if (getsid (0) == getpid ())
2333     unset_option (OPTSUSPEND);
2334 #endif
2335
2336   mutt_init_history ();
2337
2338   if (!Muttrc) {
2339       snprintf (buffer, sizeof(buffer), "%s/.madmuttrc", NONULL (Homedir));
2340     if (access (buffer, F_OK) == -1)
2341       snprintf (buffer, sizeof(buffer), "%s/.madmutt/madmuttrc",
2342                 NONULL (Homedir));
2343
2344     default_rc = 1;
2345     Muttrc = m_strdup(buffer);
2346   }
2347   else {
2348     m_strcpy(buffer, sizeof(buffer), Muttrc);
2349     p_delete(&Muttrc);
2350     mutt_expand_path (buffer, sizeof(buffer));
2351     Muttrc = m_strdup(buffer);
2352   }
2353   p_delete(&AliasFile);
2354   AliasFile = m_strdup(NONULL (Muttrc));
2355
2356   /* Process the global rc file if it exists and the user hasn't explicity
2357      requested not to via "-n".  */
2358   if (!skip_sys_rc) {
2359     snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", SYSCONFDIR,
2360               MUTT_VERSION);
2361     if (access (buffer, F_OK) == -1)
2362       snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", SYSCONFDIR);
2363     if (access (buffer, F_OK) == -1)
2364       snprintf (buffer, sizeof(buffer), "%s/Madmuttrc-%s", PKGDATADIR,
2365                 MUTT_VERSION);
2366     if (access (buffer, F_OK) == -1)
2367       snprintf (buffer, sizeof(buffer), "%s/Madmuttrc", PKGDATADIR);
2368     if (access (buffer, F_OK) != -1) {
2369       if (source_rc (buffer, &err) != 0) {
2370         fputs (err.data, stderr);
2371         fputc ('\n', stderr);
2372         need_pause = 1;
2373       }
2374     }
2375   }
2376
2377   /* Read the user's initialization file.  */
2378   if (access (Muttrc, F_OK) != -1) {
2379     if (!option (OPTNOCURSES))
2380       mutt_endwin (NULL);
2381     if (source_rc (Muttrc, &err) != 0) {
2382       fputs (err.data, stderr);
2383       fputc ('\n', stderr);
2384       need_pause = 1;
2385     }
2386   }
2387   else if (!default_rc) {
2388     /* file specified by -F does not exist */
2389     snprintf (buffer, sizeof(buffer), "%s: %s", Muttrc, strerror (errno));
2390     mutt_endwin (buffer);
2391     exit (1);
2392   }
2393
2394   /* LUA {{{ */
2395   snprintf(buffer, sizeof(buffer), "%s/.madmutt.lua", NONULL(Homedir));
2396   if (access(buffer, F_OK) < 0)
2397       snprintf(buffer, sizeof(buffer), "%s/.madmutt/cfg.lua", NONULL(Homedir));
2398   if (!access(buffer, F_OK)) {
2399       need_pause = mlua_wrap(mutt_error, mlua_dofile(buffer));
2400   }
2401   /* }}} */
2402
2403   if (mutt_execute_commands (commands) != 0)
2404     need_pause = 1;
2405
2406   /* warn about synonym variables */
2407   if (Synonyms) {
2408     syn_t *syn;
2409
2410     fprintf (stderr, _("Warning: the following synonym variables were found:\n"));
2411
2412     for (syn = Synonyms; syn; syn = syn->next) {
2413       fprintf(stderr, "$%s ($%s should be used) (%s:%d)\n",
2414               syn->o ? NONULL(syn->o->option) : "",
2415               syn->n ? NONULL(syn->n->option) : "",
2416               NONULL(syn->f), syn->l);
2417     }
2418     fprintf (stderr, _("Warning: synonym variables are scheduled"
2419                        " for removal.\n"));
2420     syn_list_wipe(&Synonyms);
2421     need_pause = 1;
2422   }
2423
2424   if (need_pause && !option (OPTNOCURSES)) {
2425     if (mutt_any_key_to_continue (NULL) == -1)
2426       mutt_exit (1);
2427   }
2428 }
2429
2430 int mutt_get_hook_type (const char *name)
2431 {
2432   struct command_t *c;
2433
2434   for (c = Commands; c->name; c++)
2435     if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0)
2436       return c->data;
2437   return 0;
2438 }
2439
2440 /* dump out the value of all the variables we have */
2441 int mutt_dump_variables (int full) {
2442     ssize_t i = 0;
2443
2444     /* get all non-synonyms into list... */
2445     for (i = 0; MuttVars[i].option; i++) {
2446         struct option_t *option = MuttVars + i;
2447         char buf[LONG_STRING];
2448
2449         if (DTYPE(option->type) == DT_SYN)
2450             continue;
2451
2452         if (!full) {
2453             mutt_option_value(option->option, buf, sizeof(buf));
2454             if (!m_strcmp(buf, option->init))
2455                 continue;
2456         }
2457
2458         printf("set ");
2459         FuncTable[DTYPE(option->type)].opt_tostr(buf, sizeof(buf), option);
2460         printf ("%s\n", buf);
2461     }
2462
2463     printf ("\n# vi""m:set ft=muttrc:\n");
2464     return 0;
2465 }