[monitoring-plugins] Implement new output functionality
Lorenz Kästle
git at monitoring-plugins.org
Fri Feb 21 14:40:11 CET 2025
Module: monitoring-plugins
Branch: master
Commit: 0645c9fc2c7f801ba3c7d68a17c137a63ada299f
Author: Lorenz Kästle <12514511+RincewindsHat at users.noreply.github.com>
Date: Tue Feb 18 21:58:34 2025 +0100
URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=0645c9fc
Implement new output functionality
---
configure.ac | 13 +
lib/Makefile.am | 5 +-
lib/monitoringplug.h | 8 +
lib/output.c | 464 ++++++
lib/output.h | 87 ++
lib/perfdata.c | 516 +++++++
lib/perfdata.h | 203 +++
lib/states.h | 71 +
lib/tests/Makefile.am | 6 +-
lib/tests/test_generic_output.c | 315 ++++
lib/tests/test_generic_output.t | 6 +
lib/thresholds.c | 59 +
lib/thresholds.h | 28 +
lib/utils_base.c | 167 +-
lib/utils_base.h | 26 +-
lib/vendor/cJSON/.deps/.dirstamp | 0
lib/vendor/cJSON/.deps/cJSON.Po | 170 ++
lib/vendor/cJSON/.dirstamp | 0
lib/vendor/cJSON/cJSON.c | 3165 ++++++++++++++++++++++++++++++++++++++
lib/vendor/cJSON/cJSON.h | 306 ++++
plugins/common.h | 9 +-
plugins/popen.c | 1 -
plugins/runcmd.c | 1 +
plugins/sslutils.c | 1 +
plugins/utils.c | 48 -
plugins/utils.h | 11 +-
26 files changed, 5551 insertions(+), 135 deletions(-)
diff --git a/configure.ac b/configure.ac
index 0432336b..b12cf6c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -190,6 +190,19 @@ if test "$enable_libtap" = "yes" ; then
AC_SUBST(EXTRA_PLUGIN_TESTS)
fi
+dnl JSON capabilities (cjson)
+AC_ARG_ENABLE([json-output],
+ AC_HELP_STRING([--enable-json-output],
+ [Enables switching the output format to JSON (default: yes)]),
+ [case "${enableval}" in
+ yes) json-ouput=true ;;
+ no) json-ouput=false ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-json-ouput]) ;;
+ esac], [json_output=true])
+
+AM_CONDITIONAL([ENALBE_JSON_OUTPUT], [test x$json_output = xtrue])
+
+
dnl INI Parsing
AC_ARG_ENABLE(extra-opts,
AC_HELP_STRING([--enable-extra-opts],
diff --git a/lib/Makefile.am b/lib/Makefile.am
index dc3ee893..0ea33dd8 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -7,8 +7,8 @@ noinst_LIBRARIES = libmonitoringplug.a
AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
-I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
-libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c
-EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h
+libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c
+EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h perfdata.h output.h thresholds.h states.h vendor/cJSON/cJSON.h
if USE_PARSE_INI
libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c
@@ -16,4 +16,3 @@ endif USE_PARSE_INI
test test-debug:
cd tests && make $@
-
diff --git a/lib/monitoringplug.h b/lib/monitoringplug.h
new file mode 100644
index 00000000..5f05a974
--- /dev/null
+++ b/lib/monitoringplug.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "./states.h"
+#include "./utils.h"
+#include "./utils_base.h"
+#include "./thresholds.h"
+#include "./maxfd.h"
+#include "./output.h"
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 00000000..9ba049e2
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,464 @@
+#include "./output.h"
+#include "./utils_base.h"
+#include "../plugins/utils.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+// #include <cjson/cJSON.h>
+#include "./vendor/cJSON/cJSON.h"
+
+// == Prototypes ==
+static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation);
+static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
+
+// == Implementation ==
+
+/*
+ * Generate output string for a mp_subcheck object
+ */
+static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
+ char *result = strdup("");
+ int added = 0;
+
+ if (check.perfdata != NULL) {
+ added = xasprintf(&result, "%s", pd_list_to_string(*check.perfdata));
+ }
+
+ if (check.subchecks == NULL) {
+ // No subchecks, return here
+ return result;
+ }
+
+ mp_subcheck_list *subchecks = check.subchecks;
+
+ while (subchecks != NULL) {
+ if (added > 0) {
+ added = xasprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
+ } else {
+ // TODO free previous result here?
+ added = xasprintf(&result, "%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
+ }
+
+ subchecks = subchecks->next;
+ }
+
+ return result;
+}
+
+/*
+ * Initialiser for a mp_check object. Always use this to get a new one!
+ * It sets useful defaults
+ */
+mp_check mp_check_init(void) {
+ mp_check check = {0};
+ check.format = MP_FORMAT_DEFAULT;
+ return check;
+}
+
+/*
+ * Initialiser for a mp_subcheck object. Always use this to get a new one!
+ * It sets useful defaults
+ */
+mp_subcheck mp_subcheck_init(void) {
+ mp_subcheck tmp = {0};
+ tmp.default_state = STATE_UNKNOWN; // Default state is unknown
+ tmp.state_set_explicitly = false;
+ return tmp;
+}
+
+/*
+ * Add a subcheck to a (the one and only) check object
+ */
+int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
+ assert(subcheck.output != NULL); // There must be output in a subcheck
+
+ mp_subcheck_list *tmp = NULL;
+
+ if (check->subchecks == NULL) {
+ check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
+ if (check->subchecks == NULL) {
+ die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
+ }
+
+ check->subchecks->subcheck = subcheck;
+ check->subchecks->next = NULL;
+ } else {
+ // Search for the end
+ tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
+ if (tmp == NULL) {
+ die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
+ }
+
+ tmp->subcheck = subcheck;
+ tmp->next = check->subchecks;
+
+ check->subchecks = tmp;
+ }
+
+ return 0;
+}
+
+/*
+ * Add a mp_perfdata data point to a mp_subcheck object
+ */
+void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
+ if (check->perfdata == NULL) {
+ check->perfdata = pd_list_init();
+ }
+ pd_list_append(check->perfdata, perfData);
+}
+
+/*
+ * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
+ * hierarchy
+ */
+int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
+ if (subcheck.output == NULL) {
+ die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL");
+ }
+
+ mp_subcheck_list *tmp = NULL;
+
+ if (check->subchecks == NULL) {
+ check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
+ if (check->subchecks == NULL) {
+ die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
+ }
+
+ tmp = check->subchecks;
+ } else {
+ // Search for the end
+ tmp = check->subchecks;
+
+ while (tmp->next != NULL) {
+ tmp = tmp->next;
+ }
+
+ tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
+ if (tmp->next == NULL) {
+ die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
+ }
+
+ tmp = tmp->next;
+ }
+
+ tmp->subcheck = subcheck;
+
+ return 0;
+}
+
+/*
+ * Add a manual summary to a mp_check object, effectively replacing
+ * the autogenerated one
+ */
+void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
+
+/*
+ * Generate the summary string of a mp_check object based on it's subchecks
+ */
+char *get_subcheck_summary(mp_check check) {
+ mp_subcheck_list *subchecks = check.subchecks;
+
+ unsigned int ok = 0;
+ unsigned int warning = 0;
+ unsigned int critical = 0;
+ unsigned int unknown = 0;
+ while (subchecks != NULL) {
+ switch (subchecks->subcheck.state) {
+ case STATE_OK:
+ ok++;
+ break;
+ case STATE_WARNING:
+ warning++;
+ break;
+ case STATE_CRITICAL:
+ critical++;
+ break;
+ case STATE_UNKNOWN:
+ unknown++;
+ break;
+ default:
+ die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
+ }
+ subchecks = subchecks->next;
+ }
+ char *result = NULL;
+ xasprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
+ return result;
+}
+
+/*
+ * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states
+ */
+mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
+ if (check.state_set_explicitly) {
+ return check.state;
+ }
+
+ mp_subcheck_list *scl = check.subchecks;
+ mp_state_enum result = check.default_state;
+
+ while (scl != NULL) {
+ result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
+ scl = scl->next;
+ }
+
+ return result;
+}
+
+/*
+ * Generate the result state of a mp_check object based on it's own state and it's subchecks states
+ */
+mp_state_enum mp_compute_check_state(const mp_check check) {
+ assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
+
+ mp_subcheck_list *scl = check.subchecks;
+ mp_state_enum result = STATE_OK;
+
+ while (scl != NULL) {
+ result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
+ scl = scl->next;
+ }
+
+ return result;
+}
+
+/*
+ * Generate output string for a mp_check object
+ * Non static to be available for testing functions
+ */
+char *mp_fmt_output(mp_check check) {
+ char *result = NULL;
+
+ switch (check.format) {
+ case MP_FORMAT_SUMMARY_ONLY:
+ if (check.summary == NULL) {
+ check.summary = get_subcheck_summary(check);
+ }
+
+ xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary);
+ return result;
+
+ case MP_FORMAT_ONE_LINE: {
+ /* SERVICE STATUS: First line of output | First part of performance data
+ * Any number of subsequent lines of output, but note that buffers
+ * may have a limited size | Second part of performance data, which
+ * may have continuation lines, too
+ */
+ if (check.summary == NULL) {
+ check.summary = get_subcheck_summary(check);
+ }
+
+ xasprintf(&result, "%s: %s", state_text(mp_compute_check_state(check)), check.summary);
+
+ mp_subcheck_list *subchecks = check.subchecks;
+
+ while (subchecks != NULL) {
+ xasprintf(&result, "%s - %s", result, fmt_subcheck_output(MP_FORMAT_ONE_LINE, subchecks->subcheck, 1));
+ subchecks = subchecks->next;
+ }
+
+ break;
+ }
+ case MP_FORMAT_ICINGA_WEB_2: {
+ if (check.summary == NULL) {
+ check.summary = get_subcheck_summary(check);
+ }
+
+ xasprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
+
+ mp_subcheck_list *subchecks = check.subchecks;
+
+ while (subchecks != NULL) {
+ xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_ICINGA_WEB_2, subchecks->subcheck, 1));
+ subchecks = subchecks->next;
+ }
+
+ char *pd_string = NULL;
+ subchecks = check.subchecks;
+
+ while (subchecks != NULL) {
+ if (pd_string == NULL) {
+ xasprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
+ } else {
+ xasprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck));
+ }
+
+ subchecks = subchecks->next;
+ }
+
+ if (pd_string != NULL && strlen(pd_string) > 0) {
+ xasprintf(&result, "%s|%s", result, pd_string);
+ }
+
+ break;
+ }
+ case MP_FORMAT_TEST_JSON: {
+ cJSON *resultObject = cJSON_CreateObject();
+ if (resultObject == NULL) {
+ die(STATE_UNKNOWN, "cJSON_CreateObject failed");
+ }
+
+ cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
+ cJSON_AddItemToObject(resultObject, "state", resultState);
+
+ if (check.summary == NULL) {
+ check.summary = get_subcheck_summary(check);
+ }
+
+ cJSON *summary = cJSON_CreateString(check.summary);
+ cJSON_AddItemToObject(resultObject, "summary", summary);
+
+ if (check.subchecks != NULL) {
+ cJSON *subchecks = cJSON_CreateArray();
+
+ mp_subcheck_list *sc = check.subchecks;
+
+ while (sc != NULL) {
+ cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
+ cJSON_AddItemToArray(subchecks, sc_json);
+ sc = sc->next;
+ }
+
+ cJSON_AddItemToObject(resultObject, "checks", subchecks);
+ }
+
+ result = cJSON_PrintUnformatted(resultObject);
+ break;
+ }
+ default:
+ die(STATE_UNKNOWN, "Invalid format");
+ }
+
+ return result;
+}
+
+/*
+ * Helper function to properly indent the output lines when using multiline
+ * formats
+ */
+static char *generate_indentation_string(unsigned int indentation) {
+ char *result = calloc(indentation + 1, sizeof(char));
+
+ for (unsigned int i = 0; i < indentation; i++) {
+ result[i] = '\t';
+ }
+
+ return result;
+}
+
+/*
+ * Helper function to generate the output string of mp_subcheck
+ */
+static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) {
+ char *result = NULL;
+
+ switch (output_format) {
+ case MP_FORMAT_ICINGA_WEB_2:
+ xasprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)),
+ check.output);
+
+ mp_subcheck_list *subchecks = check.subchecks;
+
+ while (subchecks != NULL) {
+ xasprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
+ subchecks = subchecks->next;
+ }
+ return result;
+ case MP_FORMAT_ONE_LINE:
+ return result;
+ case MP_FORMAT_SUMMARY_ONLY:
+ return result;
+ default:
+ die(STATE_UNKNOWN, "Invalid format");
+ }
+}
+
+static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
+ cJSON *result = cJSON_CreateObject();
+ cJSON *output = cJSON_CreateString(subcheck.output);
+ cJSON_AddItemToObject(result, "output", output);
+ cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
+ cJSON_AddItemToObject(result, "state", state);
+
+ if (subcheck.subchecks != NULL) {
+ cJSON *subchecks = cJSON_CreateArray();
+
+ mp_subcheck_list *sc = subcheck.subchecks;
+
+ while (sc != NULL) {
+ cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
+ cJSON_AddItemToArray(subchecks, sc_json);
+ sc = sc->next;
+ }
+
+ cJSON_AddItemToObject(result, "checks", subchecks);
+ }
+
+ return result;
+}
+
+/*
+ * Wrapper function to print the output string of a mp_check object
+ * Use this in concrete plugins.
+ */
+void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
+
+/*
+ * Convenience function to print the output string of a mp_check object and exit
+ * the program with the resulting state.
+ * Intended to be used to exit a monitoring plugin.
+ */
+void mp_exit(mp_check check) {
+ mp_print_output(check);
+ exit(mp_compute_check_state(check));
+}
+
+/*
+ * Function to set the result state of a mp_subcheck object explicitly.
+ * This will overwrite the default state AND states derived from it's subchecks
+ */
+mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
+ check.state = state;
+ check.state_set_explicitly = true;
+ return check;
+}
+
+/*
+ * Function to set the default result state of a mp_subcheck object. This state
+ * will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
+ * nor does it include other subchecks
+ */
+mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
+ check.default_state = state;
+ return check;
+}
+
+char *mp_output_format_map[] = {
+ [MP_FORMAT_ONE_LINE] = "one-line",
+ [MP_FORMAT_ICINGA_WEB_2] = "icingaweb2",
+ [MP_FORMAT_SUMMARY_ONLY] = "summary-only",
+ [MP_FORMAT_TEST_JSON] = "mp-test-json",
+};
+
+/*
+ * Function to parse the output from a string
+ */
+parsed_output_format mp_parse_output_format(char *format_string) {
+ parsed_output_format result = {
+ .parsing_success = false,
+ .output_format = MP_FORMAT_DEFAULT,
+ };
+
+ for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
+ if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
+ result.parsing_success = true;
+ result.output_format = i;
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/lib/output.h b/lib/output.h
new file mode 100644
index 00000000..c7455d29
--- /dev/null
+++ b/lib/output.h
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "../config.h"
+#include "./perfdata.h"
+#include "./states.h"
+
+/*
+ * A partial check result
+ */
+typedef struct {
+ mp_state_enum state; // OK, Warning, Critical ... set explicitly
+ mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
+ bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks)
+
+ char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..")
+ pd_list *perfdata; // Performance data for this check
+ struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
+} mp_subcheck;
+
+/*
+ * A list of subchecks, used in subchecks and the main check
+ */
+typedef struct subcheck_list {
+ mp_subcheck subcheck;
+ struct subcheck_list *next;
+} mp_subcheck_list;
+
+/*
+ * Possible output formats
+ */
+typedef enum output_format {
+ MP_FORMAT_ONE_LINE,
+ MP_FORMAT_ICINGA_WEB_2,
+ MP_FORMAT_SUMMARY_ONLY,
+ MP_FORMAT_TEST_JSON,
+} mp_output_format;
+
+#define MP_FORMAT_DEFAULT MP_FORMAT_ICINGA_WEB_2
+
+/*
+ * The main state object of a plugin. Exists only ONCE per plugin.
+ * This is the "root" of a tree of singular checks.
+ * The final result is always derived from the children and the "worst" state
+ * in the first layer of subchecks
+ */
+typedef struct {
+ mp_output_format format; // The output format
+ char *summary; // Overall summary, if not set a summary will be automatically generated
+ mp_subcheck_list *subchecks;
+} mp_check;
+
+mp_check mp_check_init(void);
+mp_subcheck mp_subcheck_init(void);
+
+mp_subcheck mp_set_subcheck_state(mp_subcheck, mp_state_enum);
+mp_subcheck mp_set_subcheck_default_state(mp_subcheck, mp_state_enum);
+
+int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck);
+int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck);
+
+void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata);
+
+void mp_add_summary(mp_check check[static 1], char *summary);
+
+mp_state_enum mp_compute_check_state(mp_check);
+mp_state_enum mp_compute_subcheck_state(mp_subcheck);
+
+typedef struct {
+ bool parsing_success;
+ mp_output_format output_format;
+} parsed_output_format;
+parsed_output_format mp_parse_output_format(char *format_string);
+
+// TODO free and stuff
+// void mp_cleanup_check(mp_check check[static 1]);
+
+char *mp_fmt_output(mp_check);
+
+void mp_print_output(mp_check);
+
+/*
+ * ==================
+ * Exit functionality
+ * ==================
+ */
+
+void mp_exit(mp_check) __attribute__((noreturn));
diff --git a/lib/perfdata.c b/lib/perfdata.c
new file mode 100644
index 00000000..f894df39
--- /dev/null
+++ b/lib/perfdata.c
@@ -0,0 +1,516 @@
+#include "./perfdata.h"
+#include "../plugins/common.h"
+#include "../plugins/utils.h"
+#include "utils_base.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+char *pd_value_to_string(const mp_perfdata_value pd) {
+ char *result = NULL;
+
+ assert(pd.type != PD_TYPE_NONE);
+
+ switch (pd.type) {
+ case PD_TYPE_INT:
+ xasprintf(&result, "%lli", pd.pd_int);
+ break;
+ case PD_TYPE_UINT:
+ xasprintf(&result, "%llu", pd.pd_int);
+ break;
+ case PD_TYPE_DOUBLE:
+ xasprintf(&result, "%f", pd.pd_double);
+ break;
+ default:
+ // die here
+ die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n");
+ }
+
+ return result;
+}
+
+char *pd_to_string(mp_perfdata pd) {
+ assert(pd.label != NULL);
+ char *result = NULL;
+ xasprintf(&result, "%s=", pd.label);
+
+ xasprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
+
+ if (pd.uom != NULL) {
+ xasprintf(&result, "%s%s", result, pd.uom);
+ }
+
+ if (pd.warn_present) {
+ xasprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn));
+ } else {
+ xasprintf(&result, "%s;", result);
+ }
+
+ if (pd.crit_present) {
+ xasprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit));
+ } else {
+ xasprintf(&result, "%s;", result);
+ }
+ if (pd.min_present) {
+ xasprintf(&result, "%s;%s", result, pd_value_to_string(pd.min));
+ } else {
+ xasprintf(&result, "%s;", result);
+ }
+
+ if (pd.max_present) {
+ xasprintf(&result, "%s;%s", result, pd_value_to_string(pd.max));
+ }
+
+ /*printf("pd_to_string: %s\n", result); */
+
+ return result;
+}
+
+char *pd_list_to_string(const pd_list pd) {
+ char *result = pd_to_string(pd.data);
+
+ for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) {
+ xasprintf(&result, "%s %s", result, pd_to_string(elem->data));
+ }
+
+ return result;
+}
+
+mp_perfdata perfdata_init() {
+ mp_perfdata pd = {};
+ return pd;
+}
+
+pd_list *pd_list_init() {
+ pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list));
+ if (tmp == NULL) {
+ die(STATE_UNKNOWN, "calloc failed\n");
+ }
+ tmp->next = NULL;
+ return tmp;
+}
+
+mp_range mp_range_init() {
+ mp_range result = {
+ .alert_on_inside_range = OUTSIDE,
+ .start = {},
+ .start_infinity = true,
+ .end = {},
+ .end_infinity = true,
+ };
+
+ return result;
+}
+
+mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) {
+ input.start = perf_val;
+ input.start_infinity = false;
+ return input;
+}
+
+mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) {
+ input.end = perf_val;
+ input.end_infinity = false;
+ return input;
+}
+
+void pd_list_append(pd_list pdl[1], const mp_perfdata pd) {
+ assert(pdl != NULL);
+
+ if (pdl->data.value.type == PD_TYPE_NONE) {
+ // first entry is still empty
+ pdl->data = pd;
+ } else {
+ // find last element in the list
+ pd_list *curr = pdl;
+ pd_list *next = pdl->next;
+
+ while (next != NULL) {
+ curr = next;
+ next = next->next;
+ }
+
+ if (curr->data.value.type == PD_TYPE_NONE) {
+ // still empty
+ curr->data = pd;
+ } else {
+ // new a new one
+ curr->next = pd_list_init();
+ curr->next->data = pd;
+ }
+ }
+}
+
+void pd_list_free(pd_list pdl[1]) {
+ while (pdl != NULL) {
+ pd_list *old = pdl;
+ pdl = pdl->next;
+ free(old);
+ }
+}
+
+/*
+ * returns -1 if a < b, 0 if a == b, 1 if a > b
+ */
+int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) {
+ // Test if types are different
+ if (a.type == b.type) {
+
+ switch (a.type) {
+ case PD_TYPE_UINT:
+ if (a.pd_uint < b.pd_uint) {
+ return -1;
+ } else if (a.pd_uint == b.pd_uint) {
+ return 0;
+ } else {
+ return 1;
+ }
+ break;
+ case PD_TYPE_INT:
+ if (a.pd_int < b.pd_int) {
+ return -1;
+ } else if (a.pd_int == b.pd_int) {
+ return 0;
+ } else {
+ return 1;
+ }
+ break;
+ case PD_TYPE_DOUBLE:
+ if (a.pd_int < b.pd_int) {
+ return -1;
+ } else if (a.pd_int == b.pd_int) {
+ return 0;
+ } else {
+ return 1;
+ }
+ break;
+ default:
+ die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
+ }
+ }
+
+ // Get dirty here
+ long double floating_a = 0;
+
+ switch (a.type) {
+ case PD_TYPE_UINT:
+ floating_a = a.pd_uint;
+ break;
+ case PD_TYPE_INT:
+ floating_a = a.pd_int;
+ break;
+ case PD_TYPE_DOUBLE:
+ floating_a = a.pd_double;
+ break;
+ default:
+ die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
+ }
+
+ long double floating_b = 0;
+ switch (b.type) {
+ case PD_TYPE_UINT:
+ floating_b = b.pd_uint;
+ break;
+ case PD_TYPE_INT:
+ floating_b = b.pd_int;
+ break;
+ case PD_TYPE_DOUBLE:
+ floating_b = b.pd_double;
+ break;
+ default:
+ die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
+ }
+
+ if (floating_a < floating_b) {
+ return -1;
+ }
+ if (floating_a == floating_b) {
+ return 0;
+ }
+ return 1;
+}
+
+char *mp_range_to_string(const mp_range input) {
+ char *result = "";
+ if (input.alert_on_inside_range == INSIDE) {
+ xasprintf(&result, "@");
+ }
+
+ if (input.start_infinity) {
+ xasprintf(&result, "%s~:", result);
+ } else {
+ xasprintf(&result, "%s%s:", result, pd_value_to_string(input.start));
+ }
+
+ if (!input.end_infinity) {
+ xasprintf(&result, "%s%s", result, pd_value_to_string(input.end));
+ }
+ return result;
+}
+
+mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); }
+
+mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
+ pd.value.pd_double = value;
+ pd.value.type = PD_TYPE_DOUBLE;
+ return pd;
+}
+
+mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); }
+
+mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
+
+mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); }
+
+mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) {
+ return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
+}
+
+mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) {
+ pd.value.pd_int = value;
+ pd.value.type = PD_TYPE_INT;
+ return pd;
+}
+
+mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) {
+ pd.value.pd_uint = value;
+ pd.value.type = PD_TYPE_UINT;
+ return pd;
+}
+
+mp_perfdata_value mp_create_pd_value_double(double value) {
+ mp_perfdata_value res = {0};
+ res.type = PD_TYPE_DOUBLE;
+ res.pd_double = value;
+ return res;
+}
+
+mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); }
+
+mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); }
+
+mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
+
+mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); }
+
+mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
+
+mp_perfdata_value mp_create_pd_value_long_long(long long value) {
+ mp_perfdata_value res = {0};
+ res.type = PD_TYPE_INT;
+ res.pd_int = value;
+ return res;
+}
+
+mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) {
+ mp_perfdata_value res = {0};
+ res.type = PD_TYPE_UINT;
+ res.pd_uint = value;
+ return res;
+}
+
+char *fmt_range(range foo) { return foo.text; }
+
+typedef struct integer_parser_wrapper {
+ int error;
+ mp_perfdata_value value;
+} integer_parser_wrapper;
+
+typedef struct double_parser_wrapper {
+ int error;
+ mp_perfdata_value value;
+} double_parser_wrapper;
+
+typedef struct perfdata_value_parser_wrapper {
+ int error;
+ mp_perfdata_value value;
+} perfdata_value_parser_wrapper;
+
+double_parser_wrapper parse_double(const char *input);
+integer_parser_wrapper parse_integer(const char *input);
+perfdata_value_parser_wrapper parse_pd_value(const char *input);
+
+mp_range_parsed mp_parse_range_string(const char *input) {
+ if (input == NULL) {
+ mp_range_parsed result = {
+ .error = MP_RANGE_PARSING_FAILURE,
+ };
+ return result;
+ }
+
+ if (strlen(input) == 0) {
+ mp_range_parsed result = {
+ .error = MP_RANGE_PARSING_FAILURE,
+ };
+ return result;
+ }
+
+ mp_range_parsed result = {
+ .range = mp_range_init(),
+ .error = MP_PARSING_SUCCES,
+ };
+
+ if (input[0] == '@') {
+ // found an '@' at beginning, so invert the range logic
+ result.range.alert_on_inside_range = INSIDE;
+
+ // advance the pointer one symbol
+ input++;
+ }
+
+ char *working_copy = strdup(input);
+ input = working_copy;
+
+ char *separator = index(working_copy, ':');
+ if (separator != NULL) {
+ // Found a separator
+ // set the separator to 0, so we have two different strings
+ *separator = '\0';
+
+ if (input[0] == '~') {
+ // the beginning starts with '~', so it might be infinity
+ if (&input[1] != separator) {
+ // the next symbol after '~' is not the separator!
+ // so input is probably wrong
+ result.error = MP_RANGE_PARSING_FAILURE;
+ free(working_copy);
+ return result;
+ }
+
+ result.range.start_infinity = true;
+ } else {
+ // No '~' at the beginning, so this should be a number
+ result.range.start_infinity = false;
+ perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
+
+ if (parsed_pd.error != MP_PARSING_SUCCES) {
+ result.error = parsed_pd.error;
+ free(working_copy);
+ return result;
+ }
+
+ result.range.start = parsed_pd.value;
+ result.range.start_infinity = false;
+ }
+ // got the first part now
+ // advance the pointer
+ input = separator + 1;
+ }
+
+ // End part or no separator
+ if (input[0] == '\0') {
+ // the end is infinite
+ result.range.end_infinity = true;
+ } else {
+ perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
+
+ if (parsed_pd.error != MP_PARSING_SUCCES) {
+ result.error = parsed_pd.error;
+ return result;
+ }
+ result.range.end = parsed_pd.value;
+ result.range.end_infinity = false;
+ }
+ free(working_copy);
+ return result;
+}
+
+double_parser_wrapper parse_double(const char *input) {
+ double_parser_wrapper result = {
+ .error = MP_PARSING_SUCCES,
+ };
+
+ if (input == NULL) {
+ result.error = MP_PARSING_FAILURE;
+ return result;
+ }
+
+ char *endptr = NULL;
+ errno = 0;
+ double tmp = strtod(input, &endptr);
+
+ if (input == endptr) {
+ // man 3 strtod says, no conversion performed
+ result.error = MP_PARSING_FAILURE;
+ return result;
+ }
+
+ if (errno) {
+ // some other error
+ // TODO maybe differentiate a little bit
+ result.error = MP_PARSING_FAILURE;
+ return result;
+ }
+
+ result.value = mp_create_pd_value(tmp);
+ return result;
+}
+
+integer_parser_wrapper parse_integer(const char *input) {
+ integer_parser_wrapper result = {
+ .error = MP_PARSING_SUCCES,
+ };
+
+ if (input == NULL) {
+ result.error = MP_PARSING_FAILURE;
+ return result;
+ }
+
+ char *endptr = NULL;
+ errno = 0;
+ long long tmp = strtoll(input, &endptr, 0);
+
+ // validating *sigh*
+ if (*endptr != '\0') {
+ // something went wrong in strtoll
+ if (tmp == LLONG_MIN) {
+ // underflow
+ result.error = MP_RANGE_PARSING_UNDERFLOW;
+ return result;
+ }
+
+ if (tmp == LLONG_MAX) {
+ // overflow
+ result.error = MP_RANGE_PARSING_OVERFLOW;
+ return result;
+ }
+
+ // still wrong, but not sure why, probably invalid characters
+ if (errno == EINVAL) {
+ result.error = MP_RANGE_PARSING_INVALID_CHAR;
+ return result;
+ }
+
+ // some other error, do catch all here
+ result.error = MP_RANGE_PARSING_FAILURE;
+ return result;
+ }
+
+ // no error, should be fine
+ result.value = mp_create_pd_value(tmp);
+ return result;
+}
+
+perfdata_value_parser_wrapper parse_pd_value(const char *input) {
+ // try integer first
+ integer_parser_wrapper tmp_int = parse_integer(input);
+
+ if (tmp_int.error == MP_PARSING_SUCCES) {
+ perfdata_value_parser_wrapper result = {
+ .error = tmp_int.error,
+ .value = tmp_int.value,
+ };
+ return result;
+ }
+
+ double_parser_wrapper tmp_double = parse_double(input);
+ perfdata_value_parser_wrapper result = {};
+ if (tmp_double.error == MP_PARSING_SUCCES) {
+ result.error = tmp_double.error;
+ result.value = tmp_double.value;
+ } else {
+ result.error = tmp_double.error;
+ }
+ return result;
+}
diff --git a/lib/perfdata.h b/lib/perfdata.h
new file mode 100644
index 00000000..74583ee5
--- /dev/null
+++ b/lib/perfdata.h
@@ -0,0 +1,203 @@
+#pragma once
+
+#include "../config.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+// Enum for the specific type of a perfdata_value
+typedef enum pd_value_type {
+ PD_TYPE_NONE = 0,
+ PD_TYPE_INT,
+ PD_TYPE_UINT,
+ PD_TYPE_DOUBLE
+} pd_value_type;
+
+typedef struct {
+ enum pd_value_type type;
+ union {
+ long long pd_int;
+ unsigned long long pd_uint;
+ double pd_double;
+ };
+} mp_perfdata_value;
+
+#define MP_OUTSIDE false
+#define MP_INSIDE true
+
+/*
+ * New range type with generic numerical values
+ */
+typedef struct mp_range_struct {
+ mp_perfdata_value start;
+ bool start_infinity; /* false (default) or true */
+
+ mp_perfdata_value end;
+ bool end_infinity;
+
+ bool alert_on_inside_range; /* OUTSIDE (default) or INSIDE */
+} mp_range;
+
+/*
+ * Old range type with floating point values
+ */
+typedef struct range_struct {
+ double start;
+ bool start_infinity;
+ double end;
+ int end_infinity;
+ int alert_on; /* OUTSIDE (default) or INSIDE */
+ char *text; /* original unparsed text input */
+} range;
+
+/*
+ * Perfdata type for storing perfdata output
+ */
+typedef struct perfdata_struct {
+ char *label;
+ char *uom;
+ mp_perfdata_value value;
+
+ bool warn_present;
+ mp_range warn;
+
+ bool crit_present;
+ mp_range crit;
+
+ bool min_present;
+ mp_perfdata_value min;
+
+ bool max_present;
+ mp_perfdata_value max;
+} mp_perfdata;
+
+/*
+ * List of mp_perfdata values
+ */
+typedef struct pd_list_struct {
+ mp_perfdata data;
+ struct pd_list_struct *next;
+} pd_list;
+
+/*
+ * ============
+ * Initializers
+ * ============
+ */
+/*
+ * Initialize mp_perfdata value. Always use this to generate a new one
+ */
+mp_perfdata perfdata_init(void);
+
+/*
+ * Initialize pd_list value. Always use this to generate a new one
+ */
+pd_list *pd_list_init(void);
+
+/*
+ * Initialize a new mp_range value, with unset values (start and end are infinite
+ */
+mp_range mp_range_init(void);
+
+/*
+ * Worker functions
+ */
+
+mp_range mp_range_set_start(mp_range, mp_perfdata_value);
+mp_range mp_range_set_end(mp_range, mp_perfdata_value);
+
+/*
+ * Parsing a range from a string
+ */
+
+typedef enum {
+ MP_PARSING_SUCCES = 0,
+ MP_PARSING_FAILURE,
+ MP_RANGE_PARSING_FAILURE,
+ MP_RANGE_PARSING_UNDERFLOW,
+ MP_RANGE_PARSING_OVERFLOW,
+ MP_RANGE_PARSING_INVALID_CHAR,
+} mp_range_parser_error;
+
+typedef struct mp_range_parsed {
+ mp_range_parser_error error;
+ mp_range range;
+} mp_range_parsed;
+
+mp_range_parsed mp_parse_range_string(const char * /*input*/);
+
+/*
+ * Appends a mp_perfdata value to a pd_list
+ */
+void pd_list_append(pd_list[1], mp_perfdata);
+
+#define mp_set_pd_value(P, V) \
+ _Generic((V), \
+ float: mp_set_pd_value_float, \
+ double: mp_set_pd_value_double, \
+ int: mp_set_pd_value_int, \
+ unsigned int: mp_set_pd_value_u_int, \
+ long: mp_set_pd_value_long, \
+ unsigned long: mp_set_pd_value_u_long, \
+ long long: mp_set_pd_value_long_long, \
+ unsigned long long: mp_set_pd_value_u_long_long)(P, V)
+
+mp_perfdata mp_set_pd_value_float(mp_perfdata, float);
+mp_perfdata mp_set_pd_value_double(mp_perfdata, double);
+mp_perfdata mp_set_pd_value_int(mp_perfdata, int);
+mp_perfdata mp_set_pd_value_u_int(mp_perfdata, unsigned int);
+mp_perfdata mp_set_pd_value_long(mp_perfdata, long);
+mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long);
+mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long);
+mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
+
+#define mp_create_pd_value(V) \
+ _Generic((V), \
+ float: mp_create_pd_value_float, \
+ double: mp_create_pd_value_double, \
+ int: mp_create_pd_value_int, \
+ unsigned int: mp_create_pd_value_u_int, \
+ long: mp_create_pd_value_long, \
+ unsigned long: mp_create_pd_value_u_long, \
+ long long: mp_create_pd_value_long_long, \
+ unsigned long long: mp_create_pd_value_u_long_long)(V)
+
+mp_perfdata_value mp_create_pd_value_float(float);
+mp_perfdata_value mp_create_pd_value_double(double);
+mp_perfdata_value mp_create_pd_value_int(int);
+mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
+mp_perfdata_value mp_create_pd_value_long(long);
+mp_perfdata_value mp_create_pd_value_u_long(unsigned long);
+mp_perfdata_value mp_create_pd_value_long_long(long long);
+mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long);
+
+/*
+ * Free the memory used by a pd_list
+ */
+void pd_list_free(pd_list[1]);
+
+int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value);
+
+// =================
+// String formatters
+// =================
+/*
+ * Generate string from mp_perfdata value
+ */
+char *pd_to_string(mp_perfdata);
+
+/*
+ * Generate string from perfdata_value value
+ */
+char *pd_value_to_string(mp_perfdata_value);
+
+/*
+ * Generate string from pd_list value for the final output
+ */
+char *pd_list_to_string(pd_list);
+
+/*
+ * Generate string from a mp_range value
+ */
+char *mp_range_to_string(mp_range);
+char *fmt_range(range);
diff --git a/lib/states.h b/lib/states.h
new file mode 100644
index 00000000..4a170caa
--- /dev/null
+++ b/lib/states.h
@@ -0,0 +1,71 @@
+#ifndef _MP_STATES_
+#define _MP_STATES_
+
+#include "../config.h"
+#include <sys/param.h>
+
+typedef enum state_enum {
+ STATE_OK,
+ STATE_WARNING,
+ STATE_CRITICAL,
+ STATE_UNKNOWN,
+ STATE_DEPENDENT
+} mp_state_enum;
+
+/* **************************************************************************
+ * max_state(STATE_x, STATE_y)
+ * compares STATE_x to STATE_y and returns result based on the following
+ * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
+ *
+ * Note that numerically the above does not hold
+ ****************************************************************************/
+
+static inline mp_state_enum max_state(mp_state_enum a, mp_state_enum b) {
+ if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
+ return STATE_CRITICAL;
+ }
+ if (a == STATE_WARNING || b == STATE_WARNING) {
+ return STATE_WARNING;
+ }
+ if (a == STATE_OK || b == STATE_OK) {
+ return STATE_OK;
+ }
+ if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
+ return STATE_UNKNOWN;
+ }
+ if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
+ return STATE_DEPENDENT;
+ }
+ return MAX(a, b);
+}
+
+/* **************************************************************************
+ * max_state_alt(STATE_x, STATE_y)
+ * compares STATE_x to STATE_y and returns result based on the following
+ * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
+ *
+ * The main difference between max_state_alt and max_state it that it doesn't
+ * allow setting a default to UNKNOWN. It will instead prioritixe any valid
+ * non-OK state.
+ ****************************************************************************/
+
+static inline mp_state_enum max_state_alt(mp_state_enum a, mp_state_enum b) {
+ if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
+ return STATE_CRITICAL;
+ }
+ if (a == STATE_WARNING || b == STATE_WARNING) {
+ return STATE_WARNING;
+ }
+ if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
+ return STATE_UNKNOWN;
+ }
+ if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
+ return STATE_DEPENDENT;
+ }
+ if (a == STATE_OK || b == STATE_OK) {
+ return STATE_OK;
+ }
+ return MAX(a, b);
+}
+
+#endif
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index 31d79df6..9be94f6d 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@
AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
-I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
-EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3
+EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output
-np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t
+np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t
np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini
EXTRA_DIST = $(np_test_scripts) $(np_test_files) var
@@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags)
AM_LDFLAGS = $(tap_ldflags) -ltap
LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO)
-SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c
+SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c
test: ${noinst_PROGRAMS}
perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS)
diff --git a/lib/tests/test_generic_output.c b/lib/tests/test_generic_output.c
new file mode 100644
index 00000000..e67aefc9
--- /dev/null
+++ b/lib/tests/test_generic_output.c
@@ -0,0 +1,315 @@
+#include "../lib/output.h"
+#include "../../tap/tap.h"
+#include "./states.h"
+
+#include <string.h>
+
+void test_one_subcheck(void);
+void test_two_subchecks(void);
+
+void test_perfdata_formatting(void);
+void test_perfdata_formatting2(void);
+
+void test_deep_check_hierarchy(void);
+void test_deep_check_hierarchy2(void);
+
+void test_default_states1(void);
+void test_default_states2(void);
+
+int main(void) {
+ plan_tests(19);
+
+ diag("Simple test with one subcheck");
+ test_one_subcheck();
+
+ diag("Test with two subchecks");
+ test_two_subchecks();
+
+ diag("Test for performance data formatting");
+ test_perfdata_formatting();
+
+ diag("Another test for performance data formatting");
+ test_perfdata_formatting2();
+
+ diag("Test for deeper hierarchies");
+ test_deep_check_hierarchy();
+
+ diag("Another test for deeper hierarchies");
+ test_deep_check_hierarchy2();
+
+ diag("Testing the default state logic");
+ test_default_states1();
+
+ diag("Testing the default state logic #2");
+ test_default_states2();
+
+ return exit_status();
+}
+
+void test_one_subcheck(void) {
+ mp_subcheck sc1 = mp_subcheck_init();
+
+ sc1.output = "foobar";
+ sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
+
+ mp_check check = mp_check_init();
+ mp_add_subcheck_to_check(&check, sc1);
+
+ ok(mp_compute_check_state(check) == STATE_WARNING, "Main state should be warning");
+
+ char *output = mp_fmt_output(check);
+
+ // diag("Formatted output");
+ // diag(output);
+
+ char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
+ "\t\\_[WARNING] - foobar\n";
+
+ // diag("Expected output");
+ // diag(expected);
+
+ ok(strcmp(output, expected) == 0, "Simple output test");
+}
+
+void test_perfdata_formatting2(void) {
+ mp_perfdata pd1 = perfdata_init();
+ mp_perfdata pd2 = perfdata_init();
+
+ pd1.label = "foo";
+ pd2.label = "bar";
+
+ pd1 = mp_set_pd_value(pd1, 23);
+ pd2 = mp_set_pd_value(pd2, 1LL);
+
+ pd_list *tmp = pd_list_init();
+
+ pd_list_append(tmp, pd1);
+ pd_list_append(tmp, pd2);
+
+ char *result = pd_list_to_string(*tmp);
+
+ ok(strcmp(result, "foo=23;;; bar=1;;;") == 0, "Perfdata string formatting");
+}
+
+void test_perfdata_formatting(void) {
+ mp_perfdata pd1 = perfdata_init();
+
+ pd1.uom = "s";
+ pd1.label = "foo";
+
+ pd1 = mp_set_pd_value(pd1, 23);
+
+ char *pd_string = pd_to_string(pd1);
+
+ ok(strcmp(pd_string, "foo=23s;;;") == 0, "Perfdata string formatting");
+}
+
+void test_two_subchecks(void) {
+ mp_subcheck sc1 = mp_subcheck_init();
+
+ sc1.output = "foobar";
+ sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
+
+ ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state directly after setting it");
+
+ mp_perfdata pd1 = perfdata_init();
+
+ pd1 = mp_set_pd_value(pd1, 23);
+
+ pd1.uom = "s";
+ pd1.label = "foo";
+
+ mp_add_perfdata_to_subcheck(&sc1, pd1);
+
+ mp_subcheck sc2 = mp_subcheck_init();
+ sc2.output = "baz";
+ sc2 = mp_set_subcheck_state(sc2, STATE_OK);
+
+ ok(mp_compute_subcheck_state(sc2) == STATE_OK, "Test subcheck 2 state after setting it");
+
+ mp_add_subcheck_to_subcheck(&sc1, sc2);
+
+ ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state after adding a subcheck");
+
+ mp_check check = mp_check_init();
+ mp_add_subcheck_to_check(&check, sc1);
+
+ ok(mp_compute_check_state(check) == STATE_WARNING, "Test main check result");
+
+ char *output = mp_fmt_output(check);
+
+ // diag("Formatted output. Length: %u", strlen(output));
+ // diag(output);
+
+ ok(output != NULL, "Output should not be NULL");
+
+ char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
+ "\t\\_[WARNING] - foobar\n"
+ "\t\t\\_[OK] - baz\n"
+ "|foo=23s;;; \n";
+
+ // diag("Expected output. Length: %u", strlen(expected));
+ // diag(expected);
+
+ ok(strcmp(output, expected) == 0, "Output is as expected");
+}
+
+void test_deep_check_hierarchy(void) {
+ // level 4
+ mp_subcheck sc4 = mp_subcheck_init();
+ sc4.output = "level4";
+ sc4 = mp_set_subcheck_state(sc4, STATE_OK);
+
+ // level 3
+ mp_subcheck sc3 = mp_subcheck_init();
+ sc3.output = "level3";
+ sc3 = mp_set_subcheck_state(sc3, STATE_OK);
+
+ // level 2
+ mp_subcheck sc2 = mp_subcheck_init();
+ sc2.output = "baz";
+ sc2 = mp_set_subcheck_state(sc2, STATE_OK);
+
+ // level 1
+ mp_subcheck sc1 = mp_subcheck_init();
+
+ sc1.output = "foobar";
+ sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
+
+ mp_perfdata pd1 = perfdata_init();
+
+ pd1.uom = "s";
+ pd1.label = "foo";
+ pd1 = mp_set_pd_value(pd1, 23);
+
+ mp_add_perfdata_to_subcheck(&sc1, pd1);
+
+ // main check
+ mp_check check = mp_check_init();
+
+ mp_add_subcheck_to_subcheck(&sc3, sc4);
+ mp_add_subcheck_to_subcheck(&sc2, sc3);
+ mp_add_subcheck_to_subcheck(&sc1, sc2);
+ mp_add_subcheck_to_check(&check, sc1);
+
+ char *output = mp_fmt_output(check);
+
+ size_t output_length = strlen(output);
+
+ // diag("Formatted output of length %i", output_length);
+ // diag(output);
+
+ ok(output != NULL, "Output should not be NULL");
+
+ char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
+ "\t\\_[WARNING] - foobar\n"
+ "\t\t\\_[OK] - baz\n"
+ "\t\t\t\\_[OK] - level3\n"
+ "\t\t\t\t\\_[OK] - level4\n"
+ "|foo=23s;;; \n";
+
+ size_t expected_length = strlen(expected);
+
+ // diag("Expected output of length: %i", expected_length);
+ // diag(expected);
+
+ ok(output_length == expected_length, "Outputs are of equal length");
+ ok(strcmp(output, expected) == 0, "Output is as expected");
+}
+
+void test_deep_check_hierarchy2(void) {
+ // level 1
+ mp_subcheck sc1 = mp_subcheck_init();
+
+ sc1.output = "foobar";
+ sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
+
+ mp_perfdata pd1 = perfdata_init();
+ pd1.uom = "s";
+ pd1.label = "foo";
+ pd1 = mp_set_pd_value(pd1, 23);
+
+ mp_add_perfdata_to_subcheck(&sc1, pd1);
+
+ // level 2
+ mp_subcheck sc2 = mp_subcheck_init();
+ sc2.output = "baz";
+ sc2 = mp_set_subcheck_state(sc2, STATE_OK);
+
+ mp_perfdata pd2 = perfdata_init();
+ pd2.uom = "B";
+ pd2.label = "baz";
+ pd2 = mp_set_pd_value(pd2, 1024);
+ mp_add_perfdata_to_subcheck(&sc2, pd2);
+
+ // level 3
+ mp_subcheck sc3 = mp_subcheck_init();
+ sc3.output = "level3";
+ sc3 = mp_set_subcheck_state(sc3, STATE_OK);
+
+ mp_perfdata pd3 = perfdata_init();
+ pd3.label = "floatMe";
+ pd3 = mp_set_pd_value(pd3, 1024.1024);
+ mp_add_perfdata_to_subcheck(&sc3, pd3);
+
+ // level 4
+ mp_subcheck sc4 = mp_subcheck_init();
+ sc4.output = "level4";
+ sc4 = mp_set_subcheck_state(sc4, STATE_OK);
+
+ mp_check check = mp_check_init();
+
+ mp_add_subcheck_to_subcheck(&sc3, sc4);
+ mp_add_subcheck_to_subcheck(&sc2, sc3);
+ mp_add_subcheck_to_subcheck(&sc1, sc2);
+ mp_add_subcheck_to_check(&check, sc1);
+
+ char *output = mp_fmt_output(check);
+
+ // diag("Formatted output of length: %i", strlen(output));
+ // diag(output);
+
+ ok(output != NULL, "Output should not be NULL");
+
+ char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
+ "\t\\_[WARNING] - foobar\n"
+ "\t\t\\_[OK] - baz\n"
+ "\t\t\t\\_[OK] - level3\n"
+ "\t\t\t\t\\_[OK] - level4\n"
+ "|foo=23s;;; baz=1024B;;; floatMe=1024.102400;;; \n";
+
+ // diag("Expected output of length: %i", strlen(expected));
+ // diag(expected);
+
+ ok(strcmp(output, expected) == 0, "Output is as expected");
+}
+
+void test_default_states1(void) {
+ mp_subcheck sc = mp_subcheck_init();
+
+ mp_state_enum state1 = mp_compute_subcheck_state(sc);
+ ok(state1 == STATE_UNKNOWN, "Default default state is Unknown");
+
+ sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
+
+ mp_state_enum state2 = mp_compute_subcheck_state(sc);
+ ok(state2 == STATE_CRITICAL, "Default state is Critical");
+
+ sc = mp_set_subcheck_state(sc, STATE_OK);
+
+ mp_state_enum state3 = mp_compute_subcheck_state(sc);
+ ok(state3 == STATE_OK, "Default state is Critical");
+}
+
+void test_default_states2(void) {
+ mp_check check = mp_check_init();
+
+ mp_subcheck sc = mp_subcheck_init();
+ sc.output = "placeholder";
+ sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
+
+ mp_add_subcheck_to_check(&check, sc);
+
+ mp_state_enum result_state = mp_compute_check_state(check);
+ ok(result_state == STATE_CRITICAL, "Derived state is the proper default state");
+}
diff --git a/lib/tests/test_generic_output.t b/lib/tests/test_generic_output.t
new file mode 100644
index 00000000..48c2ddf4
--- /dev/null
+++ b/lib/tests/test_generic_output.t
@@ -0,0 +1,6 @@
+#!/usr/bin/perl
+use Test::More;
+if (! -e "./test_generic_output") {
+ plan skip_all => "./test_generic_output not compiled - please enable libtap library to test";
+}
+exec "./test_generic_output";
diff --git a/lib/thresholds.c b/lib/thresholds.c
new file mode 100644
index 00000000..ddefae37
--- /dev/null
+++ b/lib/thresholds.c
@@ -0,0 +1,59 @@
+#include "./thresholds.h"
+#include "./utils_base.h"
+#include "perfdata.h"
+
+#include <stddef.h>
+
+mp_thresholds mp_thresholds_init() {
+ mp_thresholds tmp = {
+ .critical = {},
+ .critical_is_set = false,
+ .warning = {},
+ .warning_is_set = false,
+ };
+ return tmp;
+}
+
+char *fmt_threshold_warning(const thresholds th) {
+ if (th.warning == NULL) {
+ return "";
+ }
+
+ return fmt_range(*th.warning);
+}
+
+char *fmt_threshold_critical(const thresholds th) {
+ if (th.critical == NULL) {
+ return "";
+ }
+ return fmt_range(*th.critical);
+}
+
+mp_perfdata mp_pd_set_thresholds(mp_perfdata perfdata, mp_thresholds threshold) {
+ if (threshold.critical_is_set) {
+ perfdata.crit = threshold.critical;
+ perfdata.crit_present = true;
+ }
+
+ if (threshold.warning_is_set) {
+ perfdata.warn = threshold.warning;
+ perfdata.warn_present = true;
+ }
+
+ return perfdata;
+}
+
+mp_state_enum mp_get_pd_status(mp_perfdata perfdata) {
+ if (perfdata.crit_present) {
+ if (mp_check_range(perfdata.value, perfdata.crit)) {
+ return STATE_CRITICAL;
+ }
+ }
+ if (perfdata.warn_present) {
+ if (mp_check_range(perfdata.value, perfdata.warn)) {
+ return STATE_CRITICAL;
+ }
+ }
+
+ return STATE_OK;
+}
diff --git a/lib/thresholds.h b/lib/thresholds.h
new file mode 100644
index 00000000..4e7defee
--- /dev/null
+++ b/lib/thresholds.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "./perfdata.h"
+#include "states.h"
+
+/*
+ * Old threshold type using the old range type
+ */
+typedef struct thresholds_struct {
+ range *warning;
+ range *critical;
+} thresholds;
+
+typedef struct mp_thresholds_struct {
+ bool warning_is_set;
+ mp_range warning;
+ bool critical_is_set;
+ mp_range critical;
+} mp_thresholds;
+
+mp_thresholds mp_thresholds_init(void);
+
+mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */);
+
+mp_state_enum mp_get_pd_status(mp_perfdata /* pd */);
+
+char *fmt_threshold_warning(thresholds th);
+char *fmt_threshold_critical(thresholds th);
diff --git a/lib/utils_base.c b/lib/utils_base.c
index 90a4aaa5..ff9540c7 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -55,22 +55,24 @@ void np_init(char *plugin_name, int argc, char **argv) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
}
this_monitoring_plugin->plugin_name = strdup(plugin_name);
- if (this_monitoring_plugin->plugin_name == NULL)
+ if (this_monitoring_plugin->plugin_name == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
+ }
this_monitoring_plugin->argc = argc;
this_monitoring_plugin->argv = argv;
}
}
void np_set_args(int argc, char **argv) {
- if (this_monitoring_plugin == NULL)
+ if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
+ }
this_monitoring_plugin->argc = argc;
this_monitoring_plugin->argv = argv;
}
-void np_cleanup() {
+void np_cleanup(void) {
if (this_monitoring_plugin != NULL) {
if (this_monitoring_plugin->state != NULL) {
if (this_monitoring_plugin->state->state_data) {
@@ -162,8 +164,9 @@ range *parse_range_string(char *str) {
int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
thresholds *temp_thresholds = NULL;
- if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL)
+ if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
temp_thresholds->warning = NULL;
temp_thresholds->critical = NULL;
@@ -215,6 +218,46 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
printf("\n");
}
+/* Returns true if alert should be raised based on the range, false otherwise */
+bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
+ bool is_inside = false;
+
+ if (my_range.end_infinity == false && my_range.start_infinity == false) {
+ // range: .........|---inside---|...........
+ // value
+ if ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)) {
+ is_inside = true;
+ } else {
+ is_inside = false;
+ }
+ } else if (my_range.start_infinity == false && my_range.end_infinity == true) {
+ // range: .........|---inside---------
+ // value
+ if (cmp_perfdata_value(my_range.start, value) < 0) {
+ is_inside = true;
+ } else {
+ is_inside = false;
+ }
+ } else if (my_range.start_infinity == true && my_range.end_infinity == false) {
+ // range: -inside--------|....................
+ // value
+ if (cmp_perfdata_value(value, my_range.end) == -1) {
+ is_inside = true;
+ } else {
+ is_inside = false;
+ }
+ } else {
+ // range from -inf to inf, so always inside
+ is_inside = true;
+ }
+
+ if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
+ return true;
+ }
+
+ return false;
+}
+
/* Returns true if alert should be raised based on the range */
bool check_range(double value, range *my_range) {
bool no = false;
@@ -228,24 +271,24 @@ bool check_range(double value, range *my_range) {
if (my_range->end_infinity == false && my_range->start_infinity == false) {
if ((my_range->start <= value) && (value <= my_range->end)) {
return no;
- } else {
- return yes;
}
- } else if (my_range->start_infinity == false && my_range->end_infinity == true) {
+ return yes;
+ }
+
+ if (my_range->start_infinity == false && my_range->end_infinity == true) {
if (my_range->start <= value) {
return no;
- } else {
- return yes;
}
- } else if (my_range->start_infinity == true && my_range->end_infinity == false) {
+ return yes;
+ }
+
+ if (my_range->start_infinity == true && my_range->end_infinity == false) {
if (value <= my_range->end) {
return no;
- } else {
- return yes;
}
- } else {
- return no;
+ return yes;
}
+ return no;
}
/* Returns status */
@@ -265,7 +308,8 @@ int get_status(double value, thresholds *my_thresholds) {
char *np_escaped_string(const char *string) {
char *data;
- int i, j = 0;
+ int i;
+ int j = 0;
data = strdup(string);
for (i = 0; data[i]; i++) {
if (data[i] == '\\') {
@@ -302,7 +346,8 @@ int np_check_if_root(void) { return (geteuid() == 0); }
* data strings.
*/
char *np_extract_value(const char *varlist, const char *name, char sep) {
- char *tmp = NULL, *value = NULL;
+ char *tmp = NULL;
+ char *value = NULL;
int i;
while (1) {
@@ -325,15 +370,17 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
if ((tmp = index(varlist, sep))) {
/* Value is delimited by a comma */
- if (tmp - varlist == 0)
+ if (tmp - varlist == 0) {
continue;
+ }
value = (char *)calloc(1, tmp - varlist + 1);
strncpy(value, varlist, tmp - varlist);
value[tmp - varlist] = '\0';
} else {
/* Value is delimited by a \0 */
- if (strlen(varlist) == 0)
+ if (strlen(varlist) == 0) {
continue;
+ }
value = (char *)calloc(1, strlen(varlist) + 1);
strncpy(value, varlist, strlen(varlist));
value[strlen(varlist)] = '\0';
@@ -351,9 +398,11 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
}
/* Clean-up trailing spaces/newlines */
- if (value)
- for (i = strlen(value) - 1; isspace(value[i]); i--)
+ if (value) {
+ for (i = strlen(value) - 1; isspace(value[i]); i--) {
value[i] = '\0';
+ }
+ }
return value;
}
@@ -378,14 +427,18 @@ const char *state_text(int result) {
* return the corresponding STATE_ value or ERROR)
*/
int mp_translate_state(char *state_text) {
- if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0"))
+ if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) {
return STATE_OK;
- if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1"))
+ }
+ if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) {
return STATE_WARNING;
- if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2"))
+ }
+ if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) {
return STATE_CRITICAL;
- if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3"))
+ }
+ if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) {
return STATE_UNKNOWN;
+ }
return ERROR;
}
@@ -394,7 +447,7 @@ int mp_translate_state(char *state_text) {
* hopefully a unique key per service/plugin invocation. Use the extra-opts
* parse of argv, so that uniqueness in parameters are reflected there.
*/
-char *_np_state_generate_key() {
+char *_np_state_generate_key(void) {
int i;
char **argv = this_monitoring_plugin->argv;
char keyname[41];
@@ -441,7 +494,7 @@ char *_np_state_generate_key() {
return p;
}
-void _cleanup_state_data() {
+void _cleanup_state_data(void) {
if (this_monitoring_plugin->state->state_data != NULL) {
np_free(this_monitoring_plugin->state->state_data->data);
np_free(this_monitoring_plugin->state->state_data);
@@ -453,19 +506,21 @@ void _cleanup_state_data() {
* envvar NAGIOS_PLUGIN_STATE_DIRECTORY
* statically compiled shared state directory
*/
-char *_np_state_calculate_location_prefix() {
+char *_np_state_calculate_location_prefix(void) {
char *env_dir;
/* Do not allow passing MP_STATE_PATH in setuid plugins
* for security reasons */
if (!mp_suid()) {
env_dir = getenv("MP_STATE_PATH");
- if (env_dir && env_dir[0] != '\0')
+ if (env_dir && env_dir[0] != '\0') {
return env_dir;
+ }
/* This is the former ENV, for backward-compatibility */
env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
- if (env_dir && env_dir[0] != '\0')
+ if (env_dir && env_dir[0] != '\0') {
return env_dir;
+ }
}
return NP_STATE_DIR_PREFIX;
@@ -483,19 +538,22 @@ void np_enable_state(char *keyname, int expected_data_version) {
char *p = NULL;
int ret;
- if (this_monitoring_plugin == NULL)
+ if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
+ }
this_state = (state_key *)calloc(1, sizeof(state_key));
- if (this_state == NULL)
+ if (this_state == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
if (keyname == NULL) {
temp_keyname = _np_state_generate_key();
} else {
temp_keyname = strdup(keyname);
- if (temp_keyname == NULL)
+ if (temp_keyname == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
+ }
}
/* Die if invalid characters used for keyname */
p = temp_keyname;
@@ -513,8 +571,9 @@ void np_enable_state(char *keyname, int expected_data_version) {
/* Calculate filename */
ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
this_monitoring_plugin->plugin_name, this_state->name);
- if (ret < 0)
+ if (ret < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
this_state->_filename = temp_filename;
@@ -528,21 +587,23 @@ void np_enable_state(char *keyname, int expected_data_version) {
* If numerically lower, then return as no previous state. die with UNKNOWN
* if exceptional error.
*/
-state_data *np_state_read() {
+state_data *np_state_read(void) {
state_data *this_state_data = NULL;
FILE *statefile;
bool rc = false;
- if (this_monitoring_plugin == NULL)
+ if (this_monitoring_plugin == NULL) {
die(STATE_UNKNOWN, _("This requires np_init to be called"));
+ }
/* Open file. If this fails, no previous state found */
statefile = fopen(this_monitoring_plugin->state->_filename, "r");
if (statefile != NULL) {
this_state_data = (state_data *)calloc(1, sizeof(state_data));
- if (this_state_data == NULL)
+ if (this_state_data == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
this_state_data->data = NULL;
this_monitoring_plugin->state->state_data = this_state_data;
@@ -581,8 +642,9 @@ bool _np_state_read_file(FILE *f) {
/* Note: This introduces a limit of 1024 bytes in the string data */
line = (char *)calloc(1, 1024);
- if (line == NULL)
+ if (line == NULL) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
while (!failure && (fgets(line, 1024, f)) != NULL) {
pos = strlen(line);
@@ -590,38 +652,42 @@ bool _np_state_read_file(FILE *f) {
line[pos - 1] = '\0';
}
- if (line[0] == '#')
+ if (line[0] == '#') {
continue;
+ }
switch (expected) {
case STATE_FILE_VERSION:
i = atoi(line);
- if (i != NP_STATE_FORMAT_VERSION)
+ if (i != NP_STATE_FORMAT_VERSION) {
failure++;
- else
+ } else {
expected = STATE_DATA_VERSION;
+ }
break;
case STATE_DATA_VERSION:
i = atoi(line);
- if (i != this_monitoring_plugin->state->data_version)
+ if (i != this_monitoring_plugin->state->data_version) {
failure++;
- else
+ } else {
expected = STATE_DATA_TIME;
+ }
break;
case STATE_DATA_TIME:
/* If time > now, error */
data_time = strtoul(line, NULL, 10);
- if (data_time > current_time)
+ if (data_time > current_time) {
failure++;
- else {
+ } else {
this_monitoring_plugin->state->state_data->time = data_time;
expected = STATE_DATA_TEXT;
}
break;
case STATE_DATA_TEXT:
this_monitoring_plugin->state->state_data->data = strdup(line);
- if (this_monitoring_plugin->state->state_data->data == NULL)
+ if (this_monitoring_plugin->state->state_data->data == NULL) {
die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
+ }
expected = STATE_DATA_END;
status = true;
break;
@@ -648,16 +714,18 @@ void np_state_write_string(time_t data_time, char *data_string) {
char *directories = NULL;
char *p = NULL;
- if (data_time == 0)
+ if (data_time == 0) {
time(¤t_time);
- else
+ } else {
current_time = data_time;
+ }
/* If file doesn't currently exist, create directories */
if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
- if (result < 0)
+ if (result < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
for (p = directories + 1; *p; p++) {
if (*p == '/') {
@@ -674,8 +742,9 @@ void np_state_write_string(time_t data_time, char *data_string) {
}
result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
- if (result < 0)
+ if (result < 0) {
die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
+ }
if ((fd = mkstemp(temp_file)) == -1) {
np_free(temp_file);
diff --git a/lib/utils_base.h b/lib/utils_base.h
index a209cb6d..123066f8 100644
--- a/lib/utils_base.h
+++ b/lib/utils_base.h
@@ -2,6 +2,13 @@
#define _UTILS_BASE_
/* Header file for Monitoring Plugins utils_base.c */
+#include "../config.h"
+#include <time.h>
+
+#include "./perfdata.h"
+#include "./thresholds.h"
+
+
#ifndef USE_OPENSSL
# include "sha256.h"
#endif
@@ -19,20 +26,6 @@
#define OUTSIDE 0
#define INSIDE 1
-typedef struct range_struct {
- double start;
- bool start_infinity;
- double end;
- int end_infinity;
- int alert_on; /* OUTSIDE (default) or INSIDE */
- char *text; /* original unparsed text input */
-} range;
-
-typedef struct thresholds_struct {
- range *warning;
- range *critical;
-} thresholds;
-
#define NP_STATE_FORMAT_VERSION 1
typedef struct state_data_struct {
@@ -61,6 +54,7 @@ int _set_thresholds(thresholds **, char *, char *);
void set_thresholds(thresholds **, char *, char *);
void print_thresholds(const char *, thresholds *);
bool check_range(double, range *);
+bool mp_check_range(mp_perfdata_value, mp_range);
int get_status(double, thresholds *);
/* Handle timeouts */
@@ -107,12 +101,12 @@ char *np_extract_value(const char *, const char *, char);
int mp_translate_state(char *);
void np_enable_state(char *, int);
-state_data *np_state_read();
+state_data *np_state_read(void);
void np_state_write_string(time_t, char *);
void np_init(char *, int argc, char **argv);
void np_set_args(int argc, char **argv);
-void np_cleanup();
+void np_cleanup(void);
const char *state_text(int);
#endif /* _UTILS_BASE_ */
diff --git a/lib/vendor/cJSON/.deps/.dirstamp b/lib/vendor/cJSON/.deps/.dirstamp
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/vendor/cJSON/.deps/cJSON.Po b/lib/vendor/cJSON/.deps/cJSON.Po
new file mode 100644
index 00000000..16cfe859
--- /dev/null
+++ b/lib/vendor/cJSON/.deps/cJSON.Po
@@ -0,0 +1,170 @@
+vendor/cJSON/cJSON.o: vendor/cJSON/cJSON.c /usr/include/stdc-predef.h \
+ vendor/cJSON/../../../config.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h /usr/include/assert.h \
+ /usr/include/features.h /usr/include/features-time64.h \
+ /usr/include/x86_64-linux-gnu/bits/wordsize.h \
+ /usr/include/x86_64-linux-gnu/bits/timesize.h \
+ /usr/include/x86_64-linux-gnu/sys/cdefs.h \
+ /usr/include/x86_64-linux-gnu/bits/long-double.h \
+ /usr/include/x86_64-linux-gnu/gnu/stubs.h \
+ /usr/include/x86_64-linux-gnu/gnu/stubs-64.h ../gl/string.h \
+ /usr/include/string.h \
+ /usr/include/x86_64-linux-gnu/bits/libc-header-start.h ../gl/stddef.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h \
+ /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h ../gl/strings.h \
+ /usr/include/strings.h ../gl/stdio.h /usr/include/stdio.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h \
+ /usr/include/x86_64-linux-gnu/bits/types.h \
+ /usr/include/x86_64-linux-gnu/bits/typesizes.h \
+ /usr/include/x86_64-linux-gnu/bits/time64.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
+ /usr/include/x86_64-linux-gnu/bits/types/FILE.h \
+ /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
+ /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \
+ /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
+ /usr/include/x86_64-linux-gnu/bits/floatn.h \
+ /usr/include/x86_64-linux-gnu/bits/floatn-common.h \
+ /usr/include/x86_64-linux-gnu/bits/stdio.h ../gl/sys/types.h \
+ /usr/include/x86_64-linux-gnu/sys/types.h \
+ /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/time_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \
+ /usr/include/x86_64-linux-gnu/bits/stdint-intn.h /usr/include/endian.h \
+ /usr/include/x86_64-linux-gnu/bits/endian.h \
+ /usr/include/x86_64-linux-gnu/bits/endianness.h \
+ /usr/include/x86_64-linux-gnu/bits/byteswap.h \
+ /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \
+ /usr/include/x86_64-linux-gnu/sys/select.h \
+ /usr/include/x86_64-linux-gnu/bits/select.h \
+ /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \
+ /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \
+ /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \
+ /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \
+ /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \
+ /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \
+ /usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h \
+ /usr/include/x86_64-linux-gnu/bits/struct_mutex.h \
+ /usr/include/x86_64-linux-gnu/bits/struct_rwlock.h ../gl/math.h \
+ /usr/include/math.h /usr/include/x86_64-linux-gnu/bits/math-vector.h \
+ /usr/include/x86_64-linux-gnu/bits/libm-simd-decl-stubs.h \
+ /usr/include/x86_64-linux-gnu/bits/flt-eval-method.h \
+ /usr/include/x86_64-linux-gnu/bits/fp-logb.h \
+ /usr/include/x86_64-linux-gnu/bits/fp-fast.h \
+ /usr/include/x86_64-linux-gnu/bits/mathcalls-helper-functions.h \
+ /usr/include/x86_64-linux-gnu/bits/mathcalls.h \
+ /usr/include/x86_64-linux-gnu/bits/mathcalls-narrow.h \
+ /usr/include/x86_64-linux-gnu/bits/iscanonical.h ../gl/stdlib.h \
+ /usr/include/stdlib.h /usr/include/x86_64-linux-gnu/bits/waitflags.h \
+ /usr/include/x86_64-linux-gnu/bits/waitstatus.h ../gl/alloca.h \
+ /usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h \
+ /usr/include/x86_64-linux-gnu/bits/stdlib-float.h ../gl/limits.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/limits.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/syslimits.h \
+ /usr/include/limits.h /usr/include/x86_64-linux-gnu/bits/posix1_lim.h \
+ /usr/include/x86_64-linux-gnu/bits/local_lim.h \
+ /usr/include/linux/limits.h \
+ /usr/include/x86_64-linux-gnu/bits/pthread_stack_min-dynamic.h \
+ /usr/include/x86_64-linux-gnu/bits/posix2_lim.h \
+ /usr/include/x86_64-linux-gnu/bits/xopen_lim.h \
+ /usr/include/x86_64-linux-gnu/bits/uio_lim.h /usr/include/ctype.h \
+ /usr/lib/gcc/x86_64-linux-gnu/14/include/float.h vendor/cJSON/cJSON.h
+/usr/include/stdc-predef.h:
+vendor/cJSON/../../../config.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/stdbool.h:
+/usr/include/assert.h:
+/usr/include/features.h:
+/usr/include/features-time64.h:
+/usr/include/x86_64-linux-gnu/bits/wordsize.h:
+/usr/include/x86_64-linux-gnu/bits/timesize.h:
+/usr/include/x86_64-linux-gnu/sys/cdefs.h:
+/usr/include/x86_64-linux-gnu/bits/long-double.h:
+/usr/include/x86_64-linux-gnu/gnu/stubs.h:
+/usr/include/x86_64-linux-gnu/gnu/stubs-64.h:
+../gl/string.h:
+/usr/include/string.h:
+/usr/include/x86_64-linux-gnu/bits/libc-header-start.h:
+../gl/stddef.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h:
+/usr/include/x86_64-linux-gnu/bits/types/locale_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/__locale_t.h:
+../gl/strings.h:
+/usr/include/strings.h:
+../gl/stdio.h:
+/usr/include/stdio.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h:
+/usr/include/x86_64-linux-gnu/bits/types.h:
+/usr/include/x86_64-linux-gnu/bits/typesizes.h:
+/usr/include/x86_64-linux-gnu/bits/time64.h:
+/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/__FILE.h:
+/usr/include/x86_64-linux-gnu/bits/types/FILE.h:
+/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h:
+/usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h:
+/usr/include/x86_64-linux-gnu/bits/stdio_lim.h:
+/usr/include/x86_64-linux-gnu/bits/floatn.h:
+/usr/include/x86_64-linux-gnu/bits/floatn-common.h:
+/usr/include/x86_64-linux-gnu/bits/stdio.h:
+../gl/sys/types.h:
+/usr/include/x86_64-linux-gnu/sys/types.h:
+/usr/include/x86_64-linux-gnu/bits/types/clock_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/clockid_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/time_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/timer_t.h:
+/usr/include/x86_64-linux-gnu/bits/stdint-intn.h:
+/usr/include/endian.h:
+/usr/include/x86_64-linux-gnu/bits/endian.h:
+/usr/include/x86_64-linux-gnu/bits/endianness.h:
+/usr/include/x86_64-linux-gnu/bits/byteswap.h:
+/usr/include/x86_64-linux-gnu/bits/uintn-identity.h:
+/usr/include/x86_64-linux-gnu/sys/select.h:
+/usr/include/x86_64-linux-gnu/bits/select.h:
+/usr/include/x86_64-linux-gnu/bits/types/sigset_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h:
+/usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h:
+/usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h:
+/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h:
+/usr/include/x86_64-linux-gnu/bits/thread-shared-types.h:
+/usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h:
+/usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h:
+/usr/include/x86_64-linux-gnu/bits/struct_mutex.h:
+/usr/include/x86_64-linux-gnu/bits/struct_rwlock.h:
+../gl/math.h:
+/usr/include/math.h:
+/usr/include/x86_64-linux-gnu/bits/math-vector.h:
+/usr/include/x86_64-linux-gnu/bits/libm-simd-decl-stubs.h:
+/usr/include/x86_64-linux-gnu/bits/flt-eval-method.h:
+/usr/include/x86_64-linux-gnu/bits/fp-logb.h:
+/usr/include/x86_64-linux-gnu/bits/fp-fast.h:
+/usr/include/x86_64-linux-gnu/bits/mathcalls-helper-functions.h:
+/usr/include/x86_64-linux-gnu/bits/mathcalls.h:
+/usr/include/x86_64-linux-gnu/bits/mathcalls-narrow.h:
+/usr/include/x86_64-linux-gnu/bits/iscanonical.h:
+../gl/stdlib.h:
+/usr/include/stdlib.h:
+/usr/include/x86_64-linux-gnu/bits/waitflags.h:
+/usr/include/x86_64-linux-gnu/bits/waitstatus.h:
+../gl/alloca.h:
+/usr/include/x86_64-linux-gnu/bits/stdlib-bsearch.h:
+/usr/include/x86_64-linux-gnu/bits/stdlib-float.h:
+../gl/limits.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/limits.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/syslimits.h:
+/usr/include/limits.h:
+/usr/include/x86_64-linux-gnu/bits/posix1_lim.h:
+/usr/include/x86_64-linux-gnu/bits/local_lim.h:
+/usr/include/linux/limits.h:
+/usr/include/x86_64-linux-gnu/bits/pthread_stack_min-dynamic.h:
+/usr/include/x86_64-linux-gnu/bits/posix2_lim.h:
+/usr/include/x86_64-linux-gnu/bits/xopen_lim.h:
+/usr/include/x86_64-linux-gnu/bits/uio_lim.h:
+/usr/include/ctype.h:
+/usr/lib/gcc/x86_64-linux-gnu/14/include/float.h:
+vendor/cJSON/cJSON.h:
diff --git a/lib/vendor/cJSON/.dirstamp b/lib/vendor/cJSON/.dirstamp
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/vendor/cJSON/cJSON.c b/lib/vendor/cJSON/cJSON.c
new file mode 100644
index 00000000..12076e92
--- /dev/null
+++ b/lib/vendor/cJSON/cJSON.c
@@ -0,0 +1,3165 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include "../../../config.h"
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0/0.0
+#endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+ if (!cJSON_IsString(item))
+ {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+ if (!cJSON_IsNumber(item))
+ {
+ return (double) NAN;
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void (CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+ return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+ free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*)hooks->allocate(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (hooks == NULL)
+ {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL)
+ {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL)
+ {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+ {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+ if (node)
+ {
+ memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL)
+ {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+ {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+ {
+ global_hooks.deallocate(item->valuestring);
+ item->valuestring = NULL;
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ global_hooks.deallocate(item->string);
+ item->string = NULL;
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char) lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
+ * of the current locale (for strtod)
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
+ break;
+
+ case '.':
+ number_c_string[i] = decimal_point;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ number_c_string[i] = '\0';
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
+ {
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= INT_MAX)
+ {
+ object->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ object->valueint = INT_MIN;
+ }
+ else
+ {
+ object->valueint = (int)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ size_t v1_len;
+ size_t v2_len;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+ if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+ {
+ return NULL;
+ }
+ /* return NULL if the object is corrupted or valuestring is NULL */
+ if (object->valuestring == NULL || valuestring == NULL)
+ {
+ return NULL;
+ }
+
+ v1_len = strlen(valuestring);
+ v2_len = strlen(object->valuestring);
+
+ if (v1_len <= v2_len)
+ {
+ /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */
+ if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring ))
+ {
+ return NULL;
+ }
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ if (object->valuestring != NULL)
+ {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct
+{
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL))
+ {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length))
+ {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > INT_MAX)
+ {
+ /* sizes bigger than INT_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length)
+ {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (INT_MAX / 2))
+ {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX)
+ {
+ newsize = INT_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ }
+ else
+ {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+ if (!newbuffer)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL))
+ {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d))
+ {
+ length = sprintf((char*)number_buffer, "null");
+ }
+ else if(d == (double)item->valueint)
+ {
+ length = sprintf((char*)number_buffer, "%d", item->valueint);
+ }
+ else
+ {
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+ length = sprintf((char*)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+ {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char*)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+ {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++)
+ {
+ if (number_buffer[i] == decimal_point)
+ {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9'))
+ {
+ h += (unsigned int) input[i] - '0';
+ }
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
+ {
+ h += (unsigned int) 10 + input[i] - 'A';
+ }
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
+ {
+ h += (unsigned int) 10 + input[i] - 'a';
+ }
+ else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3)
+ {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+ {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+ {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+ {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+ {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ }
+ else
+ {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
+ {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ }
+ else if (codepoint < 0x800)
+ {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ }
+ else if (codepoint < 0x10000)
+ {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ }
+ else
+ {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+ {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1)
+ {
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ }
+ else
+ {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
+ {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+ {
+ /* is escape sequence */
+ if (input_end[0] == '\\')
+ {
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+ {
+ /* prevent buffer overflow when last input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+ {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end)
+ {
+ if (*input_pointer != '\\')
+ {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else
+ {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
+ {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
+ {
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char*)output;
+
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL)
+ {
+ input_buffer->hooks.deallocate(output);
+ output = NULL;
+ }
+
+ if (input_pointer != NULL)
+ {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL)
+ {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
+ {
+ switch (*input_pointer)
+ {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32)
+ {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
+ {
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+ {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+ {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ }
+ else
+ {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
+ {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+ return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0))
+ {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+ {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+ {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+ {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value)
+ {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length)
+ {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char*)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+ {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+ {
+ goto fail;
+ }
+ }
+ if (return_parse_end)
+ {
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL)
+ {
+ error local_error;
+ local_error.json = (const unsigned char*)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length)
+ {
+ local_error.position = buffer.offset;
+ }
+ else if (buffer.length > 0)
+ {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL)
+ {
+ *return_parse_end = (const char*)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ buffer->buffer = NULL;
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ buffer->buffer = NULL;
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ printed = NULL;
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer)
+ {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p))
+ {
+ global_hooks.deallocate(p.buffer);
+ p.buffer = NULL;
+ return NULL;
+ }
+
+ return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if ((length < 0) || (buffer == NULL))
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+ {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+ {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+ {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+ {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+ {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+ {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL))
+ {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF)
+ {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw:
+ {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL)
+ {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[')
+ {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+ {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+ {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next)
+ {
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if(output_buffer->format)
+ {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+ {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+ {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ if (cannot_access_at_index(input_buffer, 1))
+ {
+ goto fail; /* nothing comes after the comma */
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+ {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item)
+ {
+ if (output_buffer->format)
+ {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (current_item->next)
+ {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (output_buffer->format)
+ {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL)
+ {
+ return 0;
+ }
+
+ child = array->child;
+
+ while(child != NULL)
+ {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL)
+ {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0))
+ {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0)
+ {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive)
+ {
+ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL)
+ {
+ return NULL;
+ }
+
+ memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item))
+ {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL)
+ {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ }
+ else
+ {
+ /* append to the end */
+ if (child->prev)
+ {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+ return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+ {
+ return false;
+ }
+
+ if (constant_key)
+ {
+ new_key = (char*)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ }
+ else
+ {
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+ if (new_key == NULL)
+ {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL)
+ {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false))
+ {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
+ {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
+ {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+ {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
+ {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
+ {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+ {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
+ {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false))
+ {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+ if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL))
+ {
+ return NULL;
+ }
+
+ if (item != parent->child)
+ {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL)
+ {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child)
+ {
+ /* first element */
+ parent->child = item->next;
+ }
+ else if (item->next == NULL)
+ {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0 || newitem == NULL)
+ {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL)
+ {
+ return add_item_to_array(array, newitem);
+ }
+
+ if (after_inserted != array->child && after_inserted->prev == NULL) {
+ /* return false if after_inserted is a corrupted array item */
+ return false;
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+ if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
+ {
+ return false;
+ }
+
+ if (replacement == item)
+ {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL)
+ {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item)
+ {
+ if (parent->child->prev == parent->child)
+ {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ }
+ else
+ { /*
+ * To find the last item in array quickly, we use prev in array.
+ * We can't modify the last item's next pointer where this item was the parent's child
+ */
+ if (replacement->prev != NULL)
+ {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL)
+ {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+ {
+ cJSON_free(replacement->string);
+ }
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if (replacement->string == NULL)
+ {
+ return false;
+ }
+
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (num <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL)
+ {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char*)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Raw;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type=cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item)
+ {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateString(strings[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p,n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
+
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ return cJSON_Duplicate_rec(item, 0, recurse );
+}
+
+cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item)
+ {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem)
+ {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+ if (!newitem->valuestring)
+ {
+ goto fail;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+ if (!newitem->string)
+ {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL)
+ {
+ if(depth >= CJSON_CIRCULAR_LIMIT) {
+ goto fail;
+ }
+ newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild)
+ {
+ goto fail;
+ }
+ if (next != NULL)
+ {
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child)
+ {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
+ {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output) {
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL)
+ {
+ return;
+ }
+
+ while (json[0] != '\0')
+ {
+ switch (json[0])
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/')
+ {
+ skip_oneline_comment(&json);
+ }
+ else if (json[1] == '*')
+ {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char**)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+ {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF)
+ {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b)
+ {
+ return true;
+ }
+
+ switch (a->type & 0xFF)
+ {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble))
+ {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
+ {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0)
+ {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array:
+ {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);)
+ {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+ object = NULL;
+}
diff --git a/lib/vendor/cJSON/cJSON.h b/lib/vendor/cJSON/cJSON.h
new file mode 100644
index 00000000..37520bbc
--- /dev/null
+++ b/lib/vendor/cJSON/cJSON.h
@@ -0,0 +1,306 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 18
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ char *valuestring;
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+ int valueint;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* Limits the length of circular references can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_CIRCULAR_LIMIT
+#define CJSON_CIRCULAR_LIMIT 10000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
+#define cJSON_SetBoolValue(object, boolValue) ( \
+ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
+ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
+ cJSON_Invalid\
+)
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/plugins/common.h b/plugins/common.h
index 833479ce..47b1e4df 100644
--- a/plugins/common.h
+++ b/plugins/common.h
@@ -32,6 +32,7 @@
#define _COMMON_H_
#include "config.h"
+#include "../lib/monitoringplug.h"
#ifdef HAVE_FEATURES_H
#include <features.h>
@@ -184,14 +185,6 @@ enum {
ERROR = -1
};
-enum {
- STATE_OK,
- STATE_WARNING,
- STATE_CRITICAL,
- STATE_UNKNOWN,
- STATE_DEPENDENT
-};
-
enum {
DEFAULT_SOCKET_TIMEOUT = 10, /* timeout after 10 seconds */
MAX_INPUT_BUFFER = 8192, /* max size of most buffers we use */
diff --git a/plugins/popen.c b/plugins/popen.c
index 2b9824bc..cfe930b6 100644
--- a/plugins/popen.c
+++ b/plugins/popen.c
@@ -40,7 +40,6 @@
#include "./common.h"
#include "./utils.h"
-#include "../lib/maxfd.h"
/* extern so plugin has pid to kill exec'd process on timeouts */
extern pid_t *childpid;
diff --git a/plugins/runcmd.c b/plugins/runcmd.c
index 74843149..4429ceb0 100644
--- a/plugins/runcmd.c
+++ b/plugins/runcmd.c
@@ -40,6 +40,7 @@
/** includes **/
#include "runcmd.h"
+#include "../lib/monitoringplug.h"
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index 3c928413..719de575 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -29,6 +29,7 @@
#define MAX_CN_LENGTH 256
#include "common.h"
#include "netutils.h"
+#include "../lib/monitoringplug.h"
#ifdef HAVE_SSL
static SSL_CTX *ctx = NULL;
diff --git a/plugins/utils.c b/plugins/utils.c
index 6d366e3d..09649429 100644
--- a/plugins/utils.c
+++ b/plugins/utils.c
@@ -42,54 +42,6 @@ extern const char *progname;
time_t start_time, end_time;
-/* **************************************************************************
- * max_state(STATE_x, STATE_y)
- * compares STATE_x to STATE_y and returns result based on the following
- * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
- *
- * Note that numerically the above does not hold
- ****************************************************************************/
-
-int max_state(int a, int b) {
- if (a == STATE_CRITICAL || b == STATE_CRITICAL)
- return STATE_CRITICAL;
- else if (a == STATE_WARNING || b == STATE_WARNING)
- return STATE_WARNING;
- else if (a == STATE_OK || b == STATE_OK)
- return STATE_OK;
- else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
- return STATE_UNKNOWN;
- else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
- return STATE_DEPENDENT;
- else
- return max(a, b);
-}
-
-/* **************************************************************************
- * max_state_alt(STATE_x, STATE_y)
- * compares STATE_x to STATE_y and returns result based on the following
- * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
- *
- * The main difference between max_state_alt and max_state it that it doesn't
- * allow setting a default to UNKNOWN. It will instead prioritixe any valid
- * non-OK state.
- ****************************************************************************/
-
-int max_state_alt(int a, int b) {
- if (a == STATE_CRITICAL || b == STATE_CRITICAL)
- return STATE_CRITICAL;
- else if (a == STATE_WARNING || b == STATE_WARNING)
- return STATE_WARNING;
- else if (a == STATE_UNKNOWN || b == STATE_UNKNOWN)
- return STATE_UNKNOWN;
- else if (a == STATE_DEPENDENT || b == STATE_DEPENDENT)
- return STATE_DEPENDENT;
- else if (a == STATE_OK || b == STATE_OK)
- return STATE_OK;
- else
- return max(a, b);
-}
-
void usage(const char *msg) {
printf("%s\n", msg);
print_usage();
diff --git a/plugins/utils.h b/plugins/utils.h
index f939e337..c7073990 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -13,11 +13,11 @@ in order to resist overflow attacks. In addition, a few functions are
provided to standardize version and error reporting across the entire
suite of plugins. */
-/* now some functions etc are being defined in ../lib/utils_base.c */
-#include "utils_base.h"
-
+#include "../config.h"
#include <stdbool.h>
-
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
#ifdef NP_EXTRA_OPTS
/* Include extra-opts functions if compiled in */
@@ -78,9 +78,6 @@ char *strpcat (char *, const char *, const char *);
int xvasprintf (char **strp, const char *fmt, va_list ap);
int xasprintf (char **strp, const char *fmt, ...);
-int max_state (int a, int b);
-int max_state_alt (int a, int b);
-
void usage (const char *) __attribute__((noreturn));
void usage2(const char *, const char *) __attribute__((noreturn));
void usage3(const char *, int) __attribute__((noreturn));
More information about the Commits
mailing list