Blob Blame History Raw
diff --git a/.travis.yml b/.travis.yml
index 7e776c3..7382736 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,29 @@
 language: c
-dist: bionic
+
+os:
+  - linux
 
 before_script:
- - mkdir -p build
- - cd build
- - sudo apt-get install -y libyaml-0-2
+  - mkdir -p build
+  - cd build
+  - sudo apt-get install -y libyaml-0-2
 
 script:
- - cmake -DENABLE_COVERAGE=yes ..
- - make && ctest -V
- - make gcov
+  - cmake -DENABLE_COVERAGE=yes ..
+  - build-wrapper-linux-x86-64 --out-dir bw-output make clean all || true # Allowed to fail in builds from forks
+  - make && ctest -V
+  - make gcov
+  - sonar-scanner -Dproject.settings=../sonar-project.properties || true # Allowed to fail in builds from forks
 
 after_success:
- - curl -s https://codecov.io/bash > cov.sh && bash cov.sh -x "$GCOV"
+  - curl -s https://codecov.io/bash > cov.sh && bash cov.sh -x "$GCOV"
+
+addons:
+  sonarcloud:
+    organization: "openscap"
+    token:
+      secure: "BoPCYdulv5+7sNQtShKE0tYVelHSL3sWNhjz5QFZCDefK7az2+Lze9Zp/sas02gmBLoc0vuuFjFgtKLSPt9mEL4YabwAysCF445jwE+wu8KJf/Bz36tMz1gE9383+Ic9LcR2bdPBY/0gBWZpmKK33mNs+P5xuI23XsK2Whkjev9tg/kjjXAd3Q79WqlDW2SKT5Ugg5SxE6RgFS/pBsxWsqp3Vfx38t3hIhloECf51/aVrBabpYmwYe8l8gq/+A2PO/gpw6SBdCu2Y9x+zlAhWkY7cri7C8LSUSMS0pUM5haij4oO/7UVUbRjWmTDpg3sSLtJka2BIIl32XINMdvBCzbhIpuNCTZlWz3KCyBWRvV8r95n+p2IahbV7ZU/Jc8QvBNC0IsjCjHORtuJzXOa3BCZ2PXggboX1uaHkLe+xECC/3gjLDXAcUvM6QJN2Ytbnzfd2jTlhCt1a3ttCPUvSqN9CIJWIKnbpHPWwAk7YuMH7GXbCle9mInDvPOe16KTQ39RMsWRE1HgyTHErfT5pyaCwv2lq4oThuCrUyAGmMYgC6OZalW9DciQwp/kzKECnrwfjCCy8uJu/BlXWGMeJWjRaLFpOpZLIPaVVvKlODb8zZUZeVV1sqyyQ6+I4Opfmi0ikVW4eOqyXRy1G1O9OxXeHC2SHFuK4E5cks05EA4="
+
+cache:
+  directories:
+    - '$HOME/.sonar/cache'
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 796709d..9c14766 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,5 @@
 cmake_minimum_required(VERSION 2.8)
+project(yaml-path C)
 
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
 
@@ -6,35 +7,35 @@ include(FindPkgConfig)
 pkg_check_modules(YAML yaml-0.1)
 find_package(codecov)
 
-include_directories(${YAML_INCLUDE_DIRS})
+include_directories(${YAML_INCLUDE_DIRS} src)
 
-add_library(yaml-path yaml-path.c)
+add_library(yaml-path src/yaml-path.c)
 target_link_libraries(yaml-path ${YAML_LIBRARIES})
+add_coverage(yaml-path)
 
-add_executable(ya yaml.c)
-target_link_libraries(ya yaml-path)
-add_coverage(ya)
-
-add_executable(yamlp yamlp.c)
+add_executable(yamlp src/yamlp.c)
 target_link_libraries(yamlp yaml-path)
 add_coverage(yamlp)
 
