From 8b47484919d61d389dfdefb07fe1b168021696e7 Mon Sep 17 00:00:00 2001 From: David Timber Date: Tue, 18 Oct 2022 14:50:48 +0800 Subject: 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 --- .vscode/launch.json | 19 ++ .vscode/tasks.json | 5 + src/Makefile.am | 11 +- src/data/yaml/prne_yaml-test.yaml | 52 +++ src/proone-hostinfod.c | 325 ++++++++----------- src/proone-yamlls.c | 281 +++++++++++++++++ src/yaml.c | 647 ++++++++++++++++++++++++++++++++++++++ src/yaml.h | 251 +++++++++++++++ templates/doxygen | 24 +- 9 files changed, 1418 insertions(+), 197 deletions(-) create mode 100644 src/data/yaml/prne_yaml-test.yaml create mode 100644 src/proone-yamlls.c create mode 100644 src/yaml.c create mode 100644 src/yaml.h diff --git a/.vscode/launch.json b/.vscode/launch.json index c6d96f8..7c6001b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -243,5 +243,24 @@ "${workspaceFolder}/src/data/proto/txtrec-testdata" ], "console": "integratedTerminal" }, + { + "name": "yamlls", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/proone-yamlls", + "args": [ + "-o", + "-a", + "${workspaceFolder}/src/data/yaml/prne_yaml-test.yaml", + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [], + "preLaunchTask": "Build yamlls", + "miDebuggerPath": "/usr/bin/gdb" + }, ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4b31a75..5507cff 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -75,6 +75,11 @@ "type": "shell", "command": "make -C ./src proone" }, + { + "label": "Build yamlls", + "type": "shell", + "command": "make -C ./src proone-yamlls" + }, { 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 -#include #include #include #include @@ -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 +* +* 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 + +#include + +#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 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 +* +* 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 +#include + +#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 +* +* 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 + +#include +#include +#include + +#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); diff --git a/templates/doxygen b/templates/doxygen index 0e5293b..f177de7 100644 --- a/templates/doxygen +++ b/templates/doxygen @@ -21,10 +21,16 @@ /** - * \brief The equality operator of the + * \brief The equality operator * \retval true if the contents of both \p a and \p b are identical * \retval false otherwise */ +/** + * \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 + */ /** * \brief The deep copy operator of the * \retval true if the contents of \p src have been successfully copied into @@ -39,6 +45,22 @@ * \return false on failure and \c errno set to \c ENOMEM * \see [/doc/impl.md#Resource Allocation](/doc/impl.md#resource_allocation) */ +/** + * \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 + */ +/** + * \brief Swap operator + */ +/** + * \brief Move opeator. Moves the contents of \p a into \p b The original + * contents of \p b are freed + */ + /** * \brief The serialisation function for the -- cgit