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