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