/* setup and teardown ********************************************************/
-static const char *home; /* home had better not change */
-DB_ENV *trackdb_env; /* db environment */
+/** @brief Database home directory
+ *
+ * All database files live below here. It had better never change.
+ */
+static const char *home;
+
+/** @brief Database environment */
+DB_ENV *trackdb_env;
/** @brief The tracks database
* - Keys are UTF-8(NFC(unicode(path name)))
*/
DB *trackdb_playlistsdb;
-static pid_t db_deadlock_pid = -1; /* deadlock manager PID */
-static pid_t rescan_pid = -1; /* rescanner PID */
-static int initialized, opened; /* state */
+/** @brief Deadlock manager PID */
+static pid_t db_deadlock_pid = -1;
+
+/** @brief Rescanner PID */
+static pid_t rescan_pid = -1;
+
+/** @brief Set when the database environment exists */
+static int initialized;
+
+/** @brief Set when databases are open */
+static int opened;
/** @brief Current stats subprocess PIDs */
static hash *stats_pids;
+/** @brief PID of current random track chooser (disorder-choose) */
static pid_t choose_pid = -1;
+
+/** @brief Our end of pipe from disorder-choose */
static int choose_fd;
+
+/** @brief Callback to supply random track to */
static random_callback *choose_callback;
+
+/** @brief Accumulator for output from disorder-choose */
static struct dynstr choose_output;
+
+/** @brief Current completion status of disorder-choose
+ * A bitmap of @ref CHOOSE_READING and @ref CHOOSE_RUNNING.
+ */
static unsigned choose_complete;
+
+/* @brief Exit status from disorder-choose */
static int choose_status;
+
+/** @brief disorder-choose process is running */
#define CHOOSE_RUNNING 1
+
+/** @brief disorder-choose pipe is still open */
#define CHOOSE_READING 2
-/* comparison function for keys */
+/** @brief Comparison function for filename-based keys */
static int compare(DB attribute((unused)) *db_,
const DBT *a, const DBT *b) {
return compare_path_raw(a->data, a->size, b->data, b->size);
D(("initialized database environment"));
}
-/* called when deadlock manager terminates */
+/** @brief Called when deadlock manager terminates */
static int reap_db_deadlock(ev_source attribute((unused)) *ev,
pid_t attribute((unused)) pid,
int status,
return 0;
}
+/** @brief Start a subprogram
+ * @param ev Event loop
+ * @param outputfd File descriptor to redirect @c stdout to, or -1
+ * @param prog Program name
+ * @param ... Arguments
+ * @return PID
+ *
+ * Starts a subprocess. Adds the following arguments:
+ * - @c --config to ensure the right config file is used
+ * - @c --debug or @c --no-debug to match debug settings
+ * - @c --syslog or @c --no-syslog to match log settings
+ */
static pid_t subprogram(ev_source *ev, int outputfd, const char *prog,
...) {
pid_t pid;
return pid;
}
-/* start deadlock manager */
+/** @brief Start deadlock manager
+ * @param ev Event loop
+ *
+ * Called from the main server (only).
+ */
void trackdb_master(ev_source *ev) {
assert(db_deadlock_pid == -1);
db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0);
D(("started deadlock manager"));
}
+/** @brief Kill a subprocess and wait for it to terminate
+ * @param ev Event loop or NULL
+ * @param pid Process ID or -1
+ * @param what Description of subprocess
+ *
+ * Used during trackdb_deinit(). This function blocks so don't use it for
+ * normal teardown as that will hang the server.
+ */
static void terminate_and_wait(ev_source *ev,
pid_t pid,
const char *what) {
ev_child_cancel(ev, pid);
}
-/* close environment */
+/** @brief Close database environment
+ * @param ev Event loop
+ */
void trackdb_deinit(ev_source *ev) {
int err;
D(("deinitialized database environment"));
}
-/* open a specific database */
+/** @brief Open a specific database
+ * @param path Relative path to database
+ * @param dbflags Database flags: DB_DUP, DB_DUPSORT, etc
+ * @param dbtype Database type: DB_HASH, DB_BTREE, etc
+ * @param openflags Open flags: DB_RDONLY, DB_CREATE, etc
+ * @param mode Permission mask: usually 0666
+ * @return Database handle
+ */
static DB *open_db(const char *path,
u_int32_t dbflags,
DBTYPE dbtype,
D(("opened databases"));
}
-/* close track databases */
+/** @brief Close track databases */
void trackdb_close(void) {
int err;
}
}
-/* encode and store a database entry. Returns 0, DB_KEYEXIST or
- * DB_LOCK_DEADLOCK. */
+/** @brief Encode and store a database entry
+ * @param db Database
+ * @param track Track name
+ * @param k List of key/value pairs to store
+ * @param tid Owning transaction
+ * @param flags DB flags e.g. DB_NOOVERWRITE
+ * @return 0, DB_KEYEXIST or DB_LOCK_DEADLOCK
+ */
int trackdb_putdata(DB *db,
const char *track,
const struct kvp *k,
}
}
-/* open a database cursor */
+/** @brief Open a database cursor
+ * @param db Database
+ * @param tid Owning transaction
+ * @return Cursor
+ */
DBC *trackdb_opencursor(DB *db, DB_TXN *tid) {
int err;
DBC *c;
return c;
}
-/* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */
+/** @brief Close a database cursor
+ * @param c Cursor
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
int trackdb_closecursor(DBC *c) {
int err;
}
}
-/* delete a (key,data) pair. Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */
+/** @brief Delete a key/data pair
+ * @param db Database
+ * @param word Key
+ * @param track Data
+ * @param tid Owning transaction
+ * @return 0, DB_NOTFOUND or DB_LOCK_DEADLOCK
+ *
+ * Used by the search and tags databases, hence the odd parameter names.
+ * See also register_word().
+ */
int trackdb_delkeydata(DB *db,
const char *word,
const char *track,
return err;
}
-/* start a transaction */
+/** @brief Start a transaction
+ * @return Transaction
+ */
DB_TXN *trackdb_begin_transaction(void) {
DB_TXN *tid;
int err;
return tid;
}
-/* abort transaction */
+/** @brief Abort transaction
+ * @param tid Transaction (or NULL)
+ *
+ * If @p tid is NULL then nothing happens.
+ */
void trackdb_abort_transaction(DB_TXN *tid) {
int err;
fatal(0, "tid->abort: %s", db_strerror(err));
}
-/* commit transaction */
+/** @brief Commit transaction
+ * @param tid Transaction (must not be NULL)
+ */
void trackdb_commit_transaction(DB_TXN *tid) {
int err;
/* search/tags shared code ***************************************************/
-/* comparison function used by dedupe() */
+/** @brief Comparison function used by dedupe()
+ * @param a Pointer to first key
+ * @param b Pointer to second key
+ * @return -1, 0 or 1
+ *
+ * Passed to qsort().
+ */
static int wordcmp(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
-/* sort and de-dupe VEC */
+/** @brief Sort and de-duplicate @p vec
+ * @param vec Vector to sort
+ * @param nvec Length of @p vec
+ * @return @p vec
+ *
+ * The returned vector is NULL-terminated, and there must be room for this NULL
+ * even if there are no duplicates (i.e. it must have more than @p nvec
+ * elements.)
+ */
static char **dedupe(char **vec, int nvec) {
int m, n;
return vec;
}
-/* update a key/track database. Returns 0 or DB_DEADLOCK. */
+/** @brief Store a key/data pair
+ * @param db Database
+ * @param what Description
+ * @param track Data
+ * @param word Key
+ * @param tid Owning transaction
+ * @return 0 or DB_DEADLOCK
+ *
+ * Used by the search and tags databases, hence the odd parameter names.
+ * See also trackdb_delkeydata().
+ */
static int register_word(DB *db, const char *what,
const char *track, const char *word,
DB_TXN *tid) {
/* search primitives *********************************************************/
-/* return true iff NAME is a trackname_display_ pref */
+/** @brief Return true iff @p name is a trackname_display_ pref
+ * @param name Preference name
+ * @return Non-zero iff @p name is a trackname_display_ pref
+ */
static int is_display_pref(const char *name) {
static const char prefix[] = "trackname_display_";
return !strncmp(name, prefix, (sizeof prefix) - 1);
}
-/** @brief Word_Break property tailor that treats underscores as spaces */
+/** @brief Word_Break property tailor that treats underscores as spaces
+ * @param c Code point
+ * @return Tailored property or -1 to use standard value
+ *
+ * Passed to utf32_word_split() when splitting a track name into words.
+ * See word_split() and @ref unicode_property_tailor.
+ */
static int tailor_underscore_Word_Break_Other(uint32_t c) {
switch(c) {
default:
return t - start;
}
-/** @brief Normalize and split a string using a given tailoring */
+/** @brief Normalize and split a string using a given tailoring
+ * @param v Where to store words from string
+ * @param s Input string
+ * @param pt Word_Break property tailor, or NULL
+ *
+ * The output words will be:
+ * - case-folded
+ * - have any combination characters stripped
+ * - not include any word break code points (as tailored)
+ *
+ * Used by track_to_words(), with @p pt set to @ref
+ * tailor_underscore_Word_Break_Other, and by normalize_tag() with no
+ * tailoring.
+ */
static void word_split(struct vector *v,
const char *s,
unicode_property_tailor *pt) {
return d->vec;
}
-/* compute the words of a track name */
+/** @brief Compute the words of a track name
+ * @param track Track name
+ * @param p Preferences (for display prefs)
+ * @return NULL-terminated, de-duplicated list or words
+ */
static char **track_to_words(const char *track,
const struct kvp *p) {
struct vector v;
return dedupe(v.vec, v.nvec);
}
-/* return nonzero iff WORD is a stopword */
+/** @brief Test for a stopword
+ * @param word Word
+ * @return Non-zero if @p word is a stopword
+ */
static int stopword(const char *word) {
int n;
return n < config->stopword.n;
}
-/* record that WORD appears in TRACK. Returns 0 or DB_LOCK_DEADLOCK. */
+/** @brief Register a search term
+ * @param track Track name
+ * @param word A word that appears in the name of @p track
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
static int register_search_word(const char *track, const char *word,
DB_TXN *tid) {
if(stopword(word)) return 0;
/* Tags **********************************************************************/
-/* Return nonzero if C is a valid tag character */
+/** @brief Test for tag characters
+ * @param c Character
+ * @return Non-zero if @p c is a tag character
+ *
+ * The current rule is that commas and the control characters 0-31 are not
+ * allowed but anything else is permitted. This is arguably a bit loose.
+ */
static int tagchar(int c) {
switch(c) {
case ',':
}
}
-/* Parse and de-dupe a tag list. If S=0 then assumes "". */
+/** @brief Parse a tag list
+ * @param s Tag list or NULL (equivalent to "")
+ * @return Parsed tag list
+ *
+ * The tags will be normalized (as per normalize_tag()) and de-duplicated.
+ */
char **parsetags(const char *s) {
const char *t;
struct vector v;
return dedupe(v.vec, v.nvec);
}
-/* Record that TRACK has TAG. Returns 0 or DB_LOCK_DEADLOCK. */
+/** @brief Register a tag
+ * @param track Track name
+ * @param tag Tag name
+ * @param tid Owning transaction
+ * @return 0 or DB_LOCK_DEADLOCK
+ */
static int register_tag(const char *track, const char *tag, DB_TXN *tid) {
return register_word(trackdb_tagsdb, "tags", track, tag, tid);
}