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