chiark / gitweb /
disable LC_COLLATE for shell globbing
[disorder] / lib / configuration.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
4  * Portions copyright (C) 2007 Mark Wooding
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  */
21 /** @file lib/configuration.c
22  * @brief Configuration file support
23  */
24
25 #include <config.h>
26 #include "types.h"
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <stddef.h>
37 #include <pwd.h>
38 #include <langinfo.h>
39 #include <pcre.h>
40 #include <signal.h>
41
42 #include "configuration.h"
43 #include "mem.h"
44 #include "log.h"
45 #include "split.h"
46 #include "syscalls.h"
47 #include "table.h"
48 #include "inputline.h"
49 #include "charset.h"
50 #include "defs.h"
51 #include "mixer.h"
52 #include "printf.h"
53 #include "regsub.h"
54 #include "signame.h"
55 #include "authhash.h"
56
57 /** @brief Path to config file 
58  *
59  * set_configfile() sets the deafult if it is null.
60  */
61 char *configfile;
62
63 /** @brief Read user configuration
64  *
65  * If clear, the user-specific configuration is not read.
66  */
67 int config_per_user = 1;
68
69 /** @brief Config file parser state */
70 struct config_state {
71   /** @brief Filename */
72   const char *path;
73   /** @brief Line number */
74   int line;
75   /** @brief Configuration object under construction */
76   struct config *config;
77 };
78
79 /** @brief Current configuration */
80 struct config *config;
81
82 /** @brief One configuration item */
83 struct conf {
84   /** @brief Name as it appears in the config file */
85   const char *name;
86   /** @brief Offset in @ref config structure */
87   size_t offset;
88   /** @brief Pointer to item type */
89   const struct conftype *type;
90   /** @brief Pointer to item-specific validation routine */
91   int (*validate)(const struct config_state *cs,
92                   int nvec, char **vec);
93 };
94
95 /** @brief Type of a configuration item */
96 struct conftype {
97   /** @brief Pointer to function to set item */
98   int (*set)(const struct config_state *cs,
99              const struct conf *whoami,
100              int nvec, char **vec);
101   /** @brief Pointer to function to free item */
102   void (*free)(struct config *c, const struct conf *whoami);
103 };
104
105 /** @brief Compute the address of an item */
106 #define ADDRESS(C, TYPE) ((TYPE *)((char *)(C) + whoami->offset))
107 /** @brief Return the value of an item */
108 #define VALUE(C, TYPE) (*ADDRESS(C, TYPE))
109
110 static int set_signal(const struct config_state *cs,
111                       const struct conf *whoami,
112                       int nvec, char **vec) {
113   int n;
114   
115   if(nvec != 1) {
116     error(0, "%s:%d: '%s' requires one argument",
117           cs->path, cs->line, whoami->name);
118     return -1;
119   }
120   if((n = find_signal(vec[0])) == -1) {
121     error(0, "%s:%d: unknown signal '%s'",
122           cs->path, cs->line, vec[0]);
123     return -1;
124   }
125   VALUE(cs->config, int) = n;
126   return 0;
127 }
128
129 static int set_collections(const struct config_state *cs,
130                            const struct conf *whoami,
131                            int nvec, char **vec) {
132   struct collectionlist *cl;
133   
134   if(nvec != 3) {
135     error(0, "%s:%d: '%s' requires three arguments",
136           cs->path, cs->line, whoami->name);
137     return -1;
138   }
139   if(vec[2][0] != '/') {
140     error(0, "%s:%d: collection root must start with '/'",
141           cs->path, cs->line);
142     return -1;
143   }
144   if(vec[2][1] && vec[2][strlen(vec[2])-1] == '/') {
145     error(0, "%s:%d: collection root must not end with '/'",
146           cs->path, cs->line);
147     return -1;
148   }
149   cl = ADDRESS(cs->config, struct collectionlist);
150   ++cl->n;
151   cl->s = xrealloc(cl->s, cl->n * sizeof (struct collection));
152   cl->s[cl->n - 1].module = xstrdup(vec[0]);
153   cl->s[cl->n - 1].encoding = xstrdup(vec[1]);
154   cl->s[cl->n - 1].root = xstrdup(vec[2]);
155   return 0;
156 }
157
158 static int set_boolean(const struct config_state *cs,
159                        const struct conf *whoami,
160                        int nvec, char **vec) {
161   int state;
162   
163   if(nvec != 1) {
164     error(0, "%s:%d: '%s' takes only one argument",
165           cs->path, cs->line, whoami->name);
166     return -1;
167   }
168   if(!strcmp(vec[0], "yes")) state = 1;
169   else if(!strcmp(vec[0], "no")) state = 0;
170   else {
171     error(0, "%s:%d: argument to '%s' must be 'yes' or 'no'",
172           cs->path, cs->line, whoami->name);
173     return -1;
174   }
175   VALUE(cs->config, int) = state;
176   return 0;
177 }
178
179 static int set_string(const struct config_state *cs,
180                       const struct conf *whoami,
181                       int nvec, char **vec) {
182   if(nvec != 1) {
183     error(0, "%s:%d: '%s' takes only one argument",
184           cs->path, cs->line, whoami->name);
185     return -1;
186   }
187   VALUE(cs->config, char *) = xstrdup(vec[0]);
188   return 0;
189 }
190
191 static int set_stringlist(const struct config_state *cs,
192                           const struct conf *whoami,
193                           int nvec, char **vec) {
194   int n;
195   struct stringlist *sl;
196
197   sl = ADDRESS(cs->config, struct stringlist);
198   sl->n = 0;
199   for(n = 0; n < nvec; ++n) {
200     sl->n++;
201     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
202     sl->s[sl->n - 1] = xstrdup(vec[n]);
203   }
204   return 0;
205 }
206
207 static int set_integer(const struct config_state *cs,
208                        const struct conf *whoami,
209                        int nvec, char **vec) {
210   char *e;
211
212   if(nvec != 1) {
213     error(0, "%s:%d: '%s' takes only one argument",
214           cs->path, cs->line, whoami->name);
215     return -1;
216   }
217   if(xstrtol(ADDRESS(cs->config, long), vec[0], &e, 0)) {
218     error(errno, "%s:%d: converting integer", cs->path, cs->line);
219     return -1;
220   }
221   if(*e) {
222     error(0, "%s:%d: invalid integer syntax", cs->path, cs->line);
223     return -1;
224   }
225   return 0;
226 }
227
228 static int set_stringlist_accum(const struct config_state *cs,
229                                 const struct conf *whoami,
230                                 int nvec, char **vec) {
231   int n;
232   struct stringlist *s;
233   struct stringlistlist *sll;
234
235   sll = ADDRESS(cs->config, struct stringlistlist);
236   if(nvec == 0) {
237     sll->n = 0;
238     return 0;
239   }
240   sll->n++;
241   sll->s = xrealloc(sll->s, (sll->n * sizeof (struct stringlist)));
242   s = &sll->s[sll->n - 1];
243   s->n = nvec;
244   s->s = xmalloc((nvec + 1) * sizeof (char *));
245   for(n = 0; n < nvec; ++n)
246     s->s[n] = xstrdup(vec[n]);
247   return 0;
248 }
249
250 static int set_string_accum(const struct config_state *cs,
251                             const struct conf *whoami,
252                             int nvec, char **vec) {
253   int n;
254   struct stringlist *sl;
255
256   sl = ADDRESS(cs->config, struct stringlist);
257   if(nvec == 0) {
258     sl->n = 0;
259     return 0;
260   }
261   for(n = 0; n < nvec; ++n) {
262     sl->n++;
263     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
264     sl->s[sl->n - 1] = xstrdup(vec[n]);
265   }
266   return 0;
267 }
268
269 static int set_restrict(const struct config_state *cs,
270                         const struct conf *whoami,
271                         int nvec, char **vec) {
272   unsigned r = 0;
273   int n, i;
274   
275   static const struct restriction {
276     const char *name;
277     unsigned bit;
278   } restrictions[] = {
279     { "remove", RESTRICT_REMOVE },
280     { "scratch", RESTRICT_SCRATCH },
281     { "move", RESTRICT_MOVE },
282   };
283
284   for(n = 0; n < nvec; ++n) {
285     if((i = TABLE_FIND(restrictions, struct restriction, name, vec[n])) < 0) {
286       error(0, "%s:%d: invalid restriction '%s'",
287             cs->path, cs->line, vec[n]);
288       return -1;
289     }
290     r |= restrictions[i].bit;
291   }
292   VALUE(cs->config, unsigned) = r;
293   return 0;
294 }
295
296 static int parse_sample_format(const struct config_state *cs,
297                                struct stream_header *format,
298                                int nvec, char **vec) {
299   char *p = vec[0];
300   long t;
301
302   if(nvec != 1) {
303     error(0, "%s:%d: wrong number of arguments", cs->path, cs->line);
304     return -1;
305   }
306   if(xstrtol(&t, p, &p, 0)) {
307     error(errno, "%s:%d: converting bits-per-sample", cs->path, cs->line);
308     return -1;
309   }
310   if(t != 8 && t != 16) {
311     error(0, "%s:%d: bad bite-per-sample (%ld)", cs->path, cs->line, t);
312     return -1;
313   }
314   if(format) format->bits = t;
315   switch (*p) {
316     case 'l': case 'L': t = ENDIAN_LITTLE; p++; break;
317     case 'b': case 'B': t = ENDIAN_BIG; p++; break;
318     default: t = ENDIAN_NATIVE; break;
319   }
320   if(format) format->endian = t;
321   if(*p != '/') {
322     error(errno, "%s:%d: expected `/' after bits-per-sample",
323           cs->path, cs->line);
324     return -1;
325   }
326   p++;
327   if(xstrtol(&t, p, &p, 0)) {
328     error(errno, "%s:%d: converting sample-rate", cs->path, cs->line);
329     return -1;
330   }
331   if(t < 1 || t > INT_MAX) {
332     error(0, "%s:%d: silly sample-rate (%ld)", cs->path, cs->line, t);
333     return -1;
334   }
335   if(format) format->rate = t;
336   if(*p != '/') {
337     error(0, "%s:%d: expected `/' after sample-rate",
338           cs->path, cs->line);
339     return -1;
340   }
341   p++;
342   if(xstrtol(&t, p, &p, 0)) {
343     error(errno, "%s:%d: converting channels", cs->path, cs->line);
344     return -1;
345   }
346   if(t < 1 || t > 8) {
347     error(0, "%s:%d: silly number (%ld) of channels", cs->path, cs->line, t);
348     return -1;
349   }
350   if(format) format->channels = t;
351   if(*p) {
352     error(0, "%s:%d: junk after channels", cs->path, cs->line);
353     return -1;
354   }
355   return 0;
356 }
357
358 static int set_sample_format(const struct config_state *cs,
359                              const struct conf *whoami,
360                              int nvec, char **vec) {
361   return parse_sample_format(cs, ADDRESS(cs->config, struct stream_header),
362                              nvec, vec);
363 }
364
365 static int set_namepart(const struct config_state *cs,
366                         const struct conf *whoami,
367                         int nvec, char **vec) {
368   struct namepartlist *npl = ADDRESS(cs->config, struct namepartlist);
369   unsigned reflags;
370   const char *errstr;
371   int erroffset, n;
372   pcre *re;
373
374   if(nvec < 3) {
375     error(0, "%s:%d: namepart needs at least 3 arguments", cs->path, cs->line);
376     return -1;
377   }
378   if(nvec > 5) {
379     error(0, "%s:%d: namepart needs at most 5 arguments", cs->path, cs->line);
380     return -1;
381   }
382   reflags = nvec >= 5 ? regsub_flags(vec[4]) : 0;
383   if(!(re = pcre_compile(vec[1],
384                          PCRE_UTF8
385                          |regsub_compile_options(reflags),
386                          &errstr, &erroffset, 0))) {
387     error(0, "%s:%d: error compiling regexp /%s/: %s (offset %d)",
388           cs->path, cs->line, vec[1], errstr, erroffset);
389     return -1;
390   }
391   npl->s = xrealloc(npl->s, (npl->n + 1) * sizeof (struct namepart));
392   npl->s[npl->n].part = xstrdup(vec[0]);
393   npl->s[npl->n].re = re;
394   npl->s[npl->n].replace = xstrdup(vec[2]);
395   npl->s[npl->n].context = xstrdup(vec[3]);
396   npl->s[npl->n].reflags = reflags;
397   ++npl->n;
398   /* XXX a bit of a bodge; relies on there being very few parts. */
399   for(n = 0; (n < cs->config->nparts
400               && strcmp(cs->config->parts[n], vec[0])); ++n)
401     ;
402   if(n >= cs->config->nparts) {
403     cs->config->parts = xrealloc(cs->config->parts,
404                                  (cs->config->nparts + 1) * sizeof (char *));
405     cs->config->parts[cs->config->nparts++] = xstrdup(vec[0]);
406   }
407   return 0;
408 }
409
410 static int set_transform(const struct config_state *cs,
411                          const struct conf *whoami,
412                          int nvec, char **vec) {
413   struct transformlist *tl = ADDRESS(cs->config, struct transformlist);
414   pcre *re;
415   unsigned reflags;
416   const char *errstr;
417   int erroffset;
418
419   if(nvec < 3) {
420     error(0, "%s:%d: transform needs at least 3 arguments", cs->path, cs->line);
421     return -1;
422   }
423   if(nvec > 5) {
424     error(0, "%s:%d: transform needs at most 5 arguments", cs->path, cs->line);
425     return -1;
426   }
427   reflags = (nvec >= 5 ? regsub_flags(vec[4]) : 0);
428   if(!(re = pcre_compile(vec[1],
429                          PCRE_UTF8
430                          |regsub_compile_options(reflags),
431                          &errstr, &erroffset, 0))) {
432     error(0, "%s:%d: error compiling regexp /%s/: %s (offset %d)",
433           cs->path, cs->line, vec[1], errstr, erroffset);
434     return -1;
435   }
436   tl->t = xrealloc(tl->t, (tl->n + 1) * sizeof (struct namepart));
437   tl->t[tl->n].type = xstrdup(vec[0]);
438   tl->t[tl->n].context = xstrdup(vec[3] ? vec[3] : "*");
439   tl->t[tl->n].re = re;
440   tl->t[tl->n].replace = xstrdup(vec[2]);
441   tl->t[tl->n].flags = reflags;
442   ++tl->n;
443   return 0;
444 }
445
446 static int set_backend(const struct config_state *cs,
447                        const struct conf *whoami,
448                        int nvec, char **vec) {
449   int *const valuep = ADDRESS(cs->config, int);
450   
451   if(nvec != 1) {
452     error(0, "%s:%d: '%s' requires one argument",
453           cs->path, cs->line, whoami->name);
454     return -1;
455   }
456   if(!strcmp(vec[0], "alsa")) {
457 #if HAVE_ALSA_ASOUNDLIB_H
458     *valuep = BACKEND_ALSA;
459 #else
460     error(0, "%s:%d: ALSA is not available on this platform",
461           cs->path, cs->line);
462     return -1;
463 #endif
464   } else if(!strcmp(vec[0], "command"))
465     *valuep = BACKEND_COMMAND;
466   else if(!strcmp(vec[0], "network"))
467     *valuep = BACKEND_NETWORK;
468   else if(!strcmp(vec[0], "coreaudio")) {
469 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
470     *valuep = BACKEND_COREAUDIO;
471 #else
472     error(0, "%s:%d: Core Audio is not available on this platform",
473           cs->path, cs->line);
474     return -1;
475 #endif
476   } else if(!strcmp(vec[0], "oss")) {
477 #if HAVE_SYS_SOUNDCARD_H
478     *valuep = BACKEND_OSS;
479 #else
480     error(0, "%s:%d: OSS is not available on this platform",
481           cs->path, cs->line);
482     return -1;
483 #endif
484   } else {
485     error(0, "%s:%d: invalid '%s' value '%s'",
486           cs->path, cs->line, whoami->name, vec[0]);
487     return -1;
488   }
489   return 0;
490 }
491
492 /* free functions */
493
494 static void free_none(struct config attribute((unused)) *c,
495                       const struct conf attribute((unused)) *whoami) {
496 }
497
498 static void free_string(struct config *c,
499                         const struct conf *whoami) {
500   xfree(VALUE(c, char *));
501 }
502
503 static void free_stringlist(struct config *c,
504                             const struct conf *whoami) {
505   int n;
506   struct stringlist *sl = ADDRESS(c, struct stringlist);
507
508   for(n = 0; n < sl->n; ++n)
509     xfree(sl->s[n]);
510   xfree(sl->s);
511 }
512
513 static void free_stringlistlist(struct config *c,
514                                 const struct conf *whoami) {
515   int n, m;
516   struct stringlistlist *sll = ADDRESS(c, struct stringlistlist);
517   struct stringlist *sl;
518
519   for(n = 0; n < sll->n; ++n) {
520     sl = &sll->s[n];
521     for(m = 0; m < sl->n; ++m)
522       xfree(sl->s[m]);
523     xfree(sl->s);
524   }
525   xfree(sll->s);
526 }
527
528 static void free_collectionlist(struct config *c,
529                                 const struct conf *whoami) {
530   struct collectionlist *cll = ADDRESS(c, struct collectionlist);
531   struct collection *cl;
532   int n;
533
534   for(n = 0; n < cll->n; ++n) {
535     cl = &cll->s[n];
536     xfree(cl->module);
537     xfree(cl->encoding);
538     xfree(cl->root);
539   }
540   xfree(cll->s);
541 }
542
543 static void free_namepartlist(struct config *c,
544                               const struct conf *whoami) {
545   struct namepartlist *npl = ADDRESS(c, struct namepartlist);
546   struct namepart *np;
547   int n;
548
549   for(n = 0; n < npl->n; ++n) {
550     np = &npl->s[n];
551     xfree(np->part);
552     pcre_free(np->re);                  /* ...whatever pcre_free is set to. */
553     xfree(np->replace);
554     xfree(np->context);
555   }
556   xfree(npl->s);
557 }
558
559 static void free_transformlist(struct config *c,
560                                const struct conf *whoami) {
561   struct transformlist *tl = ADDRESS(c, struct transformlist);
562   struct transform *t;
563   int n;
564
565   for(n = 0; n < tl->n; ++n) {
566     t = &tl->t[n];
567     xfree(t->type);
568     pcre_free(t->re);                   /* ...whatever pcre_free is set to. */
569     xfree(t->replace);
570     xfree(t->context);
571   }
572   xfree(tl->t);
573 }
574
575 /* configuration types */
576
577 static const struct conftype
578   type_signal = { set_signal, free_none },
579   type_collections = { set_collections, free_collectionlist },
580   type_boolean = { set_boolean, free_none },
581   type_string = { set_string, free_string },
582   type_stringlist = { set_stringlist, free_stringlist },
583   type_integer = { set_integer, free_none },
584   type_stringlist_accum = { set_stringlist_accum, free_stringlistlist },
585   type_string_accum = { set_string_accum, free_stringlist },
586   type_sample_format = { set_sample_format, free_none },
587   type_restrict = { set_restrict, free_none },
588   type_namepart = { set_namepart, free_namepartlist },
589   type_transform = { set_transform, free_transformlist },
590   type_backend = { set_backend, free_none };
591
592 /* specific validation routine */
593
594 #define VALIDATE_FILE(test, what) do {                          \
595   struct stat sb;                                               \
596   int n;                                                        \
597                                                                 \
598   for(n = 0; n < nvec; ++n) {                                   \
599     if(stat(vec[n], &sb) < 0) {                                 \
600       error(errno, "%s:%d: %s", cs->path, cs->line, vec[n]);    \
601       return -1;                                                \
602     }                                                           \
603     if(!test(sb.st_mode)) {                                     \
604       error(0, "%s:%d: %s is not a %s",                         \
605             cs->path, cs->line, vec[n], what);                  \
606       return -1;                                                \
607     }                                                           \
608   }                                                             \
609 } while(0)
610
611 static int validate_isabspath(const struct config_state *cs,
612                               int nvec, char **vec) {
613   int n;
614
615   for(n = 0; n < nvec; ++n)
616     if(vec[n][0] != '/') {
617       error(errno, "%s:%d: %s: not an absolute path", 
618             cs->path, cs->line, vec[n]);
619       return -1;
620     }
621   return 0;
622 }
623
624 static int validate_isdir(const struct config_state *cs,
625                           int nvec, char **vec) {
626   VALIDATE_FILE(S_ISDIR, "directory");
627   return 0;
628 }
629
630 static int validate_isreg(const struct config_state *cs,
631                           int nvec, char **vec) {
632   VALIDATE_FILE(S_ISREG, "regular file");
633   return 0;
634 }
635
636 static int validate_ischr(const struct config_state *cs,
637                           int nvec, char **vec) {
638   VALIDATE_FILE(S_ISCHR, "character device");
639   return 0;
640 }
641
642 static int validate_player(const struct config_state *cs,
643                            int nvec,
644                            char attribute((unused)) **vec) {
645   if(nvec < 2) {
646     error(0, "%s:%d: should be at least 'player PATTERN MODULE'",
647           cs->path, cs->line);
648     return -1;
649   }
650   return 0;
651 }
652
653 static int validate_tracklength(const struct config_state *cs,
654                                 int nvec,
655                                 char attribute((unused)) **vec) {
656   if(nvec < 2) {
657     error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
658           cs->path, cs->line);
659     return -1;
660   }
661   return 0;
662 }
663
664 static int validate_allow(const struct config_state *cs,
665                           int nvec,
666                           char attribute((unused)) **vec) {
667   if(nvec != 2) {
668     error(0, "%s:%d: must be 'allow NAME PASS'", cs->path, cs->line);
669     return -1;
670   }
671   return 0;
672 }
673
674 static int validate_non_negative(const struct config_state *cs,
675                                  int nvec, char **vec) {
676   long n;
677
678   if(nvec < 1) {
679     error(0, "%s:%d: missing argument", cs->path, cs->line);
680     return -1;
681   }
682   if(nvec > 1) {
683     error(0, "%s:%d: too many arguments", cs->path, cs->line);
684     return -1;
685   }
686   if(xstrtol(&n, vec[0], 0, 0)) {
687     error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
688     return -1;
689   }
690   if(n < 0) {
691     error(0, "%s:%d: must not be negative", cs->path, cs->line);
692     return -1;
693   }
694   return 0;
695 }
696
697 static int validate_positive(const struct config_state *cs,
698                           int nvec, char **vec) {
699   long n;
700
701   if(nvec < 1) {
702     error(0, "%s:%d: missing argument", cs->path, cs->line);
703     return -1;
704   }
705   if(nvec > 1) {
706     error(0, "%s:%d: too many arguments", cs->path, cs->line);
707     return -1;
708   }
709   if(xstrtol(&n, vec[0], 0, 0)) {
710     error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
711     return -1;
712   }
713   if(n <= 0) {
714     error(0, "%s:%d: must be positive", cs->path, cs->line);
715     return -1;
716   }
717   return 0;
718 }
719
720 static int validate_isauser(const struct config_state *cs,
721                             int attribute((unused)) nvec,
722                             char **vec) {
723   struct passwd *pw;
724
725   if(!(pw = getpwnam(vec[0]))) {
726     error(0, "%s:%d: no such user as '%s'", cs->path, cs->line, vec[0]);
727     return -1;
728   }
729   return 0;
730 }
731
732 static int validate_sample_format(const struct config_state *cs,
733                                   int attribute((unused)) nvec,
734                                   char **vec) {
735   return parse_sample_format(cs, 0, nvec, vec);
736 }
737
738 static int validate_channel(const struct config_state *cs,
739                             int attribute((unused)) nvec,
740                             char **vec) {
741   if(mixer_channel(vec[0]) == -1) {
742     error(0, "%s:%d: invalid channel '%s'", cs->path, cs->line, vec[0]);
743     return -1;
744   }
745   return 0;
746 }
747
748 static int validate_any(const struct config_state attribute((unused)) *cs,
749                         int attribute((unused)) nvec,
750                         char attribute((unused)) **vec) {
751   return 0;
752 }
753
754 static int validate_url(const struct config_state attribute((unused)) *cs,
755                         int attribute((unused)) nvec,
756                         char **vec) {
757   const char *s;
758   int n;
759   /* absoluteURI   = scheme ":" ( hier_part | opaque_part )
760      scheme        = alpha *( alpha | digit | "+" | "-" | "." ) */
761   s = vec[0];
762   n = strspn(s, ("abcdefghijklmnopqrstuvwxyz"
763                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
764                  "0123456789"));
765   if(s[n] != ':') {
766     error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
767     return -1;
768   }
769   if(!strncmp(s, "http:", 5)
770      || !strncmp(s, "https:", 6)) {
771     s += n + 1;
772     /* we only do a rather cursory check */
773     if(strncmp(s, "//", 2)) {
774       error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
775       return -1;
776     }
777   }
778   return 0;
779 }
780
781 static int validate_alias(const struct config_state *cs,
782                           int nvec,
783                           char **vec) {
784   const char *s;
785   int in_brackets = 0, c;
786
787   if(nvec < 1) {
788     error(0, "%s:%d: missing argument", cs->path, cs->line);
789     return -1;
790   }
791   if(nvec > 1) {
792     error(0, "%s:%d: too many arguments", cs->path, cs->line);
793     return -1;
794   }
795   s = vec[0];
796   while((c = (unsigned char)*s++)) {
797     if(in_brackets) {
798       if(c == '}')
799         in_brackets = 0;
800       else if(!isalnum(c)) {
801         error(0, "%s:%d: invalid part name in alias expansion in '%s'",
802               cs->path, cs->line, vec[0]);
803           return -1;
804       }
805     } else {
806       if(c == '{') {
807         in_brackets = 1;
808         if(*s == '/')
809           ++s;
810       } else if(c == '\\') {
811         if(!(c = (unsigned char)*s++)) {
812           error(0, "%s:%d: unterminated escape in alias expansion in '%s'",
813                 cs->path, cs->line, vec[0]);
814           return -1;
815         } else if(c != '\\' && c != '{') {
816           error(0, "%s:%d: invalid escape in alias expansion in '%s'",
817                 cs->path, cs->line, vec[0]);
818           return -1;
819         }
820       }
821     }
822     ++s;
823   }
824   if(in_brackets) {
825     error(0, "%s:%d: unterminated part name in alias expansion in '%s'",
826           cs->path, cs->line, vec[0]);
827     return -1;
828   }
829   return 0;
830 }
831
832 static int validate_addrport(const struct config_state attribute((unused)) *cs,
833                              int nvec,
834                              char attribute((unused)) **vec) {
835   switch(nvec) {
836   case 0:
837     error(0, "%s:%d: missing address",
838           cs->path, cs->line);
839     return -1;
840   case 1:
841     error(0, "%s:%d: missing port name/number",
842           cs->path, cs->line);
843     return -1;
844   case 2:
845     return 0;
846   default:
847     error(0, "%s:%d: expected ADDRESS PORT",
848           cs->path, cs->line);
849     return -1;
850   }
851 }
852
853 static int validate_port(const struct config_state attribute((unused)) *cs,
854                          int nvec,
855                          char attribute((unused)) **vec) {
856   switch(nvec) {
857   case 0:
858     error(0, "%s:%d: missing address",
859           cs->path, cs->line);
860     return -1;
861   case 1:
862   case 2:
863     return 0;
864   default:
865     error(0, "%s:%d: expected [ADDRESS] PORT",
866           cs->path, cs->line);
867     return -1;
868   }
869 }
870
871 static int validate_algo(const struct config_state attribute((unused)) *cs,
872                          int nvec,
873                          char **vec) {
874   if(nvec != 1) {
875     error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
876     return -1;
877   }
878   if(!valid_authhash(vec[0])) {
879     error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
880     return -1;
881   }
882   return 0;
883 }
884
885 /** @brief Item name and and offset */
886 #define C(x) #x, offsetof(struct config, x)
887 /** @brief Item name and and offset */
888 #define C2(x,y) #x, offsetof(struct config, y)
889
890 /** @brief All configuration items */
891 static const struct conf conf[] = {
892   { C(alias),            &type_string,           validate_alias },
893   { C(allow),            &type_stringlist_accum, validate_allow },
894   { C(authorization_algorithm), &type_string,    validate_algo },
895   { C(broadcast),        &type_stringlist,       validate_addrport },
896   { C(broadcast_from),   &type_stringlist,       validate_addrport },
897   { C(channel),          &type_string,           validate_channel },
898   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
899   { C(checkpoint_min),   &type_integer,          validate_non_negative },
900   { C(collection),       &type_collections,      validate_any },
901   { C(connect),          &type_stringlist,       validate_addrport },
902   { C(dbversion),        &type_integer,          validate_positive },
903   { C(device),           &type_string,           validate_any },
904   { C(gap),              &type_integer,          validate_non_negative },
905   { C(history),          &type_integer,          validate_positive },
906   { C(home),             &type_string,           validate_isabspath },
907   { C(listen),           &type_stringlist,       validate_port },
908   { C(lock),             &type_boolean,          validate_any },
909   { C(mixer),            &type_string,           validate_ischr },
910   { C(multicast_loop),   &type_boolean,          validate_any },
911   { C(multicast_ttl),    &type_integer,          validate_non_negative },
912   { C(namepart),         &type_namepart,         validate_any },
913   { C2(nice, nice_rescan), &type_integer,        validate_non_negative },
914   { C(nice_rescan),      &type_integer,          validate_non_negative },
915   { C(nice_server),      &type_integer,          validate_any },
916   { C(nice_speaker),     &type_integer,          validate_any },
917   { C(noticed_history),  &type_integer,          validate_positive },
918   { C(password),         &type_string,           validate_any },
919   { C(player),           &type_stringlist_accum, validate_player },
920   { C(plugins),          &type_string_accum,     validate_isdir },
921   { C(prefsync),         &type_integer,          validate_positive },
922   { C(queue_pad),        &type_integer,          validate_positive },
923   { C(refresh),          &type_integer,          validate_positive },
924   { C2(restrict, restrictions),         &type_restrict,         validate_any },
925   { C(sample_format),    &type_sample_format,    validate_sample_format },
926   { C(scratch),          &type_string_accum,     validate_isreg },
927   { C(short_display),    &type_integer,          validate_positive },
928   { C(signal),           &type_signal,           validate_any },
929   { C(sox_generation),   &type_integer,          validate_non_negative },
930   { C(speaker_backend),  &type_backend,          validate_any },
931   { C(speaker_command),  &type_string,           validate_any },
932   { C(stopword),         &type_string_accum,     validate_any },
933   { C(templates),        &type_string_accum,     validate_isdir },
934   { C(tracklength),      &type_stringlist_accum, validate_tracklength },
935   { C(transform),        &type_transform,        validate_any },
936   { C(trust),            &type_string_accum,     validate_any },
937   { C(url),              &type_string,           validate_url },
938   { C(user),             &type_string,           validate_isauser },
939   { C(username),         &type_string,           validate_any },
940 };
941
942 /** @brief Find a configuration item's definition by key */
943 static const struct conf *find(const char *key) {
944   int n;
945
946   if((n = TABLE_FIND(conf, struct conf, name, key)) < 0)
947     return 0;
948   return &conf[n];
949 }
950
951 /** @brief Set a new configuration value */
952 static int config_set(const struct config_state *cs,
953                       int nvec, char **vec) {
954   const struct conf *which;
955
956   D(("config_set %s", vec[0]));
957   if(!(which = find(vec[0]))) {
958     error(0, "%s:%d: unknown configuration key '%s'",
959           cs->path, cs->line, vec[0]);
960     return -1;
961   }
962   return (which->validate(cs, nvec - 1, vec + 1)
963           || which->type->set(cs, which, nvec - 1, vec + 1));
964 }
965
966 /** @brief Error callback used by config_include() */
967 static void config_error(const char *msg, void *u) {
968   const struct config_state *cs = u;
969
970   error(0, "%s:%d: %s", cs->path, cs->line, msg);
971 }
972
973 /** @brief Include a file by name */
974 static int config_include(struct config *c, const char *path) {
975   FILE *fp;
976   char *buffer, *inputbuffer, **vec;
977   int n, ret = 0;
978   struct config_state cs;
979
980   cs.path = path;
981   cs.line = 0;
982   cs.config = c;
983   D(("%s: reading configuration", path));
984   if(!(fp = fopen(path, "r"))) {
985     error(errno, "error opening %s", path);
986     return -1;
987   }
988   while(!inputline(path, fp, &inputbuffer, '\n')) {
989     ++cs.line;
990     if(!(buffer = mb2utf8(inputbuffer))) {
991       error(errno, "%s:%d: cannot convert to UTF-8", cs.path, cs.line);
992       ret = -1;
993       xfree(inputbuffer);
994       continue;
995     }
996     xfree(inputbuffer);
997     if(!(vec = split(buffer, &n, SPLIT_COMMENTS|SPLIT_QUOTES,
998                      config_error, &cs))) {
999       ret = -1;
1000       xfree(buffer);
1001       continue;
1002     }
1003     if(n) {
1004       if(!strcmp(vec[0], "include")) {
1005         if(n != 2) {
1006           error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
1007           ret = -1;
1008         } else
1009           config_include(c, vec[1]);
1010       } else
1011         ret |= config_set(&cs, n, vec);
1012     }
1013     for(n = 0; vec[n]; ++n) xfree(vec[n]);
1014     xfree(vec);
1015     xfree(buffer);
1016   }
1017   if(ferror(fp)) {
1018     error(errno, "error reading %s", path);
1019     ret = -1;
1020   }
1021   fclose(fp);
1022   return ret;
1023 }
1024
1025 /** @brief Make a new default configuration */
1026 static struct config *config_default(void) {
1027   struct config *c = xmalloc(sizeof *c);
1028   const char *logname;
1029   struct passwd *pw;
1030
1031   /* Strings had better be xstrdup'd as they will get freed at some point. */
1032   c->gap = 2;
1033   c->history = 60;
1034   c->home = xstrdup(pkgstatedir);
1035   if(!(pw = getpwuid(getuid())))
1036     fatal(0, "cannot determine our username");
1037   logname = pw->pw_name;
1038   c->username = xstrdup(logname);
1039   c->refresh = 15;
1040   c->prefsync = 3600;
1041   c->signal = SIGKILL;
1042   c->alias = xstrdup("{/artist}{/album}{/title}{ext}");
1043   c->lock = 1;
1044   c->device = xstrdup("default");
1045   c->nice_rescan = 10;
1046   c->speaker_command = 0;
1047   c->sample_format.bits = 16;
1048   c->sample_format.rate = 44100;
1049   c->sample_format.channels = 2;
1050   c->sample_format.endian = ENDIAN_NATIVE;
1051   c->queue_pad = 10;
1052   c->speaker_backend = -1;
1053   c->multicast_ttl = 1;
1054   c->multicast_loop = 1;
1055   c->authorization_algorithm = xstrdup("sha1");
1056   c->noticed_history = 31;
1057   c->short_display = 32;
1058   c->mixer = xstrdup("/dev/mixer");
1059   c->channel = xstrdup("pcm");
1060   c->dbversion = 2;
1061   return c;
1062 }
1063
1064 static char *get_file(struct config *c, const char *name) {
1065   char *s;
1066
1067   byte_xasprintf(&s, "%s/%s", c->home, name);
1068   return s;
1069 }
1070
1071 /** @brief Set the default configuration file */
1072 static void set_configfile(void) {
1073   if(!configfile)
1074     byte_xasprintf(&configfile, "%s/config", pkgconfdir);
1075 }
1076
1077 /** @brief Free a configuration object */
1078 static void config_free(struct config *c) {
1079   int n;
1080
1081   if(c) {
1082     for(n = 0; n < (int)(sizeof conf / sizeof *conf); ++n)
1083       conf[n].type->free(c, &conf[n]);
1084     for(n = 0; n < c->nparts; ++n)
1085       xfree(c->parts[n]);
1086     xfree(c->parts);
1087     xfree(c);
1088   }
1089 }
1090
1091 /** @brief Set post-parse defaults */
1092 static void config_postdefaults(struct config *c,
1093                                 int server) {
1094   struct config_state cs;
1095   const struct conf *whoami;
1096   int n;
1097
1098   static const char *namepart[][4] = {
1099     { "title",  "/([0-9]+ *[-:] *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
1100     { "title",  "/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort" },
1101     { "album",  "/([^/]+)/[^/]+$",                    "$1", "*" },
1102     { "artist", "/([^/]+)/[^/]+/[^/]+$",              "$1", "*" },
1103     { "ext",    "(\\.[a-zA-Z0-9]+)$",                 "$1", "*" },
1104   };
1105 #define NNAMEPART (int)(sizeof namepart / sizeof *namepart)
1106
1107   static const char *transform[][5] = {
1108     { "track", "^.*/([0-9]+ *[-:] *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display", "" },
1109     { "track", "^.*/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort", "" },
1110     { "dir",   "^.*/([^/]+)$",                          "$1", "*", "" },
1111     { "dir",   "^(the) ([^/]*)",                        "$2, $1", "sort", "i", },
1112     { "dir",   "[[:punct:]]",                           "", "sort", "g", }
1113   };
1114 #define NTRANSFORM (int)(sizeof transform / sizeof *transform)
1115
1116   cs.path = "<internal>";
1117   cs.line = 0;
1118   cs.config = c;
1119   if(!c->namepart.n) {
1120     whoami = find("namepart");
1121     for(n = 0; n < NNAMEPART; ++n)
1122       set_namepart(&cs, whoami, 4, (char **)namepart[n]);
1123   }
1124   if(!c->transform.n) {
1125     whoami = find("transform");
1126     for(n = 0; n < NTRANSFORM; ++n)
1127       set_transform(&cs, whoami, 5, (char **)transform[n]);
1128   }
1129   if(c->speaker_backend == -1) {
1130     if(c->speaker_command)
1131       c->speaker_backend = BACKEND_COMMAND;
1132     else if(c->broadcast.n)
1133       c->speaker_backend = BACKEND_NETWORK;
1134     else {
1135 #if HAVE_ALSA_ASOUNDLIB_H
1136       c->speaker_backend = BACKEND_ALSA;
1137 #elif HAVE_SYS_SOUNDCARD_H
1138       c->speaker_backend = BACKEND_OSS;
1139 #elif HAVE_COREAUDIO_AUDIOHARDWARE_H
1140       c->speaker_backend = BACKEND_COREAUDIO;
1141 #else
1142       c->speaker_backend = BACKEND_COMMAND;
1143 #endif
1144     }
1145   }
1146   if(server) {
1147     if(c->speaker_backend == BACKEND_COMMAND && !c->speaker_command)
1148       fatal(0, "speaker_backend is command but speaker_command is not set");
1149     if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n)
1150       fatal(0, "speaker_backend is network but broadcast is not set");
1151   }
1152   /* Override sample format */
1153   switch(c->speaker_backend) {
1154   case BACKEND_NETWORK:
1155     c->sample_format.rate = 44100;
1156     c->sample_format.channels = 2;
1157     c->sample_format.bits = 16;
1158     c->sample_format.endian = ENDIAN_BIG;
1159     break;
1160   case BACKEND_COREAUDIO:
1161     c->sample_format.rate = 44100;
1162     c->sample_format.channels = 2;
1163     c->sample_format.bits = 16;
1164     c->sample_format.endian = ENDIAN_NATIVE;
1165     break; 
1166  }
1167 }
1168
1169 /** @brief (Re-)read the config file
1170  * @param server If set, do extra checking
1171  */
1172 int config_read(int server) {
1173   struct config *c;
1174   char *privconf;
1175   struct passwd *pw;
1176
1177   set_configfile();
1178   c = config_default();
1179   /* standalone Disobedience installs might not have a global config file */
1180   if(access(configfile, F_OK) == 0)
1181     if(config_include(c, configfile))
1182       return -1;
1183   /* if we can read the private config file, do */
1184   if((privconf = config_private())
1185      && access(privconf, R_OK) == 0
1186      && config_include(c, privconf))
1187     return -1;
1188   xfree(privconf);
1189   /* if there's a per-user system config file for this user, read it */
1190   if(config_per_user) {
1191     if(!(pw = getpwuid(getuid())))
1192       fatal(0, "cannot determine our username");
1193     if((privconf = config_usersysconf(pw))
1194        && access(privconf, F_OK) == 0
1195        && config_include(c, privconf))
1196       return -1;
1197     xfree(privconf);
1198     /* if we have a password file, read it */
1199     if((privconf = config_userconf(getenv("HOME"), pw))
1200        && access(privconf, F_OK) == 0
1201        && config_include(c, privconf))
1202       return -1;
1203     xfree(privconf);
1204   }
1205   /* install default namepart and transform settings */
1206   config_postdefaults(c, server);
1207   /* everything is good so we shall use the new config */
1208   config_free(config);
1209   config = c;
1210   return 0;
1211 }
1212
1213 /** @brief Return the path to the private configuration file */
1214 char *config_private(void) {
1215   char *s;
1216
1217   set_configfile();
1218   byte_xasprintf(&s, "%s.private", configfile);
1219   return s;
1220 }
1221
1222 /** @brief Return the path to user's personal configuration file */
1223 char *config_userconf(const char *home, const struct passwd *pw) {
1224   char *s;
1225
1226   if(!home && !pw && !(pw = getpwuid(getuid())))
1227     fatal(0, "cannot determine our username");
1228   byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
1229   return s;
1230 }
1231
1232 /** @brief Return the path to user-specific system configuration */
1233 char *config_usersysconf(const struct passwd *pw) {
1234   char *s;
1235
1236   set_configfile();
1237   if(!strchr(pw->pw_name, '/')) {
1238     byte_xasprintf(&s, "%s.%s", configfile, pw->pw_name);
1239     return s;
1240   } else
1241     return 0;
1242 }
1243
1244 char *config_get_file(const char *name) {
1245   return get_file(config, name);
1246 }
1247
1248 /*
1249 Local Variables:
1250 c-basic-offset:2
1251 comment-column:40
1252 fill-column:79
1253 End:
1254 */