-add_coverage(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_coverage(test-path-segments)
+if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -std=c99 -W -Wall -Wnonnull -Wshadow -Wformat -Wundef -Wno-unused-parameter -Wmissing-prototypes -Wno-unknown-pragmas -D_GNU_SOURCE -D_POSIX_C_SOURCE=200112L")
+endif()
+if(${CMAKE_SYSTEM_NAME} EQUAL "Solaris")
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__EXTENSIONS__")
+endif()
+if(WIN32)
+	# Expose new WinAPI function appearing on Windows 7 (e.g. inet_pton)
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WIN32_WINNT=0x0600")
+endif()
+if(APPLE)
+	# Full Single Unix Standard v3 (SUSv3) conformance (the Unix API)
+	add_definitions(-D_DARWIN_C_SOURCE)
+endif()
 
-add_executable(test-paths test-paths.c)
-target_link_libraries(test-paths yaml-path)
-add_coverage(test-paths)
-list(APPEND LCOV_REMOVE_PATTERNS "'${CMAKE_SOURCE_DIR}/test-*'")
+enable_testing()
+add_subdirectory("tests")
 
 coverage_evaluate()
 
-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 --git a/README.md b/README.md
index 9f66685..26ecd44 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
-# YAML Path filter
+[![Build Status](https://travis-ci.org/OpenSCAP/yaml-filter.svg?branch=master)](https://travis-ci.org/OpenSCAP/yaml-filter) [![Total alerts](https://img.shields.io/lgtm/alerts/g/OpenSCAP/yaml-filter.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/OpenSCAP/yaml-filter/alerts/)
 
-YAML document filtering for libyaml
+# YAML filter
+
+YAML documnets filtering library (based on [libyaml](https://github.com/yaml/libyaml)).
+
+[How to build and test it](docs/developer.md).
+
+[YAML Path definition (v1)](docs/yaml_path_v1.md).
\ No newline at end of file
diff --git a/docs/developer.md b/docs/developer.md
new file mode 100644
index 0000000..1d396af
--- /dev/null
+++ b/docs/developer.md
@@ -0,0 +1,86 @@
+# Developer's Guide
+
+## Building on Linux
+
+If you want to build the `libyaml-path` library and `yamlp` filtering utility follow the these instructions:
+
+
+### 1. Get the source code
+
+```sh
+$ git clone https://github.com/OpenSCAP/yaml-filter.git
+$ cd yaml-filter
+```
+
+
+### 2. *Get the build dependencies*
+
+To build the library you will also need to install the build dependencies.
+
+The project relies on CMake (`cmake`) build system and C99 compiler (for example `gcc`).
+
+The only mandatroy dependency of `libyaml-path` is the YAML document parser/emitter library `libyaml`. You can also use `lcov` for coverage reports, but it is optional.
+
+```sh
+# Ubuntu
+$ sudo apt-get install -y libyaml-0-2
+```
+
+```sh
+# Fedora
+$ sudo dnf install -y libyaml lcov
+```
+
+When you have all the build dependencies installed you can build the library.
+
+
+### 3. *Build the project*
+
+Run the following commands to build the library and filtering utility:
+
+```sh
+$ mkdir -p build
+$ cd build/
+$ cmake ..
+$ make
+```
+
+
+### 3. *Run the tests*
+
+Now you can execute the following command to run library self-checks:
+
+```sh
+$ ctest
+```
+
+
+### 4. *Install*
+
+Run the installation procedure by executing the following command:
+
+```sh
+$ make install
+```
+
+You can also configure CMake to install everything into the $HOME/.local directory:
+
+```sh
+$ cd build
+$ rm -rf *
+$ cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local ..
+$ make
+$ make install
+```
+
+
+### 5. *Generate code coverage report*
+
+You can use `lcov` for code coverage report generation. It is integrated into the project's build configuration:
+
+```sh
+$ cd build
+$ cmake -DENABLE_COVERAGE=yes ..
+$ make && ctest -V
+$ make gcov
+```
\ No newline at end of file
diff --git a/docs/yaml_path_v1.md b/docs/yaml_path_v1.md
new file mode 100644
index 0000000..e84dbb3
--- /dev/null
+++ b/docs/yaml_path_v1.md
@@ -0,0 +1,119 @@
+### v1.0.0 2020-09-28
+
+## Overview
+This implementation is a subset of the intersection between [JSONPath](https://goessner.net/articles/JsonPath) and [YAML Path](https://pypi.org/project/yamlpath), and it focuses on addressing elements inside YAML files (node descriptors). There are no *query*-like capabilities.
+
+Example YAML structure:
+```yaml
+foo:
+  - bar: &bar True
+    first: First Bar
+    second: 2
+    arr: [1, 2, 3]
+  - baz: False
+    other_bar: *bar
+    first: First Baz
+    some.el/here: Delimiters...
+    "bar's": 0
+```
+
+Example JSON structure:
+```json
+{
+    "foo": [
+        {
+            "bar": true,
+            "first": "First Bar", 
+            "second": 2,
+            "arr": [1, 2, 3]
+        },
+        {
+            "baz": false,
+            "other_bar": true,
+            "first": "First Baz",
+            "some.el/here": "Delimiters...",
+            "bar's": 0,
+        }
+    ]
+}
+```
+
+## Delimiters
+Dot-notation (`.`) is the only supported notation to define *map key* path segments. Both *map key* and *sequence index* path segments could also be defined using square brackets (`[`, `]`). For *map key* and *map keys selection* segments both single (`'`) and double (`"`) quotes are supported, `['key']` is the same as `["key"]` and `['key',"other's key"]` is a valid path segment.
+
+For example: `$.foo[0].bar` or `.foo[0]['bar']` or `['foo'][0].bar` or `foo[1]["bar's"]`.
+
+## Special symbols
+A path might be prefixed by a dollar sign and a dot (`$`, `.`), but this prefix is retained for compatibility with *JSONPath* and not mandatory. Implicit *document root* is assumed unless the path explicitly starts with an *anchor* (`&...`) segment (see below for details).
+
+If the first segment is a *map key* segment (and explicit *document root* is omitted) the initial dot (`.`) is also not mandatory.
+
+For example, these paths are equal: `$.el`, `.el`, `el`, `['el']`. And they all address the value stored in the "el" key of the top-most map of the document.
+
+An asterisk (`*`) as a key name has a special meaning, and treated as an all-inclusive *keys selection* section (see below). That's it, `$.*` expression would include all keys of the map in the document root. The `[*]` syntax is also valid. One should use the `[:]` notation to acheive same effect for sequences (include all indices).
+
+## Path Segment Types
+
+#### Document Root
+`$`
+
+Optional explicit document root. Only allowed to appear at the beginning of the path.
+
+```python
+$.foo[0].bar = .foo[0].bar = foo[0].bar
+== true
+```
+
+
+#### Map Key
+`.map.key` or `.map['key']`
+
+```python
+$.foo[0].second = ['foo'][0]['second']
+== 2
+```
+
+
+#### Map Keys Selection
+`.map['key1','key2',...'keyN']`
+
+Special syntax for the all-inclusive key selection: `.*`. Also, there is the `[*]` variant of this syntax.
+
+```python
+$.foo[0]['first','second'] = ['foo'][0]['first','second']
+== {"first": "First Bar", "second": 2}
+
+foo[0]['first','second','bar','arr'] = foo[0].*
+== {"bar": true, "first": "First Bar", "second": 2, "arr": [1, 2, 3]}
+```
+
+
+#### Sequence Index
+`.array[<zero or positive number>]`
+
+```python
+$.foo[0]
+== {"bar": True, "first": "First Bar", "second": 2}
+```
+
+
+#### Sequence Indices Set
+`.array[<zero or positive number>,<zero or positive number>,...<zero or positive number>]`
+
+Special syntax for the all-inclusive indices set: `[:]`.
+
+```python
+$.foo[0].arr[0,1,2] = foo[0].arr[:]
+== [1, 2, 3]
+```
+
+
+#### Anchor
+`&anchor`
+
+Matches elements starting from the given anchor instead of the document root. This segment is only sensible in paths for YAML documents as there is no anchors/aliases concept in the JSON specification.
+
+```python
+$.foo[0].bar = &bar
+== True
+```
\ No newline at end of file
diff --git a/openshift-logging.yaml b/res/openshift-logging.yaml
similarity index 100%
rename from openshift-logging.yaml
rename to res/openshift-logging.yaml
diff --git a/res/openshift-upgradeable.yaml b/res/openshift-upgradeable.yaml
new file mode 100644
index 0000000..e962fdd
--- /dev/null
+++ b/res/openshift-upgradeable.yaml
@@ -0,0 +1,39 @@
+apiVersion: config.openshift.io/v1
+kind: ClusterOperator
+metadata:
+  annotations:
+    exclude.release.openshift.io/internal-openshift-hosted: "true"
+  creationTimestamp: "2020-06-08T04:36:36Z"
+  generation: 1
+  name: openshift-apiserver
+  resourceVersion: "53861"
+  selfLink: /apis/config.openshift.io/v1/clusteroperators/openshift-apiserver
+spec: {}
+status:
+  conditions:
+  - lastTransitionTime: "2020-06-08T04:54:58Z"
+    reason: AsExpected
+    status: "False"
+    type: Degraded
+  - lastTransitionTime: "2020-06-08T06:34:00Z"
+    reason: AsExpected
+    status: "False"
+    type: Progressing
+  - lastTransitionTime: "2020-06-08T04:51:08Z"
+    reason: AsExpected
+    status: "True"
+    type: Available
+  - lastTransitionTime: "2020-06-08T04:45:45Z"
+    reason: AsExpected
+    status: "True"
+    type: Upgradeable
+  extension: null
+  relatedObjects:
+  - group: operator.openshift.io
+    name: cluster
+    resource: openshiftapiservers
+  versions:
+  - name: operator
+    version: 4.5.0-0.nightly-2020-06-04-214605
+  - name: openshift-apiserver
+    version: 4.5.0-0.nightly-2020-06-04-214605
\ No newline at end of file
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..8983591
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,8 @@
+sonar.organization=openscap
+
+sonar.projectKey=OpenSCAP_yaml-filter
+sonar.projectName=yaml-filter
+
+sonar.sources=.
+
+sonar.cfamily.build-wrapper-output=bw-output
diff --git a/src/yaml-path.c b/src/yaml-path.c
new file mode 100644
index 0000000..004c61d
--- /dev/null
+++ b/src/yaml-path.c
@@ -0,0 +1,829 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <assert.h>
+
+#include <yaml.h>
+
+#include "yaml-path.h"
+
+
+#define YAML_PATH_MAX_SECTION_ITEMS    256
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+
+typedef enum yaml_path_section_type {
+	YAML_PATH_SECTION_ROOT,
+	YAML_PATH_SECTION_ANCHOR,
+	YAML_PATH_SECTION_INDEX,
+	YAML_PATH_SECTION_SET,
+	YAML_PATH_SECTION_KEY,
+	YAML_PATH_SECTION_SELECTION,
+} yaml_path_section_type_t;
+
+typedef struct yaml_path_selection_key_raw {
+	const char *start;
+	size_t len;
+} yaml_path_selection_key_raw_t;
+
+
+typedef struct yaml_path_key {
+	const char *key;
+	TAILQ_ENTRY(yaml_path_key) entries;
+} yaml_path_key_t;
+
+typedef TAILQ_HEAD(path_key_list, yaml_path_key) path_key_list_t;
+
+
+typedef struct yaml_path_section {
+	yaml_path_section_type_t type;
+	size_t level;
+	union {
+		const char *anchor;
+		size_t index;
+		size_t *set;
+		const char *key;
+		path_key_list_t selection;
+	} data;
+	TAILQ_ENTRY(yaml_path_section) entries;
+
+	yaml_node_type_t node_type;
+	size_t counter;
+	bool valid;
+	bool next_valid;
+} yaml_path_section_t;
+
+typedef TAILQ_HEAD(path_section_list, yaml_path_section) path_section_list_t;
+
+
+struct yaml_path {
+	path_section_list_t sections_list;
+	size_t sections_count;
+	size_t current_level;
+	size_t start_level;
+
+	yaml_path_error_t error;
+};
+
+
+static size_t
+yaml_path_set_snprint (const size_t *set, char *s, size_t max_len)
+{
+	assert(set != NULL);
+	if (s == NULL)
+		return -1;
+	size_t len = 0;
+	if (set[0] == 0) {
+		len += snprintf(s, max_len, "[:]");
+	} else {
+		for (size_t i = 1; i <= set[0]; i++)
+			len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "%s%zu", (len ? "," : "["), set[i]);
+		len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "]");
+	}
+	return len;
+}
+
+static size_t
+yaml_path_selection_snprint (const path_key_list_t *selection, char *s, size_t max_len)
+{
+	assert(selection != NULL);
+	if (s == NULL)
+		return -1;
+	size_t len = 0;
+	yaml_path_key_t *el;
+	if TAILQ_EMPTY(selection) {
+		len += snprintf(s, max_len, ".*");
+	} else {
+		TAILQ_FOREACH(el, selection, entries) {
+			char quote = strchr(el->key, '\'') ? '"' : '\'';
+			len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "%s%c%s%c", (len ? "," : "["), quote, el->key, quote);
+		}
+		len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "]");
+	}
+	return len;
+}
+
+static bool
+yaml_path_set_is_empty (const size_t *set)
+{
+	assert(set != NULL);
+	return set[0] == 0;
+}
+
+static bool
+yaml_path_set_has_index (const size_t *set, size_t idx)
+{
+	assert(set != NULL);
+	for (size_t i = 1; i <= set[0]; i++)
+		if (set[i] == idx)
+			return true;
+	return false;
+}
+
+static bool
+yaml_path_selection_is_empty (path_key_list_t *selection)
+{
+	assert(selection != NULL);
+	return TAILQ_EMPTY(selection) ? true : false;
+}
+
+static const char*
+yaml_path_selection_key_get (path_key_list_t *selection, const char *key)
+{
+	assert(selection != NULL);
+	yaml_path_key_t *el;
+	TAILQ_FOREACH(el, selection, entries) {
+		if (!strcmp(el->key, key))
+			return el->key;
+	}
+	return NULL;
+}
+
+static size_t
+yaml_path_selection_keys_add (path_key_list_t *selection, yaml_path_selection_key_raw_t *raw_keys, size_t count)
+{
+	assert(selection != NULL);
+	assert(raw_keys != NULL);
+	for (size_t i = 0; i < count; i++) {
+		yaml_path_key_t *el = malloc(sizeof(*el));
+		if (el == NULL)
+			return i;
+		TAILQ_INSERT_TAIL(selection, el, entries);
+		el->key = strndup(raw_keys[i].start, raw_keys[i].len);
+		if (el->key == NULL)
+			return i;
+	}
+	return count;
+}
+
+static void
+yaml_path_selection_keys_remove (path_key_list_t *selection)
+{
+	assert(selection != NULL);
+	while (!TAILQ_EMPTY(selection)) {
+		yaml_path_key_t *el = TAILQ_FIRST(selection);
+		TAILQ_REMOVE(selection, el, entries);
+		free((void *)el->key);
+		free(el);
+	}
+}
+
+static void
+yaml_path_sections_remove (yaml_path_t *path)
+{
+	assert(path != NULL);
+	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_SET:
+			free((void *)el->data.set);
+			break;
+		case YAML_PATH_SECTION_SELECTION:
+			yaml_path_selection_keys_remove(&el->data.selection);
+			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));
+	if (el != NULL) {
+		memset(el, 0, sizeof(*el));
+		path->sections_count++;
+		el->level = path->sections_count;
+		el->type = section_type;
+		el->node_type = YAML_NO_NODE;
+		TAILQ_INSERT_TAIL(&path->sections_list, el, entries);
+		if (el->type == YAML_PATH_SECTION_SELECTION) {
+			TAILQ_INIT(&el->data.selection);
+		}
+	}
+	return el;
+}
+
+static size_t
+yaml_path_section_snprint (yaml_path_section_t *section, char *s, size_t max_len)
+{
+	assert(section != NULL);
+	if (s == NULL)
+		return -1;
+	size_t len;
+	switch (section->type) {
+	case YAML_PATH_SECTION_ROOT:
+		len = snprintf(s, max_len, "$");
+		break;
+	case YAML_PATH_SECTION_KEY: {
+			char quote = '\0';
+			if (strpbrk(section->data.key, "[]().$&*"))
+				quote = strchr(section->data.key, '\'') ? '"' : '\'';
+			if (quote) {
+				len = snprintf(s, max_len, "[%c%s%c]", quote, section->data.key, quote);
+			} else {
+				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, "[%zu]", section->data.index);
+		break;
+	case YAML_PATH_SECTION_SET:
+		len = yaml_path_set_snprint(section->data.set, s, max_len);
+		break;
+	case YAML_PATH_SECTION_SELECTION:
+		len = yaml_path_selection_snprint(&section->data.selection, s, max_len);
+		break;
+	default:
+		len = snprintf(s, max_len, "<?>");
+		break;
+	}
+	return len;
+}
+
+static void
+yaml_path_error_set (yaml_path_t *path, yaml_path_error_type_t error_type, const char *message, size_t pos)
+{
+	assert(path != NULL);
+	path->error.type = error_type;
+	path->error.message = message;
+	path->error.pos = pos;
+}
+
+static void
+yaml_path_error_clear (yaml_path_t *path)
+{
+	yaml_path_error_set(path, YAML_PATH_ERROR_NONE, NULL, 0);
+}
+
+#define return_with_error(error_type, error_text, error_pos)          \
+do {                                                                  \
+	yaml_path_error_set(path, error_type, (error_text), (error_pos)); \
+	goto error;                                                       \
+} while (0)
+
+static void
+yaml_path_parse_impl (yaml_path_t *path, char *s_path) {
+	char *sp = s_path;
+	char *spe = NULL;
+
+	assert(path != NULL);
+
+	if (s_path == NULL || !s_path[0]) {
+		yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Path string is NULL or empty", 0);
+		return;
+	}
+
+	while (*sp != '\0') {
+		switch (*sp) {
+		case '.':
+		case '[':
+			if (path->sections_count == 0) {
+				yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
+				if (sec == NULL)
+					return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+			}
+			if (*sp == '.') {
+				// Key or Selection
+				spe = sp + 1;
+				if (*spe == '*') {
+					// Empty key selection section means that all keys were selected
+					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION);
+					if (sec == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+					while (*spe != '.' && *spe != '[' && *spe != '\0')
+						spe++;
+				} else {
+					while (*spe != '.' && *spe != '[' && *spe != '\0')
+						spe++;
+					if (spe == sp+1)
+						return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path);
+					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+					if (sec == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+					sec->data.key = strndup(sp + 1, spe-sp - 1);
+					if (sec->data.key == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path);
+				}
+				sp = spe-1;
+			} else if (*sp == '[') {
+				spe = sp + 1;
+				if (*spe == '*' && *(spe+1) == ']') {
+					// Empty key selection section means that all keys were selected
+					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION);
+					if (sec == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+					sp = spe+1;
+				} else if (*spe == '\'' || *spe == '"') {
+					// Key(s)
+					size_t keys_count = 0;
+					yaml_path_selection_key_raw_t raw_keys[YAML_PATH_MAX_SECTION_ITEMS] = {{NULL, 0}};
+					sp = spe;
+					while (*spe != ']' && *spe != '\0') {
+						if (keys_count >= YAML_PATH_MAX_SECTION_ITEMS)
+							return_with_error(YAML_PATH_ERROR_SECTION, "Segment keys selection has reached the limit of keys: "STR(YAML_PATH_MAX_SECTION_ITEMS), sp - s_path);
+						char quote = *spe;
+						spe++;
+						while (*spe != quote && *spe != '\0')
+							spe++;
+						if (*spe == '\0')
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (unexpected end of string, missing closing quotation mark)", sp - s_path);
+						if (spe == sp+1)
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path);
+						spe++;
+						if (*spe == '\0')
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (unexpected end of string, missing ']')", sp - s_path);
+						if (*spe == ']') {
+							raw_keys[keys_count].start = sp + 1;
+							raw_keys[keys_count].len = spe-sp - 2;
+							keys_count++;
+						} else if (*spe == ',') {
+							spe++;
+							if (*spe != '\'' && *spe != '"')
+								return_with_error(YAML_PATH_ERROR_PARSE, "Segment keys selection is invalid (invalid character)", spe - s_path);
+							raw_keys[keys_count].start = sp + 1;
+							raw_keys[keys_count].len = spe-sp - 3;
+							keys_count++;
+							sp = spe;
+						} else {
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (invalid character)", spe - s_path);
+						}
+					}
+					if (keys_count == 1) {
+						yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+						if (sec == NULL)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+						sec->data.key = strndup(raw_keys[0].start, raw_keys[0].len);
+						if (sec->data.key == NULL)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path);
+					} else if (keys_count > 1) {
+						yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION);
+						if (sec == NULL)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+						if (yaml_path_selection_keys_add(&sec->data.selection, raw_keys, keys_count) != keys_count)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (keys selection)", sp - s_path);
+					}
+					sp = spe;
+				} else {
+					// Indices
+					if (*spe == ':' && *(spe+1) == ']') {
+						// Set (all-inclusive)
+						yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SET);
+						if (sec == NULL)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+						sec->data.set = malloc(sizeof(size_t) * 1);
+						if (sec->data.set == NULL)
+							return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (set)", sp - s_path);
+						sec->data.set[0] = 0;
+						sp = spe+1;
+					} else {
+						while (*spe == ' ' || *spe == '\t')
+							spe++;
+						if (*spe == '-')
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (negative number)", spe - s_path);
+						size_t idx = strtoul(spe, &spe, 10);
+						if (*spe == ']') {
+							// Index
+							yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_INDEX);
+							if (sec == NULL)
+								return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+							sec->data.index = idx;
+							sp = spe;
+						} else if (*spe == ',') {
+							// Set
+							size_t indices[YAML_PATH_MAX_SECTION_ITEMS+1] = {0};
+							while (*spe == ',' && spe > sp+1) {
+								if (indices[0] >= YAML_PATH_MAX_SECTION_ITEMS)
+									return_with_error(YAML_PATH_ERROR_SECTION, "Segment indices set has reached the limit of indices: "STR(YAML_PATH_MAX_SECTION_ITEMS), sp - s_path);
+								sp = spe++;
+								indices[0]++;
+								indices[indices[0]] = idx;
+								while (*spe == ' ' || *spe == '\t')
+									spe++;
+								if (*spe == '-')
+									return_with_error(YAML_PATH_ERROR_PARSE, "Segment set index is invalid (negative number)", spe - s_path);
+								idx = strtoul(spe, &spe, 10);
+							}
+							if (*spe == ']' && spe > sp+1) {
+								indices[0]++;
+								indices[indices[0]] = idx;
+								yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SET);
+								if (sec == NULL)
+									return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+								sec->data.set = malloc(sizeof(*indices) * (indices[0] + 1));
+								if (sec->data.set == NULL)
+									return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (set)", sp - s_path);
+								memcpy(sec->data.set, indices, sizeof(*indices) * (indices[0] + 1));
+								sp = spe;
+							} else {
+								return_with_error(YAML_PATH_ERROR_PARSE, "Segment set is invalid (invalid character)", spe - s_path);
+							}
+						} else if (*spe == '\0') {
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (unexpected end of string, missing ']')", spe - s_path);
+						} else {
+							return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (invalid character)", spe - s_path);
+						}
+					}
+				}
+			}
+			break;
+		case '&':
+			if (path->sections_count == 0) {
+				spe = sp + 1;
+				while (*spe != '.' && *spe != '[' && *spe != '\0')
+					spe++;
+				if (spe - sp > 1) {
+					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ANCHOR);
+					if (sec == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+					sec->data.anchor = strndup(sp+1, spe-sp-1);
+					if (sec->data.anchor == NULL)
+						return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (anchor)", sp - s_path);
+				} else {
+					return_with_error(YAML_PATH_ERROR_PARSE, "Segment anchor is invalid (empty)", spe - s_path);
+				}
+			} else {
+				return_with_error(YAML_PATH_ERROR_SECTION, "Anchor segment is only allowed at the beginning of the path", sp - s_path);
+			}
+			sp = spe - 1;
+			break;
+		case '$':
+			if (path->sections_count == 0) {
+				yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
+				if (sec == NULL)
+					return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+			} else {
+				return_with_error(YAML_PATH_ERROR_SECTION, "Root segment is only allowed at the beginning of the path", sp - s_path);
+			}
+			break;
+		default:
+			if (path->sections_count == 0) {
+				spe = sp + 1;
+				// Special beginning of the path (implicit key)
+				while (*spe != '.' && *spe != '[' && *spe != '\0')
+					spe++;
+				yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
+				if (sec == NULL)
+					return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+				sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY);
+				if (sec == NULL)
+					return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path);
+				sec->data.key = strndup(sp, spe-sp);
+				if (sec->data.key == NULL)
+					return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path);
+				sp = spe-1;
+			}
+			break;
+		}
+		sp++;
+	}
+
+	if (path->sections_count == 0)
+		return_with_error(YAML_PATH_ERROR_SECTION, "Invalid, empty or meaningless path", 0);
+
+	return; // OK
+
+error:
+	yaml_path_sections_remove(path);
+	if (path->error.type == YAML_PATH_ERROR_NONE)
+		yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Unable to parse the path string", 0);
+}
+
+static yaml_path_section_t*
+yaml_path_section_get_at_level (yaml_path_t *path, size_t level)
+{
+	assert(path != 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_first (yaml_path_t *path)
+{
+	return yaml_path_section_get_at_level(path, 1);
+}
+
+static yaml_path_section_t*
+yaml_path_section_get_current (yaml_path_t *path)
+{
+	assert(path != NULL);
+	if (!path->start_level)
+		return NULL;
+	return yaml_path_section_get_at_level(path, path->current_level - path->start_level + 1);
+}
+
+static bool
+yaml_path_section_current_is_last (yaml_path_t *path)
+{
+	assert(path != NULL);
+	yaml_path_section_t *sec = yaml_path_section_get_current(path);
+	if (sec == NULL)
+		return false;
+	return sec->level == path->sections_count;
+}
+
+static bool
+yaml_path_sections_prev_are_valid (yaml_path_t *path)
+{
+	assert(path != NULL);
+	int valid = true;
+	yaml_path_section_t *el;
+	TAILQ_FOREACH(el, &path->sections_list, entries) {
+		if (el->level < path->current_level - path->start_level + 1)
+			valid = el->valid && valid;
+	}
+	return valid;
+}
+
+static bool
+yaml_path_section_is_mandatory_container (yaml_path_section_t *sec)
+{
+	assert(sec != NULL);
+	bool res = false;
+	if ((sec->type == YAML_PATH_SECTION_SELECTION && sec->node_type == YAML_MAPPING_NODE)
+	    ||
+	    (sec->type == YAML_PATH_SECTION_SET && sec->node_type == YAML_SEQUENCE_NODE))
+		res = true;
+	return res;
+}
+
+static const char *
+yaml_path_filter_event_get_anchor (const yaml_event_t *event)
+{
+	assert(event != NULL);
+	switch(event->type) {
+	case YAML_MAPPING_START_EVENT:
+		return (const char *)event->data.mapping_start.anchor;
+	case YAML_SEQUENCE_START_EVENT:
+		return (const char *)event->data.sequence_start.anchor;
+	case YAML_SCALAR_EVENT:
+		return (const char *)event->data.scalar.anchor;
+	default:
+		break;
+	}
+	return NULL;
+}
+
+static bool
+yaml_path_is_valid (yaml_path_t *path)
+{
+	assert(path != NULL);
+	bool valid = true;
+	yaml_path_section_t *el;
+	TAILQ_FOREACH(el, &path->sections_list, entries) {
+		valid = el->valid && valid;
+	}
+	return valid;
+}
+
+
+/* Public API -------------------------------------------------------------- */
+
+yaml_path_t*
+yaml_path_create (void)
+{
+	yaml_path_t *ypath = malloc(sizeof(*ypath));
+	if (ypath != NULL) {
+		memset (ypath, 0, sizeof(*ypath));
+		TAILQ_INIT(&ypath->sections_list);
+	}
+	return ypath;
+}
+
+int
+yaml_path_parse (yaml_path_t *path, char *s_path)
+{
+	if (path == NULL)
+		return -1;
+
+	yaml_path_sections_remove(path);
+	yaml_path_error_clear(path);
+
+	yaml_path_parse_impl(path, s_path);
+	if (path->error.type != YAML_PATH_ERROR_NONE)
+		return -2;
+
+	return 0;
+}
+
+void
+yaml_path_destroy (yaml_path_t *path)
+{
+	if (path == NULL)
+		return;
+	yaml_path_sections_remove(path);
+	free(path);
+}
+
+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;
+}
+
+yaml_path_filter_result_t
+yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event)
+{
+	if (path == NULL || parser == NULL || event == NULL || path->sections_count == 0)
+		return YAML_PATH_FILTER_RESULT_OUT;
+
+	int res = YAML_PATH_FILTER_RESULT_OUT;
+
+	const char *anchor = yaml_path_filter_event_get_anchor(event);
+
+	if (!path->start_level) {
+		switch (yaml_path_section_get_first(path)->type) {
+		case YAML_PATH_SECTION_ROOT:
+			if (event->type == YAML_DOCUMENT_START_EVENT) {
+				path->start_level = 1;
+				yaml_path_section_get_first(path)->valid = true;
+			}
+			break;
+		case YAML_PATH_SECTION_ANCHOR:
+			if (anchor != NULL && !strcmp(yaml_path_section_get_first(path)->data.anchor, anchor)) {
+				path->start_level = path->current_level;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	yaml_path_section_t *current_section = yaml_path_section_get_current(path);
+	if (current_section) {
+		switch (event->type) {
+		case YAML_DOCUMENT_START_EVENT:
+		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_NO_NODE:
+				if (current_section->type == YAML_PATH_SECTION_ANCHOR) {
+					current_section->valid = false;
+					if (anchor != NULL && !strcmp(current_section->data.anchor, anchor))
+						current_section->valid = true;
+				}
+			break;
+			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 = false;
+					} else {
+						current_section->next_valid = !strcmp(current_section->data.key, (const char *)event->data.scalar.value);
+						current_section->valid = false;
+					}
+				} else if (current_section->type == YAML_PATH_SECTION_SELECTION) {
+					if (current_section->counter % 2) {
+						current_section->valid = current_section->next_valid;
+						current_section->next_valid = false;
+					} else {
+						current_section->next_valid = yaml_path_selection_is_empty(&current_section->data.selection)
+						                              || yaml_path_selection_key_get(&current_section->data.selection, (const char *)event->data.scalar.value) != NULL;
+						current_section->valid = current_section->next_valid;
+					}
+				} else {
+					current_section->valid = false;
+				}
+				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_SET) {
+					current_section->valid = yaml_path_set_is_empty(current_section->data.set)
+					                         || yaml_path_set_has_index(current_section->data.set, current_section->counter);
+				} else {
+					current_section->valid = false;
+				}
+				break;
+			default:
+				break;
+			}
+			current_section->counter++;
+		default:
+			break;
+		}
+	}
+
+	switch (event->type) {
+	case YAML_STREAM_START_EVENT:
+	case YAML_STREAM_END_EVENT:
+	case YAML_NO_EVENT:
+		res = YAML_PATH_FILTER_RESULT_IN;
+		break;
+	case YAML_DOCUMENT_START_EVENT:
+		if (path->start_level == 1)
+			path->current_level++;
+		res = YAML_PATH_FILTER_RESULT_IN;
+		break;
+	case YAML_DOCUMENT_END_EVENT:
+		if (path->start_level == 1)
+			path->current_level--;
+		res = YAML_PATH_FILTER_RESULT_IN;
+		break;
+	case YAML_MAPPING_START_EVENT:
+	case YAML_SEQUENCE_START_EVENT:
+		if (current_section) {
+			if (yaml_path_section_current_is_last(path))
+				if (yaml_path_is_valid(path))
+					res = YAML_PATH_FILTER_RESULT_IN;
+		} else {
+			if (path->current_level > path->start_level) {
+				if (yaml_path_is_valid(path))
+					res = YAML_PATH_FILTER_RESULT_IN;
+			}
+		}
+		path->current_level++;
+		current_section = yaml_path_section_get_current(path);
+		if (current_section) {
+			current_section->node_type = event->type == YAML_MAPPING_START_EVENT ? YAML_MAPPING_NODE : YAML_SEQUENCE_NODE;
+			current_section->counter = 0;
+		}
+		if (current_section) {
+			if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path))
+				res = YAML_PATH_FILTER_RESULT_IN;
+		}
+		break;
+	case YAML_MAPPING_END_EVENT:
+	case YAML_SEQUENCE_END_EVENT:
+		if (current_section) {
+			if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path))
+				res = YAML_PATH_FILTER_RESULT_IN;
+
+		}
+		path->current_level--;
+		current_section = yaml_path_section_get_current(path);
+		if (current_section) {
+			if (yaml_path_section_current_is_last(path))
+				if (yaml_path_is_valid(path))
+					res = YAML_PATH_FILTER_RESULT_IN;
+		} else {
+			if (path->current_level > path->start_level) {
+				if (yaml_path_is_valid(path))
+					res = YAML_PATH_FILTER_RESULT_IN;
+			}
+		}
+		break;
+	case YAML_ALIAS_EVENT:
+	case YAML_SCALAR_EVENT:
+		if (!current_section) {
+			if (path->current_level >= path->start_level)
+				if (yaml_path_is_valid(path))
+					res = YAML_PATH_FILTER_RESULT_IN;
+		} else {
+			if (yaml_path_section_current_is_last(path) && yaml_path_is_valid(path))
+				res = YAML_PATH_FILTER_RESULT_IN;
+			if (current_section->valid
+				&& current_section->node_type == YAML_MAPPING_NODE
+				&& current_section->counter % 2) {
+					if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path))
+						res = YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY;
+				}
+		}
+		break;
+	default:
+		break;
+	}
+
+	return res;
+}
diff --git a/yaml-path.h b/src/yaml-path.h
similarity index 72%
rename from yaml-path.h
rename to src/yaml-path.h
index eb9e9dc..66e0298 100644
--- a/yaml-path.h
+++ b/src/yaml-path.h
@@ -8,20 +8,22 @@ typedef struct yaml_path yaml_path_t;
 
 typedef enum yaml_path_error_type {
 	YAML_PATH_ERROR_NONE,
+	YAML_PATH_ERROR_NOMEM,
 	YAML_PATH_ERROR_PARSE,
+	YAML_PATH_ERROR_SECTION,
 } 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;
