aboutsummaryrefslogtreecommitdiff
/**
 * 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;
}