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