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