/*
* This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2006 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
+/** @file lib/configuration.c
+ * @brief Configuration file support
+ */
#include <config.h>
#include "types.h"
#include "regsub.h"
#include "signame.h"
+/** @brief Path to config file
+ *
+ * set_configfile() sets the deafult if it is null.
+ */
char *configfile;
+/** @brief Config file parser state */
struct config_state {
+ /** @brief Filename */
const char *path;
+ /** @brief Line number */
int line;
+ /** @brief Configuration object under construction */
struct config *config;
};
+/** @brief Current configuration */
struct config *config;
+/** @brief One configuration item */
struct conf {
+ /** @brief Name as it appears in the config file */
const char *name;
+ /** @brief Offset in @ref config structure */
size_t offset;
+ /** @brief Pointer to item type */
const struct conftype *type;
+ /** @brief Pointer to item-specific validation routine */
int (*validate)(const struct config_state *cs,
int nvec, char **vec);
};
+/** @brief Type of a configuration item */
struct conftype {
+ /** @brief Pointer to function to set item */
int (*set)(const struct config_state *cs,
const struct conf *whoami,
int nvec, char **vec);
+ /** @brief Pointer to function to free item */
void (*free)(struct config *c, const struct conf *whoami);
};
+/** @brief Compute the address of an item */
#define ADDRESS(C, TYPE) ((TYPE *)((char *)(C) + whoami->offset))
+/** @brief Return the value of an item */
#define VALUE(C, TYPE) (*ADDRESS(C, TYPE))
static int set_signal(const struct config_state *cs,
return 0;
}
+static int set_backend(const struct config_state *cs,
+ const struct conf *whoami,
+ int nvec, char **vec) {
+ int *const valuep = ADDRESS(cs->config, int);
+
+ if(nvec != 1) {
+ error(0, "%s:%d: '%s' requires one argument",
+ cs->path, cs->line, whoami->name);
+ return -1;
+ }
+ if(!strcmp(vec[0], "alsa")) {
+#if API_ALSA
+ *valuep = BACKEND_ALSA;
+#else
+ error(0, "%s:%d: ALSA is not available on this platform",
+ cs->path, cs->line);
+ return -1;
+#endif
+ } else if(!strcmp(vec[0], "command"))
+ *valuep = BACKEND_COMMAND;
+ else if(!strcmp(vec[0], "network"))
+ *valuep = BACKEND_NETWORK;
+ else {
+ error(0, "%s:%d: invalid '%s' value '%s'",
+ cs->path, cs->line, whoami->name, vec[0]);
+ return -1;
+ }
+ return 0;
+}
+
/* free functions */
static void free_none(struct config attribute((unused)) *c,
type_sample_format = { set_sample_format, free_none },
type_restrict = { set_restrict, free_none },
type_namepart = { set_namepart, free_namepartlist },
- type_transform = { set_transform, free_transformlist };
+ type_transform = { set_transform, free_transformlist },
+ type_backend = { set_backend, free_none };
/* specific validation routine */
return 0;
}
-/* configuration table */
+static int validate_addrport(const struct config_state attribute((unused)) *cs,
+ int nvec,
+ char attribute((unused)) **vec) {
+ switch(nvec) {
+ case 0:
+ error(0, "%s:%d: missing address",
+ cs->path, cs->line);
+ return -1;
+ case 1:
+ error(0, "%s:%d: missing port name/number",
+ cs->path, cs->line);
+ return -1;
+ case 2:
+ return 0;
+ default:
+ error(0, "%s:%d: expected ADDRESS PORT",
+ cs->path, cs->line);
+ return -1;
+ }
+}
+
+static int validate_port(const struct config_state attribute((unused)) *cs,
+ int nvec,
+ char attribute((unused)) **vec) {
+ switch(nvec) {
+ case 0:
+ error(0, "%s:%d: missing address",
+ cs->path, cs->line);
+ return -1;
+ case 1:
+ case 2:
+ return 0;
+ default:
+ error(0, "%s:%d: expected [ADDRESS] PORT",
+ cs->path, cs->line);
+ return -1;
+ }
+}
+/** @brief Item name and and offset */
#define C(x) #x, offsetof(struct config, x)
+/** @brief Item name and and offset */
#define C2(x,y) #x, offsetof(struct config, y)
+/** @brief All configuration items */
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(checkpoint_kbyte), &type_integer, validate_non_negative },
{ C(checkpoint_min), &type_integer, validate_non_negative },
{ C(collection), &type_collections, validate_any },
- { C(connect), &type_stringlist, 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(home), &type_string, validate_isdir },
- { C(listen), &type_stringlist, validate_any },
+ { C(listen), &type_stringlist, validate_port },
{ C(lock), &type_boolean, validate_any },
{ C(mixer), &type_string, validate_ischr },
+ { C(multicast_ttl), &type_integer, validate_non_negative },
{ C(namepart), &type_namepart, validate_any },
{ C2(nice, nice_rescan), &type_integer, validate_non_negative },
{ C(nice_rescan), &type_integer, validate_non_negative },
{ 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(username), &type_string, validate_any },
};
-/* find a configuration item's definition by key */
+/** @brief Find a configuration item's definition by key */
static const struct conf *find(const char *key) {
int n;
return &conf[n];
}
-/* set a new configuration value */
+/** @brief Set a new configuration value */
static int config_set(const struct config_state *cs,
int nvec, char **vec) {
const struct conf *which;
|| which->type->set(cs, which, nvec - 1, vec + 1));
}
+/** @brief Error callback used by config_include() */
static void config_error(const char *msg, void *u) {
const struct config_state *cs = u;
error(0, "%s:%d: %s", cs->path, cs->line, msg);
}
-/* include a file by name */
+/** @brief Include a file by name */
static int config_include(struct config *c, const char *path) {
FILE *fp;
char *buffer, *inputbuffer, **vec;
return ret;
}
-/* make a new default config */
+/** @brief Make a new default configuration */
static struct config *config_default(void) {
struct config *c = xmalloc(sizeof *c);
const char *logname;
c->sample_format.channels = 2;
c->sample_format.byte_format = AO_FMT_NATIVE;
c->queue_pad = 10;
+ c->speaker_backend = -1;
+ c->multicast_ttl = 1;
return c;
}
return s;
}
+/** @brief Set the default configuration file */
static void set_configfile(void) {
if(!configfile)
byte_xasprintf(&configfile, "%s/config", pkgconfdir);
}
-/* free the config file */
+/** @brief Free a configuration object */
static void config_free(struct config *c) {
int n;
}
}
+/** @brief Set post-parse defaults */
static void config_postdefaults(struct config *c) {
struct config_state cs;
const struct conf *whoami;
for(n = 0; n < NTRANSFORM; ++n)
set_transform(&cs, whoami, 5, (char **)transform[n]);
}
+ if(c->speaker_backend == -1) {
+ if(c->speaker_command)
+ c->speaker_backend = BACKEND_COMMAND;
+ else if(c->broadcast.n)
+ c->speaker_backend = BACKEND_NETWORK;
+ else {
+#if API_ALSA
+ c->speaker_backend = BACKEND_ALSA;
+#else
+ c->speaker_backend = BACKEND_COMMAND;
+#endif
+ }
+ }
+ 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");
}
-/* re-read the config file */
+/** @brief (Re-)read the config file */
int config_read() {
struct config *c;
char *privconf;
set_configfile();
c = config_default();
- if(config_include(c, configfile))
- return -1;
+ /* standalone Disobedience installs might not have a global config file */
+ if(access(configfile, F_OK) == 0)
+ if(config_include(c, configfile))
+ return -1;
/* if we can read the private config file, do */
if((privconf = config_private())
&& access(privconf, R_OK) == 0
return 0;
}
+/** @brief Return the path to the private configuration file */
char *config_private(void) {
char *s;
return s;
}
+/** @brief Return the path to user's personal configuration file */
char *config_userconf(const char *home, const struct passwd *pw) {
char *s;
return s;
}
-char *config_usersysconf(const struct passwd *pw ) {
+/** @brief Return the path to user-specific system configuration */
+char *config_usersysconf(const struct passwd *pw) {
char *s;
set_configfile();