From 1974e1f1aef30e536500823bc8778db564fe1c2e Mon Sep 17 00:00:00 2001 From: Timothy Stack Date: Mon, 5 May 2014 06:44:58 -0700 Subject: [PATCH] [jsonptr] initial impl --- TESTS_ENVIRONMENT.in | 3 + configure | 4 +- configure.ac | 4 +- src/Makefile.am | 5 + src/Makefile.in | 20 +- src/default-log-formats.json | 5 +- src/fs-extension-functions.cc | 2 + src/json-extension-functions.cc | 228 +++++++++++++++++ src/json_op.cc | 295 ++++++++++++++++++++++ src/json_op.hh | 81 ++++++ src/json_ptr.cc | 169 +++++++++++++ src/json_ptr.hh | 380 +++++++++++++++++++++++++++++ src/lnav.cc | 248 ++++++++++--------- src/lnav_log.cc | 14 ++ src/lnav_log.hh | 1 + src/log_data_helper.hh | 40 +++ src/log_format.cc | 8 +- src/log_format.hh | 3 + src/log_format_loader.cc | 2 +- src/log_vtab_impl.cc | 1 + src/network-extension-functions.cc | 2 + src/sql_util.cc | 44 +++- src/sql_util.hh | 2 + src/sqlite-extension-func.c | 1 + src/sqlite-extension-func.h | 3 + src/state-extension-functions.cc | 2 + src/view_curses.hh | 7 +- src/yajlpp.hh | 17 -- test/Makefile.am | 16 ++ test/Makefile.in | 85 ++++++- test/drive_json_op.cc | 198 +++++++++++++++ test/drive_json_ptr_walk.cc | 79 ++++++ test/drive_sql.cc | 2 + test/test_json_op.sh | 94 +++++++ test/test_json_ptr.cc | 73 ++++++ test/test_json_ptr_walk.sh | 67 +++++ test/test_sql_json_func.sh | 55 +++++ 37 files changed, 2105 insertions(+), 155 deletions(-) create mode 100644 src/json-extension-functions.cc create mode 100644 src/json_op.cc create mode 100644 src/json_op.hh create mode 100644 src/json_ptr.cc create mode 100644 src/json_ptr.hh create mode 100644 test/drive_json_op.cc create mode 100644 test/drive_json_ptr_walk.cc create mode 100644 test/test_json_op.sh create mode 100644 test/test_json_ptr.cc create mode 100644 test/test_json_ptr_walk.sh create mode 100644 test/test_sql_json_func.sh diff --git a/TESTS_ENVIRONMENT.in b/TESTS_ENVIRONMENT.in index 7e61a8df..2ea02df0 100644 --- a/TESTS_ENVIRONMENT.in +++ b/TESTS_ENVIRONMENT.in @@ -29,6 +29,9 @@ test_num=0 lnav_test="${top_builddir}/src/lnav-test" export lnav_test +LNAV_LOG_PATH="${top_builddir}/test/test.log" +export LNAV_LOG_PATH + ## BEGIN Functions LAST_TEST="" diff --git a/configure b/configure index 99db4b70..8c321587 100755 --- a/configure +++ b/configure @@ -3986,8 +3986,8 @@ fi CPPFLAGS="$CPPFLAGS -D_ISOC99_SOURCE -D__STDC_LIMIT_MACROS" -CFLAGS=`echo $CFLAGS | sed 's/-O2//g'` -CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'` +# CFLAGS=`echo $CFLAGS | sed 's/-O2//g'` +# CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'` # Check whether --enable-profiling was given. if test "${enable_profiling+set}" = set; then : diff --git a/configure.ac b/configure.ac index 73e1c6ee..10ade9d8 100644 --- a/configure.ac +++ b/configure.ac @@ -41,8 +41,8 @@ AC_PROG_CXX CPPFLAGS="$CPPFLAGS -D_ISOC99_SOURCE -D__STDC_LIMIT_MACROS" -CFLAGS=`echo $CFLAGS | sed 's/-O2//g'` -CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'` +# CFLAGS=`echo $CFLAGS | sed 's/-O2//g'` +# CXXFLAGS=`echo $CXXFLAGS | sed 's/-O2//g'` AC_ARG_ENABLE([profiling], AS_HELP_STRING([--enable-profiling], diff --git a/src/Makefile.am b/src/Makefile.am index b1d7e0de..905e0421 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,6 +83,8 @@ noinst_HEADERS = \ hist_source.hh \ init.sql \ init-sql.hh \ + json_op.hh \ + json_ptr.hh \ k_merge_tree.h \ line_buffer.hh \ listview_curses.hh \ @@ -148,6 +150,9 @@ libdiag_a_SOURCES = \ fs-extension-functions.cc \ grep_proc.cc \ hist_source.cc \ + json-extension-functions.cc \ + json_op.cc \ + json_ptr.cc \ line_buffer.cc \ listview_curses.cc \ lnav_commands.cc \ diff --git a/src/Makefile.in b/src/Makefile.in index 89519b9d..74becb10 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -113,11 +113,13 @@ am_libdiag_a_OBJECTS = ansi_scrubber.$(OBJEXT) bookmarks.$(OBJEXT) \ collation-functions.$(OBJEXT) db_sub_source.$(OBJEXT) \ extension-functions.$(OBJEXT) fs-extension-functions.$(OBJEXT) \ grep_proc.$(OBJEXT) hist_source.$(OBJEXT) \ - line_buffer.$(OBJEXT) listview_curses.$(OBJEXT) \ - lnav_commands.$(OBJEXT) lnav_config.$(OBJEXT) \ - lnav_log.$(OBJEXT) lnav_util.$(OBJEXT) log_accel.$(OBJEXT) \ - log_format.$(OBJEXT) log_format_loader.$(OBJEXT) \ - logfile.$(OBJEXT) logfile_sub_source.$(OBJEXT) \ + json-extension-functions.$(OBJEXT) json_op.$(OBJEXT) \ + json_ptr.$(OBJEXT) line_buffer.$(OBJEXT) \ + listview_curses.$(OBJEXT) lnav_commands.$(OBJEXT) \ + lnav_config.$(OBJEXT) lnav_log.$(OBJEXT) lnav_util.$(OBJEXT) \ + log_accel.$(OBJEXT) log_format.$(OBJEXT) \ + log_format_loader.$(OBJEXT) logfile.$(OBJEXT) \ + logfile_sub_source.$(OBJEXT) \ network-extension-functions.$(OBJEXT) data_scanner.$(OBJEXT) \ data_parser.$(OBJEXT) ptimec_rt.$(OBJEXT) \ readline_curses.$(OBJEXT) readline_highlighters.$(OBJEXT) \ @@ -412,6 +414,8 @@ noinst_HEADERS = \ hist_source.hh \ init.sql \ init-sql.hh \ + json_op.hh \ + json_ptr.hh \ k_merge_tree.h \ line_buffer.hh \ listview_curses.hh \ @@ -477,6 +481,9 @@ libdiag_a_SOURCES = \ fs-extension-functions.cc \ grep_proc.cc \ hist_source.cc \ + json-extension-functions.cc \ + json_op.cc \ + json_ptr.cc \ line_buffer.cc \ listview_curses.cc \ lnav_commands.cc \ @@ -730,6 +737,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs-extension-functions.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grep_proc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hist_source.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json-extension-functions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json_op.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json_ptr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/line_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listview_curses.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lnav.Po@am__quote@ diff --git a/src/default-log-formats.json b/src/default-log-formats.json index a1e55a32..13b7b69b 100644 --- a/src/default-log-formats.json +++ b/src/default-log-formats.json @@ -387,7 +387,7 @@ ], "regex" : { "std" : { - "pattern" : "^TCF (?\\d{2}:\\d{2}.\\d{3}): (?:Server-Properties: |channel server|\\w+: (?--->|<---) (?\\w)(?: (?\\w+))?(?: (?\\w+))?(?: (?\\w+))? )(?.*)" + "pattern" : "^TCF (?\\d{2}:\\d{2}.\\d{3}): (?:Server-Properties: (?:.*)|channel server|\\w+: (?--->|<---) (?\\w)(?: (?\\w+))?(?: (?\\w+))?(?: (?\\w+))?(?: (?.*))?(?: ))$" } }, "value" : { @@ -409,6 +409,9 @@ "name" : { "kind" : "string", "identifier" : true + }, + "msg" : { + "kind" : "json" } }, "sample" : [ diff --git a/src/fs-extension-functions.cc b/src/fs-extension-functions.cc index 62f2b969..e4c952c6 100644 --- a/src/fs-extension-functions.cc +++ b/src/fs-extension-functions.cc @@ -29,6 +29,8 @@ * @file fs-extension-functions.cc */ +#include "config.h" + #include #include #include diff --git a/src/json-extension-functions.cc b/src/json-extension-functions.cc new file mode 100644 index 00000000..9f87399e --- /dev/null +++ b/src/json-extension-functions.cc @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file json-extension-functions.cc + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "sqlite3.h" + +#include "json_op.hh" + +#include "sqlite-extension-func.h" + +using namespace std; + +class sql_json_op : public json_op { +public: + sql_json_op(json_ptr &ptr) : json_op(ptr), sjo_type(-1) { }; + + int sjo_type; + string sjo_str; + int sjo_int; +}; + +static void printer(void *ctx, const char *numberVal, size_t numberLen) +{ + string &str = *(string *)ctx; + + str.append(numberVal, numberLen); +} + +static void null_or_default(sqlite3_context *context, int argc, sqlite3_value **argv) +{ + if (argc > 2) { + sqlite3_result_value(context, argv[2]); + } + else { + sqlite3_result_null(context); + } +} + +static int gen_handle_null(void *ctx) +{ + sql_json_op *sjo = (sql_json_op *)ctx; + yajl_gen gen = (yajl_gen)sjo->jo_ptr_data; + + if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) { + sjo->sjo_type = SQLITE_NULL; + } + else { + sjo->jo_ptr_error_code = yajl_gen_null(gen); + } + + return sjo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_boolean(void *ctx, int boolVal) +{ + sql_json_op *sjo = (sql_json_op *)ctx; + yajl_gen gen = (yajl_gen)sjo->jo_ptr_data; + + if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) { + sjo->sjo_type = SQLITE_INTEGER; + sjo->sjo_int = boolVal; + } + else { + sjo->jo_ptr_error_code = yajl_gen_bool(gen, boolVal); + } + + return sjo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_string(void *ctx, const unsigned char * stringVal, size_t len) +{ + sql_json_op *sjo = (sql_json_op *)ctx; + yajl_gen gen = (yajl_gen)sjo->jo_ptr_data; + + if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) { + sjo->sjo_type = SQLITE3_TEXT; + sjo->sjo_str = string((char *)stringVal, len); + } + else { + sjo->jo_ptr_error_code = yajl_gen_string(gen, stringVal, len); + } + + return sjo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static void sql_jget(sqlite3_context *context, + int argc, sqlite3_value **argv) +{ + if (argc < 2) { + sqlite3_result_error(context, "expecting JSON value and pointer", -1); + return; + } + + if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { + null_or_default(context, argc, argv); + return; + } + + const char *json_in = (const char *)sqlite3_value_text(argv[0]); + + if (sqlite3_value_type(argv[1]) == SQLITE_NULL) { + sqlite3_result_text(context, json_in, -1, SQLITE_TRANSIENT); + return; + } + + const char *ptr_in = (const char *)sqlite3_value_text(argv[1]); + json_ptr jp(ptr_in); + sql_json_op jo(jp); + auto_mem gen(yajl_gen_free); + auto_mem handle(yajl_free); + const unsigned char *err; + string result; + + gen = yajl_gen_alloc(NULL); + yajl_gen_config(gen.in(), yajl_gen_print_callback, printer, &result); + yajl_gen_config(gen.in(), yajl_gen_beautify, false); + + jo.jo_ptr_callbacks = json_op::gen_callbacks; + jo.jo_ptr_callbacks.yajl_null = gen_handle_null; + jo.jo_ptr_callbacks.yajl_boolean = gen_handle_boolean; + jo.jo_ptr_callbacks.yajl_string = gen_handle_string; + jo.jo_ptr_data = gen.in(); + + handle.reset(yajl_alloc(&json_op::ptr_callbacks, NULL, &jo)); + switch (yajl_parse(handle.in(), (const unsigned char *)json_in, strlen(json_in))) { + case yajl_status_error: + err = yajl_get_error(handle.in(), 0, (const unsigned char *)json_in, strlen(json_in)); + sqlite3_result_error(context, (const char *)err, -1); + return; + case yajl_status_client_canceled: + if (jo.jo_ptr.jp_state == json_ptr::MS_ERR_INVALID_ESCAPE) { + sqlite3_result_error(context, jo.jo_ptr.error_msg().c_str(), -1); + } + else { + null_or_default(context, argc, argv); + } + return; + default: + break; + } + + switch (yajl_complete_parse(handle.in())) { + case yajl_status_error: + err = yajl_get_error(handle.in(), 0, (const unsigned char *)json_in, strlen(json_in)); + sqlite3_result_error(context, (const char *)err, -1); + return; + case yajl_status_client_canceled: + if (jo.jo_ptr.jp_state == json_ptr::MS_ERR_INVALID_ESCAPE) { + sqlite3_result_error(context, jo.jo_ptr.error_msg().c_str(), -1); + } + else { + null_or_default(context, argc, argv); + } + return; + default: + break; + } + + switch (jo.sjo_type) { + case SQLITE3_TEXT: + sqlite3_result_text(context, jo.sjo_str.c_str(), jo.sjo_str.size(), SQLITE_TRANSIENT); + return; + case SQLITE_NULL: + sqlite3_result_null(context); + return; + case SQLITE_INTEGER: + sqlite3_result_int(context, jo.sjo_int); + return; + } + + if (result.empty()) { + null_or_default(context, argc, argv); + return; + } + + sqlite3_result_text(context, result.c_str(), result.size(), SQLITE_TRANSIENT); +} + +int json_extension_functions(const struct FuncDef **basic_funcs, + const struct FuncDefAgg **agg_funcs) +{ + static const struct FuncDef fs_funcs[] = { + { "jget", -1, 0, SQLITE_UTF8, 0, sql_jget }, + + { NULL } + }; + + *basic_funcs = fs_funcs; + *agg_funcs = NULL; + + return SQLITE_OK; +} diff --git a/src/json_op.cc b/src/json_op.cc new file mode 100644 index 00000000..ecb0abf3 --- /dev/null +++ b/src/json_op.cc @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file json_op.cc + */ + +#include "config.h" + +#include "json_op.hh" +#include "lnav_log.hh" + +static int gen_handle_start_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_map_open(gen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_map_key(void *ctx, const unsigned char * key, size_t len) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_string(gen, key, len); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_end_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_map_close(gen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_null(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_null(gen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_boolean(void *ctx, int boolVal) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_bool(gen, boolVal); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_number(void *ctx, const char *numberVal, size_t numberLen) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_number(gen, numberVal, numberLen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_string(void *ctx, const unsigned char * stringVal, size_t len) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_string(gen, stringVal, len); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_start_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_array_open(gen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +static int gen_handle_end_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + jo->jo_ptr_error_code = yajl_gen_array_close(gen); + + return jo->jo_ptr_error_code == yajl_gen_status_ok; +} + +const yajl_callbacks json_op::gen_callbacks = { + gen_handle_null, + gen_handle_boolean, + NULL, + NULL, + gen_handle_number, + gen_handle_string, + gen_handle_start_map, + gen_handle_map_key, + gen_handle_end_map, + gen_handle_start_array, + gen_handle_end_array, +}; + +const yajl_callbacks json_op::ptr_callbacks = { + handle_null, + handle_boolean, + NULL, + NULL, + handle_number, + handle_string, + handle_start_map, + handle_map_key, + handle_end_map, + handle_start_array, + handle_end_array, +}; + +int json_op::handle_null(void *ctx) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_null != NULL) { + retval = jo->jo_ptr_callbacks.yajl_null(ctx); + } + } + + return retval; +} + +int json_op::handle_boolean(void *ctx, int boolVal) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_boolean != NULL) { + retval = jo->jo_ptr_callbacks.yajl_boolean(ctx, boolVal); + } + } + + return retval; +} + +int json_op::handle_number(void *ctx, const char *numberVal, size_t numberLen) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_number != NULL) { + retval = jo->jo_ptr_callbacks.yajl_number(ctx, numberVal, numberLen); + } + } + + return retval; +} + +int json_op::handle_string(void *ctx, const unsigned char *stringVal, size_t stringLen) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_string != NULL) { + retval = jo->jo_ptr_callbacks.yajl_string(ctx, stringVal, stringLen); + } + } + + return retval; +} + +int json_op::handle_start_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index(false)) { + if (jo->jo_ptr_callbacks.yajl_start_map != NULL) { + retval = jo->jo_ptr_callbacks.yajl_start_map(ctx); + } + } + + if (!jo->jo_ptr.expect_map(jo->jo_depth)) { + retval = 0; + } + + return retval; +} + +int json_op::handle_map_key(void *ctx, const unsigned char * key, size_t len) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index(false)) { + if (jo->jo_ptr_callbacks.yajl_map_key != NULL) { + retval = jo->jo_ptr_callbacks.yajl_map_key(ctx, key, len); + } + } + + if (!jo->jo_ptr.at_key(jo->jo_depth, (const char *)key, len)) { + retval = 0; + } + + return retval; +} + +int json_op::handle_end_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_end_map != NULL) { + retval = jo->jo_ptr_callbacks.yajl_end_map(ctx); + } + } + + jo->jo_ptr.exit_container(jo->jo_depth, jo->jo_array_index); + + return retval; +} + +int json_op::handle_start_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index(false)) { + if (jo->jo_ptr_callbacks.yajl_start_array != NULL) { + retval = jo->jo_ptr_callbacks.yajl_start_array(ctx); + } + } + + if (!jo->jo_ptr.expect_array(jo->jo_depth, jo->jo_array_index)) { + retval = 0; + } + + return retval; +} + +int json_op::handle_end_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + int retval = 1; + + if (jo->check_index()) { + if (jo->jo_ptr_callbacks.yajl_end_array != NULL) { + retval = jo->jo_ptr_callbacks.yajl_end_array(ctx); + } + } + + jo->jo_ptr.exit_container(jo->jo_depth, jo->jo_array_index); + + return retval; +} diff --git a/src/json_op.hh b/src/json_op.hh new file mode 100644 index 00000000..8d9cbf6e --- /dev/null +++ b/src/json_op.hh @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file json_op.hh + */ + +#ifndef __json_op_hh +#define __json_op_hh + +#include + +#include + +#include "json_ptr.hh" +#include "yajlpp.hh" + +class json_op { + static int handle_null(void *ctx); + static int handle_boolean(void *ctx, int boolVal); + static int handle_number(void *ctx, const char *numberVal, size_t numberLen); + static int handle_string(void *ctx, const unsigned char * stringVal, size_t len); + static int handle_start_map(void *ctx); + static int handle_map_key(void *ctx, const unsigned char * key, size_t len); + static int handle_end_map(void *ctx); + static int handle_start_array(void *ctx); + static int handle_end_array(void *ctx); + +public: + + const static yajl_callbacks gen_callbacks; + const static yajl_callbacks ptr_callbacks; + + json_op(json_ptr &ptr) + : jo_depth(0), + jo_array_index(-1), + jo_ptr(ptr), + jo_ptr_data(NULL), + jo_ptr_error_code(0) { + memset(&this->jo_ptr_callbacks, 0, sizeof(this->jo_ptr_callbacks)); + }; + + bool check_index(bool primitive = true) { + return this->jo_ptr.at_index(this->jo_depth, this->jo_array_index, primitive); + }; + + int jo_depth; + int jo_array_index; + + json_ptr jo_ptr; + yajl_callbacks jo_ptr_callbacks; + void *jo_ptr_data; + std::string jo_ptr_error; + int jo_ptr_error_code; +}; + +#endif diff --git a/src/json_ptr.cc b/src/json_ptr.cc new file mode 100644 index 00000000..ea78f099 --- /dev/null +++ b/src/json_ptr.cc @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file json_ptr.cc + */ + +#include "config.h" + +#include "json_ptr.hh" + +using namespace std; + +static int handle_null(void *ctx) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_values.push_back(make_pair(jpw->current_ptr(), "null")); + jpw->inc_array_index(); + + return 1; +} + +static int handle_boolean(void *ctx, int boolVal) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_values.push_back(make_pair(jpw->current_ptr(), boolVal ? "true" : "false")); + jpw->inc_array_index(); + + return 1; +} + +static int handle_number(void *ctx, const char *numberVal, size_t numberLen) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_values.push_back(make_pair(jpw->current_ptr(), string(numberVal, numberLen))); + jpw->inc_array_index(); + + return 1; +} + +static void appender(void *ctx, const char *strVal, size_t strLen) +{ + string &str = *(string *)ctx; + + str.append(strVal, strLen); +} + +static int handle_string(void *ctx, const unsigned char * stringVal, size_t len) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + auto_mem gen(yajl_gen_free); + string str; + + gen = yajl_gen_alloc(NULL); + yajl_gen_config(gen.in(), yajl_gen_print_callback, appender, &str); + yajl_gen_string(gen.in(), stringVal, len); + jpw->jpw_values.push_back(make_pair(jpw->current_ptr(), str)); + jpw->inc_array_index(); + + return 1; +} + +static int handle_start_map(void *ctx) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_keys.push_back(""); + jpw->jpw_array_indexes.push_back(-1); + + return 1; +} + +static int handle_map_key(void *ctx, const unsigned char * key, size_t len) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + char partially_encoded_key[len + 32]; + size_t required_len; + + jpw->jpw_keys.pop_back(); + + required_len = json_ptr::encode(partially_encoded_key, sizeof(partially_encoded_key), + (const char *)key, len); + if (required_len < sizeof(partially_encoded_key)) { + jpw->jpw_keys.push_back(string(partially_encoded_key, required_len)); + } + else { + char fully_encoded_key[required_len]; + + json_ptr::encode(fully_encoded_key, sizeof(fully_encoded_key), + (const char *)key, len); + jpw->jpw_keys.push_back(string(fully_encoded_key, required_len)); + } + + return 1; +} + +static int handle_end_map(void *ctx) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_keys.pop_back(); + jpw->jpw_array_indexes.pop_back(); + + jpw->inc_array_index(); + + return 1; +} + +static int handle_start_array(void *ctx) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_keys.push_back(""); + jpw->jpw_array_indexes.push_back(0); + + return 1; +} + +static int handle_end_array(void *ctx) +{ + json_ptr_walk *jpw = (json_ptr_walk *)ctx; + + jpw->jpw_keys.pop_back(); + jpw->jpw_array_indexes.pop_back(); + jpw->inc_array_index(); + + return 1; +} + +const yajl_callbacks json_ptr_walk::callbacks = { + handle_null, + handle_boolean, + NULL, + NULL, + handle_number, + handle_string, + handle_start_map, + handle_map_key, + handle_end_map, + handle_start_array, + handle_end_array +}; diff --git a/src/json_ptr.hh b/src/json_ptr.hh new file mode 100644 index 00000000..2fc5fbd4 --- /dev/null +++ b/src/json_ptr.hh @@ -0,0 +1,380 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file json_ptr.hh + */ + +#ifndef __json_ptr_hh +#define __json_ptr_hh + +#include +#include +#include +#include + +#include +#include + +#include "yajlpp.hh" +#include "lnav_log.hh" + +class json_ptr_walk { +public: + const static yajl_callbacks callbacks; + + json_ptr_walk() : jpw_handle(yajl_free) { + this->jpw_handle = yajl_alloc(&callbacks, NULL, this); + }; + + yajl_status parse(const char *buffer, ssize_t len) { + yajl_status retval; + + retval = yajl_parse(this->jpw_handle, (const unsigned char *)buffer, len); + this->update_error_msg(retval, buffer, len); + return retval; + }; + + yajl_status complete_parse() { + yajl_status retval; + + retval = yajl_complete_parse(this->jpw_handle); + this->update_error_msg(retval, NULL, -1); + return retval; + }; + + void update_error_msg(yajl_status status, const char *buffer, ssize_t len) { + switch (status) { + case yajl_status_ok: + break; + case yajl_status_client_canceled: + this->jpw_error_msg = "internal error"; + break; + case yajl_status_error: + this->jpw_error_msg = std::string((const char *)yajl_get_error( + this->jpw_handle, 1, (const unsigned char *)buffer, len)); + break; + } + }; + + void clear() { + this->jpw_values.clear(); + }; + + void inc_array_index() { + if (!this->jpw_array_indexes.empty() && + this->jpw_array_indexes.back() != -1) { + this->jpw_array_indexes.back() += 1; + } + }; + + std::string current_ptr() { + std::string retval; + + for (int lpc = 0; lpc < this->jpw_array_indexes.size(); lpc++) { + retval.append("/"); + if (this->jpw_array_indexes[lpc] == -1) { + retval.append(this->jpw_keys[lpc]); + } + else { + char num[64]; + + snprintf(num, sizeof(num), "%d", this->jpw_array_indexes[lpc]); + retval.append(num); + } + } + + return retval; + }; + + typedef std::vector > pair_list_t; + + auto_mem jpw_handle; + std::string jpw_error_msg; + pair_list_t jpw_values; + std::vector jpw_keys; + std::vector jpw_array_indexes; +}; + +class json_ptr { +public: + enum match_state_t { + MS_DONE, + MS_VALUE, + MS_ELEMENT, + MS_ERR_INVALID_TYPE, + MS_ERR_NO_SLASH, + MS_ERR_INVALID_ESCAPE, + MS_ERR_INVALID_INDEX, + }; + + static size_t encode(char *dst, size_t dst_len, const char *src, size_t src_len = -1) { + size_t retval = 0; + + if (src_len == -1) { + src_len = strlen(src); + } + + for (size_t lpc = 0; lpc < src_len; lpc++) { + switch (src[lpc]) { + case '/': + case '~': + if (retval < dst_len) { + dst[retval] = '~'; + retval += 1; + if (src[lpc] == '~') { + dst[retval] = '0'; + } + else { + dst[retval] = '1'; + } + } + else { + retval += 1; + } + break; + default: + if (retval < dst_len) { + dst[retval] = src[lpc]; + } + break; + } + retval += 1; + } + + return retval; + }; + + json_ptr(const char *value) + : jp_value(value), jp_pos(value), jp_depth(0), jp_array_index(-1), + jp_state(MS_VALUE) { + + }; + + bool expect_map(int32_t &depth) { + bool retval; + + if (this->jp_state == MS_DONE) { + retval = true; + } + else if (depth != this->jp_depth) { + retval = true; + } + else if (this->reached_end()) { + retval = true; + } + else if (this->jp_state == MS_VALUE) { + if (this->jp_pos[0] == '/') { + this->jp_pos += 1; + this->jp_depth += 1; + this->jp_state = MS_ELEMENT; + retval = true; + } + else { + this->jp_state = MS_ERR_NO_SLASH; + retval = false; + } + } + else { + retval = true; + } + depth += 1; + + return retval; + }; + + bool at_key(int32_t depth, const char *component, ssize_t len = -1) { + const char *component_end; + int lpc; + + if (this->jp_state == MS_DONE || depth != this->jp_depth) { + return true; + } + + if (len == -1) { + len = strlen(component); + } + component_end = component + len; + + for (lpc = 0; component < component_end; lpc++, component++) { + char ch = this->jp_pos[lpc]; + + if (this->jp_pos[lpc] == '~') { + switch (this->jp_pos[lpc + 1]) { + case '0': + ch = '~'; + break; + case '1': + ch = '/'; + break; + default: + this->jp_state = MS_ERR_INVALID_ESCAPE; + return false; + } + lpc += 1; + } + else if (this->jp_pos[lpc] == '/') { + ch = '\0'; + } + + if (ch != *component) { + return true; + } + } + + this->jp_pos += lpc; + this->jp_state = MS_VALUE; + + return true; + }; + + void exit_container(int32_t &depth, int32_t &index) { + depth -= 1; + if (this->jp_state == MS_VALUE && depth == this->jp_depth) { + this->jp_state = MS_DONE; + index = -1; + } + }; + + bool expect_array(int32_t &depth, int32_t &index) { + bool retval; + + if (this->jp_state == MS_DONE) { + retval = true; + } + else if (depth != this->jp_depth) { + retval = true; + } + else if (this->reached_end()) { + retval = true; + } + else if (this->jp_pos[0] == '/') { + int offset; + + this->jp_depth += 1; + + if (sscanf(this->jp_pos, "/%d%n", &this->jp_array_index, &offset) != 1) { + this->jp_state = MS_ERR_INVALID_INDEX; + retval = false; + } + else if (this->jp_pos[offset] != '\0' && this->jp_pos[offset] != '/') { + this->jp_state = MS_ERR_INVALID_INDEX; + retval = false; + } + else { + index = 0; + this->jp_pos += offset; + this->jp_state = MS_ELEMENT; + retval = true; + } + } + else { + this->jp_state = MS_ERR_NO_SLASH; + retval = false; + } + + depth += 1; + + return retval; + }; + + bool at_index(int32_t &depth, int32_t &index, bool primitive = true) { + bool retval; + + if (this->jp_state == MS_DONE) { + retval = false; + } + else if (depth < this->jp_depth) { + retval = false; + } + else if (depth == this->jp_depth) { + if (index == -1) { + if (this->jp_array_index == -1) { + retval = this->reached_end(); + if (primitive && retval) { + this->jp_state = MS_DONE; + } + } + else { + retval = false; + } + } + else if (index == this->jp_array_index) { + retval = this->reached_end(); + index = -1; + if (primitive && retval) { + this->jp_state = MS_DONE; + } + } + else { + index += 1; + retval = false; + } + } + else if (index == -1) { + retval = this->reached_end(); + } + else { + retval = false; + } + + return retval; + }; + + bool reached_end() { + return this->jp_pos[0] == '\0'; + }; + + std::string error_msg() const { + std::string retval; + + switch (this->jp_state) { + case MS_ERR_INVALID_ESCAPE: + retval = ("invalid escape sequence near -- " + std::string(this->jp_pos)); + break; + case MS_ERR_INVALID_INDEX: + retval = ("expecting array index at -- " + std::string(this->jp_pos)); + break; + case MS_ERR_INVALID_TYPE: + retval = ("expecting container at -- " + std::string( + this->jp_value, this->jp_pos - this->jp_value)); + break; + default: + break; + } + + return retval; + }; + + const char *jp_value; + const char *jp_pos; + int32_t jp_depth; + int32_t jp_array_index; + match_state_t jp_state; +}; + +#endif diff --git a/src/lnav.cc b/src/lnav.cc index d2d36e66..7cdc181d 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -215,6 +215,7 @@ public: size_t list_overlay_count(const listview_curses &lv) { logfile_sub_source &lss = lnav_data.ld_log_source; + view_colors &vc = view_colors::singleton(); if (!this->fos_active) { return 0; @@ -231,13 +232,13 @@ public: return 0; } - this->fos_key_size = 0; + this->fos_known_key_size = 0; for (std::vector::iterator iter = this->fos_log_helper.ldh_line_values.begin(); iter != this->fos_log_helper.ldh_line_values.end(); ++iter) { - this->fos_key_size = max(this->fos_key_size, + this->fos_known_key_size = max(this->fos_known_key_size, (int)iter->lv_name.length()); } @@ -249,127 +250,134 @@ public: iter->e_sub_elements->front()); colname = this->fos_log_helper.ldh_namer->add_column(colname); - this->fos_key_size = max(this->fos_key_size, (int)colname.length()); + this->fos_unknown_key_size = max( + this->fos_unknown_key_size, (int)colname.length()); } - return 1 + - 1 + - this->fos_log_helper.ldh_line_values.size() + - 1 + - this->fos_log_helper.ldh_parser->dp_pairs.size(); + this->fos_lines.clear(); + + char old_timestamp[64], curr_timestamp[64]; + struct timeval curr_tv, offset_tv, orig_tv; + char log_time[256]; + + sql_strftime(curr_timestamp, sizeof(curr_timestamp), + this->fos_log_helper.ldh_line->get_time(), + this->fos_log_helper.ldh_line->get_millis(), + 'T'); + curr_tv = this->fos_log_helper.ldh_line->get_timeval(); + offset_tv = this->fos_log_helper.ldh_file->get_time_offset(); + timersub(&curr_tv, &offset_tv, &orig_tv); + sql_strftime(old_timestamp, sizeof(old_timestamp), + orig_tv.tv_sec, orig_tv.tv_usec / 1000, + 'T'); + snprintf(log_time, sizeof(log_time), + " Current Time: %s Original Time: %s Offset: %+d.%03d", + curr_timestamp, + old_timestamp, + (int)offset_tv.tv_sec, offset_tv.tv_usec / 1000); + this->fos_lines.push_back(log_time); + + if (this->fos_log_helper.ldh_line_values.empty()) { + this->fos_lines.push_back(" No known message fields"); + } + else{ + this->fos_lines.push_back(" Known message fields:"); + } + + for (size_t lpc = 0; lpc < this->fos_log_helper.ldh_line_values.size(); lpc++) { + logline_value &lv = this->fos_log_helper.ldh_line_values[lpc]; + attr_line_t al; + string str; + + str = " " + lv.lv_name; + str.append(this->fos_known_key_size - lv.lv_name.size() + 3, ' '); + str += " = " + lv.to_string(); + + + al.with_string(str) + .with_attr(string_attr( + line_range(3, 3 + lv.lv_name.length()), + &view_curses::VC_STYLE, + vc.attrs_for_ident(lv.lv_name))); + + this->fos_lines.push_back(al); + this->add_key_line_attrs(this->fos_known_key_size); + } + + std::map::iterator json_iter; + + if (!this->fos_log_helper.ldh_json_pairs.empty()) { + this->fos_lines.push_back(" JSON fields:"); + } + + for (json_iter = this->fos_log_helper.ldh_json_pairs.begin(); + json_iter != this->fos_log_helper.ldh_json_pairs.end(); + ++json_iter) { + json_ptr_walk::pair_list_t &jpairs = json_iter->second; + + for (size_t lpc = 0; lpc < jpairs.size(); lpc++) { + this->fos_lines.push_back(" " + + this->fos_log_helper.format_json_getter(json_iter->first, lpc) + " = " + + jpairs[lpc].second); + this->add_key_line_attrs(0); + } + } + + if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) { + this->fos_lines.push_back(" No discovered message fields"); + } + else { + this->fos_lines.push_back(" Discovered message fields:"); + } + + data_parser::element_list_t::iterator iter; + + iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); + for (size_t lpc = 0; + lpc < this->fos_log_helper.ldh_parser->dp_pairs.size(); lpc++, ++iter) { + string &name = this->fos_log_helper.ldh_namer->cn_names[lpc]; + string val = this->fos_log_helper.ldh_parser->get_element_string( + iter->e_sub_elements->back()); + attr_line_t al(" " + name + " = " + val); + + al.with_attr(string_attr( + line_range(3, 3 + name.length()), + &view_curses::VC_STYLE, + vc.attrs_for_ident(name))); + + this->fos_lines.push_back(al); + this->add_key_line_attrs(this->fos_unknown_key_size, + lpc == (this->fos_log_helper.ldh_parser->dp_pairs.size() - 1)); + } + + return this->fos_lines.size(); + }; + + void add_key_line_attrs(int key_size, bool last_line = false) { + string_attrs_t &sa = this->fos_lines.back().get_attrs(); + struct line_range lr(1, 2); + sa.push_back(string_attr(lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE)); + + lr.lr_start = 3 + key_size + 3; + lr.lr_end = -1; + sa.push_back(string_attr(lr, &view_curses::VC_STYLE, A_BOLD)); }; bool list_value_for_overlay(const listview_curses &lv, vis_line_t y, attr_line_t &value_out) { - view_colors &vc = view_colors::singleton(); - if (!this->fos_active || this->fos_log_helper.ldh_parser.get() == NULL) { return false; } - size_t total_count = (1 + - 1 + - this->fos_log_helper.ldh_line_values.size() + - 1 + - this->fos_log_helper.ldh_parser->dp_pairs.size()); int row = (int)y - 1; - bool last_line = (row == (int)(total_count - 1)); - if (row < 0 || row >= (int)total_count) { + if (row < 0 || row >= (int)this->fos_lines.size()) { return false; } - std::string &str = value_out.get_string(); - - if (row == 0) { - char old_timestamp[64], curr_timestamp[64]; - struct timeval curr_tv, offset_tv, orig_tv; - char log_time[256]; - - sql_strftime(curr_timestamp, sizeof(curr_timestamp), - this->fos_log_helper.ldh_line->get_time(), - this->fos_log_helper.ldh_line->get_millis(), - 'T'); - curr_tv = this->fos_log_helper.ldh_line->get_timeval(); - offset_tv = this->fos_log_helper.ldh_file->get_time_offset(); - timersub(&curr_tv, &offset_tv, &orig_tv); - sql_strftime(old_timestamp, sizeof(old_timestamp), - orig_tv.tv_sec, orig_tv.tv_usec / 1000, - 'T'); - snprintf(log_time, sizeof(log_time), - " Current Time: %s Original Time: %s Offset: %+d.%03d", - curr_timestamp, - old_timestamp, - (int)offset_tv.tv_sec, offset_tv.tv_usec / 1000); - str = log_time; - return true; - } - row -= 1; - if (row == 0) { - if (this->fos_log_helper.ldh_line_values.empty()) { - str = " No known message fields"; - } - else{ - str = " Known message fields:"; - } - return true; - } - row -= 1; - if (row == (int)this->fos_log_helper.ldh_line_values.size()) { - if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) { - str = " No discovered message fields"; - } - else{ - str = " Discovered message fields:"; - } - return true; - } - - if (row < (int)this->fos_log_helper.ldh_line_values.size()) { - string &name = this->fos_log_helper.ldh_line_values[row].lv_name; - - str = " " + name; - - int padding = this->fos_key_size - str.length() + 3; - - value_out.get_attrs().push_back(string_attr( - line_range(3, 3 + name.length()), - &view_curses::VC_STYLE, - vc.attrs_for_ident(name))); - str.append(padding, ' '); - str += " = " + this->fos_log_helper.ldh_line_values[row].to_string(); - } - else { - data_parser::element_list_t::iterator iter; - - row -= this->fos_log_helper.ldh_line_values.size() + 1; - - iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); - std::advance(iter, row); - - string &name = this->fos_log_helper.ldh_namer->cn_names[row]; - - str = " " + name; - int padding = this->fos_key_size - str.length() + 3; - - value_out.get_attrs().push_back(string_attr( - line_range(3, 3 + name.length()), - &view_curses::VC_STYLE, - vc.attrs_for_ident(name))); - str.append(padding, ' '); - str += " = " + this->fos_log_helper.ldh_parser->get_element_string( - iter->e_sub_elements->back()); - } - - string_attrs_t & sa = value_out.get_attrs(); - struct line_range lr(1, 2); - sa.push_back(string_attr(lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE)); - - lr.lr_start = 3 + this->fos_key_size + 3; - lr.lr_end = -1; - sa.push_back(string_attr(lr, &view_curses::VC_STYLE, A_BOLD)); + value_out = this->fos_lines[row]; return true; }; @@ -377,7 +385,9 @@ public: bool fos_active; bool fos_active_prev; log_data_helper fos_log_helper; - int fos_key_size; + int fos_known_key_size; + int fos_unknown_key_size; + vector fos_lines; }; static int handle_collation_list(void *ptr, @@ -552,6 +562,11 @@ bool setup_logline_table() textview_curses &log_view = lnav_data.ld_views[LNV_LOG]; bool retval = false; + if (lnav_data.ld_rl_view != NULL) { + lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*"); + add_view_text_possibilities(LNM_SQL, &log_view); + } + if (log_view.get_inner_height()) { vis_line_t vl = log_view.get_top(); content_line_t cl = lnav_data.ld_log_source.at_base(vl); @@ -559,15 +574,28 @@ bool setup_logline_table() lnav_data.ld_vtab_manager->unregister_vtab("logline"); lnav_data.ld_vtab_manager->register_vtab(new log_data_table(cl)); + if (lnav_data.ld_rl_view != NULL) { + log_data_helper ldh(lnav_data.ld_log_source); + + ldh.parse_line(cl); + + std::map::const_iterator pair_iter; + for (pair_iter = ldh.ldh_json_pairs.begin(); + pair_iter != ldh.ldh_json_pairs.end(); + ++pair_iter) { + for (int lpc = 0; lpc < pair_iter->second.size(); lpc++) { + lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", + ldh.format_json_getter(pair_iter->first, lpc)); + } + } + } + retval = true; } lnav_data.ld_db_key_names.clear(); if (lnav_data.ld_rl_view != NULL) { - lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*"); - - add_view_text_possibilities(LNM_SQL, &log_view); add_env_possibilities(LNM_SQL); lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_keywords); diff --git a/src/lnav_log.cc b/src/lnav_log.cc index 25f0bd07..2499203e 100644 --- a/src/lnav_log.cc +++ b/src/lnav_log.cc @@ -133,6 +133,20 @@ static char *log_alloc(void) return &log_ring.lr_data[log_ring.lr_length]; } +void log_argv(int argc, char *argv[]) +{ + const char *log_path = getenv("LNAV_LOG_PATH"); + + if (log_path != NULL) { + lnav_log_file = fopen(log_path, "a"); + } + + log_info("argv[%d] =", argc); + for (int lpc = 0; lpc < argc; lpc++) { + log_info(" [%d] = %s", lpc, argv[lpc]); + } +} + void log_host_info(void) { struct utsname un; diff --git a/src/lnav_log.hh b/src/lnav_log.hh index 982a01d5..4632fd55 100644 --- a/src/lnav_log.hh +++ b/src/lnav_log.hh @@ -48,6 +48,7 @@ enum lnav_log_level_t { LOG_LEVEL_ERROR, }; +void log_argv(int argc, char *argv[]); void log_host_info(void); void log_msg(enum lnav_log_level_t level, const char *src_file, int line_number, const char *fmt, ...); diff --git a/src/log_data_helper.hh b/src/log_data_helper.hh index 875e81b8..294b2546 100644 --- a/src/log_data_helper.hh +++ b/src/log_data_helper.hh @@ -32,12 +32,18 @@ #ifndef __log_data_helper_hh #define __log_data_helper_hh +#include #include #include +#include + #include "logfile_sub_source.hh" #include "data_parser.hh" #include "column_namer.hh" +#include "json_ptr.hh" +#include "lnav_log.hh" +#include "sql_util.hh" class log_data_helper { @@ -50,6 +56,7 @@ public: this->ldh_parser.reset(); this->ldh_scanner.reset(); this->ldh_namer.reset(); + this->ldh_json_pairs.clear(); }; bool parse_line(vis_line_t line, bool allow_middle = false) { @@ -74,6 +81,7 @@ public: this->ldh_parser.reset(); this->ldh_scanner.reset(); this->ldh_namer.reset(); + this->ldh_json_pairs.clear(); } else { log_format *format = this->ldh_file->get_format(); @@ -94,6 +102,24 @@ public: this->ldh_parser.reset(new data_parser(this->ldh_scanner.get())); this->ldh_parser->parse(); this->ldh_namer.reset(new column_namer()); + this->ldh_json_pairs.clear(); + + for (std::vector::iterator iter = + this->ldh_line_values.begin(); + iter != this->ldh_line_values.end(); + ++iter) { + switch (iter->lv_kind) { + case logline_value::VALUE_JSON: { + json_ptr_walk jpw; + + jpw.parse(iter->lv_sbr.get_data(), iter->lv_sbr.length()); + this->ldh_json_pairs[iter->lv_name] = jpw.jpw_values; + } + break; + default: + break; + } + } retval = true; } @@ -134,6 +160,19 @@ public: '\n'); }; + std::string format_json_getter(std::string field, int index) { + auto_mem qname; + auto_mem jget; + std::string retval; + + qname = sql_quote_ident(field.c_str()); + jget = sqlite3_mprintf("jget(%s,%Q)", qname.in(), + this->ldh_json_pairs[field][index].first.c_str()); + retval = std::string(jget); + + return retval; + }; + logfile_sub_source &ldh_log_source; content_line_t ldh_source_line; logfile *ldh_file; @@ -145,6 +184,7 @@ public: std::auto_ptr ldh_parser; std::auto_ptr ldh_namer; std::vector ldh_line_values; + std::map ldh_json_pairs; }; #endif diff --git a/src/log_format.cc b/src/log_format.cc index ff72e283..84320eae 100644 --- a/src/log_format.cc +++ b/src/log_format.cc @@ -171,7 +171,8 @@ const char *logline_value::value_names[VALUE__MAX] = { "text", "int", "float", - "bool" + "bool", + "json" }; logline_value::kind_t logline_value::string2kind(const char *kindstr) @@ -188,6 +189,9 @@ logline_value::kind_t logline_value::string2kind(const char *kindstr) else if (strcmp(kindstr, "boolean") == 0) { return VALUE_BOOLEAN; } + else if (strcmp(kindstr, "json") == 0) { + return VALUE_JSON; + } return VALUE_UNKNOWN; } @@ -448,6 +452,7 @@ static int json_array_end(void *ctx) jlu->jlu_format->jlf_line_values.push_back( logline_value(field_name, tsb.tsb_ref)); + jlu->jlu_format->jlf_line_values.back().lv_kind = logline_value::VALUE_JSON; } return 1; @@ -1136,6 +1141,7 @@ public: switch (vd.vd_kind) { case logline_value::VALUE_NULL: case logline_value::VALUE_TEXT: + case logline_value::VALUE_JSON: type = SQLITE3_TEXT; break; case logline_value::VALUE_FLOAT: diff --git a/src/log_format.hh b/src/log_format.hh index 12224be9..2cc4a8a6 100644 --- a/src/log_format.hh +++ b/src/log_format.hh @@ -343,6 +343,7 @@ public: VALUE_INTEGER, VALUE_FLOAT, VALUE_BOOLEAN, + VALUE_JSON, VALUE__MAX }; @@ -378,6 +379,7 @@ public: } switch (kind) { + case VALUE_JSON: case VALUE_TEXT: this->lv_sbr = sbr; break; @@ -429,6 +431,7 @@ public: case VALUE_NULL: return "null"; + case VALUE_JSON: case VALUE_TEXT: return std::string(this->lv_sbr.get_data(), this->lv_sbr.length()); diff --git a/src/log_format_loader.cc b/src/log_format_loader.cc index fdd3f816..0a88fe7e 100644 --- a/src/log_format_loader.cc +++ b/src/log_format_loader.cc @@ -169,7 +169,7 @@ static int read_value_def(yajlpp_parse_context *ypc, const unsigned char *str, s if ((kind = logline_value::string2kind(val.c_str())) == logline_value::VALUE_UNKNOWN) { - fprintf(stderr, "unknown value kind %s\n", val.c_str()); + fprintf(stderr, "error: unknown value kind %s\n", val.c_str()); return 0; } elf->elf_value_defs[value_name].vd_kind = kind; diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index d78844cf..ccc69a80 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -381,6 +381,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) case logline_value::VALUE_NULL: sqlite3_result_null(ctx); break; + case logline_value::VALUE_JSON: case logline_value::VALUE_TEXT: { const char *text_value = lv_iter->lv_sbr.get_data(); diff --git a/src/network-extension-functions.cc b/src/network-extension-functions.cc index 5f1d9383..9d00134b 100644 --- a/src/network-extension-functions.cc +++ b/src/network-extension-functions.cc @@ -29,6 +29,8 @@ * @file nextwork-extension-functions.cc */ +#include "config.h" + #include #include diff --git a/src/sql_util.cc b/src/sql_util.cc index 02b14c32..05440a7a 100644 --- a/src/sql_util.cc +++ b/src/sql_util.cc @@ -32,7 +32,7 @@ #include "config.h" #include - +#include #include #include "auto_mem.hh" @@ -519,3 +519,45 @@ void sql_install_logger(void) { sqlite3_config(SQLITE_CONFIG_LOG, sqlite_logger, NULL); } + +char *sql_quote_ident(const char *ident) +{ + char *quote = (char *)ident; + size_t quote_count = 0; + char *retval; + + while ((quote = strchr(quote, '"')) != NULL) { + quote_count += 1; + quote += 1; + } + + if ((retval = (char *)sqlite3_malloc( + strlen(ident) + quote_count * 2 + (quote_count ? 2: 0))) == NULL) { + retval = NULL; + } + else { + char *curr = retval; + + if (quote_count) { + curr[0] = '"'; + curr += 1; + } + for (size_t lpc = 0; ident[lpc] != '\0'; lpc++) { + switch (ident[lpc]) { + case '"': + curr[0] = '"'; + curr += 1; + default: + curr[0] = ident[lpc]; + break; + } + curr += 1; + } + if (quote_count) { + curr[0] = '"'; + curr += 1; + } + } + + return retval; +} diff --git a/src/sql_util.hh b/src/sql_util.hh index 27be350d..da90ef06 100644 --- a/src/sql_util.hh +++ b/src/sql_util.hh @@ -75,4 +75,6 @@ inline void sql_strftime(char *buffer, size_t buffer_size, void sql_install_logger(void); +char *sql_quote_ident(const char *ident); + #endif diff --git a/src/sqlite-extension-func.c b/src/sqlite-extension-func.c index ba5c239d..10c9a292 100644 --- a/src/sqlite-extension-func.c +++ b/src/sqlite-extension-func.c @@ -42,6 +42,7 @@ sqlite_registration_func_t sqlite_registration_funcs[] = { string_extension_functions, network_extension_functions, fs_extension_functions, + json_extension_functions, NULL }; diff --git a/src/sqlite-extension-func.h b/src/sqlite-extension-func.h index 54f2a68d..624900b6 100644 --- a/src/sqlite-extension-func.h +++ b/src/sqlite-extension-func.h @@ -75,6 +75,9 @@ int network_extension_functions(const struct FuncDef **basic_funcs, int fs_extension_functions(const struct FuncDef **basic_funcs, const struct FuncDefAgg **agg_funcs); +int json_extension_functions(const struct FuncDef **basic_funcs, + const struct FuncDefAgg **agg_funcs); + extern sqlite_registration_func_t sqlite_registration_funcs[]; int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs); diff --git a/src/state-extension-functions.cc b/src/state-extension-functions.cc index 354c63c7..d470e553 100644 --- a/src/state-extension-functions.cc +++ b/src/state-extension-functions.cc @@ -29,6 +29,8 @@ * @file state-extension-functions.cc */ +#include "config.h" + #include #include #include diff --git a/src/view_curses.hh b/src/view_curses.hh index a8038be4..a7994044 100644 --- a/src/view_curses.hh +++ b/src/view_curses.hh @@ -293,10 +293,15 @@ public: attr_line_t() { this->al_attrs.reserve(RESERVE_SIZE); }; + attr_line_t(const std::string &str) : al_string(str) { this->al_attrs.reserve(RESERVE_SIZE); }; + attr_line_t(const char *str) : al_string(str) { + this->al_attrs.reserve(RESERVE_SIZE); + }; + /** @return The string itself. */ std::string &get_string() { return this->al_string; }; @@ -315,8 +320,6 @@ public: size_t length() const { return this->al_string.length(); }; - void operator=(const std::string &rhs) { this->al_string = rhs; }; - /** Clear the string and the attributes for the string. */ attr_line_t &clear() { diff --git a/src/yajlpp.hh b/src/yajlpp.hh index c50a0e98..d3680a2e 100644 --- a/src/yajlpp.hh +++ b/src/yajlpp.hh @@ -142,23 +142,6 @@ struct json_path_handler : public json_path_handler_base { class yajlpp_parse_context { public: - - struct json_path_element { - json_path_element(int index = 0) : jpe_index(index) { }; - json_path_element(const std::string &name) - : jpe_name(name), jpe_index(0) {}; - json_path_element(const unsigned char *name) - : jpe_name((const char *)name), jpe_index(0) {}; - - void set_name(const unsigned char *name, size_t len) - { - this->jpe_name = std::string((const char *)name, len); - }; - - std::string jpe_name; - int jpe_index; - }; - yajlpp_parse_context(std::string source, struct json_path_handler *handlers = NULL) : ypc_source(source), ypc_handlers(handlers), ypc_ignore_unused(false) diff --git a/test/Makefile.am b/test/Makefile.am index 1a167838..252be8fc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,8 @@ check_PROGRAMS = \ drive_data_scanner \ drive_line_buffer \ drive_grep_proc \ + drive_json_op \ + drive_json_ptr_walk \ drive_listview \ drive_logfile \ drive_mvwattrline \ @@ -35,6 +37,7 @@ check_PROGRAMS = \ test_date_time_scanner \ test_grep_proc2 \ test_hist_source \ + test_json_ptr \ test_line_buffer2 \ test_log_accel \ test_pcrepp \ @@ -86,12 +89,21 @@ test_yajlpp_LDADD = ../src/libdiag.a test_concise_SOURCES = test_concise.cc test_concise_LDADD = ../src/libdiag.a +test_json_ptr_SOURCES = test_json_ptr.cc +test_json_ptr_LDADD = ../src/libdiag.a + drive_line_buffer_SOURCES = drive_line_buffer.cc drive_line_buffer_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz drive_grep_proc_SOURCES = drive_grep_proc.cc drive_grep_proc_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz +drive_json_op_SOURCES = drive_json_op.cc +drive_json_op_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz + +drive_json_ptr_walk_SOURCES = drive_json_ptr_walk.cc +drive_json_ptr_walk_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz + drive_listview_SOURCES = drive_listview.cc drive_listview_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz @@ -160,6 +172,7 @@ dist_noinst_SCRIPTS = \ test_sessions.sh \ test_sql.sh \ test_sql_coll_func.sh \ + test_sql_json_func.sh \ test_sql_str_func.sh \ test_sql_fs_func.sh \ test_view_colors.sh \ @@ -233,12 +246,15 @@ TESTS = \ test_grep_proc2 \ test_hist_source \ test_json_format.sh \ + test_json_op.sh \ + test_json_ptr_walk.sh \ test_log_accel \ test_logfile.sh \ test_pcrepp \ test_sessions.sh \ test_sql.sh \ test_sql_coll_func.sh \ + test_sql_json_func.sh \ test_sql_fs_func.sh \ test_sql_str_func.sh \ test_view_colors.sh \ diff --git a/test/Makefile.in b/test/Makefile.in index 644789b1..7395a02d 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -81,6 +81,7 @@ build_triplet = @build@ host_triplet = @host@ check_PROGRAMS = drive_data_scanner$(EXEEXT) \ drive_line_buffer$(EXEEXT) drive_grep_proc$(EXEEXT) \ + drive_json_op$(EXEEXT) drive_json_ptr_walk$(EXEEXT) \ drive_listview$(EXEEXT) drive_logfile$(EXEEXT) \ drive_mvwattrline$(EXEEXT) drive_sequencer$(EXEEXT) \ drive_sql$(EXEEXT) drive_view_colors$(EXEEXT) \ @@ -89,18 +90,20 @@ check_PROGRAMS = drive_data_scanner$(EXEEXT) \ test_auto_fd$(EXEEXT) test_auto_mem$(EXEEXT) \ test_bookmarks$(EXEEXT) test_concise$(EXEEXT) \ test_date_time_scanner$(EXEEXT) test_grep_proc2$(EXEEXT) \ - test_hist_source$(EXEEXT) test_line_buffer2$(EXEEXT) \ - test_log_accel$(EXEEXT) test_pcrepp$(EXEEXT) \ - test_top_status$(EXEEXT) test_yajlpp$(EXEEXT) + test_hist_source$(EXEEXT) test_json_ptr$(EXEEXT) \ + test_line_buffer2$(EXEEXT) test_log_accel$(EXEEXT) \ + test_pcrepp$(EXEEXT) test_top_status$(EXEEXT) \ + test_yajlpp$(EXEEXT) TESTS = test_ansi_scrubber$(EXEEXT) test_auto_fd$(EXEEXT) \ test_auto_mem$(EXEEXT) test_bookmarks$(EXEEXT) \ test_date_time_scanner$(EXEEXT) test_cmds.sh \ test_concise$(EXEEXT) test_line_buffer2$(EXEEXT) \ test_line_buffer.sh test_listview.sh test_mvwattrline.sh \ test_grep_proc.sh test_grep_proc2$(EXEEXT) \ - test_hist_source$(EXEEXT) test_json_format.sh \ - test_log_accel$(EXEEXT) test_logfile.sh test_pcrepp$(EXEEXT) \ - test_sessions.sh test_sql.sh test_sql_coll_func.sh \ + test_hist_source$(EXEEXT) test_json_format.sh test_json_op.sh \ + test_json_ptr_walk.sh test_log_accel$(EXEEXT) test_logfile.sh \ + test_pcrepp$(EXEEXT) test_sessions.sh test_sql.sh \ + test_sql_coll_func.sh test_sql_json_func.sh \ test_sql_fs_func.sh test_sql_str_func.sh test_view_colors.sh \ test_vt52_curses.sh test_top_status$(EXEEXT) \ test_data_parser.sh test_yajlpp$(EXEEXT) @@ -129,6 +132,13 @@ drive_data_scanner_DEPENDENCIES = ../src/libdiag.a \ am_drive_grep_proc_OBJECTS = drive_grep_proc.$(OBJEXT) drive_grep_proc_OBJECTS = $(am_drive_grep_proc_OBJECTS) drive_grep_proc_DEPENDENCIES = ../src/libdiag.a $(am__DEPENDENCIES_1) +am_drive_json_op_OBJECTS = drive_json_op.$(OBJEXT) +drive_json_op_OBJECTS = $(am_drive_json_op_OBJECTS) +drive_json_op_DEPENDENCIES = ../src/libdiag.a $(am__DEPENDENCIES_1) +am_drive_json_ptr_walk_OBJECTS = drive_json_ptr_walk.$(OBJEXT) +drive_json_ptr_walk_OBJECTS = $(am_drive_json_ptr_walk_OBJECTS) +drive_json_ptr_walk_DEPENDENCIES = ../src/libdiag.a \ + $(am__DEPENDENCIES_1) am_drive_line_buffer_OBJECTS = drive_line_buffer.$(OBJEXT) drive_line_buffer_OBJECTS = $(am_drive_line_buffer_OBJECTS) drive_line_buffer_DEPENDENCIES = ../src/libdiag.a \ @@ -200,6 +210,9 @@ test_grep_proc2_DEPENDENCIES = ../src/libdiag.a $(am__DEPENDENCIES_1) am_test_hist_source_OBJECTS = test_hist_source.$(OBJEXT) test_hist_source_OBJECTS = $(am_test_hist_source_OBJECTS) test_hist_source_DEPENDENCIES = ../src/libdiag.a $(am__DEPENDENCIES_1) +am_test_json_ptr_OBJECTS = test_json_ptr.$(OBJEXT) +test_json_ptr_OBJECTS = $(am_test_json_ptr_OBJECTS) +test_json_ptr_DEPENDENCIES = ../src/libdiag.a am_test_line_buffer2_OBJECTS = test_line_buffer2.$(OBJEXT) test_line_buffer2_OBJECTS = $(am_test_line_buffer2_OBJECTS) test_line_buffer2_DEPENDENCIES = ../src/libdiag.a @@ -247,6 +260,7 @@ am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(drive_data_scanner_SOURCES) $(drive_grep_proc_SOURCES) \ + $(drive_json_op_SOURCES) $(drive_json_ptr_walk_SOURCES) \ $(drive_line_buffer_SOURCES) $(drive_listview_SOURCES) \ $(drive_logfile_SOURCES) $(drive_mvwattrline_SOURCES) \ $(drive_readline_curses_SOURCES) $(drive_sequencer_SOURCES) \ @@ -256,11 +270,13 @@ SOURCES = $(drive_data_scanner_SOURCES) $(drive_grep_proc_SOURCES) \ $(test_auto_fd_SOURCES) $(test_auto_mem_SOURCES) \ $(test_bookmarks_SOURCES) $(test_concise_SOURCES) \ $(test_date_time_scanner_SOURCES) $(test_grep_proc2_SOURCES) \ - $(test_hist_source_SOURCES) $(test_line_buffer2_SOURCES) \ - $(test_log_accel_SOURCES) $(test_pcrepp_SOURCES) \ - $(test_top_status_SOURCES) $(test_yajlpp_SOURCES) + $(test_hist_source_SOURCES) $(test_json_ptr_SOURCES) \ + $(test_line_buffer2_SOURCES) $(test_log_accel_SOURCES) \ + $(test_pcrepp_SOURCES) $(test_top_status_SOURCES) \ + $(test_yajlpp_SOURCES) DIST_SOURCES = $(drive_data_scanner_SOURCES) \ - $(drive_grep_proc_SOURCES) $(drive_line_buffer_SOURCES) \ + $(drive_grep_proc_SOURCES) $(drive_json_op_SOURCES) \ + $(drive_json_ptr_walk_SOURCES) $(drive_line_buffer_SOURCES) \ $(drive_listview_SOURCES) $(drive_logfile_SOURCES) \ $(drive_mvwattrline_SOURCES) $(drive_readline_curses_SOURCES) \ $(drive_sequencer_SOURCES) $(drive_sql_SOURCES) \ @@ -270,9 +286,9 @@ DIST_SOURCES = $(drive_data_scanner_SOURCES) \ $(test_auto_mem_SOURCES) $(test_bookmarks_SOURCES) \ $(test_concise_SOURCES) $(test_date_time_scanner_SOURCES) \ $(test_grep_proc2_SOURCES) $(test_hist_source_SOURCES) \ - $(test_line_buffer2_SOURCES) $(test_log_accel_SOURCES) \ - $(test_pcrepp_SOURCES) $(test_top_status_SOURCES) \ - $(test_yajlpp_SOURCES) + $(test_json_ptr_SOURCES) $(test_line_buffer2_SOURCES) \ + $(test_log_accel_SOURCES) $(test_pcrepp_SOURCES) \ + $(test_top_status_SOURCES) $(test_yajlpp_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -664,10 +680,16 @@ test_yajlpp_SOURCES = test_yajlpp.cc test_yajlpp_LDADD = ../src/libdiag.a test_concise_SOURCES = test_concise.cc test_concise_LDADD = ../src/libdiag.a +test_json_ptr_SOURCES = test_json_ptr.cc +test_json_ptr_LDADD = ../src/libdiag.a drive_line_buffer_SOURCES = drive_line_buffer.cc drive_line_buffer_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz drive_grep_proc_SOURCES = drive_grep_proc.cc drive_grep_proc_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz +drive_json_op_SOURCES = drive_json_op.cc +drive_json_op_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz +drive_json_ptr_walk_SOURCES = drive_json_ptr_walk.cc +drive_json_ptr_walk_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz drive_listview_SOURCES = drive_listview.cc drive_listview_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz drive_logfile_SOURCES = drive_logfile.cc @@ -730,6 +752,7 @@ dist_noinst_SCRIPTS = \ test_sessions.sh \ test_sql.sh \ test_sql_coll_func.sh \ + test_sql_json_func.sh \ test_sql_str_func.sh \ test_sql_fs_func.sh \ test_view_colors.sh \ @@ -844,6 +867,14 @@ drive_grep_proc$(EXEEXT): $(drive_grep_proc_OBJECTS) $(drive_grep_proc_DEPENDENC @rm -f drive_grep_proc$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(drive_grep_proc_OBJECTS) $(drive_grep_proc_LDADD) $(LIBS) +drive_json_op$(EXEEXT): $(drive_json_op_OBJECTS) $(drive_json_op_DEPENDENCIES) $(EXTRA_drive_json_op_DEPENDENCIES) + @rm -f drive_json_op$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(drive_json_op_OBJECTS) $(drive_json_op_LDADD) $(LIBS) + +drive_json_ptr_walk$(EXEEXT): $(drive_json_ptr_walk_OBJECTS) $(drive_json_ptr_walk_DEPENDENCIES) $(EXTRA_drive_json_ptr_walk_DEPENDENCIES) + @rm -f drive_json_ptr_walk$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(drive_json_ptr_walk_OBJECTS) $(drive_json_ptr_walk_LDADD) $(LIBS) + drive_line_buffer$(EXEEXT): $(drive_line_buffer_OBJECTS) $(drive_line_buffer_DEPENDENCIES) $(EXTRA_drive_line_buffer_DEPENDENCIES) @rm -f drive_line_buffer$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(drive_line_buffer_OBJECTS) $(drive_line_buffer_LDADD) $(LIBS) @@ -920,6 +951,10 @@ test_hist_source$(EXEEXT): $(test_hist_source_OBJECTS) $(test_hist_source_DEPEND @rm -f test_hist_source$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_hist_source_OBJECTS) $(test_hist_source_LDADD) $(LIBS) +test_json_ptr$(EXEEXT): $(test_json_ptr_OBJECTS) $(test_json_ptr_DEPENDENCIES) $(EXTRA_test_json_ptr_DEPENDENCIES) + @rm -f test_json_ptr$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(test_json_ptr_OBJECTS) $(test_json_ptr_LDADD) $(LIBS) + test_line_buffer2$(EXEEXT): $(test_line_buffer2_OBJECTS) $(test_line_buffer2_DEPENDENCIES) $(EXTRA_test_line_buffer2_DEPENDENCIES) @rm -f test_line_buffer2$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(test_line_buffer2_OBJECTS) $(test_line_buffer2_LDADD) $(LIBS) @@ -948,6 +983,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_data_scanner.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_grep_proc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_json_op.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_json_ptr_walk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_line_buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_listview.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drive_logfile.Po@am__quote@ @@ -967,6 +1004,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_date_time_scanner.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_grep_proc2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hist_source.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_json_ptr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_line_buffer2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_log_accel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_pcrepp.Po@am__quote@ @@ -1287,6 +1325,20 @@ test_json_format.sh.log: test_json_format.sh --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +test_json_op.sh.log: test_json_op.sh + @p='test_json_op.sh'; \ + b='test_json_op.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test_json_ptr_walk.sh.log: test_json_ptr_walk.sh + @p='test_json_ptr_walk.sh'; \ + b='test_json_ptr_walk.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) test_log_accel.log: test_log_accel$(EXEEXT) @p='test_log_accel$(EXEEXT)'; \ b='test_log_accel'; \ @@ -1329,6 +1381,13 @@ test_sql_coll_func.sh.log: test_sql_coll_func.sh --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +test_sql_json_func.sh.log: test_sql_json_func.sh + @p='test_sql_json_func.sh'; \ + b='test_sql_json_func.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) test_sql_fs_func.sh.log: test_sql_fs_func.sh @p='test_sql_fs_func.sh'; \ b='test_sql_fs_func.sh'; \ diff --git a/test/drive_json_op.cc b/test/drive_json_op.cc new file mode 100644 index 00000000..575e76ae --- /dev/null +++ b/test/drive_json_op.cc @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file drive_json_op.cc + */ + +#include "config.h" + +#include + +#include "json_op.hh" + +static void printer(void *ctx, const char *numberVal, size_t numberLen) +{ + write(STDOUT_FILENO, numberVal, numberLen); +} + +static int handle_start_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_map_open(gen); + + return 1; +} + +static int handle_map_key(void *ctx, const unsigned char * key, size_t len) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_string(gen, key, len); + + return 1; +} + +static int handle_end_map(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_map_close(gen); + + return 1; +} + +static int handle_null(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_null(gen); + + return 1; +} + +static int handle_boolean(void *ctx, int boolVal) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_bool(gen, boolVal); + + return 1; +} + +static int handle_number(void *ctx, const char *numberVal, size_t numberLen) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_number(gen, numberVal, numberLen); + + return 1; +} + +static int handle_string(void *ctx, const unsigned char * stringVal, size_t len) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_string(gen, stringVal, len); + + return 1; +} + +static int handle_start_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_array_open(gen); + + return 1; +} + +static int handle_end_array(void *ctx) +{ + json_op *jo = (json_op *)ctx; + yajl_gen gen = (yajl_gen)jo->jo_ptr_data; + + yajl_gen_array_close(gen); + + return 1; +} + +int main(int argc, char *argv[]) +{ + int retval = EXIT_SUCCESS; + + log_argv(argc, argv); + + if (argc != 3) { + fprintf(stderr, "error: expecting operation and json-pointer\n"); + retval = EXIT_FAILURE; + } + else if (strcmp(argv[1], "get") == 0) { + unsigned char buffer[1024]; + json_ptr jptr(argv[2]); + json_op jo(jptr); + yajl_handle handle; + yajl_status status; + yajl_gen gen; + ssize_t rc; + + gen = yajl_gen_alloc(NULL); + yajl_gen_config(gen, yajl_gen_print_callback, printer, NULL); + yajl_gen_config(gen, yajl_gen_beautify, true); + + jo.jo_ptr_callbacks.yajl_start_map = handle_start_map; + jo.jo_ptr_callbacks.yajl_map_key = handle_map_key; + jo.jo_ptr_callbacks.yajl_end_map = handle_end_map; + jo.jo_ptr_callbacks.yajl_start_array = handle_start_array; + jo.jo_ptr_callbacks.yajl_end_array = handle_end_array; + jo.jo_ptr_callbacks.yajl_null = handle_null; + jo.jo_ptr_callbacks.yajl_boolean = handle_boolean; + jo.jo_ptr_callbacks.yajl_number = handle_number; + jo.jo_ptr_callbacks.yajl_string = handle_string; + jo.jo_ptr_data = gen; + + handle = yajl_alloc(&json_op::ptr_callbacks, NULL, &jo); + while ((rc = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { + status = yajl_parse(handle, buffer, rc); + if (status == yajl_status_error) { + fprintf(stderr, "error:cannot parse JSON input -- %s\n", + yajl_get_error(handle, 1, buffer, rc)); + retval = EXIT_FAILURE; + break; + } + else if (status == yajl_status_client_canceled) { + fprintf(stderr, "client cancel\n"); + break; + } + } + status = yajl_complete_parse(handle); + if (status == yajl_status_error) { + fprintf(stderr, "error:cannot parse JSON input -- %s\n", + yajl_get_error(handle, 1, buffer, rc)); + retval = EXIT_FAILURE; + } + else if (status == yajl_status_client_canceled) { + fprintf(stderr, "client cancel\n"); + } + yajl_free(handle); + } + else { + fprintf(stderr, "error: unknown operation -- %s\n", argv[1]); + retval = EXIT_FAILURE; + } + + return retval; +} diff --git a/test/drive_json_ptr_walk.cc b/test/drive_json_ptr_walk.cc new file mode 100644 index 00000000..72835e7b --- /dev/null +++ b/test/drive_json_ptr_walk.cc @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2014, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TIMOTHY STACK AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file drive_json_ptr_dump.cc + */ + +#include "config.h" + +#include + +#include "json_ptr.hh" + +int main(int argc, char *argv[]) +{ + int retval = EXIT_SUCCESS; + + char buffer[1024]; + yajl_status status; + json_ptr_walk jpw; + ssize_t rc; + + log_argv(argc, argv); + + while ((rc = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { + status = jpw.parse(buffer, rc); + if (status == yajl_status_error) { + fprintf(stderr, "error:cannot parse JSON input -- %s\n", + jpw.jpw_error_msg.c_str()); + retval = EXIT_FAILURE; + break; + } + else if (status == yajl_status_client_canceled) { + fprintf(stderr, "client cancel\n"); + break; + } + } + status = jpw.complete_parse(); + if (status == yajl_status_error) { + fprintf(stderr, "error:cannot parse JSON input -- %s\n", + jpw.jpw_error_msg.c_str()); + retval = EXIT_FAILURE; + } + else if (status == yajl_status_client_canceled) { + fprintf(stderr, "client cancel\n"); + } + + for (json_ptr_walk::pair_list_t::iterator iter = jpw.jpw_values.begin(); + iter != jpw.jpw_values.end(); + ++iter) { + printf("%s = %s\n", iter->first.c_str(), iter->second.c_str()); + } + + return retval; +} diff --git a/test/drive_sql.cc b/test/drive_sql.cc index 8dc26afb..d5ecb9fe 100644 --- a/test/drive_sql.cc +++ b/test/drive_sql.cc @@ -37,6 +37,8 @@ int main(int argc, char *argv[]) int retval = EXIT_SUCCESS; auto_mem db(sqlite3_close); + log_argv(argc, argv); + if (argc != 2) { fprintf(stderr, "error: expecting an SQL statement\n"); retval = EXIT_FAILURE; diff --git a/test/test_json_op.sh b/test/test_json_op.sh new file mode 100644 index 00000000..6bc136da --- /dev/null +++ b/test/test_json_op.sh @@ -0,0 +1,94 @@ +#! /bin/bash + +run_test ./drive_json_op get "" < +#include +#include + +#include "json_ptr.hh" + +int main(int argc, const char *argv[]) +{ + int32_t depth, index; + + { + json_ptr jptr(""); + + depth = 0; + index = -1; + assert(jptr.at_index(depth, index)); + } + + { + json_ptr jptr("/"); + + depth = 0; + index = -1; + assert(!jptr.at_index(depth, index)); + assert(jptr.expect_map(depth)); + assert(jptr.at_index(depth, index)); + } + + { + json_ptr jptr("/foo/bar"); + + depth = 0; + index = -1; + assert(jptr.expect_map(depth)); + assert(jptr.at_key(depth, "foo")); + assert(jptr.expect_map(depth)); + assert(jptr.at_key(depth, "bar")); + assert(jptr.at_index(depth, index)); + } +} diff --git a/test/test_json_ptr_walk.sh b/test/test_json_ptr_walk.sh new file mode 100644 index 00000000..1b19a28e --- /dev/null +++ b/test/test_json_ptr_walk.sh @@ -0,0 +1,67 @@ +#! /bin/bash + +run_test ./drive_json_ptr_walk <