chiark / gitweb /
Cope with various header files being missing.
[disorder] / lib / configuration.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2011, 2013 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 3 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,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU 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, see <http://www.gnu.org/licenses/>.
18  */
19 /** @file lib/configuration.c
20  * @brief Configuration file support
21  */
22
23 #include "common.h"
24
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #if HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <ctype.h>
32 #include <stddef.h>
33 #if HAVE_PWD_H
34 # include <pwd.h>
35 #endif
36 #if HAVE_LANGINFO_H
37 # include <langinfo.h>
38 #endif
39 #if HAVE_PCRE_H
40 # include <pcre.h>
41 #endif
42 #include <signal.h>
43
44 #include "rights.h"
45 #include "configuration.h"
46 #include "mem.h"
47 #include "log.h"
48 #include "split.h"
49 #include "syscalls.h"
50 #include "table.h"
51 #include "inputline.h"
52 #include "charset.h"
53 #include "defs.h"
54 #include "printf.h"
55 #if HAVE_PCRE_H
56 # include "regsub.h"
57 #endif
58 #include "signame.h"
59 #include "authhash.h"
60 #include "vector.h"
61 #include "uaudio.h"
62
63 /** @brief Path to config file 
64  *
65  * set_configfile() sets the default if it is null.
66  */
67 char *configfile;
68
69 /** @brief Read user configuration
70  *
71  * If clear, the user-specific configuration is not read.
72  */
73 int config_per_user = 1;
74
75 /** @brief Table of audio APIs
76  *
77  * Only set in server processes.
78  */
79 const struct uaudio *const *config_uaudio_apis;
80
81 /** @brief Config file parser state */
82 struct config_state {
83   /** @brief Filename */
84   const char *path;
85
86   /** @brief Line number */
87   int line;
88
89   /** @brief Configuration object under construction */
90   struct config *config;
91 };
92
93 /** @brief Current configuration */
94 struct config *config;
95
96 /** @brief One configuration item */
97 struct conf {
98   /** @brief Name as it appears in the config file */
99   const char *name;
100
101   /** @brief Offset in @ref config structure */
102   size_t offset;
103
104   /** @brief Pointer to item type */
105   const struct conftype *type;
106
107   /** @brief Pointer to item-specific validation routine
108    * @param cs Configuration state
109    * @param nvec Length of (proposed) new value
110    * @param vec Elements of new value
111    * @return 0 on success, non-0 on error
112    *
113    * The validate function should report any error it detects.
114    */
115   int (*validate)(const struct config_state *cs,
116                   int nvec, char **vec);
117 };
118
119 /** @brief Type of a configuration item */
120 struct conftype {
121   /** @brief Pointer to function to set item
122    * @param cs Configuration state
123    * @param whoami Configuration item to set
124    * @param nvec Length of new value
125    * @param vec New value
126    * @return 0 on success, non-0 on error
127    */
128   int (*set)(const struct config_state *cs,
129              const struct conf *whoami,
130              int nvec, char **vec);
131
132   /** @brief Pointer to function to free item
133    * @param c Configuration structure to free an item of
134    * @param whoami Configuration item to free
135    */
136   void (*free)(struct config *c, const struct conf *whoami);
137 };
138
139 /** @brief Compute the address of an item */
140 #define ADDRESS(C, TYPE) ((TYPE *)((char *)(C) + whoami->offset))
141 /** @brief Return the value of an item */
142 #define VALUE(C, TYPE) (*ADDRESS(C, TYPE))
143
144 static int stringlist_compare(const struct stringlist *a,
145                               const struct stringlist *b);
146 static int namepartlist_compare(const struct namepartlist *a,
147                                 const struct namepartlist *b);
148
149 static int set_signal(const struct config_state *cs,
150                       const struct conf *whoami,
151                       int nvec, char **vec) {
152   int n;
153   
154   if(nvec != 1) {
155     disorder_error(0, "%s:%d: '%s' requires one argument",
156           cs->path, cs->line, whoami->name);
157     return -1;
158   }
159   if((n = find_signal(vec[0])) == -1) {
160     disorder_error(0, "%s:%d: unknown signal '%s'",
161           cs->path, cs->line, vec[0]);
162     return -1;
163   }
164   VALUE(cs->config, int) = n;
165   return 0;
166 }
167
168 static int set_collections(const struct config_state *cs,
169                            const struct conf *whoami,
170                            int nvec, char **vec) {
171   struct collectionlist *cl;
172   const char *root, *encoding, *module;
173   
174   switch(nvec) {
175   case 1:
176     module = 0;
177     encoding = 0;
178     root = vec[0];
179     break;
180   case 2:
181     module = vec[0];
182     encoding = 0;
183     root = vec[1];
184     break;
185   case 3:
186     module = vec[0];
187     encoding = vec[1];
188     root = vec[2];
189     break;
190   case 0:
191     disorder_error(0, "%s:%d: '%s' requires at least one argument",
192                    cs->path, cs->line, whoami->name);
193     return -1;
194   default:
195     disorder_error(0, "%s:%d: '%s' requires at most three arguments",
196                    cs->path, cs->line, whoami->name);
197     return -1;
198   }
199   /* Sanity check root */
200   if(root[0] != '/') {
201     disorder_error(0, "%s:%d: collection root must start with '/'",
202                    cs->path, cs->line);
203     return -1;
204   }
205   if(root[1] && root[strlen(root)-1] == '/') {
206     disorder_error(0, "%s:%d: collection root must not end with '/'",
207                    cs->path, cs->line);
208     return -1;
209   }
210   /* Defaults */
211   if(!module)
212     module = "fs";
213 #if HAVE_LANGINFO_H
214   if(!encoding)
215     encoding = nl_langinfo(CODESET);
216 #else
217   if(!encoding)
218     encoding = "ascii";
219 #endif
220   cl = ADDRESS(cs->config, struct collectionlist);
221   ++cl->n;
222   cl->s = xrealloc(cl->s, cl->n * sizeof (struct collection));
223   cl->s[cl->n - 1].module = xstrdup(module);
224   cl->s[cl->n - 1].encoding = xstrdup(encoding);
225   cl->s[cl->n - 1].root = xstrdup(root);
226   return 0;
227 }
228
229 static int set_boolean(const struct config_state *cs,
230                        const struct conf *whoami,
231                        int nvec, char **vec) {
232   int state;
233   
234   if(nvec != 1) {
235     disorder_error(0, "%s:%d: '%s' takes only one argument",
236                    cs->path, cs->line, whoami->name);
237     return -1;
238   }
239   if(!strcmp(vec[0], "yes")) state = 1;
240   else if(!strcmp(vec[0], "no")) state = 0;
241   else {
242     disorder_error(0, "%s:%d: argument to '%s' must be 'yes' or 'no'",
243                    cs->path, cs->line, whoami->name);
244     return -1;
245   }
246   VALUE(cs->config, int) = state;
247   return 0;
248 }
249
250 static int set_string(const struct config_state *cs,
251                       const struct conf *whoami,
252                       int nvec, char **vec) {
253   if(nvec != 1) {
254     disorder_error(0, "%s:%d: '%s' takes only one argument",
255                    cs->path, cs->line, whoami->name);
256     return -1;
257   }
258   xfree(VALUE(cs->config, char *));
259   VALUE(cs->config, char *) = xstrdup(vec[0]);
260   return 0;
261 }
262
263 static int set_stringlist(const struct config_state *cs,
264                           const struct conf *whoami,
265                           int nvec, char **vec) {
266   int n;
267   struct stringlist *sl;
268
269   sl = ADDRESS(cs->config, struct stringlist);
270   sl->n = 0;
271   for(n = 0; n < nvec; ++n) {
272     sl->n++;
273     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
274     sl->s[sl->n - 1] = xstrdup(vec[n]);
275   }
276   return 0;
277 }
278
279 static int set_integer(const struct config_state *cs,
280                        const struct conf *whoami,
281                        int nvec, char **vec) {
282   char *e;
283
284   if(nvec != 1) {
285     disorder_error(0, "%s:%d: '%s' takes only one argument",
286                    cs->path, cs->line, whoami->name);
287     return -1;
288   }
289   if(xstrtol(ADDRESS(cs->config, long), vec[0], &e, 0)) {
290     disorder_error(errno, "%s:%d: converting integer", cs->path, cs->line);
291     return -1;
292   }
293   if(*e) {
294     disorder_error(0, "%s:%d: invalid integer syntax", cs->path, cs->line);
295     return -1;
296   }
297   return 0;
298 }
299
300 static int set_stringlist_accum(const struct config_state *cs,
301                                 const struct conf *whoami,
302                                 int nvec, char **vec) {
303   int n;
304   struct stringlist *s;
305   struct stringlistlist *sll;
306
307   sll = ADDRESS(cs->config, struct stringlistlist);
308   if(nvec == 0) {
309     sll->n = 0;
310     return 0;
311   }
312   sll->n++;
313   sll->s = xrealloc(sll->s, (sll->n * sizeof (struct stringlist)));
314   s = &sll->s[sll->n - 1];
315   s->n = nvec;
316   s->s = xmalloc((nvec + 1) * sizeof (char *));
317   for(n = 0; n < nvec; ++n)
318     s->s[n] = xstrdup(vec[n]);
319   return 0;
320 }
321
322 static int set_string_accum(const struct config_state *cs,
323                             const struct conf *whoami,
324                             int nvec, char **vec) {
325   int n;
326   struct stringlist *sl;
327
328   sl = ADDRESS(cs->config, struct stringlist);
329   if(nvec == 0) {
330     sl->n = 0;
331     return 0;
332   }
333   for(n = 0; n < nvec; ++n) {
334     sl->n++;
335     sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
336     sl->s[sl->n - 1] = xstrdup(vec[n]);
337   }
338   return 0;
339 }
340
341 static int parse_sample_format(const struct config_state *cs,
342                                struct stream_header *format,
343                                int nvec, char **vec) {
344   char *p = vec[0];
345   long t;
346
347   if(nvec != 1) {
348     disorder_error(0, "%s:%d: wrong number of arguments", cs->path, cs->line);
349     return -1;
350   }
351   if(xstrtol(&t, p, &p, 0)) {
352     disorder_error(errno, "%s:%d: converting bits-per-sample",
353                    cs->path, cs->line);
354     return -1;
355   }
356   if(t != 8 && t != 16) {
357     disorder_error(0, "%s:%d: bad bits-per-sample (%ld)",
358                    cs->path, cs->line, t);
359     return -1;
360   }
361   if(format) format->bits = t;
362   switch (*p) {
363     case 'l': case 'L': t = ENDIAN_LITTLE; p++; break;
364     case 'b': case 'B': t = ENDIAN_BIG; p++; break;
365     default: t = ENDIAN_NATIVE; break;
366   }
367   if(format) format->endian = t;
368   if(*p != '/') {
369     disorder_error(errno, "%s:%d: expected `/' after bits-per-sample",
370           cs->path, cs->line);
371     return -1;
372   }
373   p++;
374   if(xstrtol(&t, p, &p, 0)) {
375     disorder_error(errno, "%s:%d: converting sample-rate", cs->path, cs->line);
376     return -1;
377   }
378   if(t < 1 || t > INT_MAX) {
379     disorder_error(0, "%s:%d: silly sample-rate (%ld)", cs->path, cs->line, t);
380     return -1;
381   }
382   if(format) format->rate = t;
383   if(*p != '/') {
384     disorder_error(0, "%s:%d: expected `/' after sample-rate",
385                    cs->path, cs->line);
386     return -1;
387   }
388   p++;
389   if(xstrtol(&t, p, &p, 0)) {
390     disorder_error(errno, "%s:%d: converting channels", cs->path, cs->line);
391     return -1;
392   }
393   if(t < 1 || t > 8) {
394     disorder_error(0, "%s:%d: silly number (%ld) of channels",
395                    cs->path, cs->line, t);
396     return -1;
397   }
398   if(format) format->channels = t;
399   if(*p) {
400     disorder_error(0, "%s:%d: junk after channels", cs->path, cs->line);
401     return -1;
402   }
403   return 0;
404 }
405
406 static int set_sample_format(const struct config_state *cs,
407                              const struct conf *whoami,
408                              int nvec, char **vec) {
409   return parse_sample_format(cs, ADDRESS(cs->config, struct stream_header),
410                              nvec, vec);
411 }
412
413 #if HAVE_PCRE_H
414 static int set_namepart(const struct config_state *cs,
415                         const struct conf *whoami,
416                         int nvec, char **vec) {
417   struct namepartlist *npl = ADDRESS(cs->config, struct namepartlist);
418   unsigned reflags;
419   const char *errstr;
420   int erroffset, n;
421   pcre *re;
422
423   if(nvec < 3) {
424     disorder_error(0, "%s:%d: namepart needs at least 3 arguments",
425                    cs->path, cs->line);
426     return -1;
427   }
428   if(nvec > 5) {
429     disorder_error(0, "%s:%d: namepart needs at most 5 arguments",
430                    cs->path, cs->line);
431     return -1;
432   }
433   reflags = nvec >= 5 ? regsub_flags(vec[4]) : 0;
434   if(!(re = pcre_compile(vec[1],
435                          PCRE_UTF8
436                          |regsub_compile_options(reflags),
437                          &errstr, &erroffset, 0))) {
438     disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
439                    cs->path, cs->line, vec[1], errstr, erroffset);
440     return -1;
441   }
442   npl->s = xrealloc(npl->s, (npl->n + 1) * sizeof (struct namepart));
443   npl->s[npl->n].part = xstrdup(vec[0]);
444   npl->s[npl->n].re = re;
445   npl->s[npl->n].res = xstrdup(vec[1]);
446   npl->s[npl->n].replace = xstrdup(vec[2]);
447   npl->s[npl->n].context = xstrdup(vec[3]);
448   npl->s[npl->n].reflags = reflags;
449   ++npl->n;
450   /* XXX a bit of a bodge; relies on there being very few parts. */
451   for(n = 0; (n < cs->config->nparts
452               && strcmp(cs->config->parts[n], vec[0])); ++n)
453     ;
454   if(n >= cs->config->nparts) {
455     cs->config->parts = xrealloc(cs->config->parts,
456                                  (cs->config->nparts + 1) * sizeof (char *));
457     cs->config->parts[cs->config->nparts++] = xstrdup(vec[0]);
458   }
459   return 0;
460 }
461
462 static int set_transform(const struct config_state *cs,
463                          const struct conf *whoami,
464                          int nvec, char **vec) {
465   struct transformlist *tl = ADDRESS(cs->config, struct transformlist);
466   pcre *re;
467   unsigned reflags;
468   const char *errstr;
469   int erroffset;
470
471   if(nvec < 3) {
472     disorder_error(0, "%s:%d: transform needs at least 3 arguments",
473                    cs->path, cs->line);
474     return -1;
475   }
476   if(nvec > 5) {
477     disorder_error(0, "%s:%d: transform needs at most 5 arguments",
478                    cs->path, cs->line);
479     return -1;
480   }
481   reflags = (nvec >= 5 ? regsub_flags(vec[4]) : 0);
482   if(!(re = pcre_compile(vec[1],
483                          PCRE_UTF8
484                          |regsub_compile_options(reflags),
485                          &errstr, &erroffset, 0))) {
486     disorder_error(0, "%s:%d: compiling regexp /%s/: %s (offset %d)",
487                    cs->path, cs->line, vec[1], errstr, erroffset);
488     return -1;
489   }
490   tl->t = xrealloc(tl->t, (tl->n + 1) * sizeof (struct namepart));
491   tl->t[tl->n].type = xstrdup(vec[0]);
492   tl->t[tl->n].context = xstrdup(vec[3] ? vec[3] : "*");
493   tl->t[tl->n].re = re;
494   tl->t[tl->n].replace = xstrdup(vec[2]);
495   tl->t[tl->n].flags = reflags;
496   ++tl->n;
497   return 0;
498 }
499 #endif
500
501 static int set_rights(const struct config_state *cs,
502                       const struct conf *whoami,
503                       int nvec, char **vec) {
504   if(nvec != 1) {
505     disorder_error(0, "%s:%d: '%s' requires one argument",
506                    cs->path, cs->line, whoami->name);
507     return -1;
508   }
509   if(parse_rights(vec[0], 0, 1)) {
510     disorder_error(0, "%s:%d: invalid rights string '%s'",
511                    cs->path, cs->line, vec[0]);
512     return -1;
513   }
514   return set_string(cs, whoami, nvec, vec);
515 }
516
517 static int set_netaddress(const struct config_state *cs,
518                           const struct conf *whoami,
519                           int nvec, char **vec) {
520   struct netaddress *na = ADDRESS(cs->config, struct netaddress);
521
522   if(netaddress_parse(na, nvec, vec)) {
523     disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
524     return -1;
525   }
526   return 0;
527 }
528
529 /* free functions */
530
531 static void free_none(struct config attribute((unused)) *c,
532                       const struct conf attribute((unused)) *whoami) {
533 }
534
535 static void free_string(struct config *c,
536                         const struct conf *whoami) {
537   xfree(VALUE(c, char *));
538   VALUE(c, char *) = 0;
539 }
540
541 static void free_stringlist(struct config *c,
542                             const struct conf *whoami) {
543   int n;
544   struct stringlist *sl = ADDRESS(c, struct stringlist);
545
546   for(n = 0; n < sl->n; ++n)
547     xfree(sl->s[n]);
548   xfree(sl->s);
549 }
550
551 static void free_stringlistlist(struct config *c,
552                                 const struct conf *whoami) {
553   int n, m;
554   struct stringlistlist *sll = ADDRESS(c, struct stringlistlist);
555   struct stringlist *sl;
556
557   for(n = 0; n < sll->n; ++n) {
558     sl = &sll->s[n];
559     for(m = 0; m < sl->n; ++m)
560       xfree(sl->s[m]);
561     xfree(sl->s);
562   }
563   xfree(sll->s);
564 }
565
566 static void free_collectionlist(struct config *c,
567                                 const struct conf *whoami) {
568   struct collectionlist *cll = ADDRESS(c, struct collectionlist);
569   struct collection *cl;
570   int n;
571
572   for(n = 0; n < cll->n; ++n) {
573     cl = &cll->s[n];
574     xfree(cl->module);
575     xfree(cl->encoding);
576     xfree(cl->root);
577   }
578   xfree(cll->s);
579 }
580
581 #if HAVE_PCRE_H
582 static void free_namepartlist(struct config *c,
583                               const struct conf *whoami) {
584   struct namepartlist *npl = ADDRESS(c, struct namepartlist);
585   struct namepart *np;
586   int n;
587
588   for(n = 0; n < npl->n; ++n) {
589     np = &npl->s[n];
590     xfree(np->part);
591     pcre_free(np->re);                  /* ...whatever pcre_free is set to. */
592     xfree(np->res);
593     xfree(np->replace);
594     xfree(np->context);
595   }
596   xfree(npl->s);
597 }
598
599 static void free_transformlist(struct config *c,
600                                const struct conf *whoami) {
601   struct transformlist *tl = ADDRESS(c, struct transformlist);
602   struct transform *t;
603   int n;
604
605   for(n = 0; n < tl->n; ++n) {
606     t = &tl->t[n];
607     xfree(t->type);
608     pcre_free(t->re);                   /* ...whatever pcre_free is set to. */
609     xfree(t->replace);
610     xfree(t->context);
611   }
612   xfree(tl->t);
613 }
614 #endif
615
616 static void free_netaddress(struct config *c,
617                             const struct conf *whoami) {
618   struct netaddress *na = ADDRESS(c, struct netaddress);
619
620   xfree(na->address);
621 }
622
623 /* configuration types */
624
625 static const struct conftype
626   type_signal = { set_signal, free_none },
627   type_collections = { set_collections, free_collectionlist },
628   type_boolean = { set_boolean, free_none },
629   type_string = { set_string, free_string },
630   type_stringlist = { set_stringlist, free_stringlist },
631   type_integer = { set_integer, free_none },
632   type_stringlist_accum = { set_stringlist_accum, free_stringlistlist },
633   type_string_accum = { set_string_accum, free_stringlist },
634   type_sample_format = { set_sample_format, free_none },
635   type_namepart = { set_namepart, free_namepartlist },
636   type_transform = { set_transform, free_transformlist },
637   type_netaddress = { set_netaddress, free_netaddress },
638   type_rights = { set_rights, free_string };
639
640 /* specific validation routine */
641
642 /** @brief Perform a test on a filename
643  * @param test Test function to call on mode bits
644  * @param what Type of file sought
645  *
646  * If @p test returns 0 then the file is not a @p what and an error
647  * is reported and -1 is returned.
648  */
649 #define VALIDATE_FILE(test, what) do {                  \
650   struct stat sb;                                       \
651   int n;                                                \
652                                                         \
653   for(n = 0; n < nvec; ++n) {                           \
654     if(stat(vec[n], &sb) < 0) {                         \
655       disorder_error(errno, "%s:%d: %s",                \
656                      cs->path, cs->line, vec[n]);       \
657       return -1;                                        \
658     }                                                   \
659     if(!test(sb.st_mode)) {                             \
660       disorder_error(0, "%s:%d: %s is not a %s",        \
661                      cs->path, cs->line, vec[n], what); \
662       return -1;                                        \
663     }                                                   \
664   }                                                     \
665 } while(0)
666
667 /** @brief Validate an absolute path
668  * @param cs Configuration state
669  * @param nvec Length of (proposed) new value
670  * @param vec Elements of new value
671  * @return 0 on success, non-0 on error
672  */
673 static int validate_isabspath(const struct config_state *cs,
674                               int nvec, char **vec) {
675   int n;
676
677   for(n = 0; n < nvec; ++n)
678     if(vec[n][0] != '/') {
679       disorder_error(errno, "%s:%d: %s: not an absolute path", 
680                      cs->path, cs->line, vec[n]);
681       return -1;
682     }
683   return 0;
684 }
685
686 /** @brief Validate an existing directory
687  * @param cs Configuration state
688  * @param nvec Length of (proposed) new value
689  * @param vec Elements of new value
690  * @return 0 on success, non-0 on error
691  */
692 static int validate_isdir(const struct config_state *cs,
693                           int nvec, char **vec) {
694   VALIDATE_FILE(S_ISDIR, "directory");
695   return 0;
696 }
697
698 /** @brief Validate an existing regular file
699  * @param cs Configuration state
700  * @param nvec Length of (proposed) new value
701  * @param vec Elements of new value
702  * @return 0 on success, non-0 on error
703  */
704 static int validate_isreg(const struct config_state *cs,
705                           int nvec, char **vec) {
706   VALIDATE_FILE(S_ISREG, "regular file");
707   return 0;
708 }
709
710 /** @brief Validate a player pattern
711  * @param cs Configuration state
712  * @param nvec Length of (proposed) new value
713  * @param vec Elements of new value
714  * @return 0 on success, non-0 on error
715  */
716 static int validate_player(const struct config_state *cs,
717                            int nvec,
718                            char attribute((unused)) **vec) {
719   if(nvec && nvec < 2) {
720     disorder_error(0, "%s:%d: should be at least 'player PATTERN MODULE'",
721                    cs->path, cs->line);
722     return -1;
723   }
724   return 0;
725 }
726
727 /** @brief Validate a track length pattern
728  * @param cs Configuration state
729  * @param nvec Length of (proposed) new value
730  * @param vec Elements of new value
731  * @return 0 on success, non-0 on error
732  */
733 static int validate_tracklength(const struct config_state *cs,
734                                 int nvec,
735                                 char attribute((unused)) **vec) {
736   if(nvec && nvec < 2) {
737     disorder_error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
738                    cs->path, cs->line);
739     return -1;
740   }
741   return 0;
742 }
743
744 /** @brief Validate a non-negative (@c long) integer
745  * @param cs Configuration state
746  * @param nvec Length of (proposed) new value
747  * @param vec Elements of new value
748  * @return 0 on success, non-0 on error
749  */
750 static int validate_non_negative(const struct config_state *cs,
751                                  int nvec, char **vec) {
752   long n;
753
754   if(nvec < 1) {
755     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
756     return -1;
757   }
758   if(nvec > 1) {
759     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
760     return -1;
761   }
762   if(xstrtol(&n, vec[0], 0, 0)) {
763     disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
764     return -1;
765   }
766   if(n < 0) {
767     disorder_error(0, "%s:%d: must not be negative", cs->path, cs->line);
768     return -1;
769   }
770   return 0;
771 }
772
773 /** @brief Validate a positive (@c long) integer
774  * @param cs Configuration state
775  * @param nvec Length of (proposed) new value
776  * @param vec Elements of new value
777  * @return 0 on success, non-0 on error
778  */
779 static int validate_positive(const struct config_state *cs,
780                           int nvec, char **vec) {
781   long n;
782
783   if(nvec < 1) {
784     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
785     return -1;
786   }
787   if(nvec > 1) {
788     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
789     return -1;
790   }
791   if(xstrtol(&n, vec[0], 0, 0)) {
792     disorder_error(0, "%s:%d: %s", cs->path, cs->line, strerror(errno));
793     return -1;
794   }
795   if(n <= 0) {
796     disorder_error(0, "%s:%d: must be positive", cs->path, cs->line);
797     return -1;
798   }
799   return 0;
800 }
801
802 /** @brief Validate a system username
803  * @param cs Configuration state
804  * @param nvec Length of (proposed) new value
805  * @param vec Elements of new value
806  * @return 0 on success, non-0 on error
807  */
808 static int validate_isauser(const struct config_state *cs,
809                             int attribute((unused)) nvec,
810                             char **vec) {
811   if(!getpwnam(vec[0])) {
812     disorder_error(0, "%s:%d: no such user as '%s'", cs->path, cs->line, vec[0]);
813     return -1;
814   }
815   return 0;
816 }
817
818 /** @brief Validate a sample format string
819  * @param cs Configuration state
820  * @param nvec Length of (proposed) new value
821  * @param vec Elements of new value
822  * @return 0 on success, non-0 on error
823  */
824 static int validate_sample_format(const struct config_state *cs,
825                                   int attribute((unused)) nvec,
826                                   char **vec) {
827   return parse_sample_format(cs, 0, nvec, vec);
828 }
829
830 /** @brief Validate anything
831  * @param cs Configuration state
832  * @param nvec Length of (proposed) new value
833  * @param vec Elements of new value
834  * @return 0
835  */
836 static int validate_any(const struct config_state attribute((unused)) *cs,
837                         int attribute((unused)) nvec,
838                         char attribute((unused)) **vec) {
839   return 0;
840 }
841
842 /** @brief Validate a URL
843  * @param cs Configuration state
844  * @param nvec Length of (proposed) new value
845  * @param vec Elements of new value
846  * @return 0 on success, non-0 on error
847  *
848  * Rather cursory.
849  */
850 static int validate_url(const struct config_state attribute((unused)) *cs,
851                         int attribute((unused)) nvec,
852                         char **vec) {
853   const char *s;
854   int n;
855   /* absoluteURI   = scheme ":" ( hier_part | opaque_part )
856      scheme        = alpha *( alpha | digit | "+" | "-" | "." ) */
857   s = vec[0];
858   n = strspn(s, ("abcdefghijklmnopqrstuvwxyz"
859                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
860                  "0123456789"));
861   if(s[n] != ':') {
862     disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
863     return -1;
864   }
865   if(!strncmp(s, "http:", 5)
866      || !strncmp(s, "https:", 6)) {
867     s += n + 1;
868     /* we only do a rather cursory check */
869     if(strncmp(s, "//", 2)) {
870       disorder_error(0, "%s:%d: invalid url '%s'", cs->path, cs->line, vec[0]);
871       return -1;
872     }
873   }
874   return 0;
875 }
876
877 /** @brief Validate an alias pattern
878  * @param cs Configuration state
879  * @param nvec Length of (proposed) new value
880  * @param vec Elements of new value
881  * @return 0 on success, non-0 on error
882  */
883 static int validate_alias(const struct config_state *cs,
884                           int nvec,
885                           char **vec) {
886   const char *s;
887   int in_brackets = 0, c;
888
889   if(nvec < 1) {
890     disorder_error(0, "%s:%d: missing argument", cs->path, cs->line);
891     return -1;
892   }
893   if(nvec > 1) {
894     disorder_error(0, "%s:%d: too many arguments", cs->path, cs->line);
895     return -1;
896   }
897   s = vec[0];
898   while((c = (unsigned char)*s++)) {
899     if(in_brackets) {
900       if(c == '}')
901         in_brackets = 0;
902       else if(!isalnum(c)) {
903         disorder_error(0, "%s:%d: invalid part name in alias expansion in '%s'",
904                        cs->path, cs->line, vec[0]);
905           return -1;
906       }
907     } else {
908       if(c == '{') {
909         in_brackets = 1;
910         if(*s == '/')
911           ++s;
912       } else if(c == '\\') {
913         if(!(c = (unsigned char)*s++)) {
914           disorder_error(0, "%s:%d: unterminated escape in alias expansion in '%s'",
915                          cs->path, cs->line, vec[0]);
916           return -1;
917         } else if(c != '\\' && c != '{') {
918           disorder_error(0, "%s:%d: invalid escape in alias expansion in '%s'",
919                          cs->path, cs->line, vec[0]);
920           return -1;
921         }
922       }
923     }
924     ++s;
925   }
926   if(in_brackets) {
927     disorder_error(0,
928                    "%s:%d: unterminated part name in alias expansion in '%s'",
929                    cs->path, cs->line, vec[0]);
930     return -1;
931   }
932   return 0;
933 }
934
935 /** @brief Validate a hash algorithm name
936  * @param cs Configuration state
937  * @param nvec Length of (proposed) new value
938  * @param vec Elements of new value
939  * @return 0 on success, non-0 on error
940  */
941 static int validate_algo(const struct config_state attribute((unused)) *cs,
942                          int nvec,
943                          char **vec) {
944   if(nvec != 1) {
945     disorder_error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
946     return -1;
947   }
948   if(!valid_authhash(vec[0])) {
949     disorder_error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
950     return -1;
951   }
952   return 0;
953 }
954
955 /** @brief Validate a playback backend name
956  * @param cs Configuration state
957  * @param nvec Length of (proposed) new value
958  * @param vec Elements of new value
959  * @return 0 on success, non-0 on error
960  */
961 static int validate_backend(const struct config_state attribute((unused)) *cs,
962                             int nvec,
963                             char **vec) {
964   int n;
965   if(nvec != 1) {
966     disorder_error(0, "%s:%d: invalid sound API specification", cs->path, cs->line);
967     return -1;
968   }
969   if(!strcmp(vec[0], "network")) {
970     disorder_error(0, "'api network' is deprecated; use 'api rtp'");
971     return 0;
972   }
973   if(config_uaudio_apis) {
974     for(n = 0; config_uaudio_apis[n]; ++n)
975       if(!strcmp(vec[0], config_uaudio_apis[n]->name))
976         return 0;
977     disorder_error(0, "%s:%d: unrecognized sound API '%s'", cs->path, cs->line, vec[0]);
978     return -1;
979   }
980   /* In non-server processes we have no idea what's valid */
981   return 0;
982 }
983
984 /** @brief Validate a pause mode string
985  * @param cs Configuration state
986  * @param nvec Length of (proposed) new value
987  * @param vec Elements of new value
988  * @return 0 on success, non-0 on error
989  */
990 static int validate_pausemode(const struct config_state attribute((unused)) *cs,
991                               int nvec,
992                               char **vec) {
993   if(nvec == 1 && (!strcmp(vec[0], "silence") || !strcmp(vec[0], "suspend")))
994     return 0;
995   disorder_error(0, "%s:%d: invalid pause mode", cs->path, cs->line);
996   return -1;
997 }
998
999 /** @brief Validate a destination network address
1000  * @param cs Configuration state
1001  * @param nvec Length of (proposed) new value
1002  * @param vec Elements of new value
1003  * @return 0 on success, non-0 on error
1004  *
1005  * By a destination address, it is meant that it must not be a wildcard
1006  * address.
1007  */
1008 static int validate_destaddr(const struct config_state attribute((unused)) *cs,
1009                              int nvec,
1010                              char **vec) {
1011   struct netaddress na[1];
1012
1013   if(netaddress_parse(na, nvec, vec)) {
1014     disorder_error(0, "%s:%d: invalid network address", cs->path, cs->line);
1015     return -1;
1016   }
1017   if(!na->address) {
1018     disorder_error(0, "%s:%d: destination address required", cs->path, cs->line);
1019     return -1;
1020   }
1021   xfree(na->address);
1022   return 0;
1023 }
1024
1025 /** @brief Item name and and offset */
1026 #define C(x) #x, offsetof(struct config, x)
1027 /** @brief Item name and and offset */
1028 #define C2(x,y) #x, offsetof(struct config, y)
1029
1030 /** @brief All configuration items */
1031 static const struct conf conf[] = {
1032   { C(alias),            &type_string,           validate_alias },
1033   { C(api),              &type_string,           validate_backend },
1034   { C(authorization_algorithm), &type_string,    validate_algo },
1035   { C(broadcast),        &type_netaddress,       validate_destaddr },
1036   { C(broadcast_from),   &type_netaddress,       validate_any },
1037   { C(channel),          &type_string,           validate_any },
1038   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
1039   { C(checkpoint_min),   &type_integer,          validate_non_negative },
1040   { C(collection),       &type_collections,      validate_any },
1041   { C(connect),          &type_netaddress,       validate_destaddr },
1042   { C(cookie_key_lifetime),  &type_integer,      validate_positive },
1043   { C(cookie_login_lifetime),  &type_integer,    validate_positive },
1044   { C(dbversion),        &type_integer,          validate_positive },
1045   { C(default_rights),   &type_rights,           validate_any },
1046   { C(device),           &type_string,           validate_any },
1047   { C(history),          &type_integer,          validate_positive },
1048   { C(home),             &type_string,           validate_isabspath },
1049   { C(listen),           &type_netaddress,       validate_any },
1050   { C(mail_sender),      &type_string,           validate_any },
1051   { C(mixer),            &type_string,           validate_any },
1052   { C(mount_rescan),     &type_boolean,          validate_any },
1053   { C(multicast_loop),   &type_boolean,          validate_any },
1054   { C(multicast_ttl),    &type_integer,          validate_non_negative },
1055 #if HAVE_PCRE_H
1056   { C(namepart),         &type_namepart,         validate_any },
1057 #endif
1058   { C(new_bias),         &type_integer,          validate_positive },
1059   { C(new_bias_age),     &type_integer,          validate_positive },
1060   { C(new_max),          &type_integer,          validate_positive },
1061   { C2(nice, nice_rescan), &type_integer,        validate_non_negative },
1062   { C(nice_rescan),      &type_integer,          validate_non_negative },
1063   { C(nice_server),      &type_integer,          validate_any },
1064   { C(nice_speaker),     &type_integer,          validate_any },
1065   { C(noticed_history),  &type_integer,          validate_positive },
1066   { C(password),         &type_string,           validate_any },
1067   { C(pause_mode),       &type_string,           validate_pausemode },
1068   { C(player),           &type_stringlist_accum, validate_player },
1069   { C(playlist_lock_timeout), &type_integer,     validate_positive },
1070   { C(playlist_max) ,    &type_integer,          validate_positive },
1071   { C(plugins),          &type_string_accum,     validate_isdir },
1072   { C(queue_pad),        &type_integer,          validate_positive },
1073   { C(refresh),          &type_integer,          validate_positive },
1074   { C(refresh_min),      &type_integer,          validate_non_negative },
1075   { C(reminder_interval), &type_integer,         validate_positive },
1076   { C(remote_userman),   &type_boolean,          validate_any },
1077   { C(replay_min),       &type_integer,          validate_non_negative },
1078   { C(rtp_delay_threshold), &type_integer,       validate_positive },
1079   { C(rtp_mode),         &type_string,           validate_any },
1080   { C(rtp_verbose),      &type_boolean,          validate_any },
1081   { C(sample_format),    &type_sample_format,    validate_sample_format },
1082   { C(scratch),          &type_string_accum,     validate_isreg },
1083   { C(sendmail),         &type_string,           validate_isabspath },
1084   { C(short_display),    &type_integer,          validate_positive },
1085   { C(signal),           &type_signal,           validate_any },
1086   { C(smtp_server),      &type_string,           validate_any },
1087   { C(sox_generation),   &type_integer,          validate_non_negative },
1088   { C2(speaker_backend, api),  &type_string,     validate_backend },
1089   { C(speaker_command),  &type_string,           validate_any },
1090   { C(stopword),         &type_string_accum,     validate_any },
1091   { C(templates),        &type_string_accum,     validate_isdir },
1092   { C(tracklength),      &type_stringlist_accum, validate_tracklength },
1093 #if HAVE_PCRE_H
1094   { C(transform),        &type_transform,        validate_any },
1095 #endif
1096   { C(url),              &type_string,           validate_url },
1097   { C(user),             &type_string,           validate_isauser },
1098   { C(username),         &type_string,           validate_any },
1099 };
1100
1101 /** @brief Find a configuration item's definition by key */
1102 static const struct conf *find(const char *key) {
1103   int n;
1104
1105   if((n = TABLE_FIND(conf, name, key)) < 0)
1106     return 0;
1107   return &conf[n];
1108 }
1109
1110 /** @brief Set a new configuration value
1111  * @param cs Configuration state
1112  * @param nvec Length of @p vec
1113  * @param vec Name and new value
1114  * @return 0 on success, non-0 on error.
1115  *
1116  * @c vec[0] is the name, the rest is the value.
1117  */
1118 static int config_set(const struct config_state *cs,
1119                       int nvec, char **vec) {
1120   const struct conf *which;
1121
1122   D(("config_set %s", vec[0]));
1123   if(!(which = find(vec[0]))) {
1124     disorder_error(0, "%s:%d: unknown configuration key '%s'",
1125           cs->path, cs->line, vec[0]);
1126     return -1;
1127   }
1128   return (which->validate(cs, nvec - 1, vec + 1)
1129           || which->type->set(cs, which, nvec - 1, vec + 1));
1130 }
1131
1132 /** @brief Set a configuration item from parameters
1133  * @param cs Configuration state
1134  * @param which Item to set
1135  * @param ... Value as strings, terminated by (char *)NULL
1136  * @return 0 on success, non-0 on error
1137  */
1138 static int config_set_args(const struct config_state *cs,
1139                            const char *which, ...) {
1140   va_list ap;
1141   struct vector v[1];
1142   char *s;
1143
1144   vector_init(v);
1145   vector_append(v, (char *)which);
1146   va_start(ap, which);
1147   while((s = va_arg(ap, char *)))
1148     vector_append(v, s);
1149   va_end(ap);
1150   vector_terminate(v);
1151   int rc = config_set(cs, v->nvec, v->vec);
1152   xfree(v->vec);
1153   return rc;
1154 }
1155
1156 /** @brief Error callback used by config_include()
1157  * @param msg Error message
1158  * @param u User data (@ref config_state)
1159  */
1160 static void config_error(const char *msg, void *u) {
1161   const struct config_state *cs = u;
1162
1163   disorder_error(0, "%s:%d: %s", cs->path, cs->line, msg);
1164 }
1165
1166 /** @brief Include a file by name
1167  * @param c Configuration to update
1168  * @param path Path to read
1169  * @return 0 on success, non-0 on error
1170  */
1171 static int config_include(struct config *c, const char *path) {
1172   FILE *fp;
1173   char *buffer, *inputbuffer, **vec;
1174   int n, ret = 0;
1175   struct config_state cs;
1176
1177   cs.path = path;
1178   cs.line = 0;
1179   cs.config = c;
1180   D(("%s: reading configuration", path));
1181   if(!(fp = fopen(path, "r"))) {
1182     disorder_error(errno, "error opening %s", path);
1183     return -1;
1184   }
1185   while(!inputline(path, fp, &inputbuffer, '\n')) {
1186     ++cs.line;
1187     if(!(buffer = mb2utf8(inputbuffer))) {
1188       disorder_error(errno, "%s:%d: cannot convert to UTF-8", cs.path, cs.line);
1189       ret = -1;
1190       xfree(inputbuffer);
1191       continue;
1192     }
1193     xfree(inputbuffer);
1194     if(!(vec = split(buffer, &n, SPLIT_COMMENTS|SPLIT_QUOTES,
1195                      config_error, &cs))) {
1196       ret = -1;
1197       xfree(buffer);
1198       continue;
1199     }
1200     if(n) {
1201       /* 'include' is special-cased */
1202       if(!strcmp(vec[0], "include")) {
1203         if(n != 2) {
1204           disorder_error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
1205           ret = -1;
1206         } else
1207           config_include(c, vec[1]);
1208       } else
1209         ret |= config_set(&cs, n, vec);
1210     }
1211     for(n = 0; vec[n]; ++n) xfree(vec[n]);
1212     xfree(vec);
1213     xfree(buffer);
1214   }
1215   if(ferror(fp)) {
1216     disorder_error(errno, "error reading %s", path);
1217     ret = -1;
1218   }
1219   fclose(fp);
1220   return ret;
1221 }
1222
1223 /** @brief Default stopword setting */
1224 static const char *const default_stopwords[] = {
1225   "stopword",
1226
1227   "01",
1228   "02",
1229   "03",
1230   "04",
1231   "05",
1232   "06",
1233   "07",
1234   "08",
1235   "09",
1236   "1",
1237   "10",
1238   "11",
1239   "12",
1240   "13",
1241   "14",
1242   "15",
1243   "16",
1244   "17",
1245   "18",
1246   "19",
1247   "2",
1248   "20",
1249   "21",
1250   "22",
1251   "23",
1252   "24",
1253   "25",
1254   "26",
1255   "27",
1256   "28",
1257   "29",
1258   "3",
1259   "30",
1260   "4",
1261   "5",
1262   "6",
1263   "7",
1264   "8",
1265   "9",
1266   "a",
1267   "am",
1268   "an",
1269   "and",
1270   "as",
1271   "for",
1272   "i",
1273   "im",
1274   "in",
1275   "is",
1276   "of",
1277   "on",
1278   "the",
1279   "to",
1280   "too",
1281   "we",
1282 };
1283 #define NDEFAULT_STOPWORDS (sizeof default_stopwords / sizeof *default_stopwords)
1284
1285 /** @brief Default player patterns */
1286 static const char *const default_players[] = {
1287   "*.ogg",
1288   "*.flac",
1289   "*.mp3",
1290   "*.wav",
1291 };
1292 #define NDEFAULT_PLAYERS (sizeof default_players / sizeof *default_players)
1293
1294 /** @brief Make a new default configuration
1295  * @return New configuration
1296  */
1297 static struct config *config_default(void) {
1298   struct config *c = xmalloc(sizeof *c);
1299   const char *logname;
1300   struct passwd *pw;
1301   struct config_state cs;
1302   size_t n;
1303
1304   cs.path = "<internal>";
1305   cs.line = 0;
1306   cs.config = c;
1307   /* Strings had better be xstrdup'd as they will get freed at some point. */
1308   c->history = 60;
1309   c->home = xstrdup(pkgstatedir);
1310   if(!(pw = getpwuid(getuid())))
1311     disorder_fatal(0, "cannot determine our username");
1312   logname = pw->pw_name;
1313   c->username = xstrdup(logname);
1314   c->refresh = 15;
1315   c->refresh_min = 1;
1316   c->signal = SIGKILL;
1317   c->alias = xstrdup("{/artist}{/album}{/title}{ext}");
1318   c->device = xstrdup("default");
1319   c->nice_rescan = 10;
1320   c->speaker_command = 0;
1321   c->sample_format.bits = 16;
1322   c->sample_format.rate = 44100;
1323   c->sample_format.channels = 2;
1324   c->sample_format.endian = ENDIAN_NATIVE;
1325   c->queue_pad = 10;
1326   c->replay_min = 8 * 3600;
1327   c->api = NULL;
1328   c->multicast_ttl = 1;
1329   c->multicast_loop = 1;
1330   c->authorization_algorithm = xstrdup("sha1");
1331   c->noticed_history = 31;
1332   c->short_display = 32;
1333   c->mixer = 0;
1334   c->channel = 0;
1335   c->dbversion = 2;
1336   c->cookie_login_lifetime = 86400;
1337   c->cookie_key_lifetime = 86400 * 7;
1338   if(sendmail_binary[0] && strcmp(sendmail_binary, "none"))
1339     c->sendmail = xstrdup(sendmail_binary);
1340   c->smtp_server = xstrdup("127.0.0.1");
1341   c->new_max = 100;
1342   c->reminder_interval = 600;           /* 10m */
1343   c->new_bias_age = 7 * 86400;          /* 1 week */
1344   c->new_bias = 4500000;                /* 50 times the base weight */
1345   c->sox_generation = DEFAULT_SOX_GENERATION;
1346   c->playlist_max = INT_MAX;            /* effectively no limit */
1347   c->playlist_lock_timeout = 10;        /* 10s */
1348   c->mount_rescan = 1;
1349   /* Default stopwords */
1350   if(config_set(&cs, (int)NDEFAULT_STOPWORDS, (char **)default_stopwords))
1351     exit(1);
1352   /* Default player configuration */
1353   for(n = 0; n < NDEFAULT_PLAYERS; ++n) {
1354     if(config_set_args(&cs, "player",
1355                        default_players[n], "execraw", "disorder-decode", (char *)0))
1356       exit(1);
1357     if(config_set_args(&cs, "tracklength",
1358                        default_players[n], "disorder-tracklength", (char *)0))
1359       exit(1);
1360   }
1361   c->broadcast.af = -1;
1362   c->broadcast_from.af = -1;
1363   c->listen.af = -1;
1364   c->connect.af = -1;
1365   c->rtp_mode = xstrdup("auto");
1366   return c;
1367 }
1368
1369 /** @brief Construct a filename
1370  * @param c Configuration
1371  * @param name Base filename
1372  * @return Full filename
1373  *
1374  * Usually use config_get_file() instead.
1375  */
1376 char *config_get_file2(struct config *c, const char *name) {
1377   char *s;
1378
1379   byte_xasprintf(&s, "%s/%s", c->home, name);
1380   return s;
1381 }
1382
1383 /** @brief Set the default configuration file */
1384 static void set_configfile(void) {
1385   if(!configfile)
1386     byte_xasprintf(&configfile, "%s/config", pkgconfdir);
1387 }
1388
1389 /** @brief Free a configuration object
1390  * @param c Configuration to free
1391  *
1392  * @p c is indeterminate after this function is called.
1393  */
1394 void config_free(struct config *c) {
1395   int n;
1396
1397   if(c) {
1398     for(n = 0; n < (int)(sizeof conf / sizeof *conf); ++n)
1399       conf[n].type->free(c, &conf[n]);
1400     for(n = 0; n < c->nparts; ++n)
1401       xfree(c->parts[n]);
1402     xfree(c->parts);
1403     xfree(c);
1404   }
1405 }
1406
1407 /** @brief Set post-parse defaults
1408  * @param c Configuration to update
1409  * @param server True when running in the server
1410  *
1411  * If @p server is set then certain parts of the configuration are more
1412  * strictly validated.
1413  */
1414 static void config_postdefaults(struct config *c,
1415                                 int server) {
1416   struct config_state cs;
1417 #if HAVE_PCRE_H
1418   const struct conf *whoami;
1419   int n;
1420 #endif
1421
1422 #if HAVE_PCRE_H
1423   static const char *namepart[][4] = {
1424     { "title",  "/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
1425     { "title",  "/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort" },
1426     { "album",  "/([^/]+)/[^/]+$",                    "$1", "*" },
1427     { "artist", "/([^/]+)/[^/]+/[^/]+$",              "$1", "*" },
1428     { "ext",    "(\\.[a-zA-Z0-9]+)$",                 "$1", "*" },
1429   };
1430 #define NNAMEPART (int)(sizeof namepart / sizeof *namepart)
1431
1432   static const char *transform[][5] = {
1433     { "track", "^.*/([0-9]+ *[-:]? *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display", "" },
1434     { "track", "^.*/([^/]+)\\.[a-zA-Z0-9]+$",           "$1", "sort", "" },
1435     { "dir",   "^.*/([^/]+)$",                          "$1", "*", "" },
1436     { "dir",   "^(the) ([^/]*)",                        "$2, $1", "sort", "i", },
1437     { "dir",   "[[:punct:]]",                           "", "sort", "g", }
1438   };
1439 #define NTRANSFORM (int)(sizeof transform / sizeof *transform)
1440 #endif
1441
1442   cs.path = "<internal>";
1443   cs.line = 0;
1444   cs.config = c;
1445 #if HAVE_PCRE_H
1446   if(!c->namepart.n) {
1447     whoami = find("namepart");
1448     for(n = 0; n < NNAMEPART; ++n)
1449       set_namepart(&cs, whoami, 4, (char **)namepart[n]);
1450   }
1451   if(!c->transform.n) {
1452     whoami = find("transform");
1453     for(n = 0; n < NTRANSFORM; ++n)
1454       set_transform(&cs, whoami, 5, (char **)transform[n]);
1455   }
1456 #endif
1457   if(!c->api) {
1458     if(c->speaker_command)
1459       c->api = xstrdup("command");
1460     else if(c->broadcast.af != -1)
1461       c->api = xstrdup("rtp");
1462     else if(config_uaudio_apis)
1463       c->api = xstrdup(uaudio_default(config_uaudio_apis,
1464                                       UAUDIO_API_SERVER)->name);
1465     else
1466       c->api = xstrdup("<none>");
1467   }
1468   if(!strcmp(c->api, "network"))
1469     c->api = xstrdup("rtp");
1470   if(server) {
1471     if(!strcmp(c->api, "command") && !c->speaker_command)
1472       disorder_fatal(0, "'api command' but speaker_command is not set");
1473     if((!strcmp(c->api, "rtp")) && c->broadcast.af == -1)
1474       disorder_fatal(0, "'api rtp' but broadcast is not set");
1475   }
1476   /* Override sample format */
1477   if(!strcmp(c->api, "rtp")) {
1478     c->sample_format.rate = 44100;
1479     c->sample_format.channels = 2;
1480     c->sample_format.bits = 16;
1481     c->sample_format.endian = ENDIAN_NATIVE;
1482   }
1483   if(!strcmp(c->api, "coreaudio")) {
1484     c->sample_format.rate = 44100;
1485     c->sample_format.channels = 2;
1486     c->sample_format.bits = 16;
1487     c->sample_format.endian = ENDIAN_NATIVE;
1488   }
1489   if(!c->default_rights) {
1490     rights_type r = RIGHTS__MASK & ~(RIGHT_ADMIN|RIGHT_REGISTER
1491                                      |RIGHT_MOVE__MASK
1492                                      |RIGHT_SCRATCH__MASK
1493                                      |RIGHT_REMOVE__MASK);
1494     r |= RIGHT_SCRATCH_ANY|RIGHT_MOVE_ANY|RIGHT_REMOVE_ANY;
1495     c->default_rights = rights_string(r);
1496   }
1497 }
1498
1499 /** @brief (Re-)read the config file
1500  * @param server If set, do extra checking
1501  * @param oldconfig Old configuration for compatibility check
1502  * @return 0 on success, non-0 on error
1503  *
1504  * If @p oldconfig is set, then certain compatibility checks are done between
1505  * the old and new configurations.
1506  */
1507 int config_read(int server,
1508                 const struct config *oldconfig) {
1509   struct config *c;
1510   char *privconf;
1511   struct passwd *pw;
1512
1513   set_configfile();
1514   c = config_default();
1515   /* standalone Disobedience installs might not have a global config file */
1516   if(access(configfile, F_OK) == 0)
1517     if(config_include(c, configfile))
1518       return -1;
1519   /* if we can read the private config file, do */
1520   if((privconf = config_private())
1521      && access(privconf, R_OK) == 0
1522      && config_include(c, privconf))
1523     return -1;
1524   xfree(privconf);
1525   /* if there's a per-user system config file for this user, read it */
1526   if(config_per_user) {
1527     if(!(pw = getpwuid(getuid())))
1528       disorder_fatal(0, "cannot determine our username");
1529     if((privconf = config_usersysconf(pw))
1530        && access(privconf, F_OK) == 0
1531        && config_include(c, privconf))
1532       return -1;
1533     xfree(privconf);
1534     /* if we have a password file, read it */
1535     if((privconf = config_userconf(0, pw))
1536        && access(privconf, F_OK) == 0
1537        && config_include(c, privconf))
1538       return -1;
1539     xfree(privconf);
1540   }
1541   /* install default namepart and transform settings */
1542   config_postdefaults(c, server);
1543   if(oldconfig)  {
1544     int failed = 0;
1545     if(strcmp(c->home, oldconfig->home)) {
1546       disorder_error(0, "'home' cannot be changed without a restart");
1547       failed = 1;
1548     }
1549     if(strcmp(c->alias, oldconfig->alias)) {
1550       disorder_error(0, "'alias' cannot be changed without a restart");
1551       failed = 1;
1552     }
1553     if(strcmp(c->user, oldconfig->user)) {
1554       disorder_error(0, "'user' cannot be changed without a restart");
1555       failed = 1;
1556     }
1557     if(c->nice_speaker != oldconfig->nice_speaker) {
1558       disorder_error(0, "'nice_speaker' cannot be changed without a restart");
1559       /* ...but we accept the new config anyway */
1560     }
1561     if(c->nice_server != oldconfig->nice_server) {
1562       disorder_error(0, "'nice_server' cannot be changed without a restart");
1563       /* ...but we accept the new config anyway */
1564     }
1565 #if HAVE_PCRE_H
1566     if(namepartlist_compare(&c->namepart, &oldconfig->namepart)) {
1567       disorder_error(0, "'namepart' settings cannot be changed without a restart");
1568       failed = 1;
1569     }
1570 #endif
1571     if(stringlist_compare(&c->stopword, &oldconfig->stopword)) {
1572       disorder_error(0, "'stopword' settings cannot be changed without a restart");
1573       failed = 1;
1574     }
1575     if(failed) {
1576       disorder_error(0, "not installing incompatible new configuration");
1577       return -1;
1578     }
1579   }
1580   /* everything is good so we shall use the new config */
1581   config_free(config);
1582   /* warn about obsolete directives */
1583   config = c;
1584   return 0;
1585 }
1586
1587 /** @brief Return the path to the private configuration file */
1588 char *config_private(void) {
1589   char *s;
1590
1591   set_configfile();
1592   byte_xasprintf(&s, "%s.private", configfile);
1593   return s;
1594 }
1595
1596 /** @brief Return the path to user's personal configuration file */
1597 char *config_userconf(const char *home, const struct passwd *pw) {
1598   char *s;
1599
1600   if(!home && !pw && !(pw = getpwuid(getuid())))
1601     disorder_fatal(0, "cannot determine our username");
1602   byte_xasprintf(&s, "%s/.disorder/passwd", home ? home : pw->pw_dir);
1603   return s;
1604 }
1605
1606 /** @brief Return the path to user-specific system configuration */
1607 char *config_usersysconf(const struct passwd *pw) {
1608   char *s;
1609
1610   set_configfile();
1611   if(!strchr(pw->pw_name, '/')) {
1612     byte_xasprintf(&s, "%s.%s", configfile, pw->pw_name);
1613     return s;
1614   } else
1615     return 0;
1616 }
1617
1618 /** @brief Get a filename within the home directory
1619  * @param name Relative name
1620  * @return Full path
1621  */
1622 char *config_get_file(const char *name) {
1623   return config_get_file2(config, name);
1624 }
1625
1626 /** @brief Order two stringlists
1627  * @param a First stringlist
1628  * @param b Second stringlist
1629  * @return <0, 0 or >0 if a<b, a=b or a>b
1630  */
1631 static int stringlist_compare(const struct stringlist *a,
1632                               const struct stringlist *b) {
1633   int n = 0, c;
1634
1635   while(n < a->n && n < b->n) {
1636     if((c = strcmp(a->s[n], b->s[n])))
1637       return c;
1638     ++n;
1639   }
1640   if(a->n < b->n)
1641     return -1;
1642   else if(a->n > b->n)
1643     return 1;
1644   else
1645     return 0;
1646 }
1647
1648 #if HAVE_PCRE_H
1649 /** @brief Order two namepart definitions
1650  * @param a First namepart definition
1651  * @param b Second namepart definition
1652  * @return <0, 0 or >0 if a<b, a=b or a>b
1653  */
1654 static int namepart_compare(const struct namepart *a,
1655                             const struct namepart *b) {
1656   int c;
1657
1658   if((c = strcmp(a->part, b->part)))
1659     return c;
1660   if((c = strcmp(a->res, b->res)))
1661     return c;
1662   if((c = strcmp(a->replace, b->replace)))
1663     return c;
1664   if((c = strcmp(a->context, b->context)))
1665     return c;
1666   if(a->reflags > b->reflags)
1667     return 1;
1668   if(a->reflags < b->reflags)
1669     return -1;
1670   return 0;
1671 }
1672
1673 /** @brief Order two lists of namepart definitions
1674  * @param a First list of namepart definitions
1675  * @param b Second list of namepart definitions
1676  * @return <0, 0 or >0 if a<b, a=b or a>b
1677  */
1678 static int namepartlist_compare(const struct namepartlist *a,
1679                                 const struct namepartlist *b) {
1680   int n = 0, c;
1681
1682   while(n < a->n && n < b->n) {
1683     if((c = namepart_compare(&a->s[n], &b->s[n])))
1684       return c;
1685     ++n;
1686   }
1687   if(a->n > b->n)
1688     return 1;
1689   else if(a->n < b->n)
1690     return -1;
1691   else
1692     return 0;
1693 }
1694 #endif
1695
1696 /** @brief Verify configuration table.
1697  * @return The number of problems found
1698 */
1699 int config_verify(void) {
1700   int fails = 0;
1701   for(size_t n = 1; n < sizeof conf / sizeof *conf; ++n)
1702     if(strcmp(conf[n-1].name, conf[n].name) >= 0) {
1703       fprintf(stderr, "%s >= %s\n", conf[n-1].name, conf[n].name);
1704       ++fails;
1705     }
1706   return fails;
1707 }
1708
1709 /*
1710 Local Variables:
1711 c-basic-offset:2
1712 comment-column:40
1713 fill-column:79
1714 End:
1715 */