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