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