+typedef enum yaml_path_filter_result {
+	YAML_PATH_FILTER_RESULT_OUT,
+	YAML_PATH_FILTER_RESULT_IN,
+	YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY,
+} yaml_path_filter_result_t;
 
 
 yaml_path_t*
@@ -36,8 +38,8 @@ 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);
+yaml_path_filter_result_t
+yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event);
 
 size_t
 yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len);
diff --git a/yamlp.c b/src/yamlp.c
similarity index 80%
rename from yamlp.c
rename to src/yamlp.c
index 334c626..f4d44d2 100644
--- a/yamlp.c
+++ b/src/yamlp.c
@@ -9,13 +9,14 @@
 #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)
+static int
+parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t *path, int use_flow_style)
 {
 	yaml_event_t event;
-	yaml_event_type_t prev_event_type, event_type;
+	yaml_event_type_t event_type, prev_event_type = YAML_NO_EVENT;
+	yaml_path_filter_result_t result, prev_result = YAML_PATH_FILTER_RESULT_OUT;
 
 	do {
-
 		if (!yaml_parser_parse(parser, &event)) {
 			switch (parser->error) {
 			case YAML_MEMORY_ERROR:
@@ -53,7 +54,8 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t
 			return 1;
 		} else {
 			event_type = event.type;
-			if (!yaml_path_filter_event(path, parser, &event, mode)) {
+			result = yaml_path_filter_event(path, parser, &event);
+			if (result == YAML_PATH_FILTER_RESULT_OUT) {
 				yaml_event_delete(&event);
 			} else {
 				if (use_flow_style) {
@@ -68,11 +70,16 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t
 						break;
 					}
 				}
-				if (prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) {
+				if ((prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT)
+					|| (prev_result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY
+						&& (event_type == YAML_MAPPING_END_EVENT
+							|| event_type == YAML_SEQUENCE_END_EVENT
+							|| result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY))) {
 					yaml_event_t null_event= {0};
 					yaml_scalar_event_initialize(&null_event, NULL, (yaml_char_t *)"!!null", (yaml_char_t *)"null", 4, 1, 0, YAML_ANY_SCALAR_STYLE);
 					yaml_emitter_emit(emitter, &null_event);
 				}
+				prev_result = result;
 				prev_event_type = event_type;
 				if (!yaml_emitter_emit(emitter, &event)) {
 					switch (emitter->error)
@@ -100,11 +107,12 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t
 }
 
 
-void help (void)
+static 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("Usage: yamlp [-F] [-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");
@@ -118,8 +126,6 @@ void help (void)
 	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");
 }
@@ -129,8 +135,7 @@ 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;
+	long wrap = -1;
 
 	int opt;
 	while ((opt = getopt(argc, argv, ":f:W:vhSF")) != -1) {
@@ -138,13 +143,9 @@ int main(int argc, char *argv[])
 		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) {
@@ -158,11 +159,12 @@ int main(int argc, char *argv[])
 		case ':':
 			fprintf(stderr, "Option needs a value\n");
 			return 1;
-			break;
 		case '?':
 			fprintf(stderr, "Unknown option '%c'\n", optopt);
 			return 1;
-			break;
+        default:
+            fprintf(stderr, "Unhandled option '%c'\n", opt);
+            return 1;
 		}
 	}
 
@@ -186,9 +188,10 @@ int main(int argc, char *argv[])
 
 	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);
+		fprintf(stderr, "Invalid path: '%s'\n", path_string);
+		fprintf(stderr, "               %*s^ %s [at position %zu]\n", (int)yaml_path_error_get(path)->pos, " ", yaml_path_error_get(path)->message, yaml_path_error_get(path)->pos);
 		return 3;
-	};
+	}
 
 	yaml_parser_t parser;
 	yaml_emitter_t emitter;
@@ -198,9 +201,9 @@ int main(int argc, char *argv[])
 
 	yaml_emitter_initialize(&emitter);
 	yaml_emitter_set_output_file(&emitter, stdout);
-	yaml_emitter_set_width(&emitter, wrap);
+	yaml_emitter_set_width(&emitter, (int) wrap);
 
-	if (parse_and_emit(&parser, &emitter, path, mode, flow)) {
+	if (parse_and_emit(&parser, &emitter, path, flow)) {
 		return 4;
 	}
 
@@ -208,7 +211,8 @@ int main(int argc, char *argv[])
 	yaml_emitter_delete(&emitter);
 
 	yaml_path_destroy(path);
-	fclose(file);
+	if (file != NULL)
+		fclose(file);
 
 	return 0;
 }
diff --git a/test-yamlp.sh b/test-yamlp.sh
deleted file mode 100755
index 8c2b413..0000000
--- a/test-yamlp.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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 --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..bc01b4b
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,28 @@
+function(_add_test TEST_NAME TEST_COMMAND)
+	add_test(NAME ${TEST_NAME} COMMAND ${TEST_COMMAND})
+	set_tests_properties(${TEST_NAME} PROPERTIES
+		SKIP_RETURN_CODE 255
+		ENVIRONMENT "CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR};SOURCE_DIR=${CMAKE_SOURCE_DIR};BINARY_DIR=${CMAKE_BINARY_DIR}"
+	)
+endfunction()
+
+function(add_test_executable EXECUTABLE_NAME SOURCE_FILE)
+	set(TEST_EXECUTABLE_LIBRARIES yaml-path)
+	add_executable(${EXECUTABLE_NAME} ${SOURCE_FILE} ${ARGN})
+	target_link_libraries(${EXECUTABLE_NAME} ${TEST_EXECUTABLE_LIBRARIES})
+	target_include_directories(${EXECUTABLE_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
+	add_coverage(${EXECUTABLE_NAME})
+	string(REPLACE "${CMAKE_SOURCE_DIR}/tests/" "" TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${EXECUTABLE_NAME}")
+	_add_test(${TEST_NAME} "${CMAKE_BINARY_DIR}/tests/${EXECUTABLE_NAME}")
+endfunction()
+
+function(add_test_script TEST_SCRIPT)
+	string(REPLACE "${CMAKE_SOURCE_DIR}/tests/" "" TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SCRIPT}")
+	_add_test(${TEST_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SCRIPT}")
+endfunction()
+
+add_test_executable(test-path-segments test-path-segments.c)
+add_test_executable(test-paths test-paths.c)
+add_test_script(test-yamlp.sh)
+
+list(APPEND LCOV_REMOVE_PATTERNS "'${CMAKE_SOURCE_DIR}/tests/*'")
diff --git a/test-path-segments.c b/tests/test-path-segments.c
similarity index 54%
rename from test-path-segments.c
rename to tests/test-path-segments.c
index 2340937..0889004 100644
--- a/test-path-segments.c
+++ b/tests/test-path-segments.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2020 Red Hat Inc., Durham, North Carolina.
+
 #include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
 #include "yaml-path.h"
 
@@ -17,7 +18,7 @@ yp_s[PATH_STRING_LEN] = {0};
 #define ASCII_ERR "\033[0;33m"
 #define ASCII_RST "\033[0;0m"
 
-void
+static void
 yp_test (char *p, int expected_failure)
 {
 	yaml_path_t *yp = yaml_path_create();
@@ -35,7 +36,7 @@ yp_test (char *p, int 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");
+		printf(" -- %s (at pos: %zu): %s\n", ype->message, ype->pos, !expected_failure ? ASCII_RST"FAILED" : "OK");
 	}
 	yaml_path_destroy(yp);
 }
@@ -46,7 +47,9 @@ yp_test (char *p, int expected_failure)
 
 int main (int argc, char *argv[])
 {
-	yp_test_good(".first");
+    (void) argc; (void) argv; // Yep, we don't need them
+
+    yp_test_good(".first");
 	yp_test_good(".first[0]");
 	yp_test_good(".first.second[0].third");
 	yp_test_good(".first.0");
@@ -58,31 +61,62 @@ int main (int argc, char *argv[])
 	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("[':']['*'][:]");
+	yp_test_good(".:.*[:]");
+	yp_test_good("[0,2,3,4,5,20,180]");
 
 	yp_test_good("&anc");
 	yp_test_good("&anc[0]");
 	yp_test_good("&anc[0].zzz");
 
 	yp_test_good("el['key']");
-	yp_test_good("el['key'].other[0]['key']");
+	yp_test_good("el[\"key\"]");
+	yp_test_good("el[\"k[]ey\"]");
+	yp_test_good("el[\"k'ey\"]");
+	yp_test_good("el['k\"ey']");
+	yp_test_good("el.k\"ey");
+	yp_test_good("el.k$ey");
+	yp_test_good("el.k'&'ey");
+	yp_test_good("el['key'].other[0]['key'][0,2]");
+
+	yp_test_good("el['first','other']");
+	yp_test_good("el[\"first\",\"other\"]");
+	yp_test_good("el[\"first\",'other']");
+	yp_test_good("el['key','valid']['now','allowed']");
+	yp_test_good("el.*");
+	yp_test_good("el[*]");
+	yp_test_good("el['*']");
+
+	yp_test_invalid("$$");
+	yp_test_invalid("$&");
+
+	yp_test_invalid("&");
 
 	yp_test_invalid("$.");
 	yp_test_invalid("");
 	yp_test_invalid(".");
 	yp_test_invalid("element[");
 
+	yp_test_invalid("[-5]");
+	yp_test_invalid("[1,-5]");
+
+	yp_test_invalid("[0:0]");
+	yp_test_invalid("[0:0:1]");
+	yp_test_invalid("[100:]");
+	yp_test_invalid("[100::]");
+	yp_test_invalid("[:100]");
+	yp_test_invalid("[:100:]");
+	yp_test_invalid("[::]");
+	yp_test_invalid("[-03:-200:+500]");
+
 	yp_test_invalid("[0:0:0]");
 	yp_test_invalid("[::-1]");
 	yp_test_invalid("[0.key[0]");
+	yp_test_invalid("[1,]");
+	yp_test_invalid("[,]");
+	yp_test_invalid("[1,:]");
+	yp_test_invalid("[1,2:]");
 
 	yp_test_invalid("el[&]");
 	yp_test_invalid("el[&");
@@ -93,9 +127,18 @@ int main (int argc, char *argv[])
 	yp_test_invalid("el[&anchor][100]");
 
 	yp_test_invalid("el[']");
+	yp_test_invalid("[*'");
 	yp_test_invalid("el['key].wrong");
 	yp_test_invalid("el['key.wrong");
 	yp_test_invalid("el['key'");
+	yp_test_invalid("el['key\"]");
+	yp_test_invalid("el[\"key']");
+	yp_test_invalid("el['k'ey']");
+
+	yp_test_invalid("el['key';'wrong']");
+	yp_test_invalid("el['key',]");
+	yp_test_invalid("el['key',invalid]");
+	yp_test_invalid("el['first',]");
 
 	return test_result;
 }
diff --git a/test-paths.c b/tests/test-paths.c
similarity index 65%
rename from test-paths.c
rename to tests/test-paths.c
index 58cfdad..fa722e8 100644
--- a/test-paths.c
+++ b/tests/test-paths.c
@@ -1,6 +1,8 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2020 Red Hat Inc., Durham, North Carolina.
+
 #include <ctype.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "yaml-path.h"
@@ -59,14 +61,11 @@ 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
+static int
 yp_run (char *path)
 {
 	yaml_parser_t parser;
@@ -74,11 +73,15 @@ yp_run (char *path)
 	int res = 0;
 
 	yaml_path_t *yp = yaml_path_create();
-	yaml_path_parse(yp, path);
+	if (yaml_path_parse(yp, path)) {
+		printf("Path error: %s\n", yaml_path_error_get(yp)->message);
+		yaml_path_destroy(yp);
+		return 1;
+	}
 
-	char spath[YAML_STRING_LEN] = {0};
-	yaml_path_snprint(yp, spath, YAML_STRING_LEN);
-	printf("(%s) ", spath);
+	//char spath[YAML_STRING_LEN] = {0};
+	//yaml_path_snprint(yp, spath, YAML_STRING_LEN);
+	//printf("(%s) ", spath);
 
 	yaml_emitter_initialize(&emitter);
 	yaml_parser_initialize(&parser);
@@ -89,7 +92,8 @@ yp_run (char *path)
 	yaml_emitter_set_width(&emitter, -1);
 
 	yaml_event_t event;
-	yaml_event_type_t prev_event_type, event_type;
+	yaml_event_type_t event_type, prev_event_type = YAML_NO_EVENT;
+	yaml_path_filter_result_t result, prev_result = 0;
 
 	do {
 		if (!yaml_parser_parse(&parser, &event)) {
@@ -126,30 +130,35 @@ yp_run (char *path)
 			goto error;
 		} else {
 			event_type = event.type;
-			if (!yaml_path_filter_event(yp, &parser, &event, mode)) {
+			result = yaml_path_filter_event(yp, &parser, &event);
+			if (result == YAML_PATH_FILTER_RESULT_OUT) {
 				yaml_event_delete(&event);
 			} else {
-				if (prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) {
+				if ((prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT)
+					|| (prev_result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY
+						&& (event_type == YAML_MAPPING_END_EVENT || event_type == YAML_SEQUENCE_END_EVENT || result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY))) {
 					yaml_event_t null_event= {0};
 					yaml_scalar_event_initialize(&null_event, NULL, (yaml_char_t *)"!!null", (yaml_char_t *)"null", 4, 1, 0, YAML_ANY_SCALAR_STYLE);
 					yaml_emitter_emit(&emitter, &null_event);
 				}
+				prev_result = result;
 				prev_event_type = event_type;
 				if (!yaml_emitter_emit(&emitter, &event)) {
-					printf("Error after '%s'\n", yp_event_name(event.type));
+					yaml_emitter_flush(&emitter);
+					printf("%s --> Error after '%s': ", yaml_out, yp_event_name(event.type));
 					switch (emitter.error)
 					{
 					case YAML_MEMORY_ERROR:
-						printf("Memory error: Not enough memory for emitting\n");
+						printf("Memory error (Not enough memory for emitting)");
 						break;
 					case YAML_WRITER_ERROR:
-						printf("Writer error: %s\n", emitter.problem);
+						printf("Writer error (%s)", emitter.problem);
 						break;
 					case YAML_EMITTER_ERROR:
-						printf("Emitter error: %s\n", emitter.problem);
+						printf("Emitter error (%s)", emitter.problem);
 						break;
 					default:
-						printf("Internal error\n");
+						printf("Internal error");
 						break;
 					}
 					res = 2;
@@ -171,17 +180,19 @@ yp_run (char *path)
 #define ASCII_ERR "\033[0;33m"
 #define ASCII_RST "\033[0;0m"
 
-void
+static void
 yp_test (char *path, char *yaml_exp)
 {
-	printf("%s ", path);
+	printf("%s "ASCII_ERR, path);
 	if (!yp_run(path)) {
 		rstrip(yaml_out);
 		if (!strcmp(yaml_exp, yaml_out)) {
-			printf("(%s): OK\n", yaml_exp);
+			printf(ASCII_RST"(%s): OK\n", yaml_exp);
 			return;
 		}
-		printf(ASCII_ERR"(%s != %s)"ASCII_RST": FAILED\n", yaml_exp, yaml_out);
+		printf("(%s != %s)"ASCII_RST": FAILED\n", yaml_exp, yaml_out);
+	} else {
+		printf(ASCII_RST": ERROR\n");
 	}
 	test_result++;
 }
@@ -189,6 +200,8 @@ yp_test (char *path, char *yaml_exp)
 
 int main (int argc, char *argv[])
 {
+    (void) argc; (void) argv; // Yep, we don't need them
+
 	yaml =
 		"{"
 			"first: {"
@@ -204,14 +217,18 @@ int main (int argc, char *argv[])
 				"]"
 			"},"
 			"second: ["
-				"{'abc': &anc [1, 2], 'abcdef': 2, 'z': *anc},"
-				"{'abc': [3, 4], 'abcdef': 4, 'z': 'zzz'}"
+				"{'abc': &anc [1, 2], 'def': [11, 22], 'abcdef': 2, 'z': *anc, 'q': 'Q'},"
+				"{'abc': [3, 4], 'def': {'z': '!'}, 'abcdef': 4, 'z': 'zzz'}"
+			"],"
+			"3rd: ["
+				"{'a': {'A': [0, 1], 'AA': [2, 3]}, 'b': {'A': [10, 11], 'BB': [9, 8]}},"
+				"{'z': {'A': [0, 1], 'BB': [22, 33]}},"
+				"&x {'q': [1, 2]},"
 			"]"
 		"}";
 
 	//       Path                         Expected filtered YAML result
 
-	mode = YAML_PATH_FILTER_RETURN_ALL;
 	yp_test("$.first.Map",               "{1: '1'}");
 	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.Nop",                "0");
@@ -219,24 +236,27 @@ int main (int argc, char *argv[])
 	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[:][:]",          "[[11, 12], ['31', '32'], [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(".first.Arr[:][0,1]",        "[[11, 12], ['31', '32'], [4, 5]]");
+	yp_test(".first.Arr[:][1]",          "[12, '32', 5]");
 	yp_test(".second[2].abc",            "null");
-	yp_test(".second[0:2].abc",          "[&anc [1, 2], [3, 4]]");
 	yp_test(".second[0].z",              "*anc");
+	yp_test("&anc",                      "&anc [1, 2]");
 	yp_test("&anc[0]",                   "1");
-
-	mode = YAML_PATH_FILTER_RETURN_SHALLOW;
-	yp_test(".first",                    "{}");
-	yp_test(".first.Nop",                "0");
-	yp_test(".first.Map",                "{}");
+	yp_test(".first['Nop','Yep']",       "{'Nop': 0, 'Yep': '1'}");
+	yp_test(".second[0]['abc','def'][0]","{'abc': 1, 'def': 11}");
+	yp_test(".second[:]['abc','def'][0]","[{'abc': 1, 'def': 11}, {'abc': 3, 'def': null}]");
+	yp_test(".second[:]['abc','def'].z", "[{'abc': null, 'def': null}, {'abc': null, 'def': '!'}]");
+	yp_test(".second[:][*].z",            "[{'abc': null, 'def': null, 'abcdef': null, 'z': null, 'q': null}, {'abc': null, 'def': '!', 'abcdef': null, 'z': null}]");
+	yp_test(".second[:]['abc','q']",     "[{'abc': &anc [1, 2], 'q': 'Q'}, {'abc': [3, 4]}]");
+	yp_test(".second[:]['abc','def'][:]","[{'abc': &anc [1, 2], 'def': [11, 22]}, {'abc': [3, 4], 'def': null}]");
+	yp_test(".second[0]['abc','def']",   "{'abc': &anc [1, 2], 'def': [11, 22]}");
+	yp_test(".3rd[:].*.*[:]",            "[{'a': {'A': [0, 1], 'AA': [2, 3]}, 'b': {'A': [10, 11], 'BB': [9, 8]}}, {'z': {'A': [0, 1], 'BB': [22, 33]}}, &x {'q': null}]");
 
 	return test_result;
 }
diff --git a/tests/test-yamlp.sh b/tests/test-yamlp.sh
new file mode 100755
index 0000000..d009659
--- /dev/null
+++ b/tests/test-yamlp.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: MIT
+# Copyright (c) 2020 Red Hat Inc., Durham, North Carolina.
+
+yamlp_test()
+{
+	echo "$1:"
+	echo -n "	($2) "
+	out=$("${BINARY_DIR:-../build}/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 "${SOURCE_DIR:-..}/res/openshift-logging.yaml" ".spec.pipelines[:].inputSource" "[logs.app, logs.infra, logs.audit]"
+res=$((res+$?))
+
+yamlp_test "${SOURCE_DIR:-..}/res/openshift-upgradeable.yaml" ".status.conditions[:]['status','type']" \
+           '[{status: "False", type: Degraded}, {status: "False", type: Progressing}, {status: "True", type: Available}, {status: "True", type: Upgradeable}]'
+res=$((res+$?))
+
+exit $res
diff --git a/yaml-path.c b/yaml-path.c
deleted file mode 100644
index 00611ca..0000000
--- a/yaml-path.c
+++ /dev/null
@@ -1,588 +0,0 @@
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <assert.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_ROOT,
-	YAML_PATH_SECTION_ANCHOR,
-	YAML_PATH_SECTION_KEY,
-	YAML_PATH_SECTION_INDEX,
-	YAML_PATH_SECTION_SLICE,
-} 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;
-	bool valid;
-	bool 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 sequence_level;
-
-	size_t current_level;
-	size_t start_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)
-{
-	assert(path != NULL);
-	path->error.type = error_type;
-	path->error.message = message;
-	path->error.pos = pos;
-}
-
-static void
-yaml_path_sections_remove (yaml_path_t *path)
-{
-	assert(path != NULL);
-	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));
-	assert(el != NULL);
-	memset(el, 0, sizeof(*el));
-	path->sections_count++;
-	el->level = path->sections_count;
-	el->type = section_type;
-	el->node_type = YAML_SCALAR_NODE;
-	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)
-{
-	assert(section != NULL);
-	if (s == NULL)
-		return -1;
-	size_t len = 0;
-	switch (section->type) {
-	case YAML_PATH_SECTION_ROOT:
-		len = snprintf(s, max_len, "$");
-		break;
-	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;
-
-	assert(path != 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 (path->sections_count == 0) {
-				yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
-			}
-			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 == '\'') {
-					// 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) {
-				spe = sp + 1;
-				if (*sp == '$' && (*spe == '.' || *spe == '[' || *spe == '\0')) {
-					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
-				} else if (*sp == '&') {
-					// Anchor
-					sp++;
-					while (*spe != '.' && *spe != '[' && *spe != '\0')
-						spe++;
-					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ANCHOR);
-					sec->data.anchor = strndup(sp, spe-sp);
-				} else {
-					// Key
-					while (*spe != '.' && *spe != '[' && *spe != '\0')
-						spe++;
-					yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT);
-					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)
-{
-	assert(path != 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_first (yaml_path_t *path)
-{
-	assert(path != NULL);
-	return yaml_path_section_get_at_level(path, 1);
-}
-
-static yaml_path_section_t*
-yaml_path_section_get_current (yaml_path_t *path)
-{
-	assert(path != NULL);
-	if (!path->start_level)
-		return NULL;
-	return yaml_path_section_get_at_level(path, path->current_level - path->start_level + 1);
-}
-
-static bool
-yaml_path_sections_prev_are_valid (yaml_path_t *path)
-{
-	assert(path != NULL);
-	int valid = true;
-	yaml_path_section_t *el;
-	TAILQ_FOREACH(el, &path->sections_list, entries) {
-		if (el->level < path->current_level - path->start_level + 1)
-			valid = el->valid && valid;
-	}
-	return valid;
-}
-
-static bool
-yaml_path_section_current_is_last (yaml_path_t *path)
-{
-	assert(path != NULL);
-	yaml_path_section_t *sec = yaml_path_section_get_current(path);
-	if (sec == NULL)
-		return false;
-	return sec->level == path->sections_count;
-}
-
-static bool
-yaml_path_section_current_is_mandatory_sequence (yaml_path_t *path)
-{
-	assert(path != NULL);
-	yaml_path_section_t *sec = yaml_path_section_get_current(path);
-	if (sec == NULL)
-		return false;
-	return (sec->type == YAML_PATH_SECTION_SLICE && sec->level == path->sequence_level);
-}
-
-static bool
-yaml_path_is_valid (yaml_path_t *path)
-{
-	assert(path != NULL);
-	bool valid = true;
-	yaml_path_section_t *el;
-	TAILQ_FOREACH(el, &path->sections_list, entries) {
-		valid = el->valid && valid;
-	}
-	return valid;
-}
-
-
-/* Public */
-
-yaml_path_t*
-yaml_path_create (void)
-{
-	yaml_path_t *ypath = malloc(sizeof(*ypath));
-
-	assert(ypath != NULL);
-	memset (ypath, 0, sizeof(*ypath));
-	TAILQ_INIT(&ypath->sections_list);
-
-	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;
-	const char *anchor = NULL;
-
-	if (path == NULL || parser == NULL || event == NULL)
-		goto exit;
-
-	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;
-	}
-
-	if (!path->start_level) {
-		switch (yaml_path_section_get_first(path)->type) {
-		case YAML_PATH_SECTION_ROOT:
-			if (event->type == YAML_DOCUMENT_START_EVENT) {
-				path->start_level = 1;
-				yaml_path_section_get_first(path)->valid = true;
-			}
-			break;
-		case YAML_PATH_SECTION_ANCHOR:
-			if (anchor != NULL) {
-				if (!strcmp(yaml_path_section_get_first(path)->data.anchor, anchor)) {
-					path->start_level = path->current_level;
-					if (yaml_path_section_get_current(path))
-						yaml_path_section_get_current(path)->node_type = YAML_SCALAR_NODE;
-				}
-			}
-			break;
-		default:
-			//TODO: This path is invalid
-			break;
-		}
-	} else {
-		//TODO: ?
-	}
-
-	yaml_path_section_t *current_section = yaml_path_section_get_current(path);
-	if (!current_section) {
-	} else {
-		switch (event->type) {
-		case YAML_DOCUMENT_START_EVENT:
-		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_SCALAR_NODE:
-				current_section->valid = true;
-				break;
-			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 = false;
-					} else {
-						current_section->next_valid = !strcmp(current_section->data.key, (const char *)event->data.scalar.value);
-						current_section->valid = false;
-					}
-				} else {
-					current_section->valid = false;
-				}
-				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 {
-					current_section->valid = false;
-				}
-				break;
-			default:
-				break;
-			}
-			current_section->counter++;
-		default:
-			break;
-		}
-	}
-
-	switch (event->type) {
-	case YAML_STREAM_START_EVENT:
-	case YAML_STREAM_END_EVENT:
-	case YAML_NO_EVENT:
-		res = 1;
-		break;
-	case YAML_DOCUMENT_START_EVENT:
-		if (path->start_level == 1)
-			path->current_level++;
-		res = 1;
-		break;
-	case YAML_DOCUMENT_END_EVENT:
-		if (path->start_level == 1)
-			path->current_level--;
-		res = 1;
-		break;
-	case YAML_MAPPING_START_EVENT:
-	case YAML_SEQUENCE_START_EVENT:
-		if (current_section) {
-			if (yaml_path_section_current_is_last(path))
-				res = yaml_path_is_valid(path);
-		} else {
-			if (path->current_level > path->start_level) {
-				if (mode == YAML_PATH_FILTER_RETURN_ALL)
-					res = yaml_path_is_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_sections_prev_are_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) {
-			if (yaml_path_section_current_is_mandatory_sequence(path))
-				res = yaml_path_sections_prev_are_valid(path);
-		}
-		path->current_level--;
-		current_section = yaml_path_section_get_current(path);
-		if (current_section) {
-			if (yaml_path_section_current_is_last(path))
-				res = yaml_path_is_valid(path);
-		} else {
-			if (path->current_level > path->start_level) {
-				if (mode == YAML_PATH_FILTER_RETURN_ALL)
-					res = yaml_path_is_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->start_level) || path->current_level == path->start_level)
-				res = yaml_path_is_valid(path);
-		} else {
-			res = yaml_path_is_valid(path) && yaml_path_section_current_is_last(path);
-		}
-		break;
-	default:
-		break;
-	}
-
-exit:
-	return res;
-}
diff --git a/yaml.c b/yaml.c
deleted file mode 100644
index e2d9007..0000000
--- a/yaml.c
+++ /dev/null
@@ -1,172 +0,0 @@
-#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 * (anc=\"%s\")\n", STRVAL(event->data.scalar.anchor));
-		break;
-	case YAML_SCALAR_EVENT:
-		indent(level);
-		printf("= scalar-event (anc=\"%s\" val=\"%s\", l=%d, t=%s, pl_impl=%d, q_impl=%d, st=%d)\n",
-		       STRVAL(event->data.scalar.anchor),
-		       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 (anc=\"%s\", t=%s)\n",
-			   STRVAL(event->data.sequence_start.anchor),
-		       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.Map");
-	//yaml_path_parse(yp, ".first.Arr[:].k");
-	//yaml_path_parse(yp, ".first.Arr[:][2]");
-	//yaml_path_parse(yp, ".metadata.name");
-	//yaml_path_parse(yp, ".spec.outputs[0:2].name");
-	//yaml_path_parse(yp, ".second[0].abc");
-	//yaml_path_parse(yp, "&anc[0]");
-	yaml_path_parse(yp, ".first.Map");
-
-	//const char *yaml = "2";
-	//const char *yaml = "{'el': {'Z': &anc [{'key': 0}, {'item': 1}]}, first: {'Map': &anc {1: '1'}, 'Nop': 'b', 'Yep': '2', 'Arr': [[11,12],2,[31,32],[4, 5, 6],{'k': 1, 0: 0}]}}";
-	const char *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'}"
-			"]"
-		"}";
-
-	char ypath[255] = {0};
-	yaml_path_snprint (yp, ypath, 255);
-	printf("%s\n", yaml);
-	printf("%s\n\n", ypath);
-	
-	yaml_parser_initialize(&parser);
-	//yaml_parser_set_input_file(&parser, fopen("../openshift-logging-1.yaml", "r"));
-	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;
-}