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