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