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