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