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