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