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