diff --git a/player/db/update.c b/player/db/update.c new file mode 100644 index 0000000000000000000000000000000000000000..0a2dec9e7d5c5b13989ec73707775184fe1b3912 --- /dev/null +++ b/player/db/update.c @@ -0,0 +1,134 @@ +#include "update.h" + +#include <dirent.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define SQL_LAST_UPDATE "SELECT last_update FROM misc;" + +static void +serror(sqlite3 *db, const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, sqlite3_errmsg(db)); +} + +static inline void * +mallocf(size_t n) +{ + void *res = malloc(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; +} + +static int +update_file(sqlite3 *db, const char *filename, int last_update) +{ + // TODO + return 0; +} + +static int +update_directory(sqlite3 *db, const char *directory, int last_update) +{ + int status_code = -1; + char **queue; + size_t queue_max = 128; + size_t queue_len = 0; + + queue = mallocf(queue_max * sizeof(char *)); + if (!queue) + goto error; + + queue[0] = strdup(directory); + queue_len++; + + while (queue_len) { + struct dirent **children; + int n; + char *dir = queue[--queue_len]; + + n = scandir(dir, &children, directories_and_files, alphasort); + if (n < 0) + 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 + if (!child) + goto error; + + strcpy(child, dir); + child[dirlen] = '/'; + strcpy(child + dirlen + 1, children[i]->d_name); + + if (children[i]->d_type == DT_REG) { + update_file(db, child, last_update); + free(child); + } else { + queue[queue_len++] = child; + } + } + } + + status_code = 0; +error: + for (size_t i = 0; i < queue_len; i++) + free(queue[i]); + free(queue); + return status_code; +} + +int +lektor_db_update(sqlite3 *db, const char *directory) +{ + if (sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0) != SQLITE_OK) { + serror(db, "Failed to start transaction"); + return -1; + } + + sqlite3_stmt *stmt = 0; + int last_update = 0; + + if (sqlite3_prepare_v2(db, SQL_LAST_UPDATE, -1, &stmt, 0) != SQLITE_OK) { + serror(db, "Failed to get last update time"); + goto error; + } + switch (sqlite3_step(stmt)) { + case SQLITE_ROW: + last_update = sqlite3_column_int(stmt, 0); + break; + case SQLITE_DONE: + fprintf(stderr, "Failed to get last update time: table misc is empty"); + goto error; + default: + serror(db, "Failed to get last update time"); + goto error; + } + + if (update_directory(db, directory, last_update) < 0) + goto error; + + if (sqlite3_exec(db, "COMMIT;", 0, 0, 0) != SQLITE_OK) { + serror(db, "Failed to commit transaction"); + goto error; + } + + sqlite3_finalize(stmt); + return 0; + +error: + + sqlite3_finalize(stmt); + if (sqlite3_exec(db, "ROLLBACK;", 0, 0, 0) != SQLITE_OK) + serror(db, "Failed to rollback transaction, database is corrupted"); + return -1; +} diff --git a/player/db/update.h b/player/db/update.h new file mode 100644 index 0000000000000000000000000000000000000000..0ae98194676db61ca307a27669aa7b987e61c3d0 --- /dev/null +++ b/player/db/update.h @@ -0,0 +1,6 @@ +#pragma once + +#include <sqlite3.h> + +int +lektor_db_update(struct sqlite3 *db, const char *directory);