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