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