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