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