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