#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);
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);
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 prev_event_type, 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 (prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) {
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_event_type = event_type;
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'}");
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");
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[2].abc", "null");
yp_test(".second[0:2].abc", "[&anc [1, 2], [3, 4]]");
yp_test(".second[0].z", "*anc");
yp_test("&anc[0]", "1");
mode = YAML_PATH_FILTER_RETURN_SHALLOW;
yp_test(".first", "{}");
yp_test(".first.Nop", "0");
yp_test(".first.Map", "{}");
return test_result;
}