diff --git a/player/db/update.c b/player/db/update.c index 0a2dec9e7d5c5b13989ec73707775184fe1b3912..cb5b1116332cd04ad0a85136b7941fa509c54067 100644 --- a/player/db/update.c +++ b/player/db/update.c @@ -5,8 +5,22 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> -#define SQL_LAST_UPDATE "SELECT last_update FROM misc;" +static const char SQL_LAST_UPDATE[] = "SELECT last_update FROM misc;"; +static const char SQL_INSERT_OLD_KARA[] = + "INSERT INTO " + "kara (song_name, source_name, category, language, file_path, is_new) " + "VALUES (?, ?, ?, ?, ?, 0)"; +static const char SQL_INSERT_NEW_KARA[] = + "INSERT INTO " + "kara (song_name, source_name, category, language, file_path," + " is_new, author_name) " + "VALUES (?, ?, ?, ?, ?, 1, ?)"; + +static void +convert_legacy_category_type(const char *category, size_t category_len, + const char *type, size_t type_len); static void serror(sqlite3 *db, const char *msg) @@ -23,24 +37,131 @@ mallocf(size_t n) return res; } +static inline void * +reallocf(void *ptr, size_t n) +{ + void *res = realloc(ptr, n); + if (!res) + fprintf(stderr, "Failed to allocate %ld bytes.\n", n); + return res; +} + static int directories_and_files(const struct dirent *entry) { - return entry->d_type == DT_REG || entry->d_type == DT_DIR; + return (entry->d_type == DT_REG || entry->d_type == DT_DIR) && + entry->d_name[0] != '.'; +} + +static void +convert_legacy_category_type(const char *category, size_t category_len, + const char *type, size_t type_len) +{ + // TODO convert category and type -> category and language } static int -update_file(sqlite3 *db, const char *filename, int last_update) +update_legacy_file(sqlite3 *db, const char *filename, size_t prefix, time_t last_update) { - // TODO - return 0; + struct stat attrs; + if (stat(filename, &attrs) < 0) + return -1; + if (attrs.st_mtim.tv_sec < last_update) + return 0; + + const char *pseudo = 0; + size_t pseudo_len = 0; + const char *category = 0; + size_t category_len = 0; + const char *name = 0; + size_t name_len = 0; + const char *type = 0; + size_t type_len = 0; + const char *title = 0; + size_t title_len = 0; + + const char *file_path = filename + prefix; + const char *f = file_path; + category_len = strcspn(f, "/"); + if (!strncmp(f, "nouveaux", category_len)) { + // "Nouveau" kara, in nouveaux/{pseudo}/{category}/... + f += category_len + 1; // Skip "nouveaux/" + pseudo_len = strcspn(f, "/"); + pseudo = f; + f += pseudo_len + 1; // Skip "{pseudo}/" + category_len = strcspn(f, "/"); + } + + category = f; + f += category_len + 1; // Skip "{category}/" + name_len = strcspn(f, "-"); + name = f; + f += name_len + 1; + type_len = strcspn(f, "-"); + type = f; + f += type_len + 1; + title_len = strcspn(f, "."); + title = f; + + if (!title) { + fprintf(stderr, "Bad file path: '%s'.\n", filename); + return -1; + } + + convert_legacy_category_type(category, category_len, type, type_len); + + int status_code = -1; + sqlite3_stmt *stmt = 0; + const char *sql = pseudo ? SQL_INSERT_NEW_KARA : SQL_INSERT_OLD_KARA; + + if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) { + serror(db, "Failed to prepare statement"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, name, name_len, 0) != SQLITE_OK) { + serror("Failed to bind song_name"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, title, title_len, 0) != SQLITE_OK) { + serror("Failed to bind song_source"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, category, -1, 0) != SQLITE_OK) { + serror("Failed to bind category"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, type, -1, 0) != SQLITE_OK) { + serror("Failed to bind language"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, file_path, -1, 0) != SQLITE_OK) { + serror("Failed to bind file_path"); + goto error; + } + + if (sqlite3_step(stmt) != SQLITE_DONE) { + // TODO handle SQLITE_BUSY (should rollback) + serror("Failed to execute insert statement"); + goto error; + } + + status_code = 0; +error: + sqlite3_finalize(stmt); + return status_code; } static int -update_directory(sqlite3 *db, const char *directory, int last_update) +update_directory(sqlite3 *db, const char *root, time_t last_update) { int status_code = -1; - char **queue; + const size_t root_len = strlen(root) + 1; + char **queue = 0; size_t queue_max = 128; size_t queue_len = 0; @@ -48,7 +169,7 @@ update_directory(sqlite3 *db, const char *directory, int last_update) if (!queue) goto error; - queue[0] = strdup(directory); + queue[0] = strdup(root); queue_len++; while (queue_len) { @@ -61,18 +182,28 @@ update_directory(sqlite3 *db, const char *directory, int last_update) fprintf(stderr, "Failed to open '%s': %s.\n", dir, strerror(errno)); for (int i = 0; i < n; i++) { - size_t dirlen = strlen(dir); - char *child = mallocf(dirlen + 258); // child length (256) + null byte + slash + size_t dir_len = strlen(dir); + + // child length (256) + null byte + slash + char *child = mallocf(dir_len + 258); if (!child) goto error; - strcpy(child, dir); - child[dirlen] = '/'; - strcpy(child + dirlen + 1, children[i]->d_name); + memcpy(child, dir, dir_len); + child[dir_len] = '/'; + strcpy(child + dir_len + 1, children[i]->d_name); if (children[i]->d_type == DT_REG) { - update_file(db, child, last_update); + if (update_legacy_file(db, child, root_len, last_update) < 0) + fprintf(stderr, "Failed to add '%s'.\n", child); free(child); + } else if (queue_len == queue_max) { + queue_max *= 2; + char **new_queue = reallocf(queue, queue_max * sizeof(char *)); + if (!new_queue) + goto error; + queue = new_queue; + queue[queue_len++] = child; } else { queue[queue_len++] = child; } @@ -96,7 +227,7 @@ lektor_db_update(sqlite3 *db, const char *directory) } sqlite3_stmt *stmt = 0; - int last_update = 0; + time_t last_update = 0; if (sqlite3_prepare_v2(db, SQL_LAST_UPDATE, -1, &stmt, 0) != SQLITE_OK) { serror(db, "Failed to get last update time"); @@ -104,7 +235,7 @@ lektor_db_update(sqlite3 *db, const char *directory) } switch (sqlite3_step(stmt)) { case SQLITE_ROW: - last_update = sqlite3_column_int(stmt, 0); + last_update = (time_t) sqlite3_column_int(stmt, 0); break; case SQLITE_DONE: fprintf(stderr, "Failed to get last update time: table misc is empty");