From 9ceb8a717ae940972904ef83722f71c3ee124715 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 2 Jan 2013 22:25:17 +0100 Subject: sticker: convert to C++ --- Makefile.am | 9 +- src/AllCommands.cxx | 6 +- src/Main.cxx | 6 +- src/OtherCommands.cxx | 2 +- src/SongSticker.cxx | 166 ++++++++++++ src/SongSticker.hxx | 83 ++++++ src/StickerCommands.cxx | 6 +- src/StickerDatabase.cxx | 663 ++++++++++++++++++++++++++++++++++++++++++++++++ src/StickerDatabase.hxx | 157 ++++++++++++ src/StickerPrint.cxx | 47 ++++ src/StickerPrint.hxx | 39 +++ src/UpdateRemove.cxx | 6 +- src/song_sticker.c | 167 ------------ src/song_sticker.h | 84 ------ src/sticker.c | 660 ----------------------------------------------- src/sticker.h | 159 ------------ src/sticker_print.c | 44 ---- src/sticker_print.h | 39 --- 18 files changed, 1169 insertions(+), 1174 deletions(-) create mode 100644 src/SongSticker.cxx create mode 100644 src/SongSticker.hxx create mode 100644 src/StickerDatabase.cxx create mode 100644 src/StickerDatabase.hxx create mode 100644 src/StickerPrint.cxx create mode 100644 src/StickerPrint.hxx delete mode 100644 src/song_sticker.c delete mode 100644 src/song_sticker.h delete mode 100644 src/sticker.c delete mode 100644 src/sticker.h delete mode 100644 src/sticker_print.c delete mode 100644 src/sticker_print.h diff --git a/Makefile.am b/Makefile.am index c5379a31e..1ae249c80 100644 --- a/Makefile.am +++ b/Makefile.am @@ -162,12 +162,9 @@ mpd_headers = \ src/sig_handlers.h \ src/TimePrint.cxx src/TimePrint.hxx \ src/song.h \ - src/song_sticker.h \ src/song_sort.c src/song_sort.h \ src/socket_util.h \ src/stats.h \ - src/sticker.h \ - src/sticker_print.h \ src/tag.h \ src/tag_internal.h \ src/tag_pool.h \ @@ -366,9 +363,9 @@ endif if ENABLE_SQLITE src_mpd_SOURCES += \ src/StickerCommands.cxx src/StickerCommands.hxx \ - src/sticker.c \ - src/sticker_print.c \ - src/song_sticker.c + src/StickerDatabase.cxx src/StickerDatabase.hxx \ + src/StickerPrint.cxx src/StickerPrint.hxx \ + src/SongSticker.cxx src/SongSticker.hxx endif # Generic utility library diff --git a/src/AllCommands.cxx b/src/AllCommands.cxx index 28e3d3ebd..6026c6906 100644 --- a/src/AllCommands.cxx +++ b/src/AllCommands.cxx @@ -29,7 +29,6 @@ extern "C" { #include "PlaylistCommands.hxx" #include "DatabaseCommands.hxx" #include "OutputCommands.hxx" -#include "StickerCommands.hxx" #include "MessageCommands.hxx" #include "OtherCommands.hxx" #include "permission.h" @@ -39,11 +38,12 @@ extern "C" { #include "protocol/result.h" #include "tokenizer.h" #include "client.h" +} #ifdef ENABLE_SQLITE -#include "sticker.h" +#include "StickerCommands.hxx" +#include "StickerDatabase.hxx" #endif -} #include #include diff --git a/src/Main.cxx b/src/Main.cxx index 0d8f66d7e..d0f88c23e 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -63,16 +63,14 @@ extern "C" { #include "InotifyUpdate.hxx" #endif -extern "C" { - #ifdef ENABLE_SQLITE -#include "sticker.h" +#include "StickerDatabase.hxx" #endif +extern "C" { #ifdef ENABLE_ARCHIVE #include "archive_list.h" #endif - } #include diff --git a/src/OtherCommands.cxx b/src/OtherCommands.cxx index c5aeeab2f..19d5a8c76 100644 --- a/src/OtherCommands.cxx +++ b/src/OtherCommands.cxx @@ -50,7 +50,7 @@ extern "C" { } #ifdef ENABLE_SQLITE -#include "sticker.h" +#include "StickerDatabase.hxx" #endif #include diff --git a/src/SongSticker.cxx b/src/SongSticker.cxx new file mode 100644 index 000000000..b32ef9d4b --- /dev/null +++ b/src/SongSticker.cxx @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "SongSticker.hxx" +#include "StickerDatabase.hxx" +#include "song.h" +#include "directory.h" + +#include + +#include +#include + +char * +sticker_song_get_value(const struct song *song, const char *name) +{ + char *uri, *value; + + assert(song != NULL); + assert(song_in_database(song)); + + uri = song_get_uri(song); + value = sticker_load_value("song", uri, name); + g_free(uri); + + return value; +} + +bool +sticker_song_set_value(const struct song *song, + const char *name, const char *value) +{ + char *uri; + bool ret; + + assert(song != NULL); + assert(song_in_database(song)); + + uri = song_get_uri(song); + ret = sticker_store_value("song", uri, name, value); + g_free(uri); + + return ret; +} + +bool +sticker_song_delete(const struct song *song) +{ + char *uri; + bool ret; + + assert(song != NULL); + assert(song_in_database(song)); + + uri = song_get_uri(song); + ret = sticker_delete("song", uri); + g_free(uri); + + return ret; +} + +bool +sticker_song_delete_value(const struct song *song, const char *name) +{ + char *uri; + bool success; + + assert(song != NULL); + assert(song_in_database(song)); + + uri = song_get_uri(song); + success = sticker_delete_value("song", uri, name); + g_free(uri); + + return success; +} + +struct sticker * +sticker_song_get(const struct song *song) +{ + char *uri; + struct sticker *sticker; + + assert(song != NULL); + assert(song_in_database(song)); + + uri = song_get_uri(song); + sticker = sticker_load("song", uri); + g_free(uri); + + return sticker; +} + +struct sticker_song_find_data { + struct directory *directory; + const char *base_uri; + size_t base_uri_length; + + void (*func)(struct song *song, const char *value, + gpointer user_data); + gpointer user_data; +}; + +static void +sticker_song_find_cb(const char *uri, const char *value, gpointer user_data) +{ + struct sticker_song_find_data *data = + (struct sticker_song_find_data *)user_data; + struct song *song; + + if (memcmp(uri, data->base_uri, data->base_uri_length) != 0) + /* should not happen, ignore silently */ + return; + + song = directory_lookup_song(data->directory, + uri + data->base_uri_length); + if (song != NULL) + data->func(song, value, data->user_data); +} + +bool +sticker_song_find(struct directory *directory, const char *name, + void (*func)(struct song *song, const char *value, + gpointer user_data), + gpointer user_data) +{ + struct sticker_song_find_data data; + data.directory = directory; + data.func = func; + data.user_data = user_data; + + char *allocated; + data.base_uri = directory_get_path(directory); + if (*data.base_uri != 0) + /* append slash to base_uri */ + data.base_uri = allocated = + g_strconcat(data.base_uri, "/", NULL); + else + /* searching in root directory - no trailing slash */ + allocated = NULL; + + data.base_uri_length = strlen(data.base_uri); + + bool success = sticker_find("song", data.base_uri, name, + sticker_song_find_cb, &data); + g_free(allocated); + + return success; +} diff --git a/src/SongSticker.hxx b/src/SongSticker.hxx new file mode 100644 index 000000000..81b76cef5 --- /dev/null +++ b/src/SongSticker.hxx @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SONG_STICKER_HXX +#define MPD_SONG_STICKER_HXX + +#include + +struct song; +struct directory; +struct sticker; + +/** + * Returns one value from a song's sticker record. The caller must + * free the return value with g_free(). + */ +char * +sticker_song_get_value(const struct song *song, const char *name); + +/** + * Sets a sticker value in the specified song. Overwrites existing + * values. + */ +bool +sticker_song_set_value(const struct song *song, + const char *name, const char *value); + +/** + * Deletes a sticker from the database. All values are deleted. + */ +bool +sticker_song_delete(const struct song *song); + +/** + * Deletes a sticker value. Does nothing if the sticker did not + * exist. + */ +bool +sticker_song_delete_value(const struct song *song, const char *name); + +/** + * Loads the sticker for the specified song. + * + * @param song the song object + * @return a sticker object, or NULL on error or if there is no sticker + */ +struct sticker * +sticker_song_get(const struct song *song); + +/** + * Finds stickers with the specified name below the specified + * directory. + * + * Caller must lock the #db_mutex. + * + * @param directory the base directory to search in + * @param name the name of the sticker + * @return true on success (even if no sticker was found), false on + * failure + */ +bool +sticker_song_find(struct directory *directory, const char *name, + void (*func)(struct song *song, const char *value, + gpointer user_data), + gpointer user_data); + +#endif diff --git a/src/StickerCommands.cxx b/src/StickerCommands.cxx index ad09f9b46..f4636e02c 100644 --- a/src/StickerCommands.cxx +++ b/src/StickerCommands.cxx @@ -21,12 +21,12 @@ #include "StickerCommands.hxx" #include "SongPrint.hxx" #include "DatabaseLock.hxx" +#include "SongSticker.hxx" +#include "StickerPrint.hxx" +#include "StickerDatabase.hxx" extern "C" { #include "protocol/result.h" -#include "sticker.h" -#include "sticker_print.h" -#include "song_sticker.h" #include "database.h" } diff --git a/src/StickerDatabase.cxx b/src/StickerDatabase.cxx new file mode 100644 index 000000000..9577ff734 --- /dev/null +++ b/src/StickerDatabase.cxx @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "StickerDatabase.hxx" + +extern "C" { +#include "idle.h" +} + +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "sticker" + +#if SQLITE_VERSION_NUMBER < 3003009 +#define sqlite3_prepare_v2 sqlite3_prepare +#endif + +struct sticker { + GHashTable *table; +}; + +enum sticker_sql { + STICKER_SQL_GET, + STICKER_SQL_LIST, + STICKER_SQL_UPDATE, + STICKER_SQL_INSERT, + STICKER_SQL_DELETE, + STICKER_SQL_DELETE_VALUE, + STICKER_SQL_FIND, +}; + +static const char *const sticker_sql[] = { + //[STICKER_SQL_GET] = + "SELECT value FROM sticker WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_LIST] = + "SELECT name,value FROM sticker WHERE type=? AND uri=?", + //[STICKER_SQL_UPDATE] = + "UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_INSERT] = + "INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)", + //[STICKER_SQL_DELETE] = + "DELETE FROM sticker WHERE type=? AND uri=?", + //[STICKER_SQL_DELETE_VALUE] = + "DELETE FROM sticker WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_FIND] = + "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?", +}; + +static const char sticker_sql_create[] = + "CREATE TABLE IF NOT EXISTS sticker(" + " type VARCHAR NOT NULL, " + " uri VARCHAR NOT NULL, " + " name VARCHAR NOT NULL, " + " value VARCHAR NOT NULL" + ");" + "CREATE UNIQUE INDEX IF NOT EXISTS" + " sticker_value ON sticker(type, uri, name);" + ""; + +static sqlite3 *sticker_db; +static sqlite3_stmt *sticker_stmt[G_N_ELEMENTS(sticker_sql)]; + +static GQuark +sticker_quark(void) +{ + return g_quark_from_static_string("sticker"); +} + +static sqlite3_stmt * +sticker_prepare(const char *sql, GError **error_r) +{ + int ret; + sqlite3_stmt *stmt; + + ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL); + if (ret != SQLITE_OK) { + g_set_error(error_r, sticker_quark(), ret, + "sqlite3_prepare_v2() failed: %s", + sqlite3_errmsg(sticker_db)); + return NULL; + } + + return stmt; +} + +bool +sticker_global_init(const char *path, GError **error_r) +{ + int ret; + + if (path == NULL) + /* not configured */ + return true; + + /* open/create the sqlite database */ + + ret = sqlite3_open(path, &sticker_db); + if (ret != SQLITE_OK) { + g_set_error(error_r, sticker_quark(), ret, + "Failed to open sqlite database '%s': %s", + path, sqlite3_errmsg(sticker_db)); + return false; + } + + /* create the table and index */ + + ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL); + if (ret != SQLITE_OK) { + g_set_error(error_r, sticker_quark(), ret, + "Failed to create sticker table: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + /* prepare the statements we're going to use */ + + for (unsigned i = 0; i < G_N_ELEMENTS(sticker_sql); ++i) { + assert(sticker_sql[i] != NULL); + + sticker_stmt[i] = sticker_prepare(sticker_sql[i], error_r); + if (sticker_stmt[i] == NULL) + return false; + } + + return true; +} + +void +sticker_global_finish(void) +{ + if (sticker_db == NULL) + /* not configured */ + return; + + for (unsigned i = 0; i < G_N_ELEMENTS(sticker_stmt); ++i) { + assert(sticker_stmt[i] != NULL); + + sqlite3_finalize(sticker_stmt[i]); + } + + sqlite3_close(sticker_db); +} + +bool +sticker_enabled(void) +{ + return sticker_db != NULL; +} + +char * +sticker_load_value(const char *type, const char *uri, const char *name) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET]; + int ret; + char *value; + + assert(sticker_enabled()); + assert(type != NULL); + assert(uri != NULL); + assert(name != NULL); + + if (*name == 0) + return NULL; + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return NULL; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return NULL; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return NULL; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret == SQLITE_ROW) { + /* record found */ + value = g_strdup((const char*)sqlite3_column_text(stmt, 0)); + } else if (ret == SQLITE_DONE) { + /* no record found */ + value = NULL; + } else { + /* error */ + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return NULL; + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return value; +} + +static bool +sticker_list_values(GHashTable *hash, const char *type, const char *uri) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST]; + int ret; + char *name, *value; + + assert(hash != NULL); + assert(type != NULL); + assert(uri != NULL); + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + switch (ret) { + case SQLITE_ROW: + name = g_strdup((const char*)sqlite3_column_text(stmt, 0)); + value = g_strdup((const char*)sqlite3_column_text(stmt, 1)); + g_hash_table_insert(hash, name, value); + break; + case SQLITE_DONE: + break; + case SQLITE_BUSY: + /* no op */ + break; + default: + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + } while (ret != SQLITE_DONE); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return true; +} + +static bool +sticker_update_value(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE]; + int ret; + + assert(type != NULL); + assert(uri != NULL); + assert(name != NULL); + assert(*name != 0); + assert(value != NULL); + + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, value, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 4, name, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_changes(sticker_db); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return ret > 0; +} + +static bool +sticker_insert_value(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT]; + int ret; + + assert(type != NULL); + assert(uri != NULL); + assert(name != NULL); + assert(*name != 0); + assert(value != NULL); + + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 4, value, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + + idle_add(IDLE_STICKER); + return true; +} + +bool +sticker_store_value(const char *type, const char *uri, + const char *name, const char *value) +{ + assert(sticker_enabled()); + assert(type != NULL); + assert(uri != NULL); + assert(name != NULL); + assert(value != NULL); + + if (*name == 0) + return false; + + return sticker_update_value(type, uri, name, value) || + sticker_insert_value(type, uri, name, value); +} + +bool +sticker_delete(const char *type, const char *uri) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE]; + int ret; + + assert(sticker_enabled()); + assert(type != NULL); + assert(uri != NULL); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return true; +} + +bool +sticker_delete_value(const char *type, const char *uri, const char *name) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE]; + int ret; + + assert(sticker_enabled()); + assert(type != NULL); + assert(uri != NULL); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_changes(sticker_db); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return ret > 0; +} + +static struct sticker * +sticker_new(void) +{ + struct sticker *sticker = g_new(struct sticker, 1); + + sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + return sticker; +} + +void +sticker_free(struct sticker *sticker) +{ + assert(sticker != NULL); + assert(sticker->table != NULL); + + g_hash_table_destroy(sticker->table); + g_free(sticker); +} + +const char * +sticker_get_value(const struct sticker *sticker, const char *name) +{ + return (const char *)g_hash_table_lookup(sticker->table, name); +} + +struct sticker_foreach_data { + void (*func)(const char *name, const char *value, + gpointer user_data); + gpointer user_data; +}; + +static void +sticker_foreach_func(gpointer key, gpointer value, gpointer user_data) +{ + struct sticker_foreach_data *data = + (struct sticker_foreach_data *)user_data; + + data->func((const char *)key, (const char *)value, data->user_data); +} + +void +sticker_foreach(const struct sticker *sticker, + void (*func)(const char *name, const char *value, + gpointer user_data), + gpointer user_data) +{ + struct sticker_foreach_data data; + data.func = func; + data.user_data = user_data; + + g_hash_table_foreach(sticker->table, sticker_foreach_func, &data); +} + +struct sticker * +sticker_load(const char *type, const char *uri) +{ + struct sticker *sticker = sticker_new(); + bool success; + + success = sticker_list_values(sticker->table, type, uri); + if (!success) { + sticker_free(sticker); + return NULL; + } + + if (g_hash_table_size(sticker->table) == 0) { + /* don't return empty sticker objects */ + sticker_free(sticker); + return NULL; + } + + return sticker; +} + +bool +sticker_find(const char *type, const char *base_uri, const char *name, + void (*func)(const char *uri, const char *value, + gpointer user_data), + gpointer user_data) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND]; + int ret; + + assert(type != NULL); + assert(name != NULL); + assert(func != NULL); + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + if (base_uri == NULL) + base_uri = ""; + + ret = sqlite3_bind_text(stmt, 2, base_uri, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); + if (ret != SQLITE_OK) { + g_warning("sqlite3_bind_text() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + do { + ret = sqlite3_step(stmt); + switch (ret) { + case SQLITE_ROW: + func((const char*)sqlite3_column_text(stmt, 0), + (const char*)sqlite3_column_text(stmt, 1), + user_data); + break; + case SQLITE_DONE: + break; + case SQLITE_BUSY: + /* no op */ + break; + default: + g_warning("sqlite3_step() failed: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + } while (ret != SQLITE_DONE); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return true; +} diff --git a/src/StickerDatabase.hxx b/src/StickerDatabase.hxx new file mode 100644 index 000000000..90ff9b066 --- /dev/null +++ b/src/StickerDatabase.hxx @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * This is the sticker database library. It is the backend of all the + * sticker code in MPD. + * + * "Stickers" are pieces of information attached to existing MPD + * objects (e.g. song files, directories, albums). Clients can create + * arbitrary name/value pairs. MPD itself does not assume any special + * meaning in them. + * + * The goal is to allow clients to share additional (possibly dynamic) + * information about songs, which is neither stored on the client (not + * available to other clients), nor stored in the song files (MPD has + * no write access). + * + * Client developers should create a standard for common sticker + * names, to ensure interoperability. + * + * Examples: song ratings; statistics; deferred tag writes; lyrics; + * ... + * + */ + +#ifndef MPD_STICKER_DATABASE_HXX +#define MPD_STICKER_DATABASE_HXX + +#include "gerror.h" + +struct sticker; + +/** + * Opens the sticker database (if path is not NULL). + * + * @param error_r location to store the error occurring, or NULL to + * ignore errors + * @return true on success, false on error + */ +bool +sticker_global_init(const char *path, GError **error_r); + +/** + * Close the sticker database. + */ +void +sticker_global_finish(void); + +/** + * Returns true if the sticker database is configured and available. + */ +bool +sticker_enabled(void); + +/** + * Returns one value from an object's sticker record. The caller must + * free the return value with g_free(). + */ +char * +sticker_load_value(const char *type, const char *uri, const char *name); + +/** + * Sets a sticker value in the specified object. Overwrites existing + * values. + */ +bool +sticker_store_value(const char *type, const char *uri, + const char *name, const char *value); + +/** + * Deletes a sticker from the database. All sticker values of the + * specified object are deleted. + */ +bool +sticker_delete(const char *type, const char *uri); + +/** + * Deletes a sticker value. Fails if no sticker with this name + * exists. + */ +bool +sticker_delete_value(const char *type, const char *uri, const char *name); + +/** + * Frees resources held by the sticker object. + * + * @param sticker the sticker object to be freed + */ +void +sticker_free(struct sticker *sticker); + +/** + * Determines a single value in a sticker. + * + * @param sticker the sticker object + * @param name the name of the sticker + * @return the sticker value, or NULL if none was found + */ +const char * +sticker_get_value(const struct sticker *sticker, const char *name); + +/** + * Iterates over all sticker items in a sticker. + * + * @param sticker the sticker object + * @param func a callback function + * @param user_data an opaque pointer for the callback function + */ +void +sticker_foreach(const struct sticker *sticker, + void (*func)(const char *name, const char *value, + void *user_data), + void *user_data); + +/** + * Loads the sticker for the specified resource. + * + * @param type the resource type, e.g. "song" + * @param uri the URI of the resource, e.g. the song path + * @return a sticker object, or NULL on error or if there is no sticker + */ +struct sticker * +sticker_load(const char *type, const char *uri); + +/** + * Finds stickers with the specified name below the specified URI. + * + * @param type the resource type, e.g. "song" + * @param base_uri the URI prefix of the resources, or NULL if all + * resources should be searched + * @param name the name of the sticker + * @return true on success (even if no sticker was found), false on + * failure + */ +bool +sticker_find(const char *type, const char *base_uri, const char *name, + void (*func)(const char *uri, const char *value, + void *user_data), + void *user_data); + +#endif diff --git a/src/StickerPrint.cxx b/src/StickerPrint.cxx new file mode 100644 index 000000000..6099f728d --- /dev/null +++ b/src/StickerPrint.cxx @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "StickerPrint.hxx" +#include "StickerDatabase.hxx" + +extern "C" { +#include "client.h" +} + +void +sticker_print_value(struct client *client, + const char *name, const char *value) +{ + client_printf(client, "sticker: %s=%s\n", name, value); +} + +static void +print_sticker_cb(const char *name, const char *value, void *data) +{ + struct client *client = (struct client *)data; + + sticker_print_value(client, name, value); +} + +void +sticker_print(struct client *client, const struct sticker *sticker) +{ + sticker_foreach(sticker, print_sticker_cb, client); +} diff --git a/src/StickerPrint.hxx b/src/StickerPrint.hxx new file mode 100644 index 000000000..99573d237 --- /dev/null +++ b/src/StickerPrint.hxx @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_STICKER_PRINT_HXX +#define MPD_STICKER_PRINT_HXX + +struct sticker; +struct client; + +/** + * Sends one sticker value to the client. + */ +void +sticker_print_value(struct client *client, + const char *name, const char *value); + +/** + * Sends all sticker values to the client. + */ +void +sticker_print(struct client *client, const struct sticker *sticker); + +#endif diff --git a/src/UpdateRemove.cxx b/src/UpdateRemove.cxx index 71852ea96..c88eec42a 100644 --- a/src/UpdateRemove.cxx +++ b/src/UpdateRemove.cxx @@ -29,10 +29,8 @@ extern "C" { #include "Main.hxx" #ifdef ENABLE_SQLITE -extern "C" { -#include "sticker.h" -#include "song_sticker.h" -} +#include "StickerDatabase.hxx" +#include "SongSticker.hxx" #endif #include diff --git a/src/song_sticker.c b/src/song_sticker.c deleted file mode 100644 index 78025906e..000000000 --- a/src/song_sticker.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "song_sticker.h" -#include "song.h" -#include "directory.h" -#include "sticker.h" - -#include - -#include -#include - -char * -sticker_song_get_value(const struct song *song, const char *name) -{ - char *uri, *value; - - assert(song != NULL); - assert(song_in_database(song)); - - uri = song_get_uri(song); - value = sticker_load_value("song", uri, name); - g_free(uri); - - return value; -} - -bool -sticker_song_set_value(const struct song *song, - const char *name, const char *value) -{ - char *uri; - bool ret; - - assert(song != NULL); - assert(song_in_database(song)); - - uri = song_get_uri(song); - ret = sticker_store_value("song", uri, name, value); - g_free(uri); - - return ret; -} - -bool -sticker_song_delete(const struct song *song) -{ - char *uri; - bool ret; - - assert(song != NULL); - assert(song_in_database(song)); - - uri = song_get_uri(song); - ret = sticker_delete("song", uri); - g_free(uri); - - return ret; -} - -bool -sticker_song_delete_value(const struct song *song, const char *name) -{ - char *uri; - bool success; - - assert(song != NULL); - assert(song_in_database(song)); - - uri = song_get_uri(song); - success = sticker_delete_value("song", uri, name); - g_free(uri); - - return success; -} - -struct sticker * -sticker_song_get(const struct song *song) -{ - char *uri; - struct sticker *sticker; - - assert(song != NULL); - assert(song_in_database(song)); - - uri = song_get_uri(song); - sticker = sticker_load("song", uri); - g_free(uri); - - return sticker; -} - -struct sticker_song_find_data { - struct directory *directory; - const char *base_uri; - size_t base_uri_length; - - void (*func)(struct song *song, const char *value, - gpointer user_data); - gpointer user_data; -}; - -static void -sticker_song_find_cb(const char *uri, const char *value, gpointer user_data) -{ - struct sticker_song_find_data *data = user_data; - struct song *song; - - if (memcmp(uri, data->base_uri, data->base_uri_length) != 0) - /* should not happen, ignore silently */ - return; - - song = directory_lookup_song(data->directory, - uri + data->base_uri_length); - if (song != NULL) - data->func(song, value, data->user_data); -} - -bool -sticker_song_find(struct directory *directory, const char *name, - void (*func)(struct song *song, const char *value, - gpointer user_data), - gpointer user_data) -{ - struct sticker_song_find_data data = { - .directory = directory, - .func = func, - .user_data = user_data, - }; - char *allocated; - bool success; - - data.base_uri = directory_get_path(directory); - if (*data.base_uri != 0) - /* append slash to base_uri */ - data.base_uri = allocated = - g_strconcat(data.base_uri, "/", NULL); - else - /* searching in root directory - no trailing slash */ - allocated = NULL; - - data.base_uri_length = strlen(data.base_uri); - - success = sticker_find("song", data.base_uri, name, - sticker_song_find_cb, &data); - g_free(allocated); - - return success; -} diff --git a/src/song_sticker.h b/src/song_sticker.h deleted file mode 100644 index 20ae68ce9..000000000 --- a/src/song_sticker.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef SONG_STICKER_H -#define SONG_STICKER_H - -#include -#include - -struct song; -struct directory; -struct sticker; - -/** - * Returns one value from a song's sticker record. The caller must - * free the return value with g_free(). - */ -char * -sticker_song_get_value(const struct song *song, const char *name); - -/** - * Sets a sticker value in the specified song. Overwrites existing - * values. - */ -bool -sticker_song_set_value(const struct song *song, - const char *name, const char *value); - -/** - * Deletes a sticker from the database. All values are deleted. - */ -bool -sticker_song_delete(const struct song *song); - -/** - * Deletes a sticker value. Does nothing if the sticker did not - * exist. - */ -bool -sticker_song_delete_value(const struct song *song, const char *name); - -/** - * Loads the sticker for the specified song. - * - * @param song the song object - * @return a sticker object, or NULL on error or if there is no sticker - */ -struct sticker * -sticker_song_get(const struct song *song); - -/** - * Finds stickers with the specified name below the specified - * directory. - * - * Caller must lock the #db_mutex. - * - * @param directory the base directory to search in - * @param name the name of the sticker - * @return true on success (even if no sticker was found), false on - * failure - */ -bool -sticker_song_find(struct directory *directory, const char *name, - void (*func)(struct song *song, const char *value, - gpointer user_data), - gpointer user_data); - -#endif diff --git a/src/sticker.c b/src/sticker.c deleted file mode 100644 index 346a827a5..000000000 --- a/src/sticker.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "sticker.h" -#include "idle.h" - -#include -#include -#include - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "sticker" - -#if SQLITE_VERSION_NUMBER < 3003009 -#define sqlite3_prepare_v2 sqlite3_prepare -#endif - -struct sticker { - GHashTable *table; -}; - -enum sticker_sql { - STICKER_SQL_GET, - STICKER_SQL_LIST, - STICKER_SQL_UPDATE, - STICKER_SQL_INSERT, - STICKER_SQL_DELETE, - STICKER_SQL_DELETE_VALUE, - STICKER_SQL_FIND, -}; - -static const char *const sticker_sql[] = { - [STICKER_SQL_GET] = - "SELECT value FROM sticker WHERE type=? AND uri=? AND name=?", - [STICKER_SQL_LIST] = - "SELECT name,value FROM sticker WHERE type=? AND uri=?", - [STICKER_SQL_UPDATE] = - "UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?", - [STICKER_SQL_INSERT] = - "INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)", - [STICKER_SQL_DELETE] = - "DELETE FROM sticker WHERE type=? AND uri=?", - [STICKER_SQL_DELETE_VALUE] = - "DELETE FROM sticker WHERE type=? AND uri=? AND name=?", - [STICKER_SQL_FIND] = - "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?", -}; - -static const char sticker_sql_create[] = - "CREATE TABLE IF NOT EXISTS sticker(" - " type VARCHAR NOT NULL, " - " uri VARCHAR NOT NULL, " - " name VARCHAR NOT NULL, " - " value VARCHAR NOT NULL" - ");" - "CREATE UNIQUE INDEX IF NOT EXISTS" - " sticker_value ON sticker(type, uri, name);" - ""; - -static sqlite3 *sticker_db; -static sqlite3_stmt *sticker_stmt[G_N_ELEMENTS(sticker_sql)]; - -static GQuark -sticker_quark(void) -{ - return g_quark_from_static_string("sticker"); -} - -static sqlite3_stmt * -sticker_prepare(const char *sql, GError **error_r) -{ - int ret; - sqlite3_stmt *stmt; - - ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL); - if (ret != SQLITE_OK) { - g_set_error(error_r, sticker_quark(), ret, - "sqlite3_prepare_v2() failed: %s", - sqlite3_errmsg(sticker_db)); - return NULL; - } - - return stmt; -} - -bool -sticker_global_init(const char *path, GError **error_r) -{ - int ret; - - if (path == NULL) - /* not configured */ - return true; - - /* open/create the sqlite database */ - - ret = sqlite3_open(path, &sticker_db); - if (ret != SQLITE_OK) { - g_set_error(error_r, sticker_quark(), ret, - "Failed to open sqlite database '%s': %s", - path, sqlite3_errmsg(sticker_db)); - return false; - } - - /* create the table and index */ - - ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL); - if (ret != SQLITE_OK) { - g_set_error(error_r, sticker_quark(), ret, - "Failed to create sticker table: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - /* prepare the statements we're going to use */ - - for (unsigned i = 0; i < G_N_ELEMENTS(sticker_sql); ++i) { - assert(sticker_sql[i] != NULL); - - sticker_stmt[i] = sticker_prepare(sticker_sql[i], error_r); - if (sticker_stmt[i] == NULL) - return false; - } - - return true; -} - -void -sticker_global_finish(void) -{ - if (sticker_db == NULL) - /* not configured */ - return; - - for (unsigned i = 0; i < G_N_ELEMENTS(sticker_stmt); ++i) { - assert(sticker_stmt[i] != NULL); - - sqlite3_finalize(sticker_stmt[i]); - } - - sqlite3_close(sticker_db); -} - -bool -sticker_enabled(void) -{ - return sticker_db != NULL; -} - -char * -sticker_load_value(const char *type, const char *uri, const char *name) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET]; - int ret; - char *value; - - assert(sticker_enabled()); - assert(type != NULL); - assert(uri != NULL); - assert(name != NULL); - - if (*name == 0) - return NULL; - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return NULL; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return NULL; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return NULL; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret == SQLITE_ROW) { - /* record found */ - value = g_strdup((const char*)sqlite3_column_text(stmt, 0)); - } else if (ret == SQLITE_DONE) { - /* no record found */ - value = NULL; - } else { - /* error */ - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return NULL; - } - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - return value; -} - -static bool -sticker_list_values(GHashTable *hash, const char *type, const char *uri) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST]; - int ret; - char *name, *value; - - assert(hash != NULL); - assert(type != NULL); - assert(uri != NULL); - assert(sticker_enabled()); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - switch (ret) { - case SQLITE_ROW: - name = g_strdup((const char*)sqlite3_column_text(stmt, 0)); - value = g_strdup((const char*)sqlite3_column_text(stmt, 1)); - g_hash_table_insert(hash, name, value); - break; - case SQLITE_DONE: - break; - case SQLITE_BUSY: - /* no op */ - break; - default: - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - } while (ret != SQLITE_DONE); - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - return true; -} - -static bool -sticker_update_value(const char *type, const char *uri, - const char *name, const char *value) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE]; - int ret; - - assert(type != NULL); - assert(uri != NULL); - assert(name != NULL); - assert(*name != 0); - assert(value != NULL); - - assert(sticker_enabled()); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, value, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 3, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 4, name, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_changes(sticker_db); - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - idle_add(IDLE_STICKER); - return ret > 0; -} - -static bool -sticker_insert_value(const char *type, const char *uri, - const char *name, const char *value) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT]; - int ret; - - assert(type != NULL); - assert(uri != NULL); - assert(name != NULL); - assert(*name != 0); - assert(value != NULL); - - assert(sticker_enabled()); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 4, value, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - - idle_add(IDLE_STICKER); - return true; -} - -bool -sticker_store_value(const char *type, const char *uri, - const char *name, const char *value) -{ - assert(sticker_enabled()); - assert(type != NULL); - assert(uri != NULL); - assert(name != NULL); - assert(value != NULL); - - if (*name == 0) - return false; - - return sticker_update_value(type, uri, name, value) || - sticker_insert_value(type, uri, name, value); -} - -bool -sticker_delete(const char *type, const char *uri) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE]; - int ret; - - assert(sticker_enabled()); - assert(type != NULL); - assert(uri != NULL); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - idle_add(IDLE_STICKER); - return true; -} - -bool -sticker_delete_value(const char *type, const char *uri, const char *name) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE]; - int ret; - - assert(sticker_enabled()); - assert(type != NULL); - assert(uri != NULL); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_changes(sticker_db); - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - idle_add(IDLE_STICKER); - return ret > 0; -} - -static struct sticker * -sticker_new(void) -{ - struct sticker *sticker = g_new(struct sticker, 1); - - sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - return sticker; -} - -void -sticker_free(struct sticker *sticker) -{ - assert(sticker != NULL); - assert(sticker->table != NULL); - - g_hash_table_destroy(sticker->table); - g_free(sticker); -} - -const char * -sticker_get_value(const struct sticker *sticker, const char *name) -{ - return g_hash_table_lookup(sticker->table, name); -} - -struct sticker_foreach_data { - void (*func)(const char *name, const char *value, - gpointer user_data); - gpointer user_data; -}; - -static void -sticker_foreach_func(gpointer key, gpointer value, gpointer user_data) -{ - struct sticker_foreach_data *data = user_data; - - data->func(key, value, data->user_data); -} - -void -sticker_foreach(const struct sticker *sticker, - void (*func)(const char *name, const char *value, - gpointer user_data), - gpointer user_data) -{ - struct sticker_foreach_data data = { - .func = func, - .user_data = user_data, - }; - - g_hash_table_foreach(sticker->table, sticker_foreach_func, &data); -} - -struct sticker * -sticker_load(const char *type, const char *uri) -{ - struct sticker *sticker = sticker_new(); - bool success; - - success = sticker_list_values(sticker->table, type, uri); - if (!success) { - sticker_free(sticker); - return NULL; - } - - if (g_hash_table_size(sticker->table) == 0) { - /* don't return empty sticker objects */ - sticker_free(sticker); - return NULL; - } - - return sticker; -} - -bool -sticker_find(const char *type, const char *base_uri, const char *name, - void (*func)(const char *uri, const char *value, - gpointer user_data), - gpointer user_data) -{ - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND]; - int ret; - - assert(type != NULL); - assert(name != NULL); - assert(func != NULL); - assert(sticker_enabled()); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - if (base_uri == NULL) - base_uri = ""; - - ret = sqlite3_bind_text(stmt, 2, base_uri, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, NULL); - if (ret != SQLITE_OK) { - g_warning("sqlite3_bind_text() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - - do { - ret = sqlite3_step(stmt); - switch (ret) { - case SQLITE_ROW: - func((const char*)sqlite3_column_text(stmt, 0), - (const char*)sqlite3_column_text(stmt, 1), - user_data); - break; - case SQLITE_DONE: - break; - case SQLITE_BUSY: - /* no op */ - break; - default: - g_warning("sqlite3_step() failed: %s", - sqlite3_errmsg(sticker_db)); - return false; - } - } while (ret != SQLITE_DONE); - - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); - - return true; -} diff --git a/src/sticker.h b/src/sticker.h deleted file mode 100644 index 66f12294b..000000000 --- a/src/sticker.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * This is the sticker database library. It is the backend of all the - * sticker code in MPD. - * - * "Stickers" are pieces of information attached to existing MPD - * objects (e.g. song files, directories, albums). Clients can create - * arbitrary name/value pairs. MPD itself does not assume any special - * meaning in them. - * - * The goal is to allow clients to share additional (possibly dynamic) - * information about songs, which is neither stored on the client (not - * available to other clients), nor stored in the song files (MPD has - * no write access). - * - * Client developers should create a standard for common sticker - * names, to ensure interoperability. - * - * Examples: song ratings; statistics; deferred tag writes; lyrics; - * ... - * - */ - -#ifndef STICKER_H -#define STICKER_H - -#include "gerror.h" - -#include - -struct sticker; - -/** - * Opens the sticker database (if path is not NULL). - * - * @param error_r location to store the error occurring, or NULL to - * ignore errors - * @return true on success, false on error - */ -bool -sticker_global_init(const char *path, GError **error_r); - -/** - * Close the sticker database. - */ -void -sticker_global_finish(void); - -/** - * Returns true if the sticker database is configured and available. - */ -bool -sticker_enabled(void); - -/** - * Returns one value from an object's sticker record. The caller must - * free the return value with g_free(). - */ -char * -sticker_load_value(const char *type, const char *uri, const char *name); - -/** - * Sets a sticker value in the specified object. Overwrites existing - * values. - */ -bool -sticker_store_value(const char *type, const char *uri, - const char *name, const char *value); - -/** - * Deletes a sticker from the database. All sticker values of the - * specified object are deleted. - */ -bool -sticker_delete(const char *type, const char *uri); - -/** - * Deletes a sticker value. Fails if no sticker with this name - * exists. - */ -bool -sticker_delete_value(const char *type, const char *uri, const char *name); - -/** - * Frees resources held by the sticker object. - * - * @param sticker the sticker object to be freed - */ -void -sticker_free(struct sticker *sticker); - -/** - * Determines a single value in a sticker. - * - * @param sticker the sticker object - * @param name the name of the sticker - * @return the sticker value, or NULL if none was found - */ -const char * -sticker_get_value(const struct sticker *sticker, const char *name); - -/** - * Iterates over all sticker items in a sticker. - * - * @param sticker the sticker object - * @param func a callback function - * @param user_data an opaque pointer for the callback function - */ -void -sticker_foreach(const struct sticker *sticker, - void (*func)(const char *name, const char *value, - void *user_data), - void *user_data); - -/** - * Loads the sticker for the specified resource. - * - * @param type the resource type, e.g. "song" - * @param uri the URI of the resource, e.g. the song path - * @return a sticker object, or NULL on error or if there is no sticker - */ -struct sticker * -sticker_load(const char *type, const char *uri); - -/** - * Finds stickers with the specified name below the specified URI. - * - * @param type the resource type, e.g. "song" - * @param base_uri the URI prefix of the resources, or NULL if all - * resources should be searched - * @param name the name of the sticker - * @return true on success (even if no sticker was found), false on - * failure - */ -bool -sticker_find(const char *type, const char *base_uri, const char *name, - void (*func)(const char *uri, const char *value, - void *user_data), - void *user_data); - -#endif diff --git a/src/sticker_print.c b/src/sticker_print.c deleted file mode 100644 index b19dcdc9c..000000000 --- a/src/sticker_print.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "sticker_print.h" -#include "sticker.h" -#include "client.h" - -void -sticker_print_value(struct client *client, - const char *name, const char *value) -{ - client_printf(client, "sticker: %s=%s\n", name, value); -} - -static void -print_sticker_cb(const char *name, const char *value, void *data) -{ - struct client *client = data; - - sticker_print_value(client, name, value); -} - -void -sticker_print(struct client *client, const struct sticker *sticker) -{ - sticker_foreach(sticker, print_sticker_cb, client); -} diff --git a/src/sticker_print.h b/src/sticker_print.h deleted file mode 100644 index 7398c8083..000000000 --- a/src/sticker_print.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_STICKER_PRINT_H -#define MPD_STICKER_PRINT_H - -struct sticker; -struct client; - -/** - * Sends one sticker value to the client. - */ -void -sticker_print_value(struct client *client, - const char *name, const char *value); - -/** - * Sends all sticker values to the client. - */ -void -sticker_print(struct client *client, const struct sticker *sticker); - -#endif -- cgit v1.2.3