diff options
Diffstat (limited to 'scriptbase_build.c')
-rw-r--r-- | scriptbase_build.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/scriptbase_build.c b/scriptbase_build.c new file mode 100644 index 0000000..2bb9e80 --- /dev/null +++ b/scriptbase_build.c @@ -0,0 +1,534 @@ +/** + * part of Hydrilla + * Routines for building scriptbase from resources and configuratons (stored in + * `/var/lib/hydrilla/content/' or another, user-specified location. + * + * Copyright (C) 2021 Wojtek Kosior + * Redistribution terms are gathered in the `copyright' file. + */ + +#define _POSIX_C_SOURCE 200809L /* strdup() */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "hashtable.h" +#include "string_buf.h" + +#include "scriptbase.h" + +static const char scriptstr[] = "script", bagstr[] = "bag", pagestr[] = "page"; + + +static void *add_or_get(const char *component_name, char component_type, + struct scriptbase *base); + +static struct script *script_create(const char *name) +{ + struct script *script; + + script = malloc(sizeof(struct script)); + if (!script) + return NULL; + + script->name = strdup(name); + + if (!script->name) + goto free_script; + + script->location = NULL; + script->sha256 = NULL; + script->filled = false; + + return script; + +free_script: + free(script); + + return NULL; +} + +static int script_fill(struct script *script, const char *dir, + const char *filename, const char *sha256) +{ + char *location = NULL; + size_t location_len = 0, location_filled = 0; + + if (sb_raw_sprintf(&location, &location_len, &location_filled, + "%s/%s", dir, filename)) + goto free_location; + script->location = location; + + script->sha256 = strdup(sha256); + if (!script->sha256) + goto free_location; + + script->filled = true; + + return 0; + +free_location: + free(location); + script->location = NULL; + + return -1; +} + +static void script_free(struct script *script) +{ + if (!script) + return; + + free(script->name); + free(script->location); + free(script->sha256); + free(script); +} + +static struct bag *bag_create(const char *name) +{ + struct bag *bag; + + bag = malloc(sizeof(struct bag)); + if (!bag) + return NULL; + + bag->name = strdup(name); + + if (!bag->name) + goto free_bag; + + bag->components = NULL; + bag->last_component = NULL; + bag->filled = false; + + return bag; + +free_bag: + free(bag); + + return NULL; +} + +static int bag_add(struct bag *bag, const char *component_name, + const char *component_type, struct scriptbase *base) +{ + struct component_ref *new_ref; + + new_ref = malloc(sizeof(struct component_ref)); + if (!new_ref) + return -1; + + new_ref->component.any = + add_or_get(component_name, *component_type, base); + + if (!new_ref->component.any) + goto free_ref; + + new_ref->component_type = component_type; + new_ref->next = NULL; + + *(bag->components ? &bag->last_component->next : &bag->components) = + new_ref; + bag->last_component = new_ref; + bag->filled = true; + + return 0; + +free_ref: + free(new_ref); + + return -1; +} + +static void bag_free(struct bag *bag) +{ + struct component_ref *tmp1, *tmp2; + + if (!bag) + return; + + tmp1 = bag->components; + + while (tmp1) { + tmp2 = tmp1->next; + free(tmp1); + tmp1 = tmp2; + } + + free(bag->name); + free(bag); +} + +static struct page *page_create(const char *pattern, const char *payload_name, + const char *payload_type, + struct scriptbase *base) +{ + struct page *page; + + page = malloc(sizeof(struct page)); + if (!page) + return NULL; + + page->pattern = strdup(pattern); + if (!page->pattern) + goto free_page; + + if (payload_name) { + page->payload.any = + add_or_get(payload_name, *payload_type, base); + if (!page->payload.any) + goto free_pattern; + } else { + page->payload.any = NULL; + } + + page->payload_type = payload_type; + + return page; + +free_pattern: + free(page->pattern); + +free_page: + free(page); + + return NULL; +} + +static void page_free(struct page *page) +{ + if (!page) + return; + + free(page->pattern); + free(page); +} + +static void *add_or_get(const char *component_name, char component_type, + struct scriptbase *base) +{ + void *component; + hashtable_t *relevant_ht; + bool found = true; + + relevant_ht = component_type == *bagstr ? &base->bags : &base->scripts; + if (ht_get(relevant_ht, component_name, NULL, &component)) { + if (component_type == *bagstr) + component = bag_create(component_name); + else + component = script_create(component_name); + + found = false; + } + + if (!component) + return NULL; + + /* Name is at the same position in both struct bag and struct script. */ + if (!found) { + switch (ht_add(relevant_ht, ((struct bag*) component)->name, + component)) { + case HT_NO_MEM: + errno = ENOMEM; + case HT_KEY_PRESENT: + goto free_component; + } + } + + return component; + +free_component: + if (component_type == *bagstr) + bag_free(component); + else + script_free(component); + + return NULL; +} + +int scriptbase_init(struct scriptbase *base, const char *repo_url) +{ + base->repo_url = strdup(repo_url); + if (!base->repo_url) + goto end; + + if (ht_string_init(&base->scripts)) + goto free_url; + + if (ht_string_init(&base->bags)) + goto free_scripts; + + if (ht_string_init(&base->pages)) + goto free_bags; + + return 0; + +free_bags: + ht_destroy(&base->bags); + +free_scripts: + ht_destroy(&base->scripts); + +free_url: + free(base->repo_url); + +end: + errno = ENOMEM; + return -1; +} + +static void destroy_cb(void *key, void *val, void *arg) +{ + char type = *((const char*) arg); + + if (type == 's') + script_free(val); + else if (type == 'b') + bag_free(val); + else + page_free(val); +} + +void scriptbase_destroy(struct scriptbase *base) +{ + char keys[] = {'s', 'b', 'p'}; + + ht_map_destroy(&base->scripts, keys, destroy_cb); + ht_map_destroy(&base->bags, keys + 1, destroy_cb); + ht_map_destroy(&base->pages, keys + 2, destroy_cb); + + free(base->repo_url); +} + +static int add_component_to_base(const char *name, const void *component, + const char *type, struct scriptbase *base) +{ + hashtable_t *ht = *type == 's' ? &base->scripts : + *type == 'b' ? &base->bags : &base->pages; + bool name_collision = false; + void *old_val = NULL; + + if (ht_set(ht, name, component, NULL, &old_val)) { + errno = ENOMEM; + return -1; + } + + if (old_val) { + if (*type == 's') { + name_collision = ((struct script*) old_val)->filled; + script_free(old_val); + } else if (*type == 'b') { + name_collision = ((struct bag*) old_val)->filled; + bag_free(old_val); + } else { + name_collision = true; + page_free(old_val); + } + } + + if (name_collision) { + fprintf(stderr, "Multiple occurences of %s %s.\n", type, name); + errno = 0; + return -1; + } + + return 0; +} + +static int catalogue_script(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *name, *sha256, *location; + const char *bad = NULL; + struct script *script; + + name = cJSON_GetObjectItemCaseSensitive(index_json, "name"); + sha256 = cJSON_GetObjectItemCaseSensitive(index_json, "sha256"); + location = cJSON_GetObjectItemCaseSensitive(index_json, "location"); + + if (!cJSON_IsString(name) || name->valuestring == NULL) + bad = "name"; + else if (!cJSON_IsString(sha256) || sha256->valuestring == NULL) + bad = "sha256"; + else if (!cJSON_IsString(location) || location->valuestring == NULL) + bad = "location"; + + if (bad) { + fprintf(stderr, "Missing or invalid field \"%s\".\n", bad); + errno = 0; + return -1; + } + + script = script_create(name->valuestring); + if (!script) + return -1; + + if (script_fill(script, path, location->valuestring, + sha256->valuestring)) + goto free_script; + + return add_component_to_base(script->name, script, scriptstr, base); + +free_script: + script_free(script); + return -1; +} + +static int component_ref_from_json(const cJSON *component_cJSON, + const char **component_type, + const char **component_name) +{ + const cJSON *component_type_cJSON, *component_name_cJSON; + + if (!cJSON_IsArray(component_cJSON)) + return -1; + + component_type_cJSON = component_cJSON->child; + if (!component_type_cJSON) + return -1; + component_name_cJSON = component_type_cJSON->next; + if (!component_name_cJSON || component_name_cJSON->next) + return -1; + + if (!cJSON_IsString(component_type_cJSON) || + !cJSON_IsString(component_name_cJSON)) + return -1; + + if (!strcmp(scriptstr, component_type_cJSON->valuestring)) + *component_type = scriptstr; + else if (!strcmp(bagstr, component_type_cJSON->valuestring)) + *component_type = bagstr; + else + return -1; + + *component_name = component_name_cJSON->valuestring; + + return 0; +} + +static int catalogue_bag(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *name, *components, *component; + const char *component_type, *component_name; + struct bag *bag; + + name = cJSON_GetObjectItemCaseSensitive(index_json, "name"); + + if (!cJSON_IsString(name) || name->valuestring == NULL) { + fprintf(stderr, "Missing or invalid field \"name\".\n"); + errno = 0; + return -1; + } + + bag = bag_create(name->valuestring); + if (!bag) + return -1; + + bag->filled = true; + + components = cJSON_GetObjectItemCaseSensitive(index_json, "components"); + if (!components) + return 0; + if (!cJSON_IsArray(components)) + goto invalid_components; + + cJSON_ArrayForEach(component, components) { + if (component_ref_from_json(component, + &component_type, &component_name)) + goto invalid_components; + + /* + * component_type now points to a static buffer and + * component_name to cJSON-owned memory + */ + + if (bag_add(bag, component_name, component_type, base)) + goto free_bag; + } + + if (add_component_to_base(bag->name, bag, bagstr, base)) + goto free_bag; + + return 0; + +invalid_components: + fprintf(stderr, "Invalid field \"components\""); + errno = 0; + +free_bag: + bag_free(bag); + return -1; +} + +static int catalogue_page(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *pattern, *payload; + const char *payload_type = "", *payload_name = NULL; + struct page *page; + + pattern = cJSON_GetObjectItemCaseSensitive(index_json, "pattern"); + payload = cJSON_GetObjectItemCaseSensitive(index_json, "payload"); + + if (!cJSON_IsString(pattern) || pattern->valuestring == NULL) { + fprintf(stderr, "Missing or invalid field \"pattern\".\n"); + errno = 0; + return -1; + } + + if (!payload) + goto skip_payload; + + if (component_ref_from_json(payload, &payload_type, &payload_name)) + goto invalid_payload; + +skip_payload: + page = page_create(pattern->valuestring, payload_name, payload_type, + base); + if (!page) + goto free_page; + + if (add_component_to_base(page->pattern, page, pagestr, base)) + goto free_page; + + return 0; + +invalid_payload: + fprintf(stderr, "Invalid field \"payload\""); + errno = 0; + +free_page: + page_free(page); + return -1; +} + +int catalogue_component(const char *path, cJSON *index_json, + struct scriptbase *base) +{ + const cJSON *type; + + type = cJSON_GetObjectItemCaseSensitive(index_json, "type"); + if (!cJSON_IsString(type) || type->valuestring == NULL) + goto bad_type; + + if (!strcmp(type->valuestring, scriptstr)) + return catalogue_script(path, index_json, base); + else if (!strcmp(type->valuestring, bagstr)) + return catalogue_bag(path, index_json, base); + else if (!strcmp(type->valuestring, pagestr)) + return catalogue_page(path, index_json, base); + else + goto bad_type; + + return 0; + +bad_type: + fprintf(stderr, "Missing or invalid type."); + + errno = 0; + return -1; +} |