aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Timber <dxdt@dev.snart.me>2022-10-18 14:50:48 +0800
committerDavid Timber <dxdt@dev.snart.me>2022-10-18 14:50:48 +0800
commit8b47484919d61d389dfdefb07fe1b168021696e7 (patch)
tree7feb68d11dd73e3b98a43628572d9b70442dd085 /src
parent44d5b5b1bada4ef81a8b82612e8e248729c71e5d (diff)
Add yaml.h, hostinfod bug fix, compilation setting
- Add yaml helper functions - Add yaml test tool proone-yamlls - Add yaml test doc - Refactor hostinfod with yaml.h - Fix bug in hostinfod where specifying verbosity results in config error - Add C flag '-Werror=implicit-function-declaration' - Update doxygen template
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am11
-rw-r--r--src/data/yaml/prne_yaml-test.yaml52
-rw-r--r--src/proone-hostinfod.c325
-rw-r--r--src/proone-yamlls.c281
-rw-r--r--src/yaml.c647
-rw-r--r--src/yaml.h251
6 files changed, 1371 insertions, 196 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 02b8988..d2e31b0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ AM_CFLAGS =\
-Wno-switch\
-D_POSIX_C_SOURCE=200112L\
-Wno-unused-parameter\
+ -Werror=implicit-function-declaration\
-fdata-sections\
-ffunction-sections\
-Wl,--gc-sections
@@ -42,7 +43,10 @@ bin_PROGRAMS =\
proone-mkcdict\
proone-bne
if BUILD_MTTOOLS
-bin_PROGRAMS += proone-hostinfod proone-htbtclient
+bin_PROGRAMS +=\
+ proone-htbtclient\
+ proone-hostinfod\
+ proone-yamlls
endif
proone_tests =\
@@ -74,6 +78,9 @@ libproone_a_SOURCES =\
cred_dict.c\
bne.c\
bitfield.c
+if BUILD_MTTOOLS
+libproone_a_SOURCES += yaml.c
+endif
proone: proone.bin dvault.bin
cp -fa proone.bin proone
@@ -130,6 +137,8 @@ proone_hostinfod_LDADD = libproone.a
proone_hostinfod_SOURCES = proone-hostinfod.c
proone_htbtclient_LDADD = libproone.a
proone_htbtclient_SOURCES = proone-htbtclient.c
+proone_yamlls_LDADD = libproone.a
+proone_yamlls_SOURCES = proone-yamlls.c
endif
proone_test_proto_LDADD = libproone.a
diff --git a/src/data/yaml/prne_yaml-test.yaml b/src/data/yaml/prne_yaml-test.yaml
new file mode 100644
index 0000000..8b8d012
--- /dev/null
+++ b/src/data/yaml/prne_yaml-test.yaml
@@ -0,0 +1,52 @@
+---
+-
+ -
+ - a
+ - b
+ -
+ - 1
+ - 2
+ - "b"
+---
+main-doc:
+ mapping-0:
+ apple: fruit
+ cabbage: veg
+ orange: fruit
+ lettuce: veg
+ mapping-1:
+ persons: &the_alias
+ - name: John Doe
+ email: john.d@example.com
+ extra-data:
+ address: |
+ 137 Example St
+ Example City EX 0000
+ Example Country
+ vehicles:
+ - ABC123
+ - EXAMPLE
+ - name: Jane Doe
+ email: jane.d@example.com
+ members: *the_alias
+---
+override_dot:
+ a.b: 1
+ a:
+ b: 2
+---
+override_slash:
+ a/b: 1
+ a:
+ b: 2
+---
+lone_text_scalar
+---
+123
+---
+- "root"
+- "text"
+- "array"
+- 3.14
+---
+last-doc: hello
diff --git a/src/proone-hostinfod.c b/src/proone-hostinfod.c
index 97f4d09..96ddb38 100644
--- a/src/proone-hostinfod.c
+++ b/src/proone-hostinfod.c
@@ -36,7 +36,6 @@
#include <sys/sysinfo.h>
-#include <yaml.h>
#include <mysql/mysql.h>
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
@@ -49,6 +48,7 @@
#include "protocol.h"
#include "mbedtls.h"
#include "rnd.h"
+#include "yaml.h"
#if !defined(MBEDTLS_THREADING_C)
#error "Mbedtls must be compiled with threading support"
@@ -185,263 +185,198 @@ static void set_def_prog_param (void) {
prog_conf.listen_port = DEFCONF_LISTEN_PORT;
}
-typedef enum {
- PS_INIT,
- PS_STREAM,
- PS_DOC,
- PS_SEQ_START,
- PS_SEQ,
- PS_MAPPING_START,
- PS_MAPPING_VAL
-} parse_state_t;
-
-typedef struct {
- char *path;
- prne_llist_t path_list;
- parse_state_t state;
- bool err;
-} parse_context_t;
-
-static void yaml_parse_build_path (parse_context_t *ctx) {
- const char **sb = prne_malloc(sizeof(char*) * 2, ctx->path_list.size);
- size_t ptr = 0;
-
- for (prne_llist_entry_t *e = ctx->path_list.head; e != NULL; e = e->next) {
- sb[ptr + 0] = "/";
- sb[ptr + 1] = (char*)e->element;
- ptr += 2;
+static void dup_assign_str (char **dst, const char *src) {
+ void *ny = prne_redup_str(*dst, src);
+ if (ny == NULL) {
+ perror("prne_redup_str()");
+ abort();
}
+ *dst = (char*)ny;
+}
- ctx->path = prne_rebuild_str(ctx->path, sb, ctx->path_list.size * 2);
- assert(ctx->path != NULL);
+bool yaml_doc_end_cb (void *ctx, const yaml_event_t *event) {
+ // Accept the first doc only.
+ return false;
}
-static void yaml_parse_push_path (
- parse_context_t *ctx,
- const yaml_char_t *name)
+static bool yaml_scalar_cb (
+ void *ctx,
+ const char *val,
+ const prne_yaml_path_t *path_obj)
{
- char *tag;
- prne_llist_entry_t *ent;
+ const char *err_msg = NULL;
+ char *path = NULL;
+ bool ret = true;
- tag = prne_dup_str((const char*)name);
- ent = prne_llist_append(&ctx->path_list, (prne_llist_element_t)tag);
- assert(tag != NULL && ent != NULL);
-}
+ path = prne_yaml_path_tostr(path_obj, ".", true, NULL);
+ prne_assert(path != NULL);
-static void yaml_parse_pop_path (parse_context_t *ctx) {
- if (ctx->path_list.size == 0) {
- return;
+ if (strstr(path, ".hostinfod") != path) {
+ ret = false;
+ fprintf(stderr, "%s: invalid root\n", path);
+ goto END;
}
- prne_free((void*)ctx->path_list.tail->element);
- prne_llist_erase(&ctx->path_list, ctx->path_list.tail);
-}
-
-static void yaml_parse_dup_str (char **str, const yaml_char_t *val) {
- prne_free(*str);
- *str = prne_dup_str((const char*)val);
- assert(*str != NULL);
-}
-static bool yaml_parse_handle_node (
- parse_context_t *ctx,
- const yaml_char_t *val)
-{
- if (strcmp(ctx->path, "/hostinfod/db/host") == 0) {
- yaml_parse_dup_str(&prog_conf.db.host, val);
+ if (strcmp(path, ".hostinfod.db.host") == 0) {
+ dup_assign_str(&prog_conf.db.host, val);
}
- else if (strcmp(ctx->path, "/hostinfod/db/port") == 0) {
+ else if (strcmp(path, ".hostinfod.db.port") == 0) {
int tmp;
- ctx->err =
- sscanf((const char*)val, "%d", &tmp) != 1 ||
- !(0 < tmp && tmp <= 65535);
- if (!ctx->err) {
+ ret =
+ sscanf((const char*)val, "%d", &tmp) == 1 &&
+ (0 < tmp && tmp <= 65535);
+ if (ret) {
prog_conf.db.port = (uint16_t)tmp;
}
+ else {
+ errno = EINVAL;
+ err_msg = ".hostinfod.db.port";
+ }
}
- else if (strcmp(ctx->path, "/hostinfod/db/user") == 0) {
- yaml_parse_dup_str(&prog_conf.db.user, val);
+ else if (strcmp(path, ".hostinfod.db.user") == 0) {
+ dup_assign_str(&prog_conf.db.user, val);
}
- else if (strcmp(ctx->path, "/hostinfod/db/pw") == 0) {
- yaml_parse_dup_str(&prog_conf.db.pw, val);
+ else if (strcmp(path, ".hostinfod.db.pw") == 0) {
+ dup_assign_str(&prog_conf.db.pw, val);
}
- else if (strcmp(ctx->path, "/hostinfod/db/db") == 0) {
- yaml_parse_dup_str(&prog_conf.db.db, val);
+ else if (strcmp(path, ".hostinfod.db.db") == 0) {
+ dup_assign_str(&prog_conf.db.db, val);
}
- else if (strcmp(ctx->path, "/hostinfod/db/table_prefix") == 0) {
- yaml_parse_dup_str(&prog_conf.db.tbl_pfx, val);
+ else if (strcmp(path, ".hostinfod.db.table_prefix") == 0) {
+ dup_assign_str(&prog_conf.db.tbl_pfx, val);
}
- else if (strcmp(ctx->path, "/hostinfod/ssl/ca") == 0) {
- yaml_parse_dup_str(&prog_conf.ssl.path_ca, val);
+ else if (strcmp(path, ".hostinfod.ssl.ca") == 0) {
+ dup_assign_str(&prog_conf.ssl.path_ca, val);
}
- else if (strcmp(ctx->path, "/hostinfod/ssl/crt") == 0) {
- yaml_parse_dup_str(&prog_conf.ssl.path_crt, val);
+ else if (strcmp(path, ".hostinfod.ssl.crt") == 0) {
+ dup_assign_str(&prog_conf.ssl.path_crt, val);
}
- else if (strcmp(ctx->path, "/hostinfod/ssl/key") == 0) {
- yaml_parse_dup_str(&prog_conf.ssl.path_key, val);
+ else if (strcmp(path, ".hostinfod.ssl.key") == 0) {
+ dup_assign_str(&prog_conf.ssl.path_key, val);
}
- else if (strcmp(ctx->path, "/hostinfod/ssl/key_pw") == 0) {
- yaml_parse_dup_str(&prog_conf.ssl.key_pw, val);
+ else if (strcmp(path, ".hostinfod.ssl.key_pw") == 0) {
+ dup_assign_str(&prog_conf.ssl.key_pw, val);
}
- else if (strcmp(ctx->path, "/hostinfod/ssl/dh") == 0) {
- yaml_parse_dup_str(&prog_conf.ssl.path_dh, val);
+ else if (strcmp(path, ".hostinfod.ssl.dh") == 0) {
+ dup_assign_str(&prog_conf.ssl.path_dh, val);
}
- else if (strcmp(ctx->path, "/hostinfod/max_conn") == 0) {
- ctx->err = sscanf((const char*)val, "%zu", &prog_conf.max_conn) != 1;
+ else if (strcmp(path, ".hostinfod.max_conn") == 0) {
+ ret = sscanf(val, "%zu", &prog_conf.max_conn) == 1;
+ err_msg = ".hostinfod.max_conn";
}
- else if (strcmp(ctx->path, "/hostinfod/db_q_size") == 0) {
- ctx->err = sscanf((const char*)val, "%zu", &prog_conf.db_q_size) != 1;
+ else if (strcmp(path, ".hostinfod.db_q_size") == 0) {
+ ret = sscanf(val, "%zu", &prog_conf.db_q_size) == 1;
+ err_msg = ".hostinfod.db_q_size";
}
- else if (strcmp(ctx->path, "/hostinfod/report_int") == 0) {
+ else if (strcmp(path, ".hostinfod.report_int") == 0) {
unsigned long tmp;
- ctx->err = sscanf((const char*)val, "%lu", &tmp) != 1;
- if (!ctx->err) {
+ ret = sscanf(val, "%lu", &tmp) == 1;
+ err_msg = ".hostinfod.report_int";
+ if (ret) {
prog_conf.report_int = prne_ms_timespec(tmp);
}
}
- else if (strcmp(ctx->path, "/hostinfod/sck_op_timeout") == 0) {
+ else if (strcmp(path, ".hostinfod.sck_op_timeout") == 0) {
unsigned long tmp;
- ctx->err = sscanf((const char*)val, "%lu", &tmp) != 1;
- if (!ctx->err) {
+ ret = sscanf(val, "%lu", &tmp) == 1;
+ err_msg = ".hostinfod.sck_op_timeout";
+ if (ret) {
prog_conf.sck_op_timeout = prne_ms_timespec(tmp);
}
}
- else if (strcmp(ctx->path, "/hostinfod/nb_thread") == 0) {
- ctx->err = sscanf((const char*)val, "%u", &prog_conf.nb_thread) != 1;
+ else if (strcmp(path, ".hostinfod.nb_thread") == 0) {
+ ret = sscanf(val, "%u", &prog_conf.nb_thread) == 1;
+ err_msg = ".hostinfod.nb_thread";
}
- else if (strcmp(ctx->path, "/hostinfod/backlog") == 0) {
- ctx->err = sscanf((const char*)val, "%u", &prog_conf.backlog) != 1;
+ else if (strcmp(path, ".hostinfod.backlog") == 0) {
+ ret = sscanf(val, "%u", &prog_conf.backlog) == 1;
+ err_msg = ".hostinfod.backlog";
}
- else if (strcmp(ctx->path, "/hostinfod/listen_port") == 0) {
- ctx->err = sscanf(
- (const char*)val,
- "%"SCNu16,
- &prog_conf.listen_port) != 1;
+ else if (strcmp(path, ".hostinfod.listen_port") == 0) {
+ ret = sscanf(val, "%"SCNu16, &prog_conf.listen_port) == 1;
+ err_msg = ".hostinfod.listen_port";
}
- else if (strcmp(ctx->path, "/hostinfod/verbose") == 0) {
- ctx->err = sscanf((const char*)val, "%d", &prog_conf.verbose) != 1;
+ else if (strcmp(path, ".hostinfod.verbose") == 0) {
+ ret = sscanf(val, "%d", &prog_conf.verbose) == 1;
+ err_msg = ".hostinfod.verbose";
}
- return true;
-}
-
-static void yaml_parse_handle_doc (
- parse_context_t *ctx,
- const yaml_event_t *event)
-{
- switch (event->type) {
- case YAML_SEQUENCE_START_EVENT:
- ctx->state = PS_SEQ_START;
- break;
- case YAML_MAPPING_START_EVENT:
- ctx->state = PS_MAPPING_START;
- break;
- case YAML_SCALAR_EVENT:
- switch (ctx->state) {
- case PS_SEQ_START:
- yaml_parse_push_path(ctx, event->data.scalar.value);
- ctx->state = PS_SEQ;
- break;
- case PS_MAPPING_START:
- yaml_parse_push_path(ctx, event->data.scalar.value);
- ctx->state = PS_MAPPING_VAL;
- break;
- case PS_SEQ:
- break;
- case PS_MAPPING_VAL:
- yaml_parse_build_path(ctx);
- yaml_parse_handle_node(ctx, event->data.scalar.value);
- yaml_parse_pop_path(ctx);
- ctx->state = PS_MAPPING_START;
- break;
- }
- break;
- case YAML_SEQUENCE_END_EVENT:
- case YAML_MAPPING_END_EVENT:
- yaml_parse_pop_path(ctx);
- ctx->state = PS_MAPPING_START;
- break;
- case YAML_DOCUMENT_END_EVENT:
- ctx->state = PS_STREAM;
- break;
- default: abort();
- }
-}
-
-static bool yaml_parse_func (
- parse_context_t *ctx,
- const yaml_event_t *event)
-{
- switch (ctx->state) {
- case PS_INIT:
- assert(event->type == YAML_STREAM_START_EVENT);
- ctx->state = PS_STREAM;
- break;
- case PS_STREAM:
- if (event->type == YAML_STREAM_END_EVENT) {
- return false;
- }
- ctx->state = PS_DOC;
- assert(event->type == YAML_DOCUMENT_START_EVENT);
- break;
- default:
- yaml_parse_handle_doc(ctx, event);
+END:
+ if (!ret) {
+ *((bool*)ctx) = true;
+ prne_assert(err_msg != NULL);
+ perror(err_msg);
}
+ prne_free(path);
- return true;
+ return ret;
}
static bool load_conf (FILE *file) {
yaml_parser_t parser;
- yaml_event_t event;
- parse_context_t p_ctx;
- bool p_ret;
+ prne_yaml_ctx_t p_ctx;
+ prne_yaml_parse_opt_t p_opt;
+ prne_yaml_parse_ret_t p_ret;
+ bool err = false;
+ bool ret = false;
- prne_memzero(&p_ctx, sizeof(parse_context_t));
- prne_init_llist(&p_ctx.path_list);
+ prne_yaml_init_ctx(&p_ctx);
+ prne_yaml_init_parse_opt(&p_opt);
+
+ p_opt.uctx = &err;
+ p_opt.cb.doc_end = yaml_doc_end_cb;
+ p_opt.cb.scalar = yaml_scalar_cb;
if (yaml_parser_initialize(&parser) == 0) {
- fprintf(
- stderr,
- "*** YAML error: %s\n",
- parser.problem);
+ fprintf(stderr, "*** YAML error: %s\n", parser.problem);
abort();
}
yaml_parser_set_input_file(&parser, file);
- do {
- if (yaml_parser_parse(&parser, &event) == 0) {
+ p_ret = prne_yaml_do_parse(&parser, &p_ctx, &p_opt);
+ switch (p_ret) {
+ case PRNE_YAML_PR_CBHALT:
+ if (err) {
fprintf(
stderr,
- "*** YAML parse error %zu:%zu: %s\n",
- parser.problem_mark.line,
- parser.problem_mark.column,
- parser.problem);
- p_ctx.err = true;
- break;
- }
- p_ret = yaml_parse_func(&p_ctx, &event);
- if (p_ctx.err) {
- fprintf(
- stderr,
- "*** Config error %zu:%zu: invalid value\n",
+ "*** Config error at %zu:%zu\n",
parser.mark.line,
parser.mark.column);
break;
}
- yaml_event_delete(&event);
- } while (p_ret);
+ else {
+ // Halted at end of document. Fall through
+ }
+ /* fall-through */
+ case PRNE_YAML_PR_END:
+ ret = true;
+ break;
+ case PRNE_YAML_PR_ERRNO:
+ perror("prne_yaml_do_parse()");
+ break;
+ case PRNE_YAML_PR_APIERR:
+ fprintf(
+ stderr,
+ "*** YAML parse error %zu:%zu: %s\n",
+ parser.problem_mark.line,
+ parser.problem_mark.column,
+ parser.problem);
+ break;
+ default:
+ fprintf(
+ stderr,
+ "prne_yaml_do_parse(): %s\n",
+ prne_yaml_pr_tostr(p_ret));
+ }
yaml_parser_delete(&parser);
- for (prne_llist_entry_t *e = p_ctx.path_list.head; e != NULL; e = e->next) {
- prne_free((void*)e->element);
- }
- prne_free_llist(&p_ctx.path_list);
- prne_free(p_ctx.path);
+ prne_yaml_free_ctx(&p_ctx);
+ prne_yaml_free_parse_opt(&p_opt);
- return !p_ctx.err;
+ return ret;
}
static int setup_conf (const char *conf_path) {
diff --git a/src/proone-yamlls.c b/src/proone-yamlls.c
new file mode 100644
index 0000000..59fb851
--- /dev/null
+++ b/src/proone-yamlls.c
@@ -0,0 +1,281 @@
+/*
+* Copyright (c) 2019-2021 David Timber <mieabby@gmail.com>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include <stdio.h>
+
+#include <getopt.h>
+
+#include "yaml.h"
+#include "llist.h"
+#include "util_rt.h"
+
+#define HELP_STR \
+"Proone YAML Utility Test Tool\n"\
+"Usage: %s [options] [FILES ...]\n"\
+"\n"\
+"Options:\n"\
+" -s <SEP> specify path separator (default: '.')\n"\
+" -o allow path names to be overridden\n"\
+" -a accept aliases\n"\
+" -h print this message to stdout and exit normally\n"\
+"FILES: use '-' to read stdin. This is default if no file is passed\n"
+
+#define DEFAULT_PATH_SEP "/"
+
+
+static struct {
+ prne_llist_t files;
+ char *path_sep;
+ bool allow_override;
+ prne_yaml_parse_opt_t p_opt;
+} prog_conf;
+
+typedef struct {
+ char *path_str;
+} uctx_t;
+
+
+static void print_help (FILE *o, const char *arg0) {
+ fprintf(o, HELP_STR, arg0);
+}
+
+static int load_opts (const int argc, const char **argv, int *ec) {
+ int f_ret;
+
+ while ((f_ret = getopt(argc, (char*const*)argv, "s:oha")) >= 0) {
+ switch (f_ret) {
+ case 's':
+ prog_conf.path_sep = prne_redup_str(prog_conf.path_sep, optarg);
+ prne_assert(prog_conf.path_sep != NULL);
+ break;
+ case 'o': prog_conf.allow_override = true; break;
+ case 'a': prog_conf.p_opt.accept_alias = true; break;
+ case 'h': print_help(stdout, argv[0]); return 0;
+ default:
+ fprintf(
+ stderr,
+ "Run `%s -h` for help.\n",
+ argv[0]);
+ *ec = 2;
+ return -1;
+ }
+ }
+
+ for (int i = optind; i < argc; i += 1) {
+ prne_assert(prne_llist_append(
+ &prog_conf.files,
+ (const prne_llist_element_t)prne_dup_str(argv[i])) != NULL);
+ }
+
+ if (prog_conf.files.size == 0) {
+ // Read stdin if no file given
+ prne_assert(prne_llist_append(
+ &prog_conf.files,
+ (const prne_llist_element_t)prne_dup_str("-")) != NULL);
+ }
+
+ return 1;
+}
+
+static bool build_path_str (const prne_yaml_path_t *path, uctx_t *uc) {
+ char *ny = prne_yaml_path_tostr(
+ path,
+ prog_conf.path_sep,
+ prog_conf.allow_override,
+ uc->path_str);
+
+ if (ny == NULL) {
+ perror("prne_yaml_path_tostr()");
+ return false;
+ }
+
+ uc->path_str = ny;
+ return true;
+}
+
+static bool doc_start_cb (void *ctx, const yaml_event_t *event) {
+ printf("---\n");
+ return true;
+}
+
+static bool doc_end_cb (void *ctx, const yaml_event_t *event) {
+ return true;
+}
+
+static bool scalar_cb (
+ void *ctx,
+ const char *value,
+ const prne_yaml_path_t *path)
+{
+ bool rv;
+ uctx_t *uc = (uctx_t*)ctx;
+
+ if (build_path_str(path, uc)) {
+ printf("%s%s%s\n",
+ uc->path_str,
+ strlen(uc->path_str) > 0 ? ": " : "",
+ value);
+ rv = true;
+ }
+ else {
+ rv = false;
+ }
+
+ return rv;
+}
+
+static bool anchor_cb (
+ void *ctx,
+ const char *anchor,
+ const prne_yaml_path_t *path)
+{
+ bool rv;
+ uctx_t *uc = (uctx_t*)ctx;
+
+ if (build_path_str(path, uc)) {
+ printf("%s: &%s\n", uc->path_str, anchor);
+ rv = true;
+ }
+ else {
+ rv = false;
+ }
+
+ return rv;
+}
+
+static bool alias_cb (
+ void *ctx,
+ const char *anchor,
+ const prne_yaml_path_t *path)
+{
+ bool rv;
+ uctx_t *uc = (uctx_t*)ctx;
+
+ if (build_path_str(path, uc)) {
+ printf("%s: *%s\n", uc->path_str, anchor);
+ rv = true;
+ }
+ else {
+ rv = false;
+ }
+
+ return rv;
+}
+
+static int do_parse (yaml_parser_t *parser, prne_yaml_ctx_t *p_ctx, FILE *f) {
+ static prne_yaml_parse_ret_t p_ret;
+
+ yaml_parser_set_input_file(parser, f);
+ p_ret = prne_yaml_do_parse(parser, p_ctx, &prog_conf.p_opt);
+ switch (p_ret) {
+ case PRNE_YAML_PR_END:
+ break;
+ case PRNE_YAML_PR_ERRNO:
+ perror("prne_yaml_do_parse()");
+ break;
+ case PRNE_YAML_PR_APIERR:
+ fprintf(
+ stderr,
+ "%zu:%zu %s\n",
+ parser->problem_mark.line,
+ parser->problem_mark.column,
+ parser->problem);
+ /* fall-through */
+ default:
+ fprintf(
+ stderr,
+ "prne_yaml_do_parse(): %s\n",
+ prne_yaml_pr_tostr(p_ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+int main (const int argc, const char **argv) {
+ static int ec = 0;
+ static prne_yaml_ctx_t p_ctx;
+ static yaml_parser_t parser;
+ static uctx_t uctx;
+
+ prne_assert(yaml_parser_initialize(&parser) != 0);
+ prne_yaml_init_ctx(&p_ctx);
+ prne_yaml_init_parse_opt(&prog_conf.p_opt);
+ prog_conf.path_sep = prne_redup_str(prog_conf.path_sep, DEFAULT_PATH_SEP);
+ prne_init_llist(&prog_conf.files);
+ prog_conf.p_opt.uctx = &uctx;
+ prog_conf.p_opt.cb.doc_start = doc_start_cb;
+ prog_conf.p_opt.cb.doc_end = doc_end_cb;
+ prog_conf.p_opt.cb.scalar = scalar_cb;
+ prog_conf.p_opt.cb.anchor = anchor_cb;
+ prog_conf.p_opt.cb.alias = alias_cb;
+
+ if (load_opts(argc, argv, &ec) <= 0) {
+ goto END;
+ }
+
+ for (prne_llist_entry_t *i = prog_conf.files.head; i != NULL; i = i->next) {
+ static FILE *f;
+ static const char *path;
+ static bool our_file;
+
+ path = (const char*)i->element;
+ if (prne_nstreq(path, "-")) {
+ f = stdin;
+ our_file = false;
+ }
+ else {
+ f = fopen(path, "r");
+ our_file = true;
+ }
+
+ if (f == NULL) {
+ perror(path);
+ ec = 1;
+ break;
+ }
+
+ ec = do_parse(&parser, &p_ctx, f);
+
+ if (our_file) {
+ fclose(f);
+ f = NULL;
+ }
+ if (ec != 0) {
+ break;
+ }
+ }
+
+END:
+ for (prne_llist_entry_t *i = prog_conf.files.head; i != NULL; i = i->next) {
+ prne_free((void*)i->element);
+ }
+
+ prne_yaml_free_ctx(&p_ctx);
+ prne_yaml_free_parse_opt(&prog_conf.p_opt);
+ yaml_parser_delete(&parser);
+ prne_free_llist(&prog_conf.files);
+
+ prne_free(uctx.path_str);
+ prne_free(prog_conf.path_sep);
+
+ return ec;
+}
diff --git a/src/yaml.c b/src/yaml.c
new file mode 100644
index 0000000..6a31079
--- /dev/null
+++ b/src/yaml.c
@@ -0,0 +1,647 @@
+/*
+* Copyright (c) 2019-2021 David Timber <mieabby@gmail.com>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include "yaml.h"
+
+#include <inttypes.h>
+#include <errno.h>
+
+#include "util_rt.h"
+
+
+static prne_yaml_path_entry_t *prne_yaml_pst_top (prne_yaml_ctx_t *ctx) {
+ if (ctx->path_st.tail == NULL) {
+ return NULL;
+ }
+ return (prne_yaml_path_entry_t*)ctx->path_st.tail->element;
+}
+
+static prne_yaml_path_entry_t *prne_yaml_pst_push (
+ prne_yaml_ctx_t *ctx,
+ const prne_yaml_ent_type_t t)
+{
+ prne_yaml_path_entry_t *ny;
+
+ ny = prne_calloc(sizeof(prne_yaml_path_entry_t), 1);
+ if (ny == NULL) {
+ return false;
+ }
+
+ prne_yaml_init_path_entry(ny);
+ ny->type = t;
+
+ if (prne_llist_append(&ctx->path_st, (prne_llist_element_t)ny) == NULL) {
+ prne_free(ny);
+ ny = NULL;
+ }
+
+ return ny;
+}
+
+static void prne_yaml_pst_prep_last (prne_yaml_ctx_t *ctx) {
+ prne_yaml_path_entry_t *top;
+
+ top = prne_yaml_pst_top(ctx);
+ if (top == NULL) {
+ return;
+ }
+ else if (top->type == PRNE_YAML_ENT_MAP) {
+ prne_free(top->map.name);
+ top->map.name = NULL;
+ top->map.own = false;
+ }
+ else if (top->type == PRNE_YAML_ENT_SEQ) {
+ top->seq.idx += 1;
+ }
+}
+
+static void prne_yaml_pst_pop (prne_yaml_ctx_t *ctx) {
+ prne_yaml_path_entry_t *top;
+
+ top = (prne_yaml_path_entry_t*)ctx->path_st.tail->element;
+ prne_llist_erase(&ctx->path_st, ctx->path_st.tail);
+ prne_yaml_free_path_entry(top);
+ prne_free(top);
+}
+
+static void prne_yaml_path_st_clear (prne_yaml_ctx_t *ctx) {
+ prne_yaml_path_entry_t *p;
+
+ for (prne_llist_entry_t *i = ctx->path_st.head;
+ i != NULL;
+ i = i->next)
+ {
+ p = (prne_yaml_path_entry_t*)i->element;
+ prne_yaml_free_path_entry(p);
+ prne_free(p);
+ }
+
+ prne_llist_clear(&ctx->path_st);
+ prne_yaml_free_path(&ctx->path);
+}
+
+static bool prne_yaml_build_path (prne_yaml_ctx_t *ctx) {
+ size_t i;
+ prne_llist_entry_t *e;
+ const prne_yaml_path_entry_t *p;
+
+ prne_yaml_free_path(&ctx->path);
+
+ if (!prne_yaml_alloc_path(&ctx->path, ctx->path_st.size)) {
+ return false;
+ }
+
+ i = 0;
+ e = ctx->path_st.head;
+ while (e != NULL) {
+ p = (prne_yaml_path_entry_t*)e->element;
+ ctx->path.entries[i] = *p;
+ i += 1;
+ e = e->next;
+ }
+
+ return true;
+}
+
+#define build_path_()\
+if (!prne_yaml_build_path(ctx)) {\
+ *ret = PRNE_YAML_PR_ERRNO;\
+ return;\
+}
+#define handle_anchor_(a)\
+if (!prne_yaml_handle_anchor(ctx, (const char*)(a), ret)) {\
+ return;\
+}
+
+static bool prne_yaml_handle_anchor (
+ prne_yaml_ctx_t *ctx,
+ const char *anchor,
+ prne_yaml_parse_ret_t *ret)
+{
+ if (prne_nstrlen(anchor) == 0) {
+ return true;
+ }
+
+ if (!ctx->opt->accept_alias) {
+ *ret = PRNE_YAML_PR_NALIAS;
+ return false;
+ }
+
+ if (!prne_yaml_build_path(ctx)) {
+ *ret = PRNE_YAML_PR_ERRNO;
+ return false;
+ }
+
+ if (ctx->opt->cb.anchor != NULL &&
+ !ctx->opt->cb.anchor(ctx->opt->uctx, anchor, &ctx->path))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ return false;
+ }
+
+ return true;
+}
+
+static void prne_yaml_handle_event_doc_start (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ if (ctx->opt->cb.doc_start != NULL &&
+ !ctx->opt->cb.doc_start(ctx->opt->uctx, event))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+}
+
+static void prne_yaml_handle_event_doc_end (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ if (ctx->opt->cb.doc_end != NULL &&
+ !ctx->opt->cb.doc_end(ctx->opt->uctx, event))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+ prne_yaml_path_st_clear(ctx);
+}
+
+static void prne_yaml_handle_event_alias (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ if (ctx->opt->accept_alias) {
+ build_path_()
+
+ if (ctx->opt->cb.alias != NULL &&
+ !ctx->opt->cb.alias(
+ ctx->opt->uctx,
+ (const char*)event->data.alias.anchor,
+ &ctx->path))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+ }
+ else {
+ *ret = PRNE_YAML_PR_NALIAS;
+ }
+}
+
+static void prne_yaml_handle_event_scalar (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ prne_yaml_path_entry_t *top = prne_yaml_pst_top(ctx);
+
+ handle_anchor_(event->data.scalar.anchor);
+
+ if (top == NULL) {
+ // lone scalar
+ build_path_()
+
+ if (ctx->opt->cb.scalar != NULL &&
+ !ctx->opt->cb.scalar(
+ ctx->opt->uctx,
+ (const char*)event->data.scalar.value,
+ &ctx->path))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+ }
+ else if (top->type == PRNE_YAML_ENT_MAP) {
+ if (top->map.name == NULL) {
+ top->map.name = prne_dup_str((const char*)event->data.scalar.value);
+ top->map.own = top->map.name != NULL;
+ if (!top->map.own) {
+ *ret = PRNE_YAML_PR_ERRNO;
+ }
+ }
+ else {
+ build_path_()
+
+ if (ctx->opt->cb.scalar != NULL &&
+ !ctx->opt->cb.scalar(
+ ctx->opt->uctx,
+ (const char*)event->data.scalar.value,
+ &ctx->path))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+
+ prne_free(top->map.name);
+ top->map.name = NULL;
+ }
+ }
+ else if (top->type == PRNE_YAML_ENT_SEQ) {
+ build_path_()
+
+ if (ctx->opt->cb.scalar != NULL &&
+ !ctx->opt->cb.scalar(
+ ctx->opt->uctx,
+ (const char*)event->data.scalar.value,
+ &ctx->path))
+ {
+ *ret = PRNE_YAML_PR_CBHALT;
+ }
+
+ top->seq.idx += 1;
+ }
+ else {
+ abort();
+ }
+}
+
+static void prne_yaml_handle_event_seq_start (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ handle_anchor_(event->data.sequence_start.anchor);
+
+ if (!prne_yaml_pst_push(ctx, PRNE_YAML_ENT_SEQ)) {
+ *ret = PRNE_YAML_PR_ERRNO;
+ }
+}
+
+static void prne_yaml_handle_event_seq_end (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ prne_yaml_pst_pop(ctx);
+ prne_yaml_pst_prep_last(ctx);
+}
+
+static void prne_yaml_handle_event_map_start (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ handle_anchor_(event->data.mapping_start.anchor);
+
+ if (!prne_yaml_pst_push(ctx, PRNE_YAML_ENT_MAP)) {
+ *ret = PRNE_YAML_PR_ERRNO;
+ }
+}
+
+static void prne_yaml_handle_event_map_end (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ prne_yaml_pst_pop(ctx);
+ prne_yaml_pst_prep_last(ctx);
+}
+
+#undef build_path_
+#undef handle_anchor_
+
+static void prne_yaml_handle_event (
+ prne_yaml_ctx_t *ctx,
+ const yaml_event_t *event,
+ prne_yaml_parse_ret_t *ret)
+{
+ switch (event->type) {
+ case YAML_DOCUMENT_START_EVENT:
+ prne_yaml_handle_event_doc_start(ctx, event, ret);
+ break;
+ case YAML_DOCUMENT_END_EVENT:
+ prne_yaml_handle_event_doc_end(ctx, event, ret);
+ break;
+ case YAML_ALIAS_EVENT:
+ prne_yaml_handle_event_alias(ctx, event, ret);
+ break;
+ case YAML_SCALAR_EVENT:
+ prne_yaml_handle_event_scalar(ctx, event, ret);
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ prne_yaml_handle_event_seq_start(ctx, event, ret);
+ break;
+ case YAML_SEQUENCE_END_EVENT:
+ prne_yaml_handle_event_seq_end(ctx, event, ret);
+ break;
+ case YAML_MAPPING_START_EVENT:
+ prne_yaml_handle_event_map_start(ctx, event, ret);
+ break;
+ case YAML_MAPPING_END_EVENT:
+ prne_yaml_handle_event_map_end(ctx, event, ret);
+ break;
+ default: abort();
+ }
+}
+
+void prne_yaml_init_parse_opt (prne_yaml_parse_opt_t *p) {
+ prne_memzero(p, sizeof(prne_yaml_parse_opt_t));
+}
+
+void prne_yaml_free_parse_opt (prne_yaml_parse_opt_t *p) {}
+
+void prne_yaml_init_ctx (prne_yaml_ctx_t *ctx) {
+ prne_memzero(ctx, sizeof(prne_yaml_ctx_t));
+ prne_init_llist(&ctx->path_st);
+ prne_yaml_init_path(&ctx->path);
+}
+
+void prne_yaml_free_ctx (prne_yaml_ctx_t *ctx) {
+ if (ctx != NULL) {
+ prne_yaml_path_st_clear(ctx);
+ prne_free_llist(&ctx->path_st);
+ prne_yaml_free_path(&ctx->path);
+ }
+}
+
+prne_yaml_parse_ret_t prne_yaml_do_parse (
+ yaml_parser_t *parser,
+ prne_yaml_ctx_t *ctx,
+ const prne_yaml_parse_opt_t *opt)
+{
+ prne_yaml_parse_ret_t ret = PRNE_YAML_PR_END;
+ bool has_event = false;
+ bool s_flag = false;
+ yaml_event_t event;
+
+ prne_yaml_free_ctx(ctx);
+ prne_yaml_init_ctx(ctx);
+ ctx->opt = opt;
+
+ do {
+ if (yaml_parser_parse(parser, &event) == 0) {
+ ret = PRNE_YAML_PR_APIERR;
+ goto END;
+ }
+ has_event = true;
+
+ switch (event.type) {
+ case YAML_STREAM_START_EVENT:
+ s_flag = true;
+ break;
+ case YAML_STREAM_END_EVENT:
+ s_flag = false;
+ break;
+ default:
+ prne_yaml_handle_event(ctx, &event, &ret);
+ if (ret <= PRNE_YAML_PR_CBHALT) {
+ s_flag = false;
+ }
+ }
+
+ yaml_event_delete(&event);
+ has_event = false;
+ } while (s_flag);
+
+END:
+ if (has_event) {
+ yaml_event_delete(&event);
+ }
+
+ return ret;
+}
+
+const char *prne_yaml_pr_tostr (const prne_yaml_parse_ret_t x) {
+ switch (x) {
+ case PRNE_YAML_PR_ERRNO: return "errno set";
+ case PRNE_YAML_PR_APIERR: return "libyaml error";
+ case PRNE_YAML_PR_CBHALT: return "halted by callback";
+ case PRNE_YAML_PR_END: return "end of stream";
+ }
+ return NULL;
+}
+
+void prne_yaml_init_path_entry (prne_yaml_path_entry_t *p) {
+ prne_memzero(p, sizeof(prne_yaml_path_entry_t));
+}
+
+void prne_yaml_free_path_entry (prne_yaml_path_entry_t *p) {
+ if (p == NULL) {
+ return;
+ }
+
+ if (p->map.own) {
+ switch (p->type) {
+ case PRNE_YAML_ENT_MAP:
+ prne_free(p->map.name);
+ p->map.name = NULL;
+ break;
+ }
+ }
+}
+
+void prne_yaml_init_path (prne_yaml_path_t *p) {
+ prne_memzero(p, sizeof(prne_yaml_path_t));
+}
+
+void prne_yaml_free_path (prne_yaml_path_t *p) {
+ if (p == NULL) {
+ return;
+ }
+
+ if (p->own) {
+ for (size_t i = 0; i < p->depth; i += 1) {
+ prne_yaml_free_path_entry(p->entries + i);
+ }
+ }
+
+ prne_free(p->entries);
+ p->entries = NULL;
+ p->depth = 0;
+}
+
+bool prne_yaml_alloc_path (prne_yaml_path_t *p, const size_t depth) {
+ prne_yaml_path_entry_t *entries = NULL;
+
+ entries = (prne_yaml_path_entry_t*)prne_realloc(
+ p->entries,
+ sizeof(prne_yaml_path_entry_t),
+ depth);
+
+ if (depth > 0 && entries == NULL) {
+ return false;
+ }
+
+ p->depth = depth;
+ p->entries = entries;
+
+ return true;
+}
+
+bool prne_yaml_copy_path (const prne_yaml_path_t *src, prne_yaml_path_t *dst) {
+ bool ret = false;
+ prne_yaml_path_t ny;
+
+ prne_yaml_init_path(&ny);
+// TRY
+ if (!prne_yaml_alloc_path(&ny, src->depth)) {
+ goto END;
+ }
+
+ for (size_t i = 0; i < src->depth; i += 1) {
+ prne_yaml_init_path_entry(ny.entries + i);
+ }
+ ny.own = true;
+
+ ny.depth = src->depth;
+ for (size_t i = 0; i < src->depth; i += 1) {
+ prne_yaml_init_path_entry(dst->entries + i);
+
+ dst->entries[i].type = src->entries[i].type;
+
+ switch (src->entries[i].type) {
+ case PRNE_YAML_ENT_MAP:
+ dst->entries[i].map.name = prne_dup_str(src->entries[i].map.name);
+ dst->entries[i].map.own = true;
+ if (dst->entries[i].map.name == NULL) {
+ goto END;
+ }
+ break;
+ case PRNE_YAML_ENT_SEQ:
+ dst->entries[i].seq.idx = src->entries[i].seq.idx;
+ break;
+ }
+ }
+
+ prne_yaml_move_path(&ny, dst);
+ ret = true;
+
+END: // FINALLY
+ prne_yaml_free_path(&ny);
+ return ret;
+}
+
+void prne_yaml_swap_path (prne_yaml_path_t *a, prne_yaml_path_t *b) {
+ prne_yaml_path_t c;
+
+ c = *b;
+ *b = *a;
+ *a = c;
+}
+
+void prne_yaml_move_path (prne_yaml_path_t *a, prne_yaml_path_t *b) {
+ prne_yaml_free_path(b);
+ *b = *a;
+ prne_yaml_init_path(a);
+}
+
+int prne_yaml_cmp_path (const prne_yaml_path_t *a, const prne_yaml_path_t *b) {
+ int rv;
+
+ rv = prne_op_spaceship(a->depth, b->depth);
+ if (rv != 0) {
+ return rv;
+ }
+
+ for (size_t i = 0; i < a->depth; i += 1) {
+ rv = prne_op_spaceship(a->entries[i].type, b->entries[i].type);
+ if (rv != 0) {
+ return rv * 2;
+ }
+
+ switch (a->entries[i].type) {
+ case PRNE_YAML_ENT_MAP:
+ rv = strcmp(a->entries[i].map.name, b->entries[i].map.name);
+ break;
+ case PRNE_YAML_ENT_SEQ:
+ rv = prne_op_spaceship(
+ a->entries[i].seq.idx,
+ b->entries[i].seq.idx);
+ break;
+ }
+
+ if (rv != 0) {
+ return rv * 3;
+ }
+ }
+
+ return 0;
+}
+
+char *prne_yaml_path_tostr (
+ const prne_yaml_path_t *path,
+ const char *path_sep,
+ const bool ovr,
+ char *old)
+{
+ static const char *FMT_MAP = "%s%s";
+ static const char *FMT_SEQ = "[%zu]";
+ char *ret = NULL;
+ char **sb = NULL;
+ const prne_yaml_path_entry_t *e;
+ int l;
+
+ if (path_sep == NULL || path_sep[0] == 0) {
+ // *printf functions on various platform handle NULL differently
+ errno = EINVAL;
+ return NULL;
+ }
+
+ sb = prne_calloc(sizeof(char*), path->depth);
+ if (path->depth > 0 && sb == NULL) {
+ goto END;
+ }
+
+ for (size_t i = 0; i < path->depth; i += 1) {
+ e = path->entries + i;
+
+ switch (e->type) {
+ case PRNE_YAML_ENT_MAP:
+ if (!ovr && strstr(e->map.name, path_sep) != NULL) {
+ errno = EILSEQ;
+ goto END;
+ }
+ l = snprintf(NULL, 0, FMT_MAP, path_sep, e->map.name);
+ break;
+ case PRNE_YAML_ENT_SEQ:
+ l = snprintf(NULL, 0, FMT_SEQ, e->seq.idx);
+ break;
+ }
+
+ if (l < 0) {
+ goto END;
+ }
+ sb[i] = prne_alloc_str(l);
+ if (sb[i] == NULL) {
+ goto END;
+ }
+
+ switch (e->type) {
+ case PRNE_YAML_ENT_MAP:
+ snprintf(sb[i], l + 1, FMT_MAP, path_sep, e->map.name);
+ break;
+ case PRNE_YAML_ENT_SEQ:
+ snprintf(sb[i], l + 1, FMT_SEQ, e->seq.idx);
+ break;
+ }
+ }
+
+ ret = prne_rebuild_str(old, (const char**)sb, path->depth);
+
+END:
+ if (sb != NULL) {
+ for (size_t i = 0; i < path->depth; i += 1) {
+ prne_free(sb[i]);
+ }
+ prne_free(sb);
+ }
+
+ return ret;
+}
diff --git a/src/yaml.h b/src/yaml.h
new file mode 100644
index 0000000..eafc964
--- /dev/null
+++ b/src/yaml.h
@@ -0,0 +1,251 @@
+/** \file
+ * \brief The YAML helper. Only listen to terminating scalar events: the helper
+ * removes the need to listen to mapping start and sequence start events in
+ * between. The mapping and sequence start events are reformed as
+ * \c prne_yaml_path_t which can be converted to a file-system-path-like string
+ * using \c prne_yaml_path_tostr() The string can be matched against regexp or
+ * simple string mapping to extract data from the document.
+ */
+/*
+* Copyright (c) 2019-2021 David Timber <mieabby@gmail.com>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#pragma once
+#include <yaml.h>
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "util_ct.h"
+#include "llist.h"
+
+
+typedef enum {
+ PRNE_YAML_PR_NALIAS = -3, // Alias encountered
+ PRNE_YAML_PR_ERRNO, // errno set
+ PRNE_YAML_PR_APIERR, // libyaml error
+ PRNE_YAML_PR_CBHALT, // cb func returned false
+ PRNE_YAML_PR_END // no more event
+} prne_yaml_parse_ret_t;
+prne_static_assert(PRNE_YAML_PR_CBHALT == 0, "PRNE_YAML_PR_CBHALT == 0");
+
+typedef enum {
+ PRNE_YAML_ENT_NONE,
+ PRNE_YAML_ENT_MAP, // entry representing a mapping start
+ PRNE_YAML_ENT_SEQ // entry representing a sequence start
+} prne_yaml_ent_type_t;
+
+/**
+ * \brief Path entry object. One path entry corresponds to one mapping or
+ * sequence start event.
+ */
+typedef struct {
+ // Entry type: mapping or sequence
+ prne_yaml_ent_type_t type;
+ union {
+ // Data for mapping entry
+ struct {
+ char *name; // The first scalar value of the mapping
+ bool own; // True if the object has the ownership of \c name
+ } map;
+ // Data for sequence entry
+ struct {
+ size_t idx; // Index from the first occurrence in sequence
+ } seq;
+ };
+} prne_yaml_path_entry_t;
+
+/**
+ * \brief Path object
+ */
+typedef struct {
+ // The number of entries
+ size_t depth;
+ // The entries with the elements in the order of events occurrence.
+ prne_yaml_path_entry_t *entries;
+ // True if the object is responsible for freeing the path entries
+ bool own;
+} prne_yaml_path_t;
+
+/**
+ * \brief Callback function set object
+ */
+typedef struct {
+ // YAML_DOCUMENT_START_EVENT
+ bool(*doc_start)(void *ctx, const yaml_event_t *event);
+ // YAML_DOCUMENT_END_EVENT
+ bool(*doc_end)(void *ctx, const yaml_event_t *event);
+ // Terminating YAML_SCALAR_EVENT
+ bool(*scalar)(
+ void *ctx,
+ const char *value,
+ const prne_yaml_path_t *path);
+ /**
+ * \brief Anchor event
+ * \details This is a special event that is emitted prior to
+ * processing the event from libyaml. It can be used to support anchors and
+ * aliases in the document. The events that can have anchor are:
+ * - scalar
+ * - sequence start
+ * - mapping start
+ * The alias events can be processed in the separate callback function.
+ *
+ * Handling of anchors and aliases is expensive and impractical. The helper
+ * won't accept aliases unless requested.
+ *
+ * \see \c prne_yaml_parse_opt_t
+ * \see \c PRNE_YAML_PR_NALIAS
+ */
+ bool(*anchor)(
+ void *ctx,
+ const char *anchor,
+ const prne_yaml_path_t *path);
+ // YAML_ALIAS_EVENT
+ bool(*alias)(
+ void *ctx,
+ const char *anchor,
+ const prne_yaml_path_t *path);
+} prne_yaml_cbset_t;
+
+/**
+ * \brief Parsing Option Object
+ */
+typedef struct {
+ void *uctx; // User context for callback functions
+ prne_yaml_cbset_t cb; // Callback function set object
+ /* Accept anchors and anliases. Not allowed by default - the parser will
+ * produce an error if the document contains an anchor or an alias.
+ */
+ bool accept_alias;
+} prne_yaml_parse_opt_t;
+
+typedef struct {
+ // A pointer to a parsing Option Object instnace
+ const prne_yaml_parse_opt_t *opt;
+ // Internal path stack
+ prne_llist_t path_st;
+ // Internal path object
+ prne_yaml_path_t path;
+} prne_yaml_ctx_t;
+
+
+/**
+ * \brief Initialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_init_parse_opt (prne_yaml_parse_opt_t *p);
+/**
+ * \brief Deinitialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_free_parse_opt (prne_yaml_parse_opt_t *p);
+
+/**
+ * \brief Initialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_init_ctx (prne_yaml_ctx_t *ctx);
+/**
+ * \brief Deinitialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_free_ctx (prne_yaml_ctx_t *ctx);
+prne_yaml_parse_ret_t prne_yaml_do_parse (
+ yaml_parser_t *parser,
+ prne_yaml_ctx_t *ctx,
+ const prne_yaml_parse_opt_t *opt);
+
+const char *prne_yaml_pr_tostr (const prne_yaml_parse_ret_t x);
+
+/**
+ * \brief Initialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_init_path_entry (prne_yaml_path_entry_t *p);
+/**
+ * \brief Deinitialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_free_path_entry (prne_yaml_path_entry_t *p);
+
+/**
+ * \brief Initialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_init_path (prne_yaml_path_t *p);
+/**
+ * \brief Deinitialisation function
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+void prne_yaml_free_path (prne_yaml_path_t *p);
+/**
+ * \brief Allocate variable-length members of the Path Object
+ * \param p The pointer to the object
+ * \param depth The number of elements required
+ * \return true if allocation was successful
+ * \return false on failure and \c errno set to \c ENOMEM
+ * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation)
+ */
+bool prne_yaml_alloc_path (prne_yaml_path_t *p, const size_t depth);
+/**
+ * \brief Deep copy operator
+ *
+ * \param src The source object
+ * \param dst The destination object. The original contents are be freed
+ * \return true on successful operation
+ * \return false on failure and \c errno set to \c ENOMEM
+ */
+bool prne_yaml_copy_path (const prne_yaml_path_t *src, prne_yaml_path_t *dst);
+/**
+ * \brief Swap operator
+ */
+void prne_yaml_swap_path (prne_yaml_path_t *a, prne_yaml_path_t *b);
+/**
+ * \brief Move opeator. Moves the contents of \p a into \p b The original
+ * contents of \p b are freed
+ */
+void prne_yaml_move_path (prne_yaml_path_t *a, prne_yaml_path_t *b);
+/**
+ * \brief The comparison operator
+ * \returns Negative value if \p a is less than \p b
+ * \returns Positive value if \p a is greater than \p b
+ * \retval 0 if \p a and \p b are identical
+ */
+int prne_yaml_cmp_path (const prne_yaml_path_t *a, const prne_yaml_path_t *b);
+/**
+ * \brief Fabricate string representation of the Path Object
+ *
+ * \param path The object
+ * \param path_sep The path separator. Conventionally "/" or "."
+ * \param ovr Set true to allow the path separator in path names
+ * \param old The old pointer to the string for \c prne_rebuild_str()
+ * \returns A pointer to the fabricated string
+ * \retval NULL and \c errno set to \c ENOMEM on memory allocation error
+ * \retval NULL and \c errno set to \c EINVAL if \p path_sep is NULL or a
+ * pointer to an empty string
+ * \retval NULL and \c errno set to \c EILSEQ if \p ovr is set and the path
+ * separator was found in one of the path names
+ */
+char *prne_yaml_path_tostr (
+ const prne_yaml_path_t *path,
+ const char *path_sep,
+ const bool ovr,
+ char *old);