diff --git a/doc/lektord.1 b/doc/lektord.1 index 13ca581972f64ea79bd2c4a2e07b50f22b18f5eb..66245cf2900548f5b488318b667df9bdbb787b76 100644 --- a/doc/lektord.1 +++ b/doc/lektord.1 @@ -9,18 +9,19 @@ You may be interested in other related programs like: .PP .PD 0 .TP -\fIkaradata\fP Metadata manipulation around matroska files for lektor +\fIlktadm\fP Administration of lektor from a separate executable .TP -\fIlkinfo\fP Metadata printing with lektor compatible matroska files +\fIlkt\fP A standard client for lektord .PD .SH "DESCRIPTION" .PP Lektor allows one to interactivelly and remotly interact with the player. The -lektor daemon is mpd compatible. +lektor daemon is mpd compatible, even if some functionnalities may differ. +You can almost use \fBmpc\fP with lektord. .SH "AUTHOR" Lektor is a karaoke player for POSIX compliant systems (i.e. not MS-Windows) writen initially in C. Some may call it Lektor mk 7. It was writen by Hubert -\'Taiite' HIRTZ and Maël 'Kubat' MARTIN. +\'Taiite' HIRTZ, Maël 'Kubat' MARTIN and Louis 'Elliu' GOYARD. .SH "AVAILABILITY" I don't know what to do in this section. .PP @@ -37,6 +38,20 @@ Just see one of the dev from lektor or a bakateux. .TP \fBhttps://manga.iiens.net\fP The home website for the Bakaclub .PD +.SH "INSTALLATION" +You may install lektor from source like this: +.sp +.RS 4 +.nf +\fB +% sudo mkdir /home/kara && sudo chown USER:USER /home/kara +% git clone https://git.iiens.net/martin2018/lektor && cd lektor +% mkdir build && meson build && cd build && ninja # Compilation +% sudo meson install --no-rebuild # Install +% sudo chown USER:USER /home/kara/kara.db # Or you will have some problems +.fi \fR +.P +.RE .SH "INVOCATION" To simply launch the daemon do not set any flag. The following flags are interpreted by the shell when invoked to determine where the shell will read @@ -45,36 +60,32 @@ commands from: .PD 0 .TP .PD -\fB\-\-init\fP -Write default config files on the FS. May create folder if they doesn't exist. -If a configuration is already present it may be overwriten. -.TP -\fB\-\-populate\fP -Read the configuration and populate the sqlite3 database with the found -compatible matroska video files from the FS. +\fB\-\-help\fP / \fB\-h\fP +Prints the help message. .TP \fB\-\-version\fP / \fB\-v\fP Print the version of the lektor daemon. .PP -If the lektor daemon is launched without configuration the behaviour is not -determined. When installing from scratch the following commands need to be -launched \fBin order\fP: +If the lektor daemon is launched without a configuration file, it will exit +with the code \fB1\fP. To init the default configuration file (normally done +by meson during the install), you should just use: .sp .RS 4 .nf \fB -% lektord --init -% karadata --init -% lektord --populate +% mkdir ~/.config/lektor/ +% lktadm init conf > ~/.config/lektor/lektor.ini % lektord .fi \fR .P .RE .PP -When downloading karas directly from \fIkurisu\fP the \fBkaradata --init\fP -should not be needed. +You should edit the configuration file to set correct path to \fB.so\fP file, +those files are modules for the window of lektord. .SH "SUPPORTED URIS" -When searching the database, the following URIs are supported by lektor: +When searching the database, the following URIs are supported by lektor. Those +URIs are hidden, you should not use them, but here is some documentation about +them. .PP .PD 0 .TP @@ -121,21 +132,15 @@ The supported categories in lektor are: \fBvo\fP, \fBva\fP, \fBcdg\fP, .SH "FILES" .PD 0 .TP -\fB$HOME/.config/lektor/config\fP general config file +\fB$HOME/.config/lektor/lektor.ini\fP general config file for the user .TP \fB/home/kara/\fP default prefix for the bakabase .TP \fB/home/kara/kara.db\fP default sqlite3 base for the bakabase -.TP -\fB$HOME/.local/libexec/lektor_info.csh\fP script used to get informations about a running instance of lektor -.TP -\fB$HOME/.local/libexec/lektor_info_multiline.csh\fP same, but with multiline output -.TP -\fB$HOME/.local/etc/lektor_host\fP the file storing the host to look to for the lektord running instance .PD .SH "SEE ALSO" -\fIkaradata\fP(1), -\fIlkinfo\fP(1), +\fIlktadm\fP(1), +\fIlkt\fP(1), \fImpc\fP(1), \fImpd\fP(1) .PP diff --git a/inc/common/common.h b/inc/common/common.h index 09d87d2d509af17bb1e02fa8f0ea9ab8a80dd553..010317a55701bac4c098445ba77458d04ac06c93 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -5,3 +5,8 @@ #define not_implemented() __not_implemented(__func__,__FILE__,__LINE__) extern void __not_implemented(const char *func, char *file, int line); + +#define UNUSED(...) __unused((void *) __VA_ARGS__) +void __unused(void *, ...); + +long get_mtime(const char *path); diff --git a/inc/lektor/bufferfd.h b/inc/lektor/bufferfd.h index 97bc0237268efdc33f5757032c108be91187dc35..c6e4ccbc826bcc4608bc1f67618aa2bff3c36f71 100644 --- a/inc/lektor/bufferfd.h +++ b/inc/lektor/bufferfd.h @@ -23,7 +23,7 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <stdint.h> #include <stdlib.h> diff --git a/inc/lektor/cmd.h b/inc/lektor/cmd.h index 3ac6c3ad47698fa9996cce8320b3462ce945bc5e..de18772650d1e335c7fdfc9e1aa937a4ee2e555e 100644 --- a/inc/lektor/cmd.h +++ b/inc/lektor/cmd.h @@ -1,6 +1,5 @@ #pragma once -#include <lektor/define.h> #include <stdnoreturn.h> #include <stddef.h> diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h index 9e029011cb6b3c482da6393a6b2cea5ff9d7678d..8ea3e82fbb321e50a1f17a144296a5949fb4f52c 100644 --- a/inc/lektor/commands.h +++ b/inc/lektor/commands.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <lektor/net.h> #include <lektor/window.h> @@ -15,50 +15,43 @@ /* Querying lektor's status */ bool command_currentsong(struct lkt_state *srv, size_t c); -bool command_status(struct lkt_state *srv, size_t c); +bool command_status (struct lkt_state *srv, size_t c); /* Controlling playback */ -bool command_next(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); -bool command_pause(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); -bool command_previous(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); -bool command_play(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], - enum mpd_idle_flag *watch_mask_ptr); -bool command_stop(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); +bool command_next (sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); +bool command_pause (sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); +bool command_previous(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); +bool command_play (sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_stop (sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr); bool command_set_pos(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_ptr, int index); /* The queue */ -bool command_add(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], - enum mpd_idle_flag *watch_mask_ptr); -bool command_addid(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], - enum mpd_idle_flag *watch_mask_ptr); -bool command_del(sqlite3 *db, struct lkt_win *win, char *pos_range, enum mpd_idle_flag *watch_mask_ptr); -bool command_delid(sqlite3 *db, struct lkt_win *win, char *id, enum mpd_idle_flag *watch_mask_ptr); -bool command_clear(sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); -bool command_crop(sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); -bool command_move(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); -bool command_shuffle(sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); +bool command_add (sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_addid (sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_del (sqlite3 *db, struct lkt_win *win, char *pos_range, enum mpd_idle_flag *watch_mask_ptr); +bool command_delid (sqlite3 *db, struct lkt_win *win, char *id, enum mpd_idle_flag *watch_mask_ptr); +bool command_clear (sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); +bool command_crop (sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); +bool command_move (sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_shuffle(sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr); +bool command_playid (sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); + bool command_queue_list(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX]); -bool command_playid(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], - enum mpd_idle_flag *watch_mask_ptr); -bool command_queue_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], - long continuation); +bool command_queue_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], long continuation); /* The playlists */ bool command_plt_create(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); bool command_plt_remove(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); -bool command_plt_clear(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_plt_clear (sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); bool command_plt_rename(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); bool command_plt_export(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); bool command_plt_import(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); -bool command_plt_add(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); +bool command_plt_add (sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr); /* The help */ bool command_help(struct lkt_state *srv, size_t c); -/* Update the database */ -bool command_update(struct lkt_state *srv, enum mpd_idle_flag *watch_mask_ptr); - /* Respond to the idle command */ bool command_idle(struct lkt_state *srv, size_t c, struct lkt_command *cmd); @@ -74,8 +67,7 @@ enum lkt_find_action { }; /* Find and send karas in the db that match the search expression */ -bool command_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], - long continuation, enum lkt_find_action action); +bool command_find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], long continuation, enum lkt_find_action action); /* Set options for the lektor such as `random`, `single`, `repeat`, etc */ enum lkt_playback_option { @@ -87,20 +79,20 @@ enum lkt_playback_option { lkt_playback_option_volume, }; -bool command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_option opt, - char *args[LKT_MESSAGE_ARGS_MAX]); +bool command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_option opt, char *args[LKT_MESSAGE_ARGS_MAX]); /* Authentificate users */ -bool command_password(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_password(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); bool command_user_add(struct lkt_state *srv, size_t c, sqlite3 *db, char *argv[LKT_MESSAGE_ARGS_MAX]); /* Program management control */ bool command_restart(struct lkt_state *srv, size_t c); -bool command_kill(struct lkt_state *srv, size_t c); -bool command_rescan(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_kill (struct lkt_state *srv, size_t c); +bool command_update (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_rescan (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); /* Sticker management */ -bool command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); -bool command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_sticker_get (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_sticker_set (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); bool command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); -bool command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); +bool command_sticker_list (struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]); diff --git a/inc/lektor/common.h b/inc/lektor/common.h new file mode 100644 index 0000000000000000000000000000000000000000..3495f2519ccaadb965b05807dab896e78c3056c5 --- /dev/null +++ b/inc/lektor/common.h @@ -0,0 +1,130 @@ +#pragma once + +#include <common/macro.h> +#include <common/define.h> +#include <stdint.h> +#include <stdlib.h> + +/* Defines */ + +#define URL_MAX_LEN 1024 +#define DEFAULT_URL "https://kurisu.iiens.net" +#define GET_ID_JSON DEFAULT_URL "/api_karas.php?id=%ld" +#define GET_ID_FILE DEFAULT_URL "/download.php?id=%ld" + +#define SELF_EXECUTABLE_LINUX "/proc/self/exe" +#define SELF_EXECUTABLE_FREEBSD "/proc/curproc/file" +#define SELF_EXECUTABLE_SOLARIS "/proc/self/path/a.out" + + +#define LKT_MAX_SQLITE_STATEMENT 1024 +#define PROTECTED_DATABASE "disk" + +#define LKT_DATABASE_NAME_KID "id" +#define LKT_DATABASE_NAME_KNAME "source_name" +#define LKT_DATABASE_NAME_KTITLE "song_title" +#define LKT_DATABASE_NAME_KCAT "category" +#define LKT_DATABASE_NAME_KTYPE "song_type" +#define LKT_DATABASE_NAME_KAUTHOR "author_name" +#define LKT_DATABASE_NAME_KAUTHOR_YEAR "author_year" +#define LKT_DATABASE_NAME_KLANG "language" +#define LKT_DATABASE_KARA_COLUMNT_ANY "any_col" +#define LKT_DATABASE_KARA_ALL "string" + +#define LEKTOR_TAG_MAX 256 +#define LKT_MESSAGE_ARGS_MAX 32 +#define LKT_MESSAGE_MAX 2048 +#define LKT_DEFAULT_LIST_SIZE 10 + +enum mpd_idle_flag { + MPD_IDLE_NONE = 0, + + MPD_IDLE_DATABASE = ( 1 << 1 ), + MPD_IDLE_UPDATE = ( 1 << 2 ), + MPD_IDLE_STORED_PLAYLIST = ( 1 << 3 ), + MPD_IDLE_PLAYLIST = ( 1 << 4 ), + MPD_IDLE_PLAYER = ( 1 << 5 ), + MPD_IDLE_MIXER = ( 1 << 6 ), + MPD_IDLE_OUTPUT = ( 1 << 7 ), + MPD_IDLE_OPTIONS = ( 1 << 8 ), + MPD_IDLE_PARTITION = ( 1 << 9 ), + MPD_IDLE_STICKER = ( 1 << 10 ), + MPD_IDLE_SUBSCRIPTION = ( 1 << 11 ), + MPD_IDLE_MESSAGE = ( 1 << 12 ), + + MPD_IDLE_ALL = + MPD_IDLE_DATABASE | MPD_IDLE_UPDATE | MPD_IDLE_STORED_PLAYLIST | + MPD_IDLE_PLAYLIST | MPD_IDLE_PLAYER | MPD_IDLE_MIXER | + MPD_IDLE_OUTPUT | MPD_IDLE_OPTIONS | MPD_IDLE_PARTITION | + MPD_IDLE_STICKER | MPD_IDLE_SUBSCRIPTION | MPD_IDLE_MESSAGE, +}; + +#define LKT_BACKLOG 32 +#define COMMAND_LIST_MAX 64 +#define BUFFER_OUT_MAX 16 +#define MPD_VERSION "0.21.16" + +/* Macros */ + +#define SQLITE_PREPARE(db, stmt, SQL, goto_label) \ + if (sqlite3_prepare_v2((sqlite3 *) db, SQL, -1, &(stmt), 0) != SQLITE_OK) { \ + LOG_ERROR_SCT("DB", "Failed to prepare statement: %s", \ + sqlite3_errmsg((sqlite3 *) db)); \ + goto goto_label; \ + } + +#define SQLITE_EXEC(db, SQL, goto_label) \ + if (sqlite3_exec((sqlite3 *) db, SQL, NULL, NULL, NULL) != SQLITE_OK) { \ + LOG_ERROR_SCT("DB", "Failed to exec statement: %s", \ + sqlite3_errmsg((sqlite3 *) db)); \ + goto goto_label; \ + } + +#define SQLITE_BIND_TEXT(db, stmt, pos, text, error) \ + if (sqlite3_bind_text(stmt, pos, text, -1, 0) != SQLITE_OK) { \ + LOG_ERROR_SCT("DB", "Failed to bind text %s at pos %d: %s", \ + text, pos, sqlite3_errmsg((sqlite3 *) db)); \ + goto error; \ + } + +#define SQLITE_BIND_INT(db, stmt, pos, integer, error) \ + if (sqlite3_bind_int(stmt, pos, integer) != SQLITE_OK) { \ + LOG_ERROR_SCT("DB", "Failed to bind int %d at pos %d: %s", \ + integer, pos, sqlite3_errmsg((sqlite3 *) db)); \ + goto error; \ + } + +#define SQLITE_STEP(db, stmt, code, error) \ + if (sqlite3_step(stmt) != code) { \ + LOG_ERROR_SCT("DB", "Failed to step: %s", \ + sqlite3_errmsg((sqlite3 *) db)); \ + goto error; \ + } + +#define SQLITE_STEP_ROW(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_ROW, error) +#define SQLITE_STEP_DONE(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_DONE, error) +#define SQLITE_STEP_OK(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_OK, error) + +#define SQLITE_DO_ROLLBACK(db) \ + sqlite3_exec((sqlite3 *) db, "ROLLBACK TRANSACTION;\n", NULL, NULL, NULL); + +/* Random things */ + +/* Read `bytes` as the big endian representation of a 32-bit unsigned integer. + `n` is the number of bytes in `bytes` + Returns 0 if n is 0. + Restriction: n <= 4 */ +uint32_t be_uint32_t(const uint8_t bytes[], size_t n); + +/* Same as `be_uint32_t` but for 64-bit unsigned integers. + Restriction: n <= 8 */ +uint64_t be_uint64_t(const uint8_t bytes[], size_t n); + +/* Trim the string `str` of the char `c` from the right and the left. + The string may not be null terminated. + Returns a pointer to the the trimmed string + Restrictions: the len of the string must be `len`. */ +char *trim(char *str, size_t len, char c); + +/* Get a line from stdin safely. */ +int get_stdin_line(const char *prompt, char *buf, size_t len); diff --git a/inc/lektor/config.h b/inc/lektor/config.h index fc013160204b3dd3f45f7fc5ff0aa9789a38da8a..618de57c06aacfaf1cb041d704032f1e7dbe7441 100644 --- a/inc/lektor/config.h +++ b/inc/lektor/config.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <stddef.h> #include <sqlite3.h> diff --git a/inc/lektor/database.h b/inc/lektor/database.h index 3ec3f63d3cb119d57f6a03b60b7cb23132aa7866..f74e2a16332743df3e584b1da30aa229c5bad011 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <lektor/mkv.h> #include <lektor/uri.h> @@ -22,57 +22,62 @@ struct lkt_queue_state { int length; }; +long database_get_timestamp(volatile sqlite3 *db); +void database_stamp(volatile sqlite3 *db); +void database_updated(volatile sqlite3 *db); + /* Open correctly a database for lektor. */ -bool database_new(sqlite3 **db); -bool database_open(sqlite3 *db, const char *dbpath); +bool database_new (sqlite3 **db); +bool database_init (const char *dbpath); +bool database_open (sqlite3 *db, const char *dbpath); bool database_attach(sqlite3 *db, const char *name, const char *dbpath); bool database_detach(sqlite3 *db, const char *name); /* Get information on the queue and currently playing kara. */ -bool database_queue_state(sqlite3 *db, struct lkt_queue_state *res); +bool database_queue_state (sqlite3 *db, struct lkt_queue_state *res); bool database_queue_current_kara(sqlite3 *db, struct kara_metadata *res, int *id); -bool database_queue_set_paused(sqlite3 *db, bool paused); +bool database_queue_set_paused (sqlite3 *db, bool paused); bool database_queue_set_current_index(sqlite3 *db, int idx); -bool database_queue_get_current_file(sqlite3 *db, char filepath[PATH_MAX]); +bool database_queue_get_current_file (sqlite3 *db, char filepath[PATH_MAX]); /* Mpv and database synchronisation. */ bool database_sync_mpv_state(sqlite3 *db, mpv_handle **mpv); /* Update the database. */ -bool database_update(sqlite3 *db, const char *kara_dir); -bool database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, - bool avail); +bool database_update(volatile sqlite3 *db, const char *kara_dir); +bool database_update_add(volatile sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail); +bool database_update_set_available(volatile sqlite3 *db, uint64_t id); /* Control the content of the queue. */ -bool database_queue_add_id(sqlite3 *db, int id, int priority); -bool database_queue_add_query(sqlite3 *db, const char *query, int priority); -bool database_queue_add_author(sqlite3 *db, const char *author, int priority); -bool database_queue_add_language(sqlite3 *db, const char *author, int priority); -bool database_queue_add_category(sqlite3 *db, const char *author, int priority); -bool database_queue_add_type(sqlite3 *db, const char *author, int priority); -bool database_queue_add_plt(sqlite3 *db, const char *plt_name, int priority); - -bool database_queue_insert_query(sqlite3 *db, const char *query, int pos); -bool database_queue_insert_id(sqlite3 *db, int id); -bool database_queue_insert_author(sqlite3 *db, const char *author); +bool database_queue_add_id (sqlite3 *db, int id, int priority); +bool database_queue_add_query (sqlite3 *db, const char *query, int priority); +bool database_queue_add_author (sqlite3 *db, const char *author, int priority); +bool database_queue_add_language(sqlite3 *db, const char *author, int priority); +bool database_queue_add_category(sqlite3 *db, const char *author, int priority); +bool database_queue_add_type (sqlite3 *db, const char *author, int priority); +bool database_queue_add_plt (sqlite3 *db, const char *plt_name, int priority); + +bool database_queue_insert_query (sqlite3 *db, const char *query, int pos); +bool database_queue_insert_id (sqlite3 *db, int id); +bool database_queue_insert_author (sqlite3 *db, const char *author); bool database_queue_insert_language(sqlite3 *db, const char *author); bool database_queue_insert_category(sqlite3 *db, const char *author); -bool database_queue_insert_type(sqlite3 *db, const char *author); -bool database_queue_insert_plt(sqlite3 *db, const char *plt_name); +bool database_queue_insert_type (sqlite3 *db, const char *author); +bool database_queue_insert_plt (sqlite3 *db, const char *plt_name); -bool database_queue_del_id(sqlite3 *db, int id); +bool database_queue_del_id (sqlite3 *db, int id); bool database_queue_del_pos(sqlite3 *db, int pos); -bool database_queue_clear(sqlite3 *db); -bool database_queue_crop(sqlite3 *db); -bool database_queue_move(sqlite3 *db, int from, int to); +bool database_queue_clear (sqlite3 *db); +bool database_queue_crop (sqlite3 *db); +bool database_queue_move (sqlite3 *db, int from, int to); bool database_queue_shuffle(sqlite3 *db); -bool database_queue_seekid(sqlite3 *db, int id, int *out_pos); +bool database_queue_seekid (sqlite3 *db, int id, int *out_pos); /* Control the playing state of the queue. */ bool database_queue_toggle_pause(sqlite3 *db); -bool database_queue_play(sqlite3 *db, int pos); -bool database_queue_stop(sqlite3 *db); +bool database_queue_play (sqlite3 *db, int pos); +bool database_queue_stop (sqlite3 *db); /* A search callback to be called after each matched row */ struct lkt_callback { @@ -105,7 +110,7 @@ typedef bool (*lkt_search_database_func)(struct lkt_state *srv, size_t c, int id const char *row); bool database_search_database_init(sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); -bool database_search_queue_init(sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); +bool database_search_queue_init (sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret); bool database_search_iter(struct lkt_search *item); /* Next and prev operation on the queue. */ @@ -113,11 +118,11 @@ bool database_queue_next(sqlite3 *db, char filepath[PATH_MAX]); bool database_queue_prev(sqlite3 *db, char filepath[PATH_MAX]); /* Set a value in the config table */ -bool database_config_set(sqlite3 *db, const char *section, const char *key, const char *value); +bool database_config_set (sqlite3 *db, const char *section, const char *key, const char *value); bool database_config_get_text(sqlite3 *db, const char *section, const char *key, char *value, size_t len); -bool database_config_get_int(sqlite3 *db, const char *section, const char *key, int *value); -bool database_config_exists(sqlite3 *db, const char *section, const char *key); -bool database_config_queue(sqlite3 *db, const char *option, int value); +bool database_config_get_int (sqlite3 *db, const char *section, const char *key, int *value); +bool database_config_exists (sqlite3 *db, const char *section, const char *key); +bool database_config_queue (sqlite3 *db, const char *option, int value); bool database_config_queue_default(sqlite3 *db); /* Get a value from the config table */ @@ -131,16 +136,16 @@ struct lkt_playlist_metadata { }; /* Control playlists */ -bool database_plt_create(sqlite3 *db, const char *name); -bool database_plt_remove(sqlite3 *db, const char *name); +bool database_plt_create (sqlite3 *db, const char *name); +bool database_plt_remove (sqlite3 *db, const char *name); bool database_plt_remove_pos(sqlite3 *db, const char *name, int pos); -bool database_plt_clear(sqlite3 *db, const char *name); -bool database_plt_rename(sqlite3 *db, const char *old_name, const char *new_name); +bool database_plt_clear (sqlite3 *db, const char *name); +bool database_plt_rename (sqlite3 *db, const char *old_name, const char *new_name); bool database_plt_export(sqlite3 *db, const char *name); bool database_plt_import(sqlite3 *db, const char *name); -bool database_plt_add_uri(sqlite3 *db, const char *name, struct lkt_uri_t *uri); +bool database_plt_add_uri(sqlite3 *db, const char *name, struct lkt_uri *uri); /* User control, yeah, MPD authentification sucks. */ bool database_user_authentificate(sqlite3 *db, const char *password); @@ -155,10 +160,9 @@ struct sticker_callback { bool (*call)(void *args, const char *sticker, const char *type, int uri, int value); }; -bool database_sticker_create(sqlite3 *db, const char *name); -bool database_sticker_delete(sqlite3 *db, const char *name); +bool database_sticker_create (sqlite3 *db, const char *name); +bool database_sticker_delete (sqlite3 *db, const char *name); bool database_sticker_delete_specify(sqlite3 *sb, const char *type, int uri, const char *name); -bool database_sticker_list(sqlite3 *db, const char *type, struct sticker_callback *call); -bool database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, int value); -bool database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, - struct sticker_callback *call); +bool database_sticker_list (sqlite3 *db, const char *type, struct sticker_callback *call); +bool database_sticker_set (sqlite3 *db, const char *type, const char *name, int uri, int value); +bool database_sticker_get (sqlite3 *db, const char *type, const char *name, int uri, struct sticker_callback *call); diff --git a/inc/lektor/define.h b/inc/lektor/define.h deleted file mode 100644 index d68144f42a19e9dae0c1378489aa92e005361c8e..0000000000000000000000000000000000000000 --- a/inc/lektor/define.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <common/define.h> - -#define LKT_MAX_SQLITE_STATEMENT 1024 -#define PROTECTED_DATABASE "disk" - -#define LKT_DATABASE_NAME_KID "id" -#define LKT_DATABASE_NAME_KNAME "source_name" -#define LKT_DATABASE_NAME_KTITLE "song_title" -#define LKT_DATABASE_NAME_KCAT "song_type" -#define LKT_DATABASE_NAME_KTYPE "category" -#define LKT_DATABASE_NAME_KAUTHOR "author_name" -#define LKT_DATABASE_NAME_KAUTHOR_YEAR "author_year" -#define LKT_DATABASE_NAME_KLANG "language" -#define LKT_DATABASE_KARA_COLUMNT_ANY "any_col" -#define LKT_DATABASE_KARA_ALL "string" - -#define LEKTOR_TAG_MAX 256 -#define LKT_MESSAGE_ARGS_MAX 32 -#define LKT_MESSAGE_MAX 2048 -#define LKT_DEFAULT_LIST_SIZE 10 - -enum mpd_idle_flag { - MPD_IDLE_NONE = 0, - - MPD_IDLE_DATABASE = ( 1 << 1 ), - MPD_IDLE_UPDATE = ( 1 << 2 ), - MPD_IDLE_STORED_PLAYLIST = ( 1 << 3 ), - MPD_IDLE_PLAYLIST = ( 1 << 4 ), - MPD_IDLE_PLAYER = ( 1 << 5 ), - MPD_IDLE_MIXER = ( 1 << 6 ), - MPD_IDLE_OUTPUT = ( 1 << 7 ), - MPD_IDLE_OPTIONS = ( 1 << 8 ), - MPD_IDLE_PARTITION = ( 1 << 9 ), - MPD_IDLE_STICKER = ( 1 << 10 ), - MPD_IDLE_SUBSCRIPTION = ( 1 << 11 ), - MPD_IDLE_MESSAGE = ( 1 << 12 ), - - MPD_IDLE_ALL = - MPD_IDLE_DATABASE | MPD_IDLE_UPDATE | MPD_IDLE_STORED_PLAYLIST | - MPD_IDLE_PLAYLIST | MPD_IDLE_PLAYER | MPD_IDLE_MIXER | - MPD_IDLE_OUTPUT | MPD_IDLE_OPTIONS | MPD_IDLE_PARTITION | - MPD_IDLE_STICKER | MPD_IDLE_SUBSCRIPTION | MPD_IDLE_MESSAGE, -}; - -#define LKT_BACKLOG 32 -#define COMMAND_LIST_MAX 64 -#define BUFFER_OUT_MAX 16 -#define MPD_VERSION "0.21.16" - diff --git a/inc/lektor/macro.h b/inc/lektor/macro.h deleted file mode 100644 index 48c66716fec43acae1c01471611bcd0c95a87113..0000000000000000000000000000000000000000 --- a/inc/lektor/macro.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include <common/macro.h> - -#define SQLITE_PREPARE(db, stmt, SQL, goto_label) \ - if (sqlite3_prepare_v2(db, SQL, -1, &(stmt), 0) != SQLITE_OK) { \ - LOG_ERROR_SCT("DB", "Failed to prepare statement: %s", \ - sqlite3_errmsg(db)); \ - goto goto_label; \ - } - -#define SQLITE_EXEC(db, SQL, goto_label) \ - if (sqlite3_exec(db, SQL, NULL, NULL, NULL) != SQLITE_OK) { \ - LOG_ERROR_SCT("DB", "Failed to exec statement: %s", \ - sqlite3_errmsg(db)); \ - goto goto_label; \ - } - -#define SQLITE_BIND_TEXT(db, stmt, pos, text, error) \ - if (sqlite3_bind_text(stmt, pos, text, -1, 0) != SQLITE_OK) { \ - LOG_ERROR_SCT("DB", "Failed to bind text %s at pos %d: %s", \ - text, pos, sqlite3_errmsg(db)); \ - goto error; \ - } - -#define SQLITE_BIND_INT(db, stmt, pos, integer, error) \ - if (sqlite3_bind_int(stmt, pos, integer) != SQLITE_OK) { \ - LOG_ERROR_SCT("DB", "Failed to bind int %d at pos %d: %s", \ - integer, pos, sqlite3_errmsg(db)); \ - goto error; \ - } - -#define SQLITE_STEP(db, stmt, code, error) \ - if (sqlite3_step(stmt) != code) { \ - LOG_ERROR_SCT("DB", "Failed to step and get a row: %s", \ - sqlite3_errmsg(db)); \ - goto error; \ - } - -#define SQLITE_STEP_ROW(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_ROW, error) -#define SQLITE_STEP_DONE(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_DONE, error) -#define SQLITE_STEP_OK(db, stmt, error) SQLITE_STEP(db, stmt, SQLITE_OK, error) - -#define SQLITE_DO_ROLLBACK(db) \ - sqlite3_exec(db, "ROLLBACK TRANSACTION;\n", NULL, NULL, NULL); diff --git a/inc/lektor/mkv.h b/inc/lektor/mkv.h index 681315934b96e0b0b5557ef0dbaaeea50d20f71b..c88114269b99f7087f27a031160f77922faf0cff 100644 --- a/inc/lektor/mkv.h +++ b/inc/lektor/mkv.h @@ -23,7 +23,7 @@ */ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <linux/limits.h> #include <stddef.h> @@ -56,8 +56,7 @@ struct kara { int kara_metadata_read(struct kara_metadata *dst, const char *filename); /* Write metadata to a mkv file */ -int kara_metadata_write(struct kara_metadata *mdt, const char *filename, - const char *mkvpropedit); +int kara_metadata_write(struct kara_metadata *mdt, const char *filename, const char *mkvpropedit); /* Set the metadata for the file according to its path. */ int metadata_set_file(char *karapath, const char *mkvpropedit); diff --git a/inc/lektor/module/module_sdl2.h b/inc/lektor/module/module_sdl2.h index e4a2dd2c8c602baa453b69c8e20233f6e9cbd1e1..90eacc578aa4d769643c33c59e9eff5ff847fd22 100644 --- a/inc/lektor/module/module_sdl2.h +++ b/inc/lektor/module/module_sdl2.h @@ -1,6 +1,5 @@ #pragma once -#include <lektor/define.h> #include <stdbool.h> #include <sqlite3.h> #include <lektor/window.h> @@ -16,6 +15,7 @@ int module_set_function(void *mod, void *handle); bool module_sdl2_new(struct lkt_win *const win); void module_sdl2_close(struct lkt_win *const win); void module_sdl2_free(struct lkt_win *const win); +void module_sdl2_attach(struct lkt_win *const win, void *server); bool module_sdl2_toggle_pause(struct lkt_win *const win); bool module_sdl2_load_file(struct lkt_win *const win, const char *filepath); diff --git a/inc/lektor/module/module_x11.h b/inc/lektor/module/module_x11.h index 880452463c30b72ac7f1e6405a91abf92a6df7ba..0ea84737be6fed8b92296da2d9dc3b46483ed552 100644 --- a/inc/lektor/module/module_x11.h +++ b/inc/lektor/module/module_x11.h @@ -1,6 +1,5 @@ #pragma once -#include <lektor/define.h> #include <stdbool.h> #include <sqlite3.h> #include <lektor/window.h> @@ -16,6 +15,7 @@ int module_set_function(void *mod, void *handle); bool module_x11_new(struct lkt_win *const win); void module_x11_close(struct lkt_win *const win); void module_x11_free(struct lkt_win *const win); +void module_x11_attach(struct lkt_win *const win, void *server); bool module_x11_toggle_pause(struct lkt_win *const win); bool module_x11_load_file(struct lkt_win *const win, const char *filepath); diff --git a/inc/lektor/net.h b/inc/lektor/net.h index 20caa955aaee2f512405b8f5d9458824073df24a..f6a7f4afc8349ccb53c491ee70d69697a7ffbe8c 100644 --- a/inc/lektor/net.h +++ b/inc/lektor/net.h @@ -1,10 +1,19 @@ #pragma once -#include <lektor/define.h> +#include <lektor/mkv.h> +#include <lektor/common.h> #include <lektor/config.h> #include <lektor/window.h> +#include <lektor/thread.h> +#include <lektor/uri.h> + #include <sqlite3.h> #include <mpv/client.h> +#include <curl/curl.h> +#include <sqlite3.h> +#include <json-c/json.h> +#include <inttypes.h> +#include <stdbool.h> #include <stddef.h> @@ -14,11 +23,34 @@ struct lkt_command { long cont; }; +/* Repository, e.g. Kurisu */ +struct lkt_repo { + /* Just the repo */ + const char *name; + const char *base_url; + const char *kara_dir; + const char *get_all_json; + const char *get_id_json; + const char *get_id_file; + const uint64_t version; + + /* The database */ + volatile sqlite3 *db; +}; + +int repo_new (struct lkt_repo *const repo, const char *name, const char *url, volatile sqlite3 *db); +void repo_free(struct lkt_repo *const repo); + +int repo_get_id (struct lkt_repo *const repo, const uint64_t id, struct kara_metadata *mdt); +int repo_update (struct lkt_repo *const repo); +int repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt); + /* Create and destruct a command structure. */ struct lkt_command lkt_command_parse(char *raw); struct lkt_command lkt_command_clone(struct lkt_command *c); void lkt_command_free(struct lkt_command *c); +/* Messages that are send and recieved */ struct lkt_message { size_t ref_count; char data[LKT_MESSAGE_MAX]; @@ -36,10 +68,12 @@ struct lkt_state { size_t fds_len; size_t fds_max; - sqlite3 *db; + volatile sqlite3 *db; const char *kara_prefix; - enum mpd_idle_flag mpd_idle_events; + struct lkt_repo repo; + + volatile enum mpd_idle_flag mpd_idle_events; struct lkt_win win; }; @@ -50,7 +84,7 @@ void lkt_state_send(struct lkt_state *srv, size_t c, struct lkt_message *msg); /* Get and set continuation state for a client. */ size_t lkt_remaining_msg(struct lkt_state *srv, size_t c); -int lkt_get_continuation(struct lkt_state *srv, size_t c); +int lkt_get_continuation(struct lkt_state *srv, size_t c); void lkt_set_continuation(struct lkt_state *srv, size_t c, int i); /* Get the mask to watch for events for a given client, to be used with the `idle` command. @@ -62,3 +96,4 @@ bool lkt_client_auth(struct lkt_state *srv, size_t c, bool set_auth); /* The server's listen function. */ int lkt_listen(void); + diff --git a/inc/lektor/repo.h b/inc/lektor/repo.h deleted file mode 100644 index 11b127e9cad0febc66484c4b30392a6fc90590a9..0000000000000000000000000000000000000000 --- a/inc/lektor/repo.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include <lektor/define.h> -#include <lektor/mkv.h> -#include <lektor/thread.h> -#include <curl/curl.h> -#include <sqlite3.h> -#include <json-c/json.h> -#include <inttypes.h> -#include <stdbool.h> - -struct lkt_repo { - const char *name; - const char *base_url; - const char *kara_dir; - const char *get_all_json; - const char *get_id_json; - const char *get_id_file; - const uint64_t version; -}; - -int repo_new(struct lkt_repo *const repo, const char *name, const char *url); -void repo_free(struct lkt_repo *const repo); - -/* Only one possible repo thread is authorized. */ -int repo_new_thread(struct lkt_repo *const repo); -int repo_join_thread(void); - -/* Get metadata of a kara. */ -int repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata *mdt); -int repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json); -int repo_get_allid_async(void); - -/* Download a kara. */ -int repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t id, - const char *kara_path, struct kara_metadata *mdt_ret); -int repo_download_id_async(const size_t id); -int repo_get_kara_async(struct kara **downloaded); diff --git a/inc/lektor/thread.h b/inc/lektor/thread.h index dcd3d75c23ede39f42f314ea098e04c1bb1a812b..3e5e07a7ec809eedcda4642e8ac47233f2fd4e75 100644 --- a/inc/lektor/thread.h +++ b/inc/lektor/thread.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <pthread.h> #include <sys/types.h> @@ -28,18 +28,17 @@ struct poller_thread_arg { /* Create a new thread. Returns 0 on success, a non zero value on error. */ -int lkt_th_new(struct poller_thread *th, unsigned int init_sizes, - void *(*func)(struct poller_thread_arg *), void *args); +int poller_new(struct poller_thread *th, unsigned int init_sizes, void *(*func)(struct poller_thread_arg *), void *args); /* Joins a thread. Returns 0 on success, a non zero value on error. */ -int lkt_th_join(struct poller_thread *th, void **ret); +int poller_join(struct poller_thread *th, void **ret); /* Input qeueu manipulations. */ -int lkt_th_append_input(struct poller_thread *th, void *ptr); -int lkt_th_pop_input(struct poller_thread *th, void **ptr); -int lkt_th_head_input(struct poller_thread *th, void **ptr); +int poller_append_input(struct poller_thread *th, void *ptr); +int poller_pop_input (struct poller_thread *th, void **ptr); +int poller_head_input (struct poller_thread *th, void **ptr); /* Output qeueu manipulations. */ -int lkt_th_append_output(struct poller_thread *th, void *ptr); -int lkt_th_pop_output(struct poller_thread *th, void **ptr); -int lkt_th_head_output(struct poller_thread *th, void **ptr); +int poller_append_output(struct poller_thread *th, void *ptr); +int poller_pop_output (struct poller_thread *th, void **ptr); +int poller_head_output (struct poller_thread *th, void **ptr); diff --git a/inc/lektor/uri.h b/inc/lektor/uri.h index ea2d76631c7e5990d02bc5c43856e16d30486496..c9a4d932566e8432b932bcbe676d69803d6cab41 100644 --- a/inc/lektor/uri.h +++ b/inc/lektor/uri.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <stddef.h> #include <stddef.h> #include <stdbool.h> @@ -16,7 +16,7 @@ enum lkt_uri_type { uri_query, }; -struct lkt_uri_t { +struct lkt_uri { enum lkt_uri_type type; void *value; @@ -24,5 +24,9 @@ struct lkt_uri_t { }; /* Create and delete URIs */ -bool lkt_uri_from(struct lkt_uri_t *ret, char *const str); -void lkt_uri_free(struct lkt_uri_t *ret); +bool lkt_uri_from(struct lkt_uri *ret, char *const str); +void lkt_uri_free(struct lkt_uri *ret); + +/* Make an URL to download from kurisu. + base_url is the prefix for the api, e.g. https://kurisu.iiens.net/api_kara.php */ +int lkt_uri_to_url(struct lkt_uri *uri, const char *base_url, char *ret, size_t len); diff --git a/inc/lektor/utils.h b/inc/lektor/utils.h deleted file mode 100644 index aa175cc8f40d0410a82acdd9677487e98a7edfb3..0000000000000000000000000000000000000000 --- a/inc/lektor/utils.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Random things. */ -#pragma once - -#include <lektor/define.h> -#include <stdint.h> -#include <stdlib.h> - -/* Read `bytes` as the big endian representation of a 32-bit unsigned integer. - `n` is the number of bytes in `bytes` - Returns 0 if n is 0. - Restriction: n <= 4 */ -uint32_t be_uint32_t(const uint8_t bytes[], size_t n); - -/* Same as `be_uint32_t` but for 64-bit unsigned integers. - Restriction: n <= 8 */ -uint64_t be_uint64_t(const uint8_t bytes[], size_t n); - -/* Trim the string `str` of the char `c` from the right and the left. - The string may not be null terminated. - Returns a pointer to the the trimmed string - Restrictions: the len of the string must be `len`. */ -char *trim(char *str, size_t len, char c); - -/* Get a line from stdin safely. */ -int get_stdin_line(const char *prompt, char *buf, size_t len); diff --git a/inc/lektor/window.h b/inc/lektor/window.h index 35d87fbccbab09cdc17f8537d9b87527dc7b0975..624c05c8284414c2f9b00e2c9ce570d7a9a4b281 100644 --- a/inc/lektor/window.h +++ b/inc/lektor/window.h @@ -1,6 +1,6 @@ #pragma once -#include <lektor/define.h> +#include <lektor/common.h> #include <stdbool.h> #include <sqlite3.h> @@ -15,9 +15,10 @@ struct lkt_win { void *handle; /* the libdl handle */ /* Create and free the window */ - bool (*new)(struct lkt_win *win); /* Create a window or only the mpv context */ - void (*close)(struct lkt_win *win); /* Close the mpv context, not the window. */ - void (*free)(struct lkt_win *win); /* Entirelly liberate all the resources */ + bool (*new)(struct lkt_win *win); /* Create a window or only the mpv context */ + void (*close)(struct lkt_win *win); /* Close the mpv context, not the window */ + void (*free)(struct lkt_win *win); /* Entirelly liberate all the resources */ + void (*attach)(struct lkt_win *win, void *server); /* Get properties from the struct lkt_state */ /* Playback control */ bool (*toggle_pause)(struct lkt_win *win); @@ -25,7 +26,6 @@ struct lkt_win { bool (*set_volume)(struct lkt_win *win, int vol); /* Get playback properties */ - // bool (*is_paused)(struct lkt_win *win, bool *ret); bool (*get_duration)(struct lkt_win *win, int *dur_sec); bool (*get_elapsed)(struct lkt_win *win, int *elapsed_sec); diff --git a/inc/mthread/mthread.h b/inc/mthread/mthread.h index c708a4414d54bce351aaa564ee4a5b4da46c9f2d..869e0ddd4c3b0ecfc0b1e4eaca697a5a5761046a 100644 --- a/inc/mthread/mthread.h +++ b/inc/mthread/mthread.h @@ -9,8 +9,11 @@ typedef struct mthread_list_s mthread_list_t; struct mthread_s; typedef struct mthread_s *mthread_t; -struct mthread_attr_s; -typedef struct mthread_attr_s mthread_attr_t; +typedef enum { + ATTR_NONE = 0, + ATTR_DETACHED = (1 << 1), + ATTR_DETACHED_FREE = (1 << 2), +} mthread_attr_t; struct mthread_mutex_s { volatile int nb_thread; @@ -45,38 +48,35 @@ typedef struct mthread_sem_s mthread_sem_t; /* Function for handling threads. */ -int mthread_create(mthread_t *__threadp, const mthread_attr_t *__attr, void *(*__start_routine) (void *), - void *__arg); +extern int mthread_create(mthread_t *__threadp, const mthread_attr_t __attr, void *(*__start_routine) (void *), void *__arg); -mthread_t mthread_self(void); -int mthread_equal(mthread_t __thread1, mthread_t __thread2); -void mthread_exit(void *__retval); -int mthread_join(mthread_t __th, void **__thread_return); +extern mthread_t mthread_self(void); +extern int mthread_equal (mthread_t __thread1, mthread_t __thread2); +extern void mthread_exit (void *__retval); +extern int mthread_join (mthread_t __th, void **__thread_return); /* Functions for mutex handling. */ -int mthread_mutex_init (mthread_mutex_t *__mutex, - const mthread_mutexattr_t *__mutex_attr); -int mthread_mutex_destroy(mthread_mutex_t *__mutex); -int mthread_mutex_trylock(mthread_mutex_t *__mutex); -int mthread_mutex_lock(mthread_mutex_t *__mutex); -int mthread_mutex_unlock(mthread_mutex_t *__mutex); +extern int mthread_mutex_init (mthread_mutex_t *__mutex, const mthread_mutexattr_t *__mutex_attr); +extern int mthread_mutex_destroy(mthread_mutex_t *__mutex); +extern int mthread_mutex_trylock(mthread_mutex_t *__mutex); +extern int mthread_mutex_lock (mthread_mutex_t *__mutex); +extern int mthread_mutex_unlock (mthread_mutex_t *__mutex); /* Functions for handling conditional variables. */ -int mthread_cond_init(mthread_cond_t *__cond, - const mthread_condattr_t *__cond_attr); -int mthread_cond_destroy(mthread_cond_t *__cond); -int mthread_cond_signal(mthread_cond_t *__cond); -int mthread_cond_broadcast(mthread_cond_t *__cond); -int mthread_cond_wait(mthread_cond_t *__cond, mthread_mutex_t *__mutex); +extern int mthread_cond_init (mthread_cond_t *__cond, const mthread_condattr_t *__cond_attr); +extern int mthread_cond_destroy (mthread_cond_t *__cond); +extern int mthread_cond_signal (mthread_cond_t *__cond); +extern int mthread_cond_broadcast(mthread_cond_t *__cond); +extern int mthread_cond_wait (mthread_cond_t *__cond, mthread_mutex_t *__mutex); /* Functions for handling thread-specific data. */ -int mthread_key_create(mthread_key_t *__key, void (*__destr_function) (void *)); -int mthread_key_delete(mthread_key_t __key); -int mthread_setspecific(mthread_key_t __key, const void *__pointer); -void *mthread_getspecific(mthread_key_t __key); +extern int mthread_key_create (mthread_key_t *__key, void (*__destr_function) (void *)); +extern int mthread_key_delete (mthread_key_t __key); +extern int mthread_setspecific (mthread_key_t __key, const void *__pointer); +extern void *mthread_getspecific(mthread_key_t __key); /* Functions for handling initialization. */ @@ -88,22 +88,21 @@ void *mthread_getspecific(mthread_key_t __key); The initialization functions might throw exception which is why this function is not marked with . */ -int mthread_once(mthread_once_t *__once_control, void (*__init_routine) (void)); +extern int mthread_once(mthread_once_t *__once_control, void (*__init_routine) (void)); /* Functions for handling semaphore. */ -int mthread_sem_init(mthread_sem_t *sem, unsigned int value); -int mthread_sem_wait(mthread_sem_t *sem); /* P(sem), wait(sem) */ -int mthread_sem_post(mthread_sem_t *sem); /* V(sem), signal(sem) */ +extern int mthread_sem_init(mthread_sem_t *sem, unsigned int value); +extern int mthread_sem_wait(mthread_sem_t *sem); /* P(sem), wait(sem) */ +extern int mthread_sem_post(mthread_sem_t *sem); /* V(sem), signal(sem) */ -int mthread_sem_getvalue(mthread_sem_t *sem, int *sval); -int mthread_sem_trywait(mthread_sem_t *sem); +extern int mthread_sem_getvalue(mthread_sem_t *sem, int *sval); +extern int mthread_sem_trywait (mthread_sem_t *sem); +extern int mthread_sem_destroy (mthread_sem_t *sem); /* undo sem_init() */ -int mthread_sem_destroy(mthread_sem_t *sem); /* undo sem_init() */ - -void mthread_yield(); +extern void mthread_yield(); /* Initialize mthread. */ -void mthread_init(void); -void *mthread_main(void *arg); +extern void mthread_init(void); +extern void *mthread_main(void *arg); diff --git a/inc/mthread/mthread_internal.h b/inc/mthread/mthread_internal.h index c4eb661d17602bc96fd4bb81395e9176ae039a26..0462d0cf9ea8a4139e3178279770d6941a23a504 100644 --- a/inc/mthread/mthread_internal.h +++ b/inc/mthread/mthread_internal.h @@ -29,6 +29,7 @@ typedef struct { } mthread_virtual_processor_t; typedef enum { RUNNING, BLOCKED, ZOMBIE } mthread_status_t; +typedef volatile enum { JOINABLE = 0, DETACHED = 1, DETACHED_FREE = 2 } mthread_detached_flag_t; #define INIT_KEYS_LIST 48 @@ -45,6 +46,7 @@ struct mthread_s { void *(*__start_routine) (void *); volatile struct mthread_s *next; volatile mthread_status_t status; + mthread_detached_flag_t detached; int not_migrable; mthread_virtual_processor_t *vp; void *stack; @@ -53,17 +55,17 @@ struct mthread_s { #define MTHREAD_LIST_INIT { .first = NULL, .last = NULL, .lock = 0 } -int mthread_test_and_set(mthread_tst_t *atomic); -void mthread_spinlock_lock(mthread_tst_t *atomic); +int mthread_test_and_set (mthread_tst_t *atomic); +void mthread_spinlock_lock (mthread_tst_t *atomic); void mthread_spinlock_unlock(mthread_tst_t *atomic); -int mthread_get_vp_rank(); +int mthread_get_vp_rank(); void *safe_malloc(size_t size); void mthread_insert_first(struct mthread_s *item, mthread_list_t *list); -void mthread_insert_last(struct mthread_s *item, mthread_list_t *list); +void mthread_insert_last (struct mthread_s *item, mthread_list_t *list); +int mthread_list_test (struct mthread_s *item, mthread_list_t *list); struct mthread_s *mthread_remove_first(mthread_list_t *list); -int mthread_list_test(struct mthread_s *item, mthread_list_t *list); void __mthread_yield(mthread_virtual_processor_t *vp); mthread_virtual_processor_t *mthread_get_vp(); diff --git a/meson.build b/meson.build index f0ab5baa0a7cd7152eab37b7b0fabbc9b72ff50d..0e421b7bb22354e6a359dacc415542c66d8aa7b6 100644 --- a/meson.build +++ b/meson.build @@ -14,9 +14,9 @@ project( 'lektor' ) libdl = meson.get_compiler('c').find_library('dl') -dep_x11 = dependency('x11', required : false) -dep_mpv = dependency('mpv', required : false) -dep_sdl = dependency('sdl2', required : false) +dep_x11 = dependency('x11', required: false) +dep_mpv = dependency('mpv', required: false) +dep_sdl = dependency('sdl2', required: false) ## Get architecture archi = run_command('uname', '-p').stdout().strip() @@ -26,8 +26,8 @@ endif if archi == 'unknown' archi = run_command('arch').stdout().strip() endif -add_global_arguments('-D' + archi + '_ARCH', language : 'c') -add_global_arguments('-D_REENTRANT', language : 'c') +add_global_arguments('-D' + archi + '_ARCH', language: 'c') +add_global_arguments('-D_REENTRANT', language: 'c') ## Sources for mthread mthread_sources = [ 'src/mthread/mthread.c' @@ -56,12 +56,10 @@ core_sources = [ 'src/mkv/bufferfd.c' , 'src/net/command.c' , 'src/net/listen.c' , 'src/net/message.c' + , 'src/net/downloader.c' , 'src/config.c' - , 'src/utils.c' , 'src/uri.c' , 'src/ini/ini.c' - , 'src/repo/curl.c' - , 'src/repo/async.c' , 'src/thread.c' ] @@ -69,78 +67,77 @@ core_sources = [ 'src/mkv/bufferfd.c' includes = include_directories('inc') # Server -core_deps = [ dependency('sqlite3', version : '>= 3.31.0') +core_deps = [ dependency('sqlite3', version: '>= 3.31.0') , dependency('libcurl') , dependency('json-c') - , dependency('threads', required : true) + , dependency('threads', required: true) ] common_deps = [ declare_dependency( link_with: static_library( 'common' , files('src/common.c') - , include_directories : includes ) + , include_directories: includes ) , include_directories: includes ) ] mthread_deps = [ declare_dependency( link_with: static_library( 'mthread' , files(mthread_sources) - , include_directories : includes ) + , include_directories: includes ) , include_directories: includes - , dependencies : [ common_deps ] ) ] + , dependencies: [ common_deps ] ) ] -lib = static_library( meson.project_name() +initsql_deps = [ declare_dependency( link_with: static_library( 'initsql' + , [ custom_target('init.c' + , output: 'sql_init.c' + , input: 'scripts/init.sql' + , command: [ find_program('xxd'), '-i', '@INPUT@', '@OUTPUT@']) ] ) ) ] + +lib = static_library( 'lektor' , files(core_sources) - , include_directories : includes - , dependencies : [ core_deps, libdl, common_deps ] - ) + , include_directories: includes + , dependencies: [ core_deps, libdl, common_deps, mthread_deps, initsql_deps ] ) bin_deps = [ declare_dependency( link_with: lib, include_directories: includes) ] srv = executable( meson.project_name() + 'd' , files('src/main/server.c') - , include_directories : includes - , dependencies : [ bin_deps, mthread_deps ] - , install : true - ) + , include_directories: includes + , dependencies: [ bin_deps ] + , install: true ) # Admin executable -metadata = executable( 'lktadm' - , files('src/main/lktadm.c', 'src/cmd.c') - , include_directories : includes - , dependencies : bin_deps - , install : true - ) +lktadm = executable( 'lktadm' + , files('src/main/lktadm.c', 'src/cmd.c') + , include_directories : includes + , dependencies: [ bin_deps ] + , install: true ) # Client executable lkt = executable( 'lkt' - , files('src/main/lkt.c', 'src/cmd.c') - , include_directories : includes - , install : true - ) + , files('src/main/lkt.c', 'src/cmd.c', 'src/common.c') + , include_directories: includes + , install: true ) # X11 window module if dep_x11.found() and dep_mpv.found() lib_mod_x11 = shared_library ( '_module_x11' , files(['src/module/module_x11.c', 'src/module/mpv.c']) - , include_directories : includes - , dependencies : [ dep_x11, dep_mpv, mthread_deps ] - , link_with : lib - , install : true - , install_dir : 'lib/lektor' - ) + , include_directories: includes + , dependencies: [ dep_x11, dep_mpv ] + , link_with: lib + , install: true + , install_dir: 'lib/lektor' ) endif # SQL2 window module if dep_sdl.found() and dep_mpv.found() lib_mod_sdl = shared_library ( '_module_sdl2' , files(['src/module/module_sdl2.c', 'src/module/mpv.c']) - , include_directories : includes - , dependencies : [ dep_sdl, dep_mpv, mthread_deps ] - , link_with : lib - , install : true - , install_dir : 'lib/lektor' - ) + , include_directories: includes + , dependencies: [ dep_sdl, dep_mpv ] + , link_with: lib + , install: true + , install_dir: 'lib/lektor' ) endif # Install -install_data('scripts/init.sql', install_dir : 'share/lektor') install_man('doc/lektord.1') meson.add_install_script('scripts/install.sh') diff --git a/scripts/astyle.sh b/scripts/astyle.sh index 234f53306ce89f4bae644ad4f84e57ad3b1cdd05..0a4a45c565660898d023d54c4bab3e9fec5fb02d 100755 --- a/scripts/astyle.sh +++ b/scripts/astyle.sh @@ -10,7 +10,7 @@ OPTIONS=' --break-return-type --convert-tabs --close-templates - --max-code-length=110 + --max-code-length=140 --mode=c --lineend=linux --quiet' diff --git a/scripts/init.sql b/scripts/init.sql index 3f36cb0a701e9e062c57390e4acc7a741ac4c835..21bffe9af33706a6d66816515760044f95c59789 100644 --- a/scripts/init.sql +++ b/scripts/init.sql @@ -20,9 +20,9 @@ CREATE TABLE IF NOT EXISTS kara , author_year INTEGER CHECK(author_year > 0) , available INTEGER CHECK(available = 0 OR available = 1) DEFAULT 1 NOT NULL , string TEXT GENERATED ALWAYS AS - ( song_type || ' - ' || language || ' / ' || source_name || ' - ' || category || + ( category || ' - ' || language || ' / ' || source_name || ' - ' || song_type || song_number || ' - ' || song_name || ' [ ' || author_name || ' ]' || - CASE WHEN available = 0 THEN '(U)' ELSE '' END + CASE WHEN available = 0 THEN ' (U)' ELSE '' END ) STORED ); @@ -32,7 +32,8 @@ CREATE TABLE IF NOT EXISTS kara_type ); INSERT OR REPLACE INTO kara_type (id, name) VALUES - (1, 'vo'), (2, 'va'), (3, 'amv'), (4, 'cdg'), (5, 'autres'), ( 6, 'vocaloid'); + (1, 'ED/d*'), (2, 'OP/d*'), (3, 'AMV'), (4, 'IS'), (5, 'VOCA'), (6, 'LIVE'), + (7, 'CDG'), (8, 'PV'), (9, 'MV'); CREATE TABLE IF NOT EXISTS kara_category ( id INTEGER PRIMARY KEY @@ -40,8 +41,7 @@ CREATE TABLE IF NOT EXISTS kara_category ); INSERT OR REPLACE INTO kara_category (id, name) VALUES - (1, 'ED/d*'), (2, 'OP/d*'), (3, 'AMV'), (4, 'IS'), (5, 'VOCA'), (6, 'LIVE'), - (7, 'CDG'), (8, 'PV'), (9, 'MV'); + (1, 'vo'), (2, 'va'), (3, 'amv'), (4, 'cdg'), (5, 'autres'), ( 6, 'vocaloid'); CREATE TABLE IF NOT EXISTS language ( id INTEGER PRIMARY KEY @@ -121,12 +121,16 @@ CREATE TABLE IF NOT EXISTS 'stickers.plt' -- Some useful values: -- last_update is the timestamp of the last time the table of kara has been --- updated. This is so lektor doesn't have to read all kara in the filesystem, +-- updated. This is so lektor doesn't have to read all kara in the filesystem, -- just the new ones (where their modified timestamp is greater than --- last_update). If last_update is NULL then the database is empty. +-- last_update). If last_update is NULL then the database is empty. +-- the last_end_update is here to mark a synchronisation point. Multiple +-- update can be done (updating last_update), but a general update can be +-- in progress. CREATE TABLE IF NOT EXISTS misc - ( id INTEGER PRIMARY KEY DEFAULT 42 CHECK(id = 42) - , last_update INTEGER + ( id INTEGER PRIMARY KEY DEFAULT 42 CHECK(id = 42) + , last_update INTEGER + , last_end_update INTEGER ); INSERT OR REPLACE INTO misc (id) VALUES (42); @@ -141,5 +145,3 @@ CREATE VIEW IF NOT EXISTS queue_ AS priority, queue.position AS old_position FROM queue; - -.exit diff --git a/src/cmd.c b/src/cmd.c index f5c0c61991087bd5e05d4e287b8e191aae8e6435..ae46c8b299db796e69d335b267d9ffdfa827b2db 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/cmd.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <sys/types.h> #include <stdlib.h> #include <strings.h> diff --git a/src/commands.c b/src/commands.c index 23acfc3854697086a69b918456db801584ae076d..890c26d318cdbf69c6e1600c1426c2baa6b67030 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,11 +1,12 @@ #define _POSIX_C_SOURCE 200809L -#include <lektor/macro.h> +#include <common/common.h> +#include <lektor/common.h> #include <lektor/commands.h> #include <lektor/database.h> #include <lektor/net.h> #include <lektor/uri.h> -#include <lektor/repo.h> +#include <mthread/mthread.h> #include <errno.h> #include <linux/limits.h> @@ -18,10 +19,6 @@ #include <poll.h> #include <unistd.h> -#define SELF_EXECUTABLE_LINUX "/proc/self/exe" -#define SELF_EXECUTABLE_FREEBSD "/proc/curproc/file" -#define SELF_EXECUTABLE_SOLARIS "/proc/self/path/a.out" - inline bool command_restart(struct lkt_state *srv, size_t c) { @@ -51,12 +48,32 @@ command_restart(struct lkt_state *srv, size_t c) abort(); } +bool +command_update(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) +{ + UNUSED(argv); + RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false); + srv->mpd_idle_events |= MPD_IDLE_UPDATE; + srv->mpd_idle_events |= MPD_IDLE_DATABASE; + return ! repo_update(&srv->repo); +} + +static inline void * +__rescan(void * arg) +{ + struct lkt_state *srv = arg; + database_update(srv->db, srv->kara_prefix); + return NULL; +} + bool command_rescan(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) { - (void) argv; + UNUSED(argv); RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false); - return ! repo_get_allid_async(); + srv->mpd_idle_events |= MPD_IDLE_UPDATE; + srv->mpd_idle_events |= MPD_IDLE_DATABASE; + return ! mthread_create(NULL, ATTR_DETACHED_FREE, __rescan, srv); } inline bool @@ -77,7 +94,7 @@ command_currentsong(struct lkt_state *srv, size_t c) memset(&kara, 0, sizeof(struct kara_metadata)); - if (!database_queue_current_kara(srv->db, &kara, NULL)) + if (!database_queue_current_kara((sqlite3 *) srv->db, &kara, NULL)) LOG_ERROR_SCT("COMMAND", "%s", "Failed to get information about the current kara"); out = lkt_message_new(); @@ -107,8 +124,8 @@ command_status(struct lkt_state *srv, size_t c) RETURN_UNLESS(srv, "Invalid argument", false); win = &srv->win; - RETURN_UNLESS(database_queue_state(srv->db, &queue_state), "Can't determine playback status", false); - database_queue_current_kara(srv->db, NULL, &songid); + RETURN_UNLESS(database_queue_state((sqlite3 *) srv->db, &queue_state), "Can't determine playback status", false); + database_queue_current_kara((sqlite3 *) srv->db, NULL, &songid); win->get_elapsed(win, &elapsed); win->get_duration(win, &duration); @@ -233,10 +250,10 @@ command_add(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], { RETURN_UNLESS(args, "Invalid argument", false); *watch_mask_ptr |= MPD_IDLE_PLAYLIST; - struct lkt_uri_t uri; + struct lkt_uri uri; char *query = args[0]; int ret, priority = 1; /* To be modified according to the command (insert or add) later (TODO) */ - (void) win; // No callbacks to the window for the moment + UNUSED(win); /* No callbacks to the window for the moment */ RETURN_UNLESS(lkt_uri_from(&uri, query), "Failed to parse query", false); switch (uri.type) { @@ -275,7 +292,7 @@ bool command_addid(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr) { - (void) win; + UNUSED(win); long id; char *endptr, *id_str; int priority = 1; @@ -308,7 +325,7 @@ command_crop(sqlite3 *db, enum mpd_idle_flag *watch_mask_ptr) bool command_delid(sqlite3 *db, struct lkt_win *win, char *id_str, enum mpd_idle_flag *watch_mask_ptr) { - (void) win; + UNUSED(win); long id; char *endptr; int uri = 0; @@ -367,14 +384,6 @@ command_help(struct lkt_state *srv, size_t c) return true; } -bool -command_update(struct lkt_state *srv, enum mpd_idle_flag *watch_mask_ptr) -{ - *watch_mask_ptr |= MPD_IDLE_UPDATE; - *watch_mask_ptr |= MPD_IDLE_DATABASE; - return database_update(srv->db, srv->kara_prefix); -} - bool command_idle(struct lkt_state *srv, size_t c, struct lkt_command *cmd) { @@ -432,7 +441,7 @@ command_noidle(struct lkt_state *srv, size_t c) static bool lkt_callback_print_row_v1(struct lkt_state *srv, size_t c, int id, int id_len, const char *sql_row) { - (void) srv; + UNUSED(srv); printf(" . from client %ld:\t%*d:%s\n", c, id_len, id, sql_row); return true; } @@ -460,10 +469,8 @@ lkt_callback_send_row_v2(struct lkt_state *srv, size_t c, int id, int id_len, co static bool lkt_callback_insert_v1(struct lkt_state *srv, size_t c, int id, int id_len, const char *sql_row) { - (void) c; - (void) sql_row; - (void) id_len; - return database_queue_add_id(srv->db, id, 5); + UNUSED(sql_row, id_len, c); + return database_queue_add_id((sqlite3 *) srv->db, id, 5); } static bool @@ -540,7 +547,7 @@ __find(struct lkt_state *srv, size_t c, char *cmd_args[LKT_MESSAGE_ARGS_MAX], lo /* Make the search langand do the right action */ - RETURN_UNLESS(init(srv->db, col_name, rgx, &search), "Failed to init search", false); + RETURN_UNLESS(init((sqlite3 *) srv->db, col_name, rgx, &search), "Failed to init search", false); for (count = 0; database_search_iter(&search); ++count) continue; @@ -569,8 +576,7 @@ command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_o char *args[LKT_MESSAGE_MAX]) { RETURN_UNLESS(srv, "Invalid argument", false); - - (void) c; + UNUSED(c); long val, ret = false; char *endptr; struct lkt_win *win = &srv->win; @@ -593,19 +599,19 @@ command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_o switch (opt) { case lkt_playback_option_random: - ret = database_config_queue(srv->db, "random", val); + ret = database_config_queue((sqlite3 *) srv->db, "random", val); break; case lkt_playback_option_single: - ret = database_config_queue(srv->db, "single", val); + ret = database_config_queue((sqlite3 *) srv->db, "single", val); break; case lkt_playback_option_consume: - ret = database_config_queue(srv->db, "consume", val); + ret = database_config_queue((sqlite3 *) srv->db, "consume", val); break; case lkt_playback_option_repeat: - ret = database_config_queue(srv->db, "repeat", val); + ret = database_config_queue((sqlite3 *) srv->db, "repeat", val); break; case lkt_playback_option_volume: - ret = database_config_queue(srv->db, "volume", val); + ret = database_config_queue((sqlite3 *) srv->db, "volume", val); LOG_INFO_SCT("COMMAND", "Set volume to %ld", val); ret &= win->set_volume(win, val); srv->mpd_idle_events |= MPD_IDLE_MIXER; @@ -626,8 +632,8 @@ command_set_pos(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask { char filepath[PATH_MAX]; *watch_mask_ptr |= MPD_IDLE_PLAYER; - RETURN_UNLESS(database_queue_set_current_index(db, index), "Failed to set position in queue", false); - RETURN_UNLESS(database_queue_get_current_file(db, filepath), "Failed to get filename", false); + RETURN_UNLESS(database_queue_set_current_index((sqlite3 *)db, index), "Failed to set position in queue", false); + RETURN_UNLESS(database_queue_get_current_file((sqlite3 *)db, filepath), "Failed to get filename", false); return win->load_file(win, filepath); } @@ -637,7 +643,7 @@ command_plt_add(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_fla RETURN_UNLESS(args && args[0], "Invalid argument", false); bool ret = false; - struct lkt_uri_t uri; + struct lkt_uri uri; if (args[1] == NULL) ret = database_plt_create(db, args[0]); @@ -732,7 +738,7 @@ bool command_plt_add_uri(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr) { RETURN_UNLESS(args && args[0] && args[1], "Invalid argument", false); - struct lkt_uri_t uri; + struct lkt_uri uri; RETURN_UNLESS(lkt_uri_from(&uri, args[1]), "Failed to parse uri", false); bool ret = database_plt_add_uri(db, args[0], &uri); lkt_uri_free(&uri); @@ -803,25 +809,25 @@ command_queue_list(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_ /* The command is used in its relative forme, display elements from the current one. */ only_one: - return database_queue_list(srv->db, from, from, &callback); + return database_queue_list((sqlite3 *) srv->db, from, from, &callback); /* The command is used with a range specifier. */ is_a_range: if (to - from + 1 < lkt_remaining_msg(srv, c) - 2) { lkt_set_continuation(srv, c, 0); - return database_queue_list(srv->db, from, to, &callback); + return database_queue_list((sqlite3 *) srv->db, from, to, &callback); } else { to = from + lkt_remaining_msg(srv, c) - 3; lkt_set_continuation(srv, c, to + 1); - return database_queue_list(srv->db, from, to, &callback); + return database_queue_list((sqlite3 *) srv->db, from, to, &callback); } } bool command_password(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) { - RETURN_UNLESS(argv[0] && !argv[1], "Invalid argument", false); - RETURN_UNLESS(database_user_authentificate(srv->db, argv[0]), "Failed to authentificate user", false); + RETURN_UNLESS(argv[0], "Invalid argument", false); + RETURN_UNLESS(database_user_authentificate((sqlite3 *) srv->db, argv[0]), "Failed to authentificate user", false); lkt_client_auth(srv, c, true); return true; } @@ -829,7 +835,7 @@ command_password(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MA bool command_user_add(struct lkt_state *srv, size_t c, sqlite3 *db, char *argv[LKT_MESSAGE_ARGS_MAX]) { - RETURN_UNLESS(argv[0] && argv[1] && !argv[2], "Invalid argument", false); + RETURN_UNLESS(argv[0] && argv[1], "Invalid argument", false); RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false); RETURN_UNLESS(database_user_add(db, argv[0], argv[1]), "Failed to add user", false); return false; @@ -840,9 +846,7 @@ command_user_add(struct lkt_state *srv, size_t c, sqlite3 *db, char *argv[LKT_ME static bool sticker_send_one_value(void *_args, const char *sticker, const char *type, int uri, int value) { - (void) sticker; - (void) type; - (void) uri; + UNUSED(sticker, type, uri); struct sticker_callback *args = (struct sticker_callback *) _args; struct lkt_message *out = lkt_message_new(); out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value); @@ -865,7 +869,7 @@ static bool sticker_send_check_uri(void *_args, const char *sticker, const char *type, int uri, int value) { struct sticker_callback *args = (struct sticker_callback *) _args; - (void) type; + UNUSED(type); if (uri == args->uri) { struct lkt_message *out = lkt_message_new(); @@ -881,7 +885,7 @@ sticker_send_value_check_uri_name(void *_args, const char *sticker, const char * { struct sticker_callback *args = (struct sticker_callback *) _args; struct lkt_message *out; - (void) type; + UNUSED(type); if (uri == args->uri || !strcasecmp(sticker, args->name)) { out = lkt_message_new(); @@ -895,7 +899,7 @@ sticker_send_value_check_uri_name(void *_args, const char *sticker, const char * static bool sticker_check_is_present_eq(void *_args, const char *sticker, const char *type, int uri, int value) { - (void) type; + UNUSED(type); struct sticker_callback *args = (struct sticker_callback *) _args; args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value == args->value); return true; @@ -904,7 +908,7 @@ sticker_check_is_present_eq(void *_args, const char *sticker, const char *type, static bool sticker_check_is_present_lt(void *_args, const char *sticker, const char *type, int uri, int value) { - (void) type; + UNUSED(type); struct sticker_callback *args = (struct sticker_callback *) _args; args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value < args->value); return true; @@ -913,7 +917,7 @@ sticker_check_is_present_lt(void *_args, const char *sticker, const char *type, static bool sticker_check_is_present_gt(void *_args, const char *sticker, const char *type, int uri, int value) { - (void) type; + UNUSED(type); struct sticker_callback *args = (struct sticker_callback *) _args; args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value > args->value); return true; @@ -929,18 +933,19 @@ command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS .c = c, .call = sticker_send_one_value, }; - RETURN_UNLESS(database_sticker_get(srv->db, argv[0], argv[2], uri, &cb), "Failed to get sticker", false); + RETURN_UNLESS(database_sticker_get((sqlite3 *) srv->db, argv[0], argv[2], uri, &cb), "Failed to get sticker", + false); return true; } bool command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) { - (void) c; + UNUSED(c); RETURN_UNLESS(argv[0] && argv[1] && argv[2] && argv[3] && !argv[4], "Invalid argument", false); int uri = atoi(argv[1]); /* FIXME: Use strtol. */ int value = atoi(argv[4]); /* FIXME: Use strtol. */ - RETURN_UNLESS(database_sticker_set(srv->db, argv[0], argv[2], uri, value), "Failed to get sticker", false); + RETURN_UNLESS(database_sticker_set((sqlite3 *) srv->db, argv[0], argv[2], uri, value), "Failed to get sticker", false); srv->mpd_idle_events |= MPD_IDLE_STICKER; return true; } @@ -977,18 +982,18 @@ command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARG just_list_all: callback.call = sticker_send_all; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); simple_list_command: callback.uri = atoi(argv[1]); /* FIXME: Use strtol. */ callback.call = sticker_send_check_uri; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); list_stickers_in_uri: callback.uri = atoi(argv[1]); /* FIXME: Use strtol. */ callback.name = argv[2]; callback.call = sticker_send_value_check_uri_name; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); return false; list_stickers_check_value: @@ -998,13 +1003,13 @@ list_stickers_check_value: switch (argv[3][0]) { case '=': callback.call = sticker_check_is_present_eq; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); case '<': callback.call = sticker_check_is_present_lt; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); case '>': callback.call = sticker_check_is_present_gt; - return database_sticker_list(srv->db, argv[0], &callback); + return database_sticker_list((sqlite3 *) srv->db, argv[0], &callback); default: return 0; } @@ -1017,9 +1022,9 @@ unknown: bool command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]) { - (void) c; + UNUSED(c); RETURN_UNLESS(argv[0] && argv[1], "Invalid argument", false); int uri = atoi(argv[1]); srv->mpd_idle_events |= MPD_IDLE_STICKER; - return database_sticker_delete_specify(srv->db, argv[0], uri, argv[2]); + return database_sticker_delete_specify((sqlite3 *) srv->db, argv[0], uri, argv[2]); } diff --git a/src/common.c b/src/common.c index f0195a6c88ec9b2660bfd091499dceab267044de..6b906e78c4bce33b04ba60c3e9c465ddba1ea56d 100644 --- a/src/common.c +++ b/src/common.c @@ -1,6 +1,9 @@ #include <common/common.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <sys/stat.h> void __not_implemented(const char *func, char *file, int line) @@ -9,4 +12,126 @@ __not_implemented(const char *func, char *file, int line) abort(); } +inline void +__unused(void *dummy, ...) +{ + (void) dummy; +} + +uint32_t +be_uint32_t(const uint8_t bytes[], size_t n) +{ + uint32_t res = 0; + for (size_t i = 0; i < n; i++) + res = (res << 8u) | bytes[i]; + return res; +} + +uint64_t +be_uint64_t(const uint8_t bytes[], size_t n) +{ + uint64_t res = 0; + for (size_t i = 0; i < n; i++) + res = (res << 8u) | bytes[i]; + return res; +} +char * +trim(char *str, size_t len, char c) +{ + char *res = (char *) str; + char *end; + + for (; len != 0 && *res == c; ++str, --len) + continue; + + if (*res == 0) + return res; + + end = res + len - sizeof(char); + len = 0; + + for (; res < end && *end == c; --end, ++len) + continue; + + if (len > 0) + end[1] = 0; + + return res; +} + +int +is_utf8(const char *string) +{ + if (!string) + return 1; + + const unsigned char *bytes = (const unsigned char *)string; + while (*bytes) { + /* ASCII */ + if (bytes[0] == 0x09 || bytes[0] == 0x0A || + bytes[0] == 0x0D || (0x20 <= bytes[0] && bytes[0] <= 0x7E)) { + bytes += 1; + continue; + } + + /* non-overlong 2-byte */ + if ((0xC2 <= bytes[0] && bytes[0] <= 0xDF) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF)) { + bytes += 2; + continue; + } + + if ( (bytes[0] == 0xE0 && + (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF)) || /* excluding overlongs */ + (((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || + bytes[0] == 0xEE || bytes[0] == 0xEF) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF)) || /* straight 3-byte */ + (bytes[0] == 0xED && + (0x80 <= bytes[1] && bytes[1] <= 0x9F) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF))) { /* excluding surrogates */ + bytes += 3; + continue; + } + + if ( (bytes[0] == 0xF0 && /* planes 1-3 */ + (0x90 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF)) || + ((0xF1 <= bytes[0] && bytes[0] <= 0xF3) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF)) || /* planes 4-15 */ + (bytes[0] == 0xF4 && + (0x80 <= bytes[1] && bytes[1] <= 0x8F) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF))) { /* plane 16 */ + bytes += 4; + continue; + } + + return 2; + } + + return 0; +} + +int +get_stdin_line(const char *prompt, char *buf, size_t len) +{ + if (prompt) + fprintf(stdout, "%s", prompt); + if (!fgets(buf, len, stdin)) + return 1; + buf[len - 1] = 0u; + return is_utf8(buf); +} + +inline long +get_mtime(const char *path) +{ + struct stat statbuf; + return (stat(path, &statbuf) == -1) ? 0 : statbuf.st_mtime; +} diff --git a/src/config.c b/src/config.c index 29b85b49cee97229cc2d4afb4b79102793c93855..7f1b3ada29f604d5212325d8e3343933aefe045b 100644 --- a/src/config.c +++ b/src/config.c @@ -90,7 +90,7 @@ static int #if INI_HANDLER_LINENO handler(void *user, const char *section, const char *name, const char *value, int lineno) { - (void) lineno; + UNUSED(lineno); #else handler(void *user, const char *section, const char *name, const char *value) { diff --git a/src/database/config.c b/src/database/config.c index a2aa57d5c9d17c37d66585e918b325dd4ce83d70..60c59427d60ebee07a688a36882d9f37cfb6c279 100644 --- a/src/database/config.c +++ b/src/database/config.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <limits.h> #include <stdio.h> diff --git a/src/database/find.c b/src/database/find.c index 56ac88822330b08ef006a2050704fc3f8bb47447..e801b631e548c0ed88c097fa3928b848e32cfbc7 100644 --- a/src/database/find.c +++ b/src/database/find.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <limits.h> #include <stdio.h> diff --git a/src/database/open.c b/src/database/open.c index e27425871c58f447f4d42e94274ad81830e57628..61414c90395852fbf6838cd4428b46f8a7428688 100644 --- a/src/database/open.c +++ b/src/database/open.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -38,6 +38,10 @@ static const char *const SQL_MEM_SCHEM = " , PRIMARY KEY (section, key)" " ) WITHOUT ROWID;\n"; +/* Should be, defined scripts_init_sql and its length scripts_init_sql_len */ +extern unsigned char ___scripts_init_sql[]; +extern int ___scripts_init_sql_len; + #define INVALID_CHARS_DBPATH ":?!'\"" #define HEAP_LIMIT_SOFT 100 * 1024 * 1024 #define HEAP_LIMIT_HARD 150 * 1024 * 1024 @@ -52,11 +56,10 @@ is_sql_str_invalid(const char *str) bool database_new(sqlite3 **db) { - /* SQLITE_OPEN_FULLMUTEX for serialized mode */ - static int flags = SQLITE_OPEN_READWRITE | - SQLITE_OPEN_SHAREDCACHE | - SQLITE_OPEN_NOFOLLOW | - SQLITE_OPEN_NOMUTEX; + static int flags = SQLITE_OPEN_READWRITE | /* Open in RW mode */ + SQLITE_OPEN_SHAREDCACHE | /* hHared cache for databases */ + SQLITE_OPEN_NOFOLLOW | /* Don't follow symlinks */ + SQLITE_OPEN_FULLMUTEX; /* Serialized */ RETURN_IF(SQLITE_OK != sqlite3_enable_shared_cache(1), "Failed to enable shared cache", false); RETURN_IF(sqlite3_soft_heap_limit64(HEAP_LIMIT_SOFT) < 0, "Failed to set soft heap limit", false); RETURN_IF(sqlite3_hard_heap_limit64(HEAP_LIMIT_HARD) < 0, "Failed to set soft heap limit", false); @@ -175,3 +178,46 @@ database_detach(sqlite3 *db, const char *name) LOG_INFO_SCT("DB", "Database '%s' detached", name); return true; } + +bool +database_init(const char *dbpath) +{ + sqlite3 *db; + GOTO_IF(SQLITE_OK != sqlite3_open(dbpath, &db), "Failed to open the database", error); + SQLITE_EXEC(db, (const char *) ___scripts_init_sql, error); + LOG_INFO_SCT("DB", "Initialized the 'disk' database successfully, path was '%s'", dbpath); + return true; +error: + LOG_ERROR_SCT("DB", "Failed to init the 'disk' database, path was '%s'", dbpath); + return false; +} + +long +database_get_timestamp(volatile sqlite3 *db) +{ + long ret = 0; + static const char *SQL = "SELECT last_update FROM misc WHERE id = 42;"; + sqlite3_stmt *stmt; + SQLITE_PREPARE(db, stmt, SQL, error); + SQLITE_STEP_ROW(db, stmt, error); + ret = sqlite3_column_int(stmt, 0); +error: + sqlite3_finalize(stmt); + return ret; +} + +void +database_stamp(volatile sqlite3 *db) +{ + SQLITE_EXEC(db, "UPDATE misc SET last_update = strftime('%s','now');", error); +error: + return; +} + +void +database_updated(volatile sqlite3 *db) +{ + SQLITE_EXEC(db, "UPDATE misc SET last_end_update = strftime('%s','now');", error); +error: + return; +} diff --git a/src/database/playlist.c b/src/database/playlist.c index f1f458fc0c89e07402b42a12c2cd9a2358969d86..086f580bd911e19526e4bb4ad3b248e918995323 100644 --- a/src/database/playlist.c +++ b/src/database/playlist.c @@ -2,7 +2,7 @@ #include <common/common.h> #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <stdio.h> #include <strings.h> @@ -151,7 +151,7 @@ error: } bool -database_plt_add_uri(sqlite3 *db, const char *name, struct lkt_uri_t *uri) +database_plt_add_uri(sqlite3 *db, const char *name, struct lkt_uri *uri) { static const char *SQL = "WITH plt_id(id) AS (SELECT playlist.id AS id FROM playlist WHERE name COLLATE nocase = ?) " diff --git a/src/database/queue.c b/src/database/queue.c index 6520009cfc38c2cc2e5f1ddd5f5227769ab90e80..341455f016c363839f23ba996643ef70a84ef045 100644 --- a/src/database/queue.c +++ b/src/database/queue.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <linux/limits.h> #include <stdio.h> @@ -268,15 +268,15 @@ database_queue_next(sqlite3 *db, char filepath[PATH_MAX]) { static const char *SQL_STMT = "SELECT file_path, position, RANDOM()" - " FROM kara" - " JOIN queue_ ON kara.id = queue_.kara_id" - " JOIN queue_state" - " ON current IS NULL" - " OR (CASE WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current" - " WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN position <> current" - " ELSE position > current END)" - " ORDER BY CASE WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN RANDOM() ELSE 2 END" - " LIMIT 1"; + " FROM kara" + " JOIN queue_ ON kara.id = queue_.kara_id" + " JOIN queue_state" + " ON available = 1 AND current IS NULL OR" + " CASE WHEN (SELECT single FROM queue_state) = 1 THEN position = current" + " WHEN (SELECT random FROM queue_state) = 1 THEN position <> current" + " ELSE position > current END" + " ORDER BY CASE WHEN (SELECT random FROM queue_state) = 1 THEN RANDOM() ELSE 2 END" + " LIMIT 1"; char SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT]; bool status = false; int code = SQLITE_OK, id; @@ -332,7 +332,7 @@ database_queue_prev(sqlite3 *db, char filepath[PATH_MAX]) static const char *SQL_STMT = "SELECT file_path, position " " FROM kara" - " JOIN queue_ ON kara.id = queue_.kara_id" + " JOIN queue_ ON kara.id = queue_.kara_id AND available = 1" " JOIN queue_state ON CASE " " WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current" " ELSE queue_.position < queue_state.current" @@ -380,9 +380,11 @@ bool database_queue_clear(sqlite3 *db) { static const char *SQL_STMT = + "BEGIN TRANSACTION;" "DELETE FROM queue;" "DELETE FROM sqlite_sequence WHERE name = 'queue';" - "UPDATE queue_state SET current = NULL;"; + "UPDATE queue_state SET current = NULL;" + "COMMIT TRANSACTION;"; SQLITE_EXEC(db, SQL_STMT, error); return true; error: @@ -578,11 +580,9 @@ database_queue_shuffle(sqlite3 *db) "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';" /* Set the current to the right kara when needed. */ "UPDATE queue_state" - " SET current =" - " CASE" + " SET current = CASE" " WHEN current NOT NULL THEN 1" - " ELSE NULL" - " END;" + " ELSE NULL END;" "COMMIT;"; SQLITE_EXEC(db, SQL, error); return true; @@ -600,8 +600,7 @@ database_queue_list(sqlite3 *db, size_t from, size_t to, struct lkt_callback *ca " JOIN kara ON kara_id = kara.id" " WHERE position >= ? AND position <= ?" " GROUP BY position ORDER BY position)" - "SELECT id, string, (SELECT MAX(len) FROM content)" - " FROM content;"; + "SELECT id, string, (SELECT MAX(len) FROM content) FROM content;"; int code, id, id_len; const char *row; bool ret = false; diff --git a/src/database/stickers.c b/src/database/stickers.c index 9d172285c9d6c4cb235f734fbc456240665036db..1b4c023b00baf88ff10bbe0f93e4a7fd1332c435 100644 --- a/src/database/stickers.c +++ b/src/database/stickers.c @@ -1,6 +1,6 @@ #define _POSIX_C_SOURCE 200809L -#include <lektor/macro.h> +#include <lektor/common.h> #include <lektor/database.h> #include <string.h> #include <strings.h> @@ -13,9 +13,7 @@ database_sticker_create(sqlite3 *db, const char *name) static const char *INSERT = "WITH id_max (id) AS (SELECT MAX(id) FROM 'stickers')" "INSERT INTO 'stickers' (id, name)" - " SELECT" - " CASE WHEN id_max.id IS NULL THEN 1 ELSE id_max.id + 1 END," - " ?" + " SELECT CASE WHEN id_max.id IS NULL THEN 1 ELSE id_max.id + 1 END, ?" " FROM id_max" "WHERE NOT EXISTS (SELECT 1 FROM 'stickers' WHERE name = ?);"; sqlite3_stmt *stmt; @@ -34,8 +32,7 @@ error: bool database_sticker_delete(sqlite3 *db, const char *name) { - static const char *INSERT = - "DELETE FROM 'stickers' WHERE name = ?;"; + static const char *INSERT = "DELETE FROM 'stickers' WHERE name = ?;"; sqlite3_stmt *stmt; int ret = false; RETURN_IF(strlen(name) == 0, "A sticker name must be at least one character long", ret); diff --git a/src/database/update.c b/src/database/update.c index a3b64b2a1cbac804dfa26fccf0c65f929d962291..80429007333c98526a9efa5988b88f498ddbcd33 100644 --- a/src/database/update.c +++ b/src/database/update.c @@ -1,8 +1,10 @@ #define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE +#include <common/common.h> #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> + #include <stdbool.h> #include <sqlite3.h> #include <stdio.h> @@ -13,7 +15,7 @@ #include <linux/limits.h> static bool -database_add_kara(sqlite3 *db, const char *filename) +database_add_kara(volatile sqlite3 *db, const char *filename) { RETURN_UNLESS(db || filename, "Invalid argument", false); static const char *SQL_STMT = @@ -21,8 +23,7 @@ database_add_kara(sqlite3 *db, const char *filename) "kara (song_name, source_name, category, song_type, language, file_path, is_new, author_name, author_year, song_number)" "SELECT ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s','now'), ?"; sqlite3_stmt *stmt = NULL; - bool status = false; - int code = SQLITE_OK; + int code = SQLITE_OK, status = false; struct kara_metadata data; time_t the_time = time(NULL); struct tm *the_local_time = localtime(&the_time); @@ -37,17 +38,17 @@ database_add_kara(sqlite3 *db, const char *filename) SQLITE_PREPARE(db, stmt, SQL_STMT, error); - if ((sqlite3_bind_text(stmt, 1, data.song_name, -1, 0) != SQLITE_OK) || + if ((sqlite3_bind_text(stmt, 1, data.song_name, -1, 0) != SQLITE_OK) || (sqlite3_bind_text(stmt, 2, data.source_name, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 3, data.category, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 4, data.song_type, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 5, data.language, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 6, filename, -1, 0) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 7, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 3, data.category, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 4, data.song_type, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 5, data.language, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 6, filename, -1, 0) != SQLITE_OK) || + (sqlite3_bind_int (stmt, 7, 0) != SQLITE_OK) || (sqlite3_bind_text(stmt, 8, data.author_name, -1, 0) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 9, data.song_number) != SQLITE_OK) + (sqlite3_bind_int (stmt, 9, data.song_number) != SQLITE_OK) ) { - LOG_ERROR_SCT("DB", "Failed to bind for kara %s: %s", filename, sqlite3_errmsg(db)); + LOG_ERROR_SCT("DB", "Failed to bind for kara %s: %s", filename, sqlite3_errmsg((sqlite3 *) db)); goto error; } @@ -59,7 +60,25 @@ error: } bool -database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail) +database_update_set_available(volatile sqlite3 *db, uint64_t id) +{ + static const char *SQL_STMT = "UPDATE kara SET available = 1 WHERE id = ?;"; + sqlite3_stmt *stmt; + SQLITE_PREPARE(db, stmt, SQL_STMT, error); + SQLITE_BIND_INT(db, stmt, 1, (int) id, error); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto error; + sqlite3_finalize(stmt); + LOG_INFO_SCT("DB", "Kara %ld is now available", id); + return true; +error: + sqlite3_finalize(stmt); + LOG_ERROR_SCT("DB", "Failed to set kara %ld available", id); + return false; +} + +bool +database_update_add(volatile sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail) { RETURN_UNLESS(db && kara_path && mdt, "Invalid argument", false); static const char *SQL_STMT = @@ -67,8 +86,7 @@ database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *md "kara (song_name, source_name, category, song_type, language, file_path, is_new, author_name, author_year, song_number, id, available)" "SELECT ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s','now'), ?, ?, ?"; sqlite3_stmt *stmt = NULL; - int code = SQLITE_OK; - bool ret = false; + int code = SQLITE_OK, ret = false; time_t the_time = time(NULL); struct tm *the_local_time = localtime(&the_time); char year[10]; @@ -79,19 +97,19 @@ database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *md SQLITE_PREPARE(db, stmt, SQL_STMT, error); - if ((sqlite3_bind_text(stmt, 1, mdt->song_name, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 2, mdt->source_name, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 3, mdt->category, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 4, mdt->song_type, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 5, mdt->language, -1, 0) != SQLITE_OK) || - (sqlite3_bind_text(stmt, 6, kara_path, -1, 0) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 7, 0) != SQLITE_OK) /* TODO */ || /* No new kara added (TODO). */ - (sqlite3_bind_text(stmt, 8, mdt->author_name, -1, 0) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 9, mdt->song_number) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 10, id) != SQLITE_OK) || - (sqlite3_bind_int(stmt, 11, avail) != SQLITE_OK) + if ((sqlite3_bind_text(stmt, 1, mdt->song_name, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 2, mdt->source_name, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 3, mdt->category, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 4, mdt->song_type, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 5, mdt->language, -1, 0) != SQLITE_OK) || + (sqlite3_bind_text(stmt, 6, kara_path, -1, 0) != SQLITE_OK) || + (sqlite3_bind_int (stmt, 7, 0) != SQLITE_OK) /* TODO */ || /* No new kara added (TODO). */ + (sqlite3_bind_text(stmt, 8, mdt->author_name, -1, 0) != SQLITE_OK) || + (sqlite3_bind_int (stmt, 9, mdt->song_number) != SQLITE_OK) || + (sqlite3_bind_int (stmt, 10, id) != SQLITE_OK) || + (sqlite3_bind_int (stmt, 11, avail) != SQLITE_OK) ) { - LOG_ERROR_SCT("DB", "Failed to bind argument for kara %s: %s", kara_path, sqlite3_errmsg(db)); + LOG_ERROR_SCT("DB", "Failed to bind argument for kara %s: %s", kara_path, sqlite3_errmsg((sqlite3 *) db)); goto error; } @@ -104,7 +122,7 @@ error_no_sqlite: } bool -database_update(sqlite3 *db, const char *kara_dir) +database_update(volatile sqlite3 *db, const char *kara_dir) { DIR *d; struct dirent *dir; @@ -121,15 +139,21 @@ database_update(sqlite3 *db, const char *kara_dir) strncat(path, "/", PATH_MAX - 1); strncat(path, dir->d_name, PATH_MAX - 1); - if (dir->d_type == DT_REG) + if (dir->d_type == DT_REG) { + if (get_mtime(path) < database_get_timestamp(db)) + continue; database_add_kara(db, path); - else if (dir->d_type == DT_DIR && - strcmp(dir->d_name, ".") != 0 && + database_stamp(db); + } + + else if (dir->d_type == DT_DIR && + strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) database_update(db, path); } LOG_INFO("Passed directory '%s'", kara_dir); + database_updated(db); closedir(d); return true; } diff --git a/src/database/user.c b/src/database/user.c index b6330b2d3319591a86e21683105afc25d6d88176..11936d7492527e567ad371ad362987e18f093161 100644 --- a/src/database/user.c +++ b/src/database/user.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <stdio.h> bool diff --git a/src/main/lkt.c b/src/main/lkt.c index e8820e1a1ee42dc5b73fc94b7d68b4046d2c900b..6ed06b2230d591b1e0892b0d3641df14e0efbe82 100644 --- a/src/main/lkt.c +++ b/src/main/lkt.c @@ -66,6 +66,7 @@ fail(const char *message) typedef struct { const char *host; /* Serveur host, may be resolved. */ const char *port; /* Serveur port. */ + const char *pwd; /* The password for the user. */ const char **argv; /* Pointer to the argv from the main function. */ int argc; /* Argument count of the args. */ @@ -75,6 +76,7 @@ typedef struct { static const char *host; static const char *port; +static const char *password; static const char *LKT_QUEUE_DEFAULT[] = { "10" }; /* Communication functions and fonction that interact with lektor stuff. */ @@ -246,6 +248,68 @@ prev__(struct lkt_cmd_args *args) lkt_send_and_exit(cmd__, sizeof(cmd__)); /* In bytes. */ } +noreturn void +simple_send_with_password__(const char *cmd) +{ + if (!password) + fail("Password not provided"); + static const char cmd__[] = "password %s\n%s\nclose\n"; + FILE *sock = lkt_connect(); + exit(write_socket_format(sock, cmd__, password, cmd)); +} + +noreturn void +restart__(struct lkt_cmd_args *args) +{ + if (args->argc != 0) + fail("Invalid argument, the previous command takes no arguments"); + simple_send_with_password__("restart"); +} + +noreturn void +kill__(struct lkt_cmd_args *args) +{ + if (args->argc != 0) + fail("Invalid argument, the previous command takes no arguments"); + simple_send_with_password__("kill"); +} + +noreturn void +rescan_or_update__(struct lkt_cmd_args *args, const char *cmd) +{ + if (!password) + fail("Password not provided"); + FILE *sock = lkt_connect(); + char buff[LKT_MESSAGE_MAX]; + int i; + + /* All the db */ + if (args->argc == 0) + exit(write_socket_format(sock, "password %s\n%s\n", password, cmd)); + + /* With a query */ + else { + memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char)); + for (i = 0; i < args->argc; ++i) + strncat(buff, args->argv[i], LKT_MESSAGE_MAX - 1); + exit(write_socket_format(sock, "password %s\n%s %s\n", password, cmd, buff)); + } + + abort(); +} + +noreturn void +rescan__(struct lkt_cmd_args *args) +{ + rescan_or_update__(args, "rescan"); +} + +noreturn void +update__(struct lkt_cmd_args *args) +{ + rescan_or_update__(args, "update"); +} + noreturn void stop__(struct lkt_cmd_args *args) { @@ -305,6 +369,23 @@ error: exit(EXIT_FAILURE); } +noreturn void +ping__(struct lkt_cmd_args *args) +{ + if (args->argc != 0) + fail("Invalid argument, the ping command takes no arguments"); + char buff[6] = {0}; + FILE *sock = lkt_connect(); + if (write_socket(sock, "ping\nclose\n", sizeof("ping\n"))) + fail("Failed to send the ping to lektord"); + if (read_socket(sock, buff, 6 * sizeof(char)) <= 0) + fail("Failed to recieve the response of lektord"); + if (strncmp(buff, "OK", 2)) + fail("ACK"); + write(1, "OK\n", sizeof("OK\n")); + exit(EXIT_SUCCESS); +} + noreturn void current__(struct lkt_cmd_args *args) { @@ -868,7 +949,7 @@ search_add__(struct lkt_cmd_args *args) noreturn void search_insert__(struct lkt_cmd_args *args) { - (void) args; + UNUSED(args); fail("Not implemented"); } @@ -921,6 +1002,24 @@ static struct lkt_cmd_opt options_search[] = { LKT_OPT_NULL, }; +static struct lkt_cmd_opt options_admin[] = { + { .name = "ping", .call = ping__, .help = "Only pings the server" }, + { .name = "kill", .call = kill__, .help = "Kills the lektord server" }, + { .name = "restart", .call = restart__, .help = "Restarts the lektord server" }, + { .name = "rescan", .call = rescan__, .help = "Scan the filesystem and update the database with present files" }, + { .name = "update", .call = update__, .help = "Update the database with files present on Kurisu" }, + LKT_OPT_NULL, +}; + +noreturn void +admin__(struct lkt_cmd_args *args) +{ + if (args->argc == 0) + fail("Invalid argument, you must specify a sub command for the admin command"); + + lkt_cmd_parse(options_admin, args->argc, args->argv, help); +} + noreturn void queue__(struct lkt_cmd_args *args) { @@ -949,16 +1048,17 @@ plt__(struct lkt_cmd_args *args) } static struct lkt_cmd_opt options_[] = { - { .name = "current", .call = current__, .help = "Get the current playing song" }, - { .name = "play", .call = play__, .help = "Toggle play/pause, may starts at a specified index" }, - { .name = "next", .call = next__, .help = "Play the next kara in the queue" }, - { .name = "previous", .call = prev__, .help = "Play the previous kara in the queue" }, - { .name = "queue", .call = queue__, .help = "The queue sub-command", .sub = options_queue }, - { .name = "shuffle", .call = shuffle__, .help = "Shuffle the queue" }, - { .name = "status", .call = status__, .help = "Get the status of lektord" }, - { .name = "stop", .call = stop__, .help = "Stop playing the queue, won't close lektord's window" }, - { .name = "plt", .call = plt__, .help = "The playlist sub-command", .sub = options_plt }, - { .name = "search", .call = search__, .help = "The search sub-command", .sub = options_search }, + { .name = "current", .call = current__, .help = "Get the current playing song" }, + { .name = "play", .call = play__, .help = "Toggle play/pause, may starts at a specified index" }, + { .name = "next", .call = next__, .help = "Play the next kara in the queue" }, + { .name = "previous", .call = prev__, .help = "Play the previous kara in the queue" }, + { .name = "queue", .call = queue__, .help = "The queue sub-command", .sub = options_queue }, + { .name = "shuffle", .call = shuffle__, .help = "Shuffle the queue" }, + { .name = "status", .call = status__, .help = "Get the status of lektord" }, + { .name = "stop", .call = stop__, .help = "Stop playing the queue, won't close lektord's window" }, + { .name = "plt", .call = plt__, .help = "The playlist sub-command", .sub = options_plt }, + { .name = "search", .call = search__, .help = "The search sub-command", .sub = options_search }, + { .name = "admin", .call = admin__, .help = "Administration commands", .sub = options_admin }, LKT_OPT_NULL, }; @@ -967,9 +1067,7 @@ static struct lkt_cmd_opt options_[] = { static void sigpipe__(int sig) { - (void) sig; - static const char *errmsg = "Exit because sigpipe\n"; - write(2, errmsg, strlen(errmsg)); + LOG_ERROR_SCT("GENERAL", "Exit because of signal sigpipe (%d)", sig); exit(EXIT_FAILURE); } @@ -993,6 +1091,11 @@ parse_args(args_t *args, int argc, const char **argv) args->port = (argv[i] + len + 1); ++got; } + + else if (STR_NMATCH("pwd", argv[i], len)) { + args->pwd = (argv[i] + len + 1); + ++got; + } } args->argv = &argv[got + 1]; @@ -1016,8 +1119,9 @@ main(int argc, const char **argv) parse_args(&args, argc, argv); - host = args.host; - port = args.port; + host = args.host; + port = args.port; + password = args.pwd; /* Communication with lektor. */ lkt_cmd_parse(options_, args.argc, args.argv, help); diff --git a/src/main/lktadm.c b/src/main/lktadm.c index 5676ad2ec7caaeeb574df389304d6e132722a3cb..352d21ed5ecb5a572a712216dec9d5b13264bc3f 100644 --- a/src/main/lktadm.c +++ b/src/main/lktadm.c @@ -7,8 +7,8 @@ #include <lektor/config.h> #include <lektor/mkv.h> #include <lektor/database.h> -#include <lektor/repo.h> -#include <lektor/utils.h> +#include <lektor/net.h> +#include <lektor/common.h> #include <stdio.h> #include <stdlib.h> @@ -63,33 +63,10 @@ open_db(void) fail("Not found database->init_script"); } -noreturn void -help(void) -{ - static const char *help__ = - "USAGE lktadm <COMMAND> [ARGS [...]]:\n" - "\n" - "COMMANDS:\n" - " init the init sub command\n" - " get <id> get the metadata of a kara from kurisu\n" - " download <id> <path> download\n" - " cat <path> display the metadata of a mkv file\n" - " conf prints the default config file to stdout\n" - "\n" - "INIT COMMANDS:\n" - " database write the default empty database\n" - " popualte populate the database from the filesystem\n" - " metadata write metadata to mkv files on the disk using theirs path\n" - " file <path> init the metadata for a single file by its path\n" - "\n"; - write(1, help__, strlen(help__)); - exit(EXIT_SUCCESS); -} - noreturn void conf__(struct lkt_cmd_args *args) { - (void) args; + UNUSED(args); fprintf(stdout, "%s\n", lkt_default_config_file); LOG_INFO("%s", "You may redirect this output to ~/.config/lektor/lektor.ini"); exit(EXIT_SUCCESS); @@ -119,7 +96,7 @@ cat__(struct lkt_cmd_args *args) noreturn void init_metadata__(struct lkt_cmd_args *args) { - (void) args; + UNUSED(args); open_db(); metadata_set_directory(kara_dir, mkvpropedit); exit(EXIT_SUCCESS); @@ -128,8 +105,10 @@ init_metadata__(struct lkt_cmd_args *args) noreturn void init_populate__(struct lkt_cmd_args *args) { - (void) args; + UNUSED(args); open_db(); + if (!database_open(db, db_path)) + fail("Failed to open database"); database_update(db, kara_dir); sqlite3_close(db); exit(EXIT_SUCCESS); @@ -194,7 +173,7 @@ get__(struct lkt_cmd_args *args) int i = atoi(args->argv[0]); - if (repo_new(&repo, "kurisu", "https://kurisu.iiens.net")) + if (repo_new(&repo, "kurisu", "https://kurisu.iiens.net", NULL)) fail("Cound not create repo"); if (repo_get_id(&repo, i, &data)) @@ -216,37 +195,9 @@ get__(struct lkt_cmd_args *args) noreturn void init_database__(struct lkt_cmd_args *args) { - (void) args; - pid_t pid; - int wstatus, status, fd; + UNUSED(args); open_db(); - char *sqlite_args[] = { sqlite3_bin, db_path, NULL }; - - if ((pid = fork()) == 0) { - if ((fd = open(init_script, O_RDONLY)) < 0) - fail("Can't open %s in O_RDONLY", init_script); - - if (dup2(fd, 0) < 0) - fail("Failed to duplicate %s to stdin", init_script); - - execvp(sqlite_args[0], sqlite_args); - fail("Failed to execute %s: %s", sqlite3_bin, strerror(errno)); - } - - else if (pid < 0) - fail("Failed to fork: %s", strerror(errno)); - - else { - do - if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) - fail("Failed to wait children: %s\n", strerror(errno)); - while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); - - if ((status = WEXITSTATUS(wstatus))) - fail("Children failed with status %d\n", status); - } - - exit(EXIT_SUCCESS); + exit(!database_init(db_path)); } noreturn void @@ -259,10 +210,10 @@ download__(struct lkt_cmd_args *args) struct kara_metadata data; int i = atoi(args->argv[0]); - if (repo_new(&repo, "kurisu", "https://kurisu.iiens.net")) + if (repo_new(&repo, "kurisu", "https://kurisu.iiens.net", NULL)) fail("Could not create the repo"); - if (repo_download_id_sync(&repo, NULL, i, args->argv[1], &data)) + if (repo_download_id_sync(&repo, i, args->argv[1], &data)) fail("Cound not download json for kara %d", i); printf("Kara %d at %s\n" @@ -288,7 +239,7 @@ static struct lkt_cmd_opt options_init[] = { noreturn void init__(struct lkt_cmd_args *args) { - lkt_cmd_parse(options_init, args->argc, args->argv, help); + lkt_cmd_parse(options_init, args->argc, args->argv, NULL); } static struct lkt_cmd_opt options[] = { diff --git a/src/mkv/mkv.c b/src/mkv/mkv.c index 8cb9dc6aa9cc9091dd9e319d2f8feacaa211dde0..39efb961d862e45aed4616d9259ae61693524455 100644 --- a/src/mkv/mkv.c +++ b/src/mkv/mkv.c @@ -7,7 +7,7 @@ #include <lektor/bufferfd.h> #include <lektor/mkv.h> -#include <lektor/utils.h> +#include <lektor/common.h> #define MKV_TAG_MAX 64 diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c index 6ec3defe8b96ce5667fc47e791bc5cc82ecb62b1..2432ca9d591188348e67630ebf577fe0cb9b634a 100644 --- a/src/module/module_sdl2.c +++ b/src/module/module_sdl2.c @@ -36,6 +36,10 @@ struct module_sdl2_window { mthread_mutex_t mtx; struct poller_thread self; volatile int launched; /* SDL you sucks */ + + /* Things from the server */ + volatile sqlite3 *db; + volatile enum mpd_idle_flag *mpd_idle_events; }; /* Private functions. */ @@ -43,14 +47,14 @@ struct module_sdl2_window { static inline void * get_proc_address_mpv(void *fn_ctx, const char *name) { - (void) fn_ctx; + UNUSED(fn_ctx); return SDL_GL_GetProcAddress(name); } static inline void on_mpv_events(void *ctx) { - (void) ctx; + UNUSED(ctx); SDL_Event event = { .type = wakeup_on_mpv_events }; SDL_PushEvent(&event); } @@ -58,7 +62,7 @@ on_mpv_events(void *ctx) static inline void on_mpv_render_update(void *ctx) { - (void) ctx; + UNUSED(ctx); SDL_Event event = { .type = wakeup_on_mpv_render_update }; SDL_PushEvent(&event); } @@ -182,6 +186,11 @@ loop: if (flags & MPV_RENDER_UPDATE_FRAME) redraw = 1; } + + if (event.type == wakeup_on_mpv_events) + lmpv_handle((struct lkt_win *) win, (mpv_handle *) sdl2->mpv, (sqlite3 *) sdl2->db, + (enum mpd_idle_flag *) &sdl2->mpd_idle_events, (int *) &sdl2->mpv_time_pos, + (int *) &sdl2->mpv_duration); } if (redraw) { @@ -219,16 +228,17 @@ module_set_function(void *arg__, void *handle__) RETURN_UNLESS(arg__ && handle__, "Invalid argument", 1); struct lkt_win *win = (struct lkt_win *) arg__; - win->new = module_sdl2_new; - win->close = module_sdl2_close; - win->free = module_sdl2_free; - win->toggle_pause = module_sdl2_toggle_pause; - win->load_file = module_sdl2_load_file; - win->set_volume = module_sdl2_set_volume; - win->get_duration = module_sdl2_get_duration; - win->get_elapsed = module_sdl2_get_elapsed; - win->handle_events = module_sdl2_handle_events; - win->handle = handle__; + win->new = module_sdl2_new; + win->close = module_sdl2_close; + win->free = module_sdl2_free; + win->toggle_pause = module_sdl2_toggle_pause; + win->load_file = module_sdl2_load_file; + win->set_volume = module_sdl2_set_volume; + win->get_duration = module_sdl2_get_duration; + win->get_elapsed = module_sdl2_get_elapsed; + win->handle_events = module_sdl2_handle_events; + win->attach = module_sdl2_attach; + win->handle = handle__; return 0; } @@ -251,7 +261,7 @@ module_sdl2_new(struct lkt_win *const win) struct poller_thread_arg *arg = calloc(1, sizeof(struct poller_thread_arg)); RETURN_UNLESS(arg, "Out of memory", false); arg->args = win; - RETURN_IF(lkt_th_new(&((struct module_sdl2_window *) win->window)->self, + RETURN_IF(poller_new(&((struct module_sdl2_window *) win->window)->self, LKT_DEFAULT_LIST_SIZE, sdl_thread__, arg), "Failed to launch the SDL thread", false); } @@ -325,11 +335,19 @@ module_sdl2_get_elapsed(struct lkt_win *const win, int *elapsed_sec) return true; } +void +module_sdl2_attach(struct lkt_win *const win, void *server) +{ + RETURN_UNLESS(win && server, "Invalid arguments", NOTHING); + struct lkt_state *srv = server; + struct module_sdl2_window *sdl2 = win->window; + sdl2->db = srv->db; + sdl2->mpd_idle_events = &srv->mpd_idle_events; +} + bool module_sdl2_handle_events(struct lkt_win *const win, sqlite3 *db, enum mpd_idle_flag *mpd_idle_events) { - struct module_sdl2_window *sdl2 = win->window; - RETURN_UNLESS(sdl2, "Can't handle events from a NULL window", false); - return ! lmpv_handle(win, (mpv_handle *) sdl2->mpv, db, mpd_idle_events, - (int *) &sdl2->mpv_time_pos, (int *) &sdl2->mpv_duration); + UNUSED(win, db, mpd_idle_events); + return true; } diff --git a/src/module/module_x11.c b/src/module/module_x11.c index a8f821c53b4a4b1aff0ab9ed6b0e1a86e2473e5d..bec80557594f741c7df43ad4f07fa5230d75afec 100644 --- a/src/module/module_x11.c +++ b/src/module/module_x11.c @@ -1,5 +1,8 @@ +#define _POSIX_C_SOURCE 200809L + +#include <common/common.h> #include <lektor/module/module_x11.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <lektor/module/mpv.h> #include <lektor/database.h> #include <lektor/commands.h> @@ -46,16 +49,17 @@ module_set_function(void *arg__, void *handle__) RETURN_UNLESS(arg__ && handle__, "Invalid arguments", 1); struct lkt_win *win = (struct lkt_win *) arg__; - win->new = module_x11_new; - win->close = module_x11_close; - win->free = module_x11_free; - win->toggle_pause = module_x11_toggle_pause; - win->load_file = module_x11_load_file; - win->set_volume = module_x11_set_volume; - win->get_duration = module_x11_get_duration; - win->get_elapsed = module_x11_get_elapsed; - win->handle_events = module_x11_handle_events; - win->handle = handle__; + win->new = module_x11_new; + win->close = module_x11_close; + win->free = module_x11_free; + win->toggle_pause = module_x11_toggle_pause; + win->load_file = module_x11_load_file; + win->set_volume = module_x11_set_volume; + win->get_duration = module_x11_get_duration; + win->get_elapsed = module_x11_get_elapsed; + win->handle_events = module_x11_handle_events; + win->attach = module_x11_attach; + win->handle = handle__; return 0; } @@ -211,6 +215,12 @@ module_x11_free(struct lkt_win *const win) win->window = NULL; } +void +module_x11_attach(struct lkt_win *const win, void *server) +{ + UNUSED(win, server); +} + bool module_x11_toggle_pause(struct lkt_win *const win) { diff --git a/src/module/mpv.c b/src/module/mpv.c index bf193417d71e92510491f64c558b4ec03d95edfd..735ba8d91509a97efeef61f41e4e9a62c0b9fc46 100644 --- a/src/module/mpv.c +++ b/src/module/mpv.c @@ -4,7 +4,7 @@ #include <lektor/module/mpv.h> #include <lektor/commands.h> #include <lektor/database.h> -#include <lektor/macro.h> +#include <lektor/common.h> #include <stdio.h> #include <string.h> #include <unistd.h> @@ -133,7 +133,7 @@ lmpv_handle(struct lkt_win *win, mpv_handle *ctx, sqlite3 *db, enum mpd_idle_fla struct lkt_queue_state state; mpv_event *event = NULL; mpv_event_property *prop; - RETURN_UNLESS(ctx && win, "Invalid argument", 1); + RETURN_UNLESS(db && mpd_idle_events && ctx && win, "Invalid argument", 1); RETURN_UNLESS(database_queue_state(db, &state), "Failed to get queue state", 1); loop: @@ -184,6 +184,13 @@ loop: && prop->format == MPV_FORMAT_FLAG) database_queue_set_paused(db, *(bool *) prop->data); break; + + /* Ignored */ + case MPV_EVENT_VIDEO_RECONFIG: + case MPV_EVENT_AUDIO_RECONFIG: + case MPV_EVENT_COMMAND_REPLY: + break; + default: LOG_WARN_SCT("WINDOW", "Unhandled mpv event '%s'", mpv_event_name(event->event_id)); break; diff --git a/src/mthread/mthread.c b/src/mthread/mthread.c index 4944174f9b70ed5ce3dae073c32d02ec46122b75..561a779a0674e356bc535f02615f4201c91c54f2 100644 --- a/src/mthread/mthread.c +++ b/src/mthread/mthread.c @@ -20,6 +20,11 @@ __mthread_clear_keys(mthread_t *thread) unsigned int i; struct keys_list *keys = &(*thread)->keys; + if (!keys) { + keys->first_avail = 0; + return; + } + for (i = 0; i < keys->first_avail; ++i) mthread_key_delete(keys->list[i]); @@ -36,9 +41,10 @@ mthread_list_init(mthread_list_t *list) static inline void mthread_init_thread(struct mthread_s *thread) { - thread->next = NULL; - thread->status = RUNNING; - thread->res = NULL; + thread->detached = JOINABLE; + thread->next = NULL; + thread->status = RUNNING; + thread->res = NULL; } void @@ -91,17 +97,13 @@ extern int mthread_list_test(struct mthread_s *item, mthread_list_t *list) { struct mthread_s *it = (struct mthread_s *) list->last; - while (it && it != item) it = (struct mthread_s *) it->next; - return it == item; } static inline int -mthread_mctx_set(struct mthread_s *mctx, - void (*func) (void *), char *stack, size_t size, - void *arg) +mthread_mctx_set(struct mthread_s *mctx, void (*func) (void *), char *stack, size_t size, void *arg) { /* fetch current context */ RETURN_IF(getcontext(&(mctx->uc)) != 0, "Error in getcontext", 1); @@ -202,8 +204,7 @@ mthread_get_vp_rank() } static inline void -mthread_init_vp(mthread_virtual_processor_t *vp, struct mthread_s *idle, - struct mthread_s *current, int rank) +mthread_init_vp(mthread_virtual_processor_t *vp, struct mthread_s *idle, struct mthread_s *current, int rank) { vp->current = current; vp->idle = idle; @@ -216,7 +217,7 @@ mthread_init_vp(mthread_virtual_processor_t *vp, struct mthread_s *idle, void * mthread_main(void *arg) { - (void)arg; + UNUSED(arg); mthread_virtual_processor_t *vp = mthread_get_vp(); while (1) { sched_yield(); @@ -233,7 +234,6 @@ mthread_init_lib(long i) char *stack = (char *) safe_malloc(MTHREAD_DEFAULT_STACK); struct mthread_s *mctx = (struct mthread_s *) safe_malloc(sizeof(struct mthread_s)); mthread_init_thread(mctx); - mthread_list_init(&(joined_list)); if (i == 0) { @@ -256,12 +256,16 @@ static void mthread_start_thread(void *arg) { struct mthread_s *mctx = (struct mthread_s *)arg; - LOG_INFO_SCT("THREAD INIT", "Thread %p started", arg); + LOG_INFO_SCT("THREAD", "Thread %p started", arg); mthread_virtual_processor_t *vp = mthread_get_vp(); __mthread_yield(vp); mctx->res = mctx->__start_routine(mctx->arg); mctx->status = ZOMBIE; - LOG_INFO_SCT("THREAD END", "Thread %p ended (%d)", arg, vp->rank); + LOG_INFO_SCT("THREAD", "Thread %p ended (%d)", arg, vp->rank); + if (mctx->detached) { + LOG_INFO_SCT("THREAD", "Thread %p (vp: %d) was detached, join it", arg, vp->rank); + mthread_join(mctx, NULL); + } vp = mthread_get_vp(); __mthread_yield(vp); } @@ -289,8 +293,7 @@ mthread_init(void) if ATTR is NULL), and call function START_ROUTINE with given arguments ARG. */ int -mthread_create(mthread_t *__threadp, const mthread_attr_t *__attr, - void *(*__start_routine) (void *), void *__arg) +mthread_create(mthread_t *__threadp, const mthread_attr_t __attr, void *(*__start_routine) (void *), void *__arg) { if (is_mthread_init == 0) { __mthread_lib_init(0); @@ -299,23 +302,30 @@ mthread_create(mthread_t *__threadp, const mthread_attr_t *__attr, mthread_virtual_processor_t *vp = mthread_get_vp(); - if (__attr == NULL) { - struct mthread_s *mctx = mthread_remove_first(&(joined_list)); - if (mctx == NULL) { - mctx = safe_malloc(sizeof(struct mthread_s)); - memset(mctx, 0, sizeof(struct mthread_s)); - } - char *stack = mctx->stack ? mctx->stack : safe_malloc(MTHREAD_DEFAULT_STACK); - - mthread_init_thread(mctx); - LOG_INFO_SCT("THREAD INIT", "Create thread %p", (void *) mctx); - mctx->arg = __arg; - mctx->__start_routine = __start_routine; - mthread_mctx_set(mctx, mthread_start_thread, stack, MTHREAD_DEFAULT_STACK, mctx); - mthread_insert_last(mctx, &(vp->ready_list)); + struct mthread_s *mctx = mthread_remove_first(&(joined_list)); + if (mctx == NULL) { + mctx = safe_malloc(sizeof(struct mthread_s)); + memset(mctx, 0, sizeof(struct mthread_s)); + } + char *stack = mctx->stack ? mctx->stack : safe_malloc(MTHREAD_DEFAULT_STACK); + + mthread_init_thread(mctx); + + if (__attr) { + if (__attr & ATTR_DETACHED) + mctx->detached = DETACHED; + if (__attr & ATTR_DETACHED_FREE) + mctx->detached = DETACHED_FREE; + } + + LOG_INFO_SCT("THREAD INIT", "Create thread %p", (void *) mctx); + mctx->arg = __arg; + mctx->__start_routine = __start_routine; + mthread_mctx_set(mctx, mthread_start_thread, stack, MTHREAD_DEFAULT_STACK, mctx); + mthread_insert_last(mctx, &(vp->ready_list)); + + if (__threadp) *__threadp = mctx; - } else - not_implemented(); return 0; } diff --git a/src/mthread/mthread_cond.c b/src/mthread/mthread_cond.c index 00ebe3098d947dd670f2c56511f250828b9bc2bf..ba25bae0edf1343cd114e6d31ffb5e2ca502fc04 100644 --- a/src/mthread/mthread_cond.c +++ b/src/mthread/mthread_cond.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> #include <stdio.h> #include <stdlib.h> diff --git a/src/mthread/mthread_debug.c b/src/mthread/mthread_debug.c index 8adcdc702748a074547bade6992c5654ceae9d2a..3230f8163e040045d7781b1c524770ec013b4ed5 100644 --- a/src/mthread/mthread_debug.c +++ b/src/mthread/mthread_debug.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> #include <assert.h> diff --git a/src/mthread/mthread_key.c b/src/mthread/mthread_key.c index fc9741bc40ce4bff6fc115586a50de65e65ab9ce..bfd5403cee699c1de3a06a60af0024d317ab73be 100644 --- a/src/mthread/mthread_key.c +++ b/src/mthread/mthread_key.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> #include <errno.h> #include <stdlib.h> diff --git a/src/mthread/mthread_mutex.c b/src/mthread/mthread_mutex.c index b82ec5f1924b9dabcc8996bd3a3185e72aa15ac1..b9ee9843b5fbcf17fe7e0c35827cf2e9ea0e8b90 100644 --- a/src/mthread/mthread_mutex.c +++ b/src/mthread/mthread_mutex.c @@ -1,3 +1,6 @@ +#define _POSIX_C_SOURCE 200809L + +#include <common/common.h> #include <errno.h> #include <string.h> #include <mthread/mthread_internal.h> @@ -9,14 +12,12 @@ int mthread_mutex_init(mthread_mutex_t *__mutex, const mthread_mutexattr_t *__mutex_attr) { - (void) __mutex_attr; - + UNUSED(__mutex_attr); __mutex->list = safe_malloc(sizeof(mthread_list_t)); __mutex->list->first = NULL; __mutex->list->last = NULL; __mutex->nb_thread = 0; __mutex->lock = 0; - LOG_INFO("%s", "MUTEX initialized"); return 0; } diff --git a/src/mthread/mthread_once.c b/src/mthread/mthread_once.c index acab4bcf75aae21aa4fb6314e77fd3e23ef7f3ab..9a269a39c13c61a7fac78fd713321ee9d8a17416 100644 --- a/src/mthread/mthread_once.c +++ b/src/mthread/mthread_once.c @@ -1,5 +1,8 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> #include <errno.h> + /* Functions for handling initialization. */ /* Guarantee that the initialization function INIT_ROUTINE will be called diff --git a/src/mthread/mthread_sem.c b/src/mthread/mthread_sem.c index 9c5534aea1896b3412f931fae2f0793b85fad2fb..6d5acbcfc7b2ea4ce26c15b13e2fc33b364e21c8 100644 --- a/src/mthread/mthread_sem.c +++ b/src/mthread/mthread_sem.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> /* Functions for handling semaphore. */ diff --git a/src/mthread/mthread_tst.c b/src/mthread/mthread_tst.c index 35bae1659166ef8c934ebbf7e2671a8bbe1623cf..422eb485afe966b0e3aa996f797bdb03da6dcf9b 100644 --- a/src/mthread/mthread_tst.c +++ b/src/mthread/mthread_tst.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include <mthread/mthread_internal.h> #include <sched.h> diff --git a/src/repo/curl.c b/src/net/downloader.c similarity index 50% rename from src/repo/curl.c rename to src/net/downloader.c index 03fb6e98d73fd5038a4ab92a53b470792be0ffde..76a9307a49ae7d002b7962c781e0b721d1a2f040 100644 --- a/src/repo/curl.c +++ b/src/net/downloader.c @@ -1,21 +1,22 @@ #define _POSIX_C_SOURCE 200809L -#include <lektor/repo.h> -#include <lektor/macro.h> -#include <lektor/database.h> #include <errno.h> -#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> #include <string.h> +#include <limits.h> +#include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> -static volatile unsigned int curl_init = false; +#include <common/common.h> +#include <mthread/mthread.h> +#include <lektor/common.h> +#include <lektor/database.h> +#include <lektor/net.h> -#define URL_MAX_LEN 1024 -#define DEFAULT_URL "https://kurisu.iiens.net" -#define GET_ID_JSON DEFAULT_URL "/api_karas.php?id=%ld" -#define GET_ID_FILE DEFAULT_URL "/download.php?id=%ld" +static volatile unsigned int curl_init = false; struct memory { void *mem; @@ -52,7 +53,7 @@ write_disk__(char *data, size_t size, size_t nmem, void *user) } int -repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_) +repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_, volatile sqlite3 *db) { if (!curl_init) { curl_global_init(CURL_GLOBAL_ALL); @@ -60,24 +61,18 @@ repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_) } else ++curl_init; - const size_t init_size = 30; - uint64_t *calloc1 = calloc(init_size, sizeof(uint64_t)); - RETURN_UNLESS(calloc1, "Out of memory", errno = ENOMEM); - uint64_t *calloc2 = calloc(init_size, sizeof(uint64_t)); - if (!calloc2) { - free(calloc1); - LOG_ERROR_SCT("MEMORY", "%s", "Out of memory"); - return ENOMEM; - } - struct lkt_repo repo = { + /* Just the repo */ .name = name_, .base_url = url_, .get_id_json = GET_ID_JSON, .get_id_file = GET_ID_FILE, - .get_all_json = DEFAULT_URL "/api_karas.php", + .get_all_json = DEFAULT_URL "/api_karas.php", // TODO .kara_dir = "/home/kara/", // TODO .version = 1, + + /* The db */ + .db = db, }; memcpy(repo_, &repo, sizeof(struct lkt_repo)); @@ -87,16 +82,12 @@ repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_) void repo_free(struct lkt_repo *const repo) { + UNUSED(repo); --curl_init; - - free((void *) repo->base_url); - free((void *) repo->kara_dir); - if (!curl_init) curl_global_cleanup(); } - -int +static inline int safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len) { const char *got; @@ -109,20 +100,41 @@ safe_json_get_string(struct json_object *jobj, const char *key, char *content, c return 0; } -int +static inline int safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret) { struct json_object *field; RETURN_UNLESS(json_object_object_get_ex(json, key, &field), "Key not found", 1); errno = 0; *ret = json_object_get_int(field); - RETURN_IF(errno = EINVAL, "Invalid 32bit integer", 1); RETURN_IF(*ret == INT32_MAX || *ret == INT32_MIN, "Out of bound 32bit integer", 1); return 0; } +static inline long +get_digit_number(long i) +{ + int count = 0; + while (i) { + i /= 10; + ++count; + } + return count; +} + int -repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json) +safe_json_get_long(struct json_object *json, const char *key, long *ret) +{ + const int len = get_digit_number(LONG_MAX); + char content[len], *endptr, err; + if (safe_json_get_string(json, key, content, len)) + return 1; + STRTOL(*ret, content, endptr, err); + return err; +} + +static inline int +__json_sync(struct lkt_repo *const repo, struct json_object **json) { RETURN_UNLESS(json, "Invalid argument", 1); @@ -162,10 +174,9 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata CURLcode res; struct json_object *jobj, *field; int ret = 1, err = 0; + char url[URL_MAX_LEN]; RETURN_UNLESS(mdt, "Invalid argument", 1); - char *url = calloc(URL_MAX_LEN, sizeof(char)); - RETURN_UNLESS(url, "Out of memory", errno = ENOMEM); struct memory file = { .mem = NULL, @@ -180,11 +191,9 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - res = curl_easy_perform(curl_handle); - if (res != CURLE_OK) { + if (CURLE_OK != (res = curl_easy_perform(curl_handle))) { LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res)); - free(url); free(file.mem); curl_easy_cleanup(curl_handle); return 1; @@ -232,87 +241,201 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata ret = 0; err: - free(url); json_object_put(jobj); /* Delete object. */ return ret; } -int -repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t id, const char *kara_path, - struct kara_metadata *mdt_ret) +static inline int +__download_kara(const char *url, const char *path, int override) { - RETURN_UNLESS(kara_path, "Invalid argument", 1); - struct kara_metadata mdt; CURL *curl_handle; - int ret = 1, fd; - char *url = calloc(URL_MAX_LEN, sizeof(char)); - char *ct; - RETURN_UNLESS(url, "Out of memory", errno = ENOMEM); - - if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) { - LOG_ERROR("%s", "Failed to get kara metadata from kurisu"); - goto err_no_curl; - } - errno = 0; - fd = open(kara_path, - O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW, - S_IRUSR | S_IWUSR); + char *ct, ret = 1; + int fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR); +retest: if (fd < 0) { - if (errno == EEXIST) - LOG_ERROR("File '%s' already exists", kara_path); + if (errno == EEXIST && ! override) + LOG_ERROR("File '%s' already exists", path); + + else if (errno == EEXIST && override) { + if (unlink(path)) { + LOG_ERROR_SCT("REPO", "Failed to unlink file '%s'", path); + return 1; + } + + fd = open(path, O_WRONLY | O_CREAT | O_NOFOLLOW, S_IRUSR | S_IWUSR); + override = false; + goto retest; + } else - LOG_ERROR("Could not open file '%s'", kara_path); + LOG_ERROR("Could not open file '%s'", path); - goto err_no_curl; + return 1; } - /* Download the kara... (TODO) */ - struct file file = { - .path = kara_path, + .path = path, .fd = fd, }; - memset(url, 0, URL_MAX_LEN * sizeof(char)); - snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id); - url[URL_MAX_LEN - 1] = 0; curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_disk__); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - ret = curl_easy_perform(curl_handle); - if (ret != CURLE_OK) { + if (CURLE_OK != (ret = curl_easy_perform(curl_handle))) { LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(ret)); goto err; } - if (CURLE_OK == ( ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct))) - LOG_INFO_SCT("CURL", "Content-Type is '%s'", ct); - else { + if (CURLE_OK != (ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct))) { LOG_ERROR_SCT("CURL", "Failed to get Content-Type: %s", curl_easy_strerror(ret)); goto err; } - if (!db) { - LOG_INFO("%s", "Skip database update here"); - goto no_db_update; + ret = 0; +err: + curl_easy_cleanup(curl_handle); + return ret; +} + +int +repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt_ret) +{ + RETURN_UNLESS(kara_path, "Invalid argument", 1); + struct kara_metadata mdt; + char url[URL_MAX_LEN]; + + if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) { + LOG_ERROR("%s", "Failed to get kara metadata from kurisu"); + return 1; } - if (! database_update_add(db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) { + memset(url, 0, URL_MAX_LEN * sizeof(char)); + snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id); + url[URL_MAX_LEN - 1] = 0; + + if (__download_kara(url, kara_path, false)) { + LOG_ERROR_SCT("REPO", "Failed to download kara '%s' with url '%s'", kara_path, url); + return 1; + } + + if (repo->db && !database_update_add((sqlite3 *) repo->db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) { LOG_ERROR("%s", "Failed to add kara to database"); - goto err; + return 1; + } + + return 0; +} + +/* Get all the kara, make them unavailable */ + +static inline void +__handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_object *json) +{ + size_t i, len = json_object_array_length(json); + struct json_object *kara_json; + int32_t integer, err; + struct kara *kara; + char url[URL_MAX_LEN]; + long filestamp, timestamp; + RETURN_UNLESS(len > 0 && json_object_get_array(json), "Json invalid or array empty", NOTHING); + + LOG_INFO_SCT("REPO", "Starting to process json for repo %s", repo->name); + for (i = 0; i < len; ++i) { + kara_json = json_object_array_get_idx(json, i); + kara = calloc(1, sizeof(struct kara)); + RETURN_UNLESS(kara, "Out of memory", NOTHING); + err = 0; + + /* Get the id of the kara. */ + if (safe_json_get_int32(kara_json, "id", &integer)) + continue; + kara->id = integer; + + /* Craft a fake filepath here, it will be used later. */ + size_t kara_dir_len = strlen(repo->kara_dir); + memcpy(kara->filename, repo->kara_dir, sizeof(char) * (kara_dir_len + 1)); + if (kara->filename[kara_dir_len - 1] != '/') { + strncat(kara->filename, "/", PATH_MAX - 1); + kara->filename[++kara_dir_len] = 0; + } + snprintf(kara->filename + kara_dir_len, PATH_MAX - kara_dir_len, "%d.mkv", integer); + kara->filename[PATH_MAX - 1] = 0; + LOG_INFO_SCT("REPO", "Crafted filename is '%s'", kara->filename); + + /* Timestamp verification */ + if (safe_json_get_long(kara_json, "unix_timestamp", ×tamp)) + continue; + filestamp = get_mtime(kara->filename); + if (database_get_timestamp(db) < filestamp) { + LOG_WARN_SCT("REPO", "Kara file %s is more recent than database timestamp, download it", kara->filename); + goto do_it; + } + if (filestamp > timestamp) { + LOG_INFO_SCT("REPO", "Ignore kara '%ld', last timestamp was %ld, new is %ld", kara->id, filestamp, timestamp); + continue; + } +do_it: + + err |= safe_json_get_string(kara_json, "song_name", kara->mdt.song_name, LEKTOR_TAG_MAX); + err |= safe_json_get_string(kara_json, "source_name", kara->mdt.source_name, LEKTOR_TAG_MAX); + err |= safe_json_get_string(kara_json, "category", kara->mdt.category, LEKTOR_TAG_MAX); + err |= safe_json_get_string(kara_json, "language", kara->mdt.language, LEKTOR_TAG_MAX); + err |= safe_json_get_string(kara_json, "author_name", kara->mdt.author_name, LEKTOR_TAG_MAX); + err |= safe_json_get_string(kara_json, "song_type", kara->mdt.song_type, LEKTOR_TAG_MAX); + GOTO_IF(err, "Invalid json", err); + + if (safe_json_get_int32(kara_json, "song_number", &kara->mdt.song_number)) + goto err; + + if (!database_update_add((sqlite3 *) db, kara->filename, &kara->mdt, kara->id, false)) { + LOG_ERROR_SCT("REPO", "Could not add unavailable kara %ld to db", kara->id); + continue; + } + + snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, kara->id); + url[URL_MAX_LEN - 1] = '\0'; + + if (__download_kara(url, kara->filename, true)) { + LOG_WARN_SCT("REPO", "Could not download kara %ld at path '%s'", kara->id, kara->filename); + continue; + } + + if (!database_update_set_available((sqlite3 *) db, kara->id)) { + LOG_WARN_SCT("REPO", "Could not set kara %ld available", kara->id); + continue; + } + + database_stamp(db); + + LOG_INFO_SCT("REPO", "Added kara %ld from repo %s, filepath is %s", kara->id, repo->name, kara->filename); } -no_db_update: - ret = 0; err: - curl_easy_cleanup(curl_handle); -err_no_curl: - free(url); - return ret; + free(kara); +} + +static inline void * +__repo_get_all_id_async(void *arg) +{ + struct json_object *json; + struct lkt_repo *const repo = arg; + LOG_INFO_SCT("REPO", "Download kara list from %s (%s), directory is %s", + repo->name, repo->base_url, repo->kara_dir); + __json_sync(repo, &json); + __handle_got_json(repo->db, repo, json); + LOG_INFO_SCT("REPO", "%s", "Finished to download and insert kara list"); + json_object_put(json); + database_updated(repo->db); + return NULL; +} + +inline int +repo_update(struct lkt_repo *const repo) +{ + return mthread_create(NULL, ATTR_DETACHED, __repo_get_all_id_async, repo); } diff --git a/src/net/listen.c b/src/net/listen.c index 9a6a226d9ee4ce6bfc0da82ba0a88919b54afa94..690c1844c6111d77864a47a7a01d5fd648c59a14 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -1,10 +1,8 @@ #define _POSIX_C_SOURCE 200809L #include <lektor/commands.h> -#include <lektor/macro.h> -#include <lektor/define.h> +#include <lektor/common.h> #include <lektor/database.h> -#include <lektor/repo.h> #include <lektor/net.h> #include <ini/ini.h> @@ -155,16 +153,18 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd) case MPD_IDLE_NONE: /* Commands that requires authentification. */ if (!strcmp(cmd.name, "__adduser")) - err = ! command_user_add(srv, c, srv->db, cmd.args); + err = ! command_user_add(srv, c, (sqlite3 *) srv->db, cmd.args); else if (!strcmp(cmd.name, "__restart")) err = ! command_restart(srv, c); else if (!strcmp(cmd.name, "kill")) err = ! command_kill(srv, c); + else if (!strcmp(cmd.name, "update")) + err = ! command_update(srv, c, cmd.args); else if (!strcmp(cmd.name, "rescan")) err = ! command_rescan(srv, c, cmd.args); /* Commands that are available if not in idle mode */ - if (!strcmp(cmd.name, "currentsong")) + else if (!strcmp(cmd.name, "currentsong")) err = !command_currentsong(srv, c); else if (!strcmp(cmd.name, "status")) err = !command_status(srv, c); @@ -175,25 +175,26 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd) err = 0; else if (!strcmp(cmd.name, "next")) - err = !command_next(srv->db, &srv->win, &srv->mpd_idle_events); + err = !command_next((sqlite3 *) srv->db, &srv->win, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "pause")) - err = !command_pause(srv->db, &srv->win, &srv->mpd_idle_events); + err = !command_pause((sqlite3 *) srv->db, &srv->win, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "previous")) - err = !command_previous(srv->db, &srv->win, &srv->mpd_idle_events); + err = !command_previous((sqlite3 *) srv->db, &srv->win, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "play")) - err = ! command_play(srv->db, &srv->win, cmd.args, &srv->mpd_idle_events); + err = ! command_play((sqlite3 *) srv->db, &srv->win, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "playid")) - err = ! command_playid(srv->db, &srv->win, cmd.args, &srv->mpd_idle_events); + err = ! command_playid((sqlite3 *) srv->db, &srv->win, cmd.args, + (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "stop")) - err = !command_stop(srv->db, &srv->win, &srv->mpd_idle_events); + err = !command_stop((sqlite3 *) srv->db, &srv->win, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "clear")) - err = !command_clear(srv->db, &srv->mpd_idle_events); + err = !command_clear((sqlite3 *) srv->db, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "crop")) - err = !command_crop(srv->db, &srv->mpd_idle_events); + err = !command_crop((sqlite3 *) srv->db, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "moveid")) - err = !command_move(srv->db, cmd.args, &srv->mpd_idle_events); + err = !command_move((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "shuffle")) - err = !command_shuffle(srv->db, &srv->mpd_idle_events); + err = !command_shuffle((sqlite3 *) srv->db, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "playlist") || !strcmp(cmd.name, "playlistinfo")) err = !command_queue_list(srv, c, cmd.args); @@ -213,31 +214,29 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd) else if (!strcmp(cmd.name, "help")) err = !command_help(srv, c); - else if (!strcmp(cmd.name, "__dbupdate")) - err = !command_update(srv, &srv->mpd_idle_events); else if (!strcmp(cmd.name, "add")) - err = !command_add(srv->db, &srv->win, cmd.args, &srv->mpd_idle_events); + err = !command_add((sqlite3 *) srv->db, &srv->win, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "addid")) - err = !command_addid(srv->db, &srv->win, cmd.args, &srv->mpd_idle_events); + err = !command_addid((sqlite3 *) srv->db, &srv->win, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "deleteid")) err = ! (cmd.args[0] != NULL && - command_delid(srv->db, &srv->win, cmd.args[0], &srv->mpd_idle_events)); + command_delid((sqlite3 *) srv->db, &srv->win, cmd.args[0], (enum mpd_idle_flag *) &srv->mpd_idle_events)); else if (!strcmp(cmd.name, "playlistclear")) - err = ! command_plt_clear(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_clear((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "rename")) - err = ! command_plt_rename(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_rename((sqlite3 * ) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "playlistdelete")) - err = ! command_plt_remove(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_remove((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "playlistadd")) - err = ! command_plt_add(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_add((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "rm") && cmd.args[0] != NULL && cmd.args[1] == NULL) - err = ! command_plt_remove(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_remove((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "save")) - err = ! command_plt_export(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_export((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "__import")) - err = ! command_plt_import(srv->db, cmd.args, &srv->mpd_idle_events); + err = ! command_plt_import((sqlite3 *) srv->db, cmd.args, (enum mpd_idle_flag *) &srv->mpd_idle_events); else if (!strcmp(cmd.name, "random")) err = !command_set_playback_option(srv, c, lkt_playback_option_random, cmd.args); @@ -686,53 +685,6 @@ lkt_client_auth(struct lkt_state *srv, size_t c, bool set) return srv->clients[c - 1].authentificated |= set; } -static inline void -handle_repo_hevents(struct lkt_state *srv) -{ - struct kara *kara; - - for (;;) { - if (repo_get_kara_async(&kara)) - goto get_out; - - switch (kara->action) { - /* Add the downloaded kara to the database. */ - case kara_action_add: - if (!database_update_add(srv->db, kara->filename, &kara->mdt, kara->id, true)) { - LOG_ERROR("Failed to add downloaded kara with id %lu and path %s", kara->id, kara->filename); - goto get_out; - } - - LOG_INFO("Added kara %lu with path %s to database", kara->id, kara->filename); - - break; - - /* Add the mdt of the kara to the database. Mark it unavailable. */ - case kara_action_unavail: - if (!database_update_add(srv->db, kara->filename, &kara->mdt, kara->id, false)) { - LOG_ERROR("Failed to add kara with id %lu with flag unavailable", kara->id); - goto get_out; - } - - LOG_INFO("Added kara %lu to database and set it to unavailable", kara->id); - - break; - - case kara_action_none: - default: - break; - } - - free(kara); - kara = NULL; - } - - /* Just get out of the loop. */ -get_out: - if (kara) - free(kara); -} - int lkt_listen(void) { @@ -740,7 +692,6 @@ lkt_listen(void) RETURN_UNLESS(memory, "Out of memory", 5); struct lkt_state srv; - struct lkt_repo repo; int autoclear; char *const db_path = memory; /* Size is PATH_MAX. */ char *const kara_dir = memory + PATH_MAX + 1; /* Size is PATH_MAX. */ @@ -751,26 +702,26 @@ lkt_listen(void) memset(&srv, 0, sizeof(struct lkt_state)); /* Initialize the system. */ - RETURN_UNLESS(database_new(&srv.db), "Failed to initialize the memory database", 1); + RETURN_UNLESS(database_new((sqlite3 **) &srv.db), "Failed to initialize the memory database", 1); RETURN_IF(config_detect_file(conf_file, PATH_MAX), "Failed to find a config file", 1); - RETURN_IF(config_new(srv.db, conf_file), "Failed to read configuration file", 1); + RETURN_IF(config_new((sqlite3 *) srv.db, conf_file), "Failed to read configuration file", 1); /* Finish to initialize. */ - RETURN_UNLESS(database_config_get_text(srv.db, "database", "db_path", db_path, PATH_MAX), "Cfg error", 2); - RETURN_UNLESS(database_open(srv.db, db_path), "Can't open database", 1); + RETURN_UNLESS(database_config_get_text((sqlite3 *) srv.db, "database", "db_path", db_path, PATH_MAX), "Cfg error", 2); + RETURN_UNLESS(database_open((sqlite3 *) srv.db, db_path), "Can't open database", 1); /* Read the configuration. */ - RETURN_UNLESS(database_config_get_int(srv.db, "player", "autoclear", &autoclear), "Cfg error", 2); - RETURN_UNLESS(database_config_get_text(srv.db, "database", "kara_dir", kara_dir, PATH_MAX), "Cfg error", 2); - RETURN_UNLESS(database_config_get_text(srv.db, "server", "host", host, HOST_NAME_MAX), "Cfg error", 2); - RETURN_UNLESS(database_config_get_text(srv.db, "server", "port", port, 5), "Cfg error", 2); - RETURN_UNLESS(database_config_get_text(srv.db, "player", "module", player_mod, INI_MAX_LINE), "Cfg error", 2); + RETURN_UNLESS(database_config_get_int( (sqlite3 *) srv.db, "player", "autoclear", &autoclear), "Cfg error", 2); + RETURN_UNLESS(database_config_get_text((sqlite3 *) srv.db, "database", "kara_dir", kara_dir, PATH_MAX), "Cfg error", 2); + RETURN_UNLESS(database_config_get_text((sqlite3 *) srv.db, "server", "host", host, HOST_NAME_MAX), "Cfg error", 2); + RETURN_UNLESS(database_config_get_text((sqlite3 *) srv.db, "server", "port", port, 5), "Cfg error", 2); + RETURN_UNLESS(database_config_get_text((sqlite3 *) srv.db, "player", "module", player_mod, INI_MAX_LINE), "Cfg error", 2); if (kara_dir[strlen(kara_dir) - 1] != '/') strncat(kara_dir, "/", PATH_MAX - 1); srv.kara_prefix = kara_dir; - database_config_queue_default(srv.db); + database_config_queue_default((sqlite3 *) srv.db); srv.fds_max = 16; srv.fds = calloc(srv.fds_max, sizeof(struct pollfd)); @@ -784,24 +735,21 @@ lkt_listen(void) srv.fds_len = 1; if (autoclear) - database_queue_clear(srv.db); + database_queue_clear((sqlite3 *) srv.db); - RETURN_UNLESS(load_module_by_name(srv.db, player_mod, &srv.win), "Can't load module", 3); + RETURN_UNLESS(load_module_by_name((sqlite3 *) srv.db, player_mod, &srv.win), "Can't load module", 3); RETURN_UNLESS(srv.win.new(&srv.win), "Can't create window", 3); - RETURN_IF(repo_new(&repo, "kurisu", "https://kurisu.iiens.net"), "Failed to create repo", 4); - RETURN_IF(repo_new_thread(&repo), "Failed to launch repo thread", 4); + srv.win.attach(&srv.win, &srv); + RETURN_IF(repo_new(&srv.repo, "kurisu", "https://kurisu.iiens.net", srv.db), "Failed to create repo", 4); for (;;) { if (handle_network_events(&srv) < 0) break; if (handle_idle_events(&srv) < 0) break; - srv.win.handle_events(&srv.win, srv.db, &srv.mpd_idle_events); - handle_repo_hevents(&srv); + srv.win.handle_events(&srv.win, (sqlite3 *) srv.db, (enum mpd_idle_flag *) &srv.mpd_idle_events); } - repo_join_thread(); srv.win.free(&srv.win); - return -1; } diff --git a/src/repo/async.c b/src/repo/async.c deleted file mode 100644 index aeeb98a4ed99c217a35448cbf93cdd8aa8d46efb..0000000000000000000000000000000000000000 --- a/src/repo/async.c +++ /dev/null @@ -1,233 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <limits.h> - -#include <mthread/mthread.h> -#include <lektor/macro.h> -#include <lektor/repo.h> -#include <lektor/thread.h> - -static struct poller_thread repo_thread; - -static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; -static volatile int init = 0; -static volatile int stop = 0; -static volatile int all_json = 0; - -int -repo_join_thread(void) -{ - int ret = 1; - RETURN_IF(pthread_mutex_lock(&mtx), "Failed to lock mutex", 3); - GOTO_UNLESS(init, "Repo thread no launched, can't join", error); - stop = 1; - GOTO_IF(pthread_join(repo_thread.th, NULL), "Failed to join repo thread", error); - LOG_INFO("%s", "repo thread joined"); - ret = 0; -error: - RETURN_IF(pthread_mutex_unlock(&mtx), "Failed to unlock mutex", 3); - return ret; -} - -/* Find it in the repo/curl.c file. */ -extern int -safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len); -extern int -safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret); - -static inline void -__handle_got_json(struct poller_thread *self, struct lkt_repo *repo, struct json_object *json) -{ - size_t i, len = json_object_array_length(json); - struct json_object *kara_json; - int32_t integer; - struct kara *kara; - int err; - - RETURN_UNLESS(len > 0 && json_object_get_array(json), "Json invalid or array empty", NOTHING); - - for (i = 0; i < len; ++i) { - kara_json = json_object_array_get_idx(json, i); - kara = calloc(1, sizeof(struct kara)); - err = 0; - - /* Get the id of the kara. */ - if (safe_json_get_int32(kara_json, "id", &integer)) - goto err; - - /* Craft a fake filepath here, it will be used later. */ - size_t kara_dir_len = strlen(repo->kara_dir); - memcpy(kara->filename, repo->kara_dir, sizeof(char) * (kara_dir_len + 1)); - if (kara->filename[kara_dir_len - 1] != '/') { - strncat(kara->filename, "/", PATH_MAX - 1); - kara->filename[++kara_dir_len] = 0; - } - integer = snprintf(kara->filename + kara_dir_len, PATH_MAX - kara_dir_len, "%d", integer); - kara->filename[PATH_MAX - 1] = 0; - LOG_INFO("Crafted filename is '%s'", kara->filename); - - RETURN_UNLESS(kara, "Out of memory", NOTHING); - - /* Get the fields from the json. */ - err |= safe_json_get_string(kara_json, "song_name", kara->mdt.song_name, LEKTOR_TAG_MAX); - err |= safe_json_get_string(kara_json, "source_name", kara->mdt.source_name, LEKTOR_TAG_MAX); - err |= safe_json_get_string(kara_json, "category", kara->mdt.category, LEKTOR_TAG_MAX); - err |= safe_json_get_string(kara_json, "language", kara->mdt.language, LEKTOR_TAG_MAX); - err |= safe_json_get_string(kara_json, "author_name", kara->mdt.author_name, LEKTOR_TAG_MAX); - err |= safe_json_get_string(kara_json, "song_type", kara->mdt.song_type, LEKTOR_TAG_MAX); - - if (err) - goto err; - - /* Get the song number. */ - if (safe_json_get_int32(kara_json, "song_number", &kara->mdt.song_number)) - goto err; - - /* Append. */ - if (lkt_th_append_output(self, kara)) { - LOG_ERROR("%s", "Could not append downloaded kara mdt"); - goto err; - } - - continue; -err: - free(kara); - } -} - -static void * -__repo_thread_function(struct poller_thread_arg *arg) -{ - size_t head; - struct lkt_repo *repo = arg->args; - struct poller_thread *self = arg->self; - struct kara *kara; - struct json_object *json = NULL; - char path[PATH_MAX]; - free(arg); - - LOG_INFO("%s", "Starting the repo thread"); - - for (;;) { - GOTO_IF(pthread_mutex_lock(&mtx), "Failed to lock mutex", end_loop); - - if (all_json) { - repo_get_alljson_sync(repo, &json); - __handle_got_json(self, repo, json); - json_object_put(json); - } - - if (stop) { - if (pthread_mutex_unlock(&mtx)) - LOG_ERROR("Failed to unlock mutex: %s", strerror(errno)); - break; - } - - head = 0; - - /* size_t has the size of a pointer (thus of a void *). */ - if (lkt_th_pop_input(self, (void **) &head)) { - LOG_ERROR("%s", "Failed to get the head of the input list"); - goto end_loop; /* Just skip all the loop to the yield function. */ - } - - /* Did we pop something? */ - if (NULL == (void *) head) - goto end_loop; - - snprintf(path, PATH_MAX - 1, "%s%lu.mkv", repo->kara_dir, head); - path[PATH_MAX - 1] = 0; - kara = calloc(1, sizeof(struct kara)); - - if (NULL == kara) { - LOG_ERROR_SCT("MEMORY", "%s", "Out of memory"); - goto end_loop; - } - - if (repo_download_id_sync(repo, NULL, head, path, &kara->mdt)) { - LOG_ERROR("Failed to download kara with id %lu", head); - goto try_later; - } - - /* Copy data to the structure that we will pass to the main thread. */ - kara->action = kara_action_add; - kara->id = head; - memcpy(kara->filename, path, (strlen(path) + 1) * sizeof(char)); - - if (lkt_th_append_output(self, (void *) kara)) { - LOG_ERROR("Failed to append to output, orphan kara %lu", head); - free(kara); - goto end_loop; - } - - LOG_INFO("Append kara %lu with path %s to out pool", kara->id, kara->filename); - - kara = NULL; - goto end_loop; - -try_later: - if (kara) - free(kara); - - /* Retry later. TODO: Implements a retry counter. */ - if (lkt_th_append_input(self, (void *) head)) - LOG_ERROR("%s", "Failed to get the head of the input list"); - -end_loop: - sched_yield(); - sleep(1); - } - - LOG_INFO("%s", "Stopping the repo thread"); - return NULL; -} - -int -repo_new_thread(struct lkt_repo *const repo) -{ - RETURN_IF(init, "Already running", 1); - struct poller_thread_arg *arg = calloc(1, sizeof(struct poller_thread_arg)); - RETURN_UNLESS(arg, "Out of memory", errno = ENOMEM); - arg->args = repo; - RETURN_IF(lkt_th_new(&repo_thread, LKT_DEFAULT_LIST_SIZE, __repo_thread_function, arg), "Thread error", 1); - init = 1; - return 0; -} - -int -repo_download_id_async(const size_t id) -{ - RETURN_IF(id == 0, "Invalid argument", 1); - RETURN_IF(lkt_th_append_input(&repo_thread, (void *) id), "Failed to push downloaded id", id); - LOG_INFO("Asked to download kara with id %lu", id); - return 0; -} - -int -repo_get_kara_async(struct kara **downloaded) -{ - /* Is there a kara that has been downloaded? */ - if (lkt_th_pop_output(&repo_thread, (void **) downloaded)) - goto err; - - if (!*downloaded) - goto err; - - return 0; -err: - *downloaded = NULL; - return 1; -} - -inline int -repo_get_allid_async(void) -{ - RETURN_IF(pthread_mutex_lock(&mtx), "Failed to lock mutex", 3); - all_json = 1; - RETURN_IF(pthread_mutex_unlock(&mtx), "Failed to lock mutex", 3); - return 0; -} diff --git a/src/thread.c b/src/thread.c index 4969c93320d752211f0c7482bbf487dced727a33..e4e3f1074945c1cae565f986c7d74efb1439bfc8 100644 --- a/src/thread.c +++ b/src/thread.c @@ -26,7 +26,7 @@ __start(void *args__) } int -lkt_th_new(struct poller_thread *th, unsigned int init_sizes, +poller_new(struct poller_thread *th, unsigned int init_sizes, void *(*func)(struct poller_thread_arg *), void *args) { pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER; @@ -73,7 +73,7 @@ out_of_memory: } int -lkt_th_join(struct poller_thread *th, void **ret) +poller_join(struct poller_thread *th, void **ret) { int sta = pthread_join(th->th, ret); @@ -157,21 +157,21 @@ end: } int -lkt_th_append_input(struct poller_thread *th, void *ptr) +poller_append_input(struct poller_thread *th, void *ptr) { return th_append((unsigned int *) &th->input_len, (unsigned int *) &th->input_size, (void ***) &th->input_cells, &th->input_lock, ptr); } int -lkt_th_append_output(struct poller_thread *th, void *ptr) +poller_append_output(struct poller_thread *th, void *ptr) { return th_append((unsigned int *) &th->output_len, (unsigned int *) &th->output_size, (void ***) &th->output_cells, &th->output_lock, ptr); } int -lkt_th_pop_input(struct poller_thread *th, void **ptr) +poller_pop_input(struct poller_thread *th, void **ptr) { return th_pop((unsigned int *) &th->input_len, (void **) th->input_cells, &th->input_lock, ptr); @@ -179,20 +179,20 @@ lkt_th_pop_input(struct poller_thread *th, void **ptr) int -lkt_th_pop_output(struct poller_thread *th, void **ptr) +poller_pop_output(struct poller_thread *th, void **ptr) { return th_pop((unsigned int *) &th->output_len, (void **) th->output_cells, &th->output_lock, ptr); } int -lkt_th_head_input(struct poller_thread *th, void **ptr) +poller_head_input(struct poller_thread *th, void **ptr) { return th_head((unsigned int) th->input_len, (void **) th->input_cells, &th->input_lock, ptr); } int -lkt_th_head_output(struct poller_thread *th, void **ptr) +poller_head_output(struct poller_thread *th, void **ptr) { return th_head((unsigned int) th->output_len, (void **) th->output_cells, &th->output_lock, ptr); } diff --git a/src/uri.c b/src/uri.c index 8219ef1a587bd20eb875d1c39363d32327bc45dd..ac8634be845720818b767601c8f5358d318dcd2d 100644 --- a/src/uri.c +++ b/src/uri.c @@ -7,7 +7,7 @@ #include <limits.h> bool -lkt_uri_from(struct lkt_uri_t *ret, char *const str) +lkt_uri_from(struct lkt_uri *ret, char *const str) { char *val, *endptr; size_t len; @@ -80,7 +80,7 @@ lkt_uri_from(struct lkt_uri_t *ret, char *const str) } void -lkt_uri_free(struct lkt_uri_t *ret) +lkt_uri_free(struct lkt_uri *ret) { if (NULL == ret) return; @@ -88,3 +88,47 @@ lkt_uri_free(struct lkt_uri_t *ret) if (ret->_allocated) free(ret->value); } + +/* Support a subrange of URIs. */ +static inline int +__lkt_to_str(struct lkt_uri *uri, char *ret, size_t len) +{ + switch (uri->type) { + case uri_id: + snprintf(ret, len - 1, "id=%s", (char *) uri->value); + break; + case uri_type: + snprintf(ret, len - 1, "type=%s", (char *) uri->value); + break; + case uri_author: + snprintf(ret, len - 1, "author=%s", (char *) uri->value); + break; + case uri_category: + snprintf(ret, len - 1, "cat=%s", (char *) uri->value); + break; + case uri_query: + snprintf(ret, len - 1, "search=%s", (char *) uri->value); + break; + default: + LOG_ERROR("URI type %d may not be supported by kurisu, generate an error", uri->type); + return 1; + } + + ret[len - 1] = '\0'; + return 0; +} + +int +lkt_uri_to_url(struct lkt_uri *uri, const char *base_url, char *ret, size_t len) +{ + RETURN_UNLESS(uri && base_url && ret && len > 0, "Invalid argument", 1); + + /* Craft URL */ + memset(ret, 0, len); + memcpy(ret, base_url, sizeof(char) * strlen(base_url)); + strncat(ret, "?", len - 1); + + /* The query */ + size_t init_len = strlen(ret); + return __lkt_to_str(uri, ret + init_len, len - init_len); +} diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index c01ce5a628ed3c640f8bf76051d1b31653defefe..0000000000000000000000000000000000000000 --- a/src/utils.c +++ /dev/null @@ -1,114 +0,0 @@ -#include <lektor/utils.h> -#include <string.h> -#include <stdio.h> - -uint32_t -be_uint32_t(const uint8_t bytes[], size_t n) -{ - uint32_t res = 0; - for (size_t i = 0; i < n; i++) - res = (res << 8u) | bytes[i]; - return res; -} - -uint64_t -be_uint64_t(const uint8_t bytes[], size_t n) -{ - uint64_t res = 0; - for (size_t i = 0; i < n; i++) - res = (res << 8u) | bytes[i]; - return res; -} - -char * -trim(char *str, size_t len, char c) -{ - char *res = (char *) str; - char *end; - - for (; len != 0 && *res == c; ++str, --len) - continue; - - if (*res == 0) - return res; - - end = res + len - sizeof(char); - len = 0; - - for (; res < end && *end == c; --end, ++len) - continue; - - if (len > 0) - end[1] = 0; - - return res; -} - -int -is_utf8(const char *string) -{ - if (!string) - return 1; - - const unsigned char *bytes = (const unsigned char *)string; - while (*bytes) { - /* ASCII */ - if (bytes[0] == 0x09 || bytes[0] == 0x0A || - bytes[0] == 0x0D || (0x20 <= bytes[0] && bytes[0] <= 0x7E)) { - bytes += 1; - continue; - } - - /* non-overlong 2-byte */ - if ((0xC2 <= bytes[0] && bytes[0] <= 0xDF) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF)) { - bytes += 2; - continue; - } - - if ( (bytes[0] == 0xE0 && - (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF)) || /* excluding overlongs */ - (((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || - bytes[0] == 0xEE || bytes[0] == 0xEF) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF)) || /* straight 3-byte */ - (bytes[0] == 0xED && - (0x80 <= bytes[1] && bytes[1] <= 0x9F) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF))) { /* excluding surrogates */ - bytes += 3; - continue; - } - - if ( (bytes[0] == 0xF0 && /* planes 1-3 */ - (0x90 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF)) || - ((0xF1 <= bytes[0] && bytes[0] <= 0xF3) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF)) || /* planes 4-15 */ - (bytes[0] == 0xF4 && - (0x80 <= bytes[1] && bytes[1] <= 0x8F) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF))) { /* plane 16 */ - bytes += 4; - continue; - } - - return 2; - } - - return 0; -} - -int -get_stdin_line(const char *prompt, char *buf, size_t len) -{ - if (prompt) - fprintf(stdout, "%s", prompt); - if (!fgets(buf, len, stdin)) - return 1; - buf[len - 1] = 0u; - return is_utf8(buf); -}