struct config_state {
/** @brief Filename */
const char *path;
+
/** @brief Line number */
int line;
+
/** @brief Configuration object under construction */
struct config *config;
};
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 */
+
+ /** @brief Pointer to item-specific validation routine
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * The validate function should report any error it detects.
+ */
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 */
+ /** @brief Pointer to function to set item
+ * @param cs Configuration state
+ * @param whoami Configuration item to set
+ * @param nvec Length of new value
+ * @param vec New value
+ * @return 0 on success, non-0 on error
+ */
int (*set)(const struct config_state *cs,
const struct conf *whoami,
int nvec, char **vec);
- /** @brief Pointer to function to free item */
+
+ /** @brief Pointer to function to free item
+ * @param c Configuration structure to free an item of
+ * @param whoami Configuration item to free
+ */
void (*free)(struct config *c, const struct conf *whoami);
};
/* specific validation routine */
+/** @brief Perform a test on a filename
+ * @param test Test function to call on mode bits
+ * @param what Type of file sought
+ *
+ * If @p test returns 0 then the file is not a @p what and an error
+ * is reported and -1 is returned.
+ */
#define VALIDATE_FILE(test, what) do { \
struct stat sb; \
int n; \
} \
} while(0)
+/** @brief Validate an absolute path
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_isabspath(const struct config_state *cs,
int nvec, char **vec) {
int n;
return 0;
}
+/** @brief Validate an existing directory
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_isdir(const struct config_state *cs,
int nvec, char **vec) {
VALIDATE_FILE(S_ISDIR, "directory");
return 0;
}
+/** @brief Validate an existing regular file
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_isreg(const struct config_state *cs,
int nvec, char **vec) {
VALIDATE_FILE(S_ISREG, "regular file");
return 0;
}
+/** @brief Validate a player pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_player(const struct config_state *cs,
int nvec,
char attribute((unused)) **vec) {
return 0;
}
+/** @brief Validate a track length pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_tracklength(const struct config_state *cs,
int nvec,
char attribute((unused)) **vec) {
return 0;
}
+/** @brief Validate an allow directive
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * Obsolete - only used for parsing legacy configuration.
+ */
static int validate_allow(const struct config_state *cs,
int nvec,
char attribute((unused)) **vec) {
return 0;
}
+/** @brief Validate a non-negative (@c long) integer
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_non_negative(const struct config_state *cs,
int nvec, char **vec) {
long n;
return 0;
}
+/** @brief Validate a positive (@c long) integer
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_positive(const struct config_state *cs,
int nvec, char **vec) {
long n;
return 0;
}
+/** @brief Validate a system username
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_isauser(const struct config_state *cs,
int attribute((unused)) nvec,
char **vec) {
return 0;
}
+/** @brief Validate a sample format string
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_sample_format(const struct config_state *cs,
int attribute((unused)) nvec,
char **vec) {
return parse_sample_format(cs, 0, nvec, vec);
}
+/** @brief Validate anything
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0
+ */
static int validate_any(const struct config_state attribute((unused)) *cs,
int attribute((unused)) nvec,
char attribute((unused)) **vec) {
return 0;
}
+/** @brief Validate a URL
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * Rather cursory.
+ */
static int validate_url(const struct config_state attribute((unused)) *cs,
int attribute((unused)) nvec,
char **vec) {
return 0;
}
+/** @brief Validate an alias pattern
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_alias(const struct config_state *cs,
int nvec,
char **vec) {
return 0;
}
+/** @brief Validate a hash algorithm name
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_algo(const struct config_state attribute((unused)) *cs,
int nvec,
char **vec) {
return 0;
}
+/** @brief Validate a playback backend name
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_backend(const struct config_state attribute((unused)) *cs,
int nvec,
char **vec) {
return 0;
}
+/** @brief Validate a pause mode string
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ */
static int validate_pausemode(const struct config_state attribute((unused)) *cs,
int nvec,
char **vec) {
return -1;
}
+/** @brief Validate a destination network address
+ * @param cs Configuration state
+ * @param nvec Length of (proposed) new value
+ * @param vec Elements of new value
+ * @return 0 on success, non-0 on error
+ *
+ * By a destination address, it is meant that it must not be a wildcard
+ * address.
+ */
static int validate_destaddr(const struct config_state attribute((unused)) *cs,
int nvec,
char **vec) {
return &conf[n];
}
-/** @brief Set a new configuration value */
+/** @brief Set a new configuration value
+ * @param cs Configuration state
+ * @param nvec Length of @p vec
+ * @param vec Name and new value
+ * @return 0 on success, non-0 on error.
+ *
+ * @c vec[0] is the name, the rest is the 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 Set a configuration item from parameters
+ * @param cs Configuration state
+ * @param which Item to set
+ * @param ... Value as strings, terminated by (char *)NULL
+ * @return 0 on success, non-0 on error
+ */
static int config_set_args(const struct config_state *cs,
const char *which, ...) {
va_list ap;
return config_set(cs, v->nvec, v->vec);
}
-/** @brief Error callback used by config_include() */
+/** @brief Error callback used by config_include()
+ * @param msg Error message
+ * @param u User data (@ref config_state)
+ */
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);
}
-/** @brief Include a file by name */
+/** @brief Include a file by name
+ * @param c Configuration to update
+ * @param path Path to read
+ * @return 0 on success, non-0 on error
+ */
static int config_include(struct config *c, const char *path) {
FILE *fp;
char *buffer, *inputbuffer, **vec;
continue;
}
if(n) {
+ /* 'include' is special-cased */
if(!strcmp(vec[0], "include")) {
if(n != 2) {
error(0, "%s:%d: must be 'include PATH'", cs.path, cs.line);
return ret;
}
+/** @brief Default stopword setting */
static const char *const default_stopwords[] = {
"stopword",
};
#define NDEFAULT_STOPWORDS (sizeof default_stopwords / sizeof *default_stopwords)
+/** @brief Default player patterns */
static const char *const default_players[] = {
"*.ogg",
"*.flac",
};
#define NDEFAULT_PLAYERS (sizeof default_players / sizeof *default_players)
-/** @brief Make a new default configuration */
+/** @brief Make a new default configuration
+ * @return New configuration
+ */
static struct config *config_default(void) {
struct config *c = xmalloc(sizeof *c);
const char *logname;
return c;
}
+/** @brief Construct a filename
+ * @param c Configuration
+ * @param name Base filename
+ * @return Full filename
+ *
+ * Usually use config_get_file() instead.
+ */
char *config_get_file2(struct config *c, const char *name) {
char *s;
byte_xasprintf(&configfile, "%s/config", pkgconfdir);
}
-/** @brief Free a configuration object */
+/** @brief Free a configuration object
+ * @param c Configuration to free
+ *
+ * @p c is indeterminate after this function is called.
+ */
static void config_free(struct config *c) {
int n;
}
}
-/** @brief Set post-parse defaults */
+/** @brief Set post-parse defaults
+ * @param c Configuration to update
+ * @param server True when running in the server
+ *
+ * If @p server is set then certain parts of the configuration are more
+ * strictly validated.
+ */
static void config_postdefaults(struct config *c,
int server) {
struct config_state cs;
return 0;
}
+/** @brief Get a filename within the home directory
+ * @param name Relative name
+ * @return Full path
+ */
char *config_get_file(const char *name) {
return config_get_file2(config, name);
}
+/** @brief Order two stringlists
+ * @param a First stringlist
+ * @param b Second stringlist
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
static int stringlist_compare(const struct stringlist *a,
const struct stringlist *b) {
int n = 0, c;
return 0;
}
+/** @brief Order two namepart definitions
+ * @param a First namepart definition
+ * @param b Second namepart definition
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
static int namepart_compare(const struct namepart *a,
const struct namepart *b) {
int c;
return 0;
}
+/** @brief Order two lists of namepart definitions
+ * @param a First list of namepart definitions
+ * @param b Second list of namepart definitions
+ * @return <0, 0 or >0 if a<b, a=b or a>b
+ */
static int namepartlist_compare(const struct namepartlist *a,
const struct namepartlist *b) {
int n = 0, c;
size_t valuesize; /* size of a value */
};
+/** @brief Hash function
+ * @param key Key to hash
+ * @return Hash code
+ */
static size_t hashfn(const char *key) {
size_t i = 0;
return i;
}
+/** @brief Expand a hash table
+ * @param h Hash table to expand
+ */
static void grow(hash *h) {
size_t n, newnslots;
struct entry **newslots, *e, *f;
h->nslots = newnslots;
}
+/** @brief Create a new hash table
+ * @param valuesize Size of value type
+ * @return Hash table
+ */
hash *hash_new(size_t valuesize) {
hash *h = xmalloc(sizeof *h);
return h;
}
+/** @brief Add an element to a hash table
+ * @param h Hash table
+ * @param key Key
+ * @param value New value (will be shallow-copied)
+ * @param mode Add mode
+ * @return 0 on success, -1 if the value could not be added
+ *
+ * Possible add modes are:
+ * - @ref HASH_INSERT - key must not exist yet
+ * - @ref HASH_REPLACE - key must already exist
+ * - @ref HASH_INSERT_OR_REPLACE - key may or may not exist
+ */
int hash_add(hash *h, const char *key, const void *value, int mode) {
size_t n = hashfn(key);
struct entry *e;
}
}
+/** @brief Remove an element from a hash table
+ * @param h Hash table
+ * @param key Key to remove
+ * @return 0 on success, -1 if the key wasn't found
+ */
int hash_remove(hash *h, const char *key) {
size_t n = hashfn(key);
struct entry *e, **ee;
return -1;
}
+/** @brief Find an item in a hash table
+ * @param h Hash table
+ * @param key Key to find
+ * @return Pointer to value or NULL if not found
+ *
+ * The return value points inside the hash table and should not be modified.
+ */
void *hash_find(hash *h, const char *key) {
size_t n = hashfn(key);
struct entry *e;
return 0;
}
+/** @brief Visit every item in a hash table
+ * @param h Hash Table
+ * @param callback Function to call for each item
+ * @param u Passed to @p callback
+ * @return 0 on completion, else last return from @p callback
+ *
+ * @p callback should return 0 to continue or non-0 to stop. The @p key and @p
+ * value pointers passed to it point into the hash table and should not be
+ * modified.
+ *
+ * It's safe to remove items from inside the callback including the visited
+ * one. It is not safe to add items from inside the callback.
+ *
+ * No particular ordering is used.
+ */
int hash_foreach(hash *h,
int (*callback)(const char *key, void *value, void *u),
void *u) {
return 0;
}
+/** @brief Count the size of a hash table
+ * @param h Hash table
+ * @return Number of elements in hash table
+ */
size_t hash_count(hash *h) {
return h->nitems;
}
+/** @brief Get all the keys of a hash table
+ * @param h Hash table
+ * @return NULL-terminated list of keys
+ *
+ * The keys point into the hash table itself and should not be modified.
+ *
+ * No particular ordering is used.
+ */
char **hash_keys(hash *h) {
size_t n;
char **vec = xcalloc(h->nitems + 1, sizeof (char *)), **vp = vec;