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