/** * part of Hydrilla * Program entry point. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. */ #define _POSIX_C_SOURCE 200809L /* S_IFMT, S_IFDIR */ #include <dirent.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <cjson/cJSON.h> #include "string_buf.h" #include "hashtable.h" #include "scriptbase.h" #define PRESERVE_ERRNO(call) \ do { \ int VERY_UNLIKELY_TO_COLLIDE_NAME_$$$$$$$$##__LINE__ = errno; \ call; \ errno = VERY_UNLIKELY_TO_COLLIDE_NAME_$$$$$$$$##__LINE__; \ } while (0) static const char default_search_path[] = CONTENTDIR; static int process_scriptbase_subdir(struct scriptbase *base, struct dirent *subdir, struct stringbuf *path_buf, struct stringbuf *json_buf) { struct stat statbuf; cJSON *index_json; int retval = -1; size_t initial_len = path_buf->buf_filled, len_dirname; if (sb_string(path_buf, subdir->d_name)) return -1; len_dirname = path_buf->buf_filled; if (stat(path_buf->buf, &statbuf)) return -1; if ((statbuf.st_mode & S_IFMT) != S_IFDIR) return 0; if (sb_string(path_buf, "/index.json")) return -1; printf("Reading %s\n", path_buf->buf); stringbuf_truncate(json_buf, 0); if (sb_filepath(json_buf, path_buf->buf)) return -1; index_json = cJSON_Parse(json_buf->buf); if (!index_json) { fprintf(stderr, "Failed to parse json.\n"); return -1; } stringbuf_truncate(path_buf, len_dirname); retval = catalogue_component(path_buf->buf + initial_len, index_json, base); PRESERVE_ERRNO(cJSON_Delete(index_json)); return retval; } static int prepare_scriptbase_from_dir(const char *dir_path, struct scriptbase *base) { DIR *maindir = NULL; struct dirent *subdir; struct stringbuf path_buf; struct stringbuf json_buf; size_t base_path_len; int retval = -1; printf("Searching %s\n", dir_path); stringbuf_init(&path_buf); stringbuf_init(&json_buf); if (scriptbase_init(base, "https://hydrilla.koszko.org/resources")) goto end; maindir = opendir(dir_path); if (!maindir) goto end; if (sb_sprintf(&path_buf, "%s/", dir_path)) goto end; base_path_len = path_buf.buf_filled; while (true) { errno = 0; subdir = readdir(maindir); if (!subdir) { if (!errno) retval = 0; break; } if (!strcmp(subdir->d_name, "..") || !strcmp(subdir->d_name, ".")) continue; stringbuf_truncate(&path_buf, base_path_len); errno = 0; if (process_scriptbase_subdir(base, subdir, &path_buf, &json_buf)) { fprintf(stderr, "Error processing subdirectory %s%s", subdir->d_name, errno ? ": " : ".\n"); if (errno) perror(NULL); } } end: if (errno) perror(NULL); stringbuf_destroy(&path_buf); stringbuf_destroy(&json_buf); if (maindir) closedir(maindir); if (retval) scriptbase_destroy(base); printf("Search in %s %s.\n", dir_path, retval ? "failed" : "finished"); return retval; } static void print_component_name(const void *key, void *val, void *arg) { char type = (char) (size_t) arg; bool unfilled; unfilled = (type == 's' && !((struct script*) val)->filled) || (type == 'b' && !((struct bag*) val)->filled); printf("%s%s\n", (const char*) key, unfilled ? " (referenced only)" : ""); } int serve_scriptbase(struct scriptbase *base, bool); int main(int argc, char *argv[]) { struct scriptbase base; const char *search_path = default_search_path; if (argc > 1) search_path = argv[1]; if (prepare_scriptbase_from_dir(search_path, &base)) return -1; puts("## LOADED SCRIPTS:"); ht_map(&base.scripts, (void*) 's', print_component_name); puts("## LOADED BAGS:"); ht_map(&base.bags, (void*) 'b', print_component_name); puts("## LOADED PAGES:"); ht_map(&base.pages, (void*) 'p', print_component_name); if (serve_scriptbase(&base, !!getenv("HYDRILLA_WAIT_SIGTERM"))) fprintf(stderr, "Error serving scriptbase.\n"); scriptbase_destroy(&base); return 0; }