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