diff -r -N -U3 a/yaml-filter/CMakeLists.txt b/yaml-filter/CMakeLists.txt
--- a/yaml-filter/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/CMakeLists.txt 2020-02-25 13:34:48.946813539 +0100
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 2.8)
+
+include(FindPkgConfig)
+pkg_check_modules(YAML yaml-0.1)
+
+include_directories(${YAML_INCLUDE_DIRS})
+
+add_library(yaml-path yaml-path.c)
+target_link_libraries(yaml-path ${YAML_LIBRARIES})
+
+add_executable(ya yaml.c)
+target_link_libraries(ya yaml-path)
+
+add_executable(yamlp yamlp.c)
+target_link_libraries(yamlp yaml-path)
+
+install(TARGETS ya RUNTIME DESTINATION bin)
+install(TARGETS yamlp RUNTIME DESTINATION bin)
+
+add_executable(test-path-segments test-path-segments.c yaml-path.c)
+
+add_executable(test-paths test-paths.c)
+target_link_libraries(test-paths yaml-path)
+
+enable_testing()
+add_test(NAME "test-path-segments" COMMAND ${CMAKE_BINARY_DIR}/test-path-segments)
+add_test(NAME "test-paths" COMMAND ${CMAKE_BINARY_DIR}/test-paths)
+add_test(NAME "test-yamlp" COMMAND ${CMAKE_SOURCE_DIR}/test-yamlp.sh)
diff -r -N -U3 a/yaml-filter/.git b/yaml-filter/.git
--- a/yaml-filter/.git 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/.git 2020-02-25 13:34:48.468803146 +0100
@@ -0,0 +1 @@
+gitdir: ../.git/modules/yaml-filter
diff -r -N -U3 a/yaml-filter/.gitignore b/yaml-filter/.gitignore
--- a/yaml-filter/.gitignore 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/.gitignore 2020-02-25 13:34:48.946813539 +0100
@@ -0,0 +1,54 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+build
\ No newline at end of file
diff -r -N -U3 a/yaml-filter/LICENSE b/yaml-filter/LICENSE
--- a/yaml-filter/LICENSE 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/LICENSE 2020-02-25 13:34:48.946813539 +0100
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 OpenSCAP
+
+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.
diff -r -N -U3 a/yaml-filter/openshift-logging.yaml b/yaml-filter/openshift-logging.yaml
--- a/yaml-filter/openshift-logging.yaml 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/openshift-logging.yaml 2020-02-25 13:34:48.946813539 +0100
@@ -0,0 +1,36 @@
+apiVersion: "logging.openshift.io/v1alpha1"
+kind: "LogForwarding"
+metadata:
+ name: instance
+ namespace: openshift-logging
+spec:
+ disableDefaultForwarding: true
+ outputs:
+ - type: "elasticsearch"
+ name: elasticsearch
+ endpoint: elasticsearch.openshift-logging.svc:9200
+ secret:
+ name: fluentd
+ - type: "elasticsearch"
+ name: elasticsearch-insecure
+ endpoint: elasticsearch-insecure.svc.messaging.cluster.local
+ insecure: true
+ - type: "forward"
+ name: secureforward-offcluster
+ endpoint: https://secureforward.offcluster.com:9200
+ secret:
+ name: secureforward
+ pipelines:
+ - name: container-logs
+ inputSource: logs.app
+ outputRefs:
+ - elasticsearch
+ - secureforward-offcluster
+ - name: infra-logs
+ inputSource: logs.infra
+ outputRefs:
+ - elasticsearch-insecure
+ - name: audit-logs
+ inputSource: logs.audit
+ outputRefs:
+ - secureforward-offcluster
diff -r -N -U3 a/yaml-filter/README.md b/yaml-filter/README.md
--- a/yaml-filter/README.md 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/README.md 2020-02-25 13:34:48.946813539 +0100
@@ -0,0 +1,3 @@
+# YAML Path filter
+
+YAML document filtering for libyaml
diff -r -N -U3 a/yaml-filter/test-paths.c b/yaml-filter/test-paths.c
--- a/yaml-filter/test-paths.c 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/test-paths.c 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,230 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "yaml-path.h"
+
+static void
+rstrip (char *s)
+{
+ // This will strip whitespace and explicit document ending
+ // to cope with emitter behavior for libyaml < 0.2
+ char *pos = s + strlen(s) - 1;
+ while (pos > s && (isspace((unsigned char)*pos) || *pos == '.')) {
+ *pos = 0;
+ pos--;
+ }
+}
+
+static const char*
+yp_event_name (yaml_event_type_t event_type)
+{
+ switch (event_type) {
+ case YAML_NO_EVENT:
+ return "no-event";
+ case YAML_STREAM_START_EVENT:
+ return "stream-start-event";
+ case YAML_STREAM_END_EVENT:
+ return "stream-end-event";
+ case YAML_DOCUMENT_START_EVENT:
+ return "document-start-event";
+ case YAML_DOCUMENT_END_EVENT:
+ return "document-end-event";
+ case YAML_ALIAS_EVENT:
+ return "alias-event";
+ case YAML_SCALAR_EVENT:
+ return "scalar-event";
+ case YAML_SEQUENCE_START_EVENT:
+ return "sequence-start-event";
+ case YAML_SEQUENCE_END_EVENT:
+ return "sequence-end-event";
+ case YAML_MAPPING_START_EVENT:
+ return "mapping-start-event";
+ case YAML_MAPPING_END_EVENT:
+ return "mapping-end-event";
+ default:
+ return "unknown-event";
+ }
+}
+
+
+static char*
+yaml;
+
+#define YAML_STRING_LEN 2048
+static char
+yaml_out[YAML_STRING_LEN] = {0};
+
+static size_t
+yaml_out_len = 0;
+
+static yaml_path_filter_mode_t
+mode = YAML_PATH_FILTER_RETURN_ALL;
+
+static int
+test_result = 0;
+
+
+int
+yp_run (char *path)
+{
+ yaml_parser_t parser;
+ yaml_emitter_t emitter;
+ int res = 0;
+
+ yaml_path_t *yp = yaml_path_create();
+ yaml_path_parse(yp, path);
+
+ yaml_emitter_initialize(&emitter);
+ yaml_parser_initialize(&parser);
+
+ yaml_parser_set_input_string(&parser, (const unsigned char *)yaml, strlen(yaml));
+ memset(yaml_out, 0, YAML_STRING_LEN);
+ yaml_emitter_set_output_string(&emitter, (unsigned char *)yaml_out, YAML_STRING_LEN, &yaml_out_len);
+ yaml_emitter_set_width(&emitter, -1);
+
+ yaml_event_t event;
+ yaml_event_type_t event_type;
+ do {
+ if (!yaml_parser_parse(&parser, &event)) {
+ switch (parser.error) {
+ case YAML_MEMORY_ERROR:
+ printf("Memory error: Not enough memory for parsing\n");
+ break;
+ case YAML_READER_ERROR:
+ if (parser.problem_value != -1) {
+ printf("Reader error: %s: #%X at %ld\n", parser.problem, parser.problem_value, (long)parser.problem_offset);
+ } else {
+ printf("Reader error: %s at %ld\n", parser.problem, (long)parser.problem_offset);
+ }
+ break;
+ case YAML_SCANNER_ERROR:
+ if (parser.context) {
+ printf("Scanner error: %s at line %d, column %d\n%s at line %d, column %d\n", parser.context, (int)parser.context_mark.line+1, (int)parser.context_mark.column+1, parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1);
+ } else {
+ printf("Scanner error: %s at line %d, column %d\n", parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1);
+ }
+ break;
+ case YAML_PARSER_ERROR:
+ if (parser.context) {
+ printf("Parser error: %s at line %d, column %d\n%s at line %d, column %d\n", parser.context, (int)parser.context_mark.line+1, (int)parser.context_mark.column+1, parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1);
+ } else {
+ printf("Parser error: %s at line %d, column %d\n", parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1);
+ }
+ break;
+ default:
+ printf("Internal error\n");
+ break;
+ }
+ res = 1;
+ goto error;
+ } else {
+ event_type = event.type;
+ if (!yaml_path_filter_event(yp, &parser, &event, mode)) {
+ yaml_event_delete(&event);
+ } else {
+ if (!yaml_emitter_emit(&emitter, &event)) {
+ printf("Error after '%s'\n", yp_event_name(event.type));
+ switch (emitter.error)
+ {
+ case YAML_MEMORY_ERROR:
+ printf("Memory error: Not enough memory for emitting\n");
+ break;
+ case YAML_WRITER_ERROR:
+ printf("Writer error: %s\n", emitter.problem);
+ break;
+ case YAML_EMITTER_ERROR:
+ printf("Emitter error: %s\n", emitter.problem);
+ break;
+ default:
+ printf("Internal error\n");
+ break;
+ }
+ res = 2;
+ goto error;
+ }
+ }
+ }
+ } while (event_type != YAML_STREAM_END_EVENT);
+
+error:
+ yaml_parser_delete(&parser);
+ yaml_emitter_delete(&emitter);
+
+ yaml_path_destroy(yp);
+
+ return res;
+}
+
+#define ASCII_ERR "\033[0;33m"
+#define ASCII_RST "\033[0;0m"
+
+void
+yp_test (char *path, char *yaml_exp)
+{
+ printf("%s ", path);
+ if (!yp_run(path)) {
+ rstrip(yaml_out);
+ if (!strcmp(yaml_exp, yaml_out)) {
+ printf("(%s): OK\n", yaml_exp);
+ return;
+ }
+ printf(ASCII_ERR"(%s != %s)"ASCII_RST": FAILED\n", yaml_exp, yaml_out);
+ }
+ test_result++;
+}
+
+
+int main (int argc, char *argv[])
+{
+ yaml =
+ "{"
+ "first: {"
+ "'Map': {1: '1'},"
+ "'Nop': 0,"
+ "'Yep': '1',"
+ "'Arr': ["
+ "[11, 12],"
+ "2,"
+ "['31', '32'],"
+ "[4, 5, 6, 7, 8, 9],"
+ "{'k': 'val', 0: 0}"
+ "]"
+ "},"
+ "second: ["
+ "{'abc': &anc [1, 2], 'abcdef': 2, 'z': *anc},"
+ "{'abc': [3, 4], 'abcdef': 4, 'z': 'zzz'}"
+ "]"
+ "}";
+
+ // Path Expected filtered YAML result
+
+ mode = YAML_PATH_FILTER_RETURN_ALL;
+ yp_test(".first", "{'Map': {1: '1'}, 'Nop': 0, 'Yep': '1', 'Arr': [[11, 12], 2, ['31', '32'], [4, 5, 6, 7, 8, 9], {'k': 'val', 0: 0}]}");
+ yp_test(".first.Map", "{1: '1'}");
+ yp_test(".first.Nop", "0");
+ yp_test(".first.Arr", "[[11, 12], 2, ['31', '32'], [4, 5, 6, 7, 8, 9], {'k': 'val', 0: 0}]");
+ yp_test(".first.Arr[0]", "[11, 12]");
+ yp_test(".first.Arr[1]", "2");
+ yp_test(".first.Arr[2][0]", "'31'");
+ yp_test(".first.Arr[:2][0]", "[11]");
+ yp_test(".first.Arr[3][:]", "[4, 5, 6, 7, 8, 9]");
+ yp_test(".first.Arr[4].k", "'val'");
+ yp_test(".first.Arr[:][0]", "[11, '31', 4]");
+ yp_test(".first.Arr[:].k", "['val']");
+ yp_test(".first.Arr[:][2]", "[6]");
+ yp_test(".first.Arr[3][1::2]", "[5, 7, 9]");
+ yp_test(".first.Arr[3][::2]", "[4, 6, 8]");
+ yp_test(".first.Arr[3][:4:2]", "[4, 6]");
+ yp_test(".second[0].abc", "&anc [1, 2]");
+ yp_test(".second[0:2].abc", "[&anc [1, 2], [3, 4]]");
+ yp_test(".second[0].z", "*anc");
+
+ mode = YAML_PATH_FILTER_RETURN_SHALLOW;
+ yp_test(".first", "{}");
+ yp_test(".first.Nop", "0");
+ yp_test(".first.Map", "{}");
+
+ return test_result;
+}
diff -r -N -U3 a/yaml-filter/test-path-segments.c b/yaml-filter/test-path-segments.c
--- a/yaml-filter/test-path-segments.c 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/test-path-segments.c 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "yaml-path.h"
+
+
+#define PATH_STRING_LEN 1024
+
+static int
+test_result = 0;
+
+static char
+yp_s[PATH_STRING_LEN] = {0};
+
+
+#define ASCII_ERR "\033[0;33m"
+#define ASCII_RST "\033[0;0m"
+
+void
+yp_test (char *p, int expected_failure)
+{
+ yaml_path_t *yp = yaml_path_create();
+ printf("%s", p);
+ if (!yaml_path_parse(yp, p)) {
+ yaml_path_snprint(yp, yp_s, PATH_STRING_LEN);
+ if (expected_failure) {
+ printf(ASCII_ERR);
+ test_result++;
+ }
+ printf(" -> %s: %s\n", yp_s, expected_failure ? ASCII_RST"FAILED" : "OK");
+ } else {
+ const yaml_path_error_t *ype = yaml_path_error_get(yp);
+ if (!expected_failure) {
+ printf(ASCII_ERR);
+ test_result++;
+ }
+ printf(" -X %s (at pos: %zu): %s\n", ype->message, ype->pos, !expected_failure ? ASCII_RST"FAILED" : "OK");
+ }
+ yaml_path_destroy(yp);
+}
+
+#define yp_test_good(p) yp_test(p, 0)
+#define yp_test_invalid(p) yp_test(p, 1)
+
+
+int main (int argc, char *argv[])
+{
+ yp_test_good(".first");
+ yp_test_good(".first[0]");
+ yp_test_good(".first.second[0].third");
+ yp_test_good(".first.0");
+ yp_test_good("$.jsonpath.something");
+ yp_test_good("unprefixed.key[0]");
+ yp_test_good("$[0]");
+ yp_test_good("[0]");
+ yp_test_good("0");
+ yp_test_good("!");
+ yp_test_good("$");
+
+ yp_test_good("[0:0]");
+ yp_test_good("[0:0:1]");
+ yp_test_good("[100:]");
+ yp_test_good("[100::]");
+ yp_test_good("[:100]");
+ yp_test_good("[:100:]");
+ yp_test_good("[:]");
+ yp_test_good("[::]");
+ yp_test_good("[-03:-200:+500]");
+
+ yp_test_good("el[&anchor]");
+ yp_test_good("el[&anchor].key");
+ yp_test_good("el[&anchor][100]");
+
+ yp_test_good("el['key']");
+ yp_test_good("el['key'].other[0]['key']");
+
+ yp_test_invalid("");
+ yp_test_invalid(".");
+ yp_test_invalid("$.");
+ yp_test_invalid("element[");
+
+ yp_test_invalid("[0:0:0]");
+ yp_test_invalid("[::-1]");
+ yp_test_invalid("[0.key[0]");
+
+ yp_test_invalid("el[&]");
+ yp_test_invalid("el[&");
+ yp_test_invalid("el[&wrong.");
+
+ yp_test_invalid("el[']");
+ yp_test_invalid("el['key].wrong");
+ yp_test_invalid("el['key.wrong");
+ yp_test_invalid("el['key'");
+
+ return test_result;
+}
diff -r -N -U3 a/yaml-filter/test-yamlp.sh b/yaml-filter/test-yamlp.sh
--- a/yaml-filter/test-yamlp.sh 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/test-yamlp.sh 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+yamlp_test()
+{
+ echo -n "$1 ($2) "
+ out=$(./yamlp -F -f "$1" "$2") || return 1
+ echo -n "-> $out"
+ if [ "$out" != "$3" ]; then
+ echo ": FAILED, expected result: $3"
+ return 2
+ else
+ echo ": OK"
+ fi
+}
+
+yamlp_test "../openshift-logging.yaml" ".spec.pipelines[:].inputSource" "[logs.app, logs.infra, logs.audit]"
+res=$((res+$?))
+
+exit $res
diff -r -N -U3 a/yaml-filter/yaml.c b/yaml-filter/yaml.c
--- a/yaml-filter/yaml.c 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/yaml.c 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,142 @@
+#include <yaml.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "yaml-path.h"
+
+#define INDENT " "
+#define STRVAL(x) ((x) ? (char*)(x) : "")
+
+void indent(int level)
+{
+ int i;
+ for (i = 0; i < level; i++) {
+ printf("%s", INDENT);
+ }
+}
+
+void print_event(yaml_event_t *event)
+{
+ static int level = 0;
+
+ switch (event->type) {
+ case YAML_NO_EVENT:
+ indent(level);
+ printf("no-event\n");
+ break;
+ case YAML_STREAM_START_EVENT:
+ indent(level++);
+ printf("stream-start-event\n");
+ break;
+ case YAML_STREAM_END_EVENT:
+ indent(--level);
+ printf("stream-end-event\n");
+ break;
+ case YAML_DOCUMENT_START_EVENT:
+ indent(level++);
+ printf("document-start-event\n");
+ break;
+ case YAML_DOCUMENT_END_EVENT:
+ indent(--level);
+ printf("document-end-event\n");
+ break;
+ case YAML_ALIAS_EVENT:
+ indent(level);
+ printf("alias-event &\n");
+ break;
+ case YAML_SCALAR_EVENT:
+ indent(level);
+ printf("= scalar-event (val=\"%s\", l=%d, t=%s, pl_impl=%d, q_impl=%d, st=%d)\n",
+ STRVAL(event->data.scalar.value),
+ (int)event->data.scalar.length,
+ event->data.scalar.tag,
+ event->data.scalar.plain_implicit, event->data.scalar.quoted_implicit, event->data.scalar.style);
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ indent(level++);
+ printf("[ sequence-start-event (t=%s)\n",
+ event->data.sequence_start.tag);
+ break;
+ case YAML_SEQUENCE_END_EVENT:
+ indent(--level);
+ printf("] sequence-end-event\n");
+ break;
+ case YAML_MAPPING_START_EVENT:
+ indent(level++);
+ printf("{ mapping-start-event\n");
+ break;
+ case YAML_MAPPING_END_EVENT:
+ indent(--level);
+ printf("} mapping-end-event\n");
+ break;
+ }
+ if (level < 0) {
+ fprintf(stderr, "indentation underflow!\n");
+ level = 0;
+ }
+}
+
+int yaml_parser_parse_and_filter (yaml_parser_t *parser, yaml_event_t *event, yaml_path_t *path)
+{
+ int valid_event = 0;
+ int res;
+ do {
+ res = yaml_parser_parse(parser, event);
+ if (res) {
+ printf("=====> ");
+ print_event(event);
+ if (!yaml_path_filter_event(path, parser, event, YAML_PATH_FILTER_RETURN_ALL)) {
+ yaml_event_delete(event);
+ } else {
+ printf("+------------------------------------------------------------------------------------> ");
+ print_event(event);
+ valid_event = 1;
+ }
+ } else {
+ break;
+ }
+ } while (!valid_event && res);
+
+ return res;
+}
+
+int main(int argc, char *argv[])
+{
+ yaml_parser_t parser;
+ yaml_event_t event;
+ yaml_event_type_t event_type;
+
+ yaml_path_t *yp = yaml_path_create();
+ //yaml_path_parse(yp, ".fruit.Oop[1]");
+ //yaml_path_parse(yp, ".first.Arr[:2][0]"); //.Arr[2][0]
+ //yaml_path_parse(yp, ".first.Arr[3][:]");
+ //yaml_path_parse(yp, ".first");
+ //yaml_path_parse(yp, ".first.Arr[:].k");
+ yaml_path_parse(yp, ".first.Arr[:][2]");
+
+ //const char *yaml = "2";
+ const char *yaml = "{first: {'Map': {1: '1'}, 'Nop': 'b', 'Yep': '2', 'Arr': [[11,12],2,[31,32],[4, 5, 6],{'k': 1, 0: 0}]}}";
+ printf("%s\n\n", yaml);
+
+ yaml_parser_initialize(&parser);
+ //yaml_parser_set_input_file(&parser, stdin);
+ yaml_parser_set_input_string(&parser, (const unsigned char*)yaml, strlen(yaml));
+
+ do {
+ if (!yaml_parser_parse_and_filter(&parser, &event, yp))
+ goto error;
+ event_type = event.type;
+ yaml_event_delete(&event);
+ } while (event_type != YAML_STREAM_END_EVENT);
+
+ yaml_path_destroy(yp);
+ yaml_parser_delete(&parser);
+ return 0;
+
+error:
+ yaml_path_destroy(yp);
+ fprintf(stderr, "Failed to parse: %s\n", parser.problem);
+ yaml_parser_delete(&parser);
+ return 1;
+}
diff -r -N -U3 a/yaml-filter/yaml-path.c b/yaml-filter/yaml-path.c
--- a/yaml-filter/yaml-path.c 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/yaml-path.c 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,568 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/queue.h>
+#include <yaml.h>
+
+#include "yaml-path.h"
+
+
+#define YAML_PATH_MAX_SECTION_LEN 1024
+#define YAML_PATH_MAX_SECTIONS 255
+#define YAML_PATH_MAX_LEN YAML_PATH_MAX_SECTION_LEN * YAML_PATH_MAX_SECTIONS
+
+
+typedef enum yaml_path_section_type {
+ YAML_PATH_SECTION_KEY,
+ YAML_PATH_SECTION_INDEX,
+ YAML_PATH_SECTION_SLICE,
+ YAML_PATH_SECTION_ANCHOR,
+} yaml_path_section_type_t;
+
+typedef struct yaml_path_section {
+ yaml_path_section_type_t type;
+ int level;
+ union {
+ const char *key;
+ const char *anchor;
+ int index;
+ struct {int start, end, stride;} slice;
+ } data;
+ TAILQ_ENTRY(yaml_path_section) entries;
+
+ yaml_node_type_t node_type;
+ int counter;
+ int valid;
+ int next_valid;
+} yaml_path_section_t;
+
+typedef TAILQ_HEAD(path_section_list, yaml_path_section) path_section_list_t;
+
+typedef struct yaml_path {
+ path_section_list_t sections_list;
+ size_t sections_count;
+
+ size_t current_level;
+ size_t sequence_level;
+
+ yaml_path_error_t error;
+} yaml_path_t;
+
+
+static void
+yaml_path_error_set (yaml_path_t *path, yaml_path_error_type_t error_type, const char *message, size_t pos)
+{
+ if (path == NULL)
+ return;
+ path->error.type = error_type;
+ path->error.message = message;
+ path->error.pos = pos;
+}
+
+static void
+yaml_path_sections_remove (yaml_path_t *path)
+{
+ if (path == NULL)
+ return;
+ while (!TAILQ_EMPTY(&path->sections_list)) {
+ yaml_path_section_t *el = TAILQ_FIRST(&path->sections_list);
+ TAILQ_REMOVE(&path->sections_list, el, entries);
+ path->sections_count--;
+ switch (el->type) {
+ case YAML_PATH_SECTION_KEY:
+ free((void *)el->data.key);
+ break;
+ case YAML_PATH_SECTION_ANCHOR:
+ free((void *)el->data.anchor);
+ break;
+ case YAML_PATH_SECTION_SLICE:
+ if (path->sequence_level == el->level)
+ path->sequence_level = 0;
+ break;
+ default:
+ break;
+ }
+ free(el);
+ }
+}
+
+static yaml_path_section_t*
+yaml_path_section_create (yaml_path_t *path, yaml_path_section_type_t section_type)
+{
+ yaml_path_section_t *el = malloc(sizeof(*el));
+ path->sections_count++;
+ el->level = path->sections_count;
+ el->type = section_type;
+ TAILQ_INSERT_TAIL(&path->sections_list, el, entries);
+ if (el->type == YAML_PATH_SECTION_SLICE && !path->sequence_level) {
+ path->sequence_level = el->level;
+ }
+ return el;
+}
+
+static size_t
+yaml_path_section_snprint (yaml_path_section_t *section, char *s, size_t max_len)
+{
+ if (s == NULL)
+ return -1;
+ if (section == NULL)
+ return 0;
+
+ size_t len = 0;
+ switch (section->type) {
+ case YAML_PATH_SECTION_KEY:
+ len = snprintf(s, max_len, ".%s", section->data.key);
+ break;
+ case YAML_PATH_SECTION_ANCHOR:
+ len = snprintf(s, max_len, "[&%s]", section->data.anchor);
+ break;
+ case YAML_PATH_SECTION_INDEX:
+ len = snprintf(s, max_len, "[%d]", section->data.index);
+ break;
+ case YAML_PATH_SECTION_SLICE:
+ len = snprintf(s, max_len, "[%d:%d:%d]", section->data.slice.start, section->data.slice.end, section->data.slice.stride);
+ break;
+ default:
+ len = snprintf(s, max_len, "<?>");
+ break;
+ }
+ return len;
+}
+
+static void
+_parse (yaml_path_t *path, char *s_path) {
+ char *sp = s_path;
+ char *spe = NULL;
+
+ if (s_path == NULL || !s_path[0]) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Path is empty", 0);
+ return;
+ }
+
+ while (*sp != '\0') {
+ switch (*sp) {
+ case '.':
+ case '[':
+ if (*sp == '.') {
+ // Key
+ spe = sp + 1;
+ while (*spe != '.' && *spe != '[' && *spe != '\0')
+ spe++;
+ if (spe == sp+1) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path);
+ goto error;
+ }
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+ sec->data.key = strndup(sp + 1, spe-sp - 1);
+ sp = spe-1;
+ } else if (*sp == '[') {
+ spe = sp+1;
+ if (*spe == '&') {
+ // Anchor
+ sp = spe;
+ while (*spe != ']' && *spe != '\0')
+ spe++;
+ if (spe == sp+1) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment anchor is missing", sp - s_path);
+ goto error;
+ }
+ if (*spe == '\0') {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment anchor is invalid (unxepected end of string, missing ']')", sp - s_path);
+ goto error;
+ }
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ANCHOR);
+ sec->data.anchor = strndup(sp + 1, spe-sp - 1);
+ sp = spe;
+ } else if (*spe == '\'') {
+ // Key
+ sp = spe;
+ spe++;
+ while (*spe != '\'' && *spe != '\0')
+ spe++;
+ if (spe == sp+1) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path);
+ goto error;
+ }
+ if (*spe == '\0') {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (unxepected end of string, missing ''')", sp - s_path);
+ goto error;
+ }
+ spe++;
+ if (*spe == '\0') {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (unxepected end of string, missing ']')", sp - s_path);
+ goto error;
+ }
+ if (*spe != ']') {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (invalid character)", spe - s_path);
+ goto error;
+ }
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+ sec->data.key = strndup(sp + 1, spe-sp - 2);
+ sp = spe;
+ } else {
+ // Index or Slice
+ int idx = strtol(spe, &spe, 10);
+ if (*spe == ']') {
+ // Index
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_INDEX);
+ sec->data.index = idx;
+ sp = spe;
+ } else if (*spe == ':') {
+ // Slice
+ int idx_start = idx;
+ sp = spe++;
+ idx = strtol(spe, &spe, 10);
+ if (*spe == ':') {
+ int idx_end = (spe == sp+1 ? __INT_MAX__ : idx);
+ sp = spe++;
+ idx = strtol(spe, &spe, 10);
+ if (*spe == ']' && (idx > 0 || spe == sp+1)) {
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SLICE);
+ sec->data.slice.start = idx_start;
+ sec->data.slice.end = idx_end;
+ sec->data.slice.stride = idx > 0 ? idx : 1;
+ sp = spe;
+ } else if (*spe == ']' && idx <= 0) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice stride can not be less than 1", spe - s_path - 1);
+ goto error;
+ } else {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice stride is invalid (invalid character)", spe - s_path);
+ goto error;
+ }
+ } else if (*spe == ']') {
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SLICE);
+ sec->data.slice.start = idx_start;
+ sec->data.slice.end = (spe == sp+1 ? __INT_MAX__ : idx);
+ sec->data.slice.stride = 1;
+ sp = spe;
+ } else {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice end index is invalid (invalid character)", spe - s_path);
+ goto error;
+ }
+ } else if (*spe == '\0') {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment index is invalid (unxepected end of string, missing ']')", spe - s_path);
+ goto error;
+ } else {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment index is invalid (invalid character)", spe - s_path);
+ goto error;
+ }
+ }
+ }
+ break;
+ default:
+ if (path->sections_count == 0) {
+ // Key
+ spe = sp + 1;
+ if (*sp == '$' && (*spe == '.' || *spe == '[')) {
+ // Ignore leading '$'
+ // TODO: Should we do this?
+ } else {
+ while (*spe != '.' && *spe != '[' && *spe != '\0')
+ spe++;
+ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+ sec->data.key = strndup(sp, spe-sp);
+ sp = spe-1;
+ }
+ }
+ break;
+ }
+ sp++;
+ }
+
+ if (path->sections_count == 0) {
+ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Invalid path segments", 0);
+ }
+
+ return;
+
+error:
+ yaml_path_sections_remove(path);
+}
+
+static yaml_path_section_t*
+yaml_path_section_get_at_level (yaml_path_t *path, size_t level)
+{
+ if (path == NULL)
+ return NULL;
+ yaml_path_section_t *el;
+ TAILQ_FOREACH(el, &path->sections_list, entries) {
+ if (el->level == level)
+ return el;
+ }
+ return NULL;
+}
+
+static yaml_path_section_t*
+yaml_path_section_get_last (yaml_path_t *path)
+{
+ if (path == NULL)
+ return NULL;
+ return yaml_path_section_get_at_level(path, path->sections_count);
+}
+
+static yaml_path_section_t*
+yaml_path_section_get_current (yaml_path_t *path)
+{
+ if (path == NULL)
+ return NULL;
+ return yaml_path_section_get_at_level(path, path->current_level);
+}
+
+static int
+yaml_path_prev_section_is_valid (yaml_path_t *path)
+{
+ if (path == NULL)
+ return 0;
+ yaml_path_section_t *sec = yaml_path_section_get_at_level(path, path->current_level-1);
+ if (sec == NULL)
+ return -1;
+ return sec->valid;
+}
+
+static int
+yaml_path_prev_sections_are_valid (yaml_path_t *path)
+{
+ if (path == NULL)
+ return 0;
+ int valid = 1;
+ yaml_path_section_t *el;
+ TAILQ_FOREACH(el, &path->sections_list, entries) {
+ if (el->level < path->current_level)
+ valid = el->valid && valid;
+ }
+ return valid;
+}
+
+static int
+yaml_path_all_sections_are_valid (yaml_path_t *path)
+{
+ if (path == NULL)
+ return 0;
+ int valid = 1;
+ yaml_path_section_t *el;
+ TAILQ_FOREACH(el, &path->sections_list, entries) {
+ valid = el->valid && valid;
+ }
+ return valid;
+}
+
+static int
+yaml_path_section_current_is_last (yaml_path_t *path)
+{
+ if (path == NULL)
+ return 0;
+ return path->current_level == path->sections_count;
+}
+
+static int
+yaml_path_section_current_is_mandatory_sequence (yaml_path_t *path)
+{
+ if (path == NULL)
+ return 0;
+ yaml_path_section_t *sec = yaml_path_section_get_current(path);
+ if (sec == NULL)
+ return 0;
+ return (sec->type == YAML_PATH_SECTION_SLICE && path->current_level == path->sequence_level);
+}
+
+/* Public */
+
+yaml_path_t*
+yaml_path_create (void)
+{
+ yaml_path_t *ypath = malloc(sizeof(*ypath));
+
+ if (ypath != NULL) {
+ TAILQ_INIT(&ypath->sections_list);
+ ypath->sections_count = 0;
+ ypath->sequence_level = 0;
+ ypath->current_level = 0;
+ }
+
+ return ypath;
+}
+
+int
+yaml_path_parse (yaml_path_t *path, char *s_path)
+{
+ if (path == NULL)
+ return -1;
+
+ yaml_path_sections_remove(path);
+ memset(&path->error, 0, sizeof(path->error));
+
+ _parse(path, s_path);
+
+ if (path->sections_count == 0)
+ return -2;
+
+ return 0;
+}
+
+void
+yaml_path_destroy (yaml_path_t *path)
+{
+ if (path == NULL)
+ return;
+ yaml_path_sections_remove(path);
+ free(path);
+}
+
+/* API */
+
+const yaml_path_error_t*
+yaml_path_error_get (yaml_path_t *path)
+{
+ if (path == NULL)
+ return NULL;
+ return &path->error;
+}
+
+size_t
+yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len)
+{
+ if (s == NULL)
+ return -1;
+ if (path == NULL)
+ return 0;
+
+ size_t len = 0;
+
+ yaml_path_section_t *el;
+ TAILQ_FOREACH(el, &path->sections_list, entries) {
+ len += yaml_path_section_snprint(el, s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len));
+ }
+
+ return len;
+}
+
+int
+yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event, yaml_path_filter_mode_t mode)
+{
+ int res = 0;
+
+ if (path == NULL || parser == NULL || event == NULL)
+ goto exit;
+
+ const char *anchor = NULL;
+ switch(event->type) {
+ case YAML_MAPPING_START_EVENT:
+ anchor = (const char *)event->data.mapping_start.anchor;
+ break;
+ case YAML_SEQUENCE_START_EVENT:
+ anchor = (const char *)event->data.sequence_start.anchor;
+ break;
+ case YAML_SCALAR_EVENT:
+ anchor = (const char *)event->data.scalar.anchor;
+ break;
+ default:
+ break;
+ }
+
+ yaml_path_section_t *current_section = yaml_path_section_get_current(path);
+ if (current_section) {
+ switch (event->type) {
+ case YAML_MAPPING_START_EVENT:
+ case YAML_SEQUENCE_START_EVENT:
+ case YAML_ALIAS_EVENT:
+ case YAML_SCALAR_EVENT:
+ switch (current_section->node_type) {
+ case YAML_MAPPING_NODE:
+ if (current_section->type == YAML_PATH_SECTION_KEY) {
+ if (current_section->counter % 2) {
+ current_section->valid = current_section->next_valid;
+ current_section->next_valid = 0;
+ } else {
+ current_section->next_valid = !strcmp(current_section->data.key, (const char *)event->data.scalar.value);
+ current_section->valid = 0;
+ }
+ } else if (current_section->type == YAML_PATH_SECTION_ANCHOR && anchor != NULL) {
+ current_section->valid = !strcmp(current_section->data.key, anchor);
+ } else {
+ current_section->valid = 0;
+ }
+ break;
+ case YAML_SEQUENCE_NODE:
+ if (current_section->type == YAML_PATH_SECTION_INDEX) {
+ current_section->valid = current_section->data.index == current_section->counter;
+ } else if (current_section->type == YAML_PATH_SECTION_SLICE) {
+ current_section->valid = current_section->data.slice.start <= current_section->counter &&
+ current_section->data.slice.end > current_section->counter &&
+ (current_section->data.slice.start + current_section->counter) % current_section->data.slice.stride == 0;
+ } else if (current_section->type == YAML_PATH_SECTION_ANCHOR && anchor != NULL) {
+ current_section->valid = !strcmp(current_section->data.key, anchor);
+ } else {
+ current_section->valid = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ current_section->counter++;
+ default:
+ break;
+ }
+ //TODO: DEBUG printf("iv: %d, t: %d, nt: %d, lev: %d\n", current_section->valid, current_section->type, current_section->node_type, current_section->level);
+ }
+
+ switch (event->type) {
+ case YAML_STREAM_START_EVENT:
+ case YAML_STREAM_END_EVENT:
+ case YAML_DOCUMENT_START_EVENT:
+ case YAML_DOCUMENT_END_EVENT:
+ case YAML_NO_EVENT:
+ res = 1;
+ break;
+ case YAML_MAPPING_START_EVENT:
+ case YAML_SEQUENCE_START_EVENT:
+ current_section = yaml_path_section_get_current(path);
+ if (current_section && yaml_path_section_current_is_last(path)) {
+ res = current_section->valid && yaml_path_prev_section_is_valid(path);
+ } else {
+ if (path->current_level > path->sections_count)
+ if ((!current_section && mode == YAML_PATH_FILTER_RETURN_ALL) || path->current_level == path->sections_count)
+ res = yaml_path_all_sections_are_valid(path);
+ };
+ path->current_level++;
+ current_section = yaml_path_section_get_current(path);
+ if (current_section && yaml_path_section_current_is_mandatory_sequence(path)) {
+ res = yaml_path_prev_section_is_valid(path);
+ }
+ if (current_section) {
+ current_section->node_type = event->type == YAML_MAPPING_START_EVENT ? YAML_MAPPING_NODE : YAML_SEQUENCE_NODE;
+ current_section->counter = 0;
+ }
+ break;
+ case YAML_MAPPING_END_EVENT:
+ case YAML_SEQUENCE_END_EVENT:
+ if (current_section && yaml_path_section_current_is_mandatory_sequence(path)) {
+ res = yaml_path_prev_section_is_valid(path);
+ }
+ path->current_level--;
+ if (path->current_level < path->sections_count && (path->current_level != path->sequence_level || !path->sequence_level))
+ break;
+ current_section = yaml_path_section_get_current(path);
+ if (current_section && yaml_path_section_current_is_last(path)) {
+ res = current_section->valid && yaml_path_prev_section_is_valid(path);
+ } else {
+ if ((!current_section && mode == YAML_PATH_FILTER_RETURN_ALL) || path->current_level == path->sections_count) {
+ res = yaml_path_all_sections_are_valid(path);
+ }
+ }
+ break;
+ case YAML_ALIAS_EVENT:
+ case YAML_SCALAR_EVENT:
+ if (!current_section) {
+ if (mode == YAML_PATH_FILTER_RETURN_ALL || path->current_level == path->sections_count)
+ res = yaml_path_all_sections_are_valid(path);
+ } else {
+ res = current_section->valid && yaml_path_prev_sections_are_valid(path) && yaml_path_section_current_is_last(path);
+ }
+ break;
+ default:
+ break;
+ }
+
+exit:
+ return res;
+}
diff -r -N -U3 a/yaml-filter/yaml-path.h b/yaml-filter/yaml-path.h
--- a/yaml-filter/yaml-path.h 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/yaml-path.h 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,46 @@
+#ifndef YAML_PATH_H
+#define YAML_PATH_H
+
+#include <yaml.h>
+
+
+typedef struct yaml_path yaml_path_t;
+
+typedef enum yaml_path_error_type {
+ YAML_PATH_ERROR_NONE,
+ YAML_PATH_ERROR_PARSE,
+} yaml_path_error_type_t;
+
+typedef struct yaml_path_error {
+ yaml_path_error_type_t type;
+ const char *message;
+ const char *context;
+ size_t pos;
+} yaml_path_error_t;
+
+typedef enum yaml_path_filter_mode {
+ YAML_PATH_FILTER_RETURN_ALL,
+ YAML_PATH_FILTER_RETURN_SHALLOW,
+} yaml_path_filter_mode_t;
+
+
+yaml_path_t*
+yaml_path_create (void);
+
+int
+yaml_path_parse (yaml_path_t *path, char *s_path);
+
+void
+yaml_path_destroy (yaml_path_t *path);
+
+const yaml_path_error_t*
+yaml_path_error_get (yaml_path_t *path);
+
+int
+yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event, yaml_path_filter_mode_t mode);
+
+size_t
+yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len);
+
+#endif//YAML_PATH_H
+
diff -r -N -U3 a/yaml-filter/yamlp.c b/yaml-filter/yamlp.c
--- a/yaml-filter/yamlp.c 1970-01-01 01:00:00.000000000 +0100
+++ b/yaml-filter/yamlp.c 2020-02-25 13:34:48.947813560 +0100
@@ -0,0 +1,207 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <yaml.h>
+
+#include "yaml-path.h"
+
+
+int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t *path, yaml_path_filter_mode_t mode, int use_flow_style)
+{
+ yaml_event_t event;
+ yaml_event_type_t event_type;
+
+ do {
+ if (!yaml_parser_parse(parser, &event)) {
+ switch (parser->error) {
+ case YAML_MEMORY_ERROR:
+ fprintf(stderr, "Memory error: Not enough memory for parsing\n");
+ break;
+ case YAML_READER_ERROR:
+ if (parser->problem_value != -1) {
+ fprintf(stderr, "Reader error: %s: #%X at %ld\n", parser->problem, parser->problem_value, (long)parser->problem_offset);
+ } else {
+ fprintf(stderr, "Reader error: %s at %ld\n", parser->problem, (long)parser->problem_offset);
+ }
+ break;
+ case YAML_SCANNER_ERROR:
+ if (parser->context) {
+ fprintf(stderr, "Scanner error: %s at line %d, column %d\n%s at line %d, column %d\n", parser->context,
+ (int)parser->context_mark.line+1,(int)parser->context_mark.column+1, parser->problem,
+ (int)parser->problem_mark.line+1, (int)parser->problem_mark.column+1);
+ } else {
+ fprintf(stderr, "Scanner error: %s at line %d, column %d\n", parser->problem, (int)parser->problem_mark.line+1, (int)parser->problem_mark.column+1);
+ }
+ break;
+ case YAML_PARSER_ERROR:
+ if (parser->context) {
+ fprintf(stderr, "Parser error: %s at line %d, column %d\n%s at line %d, column %d\n", parser->context,
+ (int)parser->context_mark.line+1, (int)parser->context_mark.column+1, parser->problem,
+ (int)parser->problem_mark.line+1, (int)parser->problem_mark.column+1);
+ } else {
+ fprintf(stderr, "Parser error: %s at line %d, column %d\n", parser->problem, (int)parser->problem_mark.line+1, (int)parser->problem_mark.column+1);
+ }
+ break;
+ default:
+ fprintf(stderr, "Internal error\n");
+ break;
+ }
+ return 1;
+ } else {
+ event_type = event.type;
+ if (!yaml_path_filter_event(path, parser, &event, mode)) {
+ yaml_event_delete(&event);
+ } else {
+ if (use_flow_style) {
+ switch (event.type) {
+ case YAML_SEQUENCE_START_EVENT:
+ event.data.sequence_start.style = YAML_FLOW_SEQUENCE_STYLE;
+ break;
+ case YAML_MAPPING_START_EVENT:
+ event.data.mapping_start.style = YAML_FLOW_MAPPING_STYLE;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!yaml_emitter_emit(emitter, &event)) {
+ switch (emitter->error)
+ {
+ case YAML_MEMORY_ERROR:
+ fprintf(stderr, "Memory error: Not enough memory for emitting\n");
+ break;
+ case YAML_WRITER_ERROR:
+ fprintf(stderr, "Writer error: %s\n", emitter->problem);
+ break;
+ case YAML_EMITTER_ERROR:
+ fprintf(stderr, "Emitter error: %s\n", emitter->problem);
+ break;
+ default:
+ fprintf(stderr, "Internal error\n");
+ break;
+ }
+ return 2;
+ }
+ }
+ }
+ } while (event_type != YAML_STREAM_END_EVENT);
+
+ return 0;
+}
+
+
+void help (void)
+{
+ printf("yamlp - filtering utility for YAML documents\n");
+ printf("\n");
+ printf("Usage: yamlp [-F] [-S] [-W <width>] [-f <file>] <path>\n");
+ printf(" yamlp -h\n");
+ printf("\n");
+ printf("The tool will take the input YAML document from <stdin> or a <file> (-f option),\n");
+ printf("and it will then return the portion of the document marked with the given <path>.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -f a filename to get the YAML document from,\n");
+ printf(" <stdin> will be used if omitted;\n");
+ printf("\n");
+ printf(" -F forced 'flow' style for the output YAML document;\n");
+ printf("\n");
+ printf(" -h help;\n");
+ printf("\n");
+ printf(" -S 'shallow' filter mode;\n");
+ printf("\n");
+ printf(" -W line wrap width, no wrapping if omitted.\n");
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int flow = 0;
+ char *file_name = NULL;
+ char *path_string = NULL;
+ int wrap = -1;
+ yaml_path_filter_mode_t mode = YAML_PATH_FILTER_RETURN_ALL;
+
+ int opt;
+ while ((opt = getopt(argc, argv, ":f:W:vhSF")) != -1) {
+ switch (opt) {
+ case 'h':
+ help();
+ return 0;
+ break;
+ case 'F':
+ flow = 1;
+ break;
+ case 'S':
+ mode = YAML_PATH_FILTER_RETURN_SHALLOW;
+ break;
+ case 'W':
+ wrap = strtol(optarg, NULL, 10);
+ if (!wrap) {
+ fprintf(stderr, "Invalid value for wrap width '%s'\n", optarg);
+ return 1;
+ }
+ break;
+ case 'f':
+ file_name = optarg;
+ break;
+ case ':':
+ fprintf(stderr, "Option needs a value\n");
+ return 1;
+ break;
+ case '?':
+ fprintf(stderr, "Unknown option '%c'\n", optopt);
+ return 1;
+ break;
+ }
+ }
+
+ for (; optind < argc; optind++) {
+ path_string = argv[optind];
+ }
+
+ FILE *file = NULL;
+ if (file_name != NULL) {
+ file = fopen(file_name, "r");
+ if (file == NULL) {
+ fprintf(stderr, "Unable to open file '%s' (%s)\n", file_name, strerror(errno));
+ return 2;
+ }
+ }
+
+ if (path_string == NULL || path_string[0] == 0) {
+ fprintf(stderr, "Empty path\n");
+ return 3;
+ }
+
+ yaml_path_t *path = yaml_path_create();
+ if (yaml_path_parse(path, path_string)) {
+ fprintf(stderr, "Invalid path '%s' (%s)\n", path_string, yaml_path_error_get(path)->message);
+ return 3;
+ };
+
+ yaml_parser_t parser;
+ yaml_emitter_t emitter;
+
+ yaml_parser_initialize(&parser);
+ yaml_parser_set_input_file(&parser, file != NULL ? file : stdin);
+
+ yaml_emitter_initialize(&emitter);
+ yaml_emitter_set_output_file(&emitter, stdout);
+ yaml_emitter_set_width(&emitter, wrap);
+
+ if (parse_and_emit(&parser, &emitter, path, mode, flow)) {
+ return 4;
+ }
+
+ yaml_parser_delete(&parser);
+ yaml_emitter_delete(&emitter);
+
+ yaml_path_destroy(path);
+ fclose(file);
+
+ return 0;
+}