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