struct stringlistlist *sll;
sll = ADDRESS(cs->config, struct stringlistlist);
struct stringlistlist *sll;
sll = ADDRESS(cs->config, struct stringlistlist);
for(n = 0; n < nvec; ++n) {
sl->n++;
sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
for(n = 0; n < nvec; ++n) {
sl->n++;
sl->s = xrealloc(sl->s, (sl->n * sizeof (char *)));
*valuep = BACKEND_COMMAND;
else if(!strcmp(vec[0], "network"))
*valuep = BACKEND_NETWORK;
*valuep = BACKEND_COMMAND;
else if(!strcmp(vec[0], "network"))
*valuep = BACKEND_NETWORK;
+ else if(!strcmp(vec[0], "coreaudio")) {
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+ *valuep = BACKEND_COREAUDIO;
+#else
+ error(0, "%s:%d: Core Audio is not available on this platform",
+ cs->path, cs->line);
+ return -1;
+#endif
+ } else if(!strcmp(vec[0], "oss")) {
+#if HAVE_SYS_SOUNDCARD_H
+ *valuep = BACKEND_OSS;
+#else
+ error(0, "%s:%d: OSS is not available on this platform",
+ cs->path, cs->line);
+ return -1;
+#endif
+ } else {
error(0, "%s:%d: invalid '%s' value '%s'",
cs->path, cs->line, whoami->name, vec[0]);
return -1;
error(0, "%s:%d: invalid '%s' value '%s'",
cs->path, cs->line, whoami->name, vec[0]);
return -1;
+static int validate_isabspath(const struct config_state *cs,
+ int nvec, char **vec) {
+ int n;
+
+ for(n = 0; n < nvec; ++n)
+ if(vec[n][0] != '/') {
+ error(errno, "%s:%d: %s: not an absolute path",
+ cs->path, cs->line, vec[n]);
+ return -1;
+ }
+ return 0;
+}
+
static int validate_isdir(const struct config_state *cs,
int nvec, char **vec) {
VALIDATE_FILE(S_ISDIR, "directory");
static int validate_isdir(const struct config_state *cs,
int nvec, char **vec) {
VALIDATE_FILE(S_ISDIR, "directory");
+static int validate_tracklength(const struct config_state *cs,
+ int nvec,
+ char attribute((unused)) **vec) {
+ if(nvec < 2) {
+ error(0, "%s:%d: should be at least 'tracklength PATTERN MODULE'",
+ cs->path, cs->line);
+ return -1;
+ }
+ return 0;
+}
+
static int validate_allow(const struct config_state *cs,
int nvec,
char attribute((unused)) **vec) {
static int validate_allow(const struct config_state *cs,
int nvec,
char attribute((unused)) **vec) {
+static int validate_algo(const struct config_state attribute((unused)) *cs,
+ int nvec,
+ char **vec) {
+ if(nvec != 1) {
+ error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
+ return -1;
+ }
+ if(!valid_authhash(vec[0])) {
+ error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
+ return -1;
+ }
+ return 0;
+}
+
/** @brief Item name and and offset */
#define C(x) #x, offsetof(struct config, x)
/** @brief Item name and and offset */
/** @brief Item name and and offset */
#define C(x) #x, offsetof(struct config, x)
/** @brief Item name and and offset */
static const struct conf conf[] = {
{ C(alias), &type_string, validate_alias },
{ C(allow), &type_stringlist_accum, validate_allow },
static const struct conf conf[] = {
{ C(alias), &type_string, validate_alias },
{ C(allow), &type_stringlist_accum, validate_allow },
{ C(broadcast), &type_stringlist, validate_addrport },
{ C(broadcast_from), &type_stringlist, validate_addrport },
{ C(channel), &type_string, validate_channel },
{ C(broadcast), &type_stringlist, validate_addrport },
{ C(broadcast_from), &type_stringlist, validate_addrport },
{ C(channel), &type_string, validate_channel },
{ C(checkpoint_min), &type_integer, validate_non_negative },
{ C(collection), &type_collections, validate_any },
{ C(connect), &type_stringlist, validate_addrport },
{ C(checkpoint_min), &type_integer, validate_non_negative },
{ C(collection), &type_collections, validate_any },
{ C(connect), &type_stringlist, validate_addrport },
{ C(device), &type_string, validate_any },
{ C(gap), &type_integer, validate_non_negative },
{ C(history), &type_integer, validate_positive },
{ C(device), &type_string, validate_any },
{ C(gap), &type_integer, validate_non_negative },
{ C(history), &type_integer, validate_positive },
- { C(home), &type_string, validate_isdir },
+ { C(home), &type_string, validate_isabspath },
{ C(listen), &type_stringlist, validate_port },
{ C(lock), &type_boolean, validate_any },
{ C(mixer), &type_string, validate_ischr },
{ C(listen), &type_stringlist, validate_port },
{ C(lock), &type_boolean, validate_any },
{ C(mixer), &type_string, validate_ischr },
{ C(nice_rescan), &type_integer, validate_non_negative },
{ C(nice_server), &type_integer, validate_any },
{ C(nice_speaker), &type_integer, validate_any },
{ C(nice_rescan), &type_integer, validate_non_negative },
{ C(nice_server), &type_integer, validate_any },
{ C(nice_speaker), &type_integer, validate_any },
{ C(password), &type_string, validate_any },
{ C(player), &type_stringlist_accum, validate_player },
{ C(plugins), &type_string_accum, validate_isdir },
{ C(password), &type_string, validate_any },
{ C(player), &type_stringlist_accum, validate_player },
{ C(plugins), &type_string_accum, validate_isdir },
{ C2(restrict, restrictions), &type_restrict, validate_any },
{ C(sample_format), &type_sample_format, validate_sample_format },
{ C(scratch), &type_string_accum, validate_isreg },
{ C2(restrict, restrictions), &type_restrict, validate_any },
{ C(sample_format), &type_sample_format, validate_sample_format },
{ C(scratch), &type_string_accum, validate_isreg },
{ C(signal), &type_signal, validate_any },
{ C(sox_generation), &type_integer, validate_non_negative },
{ C(speaker_backend), &type_backend, validate_any },
{ C(speaker_command), &type_string, validate_any },
{ C(stopword), &type_string_accum, validate_any },
{ C(templates), &type_string_accum, validate_isdir },
{ C(signal), &type_signal, validate_any },
{ C(sox_generation), &type_integer, validate_non_negative },
{ C(speaker_backend), &type_backend, validate_any },
{ C(speaker_command), &type_string, validate_any },
{ C(stopword), &type_string_accum, validate_any },
{ C(templates), &type_string_accum, validate_isdir },
{ C(transform), &type_transform, validate_any },
{ C(trust), &type_string_accum, validate_any },
{ C(url), &type_string, validate_url },
{ C(transform), &type_transform, validate_any },
{ C(trust), &type_string_accum, validate_any },
{ C(url), &type_string, validate_url },
- { "title", "/([0-9]+:)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
+ { "title", "/([0-9]+ *[-:] *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display" },
{ "title", "/([^/]+)\\.[a-zA-Z0-9]+$", "$1", "sort" },
{ "album", "/([^/]+)/[^/]+$", "$1", "*" },
{ "artist", "/([^/]+)/[^/]+/[^/]+$", "$1", "*" },
{ "title", "/([^/]+)\\.[a-zA-Z0-9]+$", "$1", "sort" },
{ "album", "/([^/]+)/[^/]+$", "$1", "*" },
{ "artist", "/([^/]+)/[^/]+/[^/]+$", "$1", "*" },
- { "track", "^.*/([0-9]+:)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display", "" },
+ { "track", "^.*/([0-9]+ *[-:] *)?([^/]+)\\.[a-zA-Z0-9]+$", "$2", "display", "" },
{ "track", "^.*/([^/]+)\\.[a-zA-Z0-9]+$", "$1", "sort", "" },
{ "dir", "^.*/([^/]+)$", "$1", "*", "" },
{ "dir", "^(the) ([^/]*)", "$2, $1", "sort", "i", },
{ "track", "^.*/([^/]+)\\.[a-zA-Z0-9]+$", "$1", "sort", "" },
{ "dir", "^.*/([^/]+)$", "$1", "*", "" },
{ "dir", "^(the) ([^/]*)", "$2, $1", "sort", "i", },
- if(c->speaker_backend == BACKEND_COMMAND && !c->speaker_command)
- fatal(0, "speaker_backend is command but speaker_command is not set");
- if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n)
- fatal(0, "speaker_backend is network but broadcast is not set");
- if(c->speaker_backend) {
- /* Override sample format */
+ if(server) {
+ if(c->speaker_backend == BACKEND_COMMAND && !c->speaker_command)
+ fatal(0, "speaker_backend is command but speaker_command is not set");
+ if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n)
+ fatal(0, "speaker_backend is network but broadcast is not set");
+ }
+ /* Override sample format */
+ switch(c->speaker_backend) {
+ case BACKEND_NETWORK:
c->sample_format.rate = 44100;
c->sample_format.channels = 2;
c->sample_format.bits = 16;
c->sample_format.endian = ENDIAN_BIG;
c->sample_format.rate = 44100;
c->sample_format.channels = 2;
c->sample_format.bits = 16;
c->sample_format.endian = ENDIAN_BIG;
+ break;
+ case BACKEND_COREAUDIO:
+ c->sample_format.rate = 44100;
+ c->sample_format.channels = 2;
+ c->sample_format.bits = 16;
+ c->sample_format.endian = ENDIAN_NATIVE;
+ break;
+ }
-/** @brief (Re-)read the config file */
-int config_read() {
+/** @brief (Re-)read the config file
+ * @param server If set, do extra checking
+ */
+int config_read(int server) {