summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am18
-rw-r--r--lib/monitoringplug.h7
-rw-r--r--lib/output.c535
-rw-r--r--lib/output.h85
-rw-r--r--lib/perfdata.c516
-rw-r--r--lib/perfdata.h203
-rw-r--r--lib/states.h71
-rw-r--r--lib/tests/Makefile.am6
-rw-r--r--lib/tests/test_generic_output.c315
-rw-r--r--lib/tests/test_generic_output.t6
-rw-r--r--lib/thresholds.c59
-rw-r--r--lib/thresholds.h28
-rw-r--r--lib/utils_base.c167
-rw-r--r--lib/utils_base.h26
-rw-r--r--lib/vendor/cJSON/cJSON.c3165
-rw-r--r--lib/vendor/cJSON/cJSON.h306
16 files changed, 5442 insertions, 71 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index dc3ee893..e41201c4 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -7,8 +7,21 @@ noinst_LIBRARIES = libmonitoringplug.a
7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 7AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 8 -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
9 9
10libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c 10libmonitoringplug_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
11EXTRA_DIST = utils_base.h utils_disk.h utils_tcp.h utils_cmd.h parse_ini.h extra_opts.h maxfd.h 11
12EXTRA_DIST = utils_base.h \
13 utils_disk.h \
14 utils_tcp.h \
15 utils_cmd.h \
16 parse_ini.h \
17 extra_opts.h \
18 maxfd.h \
19 perfdata.h \
20 output.h \
21 thresholds.h \
22 states.h \
23 vendor/cJSON/cJSON.h \
24 monitoringplug.h
12 25
13if USE_PARSE_INI 26if USE_PARSE_INI
14libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c 27libmonitoringplug_a_SOURCES += parse_ini.c extra_opts.c
@@ -16,4 +29,3 @@ endif USE_PARSE_INI
16 29
17test test-debug: 30test test-debug:
18 cd tests && make $@ 31 cd tests && make $@
19
diff --git a/lib/monitoringplug.h b/lib/monitoringplug.h
new file mode 100644
index 00000000..a555d736
--- /dev/null
+++ b/lib/monitoringplug.h
@@ -0,0 +1,7 @@
1#pragma once
2
3#include "./states.h"
4#include "./utils_base.h"
5#include "./thresholds.h"
6#include "./maxfd.h"
7#include "./output.h"
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 00000000..17919afc
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,535 @@
1#include "./output.h"
2#include "./utils_base.h"
3#include "../plugins/utils.h"
4
5#include <assert.h>
6#include <stdlib.h>
7#include <string.h>
8#include <strings.h>
9// #include <cjson/cJSON.h>
10#include "./vendor/cJSON/cJSON.h"
11#include "perfdata.h"
12#include "states.h"
13
14// == Prototypes ==
15static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation);
16static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
17
18// == Implementation ==
19
20/*
21 * Generate output string for a mp_subcheck object
22 */
23static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
24 char *result = strdup("");
25 int added = 0;
26
27 if (check.perfdata != NULL) {
28 added = asprintf(&result, "%s", pd_list_to_string(*check.perfdata));
29 }
30
31 if (check.subchecks == NULL) {
32 // No subchecks, return here
33 return result;
34 }
35
36 mp_subcheck_list *subchecks = check.subchecks;
37
38 while (subchecks != NULL) {
39 if (added > 0) {
40 added = asprintf(&result, "%s%s", result, fmt_subcheck_perfdata(subchecks->subcheck));
41 } else {
42 // TODO free previous result here?
43 added = asprintf(&result, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
44 }
45
46 subchecks = subchecks->next;
47 }
48
49 return result;
50}
51
52/*
53 * Initialiser for a mp_check object. Always use this to get a new one!
54 * It sets useful defaults
55 */
56mp_check mp_check_init(void) {
57 mp_check check = {0};
58 check.format = MP_FORMAT_DEFAULT;
59 return check;
60}
61
62/*
63 * Initialiser for a mp_subcheck object. Always use this to get a new one!
64 * It sets useful defaults
65 */
66mp_subcheck mp_subcheck_init(void) {
67 mp_subcheck tmp = {0};
68 tmp.default_state = STATE_UNKNOWN; // Default state is unknown
69 tmp.state_set_explicitly = false;
70 return tmp;
71}
72
73/*
74 * Add a subcheck to a (the one and only) check object
75 */
76int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck subcheck) {
77 assert(subcheck.output != NULL); // There must be output in a subcheck
78
79 mp_subcheck_list *tmp = NULL;
80
81 if (check->subchecks == NULL) {
82 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
83 if (check->subchecks == NULL) {
84 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
85 }
86
87 check->subchecks->subcheck = subcheck;
88 check->subchecks->next = NULL;
89 } else {
90 // Search for the end
91 tmp = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
92 if (tmp == NULL) {
93 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
94 }
95
96 tmp->subcheck = subcheck;
97 tmp->next = check->subchecks;
98
99 check->subchecks = tmp;
100 }
101
102 return 0;
103}
104
105/*
106 * Add a mp_perfdata data point to a mp_subcheck object
107 */
108void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata perfData) {
109 if (check->perfdata == NULL) {
110 check->perfdata = pd_list_init();
111 }
112 pd_list_append(check->perfdata, perfData);
113}
114
115/*
116 * Add a mp_subcheck object to another one. The seconde mp_subcheck (argument) is the lower in the
117 * hierarchy
118 */
119int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
120 if (subcheck.output == NULL) {
121 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL");
122 }
123
124 mp_subcheck_list *tmp = NULL;
125
126 if (check->subchecks == NULL) {
127 check->subchecks = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
128 if (check->subchecks == NULL) {
129 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
130 }
131
132 tmp = check->subchecks;
133 } else {
134 // Search for the end
135 tmp = check->subchecks;
136
137 while (tmp->next != NULL) {
138 tmp = tmp->next;
139 }
140
141 tmp->next = (mp_subcheck_list *)calloc(1, sizeof(mp_subcheck_list));
142 if (tmp->next == NULL) {
143 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "malloc failed");
144 }
145
146 tmp = tmp->next;
147 }
148
149 tmp->subcheck = subcheck;
150
151 return 0;
152}
153
154/*
155 * Add a manual summary to a mp_check object, effectively replacing
156 * the autogenerated one
157 */
158void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
159
160/*
161 * Generate the summary string of a mp_check object based on it's subchecks
162 */
163char *get_subcheck_summary(mp_check check) {
164 mp_subcheck_list *subchecks = check.subchecks;
165
166 unsigned int ok = 0;
167 unsigned int warning = 0;
168 unsigned int critical = 0;
169 unsigned int unknown = 0;
170 while (subchecks != NULL) {
171 switch (subchecks->subcheck.state) {
172 case STATE_OK:
173 ok++;
174 break;
175 case STATE_WARNING:
176 warning++;
177 break;
178 case STATE_CRITICAL:
179 critical++;
180 break;
181 case STATE_UNKNOWN:
182 unknown++;
183 break;
184 default:
185 die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
186 }
187 subchecks = subchecks->next;
188 }
189 char *result = NULL;
190 asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
191 return result;
192}
193
194/*
195 * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states
196 */
197mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
198 if (check.state_set_explicitly) {
199 return check.state;
200 }
201
202 mp_subcheck_list *scl = check.subchecks;
203 mp_state_enum result = check.default_state;
204
205 while (scl != NULL) {
206 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
207 scl = scl->next;
208 }
209
210 return result;
211}
212
213/*
214 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
215 */
216mp_state_enum mp_compute_check_state(const mp_check check) {
217 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
218
219 mp_subcheck_list *scl = check.subchecks;
220 mp_state_enum result = STATE_OK;
221
222 while (scl != NULL) {
223 result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck));
224 scl = scl->next;
225 }
226
227 return result;
228}
229
230/*
231 * Generate output string for a mp_check object
232 * Non static to be available for testing functions
233 */
234char *mp_fmt_output(mp_check check) {
235 char *result = NULL;
236
237 switch (check.format) {
238 case MP_FORMAT_MULTI_LINE: {
239 if (check.summary == NULL) {
240 check.summary = get_subcheck_summary(check);
241 }
242
243 asprintf(&result, "[%s] - %s", state_text(mp_compute_check_state(check)), check.summary);
244
245 mp_subcheck_list *subchecks = check.subchecks;
246
247 while (subchecks != NULL) {
248 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
249 subchecks = subchecks->next;
250 }
251
252 char *pd_string = NULL;
253 subchecks = check.subchecks;
254
255 while (subchecks != NULL) {
256 if (pd_string == NULL) {
257 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
258 } else {
259 asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck));
260 }
261
262 subchecks = subchecks->next;
263 }
264
265 if (pd_string != NULL && strlen(pd_string) > 0) {
266 asprintf(&result, "%s|%s", result, pd_string);
267 }
268
269 break;
270 }
271 case MP_FORMAT_TEST_JSON: {
272 cJSON *resultObject = cJSON_CreateObject();
273 if (resultObject == NULL) {
274 die(STATE_UNKNOWN, "cJSON_CreateObject failed");
275 }
276
277 cJSON *resultState = cJSON_CreateString(state_text(mp_compute_check_state(check)));
278 cJSON_AddItemToObject(resultObject, "state", resultState);
279
280 if (check.summary == NULL) {
281 check.summary = get_subcheck_summary(check);
282 }
283
284 cJSON *summary = cJSON_CreateString(check.summary);
285 cJSON_AddItemToObject(resultObject, "summary", summary);
286
287 if (check.subchecks != NULL) {
288 cJSON *subchecks = cJSON_CreateArray();
289
290 mp_subcheck_list *sc = check.subchecks;
291
292 while (sc != NULL) {
293 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
294 cJSON_AddItemToArray(subchecks, sc_json);
295 sc = sc->next;
296 }
297
298 cJSON_AddItemToObject(resultObject, "checks", subchecks);
299 }
300
301 result = cJSON_PrintUnformatted(resultObject);
302 break;
303 }
304 default:
305 die(STATE_UNKNOWN, "Invalid format");
306 }
307
308 return result;
309}
310
311/*
312 * Helper function to properly indent the output lines when using multiline
313 * formats
314 */
315static char *generate_indentation_string(unsigned int indentation) {
316 char *result = calloc(indentation + 1, sizeof(char));
317
318 for (unsigned int i = 0; i < indentation; i++) {
319 result[i] = '\t';
320 }
321
322 return result;
323}
324
325/*
326 * Helper function to generate the output string of mp_subcheck
327 */
328static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) {
329 char *result = NULL;
330 mp_subcheck_list *subchecks = NULL;
331
332 switch (output_format) {
333 case MP_FORMAT_MULTI_LINE:
334 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)),
335 check.output);
336
337 subchecks = check.subchecks;
338
339 while (subchecks != NULL) {
340 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
341 subchecks = subchecks->next;
342 }
343 return result;
344 default:
345 die(STATE_UNKNOWN, "Invalid format");
346 }
347}
348
349static inline cJSON *json_serialise_pd_value(mp_perfdata_value value) {
350 cJSON *result = cJSON_CreateObject();
351
352 switch (value.type) {
353 case PD_TYPE_DOUBLE:
354 cJSON_AddStringToObject(result, "type", "double");
355 break;
356 case PD_TYPE_INT:
357 cJSON_AddStringToObject(result, "type", "int");
358 break;
359 case PD_TYPE_UINT:
360 cJSON_AddStringToObject(result, "type", "uint");
361 break;
362 case PD_TYPE_NONE:
363 die(STATE_UNKNOWN, "Perfdata type was None in json_serialise_pd_value");
364 }
365 cJSON_AddStringToObject(result, "value", pd_value_to_string(value));
366
367 return result;
368}
369
370static inline cJSON *json_serialise_range(mp_range range) {
371 cJSON *result = cJSON_CreateObject();
372
373 if (range.alert_on_inside_range) {
374 cJSON_AddBoolToObject(result, "alert_on_inside", true);
375 } else {
376 cJSON_AddBoolToObject(result, "alert_on_inside", false);
377 }
378
379 if (range.end_infinity) {
380 cJSON_AddStringToObject(result, "end", "inf");
381 } else {
382 cJSON_AddItemToObject(result, "end", json_serialise_pd_value(range.end));
383 }
384
385 if (range.start_infinity) {
386 cJSON_AddStringToObject(result, "start", "inf");
387 } else {
388 cJSON_AddItemToObject(result, "start", json_serialise_pd_value(range.end));
389 }
390
391 return result;
392}
393
394static inline cJSON *json_serialise_pd(mp_perfdata pd_val) {
395 cJSON *result = cJSON_CreateObject();
396
397 // Label
398 cJSON_AddStringToObject(result, "label", pd_val.label);
399
400 // Value
401 cJSON_AddItemToObject(result, "value", json_serialise_pd_value(pd_val.value));
402
403 // Uom
404 cJSON_AddStringToObject(result, "uom", pd_val.uom);
405
406 // Warn/Crit
407 if (pd_val.warn_present) {
408 cJSON *warn = json_serialise_range(pd_val.warn);
409 cJSON_AddItemToObject(result, "warn", warn);
410 }
411 if (pd_val.crit_present) {
412 cJSON *crit = json_serialise_range(pd_val.crit);
413 cJSON_AddItemToObject(result, "crit", crit);
414 }
415
416 if (pd_val.min_present) {
417 cJSON_AddItemToObject(result, "min", json_serialise_pd_value(pd_val.min));
418 }
419 if (pd_val.max_present) {
420 cJSON_AddItemToObject(result, "max", json_serialise_pd_value(pd_val.max));
421 }
422
423 return result;
424}
425
426static inline cJSON *json_serialise_pd_list(pd_list *list) {
427 cJSON *result = cJSON_CreateArray();
428
429 do {
430 cJSON *pd_value = json_serialise_pd(list->data);
431 cJSON_AddItemToArray(result, pd_value);
432 list = list->next;
433 } while (list != NULL);
434
435 return result;
436}
437
438static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck) {
439 cJSON *result = cJSON_CreateObject();
440
441 // Human readable output
442 cJSON *output = cJSON_CreateString(subcheck.output);
443 cJSON_AddItemToObject(result, "output", output);
444
445 // Test state (aka Exit Code)
446 cJSON *state = cJSON_CreateString(state_text(mp_compute_subcheck_state(subcheck)));
447 cJSON_AddItemToObject(result, "state", state);
448
449 // Perfdata
450 if (subcheck.perfdata != NULL) {
451 cJSON *perfdata = json_serialise_pd_list(subcheck.perfdata);
452 cJSON_AddItemToObject(result, "perfdata", perfdata);
453 }
454
455 if (subcheck.subchecks != NULL) {
456 cJSON *subchecks = cJSON_CreateArray();
457
458 mp_subcheck_list *sc = subcheck.subchecks;
459
460 while (sc != NULL) {
461 cJSON *sc_json = json_serialize_subcheck(sc->subcheck);
462 cJSON_AddItemToArray(subchecks, sc_json);
463 sc = sc->next;
464 }
465
466 cJSON_AddItemToObject(result, "checks", subchecks);
467 }
468
469 return result;
470}
471
472/*
473 * Wrapper function to print the output string of a mp_check object
474 * Use this in concrete plugins.
475 */
476void mp_print_output(mp_check check) { puts(mp_fmt_output(check)); }
477
478/*
479 * Convenience function to print the output string of a mp_check object and exit
480 * the program with the resulting state.
481 * Intended to be used to exit a monitoring plugin.
482 */
483void mp_exit(mp_check check) {
484 mp_print_output(check);
485 if (check.format == MP_FORMAT_TEST_JSON) {
486 exit(0);
487 }
488
489 exit(mp_compute_check_state(check));
490}
491
492/*
493 * Function to set the result state of a mp_subcheck object explicitly.
494 * This will overwrite the default state AND states derived from it's subchecks
495 */
496mp_subcheck mp_set_subcheck_state(mp_subcheck check, mp_state_enum state) {
497 check.state = state;
498 check.state_set_explicitly = true;
499 return check;
500}
501
502/*
503 * Function to set the default result state of a mp_subcheck object. This state
504 * will be used if neither an explicit state is set (see *mp_set_subcheck_state*)
505 * nor does it include other subchecks
506 */
507mp_subcheck mp_set_subcheck_default_state(mp_subcheck check, mp_state_enum state) {
508 check.default_state = state;
509 return check;
510}
511
512char *mp_output_format_map[] = {
513 [MP_FORMAT_MULTI_LINE] = "multi-line",
514 [MP_FORMAT_TEST_JSON] = "mp-test-json",
515};
516
517/*
518 * Function to parse the output from a string
519 */
520parsed_output_format mp_parse_output_format(char *format_string) {
521 parsed_output_format result = {
522 .parsing_success = false,
523 .output_format = MP_FORMAT_DEFAULT,
524 };
525
526 for (mp_output_format i = 0; i < (sizeof(mp_output_format_map) / sizeof(char *)); i++) {
527 if (strcasecmp(mp_output_format_map[i], format_string) == 0) {
528 result.parsing_success = true;
529 result.output_format = i;
530 break;
531 }
532 }
533
534 return result;
535}
diff --git a/lib/output.h b/lib/output.h
new file mode 100644
index 00000000..ffc36f53
--- /dev/null
+++ b/lib/output.h
@@ -0,0 +1,85 @@
1#pragma once
2
3#include "../config.h"
4#include "./perfdata.h"
5#include "./states.h"
6
7/*
8 * A partial check result
9 */
10typedef struct {
11 mp_state_enum state; // OK, Warning, Critical ... set explicitly
12 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
13 bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks)
14
15 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..")
16 pd_list *perfdata; // Performance data for this check
17 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
18} mp_subcheck;
19
20/*
21 * A list of subchecks, used in subchecks and the main check
22 */
23typedef struct subcheck_list {
24 mp_subcheck subcheck;
25 struct subcheck_list *next;
26} mp_subcheck_list;
27
28/*
29 * Possible output formats
30 */
31typedef enum output_format {
32 MP_FORMAT_MULTI_LINE,
33 MP_FORMAT_TEST_JSON,
34} mp_output_format;
35
36#define MP_FORMAT_DEFAULT MP_FORMAT_MULTI_LINE
37
38/*
39 * The main state object of a plugin. Exists only ONCE per plugin.
40 * This is the "root" of a tree of singular checks.
41 * The final result is always derived from the children and the "worst" state
42 * in the first layer of subchecks
43 */
44typedef struct {
45 mp_output_format format; // The output format
46 char *summary; // Overall summary, if not set a summary will be automatically generated
47 mp_subcheck_list *subchecks;
48} mp_check;
49
50mp_check mp_check_init(void);
51mp_subcheck mp_subcheck_init(void);
52
53mp_subcheck mp_set_subcheck_state(mp_subcheck, mp_state_enum);
54mp_subcheck mp_set_subcheck_default_state(mp_subcheck, mp_state_enum);
55
56int mp_add_subcheck_to_check(mp_check check[static 1], mp_subcheck);
57int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck);
58
59void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata);
60
61void mp_add_summary(mp_check check[static 1], char *summary);
62
63mp_state_enum mp_compute_check_state(mp_check);
64mp_state_enum mp_compute_subcheck_state(mp_subcheck);
65
66typedef struct {
67 bool parsing_success;
68 mp_output_format output_format;
69} parsed_output_format;
70parsed_output_format mp_parse_output_format(char *format_string);
71
72// TODO free and stuff
73// void mp_cleanup_check(mp_check check[static 1]);
74
75char *mp_fmt_output(mp_check);
76
77void mp_print_output(mp_check);
78
79/*
80 * ==================
81 * Exit functionality
82 * ==================
83 */
84
85void mp_exit(mp_check) __attribute__((noreturn));
diff --git a/lib/perfdata.c b/lib/perfdata.c
new file mode 100644
index 00000000..661756c5
--- /dev/null
+++ b/lib/perfdata.c
@@ -0,0 +1,516 @@
1#include "./perfdata.h"
2#include "../plugins/common.h"
3#include "../plugins/utils.h"
4#include "utils_base.h"
5
6#include <assert.h>
7#include <limits.h>
8#include <stdlib.h>
9
10char *pd_value_to_string(const mp_perfdata_value pd) {
11 char *result = NULL;
12
13 assert(pd.type != PD_TYPE_NONE);
14
15 switch (pd.type) {
16 case PD_TYPE_INT:
17 asprintf(&result, "%lli", pd.pd_int);
18 break;
19 case PD_TYPE_UINT:
20 asprintf(&result, "%llu", pd.pd_int);
21 break;
22 case PD_TYPE_DOUBLE:
23 asprintf(&result, "%f", pd.pd_double);
24 break;
25 default:
26 // die here
27 die(STATE_UNKNOWN, "Invalid mp_perfdata mode\n");
28 }
29
30 return result;
31}
32
33char *pd_to_string(mp_perfdata pd) {
34 assert(pd.label != NULL);
35 char *result = NULL;
36 asprintf(&result, "%s=", pd.label);
37
38 asprintf(&result, "%s%s", result, pd_value_to_string(pd.value));
39
40 if (pd.uom != NULL) {
41 asprintf(&result, "%s%s", result, pd.uom);
42 }
43
44 if (pd.warn_present) {
45 asprintf(&result, "%s;%s", result, mp_range_to_string(pd.warn));
46 } else {
47 asprintf(&result, "%s;", result);
48 }
49
50 if (pd.crit_present) {
51 asprintf(&result, "%s;%s", result, mp_range_to_string(pd.crit));
52 } else {
53 asprintf(&result, "%s;", result);
54 }
55 if (pd.min_present) {
56 asprintf(&result, "%s;%s", result, pd_value_to_string(pd.min));
57 } else {
58 asprintf(&result, "%s;", result);
59 }
60
61 if (pd.max_present) {
62 asprintf(&result, "%s;%s", result, pd_value_to_string(pd.max));
63 }
64
65 /*printf("pd_to_string: %s\n", result); */
66
67 return result;
68}
69
70char *pd_list_to_string(const pd_list pd) {
71 char *result = pd_to_string(pd.data);
72
73 for (pd_list *elem = pd.next; elem != NULL; elem = elem->next) {
74 asprintf(&result, "%s %s", result, pd_to_string(elem->data));
75 }
76
77 return result;
78}
79
80mp_perfdata perfdata_init() {
81 mp_perfdata pd = {};
82 return pd;
83}
84
85pd_list *pd_list_init() {
86 pd_list *tmp = (pd_list *)calloc(1, sizeof(pd_list));
87 if (tmp == NULL) {
88 die(STATE_UNKNOWN, "calloc failed\n");
89 }
90 tmp->next = NULL;
91 return tmp;
92}
93
94mp_range mp_range_init() {
95 mp_range result = {
96 .alert_on_inside_range = OUTSIDE,
97 .start = {},
98 .start_infinity = true,
99 .end = {},
100 .end_infinity = true,
101 };
102
103 return result;
104}
105
106mp_range mp_range_set_start(mp_range input, mp_perfdata_value perf_val) {
107 input.start = perf_val;
108 input.start_infinity = false;
109 return input;
110}
111
112mp_range mp_range_set_end(mp_range input, mp_perfdata_value perf_val) {
113 input.end = perf_val;
114 input.end_infinity = false;
115 return input;
116}
117
118void pd_list_append(pd_list pdl[1], const mp_perfdata pd) {
119 assert(pdl != NULL);
120
121 if (pdl->data.value.type == PD_TYPE_NONE) {
122 // first entry is still empty
123 pdl->data = pd;
124 } else {
125 // find last element in the list
126 pd_list *curr = pdl;
127 pd_list *next = pdl->next;
128
129 while (next != NULL) {
130 curr = next;
131 next = next->next;
132 }
133
134 if (curr->data.value.type == PD_TYPE_NONE) {
135 // still empty
136 curr->data = pd;
137 } else {
138 // new a new one
139 curr->next = pd_list_init();
140 curr->next->data = pd;
141 }
142 }
143}
144
145void pd_list_free(pd_list pdl[1]) {
146 while (pdl != NULL) {
147 pd_list *old = pdl;
148 pdl = pdl->next;
149 free(old);
150 }
151}
152
153/*
154 * returns -1 if a < b, 0 if a == b, 1 if a > b
155 */
156int cmp_perfdata_value(const mp_perfdata_value a, const mp_perfdata_value b) {
157 // Test if types are different
158 if (a.type == b.type) {
159
160 switch (a.type) {
161 case PD_TYPE_UINT:
162 if (a.pd_uint < b.pd_uint) {
163 return -1;
164 } else if (a.pd_uint == b.pd_uint) {
165 return 0;
166 } else {
167 return 1;
168 }
169 break;
170 case PD_TYPE_INT:
171 if (a.pd_int < b.pd_int) {
172 return -1;
173 } else if (a.pd_int == b.pd_int) {
174 return 0;
175 } else {
176 return 1;
177 }
178 break;
179 case PD_TYPE_DOUBLE:
180 if (a.pd_int < b.pd_int) {
181 return -1;
182 } else if (a.pd_int == b.pd_int) {
183 return 0;
184 } else {
185 return 1;
186 }
187 break;
188 default:
189 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
190 }
191 }
192
193 // Get dirty here
194 long double floating_a = 0;
195
196 switch (a.type) {
197 case PD_TYPE_UINT:
198 floating_a = a.pd_uint;
199 break;
200 case PD_TYPE_INT:
201 floating_a = a.pd_int;
202 break;
203 case PD_TYPE_DOUBLE:
204 floating_a = a.pd_double;
205 break;
206 default:
207 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
208 }
209
210 long double floating_b = 0;
211 switch (b.type) {
212 case PD_TYPE_UINT:
213 floating_b = b.pd_uint;
214 break;
215 case PD_TYPE_INT:
216 floating_b = b.pd_int;
217 break;
218 case PD_TYPE_DOUBLE:
219 floating_b = b.pd_double;
220 break;
221 default:
222 die(STATE_UNKNOWN, "Error in %s line: %d!", __FILE__, __LINE__);
223 }
224
225 if (floating_a < floating_b) {
226 return -1;
227 }
228 if (floating_a == floating_b) {
229 return 0;
230 }
231 return 1;
232}
233
234char *mp_range_to_string(const mp_range input) {
235 char *result = "";
236 if (input.alert_on_inside_range == INSIDE) {
237 asprintf(&result, "@");
238 }
239
240 if (input.start_infinity) {
241 asprintf(&result, "%s~:", result);
242 } else {
243 asprintf(&result, "%s%s:", result, pd_value_to_string(input.start));
244 }
245
246 if (!input.end_infinity) {
247 asprintf(&result, "%s%s", result, pd_value_to_string(input.end));
248 }
249 return result;
250}
251
252mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); }
253
254mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
255 pd.value.pd_double = value;
256 pd.value.type = PD_TYPE_DOUBLE;
257 return pd;
258}
259
260mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); }
261
262mp_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); }
263
264mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); }
265
266mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) {
267 return mp_set_pd_value_u_long_long(pd, (unsigned long long)value);
268}
269
270mp_perfdata mp_set_pd_value_long_long(mp_perfdata pd, long long value) {
271 pd.value.pd_int = value;
272 pd.value.type = PD_TYPE_INT;
273 return pd;
274}
275
276mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata pd, unsigned long long value) {
277 pd.value.pd_uint = value;
278 pd.value.type = PD_TYPE_UINT;
279 return pd;
280}
281
282mp_perfdata_value mp_create_pd_value_double(double value) {
283 mp_perfdata_value res = {0};
284 res.type = PD_TYPE_DOUBLE;
285 res.pd_double = value;
286 return res;
287}
288
289mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); }
290
291mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); }
292
293mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
294
295mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); }
296
297mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
298
299mp_perfdata_value mp_create_pd_value_long_long(long long value) {
300 mp_perfdata_value res = {0};
301 res.type = PD_TYPE_INT;
302 res.pd_int = value;
303 return res;
304}
305
306mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long value) {
307 mp_perfdata_value res = {0};
308 res.type = PD_TYPE_UINT;
309 res.pd_uint = value;
310 return res;
311}
312
313char *fmt_range(range foo) { return foo.text; }
314
315typedef struct integer_parser_wrapper {
316 int error;
317 mp_perfdata_value value;
318} integer_parser_wrapper;
319
320typedef struct double_parser_wrapper {
321 int error;
322 mp_perfdata_value value;
323} double_parser_wrapper;
324
325typedef struct perfdata_value_parser_wrapper {
326 int error;
327 mp_perfdata_value value;
328} perfdata_value_parser_wrapper;
329
330double_parser_wrapper parse_double(const char *input);
331integer_parser_wrapper parse_integer(const char *input);
332perfdata_value_parser_wrapper parse_pd_value(const char *input);
333
334mp_range_parsed mp_parse_range_string(const char *input) {
335 if (input == NULL) {
336 mp_range_parsed result = {
337 .error = MP_RANGE_PARSING_FAILURE,
338 };
339 return result;
340 }
341
342 if (strlen(input) == 0) {
343 mp_range_parsed result = {
344 .error = MP_RANGE_PARSING_FAILURE,
345 };
346 return result;
347 }
348
349 mp_range_parsed result = {
350 .range = mp_range_init(),
351 .error = MP_PARSING_SUCCES,
352 };
353
354 if (input[0] == '@') {
355 // found an '@' at beginning, so invert the range logic
356 result.range.alert_on_inside_range = INSIDE;
357
358 // advance the pointer one symbol
359 input++;
360 }
361
362 char *working_copy = strdup(input);
363 input = working_copy;
364
365 char *separator = index(working_copy, ':');
366 if (separator != NULL) {
367 // Found a separator
368 // set the separator to 0, so we have two different strings
369 *separator = '\0';
370
371 if (input[0] == '~') {
372 // the beginning starts with '~', so it might be infinity
373 if (&input[1] != separator) {
374 // the next symbol after '~' is not the separator!
375 // so input is probably wrong
376 result.error = MP_RANGE_PARSING_FAILURE;
377 free(working_copy);
378 return result;
379 }
380
381 result.range.start_infinity = true;
382 } else {
383 // No '~' at the beginning, so this should be a number
384 result.range.start_infinity = false;
385 perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
386
387 if (parsed_pd.error != MP_PARSING_SUCCES) {
388 result.error = parsed_pd.error;
389 free(working_copy);
390 return result;
391 }
392
393 result.range.start = parsed_pd.value;
394 result.range.start_infinity = false;
395 }
396 // got the first part now
397 // advance the pointer
398 input = separator + 1;
399 }
400
401 // End part or no separator
402 if (input[0] == '\0') {
403 // the end is infinite
404 result.range.end_infinity = true;
405 } else {
406 perfdata_value_parser_wrapper parsed_pd = parse_pd_value(input);
407
408 if (parsed_pd.error != MP_PARSING_SUCCES) {
409 result.error = parsed_pd.error;
410 return result;
411 }
412 result.range.end = parsed_pd.value;
413 result.range.end_infinity = false;
414 }
415 free(working_copy);
416 return result;
417}
418
419double_parser_wrapper parse_double(const char *input) {
420 double_parser_wrapper result = {
421 .error = MP_PARSING_SUCCES,
422 };
423
424 if (input == NULL) {
425 result.error = MP_PARSING_FAILURE;
426 return result;
427 }
428
429 char *endptr = NULL;
430 errno = 0;
431 double tmp = strtod(input, &endptr);
432
433 if (input == endptr) {
434 // man 3 strtod says, no conversion performed
435 result.error = MP_PARSING_FAILURE;
436 return result;
437 }
438
439 if (errno) {
440 // some other error
441 // TODO maybe differentiate a little bit
442 result.error = MP_PARSING_FAILURE;
443 return result;
444 }
445
446 result.value = mp_create_pd_value(tmp);
447 return result;
448}
449
450integer_parser_wrapper parse_integer(const char *input) {
451 integer_parser_wrapper result = {
452 .error = MP_PARSING_SUCCES,
453 };
454
455 if (input == NULL) {
456 result.error = MP_PARSING_FAILURE;
457 return result;
458 }
459
460 char *endptr = NULL;
461 errno = 0;
462 long long tmp = strtoll(input, &endptr, 0);
463
464 // validating *sigh*
465 if (*endptr != '\0') {
466 // something went wrong in strtoll
467 if (tmp == LLONG_MIN) {
468 // underflow
469 result.error = MP_RANGE_PARSING_UNDERFLOW;
470 return result;
471 }
472
473 if (tmp == LLONG_MAX) {
474 // overflow
475 result.error = MP_RANGE_PARSING_OVERFLOW;
476 return result;
477 }
478
479 // still wrong, but not sure why, probably invalid characters
480 if (errno == EINVAL) {
481 result.error = MP_RANGE_PARSING_INVALID_CHAR;
482 return result;
483 }
484
485 // some other error, do catch all here
486 result.error = MP_RANGE_PARSING_FAILURE;
487 return result;
488 }
489
490 // no error, should be fine
491 result.value = mp_create_pd_value(tmp);
492 return result;
493}
494
495perfdata_value_parser_wrapper parse_pd_value(const char *input) {
496 // try integer first
497 integer_parser_wrapper tmp_int = parse_integer(input);
498
499 if (tmp_int.error == MP_PARSING_SUCCES) {
500 perfdata_value_parser_wrapper result = {
501 .error = tmp_int.error,
502 .value = tmp_int.value,
503 };
504 return result;
505 }
506
507 double_parser_wrapper tmp_double = parse_double(input);
508 perfdata_value_parser_wrapper result = {};
509 if (tmp_double.error == MP_PARSING_SUCCES) {
510 result.error = tmp_double.error;
511 result.value = tmp_double.value;
512 } else {
513 result.error = tmp_double.error;
514 }
515 return result;
516}
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 @@
1#pragma once
2
3#include "../config.h"
4
5#include <inttypes.h>
6#include <stdbool.h>
7
8// Enum for the specific type of a perfdata_value
9typedef enum pd_value_type {
10 PD_TYPE_NONE = 0,
11 PD_TYPE_INT,
12 PD_TYPE_UINT,
13 PD_TYPE_DOUBLE
14} pd_value_type;
15
16typedef struct {
17 enum pd_value_type type;
18 union {
19 long long pd_int;
20 unsigned long long pd_uint;
21 double pd_double;
22 };
23} mp_perfdata_value;
24
25#define MP_OUTSIDE false
26#define MP_INSIDE true
27
28/*
29 * New range type with generic numerical values
30 */
31typedef struct mp_range_struct {
32 mp_perfdata_value start;
33 bool start_infinity; /* false (default) or true */
34
35 mp_perfdata_value end;
36 bool end_infinity;
37
38 bool alert_on_inside_range; /* OUTSIDE (default) or INSIDE */
39} mp_range;
40
41/*
42 * Old range type with floating point values
43 */
44typedef struct range_struct {
45 double start;
46 bool start_infinity;
47 double end;
48 int end_infinity;
49 int alert_on; /* OUTSIDE (default) or INSIDE */
50 char *text; /* original unparsed text input */
51} range;
52
53/*
54 * Perfdata type for storing perfdata output
55 */
56typedef struct perfdata_struct {
57 char *label;
58 char *uom;
59 mp_perfdata_value value;
60
61 bool warn_present;
62 mp_range warn;
63
64 bool crit_present;
65 mp_range crit;
66
67 bool min_present;
68 mp_perfdata_value min;
69
70 bool max_present;
71 mp_perfdata_value max;
72} mp_perfdata;
73
74/*
75 * List of mp_perfdata values
76 */
77typedef struct pd_list_struct {
78 mp_perfdata data;
79 struct pd_list_struct *next;
80} pd_list;
81
82/*
83 * ============
84 * Initializers
85 * ============
86 */
87/*
88 * Initialize mp_perfdata value. Always use this to generate a new one
89 */
90mp_perfdata perfdata_init(void);
91
92/*
93 * Initialize pd_list value. Always use this to generate a new one
94 */
95pd_list *pd_list_init(void);
96
97/*
98 * Initialize a new mp_range value, with unset values (start and end are infinite
99 */
100mp_range mp_range_init(void);
101
102/*
103 * Worker functions
104 */
105
106mp_range mp_range_set_start(mp_range, mp_perfdata_value);
107mp_range mp_range_set_end(mp_range, mp_perfdata_value);
108
109/*
110 * Parsing a range from a string
111 */
112
113typedef enum {
114 MP_PARSING_SUCCES = 0,
115 MP_PARSING_FAILURE,
116 MP_RANGE_PARSING_FAILURE,
117 MP_RANGE_PARSING_UNDERFLOW,
118 MP_RANGE_PARSING_OVERFLOW,
119 MP_RANGE_PARSING_INVALID_CHAR,
120} mp_range_parser_error;
121
122typedef struct mp_range_parsed {
123 mp_range_parser_error error;
124 mp_range range;
125} mp_range_parsed;
126
127mp_range_parsed mp_parse_range_string(const char * /*input*/);
128
129/*
130 * Appends a mp_perfdata value to a pd_list
131 */
132void pd_list_append(pd_list[1], mp_perfdata);
133
134#define mp_set_pd_value(P, V) \
135 _Generic((V), \
136 float: mp_set_pd_value_float, \
137 double: mp_set_pd_value_double, \
138 int: mp_set_pd_value_int, \
139 unsigned int: mp_set_pd_value_u_int, \
140 long: mp_set_pd_value_long, \
141 unsigned long: mp_set_pd_value_u_long, \
142 long long: mp_set_pd_value_long_long, \
143 unsigned long long: mp_set_pd_value_u_long_long)(P, V)
144
145mp_perfdata mp_set_pd_value_float(mp_perfdata, float);
146mp_perfdata mp_set_pd_value_double(mp_perfdata, double);
147mp_perfdata mp_set_pd_value_int(mp_perfdata, int);
148mp_perfdata mp_set_pd_value_u_int(mp_perfdata, unsigned int);
149mp_perfdata mp_set_pd_value_long(mp_perfdata, long);
150mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long);
151mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long);
152mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
153
154#define mp_create_pd_value(V) \
155 _Generic((V), \
156 float: mp_create_pd_value_float, \
157 double: mp_create_pd_value_double, \
158 int: mp_create_pd_value_int, \
159 unsigned int: mp_create_pd_value_u_int, \
160 long: mp_create_pd_value_long, \
161 unsigned long: mp_create_pd_value_u_long, \
162 long long: mp_create_pd_value_long_long, \
163 unsigned long long: mp_create_pd_value_u_long_long)(V)
164
165mp_perfdata_value mp_create_pd_value_float(float);
166mp_perfdata_value mp_create_pd_value_double(double);
167mp_perfdata_value mp_create_pd_value_int(int);
168mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
169mp_perfdata_value mp_create_pd_value_long(long);
170mp_perfdata_value mp_create_pd_value_u_long(unsigned long);
171mp_perfdata_value mp_create_pd_value_long_long(long long);
172mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long);
173
174/*
175 * Free the memory used by a pd_list
176 */
177void pd_list_free(pd_list[1]);
178
179int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value);
180
181// =================
182// String formatters
183// =================
184/*
185 * Generate string from mp_perfdata value
186 */
187char *pd_to_string(mp_perfdata);
188
189/*
190 * Generate string from perfdata_value value
191 */
192char *pd_value_to_string(mp_perfdata_value);
193
194/*
195 * Generate string from pd_list value for the final output
196 */
197char *pd_list_to_string(pd_list);
198
199/*
200 * Generate string from a mp_range value
201 */
202char *mp_range_to_string(mp_range);
203char *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 @@
1#ifndef _MP_STATES_
2#define _MP_STATES_
3
4#include "../config.h"
5#include <sys/param.h>
6
7typedef enum state_enum {
8 STATE_OK,
9 STATE_WARNING,
10 STATE_CRITICAL,
11 STATE_UNKNOWN,
12 STATE_DEPENDENT
13} mp_state_enum;
14
15/* **************************************************************************
16 * max_state(STATE_x, STATE_y)
17 * compares STATE_x to STATE_y and returns result based on the following
18 * STATE_UNKNOWN < STATE_OK < STATE_WARNING < STATE_CRITICAL
19 *
20 * Note that numerically the above does not hold
21 ****************************************************************************/
22
23static inline mp_state_enum max_state(mp_state_enum a, mp_state_enum b) {
24 if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
25 return STATE_CRITICAL;
26 }
27 if (a == STATE_WARNING || b == STATE_WARNING) {
28 return STATE_WARNING;
29 }
30 if (a == STATE_OK || b == STATE_OK) {
31 return STATE_OK;
32 }
33 if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
34 return STATE_UNKNOWN;
35 }
36 if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
37 return STATE_DEPENDENT;
38 }
39 return MAX(a, b);
40}
41
42/* **************************************************************************
43 * max_state_alt(STATE_x, STATE_y)
44 * compares STATE_x to STATE_y and returns result based on the following
45 * STATE_OK < STATE_DEPENDENT < STATE_UNKNOWN < STATE_WARNING < STATE_CRITICAL
46 *
47 * The main difference between max_state_alt and max_state it that it doesn't
48 * allow setting a default to UNKNOWN. It will instead prioritixe any valid
49 * non-OK state.
50 ****************************************************************************/
51
52static inline mp_state_enum max_state_alt(mp_state_enum a, mp_state_enum b) {
53 if (a == STATE_CRITICAL || b == STATE_CRITICAL) {
54 return STATE_CRITICAL;
55 }
56 if (a == STATE_WARNING || b == STATE_WARNING) {
57 return STATE_WARNING;
58 }
59 if (a == STATE_UNKNOWN || b == STATE_UNKNOWN) {
60 return STATE_UNKNOWN;
61 }
62 if (a == STATE_DEPENDENT || b == STATE_DEPENDENT) {
63 return STATE_DEPENDENT;
64 }
65 if (a == STATE_OK || b == STATE_OK) {
66 return STATE_OK;
67 }
68 return MAX(a, b);
69}
70
71#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@
8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ 8AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \
9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins 9 -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins
10 10
11EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 11EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output
12 12
13np_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 13np_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
14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini 14np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini
15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var 15EXTRA_DIST = $(np_test_scripts) $(np_test_files) var
16 16
@@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags)
29AM_LDFLAGS = $(tap_ldflags) -ltap 29AM_LDFLAGS = $(tap_ldflags) -ltap
30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) 30LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO)
31 31
32SOURCES = 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 32SOURCES = 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
33 33
34test: ${noinst_PROGRAMS} 34test: ${noinst_PROGRAMS}
35 perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) 35 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 @@
1#include "../lib/output.h"
2#include "../../tap/tap.h"
3#include "./states.h"
4
5#include <string.h>
6
7void test_one_subcheck(void);
8void test_two_subchecks(void);
9
10void test_perfdata_formatting(void);
11void test_perfdata_formatting2(void);
12
13void test_deep_check_hierarchy(void);
14void test_deep_check_hierarchy2(void);
15
16void test_default_states1(void);
17void test_default_states2(void);
18
19int main(void) {
20 plan_tests(19);
21
22 diag("Simple test with one subcheck");
23 test_one_subcheck();
24
25 diag("Test with two subchecks");
26 test_two_subchecks();
27
28 diag("Test for performance data formatting");
29 test_perfdata_formatting();
30
31 diag("Another test for performance data formatting");
32 test_perfdata_formatting2();
33
34 diag("Test for deeper hierarchies");
35 test_deep_check_hierarchy();
36
37 diag("Another test for deeper hierarchies");
38 test_deep_check_hierarchy2();
39
40 diag("Testing the default state logic");
41 test_default_states1();
42
43 diag("Testing the default state logic #2");
44 test_default_states2();
45
46 return exit_status();
47}
48
49void test_one_subcheck(void) {
50 mp_subcheck sc1 = mp_subcheck_init();
51
52 sc1.output = "foobar";
53 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
54
55 mp_check check = mp_check_init();
56 mp_add_subcheck_to_check(&check, sc1);
57
58 ok(mp_compute_check_state(check) == STATE_WARNING, "Main state should be warning");
59
60 char *output = mp_fmt_output(check);
61
62 // diag("Formatted output");
63 // diag(output);
64
65 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
66 "\t\\_[WARNING] - foobar\n";
67
68 // diag("Expected output");
69 // diag(expected);
70
71 ok(strcmp(output, expected) == 0, "Simple output test");
72}
73
74void test_perfdata_formatting2(void) {
75 mp_perfdata pd1 = perfdata_init();
76 mp_perfdata pd2 = perfdata_init();
77
78 pd1.label = "foo";
79 pd2.label = "bar";
80
81 pd1 = mp_set_pd_value(pd1, 23);
82 pd2 = mp_set_pd_value(pd2, 1LL);
83
84 pd_list *tmp = pd_list_init();
85
86 pd_list_append(tmp, pd1);
87 pd_list_append(tmp, pd2);
88
89 char *result = pd_list_to_string(*tmp);
90
91 ok(strcmp(result, "foo=23;;; bar=1;;;") == 0, "Perfdata string formatting");
92}
93
94void test_perfdata_formatting(void) {
95 mp_perfdata pd1 = perfdata_init();
96
97 pd1.uom = "s";
98 pd1.label = "foo";
99
100 pd1 = mp_set_pd_value(pd1, 23);
101
102 char *pd_string = pd_to_string(pd1);
103
104 ok(strcmp(pd_string, "foo=23s;;;") == 0, "Perfdata string formatting");
105}
106
107void test_two_subchecks(void) {
108 mp_subcheck sc1 = mp_subcheck_init();
109
110 sc1.output = "foobar";
111 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
112
113 ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state directly after setting it");
114
115 mp_perfdata pd1 = perfdata_init();
116
117 pd1 = mp_set_pd_value(pd1, 23);
118
119 pd1.uom = "s";
120 pd1.label = "foo";
121
122 mp_add_perfdata_to_subcheck(&sc1, pd1);
123
124 mp_subcheck sc2 = mp_subcheck_init();
125 sc2.output = "baz";
126 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
127
128 ok(mp_compute_subcheck_state(sc2) == STATE_OK, "Test subcheck 2 state after setting it");
129
130 mp_add_subcheck_to_subcheck(&sc1, sc2);
131
132 ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state after adding a subcheck");
133
134 mp_check check = mp_check_init();
135 mp_add_subcheck_to_check(&check, sc1);
136
137 ok(mp_compute_check_state(check) == STATE_WARNING, "Test main check result");
138
139 char *output = mp_fmt_output(check);
140
141 // diag("Formatted output. Length: %u", strlen(output));
142 // diag(output);
143
144 ok(output != NULL, "Output should not be NULL");
145
146 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
147 "\t\\_[WARNING] - foobar\n"
148 "\t\t\\_[OK] - baz\n"
149 "|foo=23s;;; \n";
150
151 // diag("Expected output. Length: %u", strlen(expected));
152 // diag(expected);
153
154 ok(strcmp(output, expected) == 0, "Output is as expected");
155}
156
157void test_deep_check_hierarchy(void) {
158 // level 4
159 mp_subcheck sc4 = mp_subcheck_init();
160 sc4.output = "level4";
161 sc4 = mp_set_subcheck_state(sc4, STATE_OK);
162
163 // level 3
164 mp_subcheck sc3 = mp_subcheck_init();
165 sc3.output = "level3";
166 sc3 = mp_set_subcheck_state(sc3, STATE_OK);
167
168 // level 2
169 mp_subcheck sc2 = mp_subcheck_init();
170 sc2.output = "baz";
171 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
172
173 // level 1
174 mp_subcheck sc1 = mp_subcheck_init();
175
176 sc1.output = "foobar";
177 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
178
179 mp_perfdata pd1 = perfdata_init();
180
181 pd1.uom = "s";
182 pd1.label = "foo";
183 pd1 = mp_set_pd_value(pd1, 23);
184
185 mp_add_perfdata_to_subcheck(&sc1, pd1);
186
187 // main check
188 mp_check check = mp_check_init();
189
190 mp_add_subcheck_to_subcheck(&sc3, sc4);
191 mp_add_subcheck_to_subcheck(&sc2, sc3);
192 mp_add_subcheck_to_subcheck(&sc1, sc2);
193 mp_add_subcheck_to_check(&check, sc1);
194
195 char *output = mp_fmt_output(check);
196
197 size_t output_length = strlen(output);
198
199 // diag("Formatted output of length %i", output_length);
200 // diag(output);
201
202 ok(output != NULL, "Output should not be NULL");
203
204 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
205 "\t\\_[WARNING] - foobar\n"
206 "\t\t\\_[OK] - baz\n"
207 "\t\t\t\\_[OK] - level3\n"
208 "\t\t\t\t\\_[OK] - level4\n"
209 "|foo=23s;;; \n";
210
211 size_t expected_length = strlen(expected);
212
213 // diag("Expected output of length: %i", expected_length);
214 // diag(expected);
215
216 ok(output_length == expected_length, "Outputs are of equal length");
217 ok(strcmp(output, expected) == 0, "Output is as expected");
218}
219
220void test_deep_check_hierarchy2(void) {
221 // level 1
222 mp_subcheck sc1 = mp_subcheck_init();
223
224 sc1.output = "foobar";
225 sc1 = mp_set_subcheck_state(sc1, STATE_WARNING);
226
227 mp_perfdata pd1 = perfdata_init();
228 pd1.uom = "s";
229 pd1.label = "foo";
230 pd1 = mp_set_pd_value(pd1, 23);
231
232 mp_add_perfdata_to_subcheck(&sc1, pd1);
233
234 // level 2
235 mp_subcheck sc2 = mp_subcheck_init();
236 sc2.output = "baz";
237 sc2 = mp_set_subcheck_state(sc2, STATE_OK);
238
239 mp_perfdata pd2 = perfdata_init();
240 pd2.uom = "B";
241 pd2.label = "baz";
242 pd2 = mp_set_pd_value(pd2, 1024);
243 mp_add_perfdata_to_subcheck(&sc2, pd2);
244
245 // level 3
246 mp_subcheck sc3 = mp_subcheck_init();
247 sc3.output = "level3";
248 sc3 = mp_set_subcheck_state(sc3, STATE_OK);
249
250 mp_perfdata pd3 = perfdata_init();
251 pd3.label = "floatMe";
252 pd3 = mp_set_pd_value(pd3, 1024.1024);
253 mp_add_perfdata_to_subcheck(&sc3, pd3);
254
255 // level 4
256 mp_subcheck sc4 = mp_subcheck_init();
257 sc4.output = "level4";
258 sc4 = mp_set_subcheck_state(sc4, STATE_OK);
259
260 mp_check check = mp_check_init();
261
262 mp_add_subcheck_to_subcheck(&sc3, sc4);
263 mp_add_subcheck_to_subcheck(&sc2, sc3);
264 mp_add_subcheck_to_subcheck(&sc1, sc2);
265 mp_add_subcheck_to_check(&check, sc1);
266
267 char *output = mp_fmt_output(check);
268
269 // diag("Formatted output of length: %i", strlen(output));
270 // diag(output);
271
272 ok(output != NULL, "Output should not be NULL");
273
274 char expected[] = "[WARNING] - ok=0, warning=1, critical=0, unknown=0\n"
275 "\t\\_[WARNING] - foobar\n"
276 "\t\t\\_[OK] - baz\n"
277 "\t\t\t\\_[OK] - level3\n"
278 "\t\t\t\t\\_[OK] - level4\n"
279 "|foo=23s;;; baz=1024B;;; floatMe=1024.102400;;; \n";
280
281 // diag("Expected output of length: %i", strlen(expected));
282 // diag(expected);
283
284 ok(strcmp(output, expected) == 0, "Output is as expected");
285}
286
287void test_default_states1(void) {
288 mp_subcheck sc = mp_subcheck_init();
289
290 mp_state_enum state1 = mp_compute_subcheck_state(sc);
291 ok(state1 == STATE_UNKNOWN, "Default default state is Unknown");
292
293 sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
294
295 mp_state_enum state2 = mp_compute_subcheck_state(sc);
296 ok(state2 == STATE_CRITICAL, "Default state is Critical");
297
298 sc = mp_set_subcheck_state(sc, STATE_OK);
299
300 mp_state_enum state3 = mp_compute_subcheck_state(sc);
301 ok(state3 == STATE_OK, "Default state is Critical");
302}
303
304void test_default_states2(void) {
305 mp_check check = mp_check_init();
306
307 mp_subcheck sc = mp_subcheck_init();
308 sc.output = "placeholder";
309 sc = mp_set_subcheck_default_state(sc, STATE_CRITICAL);
310
311 mp_add_subcheck_to_check(&check, sc);
312
313 mp_state_enum result_state = mp_compute_check_state(check);
314 ok(result_state == STATE_CRITICAL, "Derived state is the proper default state");
315}
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 @@
1#!/usr/bin/perl
2use Test::More;
3if (! -e "./test_generic_output") {
4 plan skip_all => "./test_generic_output not compiled - please enable libtap library to test";
5}
6exec "./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 @@
1#include "./thresholds.h"
2#include "./utils_base.h"
3#include "perfdata.h"
4
5#include <stddef.h>
6
7mp_thresholds mp_thresholds_init() {
8 mp_thresholds tmp = {
9 .critical = {},
10 .critical_is_set = false,
11 .warning = {},
12 .warning_is_set = false,
13 };
14 return tmp;
15}
16
17char *fmt_threshold_warning(const thresholds th) {
18 if (th.warning == NULL) {
19 return "";
20 }
21
22 return fmt_range(*th.warning);
23}
24
25char *fmt_threshold_critical(const thresholds th) {
26 if (th.critical == NULL) {
27 return "";
28 }
29 return fmt_range(*th.critical);
30}
31
32mp_perfdata mp_pd_set_thresholds(mp_perfdata perfdata, mp_thresholds threshold) {
33 if (threshold.critical_is_set) {
34 perfdata.crit = threshold.critical;
35 perfdata.crit_present = true;
36 }
37
38 if (threshold.warning_is_set) {
39 perfdata.warn = threshold.warning;
40 perfdata.warn_present = true;
41 }
42
43 return perfdata;
44}
45
46mp_state_enum mp_get_pd_status(mp_perfdata perfdata) {
47 if (perfdata.crit_present) {
48 if (mp_check_range(perfdata.value, perfdata.crit)) {
49 return STATE_CRITICAL;
50 }
51 }
52 if (perfdata.warn_present) {
53 if (mp_check_range(perfdata.value, perfdata.warn)) {
54 return STATE_CRITICAL;
55 }
56 }
57
58 return STATE_OK;
59}
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 @@
1#pragma once
2
3#include "./perfdata.h"
4#include "states.h"
5
6/*
7 * Old threshold type using the old range type
8 */
9typedef struct thresholds_struct {
10 range *warning;
11 range *critical;
12} thresholds;
13
14typedef struct mp_thresholds_struct {
15 bool warning_is_set;
16 mp_range warning;
17 bool critical_is_set;
18 mp_range critical;
19} mp_thresholds;
20
21mp_thresholds mp_thresholds_init(void);
22
23mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */);
24
25mp_state_enum mp_get_pd_status(mp_perfdata /* pd */);
26
27char *fmt_threshold_warning(thresholds th);
28char *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) {
55 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 55 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
56 } 56 }
57 this_monitoring_plugin->plugin_name = strdup(plugin_name); 57 this_monitoring_plugin->plugin_name = strdup(plugin_name);
58 if (this_monitoring_plugin->plugin_name == NULL) 58 if (this_monitoring_plugin->plugin_name == NULL) {
59 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); 59 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
60 }
60 this_monitoring_plugin->argc = argc; 61 this_monitoring_plugin->argc = argc;
61 this_monitoring_plugin->argv = argv; 62 this_monitoring_plugin->argv = argv;
62 } 63 }
63} 64}
64 65
65void np_set_args(int argc, char **argv) { 66void np_set_args(int argc, char **argv) {
66 if (this_monitoring_plugin == NULL) 67 if (this_monitoring_plugin == NULL) {
67 die(STATE_UNKNOWN, _("This requires np_init to be called")); 68 die(STATE_UNKNOWN, _("This requires np_init to be called"));
69 }
68 70
69 this_monitoring_plugin->argc = argc; 71 this_monitoring_plugin->argc = argc;
70 this_monitoring_plugin->argv = argv; 72 this_monitoring_plugin->argv = argv;
71} 73}
72 74
73void np_cleanup() { 75void np_cleanup(void) {
74 if (this_monitoring_plugin != NULL) { 76 if (this_monitoring_plugin != NULL) {
75 if (this_monitoring_plugin->state != NULL) { 77 if (this_monitoring_plugin->state != NULL) {
76 if (this_monitoring_plugin->state->state_data) { 78 if (this_monitoring_plugin->state->state_data) {
@@ -162,8 +164,9 @@ range *parse_range_string(char *str) {
162int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) { 164int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
163 thresholds *temp_thresholds = NULL; 165 thresholds *temp_thresholds = NULL;
164 166
165 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) 167 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) {
166 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 168 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
169 }
167 170
168 temp_thresholds->warning = NULL; 171 temp_thresholds->warning = NULL;
169 temp_thresholds->critical = NULL; 172 temp_thresholds->critical = NULL;
@@ -215,6 +218,46 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
215 printf("\n"); 218 printf("\n");
216} 219}
217 220
221/* Returns true if alert should be raised based on the range, false otherwise */
222bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
223 bool is_inside = false;
224
225 if (my_range.end_infinity == false && my_range.start_infinity == false) {
226 // range: .........|---inside---|...........
227 // value
228 if ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)) {
229 is_inside = true;
230 } else {
231 is_inside = false;
232 }
233 } else if (my_range.start_infinity == false && my_range.end_infinity == true) {
234 // range: .........|---inside---------
235 // value
236 if (cmp_perfdata_value(my_range.start, value) < 0) {
237 is_inside = true;
238 } else {
239 is_inside = false;
240 }
241 } else if (my_range.start_infinity == true && my_range.end_infinity == false) {
242 // range: -inside--------|....................
243 // value
244 if (cmp_perfdata_value(value, my_range.end) == -1) {
245 is_inside = true;
246 } else {
247 is_inside = false;
248 }
249 } else {
250 // range from -inf to inf, so always inside
251 is_inside = true;
252 }
253
254 if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
255 return true;
256 }
257
258 return false;
259}
260
218/* Returns true if alert should be raised based on the range */ 261/* Returns true if alert should be raised based on the range */
219bool check_range(double value, range *my_range) { 262bool check_range(double value, range *my_range) {
220 bool no = false; 263 bool no = false;
@@ -228,24 +271,24 @@ bool check_range(double value, range *my_range) {
228 if (my_range->end_infinity == false && my_range->start_infinity == false) { 271 if (my_range->end_infinity == false && my_range->start_infinity == false) {
229 if ((my_range->start <= value) && (value <= my_range->end)) { 272 if ((my_range->start <= value) && (value <= my_range->end)) {
230 return no; 273 return no;
231 } else {
232 return yes;
233 } 274 }
234 } else if (my_range->start_infinity == false && my_range->end_infinity == true) { 275 return yes;
276 }
277
278 if (my_range->start_infinity == false && my_range->end_infinity == true) {
235 if (my_range->start <= value) { 279 if (my_range->start <= value) {
236 return no; 280 return no;
237 } else {
238 return yes;
239 } 281 }
240 } else if (my_range->start_infinity == true && my_range->end_infinity == false) { 282 return yes;
283 }
284
285 if (my_range->start_infinity == true && my_range->end_infinity == false) {
241 if (value <= my_range->end) { 286 if (value <= my_range->end) {
242 return no; 287 return no;
243 } else {
244 return yes;
245 } 288 }
246 } else { 289 return yes;
247 return no;
248 } 290 }
291 return no;
249} 292}
250 293
251/* Returns status */ 294/* Returns status */
@@ -265,7 +308,8 @@ int get_status(double value, thresholds *my_thresholds) {
265 308
266char *np_escaped_string(const char *string) { 309char *np_escaped_string(const char *string) {
267 char *data; 310 char *data;
268 int i, j = 0; 311 int i;
312 int j = 0;
269 data = strdup(string); 313 data = strdup(string);
270 for (i = 0; data[i]; i++) { 314 for (i = 0; data[i]; i++) {
271 if (data[i] == '\\') { 315 if (data[i] == '\\') {
@@ -302,7 +346,8 @@ int np_check_if_root(void) { return (geteuid() == 0); }
302 * data strings. 346 * data strings.
303 */ 347 */
304char *np_extract_value(const char *varlist, const char *name, char sep) { 348char *np_extract_value(const char *varlist, const char *name, char sep) {
305 char *tmp = NULL, *value = NULL; 349 char *tmp = NULL;
350 char *value = NULL;
306 int i; 351 int i;
307 352
308 while (1) { 353 while (1) {
@@ -325,15 +370,17 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
325 370
326 if ((tmp = index(varlist, sep))) { 371 if ((tmp = index(varlist, sep))) {
327 /* Value is delimited by a comma */ 372 /* Value is delimited by a comma */
328 if (tmp - varlist == 0) 373 if (tmp - varlist == 0) {
329 continue; 374 continue;
375 }
330 value = (char *)calloc(1, tmp - varlist + 1); 376 value = (char *)calloc(1, tmp - varlist + 1);
331 strncpy(value, varlist, tmp - varlist); 377 strncpy(value, varlist, tmp - varlist);
332 value[tmp - varlist] = '\0'; 378 value[tmp - varlist] = '\0';
333 } else { 379 } else {
334 /* Value is delimited by a \0 */ 380 /* Value is delimited by a \0 */
335 if (strlen(varlist) == 0) 381 if (strlen(varlist) == 0) {
336 continue; 382 continue;
383 }
337 value = (char *)calloc(1, strlen(varlist) + 1); 384 value = (char *)calloc(1, strlen(varlist) + 1);
338 strncpy(value, varlist, strlen(varlist)); 385 strncpy(value, varlist, strlen(varlist));
339 value[strlen(varlist)] = '\0'; 386 value[strlen(varlist)] = '\0';
@@ -351,9 +398,11 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
351 } 398 }
352 399
353 /* Clean-up trailing spaces/newlines */ 400 /* Clean-up trailing spaces/newlines */
354 if (value) 401 if (value) {
355 for (i = strlen(value) - 1; isspace(value[i]); i--) 402 for (i = strlen(value) - 1; isspace(value[i]); i--) {
356 value[i] = '\0'; 403 value[i] = '\0';
404 }
405 }
357 406
358 return value; 407 return value;
359} 408}
@@ -378,14 +427,18 @@ const char *state_text(int result) {
378 * return the corresponding STATE_ value or ERROR) 427 * return the corresponding STATE_ value or ERROR)
379 */ 428 */
380int mp_translate_state(char *state_text) { 429int mp_translate_state(char *state_text) {
381 if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) 430 if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) {
382 return STATE_OK; 431 return STATE_OK;
383 if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) 432 }
433 if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) {
384 return STATE_WARNING; 434 return STATE_WARNING;
385 if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) 435 }
436 if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) {
386 return STATE_CRITICAL; 437 return STATE_CRITICAL;
387 if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) 438 }
439 if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) {
388 return STATE_UNKNOWN; 440 return STATE_UNKNOWN;
441 }
389 return ERROR; 442 return ERROR;
390} 443}
391 444
@@ -394,7 +447,7 @@ int mp_translate_state(char *state_text) {
394 * hopefully a unique key per service/plugin invocation. Use the extra-opts 447 * hopefully a unique key per service/plugin invocation. Use the extra-opts
395 * parse of argv, so that uniqueness in parameters are reflected there. 448 * parse of argv, so that uniqueness in parameters are reflected there.
396 */ 449 */
397char *_np_state_generate_key() { 450char *_np_state_generate_key(void) {
398 int i; 451 int i;
399 char **argv = this_monitoring_plugin->argv; 452 char **argv = this_monitoring_plugin->argv;
400 char keyname[41]; 453 char keyname[41];
@@ -441,7 +494,7 @@ char *_np_state_generate_key() {
441 return p; 494 return p;
442} 495}
443 496
444void _cleanup_state_data() { 497void _cleanup_state_data(void) {
445 if (this_monitoring_plugin->state->state_data != NULL) { 498 if (this_monitoring_plugin->state->state_data != NULL) {
446 np_free(this_monitoring_plugin->state->state_data->data); 499 np_free(this_monitoring_plugin->state->state_data->data);
447 np_free(this_monitoring_plugin->state->state_data); 500 np_free(this_monitoring_plugin->state->state_data);
@@ -453,19 +506,21 @@ void _cleanup_state_data() {
453 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY 506 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
454 * statically compiled shared state directory 507 * statically compiled shared state directory
455 */ 508 */
456char *_np_state_calculate_location_prefix() { 509char *_np_state_calculate_location_prefix(void) {
457 char *env_dir; 510 char *env_dir;
458 511
459 /* Do not allow passing MP_STATE_PATH in setuid plugins 512 /* Do not allow passing MP_STATE_PATH in setuid plugins
460 * for security reasons */ 513 * for security reasons */
461 if (!mp_suid()) { 514 if (!mp_suid()) {
462 env_dir = getenv("MP_STATE_PATH"); 515 env_dir = getenv("MP_STATE_PATH");
463 if (env_dir && env_dir[0] != '\0') 516 if (env_dir && env_dir[0] != '\0') {
464 return env_dir; 517 return env_dir;
518 }
465 /* This is the former ENV, for backward-compatibility */ 519 /* This is the former ENV, for backward-compatibility */
466 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); 520 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
467 if (env_dir && env_dir[0] != '\0') 521 if (env_dir && env_dir[0] != '\0') {
468 return env_dir; 522 return env_dir;
523 }
469 } 524 }
470 525
471 return NP_STATE_DIR_PREFIX; 526 return NP_STATE_DIR_PREFIX;
@@ -483,19 +538,22 @@ void np_enable_state(char *keyname, int expected_data_version) {
483 char *p = NULL; 538 char *p = NULL;
484 int ret; 539 int ret;
485 540
486 if (this_monitoring_plugin == NULL) 541 if (this_monitoring_plugin == NULL) {
487 die(STATE_UNKNOWN, _("This requires np_init to be called")); 542 die(STATE_UNKNOWN, _("This requires np_init to be called"));
543 }
488 544
489 this_state = (state_key *)calloc(1, sizeof(state_key)); 545 this_state = (state_key *)calloc(1, sizeof(state_key));
490 if (this_state == NULL) 546 if (this_state == NULL) {
491 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 547 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
548 }
492 549
493 if (keyname == NULL) { 550 if (keyname == NULL) {
494 temp_keyname = _np_state_generate_key(); 551 temp_keyname = _np_state_generate_key();
495 } else { 552 } else {
496 temp_keyname = strdup(keyname); 553 temp_keyname = strdup(keyname);
497 if (temp_keyname == NULL) 554 if (temp_keyname == NULL) {
498 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); 555 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
556 }
499 } 557 }
500 /* Die if invalid characters used for keyname */ 558 /* Die if invalid characters used for keyname */
501 p = temp_keyname; 559 p = temp_keyname;
@@ -513,8 +571,9 @@ void np_enable_state(char *keyname, int expected_data_version) {
513 /* Calculate filename */ 571 /* Calculate filename */
514 ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(), 572 ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
515 this_monitoring_plugin->plugin_name, this_state->name); 573 this_monitoring_plugin->plugin_name, this_state->name);
516 if (ret < 0) 574 if (ret < 0) {
517 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 575 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
576 }
518 577
519 this_state->_filename = temp_filename; 578 this_state->_filename = temp_filename;
520 579
@@ -528,21 +587,23 @@ void np_enable_state(char *keyname, int expected_data_version) {
528 * If numerically lower, then return as no previous state. die with UNKNOWN 587 * If numerically lower, then return as no previous state. die with UNKNOWN
529 * if exceptional error. 588 * if exceptional error.
530 */ 589 */
531state_data *np_state_read() { 590state_data *np_state_read(void) {
532 state_data *this_state_data = NULL; 591 state_data *this_state_data = NULL;
533 FILE *statefile; 592 FILE *statefile;
534 bool rc = false; 593 bool rc = false;
535 594
536 if (this_monitoring_plugin == NULL) 595 if (this_monitoring_plugin == NULL) {
537 die(STATE_UNKNOWN, _("This requires np_init to be called")); 596 die(STATE_UNKNOWN, _("This requires np_init to be called"));
597 }
538 598
539 /* Open file. If this fails, no previous state found */ 599 /* Open file. If this fails, no previous state found */
540 statefile = fopen(this_monitoring_plugin->state->_filename, "r"); 600 statefile = fopen(this_monitoring_plugin->state->_filename, "r");
541 if (statefile != NULL) { 601 if (statefile != NULL) {
542 602
543 this_state_data = (state_data *)calloc(1, sizeof(state_data)); 603 this_state_data = (state_data *)calloc(1, sizeof(state_data));
544 if (this_state_data == NULL) 604 if (this_state_data == NULL) {
545 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 605 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
606 }
546 607
547 this_state_data->data = NULL; 608 this_state_data->data = NULL;
548 this_monitoring_plugin->state->state_data = this_state_data; 609 this_monitoring_plugin->state->state_data = this_state_data;
@@ -581,8 +642,9 @@ bool _np_state_read_file(FILE *f) {
581 642
582 /* Note: This introduces a limit of 1024 bytes in the string data */ 643 /* Note: This introduces a limit of 1024 bytes in the string data */
583 line = (char *)calloc(1, 1024); 644 line = (char *)calloc(1, 1024);
584 if (line == NULL) 645 if (line == NULL) {
585 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 646 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
647 }
586 648
587 while (!failure && (fgets(line, 1024, f)) != NULL) { 649 while (!failure && (fgets(line, 1024, f)) != NULL) {
588 pos = strlen(line); 650 pos = strlen(line);
@@ -590,38 +652,42 @@ bool _np_state_read_file(FILE *f) {
590 line[pos - 1] = '\0'; 652 line[pos - 1] = '\0';
591 } 653 }
592 654
593 if (line[0] == '#') 655 if (line[0] == '#') {
594 continue; 656 continue;
657 }
595 658
596 switch (expected) { 659 switch (expected) {
597 case STATE_FILE_VERSION: 660 case STATE_FILE_VERSION:
598 i = atoi(line); 661 i = atoi(line);
599 if (i != NP_STATE_FORMAT_VERSION) 662 if (i != NP_STATE_FORMAT_VERSION) {
600 failure++; 663 failure++;
601 else 664 } else {
602 expected = STATE_DATA_VERSION; 665 expected = STATE_DATA_VERSION;
666 }
603 break; 667 break;
604 case STATE_DATA_VERSION: 668 case STATE_DATA_VERSION:
605 i = atoi(line); 669 i = atoi(line);
606 if (i != this_monitoring_plugin->state->data_version) 670 if (i != this_monitoring_plugin->state->data_version) {
607 failure++; 671 failure++;
608 else 672 } else {
609 expected = STATE_DATA_TIME; 673 expected = STATE_DATA_TIME;
674 }
610 break; 675 break;
611 case STATE_DATA_TIME: 676 case STATE_DATA_TIME:
612 /* If time > now, error */ 677 /* If time > now, error */
613 data_time = strtoul(line, NULL, 10); 678 data_time = strtoul(line, NULL, 10);
614 if (data_time > current_time) 679 if (data_time > current_time) {
615 failure++; 680 failure++;
616 else { 681 } else {
617 this_monitoring_plugin->state->state_data->time = data_time; 682 this_monitoring_plugin->state->state_data->time = data_time;
618 expected = STATE_DATA_TEXT; 683 expected = STATE_DATA_TEXT;
619 } 684 }
620 break; 685 break;
621 case STATE_DATA_TEXT: 686 case STATE_DATA_TEXT:
622 this_monitoring_plugin->state->state_data->data = strdup(line); 687 this_monitoring_plugin->state->state_data->data = strdup(line);
623 if (this_monitoring_plugin->state->state_data->data == NULL) 688 if (this_monitoring_plugin->state->state_data->data == NULL) {
624 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); 689 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
690 }
625 expected = STATE_DATA_END; 691 expected = STATE_DATA_END;
626 status = true; 692 status = true;
627 break; 693 break;
@@ -648,16 +714,18 @@ void np_state_write_string(time_t data_time, char *data_string) {
648 char *directories = NULL; 714 char *directories = NULL;
649 char *p = NULL; 715 char *p = NULL;
650 716
651 if (data_time == 0) 717 if (data_time == 0) {
652 time(&current_time); 718 time(&current_time);
653 else 719 } else {
654 current_time = data_time; 720 current_time = data_time;
721 }
655 722
656 /* If file doesn't currently exist, create directories */ 723 /* If file doesn't currently exist, create directories */
657 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { 724 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
658 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); 725 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
659 if (result < 0) 726 if (result < 0) {
660 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 727 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
728 }
661 729
662 for (p = directories + 1; *p; p++) { 730 for (p = directories + 1; *p; p++) {
663 if (*p == '/') { 731 if (*p == '/') {
@@ -674,8 +742,9 @@ void np_state_write_string(time_t data_time, char *data_string) {
674 } 742 }
675 743
676 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); 744 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
677 if (result < 0) 745 if (result < 0) {
678 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); 746 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
747 }
679 748
680 if ((fd = mkstemp(temp_file)) == -1) { 749 if ((fd = mkstemp(temp_file)) == -1) {
681 np_free(temp_file); 750 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 @@
2#define _UTILS_BASE_ 2#define _UTILS_BASE_
3/* Header file for Monitoring Plugins utils_base.c */ 3/* Header file for Monitoring Plugins utils_base.c */
4 4
5#include "../config.h"
6#include <time.h>
7
8#include "./perfdata.h"
9#include "./thresholds.h"
10
11
5#ifndef USE_OPENSSL 12#ifndef USE_OPENSSL
6# include "sha256.h" 13# include "sha256.h"
7#endif 14#endif
@@ -19,20 +26,6 @@
19#define OUTSIDE 0 26#define OUTSIDE 0
20#define INSIDE 1 27#define INSIDE 1
21 28
22typedef struct range_struct {
23 double start;
24 bool start_infinity;
25 double end;
26 int end_infinity;
27 int alert_on; /* OUTSIDE (default) or INSIDE */
28 char *text; /* original unparsed text input */
29} range;
30
31typedef struct thresholds_struct {
32 range *warning;
33 range *critical;
34} thresholds;
35
36#define NP_STATE_FORMAT_VERSION 1 29#define NP_STATE_FORMAT_VERSION 1
37 30
38typedef struct state_data_struct { 31typedef struct state_data_struct {
@@ -61,6 +54,7 @@ int _set_thresholds(thresholds **, char *, char *);
61void set_thresholds(thresholds **, char *, char *); 54void set_thresholds(thresholds **, char *, char *);
62void print_thresholds(const char *, thresholds *); 55void print_thresholds(const char *, thresholds *);
63bool check_range(double, range *); 56bool check_range(double, range *);
57bool mp_check_range(mp_perfdata_value, mp_range);
64int get_status(double, thresholds *); 58int get_status(double, thresholds *);
65 59
66/* Handle timeouts */ 60/* Handle timeouts */
@@ -107,12 +101,12 @@ char *np_extract_value(const char *, const char *, char);
107int mp_translate_state(char *); 101int mp_translate_state(char *);
108 102
109void np_enable_state(char *, int); 103void np_enable_state(char *, int);
110state_data *np_state_read(); 104state_data *np_state_read(void);
111void np_state_write_string(time_t, char *); 105void np_state_write_string(time_t, char *);
112 106
113void np_init(char *, int argc, char **argv); 107void np_init(char *, int argc, char **argv);
114void np_set_args(int argc, char **argv); 108void np_set_args(int argc, char **argv);
115void np_cleanup(); 109void np_cleanup(void);
116const char *state_text(int); 110const char *state_text(int);
117 111
118#endif /* _UTILS_BASE_ */ 112#endif /* _UTILS_BASE_ */
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 @@
1/*
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21*/
22
23/* cJSON */
24/* JSON parser in C. */
25
26/* disable warnings about old C89 functions in MSVC */
27#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
28#define _CRT_SECURE_NO_DEPRECATE
29#endif
30
31#ifdef __GNUC__
32#pragma GCC visibility push(default)
33#endif
34#if defined(_MSC_VER)
35#pragma warning (push)
36/* disable warning about single line comments in system headers */
37#pragma warning (disable : 4001)
38#endif
39
40#include "../../../config.h"
41#include <string.h>
42#include <stdio.h>
43#include <math.h>
44#include <stdlib.h>
45#include <limits.h>
46#include <ctype.h>
47#include <float.h>
48
49#ifdef ENABLE_LOCALES
50#include <locale.h>
51#endif
52
53#if defined(_MSC_VER)
54#pragma warning (pop)
55#endif
56#ifdef __GNUC__
57#pragma GCC visibility pop
58#endif
59
60#include "cJSON.h"
61
62/* define our own boolean type */
63#ifdef true
64#undef true
65#endif
66#define true ((cJSON_bool)1)
67
68#ifdef false
69#undef false
70#endif
71#define false ((cJSON_bool)0)
72
73/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
74#ifndef isinf
75#define isinf(d) (isnan((d - d)) && !isnan(d))
76#endif
77#ifndef isnan
78#define isnan(d) (d != d)
79#endif
80
81#ifndef NAN
82#ifdef _WIN32
83#define NAN sqrt(-1.0)
84#else
85#define NAN 0.0/0.0
86#endif
87#endif
88
89typedef struct {
90 const unsigned char *json;
91 size_t position;
92} error;
93static error global_error = { NULL, 0 };
94
95CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
96{
97 return (const char*) (global_error.json + global_error.position);
98}
99
100CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
101{
102 if (!cJSON_IsString(item))
103 {
104 return NULL;
105 }
106
107 return item->valuestring;
108}
109
110CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
111{
112 if (!cJSON_IsNumber(item))
113 {
114 return (double) NAN;
115 }
116
117 return item->valuedouble;
118}
119
120/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
121#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
122 #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
123#endif
124
125CJSON_PUBLIC(const char*) cJSON_Version(void)
126{
127 static char version[15];
128 sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
129
130 return version;
131}
132
133/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
134static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
135{
136 if ((string1 == NULL) || (string2 == NULL))
137 {
138 return 1;
139 }
140
141 if (string1 == string2)
142 {
143 return 0;
144 }
145
146 for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
147 {
148 if (*string1 == '\0')
149 {
150 return 0;
151 }
152 }
153
154 return tolower(*string1) - tolower(*string2);
155}
156
157typedef struct internal_hooks
158{
159 void *(CJSON_CDECL *allocate)(size_t size);
160 void (CJSON_CDECL *deallocate)(void *pointer);
161 void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
162} internal_hooks;
163
164#if defined(_MSC_VER)
165/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
166static void * CJSON_CDECL internal_malloc(size_t size)
167{
168 return malloc(size);
169}
170static void CJSON_CDECL internal_free(void *pointer)
171{
172 free(pointer);
173}
174static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
175{
176 return realloc(pointer, size);
177}
178#else
179#define internal_malloc malloc
180#define internal_free free
181#define internal_realloc realloc
182#endif
183
184/* strlen of character literals resolved at compile time */
185#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
186
187static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
188
189static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
190{
191 size_t length = 0;
192 unsigned char *copy = NULL;
193
194 if (string == NULL)
195 {
196 return NULL;
197 }
198
199 length = strlen((const char*)string) + sizeof("");
200 copy = (unsigned char*)hooks->allocate(length);
201 if (copy == NULL)
202 {
203 return NULL;
204 }
205 memcpy(copy, string, length);
206
207 return copy;
208}
209
210CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
211{
212 if (hooks == NULL)
213 {
214 /* Reset hooks */
215 global_hooks.allocate = malloc;
216 global_hooks.deallocate = free;
217 global_hooks.reallocate = realloc;
218 return;
219 }
220
221 global_hooks.allocate = malloc;
222 if (hooks->malloc_fn != NULL)
223 {
224 global_hooks.allocate = hooks->malloc_fn;
225 }
226
227 global_hooks.deallocate = free;
228 if (hooks->free_fn != NULL)
229 {
230 global_hooks.deallocate = hooks->free_fn;
231 }
232
233 /* use realloc only if both free and malloc are used */
234 global_hooks.reallocate = NULL;
235 if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
236 {
237 global_hooks.reallocate = realloc;
238 }
239}
240
241/* Internal constructor. */
242static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
243{
244 cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
245 if (node)
246 {
247 memset(node, '\0', sizeof(cJSON));
248 }
249
250 return node;
251}
252
253/* Delete a cJSON structure. */
254CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
255{
256 cJSON *next = NULL;
257 while (item != NULL)
258 {
259 next = item->next;
260 if (!(item->type & cJSON_IsReference) && (item->child != NULL))
261 {
262 cJSON_Delete(item->child);
263 }
264 if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
265 {
266 global_hooks.deallocate(item->valuestring);
267 item->valuestring = NULL;
268 }
269 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
270 {
271 global_hooks.deallocate(item->string);
272 item->string = NULL;
273 }
274 global_hooks.deallocate(item);
275 item = next;
276 }
277}
278
279/* get the decimal point character of the current locale */
280static unsigned char get_decimal_point(void)
281{
282#ifdef ENABLE_LOCALES
283 struct lconv *lconv = localeconv();
284 return (unsigned char) lconv->decimal_point[0];
285#else
286 return '.';
287#endif
288}
289
290typedef struct
291{
292 const unsigned char *content;
293 size_t length;
294 size_t offset;
295 size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
296 internal_hooks hooks;
297} parse_buffer;
298
299/* check if the given size is left to read in a given parse buffer (starting with 1) */
300#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
301/* check if the buffer can be accessed at the given index (starting with 0) */
302#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
303#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
304/* get a pointer to the buffer at the position */
305#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
306
307/* Parse the input text to generate a number, and populate the result into item. */
308static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
309{
310 double number = 0;
311 unsigned char *after_end = NULL;
312 unsigned char number_c_string[64];
313 unsigned char decimal_point = get_decimal_point();
314 size_t i = 0;
315
316 if ((input_buffer == NULL) || (input_buffer->content == NULL))
317 {
318 return false;
319 }
320
321 /* copy the number into a temporary buffer and replace '.' with the decimal point
322 * of the current locale (for strtod)
323 * This also takes care of '\0' not necessarily being available for marking the end of the input */
324 for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
325 {
326 switch (buffer_at_offset(input_buffer)[i])
327 {
328 case '0':
329 case '1':
330 case '2':
331 case '3':
332 case '4':
333 case '5':
334 case '6':
335 case '7':
336 case '8':
337 case '9':
338 case '+':
339 case '-':
340 case 'e':
341 case 'E':
342 number_c_string[i] = buffer_at_offset(input_buffer)[i];
343 break;
344
345 case '.':
346 number_c_string[i] = decimal_point;
347 break;
348
349 default:
350 goto loop_end;
351 }
352 }
353loop_end:
354 number_c_string[i] = '\0';
355
356 number = strtod((const char*)number_c_string, (char**)&after_end);
357 if (number_c_string == after_end)
358 {
359 return false; /* parse_error */
360 }
361
362 item->valuedouble = number;
363
364 /* use saturation in case of overflow */
365 if (number >= INT_MAX)
366 {
367 item->valueint = INT_MAX;
368 }
369 else if (number <= (double)INT_MIN)
370 {
371 item->valueint = INT_MIN;
372 }
373 else
374 {
375 item->valueint = (int)number;
376 }
377
378 item->type = cJSON_Number;
379
380 input_buffer->offset += (size_t)(after_end - number_c_string);
381 return true;
382}
383
384/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
385CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
386{
387 if (number >= INT_MAX)
388 {
389 object->valueint = INT_MAX;
390 }
391 else if (number <= (double)INT_MIN)
392 {
393 object->valueint = INT_MIN;
394 }
395 else
396 {
397 object->valueint = (int)number;
398 }
399
400 return object->valuedouble = number;
401}
402
403/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
404CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
405{
406 char *copy = NULL;
407 size_t v1_len;
408 size_t v2_len;
409 /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
410 if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
411 {
412 return NULL;
413 }
414 /* return NULL if the object is corrupted or valuestring is NULL */
415 if (object->valuestring == NULL || valuestring == NULL)
416 {
417 return NULL;
418 }
419
420 v1_len = strlen(valuestring);
421 v2_len = strlen(object->valuestring);
422
423 if (v1_len <= v2_len)
424 {
425 /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */
426 if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring ))
427 {
428 return NULL;
429 }
430 strcpy(object->valuestring, valuestring);
431 return object->valuestring;
432 }
433 copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
434 if (copy == NULL)
435 {
436 return NULL;
437 }
438 if (object->valuestring != NULL)
439 {
440 cJSON_free(object->valuestring);
441 }
442 object->valuestring = copy;
443
444 return copy;
445}
446
447typedef struct
448{
449 unsigned char *buffer;
450 size_t length;
451 size_t offset;
452 size_t depth; /* current nesting depth (for formatted printing) */
453 cJSON_bool noalloc;
454 cJSON_bool format; /* is this print a formatted print */
455 internal_hooks hooks;
456} printbuffer;
457
458/* realloc printbuffer if necessary to have at least "needed" bytes more */
459static unsigned char* ensure(printbuffer * const p, size_t needed)
460{
461 unsigned char *newbuffer = NULL;
462 size_t newsize = 0;
463
464 if ((p == NULL) || (p->buffer == NULL))
465 {
466 return NULL;
467 }
468
469 if ((p->length > 0) && (p->offset >= p->length))
470 {
471 /* make sure that offset is valid */
472 return NULL;
473 }
474
475 if (needed > INT_MAX)
476 {
477 /* sizes bigger than INT_MAX are currently not supported */
478 return NULL;
479 }
480
481 needed += p->offset + 1;
482 if (needed <= p->length)
483 {
484 return p->buffer + p->offset;
485 }
486
487 if (p->noalloc) {
488 return NULL;
489 }
490
491 /* calculate new buffer size */
492 if (needed > (INT_MAX / 2))
493 {
494 /* overflow of int, use INT_MAX if possible */
495 if (needed <= INT_MAX)
496 {
497 newsize = INT_MAX;
498 }
499 else
500 {
501 return NULL;
502 }
503 }
504 else
505 {
506 newsize = needed * 2;
507 }
508
509 if (p->hooks.reallocate != NULL)
510 {
511 /* reallocate with realloc if available */
512 newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
513 if (newbuffer == NULL)
514 {
515 p->hooks.deallocate(p->buffer);
516 p->length = 0;
517 p->buffer = NULL;
518
519 return NULL;
520 }
521 }
522 else
523 {
524 /* otherwise reallocate manually */
525 newbuffer = (unsigned char*)p->hooks.allocate(newsize);
526 if (!newbuffer)
527 {
528 p->hooks.deallocate(p->buffer);
529 p->length = 0;
530 p->buffer = NULL;
531
532 return NULL;
533 }
534
535 memcpy(newbuffer, p->buffer, p->offset + 1);
536 p->hooks.deallocate(p->buffer);
537 }
538 p->length = newsize;
539 p->buffer = newbuffer;
540
541 return newbuffer + p->offset;
542}
543
544/* calculate the new length of the string in a printbuffer and update the offset */
545static void update_offset(printbuffer * const buffer)
546{
547 const unsigned char *buffer_pointer = NULL;
548 if ((buffer == NULL) || (buffer->buffer == NULL))
549 {
550 return;
551 }
552 buffer_pointer = buffer->buffer + buffer->offset;
553
554 buffer->offset += strlen((const char*)buffer_pointer);
555}
556
557/* securely comparison of floating-point variables */
558static cJSON_bool compare_double(double a, double b)
559{
560 double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
561 return (fabs(a - b) <= maxVal * DBL_EPSILON);
562}
563
564/* Render the number nicely from the given item into a string. */
565static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
566{
567 unsigned char *output_pointer = NULL;
568 double d = item->valuedouble;
569 int length = 0;
570 size_t i = 0;
571 unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
572 unsigned char decimal_point = get_decimal_point();
573 double test = 0.0;
574
575 if (output_buffer == NULL)
576 {
577 return false;
578 }
579
580 /* This checks for NaN and Infinity */
581 if (isnan(d) || isinf(d))
582 {
583 length = sprintf((char*)number_buffer, "null");
584 }
585 else if(d == (double)item->valueint)
586 {
587 length = sprintf((char*)number_buffer, "%d", item->valueint);
588 }
589 else
590 {
591 /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
592 length = sprintf((char*)number_buffer, "%1.15g", d);
593
594 /* Check whether the original double can be recovered */
595 if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
596 {
597 /* If not, print with 17 decimal places of precision */
598 length = sprintf((char*)number_buffer, "%1.17g", d);
599 }
600 }
601
602 /* sprintf failed or buffer overrun occurred */
603 if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
604 {
605 return false;
606 }
607
608 /* reserve appropriate space in the output */
609 output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
610 if (output_pointer == NULL)
611 {
612 return false;
613 }
614
615 /* copy the printed number to the output and replace locale
616 * dependent decimal point with '.' */
617 for (i = 0; i < ((size_t)length); i++)
618 {
619 if (number_buffer[i] == decimal_point)
620 {
621 output_pointer[i] = '.';
622 continue;
623 }
624
625 output_pointer[i] = number_buffer[i];
626 }
627 output_pointer[i] = '\0';
628
629 output_buffer->offset += (size_t)length;
630
631 return true;
632}
633
634/* parse 4 digit hexadecimal number */
635static unsigned parse_hex4(const unsigned char * const input)
636{
637 unsigned int h = 0;
638 size_t i = 0;
639
640 for (i = 0; i < 4; i++)
641 {
642 /* parse digit */
643 if ((input[i] >= '0') && (input[i] <= '9'))
644 {
645 h += (unsigned int) input[i] - '0';
646 }
647 else if ((input[i] >= 'A') && (input[i] <= 'F'))
648 {
649 h += (unsigned int) 10 + input[i] - 'A';
650 }
651 else if ((input[i] >= 'a') && (input[i] <= 'f'))
652 {
653 h += (unsigned int) 10 + input[i] - 'a';
654 }
655 else /* invalid */
656 {
657 return 0;
658 }
659
660 if (i < 3)
661 {
662 /* shift left to make place for the next nibble */
663 h = h << 4;
664 }
665 }
666
667 return h;
668}
669
670/* converts a UTF-16 literal to UTF-8
671 * A literal can be one or two sequences of the form \uXXXX */
672static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
673{
674 long unsigned int codepoint = 0;
675 unsigned int first_code = 0;
676 const unsigned char *first_sequence = input_pointer;
677 unsigned char utf8_length = 0;
678 unsigned char utf8_position = 0;
679 unsigned char sequence_length = 0;
680 unsigned char first_byte_mark = 0;
681
682 if ((input_end - first_sequence) < 6)
683 {
684 /* input ends unexpectedly */
685 goto fail;
686 }
687
688 /* get the first utf16 sequence */
689 first_code = parse_hex4(first_sequence + 2);
690
691 /* check that the code is valid */
692 if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
693 {
694 goto fail;
695 }
696
697 /* UTF16 surrogate pair */
698 if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
699 {
700 const unsigned char *second_sequence = first_sequence + 6;
701 unsigned int second_code = 0;
702 sequence_length = 12; /* \uXXXX\uXXXX */
703
704 if ((input_end - second_sequence) < 6)
705 {
706 /* input ends unexpectedly */
707 goto fail;
708 }
709
710 if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
711 {
712 /* missing second half of the surrogate pair */
713 goto fail;
714 }
715
716 /* get the second utf16 sequence */
717 second_code = parse_hex4(second_sequence + 2);
718 /* check that the code is valid */
719 if ((second_code < 0xDC00) || (second_code > 0xDFFF))
720 {
721 /* invalid second half of the surrogate pair */
722 goto fail;
723 }
724
725
726 /* calculate the unicode codepoint from the surrogate pair */
727 codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
728 }
729 else
730 {
731 sequence_length = 6; /* \uXXXX */
732 codepoint = first_code;
733 }
734
735 /* encode as UTF-8
736 * takes at maximum 4 bytes to encode:
737 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
738 if (codepoint < 0x80)
739 {
740 /* normal ascii, encoding 0xxxxxxx */
741 utf8_length = 1;
742 }
743 else if (codepoint < 0x800)
744 {
745 /* two bytes, encoding 110xxxxx 10xxxxxx */
746 utf8_length = 2;
747 first_byte_mark = 0xC0; /* 11000000 */
748 }
749 else if (codepoint < 0x10000)
750 {
751 /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
752 utf8_length = 3;
753 first_byte_mark = 0xE0; /* 11100000 */
754 }
755 else if (codepoint <= 0x10FFFF)
756 {
757 /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
758 utf8_length = 4;
759 first_byte_mark = 0xF0; /* 11110000 */
760 }
761 else
762 {
763 /* invalid unicode codepoint */
764 goto fail;
765 }
766
767 /* encode as utf8 */
768 for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
769 {
770 /* 10xxxxxx */
771 (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
772 codepoint >>= 6;
773 }
774 /* encode first byte */
775 if (utf8_length > 1)
776 {
777 (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
778 }
779 else
780 {
781 (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
782 }
783
784 *output_pointer += utf8_length;
785
786 return sequence_length;
787
788fail:
789 return 0;
790}
791
792/* Parse the input text into an unescaped cinput, and populate item. */
793static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
794{
795 const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
796 const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
797 unsigned char *output_pointer = NULL;
798 unsigned char *output = NULL;
799
800 /* not a string */
801 if (buffer_at_offset(input_buffer)[0] != '\"')
802 {
803 goto fail;
804 }
805
806 {
807 /* calculate approximate size of the output (overestimate) */
808 size_t allocation_length = 0;
809 size_t skipped_bytes = 0;
810 while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
811 {
812 /* is escape sequence */
813 if (input_end[0] == '\\')
814 {
815 if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
816 {
817 /* prevent buffer overflow when last input character is a backslash */
818 goto fail;
819 }
820 skipped_bytes++;
821 input_end++;
822 }
823 input_end++;
824 }
825 if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
826 {
827 goto fail; /* string ended unexpectedly */
828 }
829
830 /* This is at most how much we need for the output */
831 allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
832 output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
833 if (output == NULL)
834 {
835 goto fail; /* allocation failure */
836 }
837 }
838
839 output_pointer = output;
840 /* loop through the string literal */
841 while (input_pointer < input_end)
842 {
843 if (*input_pointer != '\\')
844 {
845 *output_pointer++ = *input_pointer++;
846 }
847 /* escape sequence */
848 else
849 {
850 unsigned char sequence_length = 2;
851 if ((input_end - input_pointer) < 1)
852 {
853 goto fail;
854 }
855
856 switch (input_pointer[1])
857 {
858 case 'b':
859 *output_pointer++ = '\b';
860 break;
861 case 'f':
862 *output_pointer++ = '\f';
863 break;
864 case 'n':
865 *output_pointer++ = '\n';
866 break;
867 case 'r':
868 *output_pointer++ = '\r';
869 break;
870 case 't':
871 *output_pointer++ = '\t';
872 break;
873 case '\"':
874 case '\\':
875 case '/':
876 *output_pointer++ = input_pointer[1];
877 break;
878
879 /* UTF-16 literal */
880 case 'u':
881 sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
882 if (sequence_length == 0)
883 {
884 /* failed to convert UTF16-literal to UTF-8 */
885 goto fail;
886 }
887 break;
888
889 default:
890 goto fail;
891 }
892 input_pointer += sequence_length;
893 }
894 }
895
896 /* zero terminate the output */
897 *output_pointer = '\0';
898
899 item->type = cJSON_String;
900 item->valuestring = (char*)output;
901
902 input_buffer->offset = (size_t) (input_end - input_buffer->content);
903 input_buffer->offset++;
904
905 return true;
906
907fail:
908 if (output != NULL)
909 {
910 input_buffer->hooks.deallocate(output);
911 output = NULL;
912 }
913
914 if (input_pointer != NULL)
915 {
916 input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
917 }
918
919 return false;
920}
921
922/* Render the cstring provided to an escaped version that can be printed. */
923static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
924{
925 const unsigned char *input_pointer = NULL;
926 unsigned char *output = NULL;
927 unsigned char *output_pointer = NULL;
928 size_t output_length = 0;
929 /* numbers of additional characters needed for escaping */
930 size_t escape_characters = 0;
931
932 if (output_buffer == NULL)
933 {
934 return false;
935 }
936
937 /* empty string */
938 if (input == NULL)
939 {
940 output = ensure(output_buffer, sizeof("\"\""));
941 if (output == NULL)
942 {
943 return false;
944 }
945 strcpy((char*)output, "\"\"");
946
947 return true;
948 }
949
950 /* set "flag" to 1 if something needs to be escaped */
951 for (input_pointer = input; *input_pointer; input_pointer++)
952 {
953 switch (*input_pointer)
954 {
955 case '\"':
956 case '\\':
957 case '\b':
958 case '\f':
959 case '\n':
960 case '\r':
961 case '\t':
962 /* one character escape sequence */
963 escape_characters++;
964 break;
965 default:
966 if (*input_pointer < 32)
967 {
968 /* UTF-16 escape sequence uXXXX */
969 escape_characters += 5;
970 }
971 break;
972 }
973 }
974 output_length = (size_t)(input_pointer - input) + escape_characters;
975
976 output = ensure(output_buffer, output_length + sizeof("\"\""));
977 if (output == NULL)
978 {
979 return false;
980 }
981
982 /* no characters have to be escaped */
983 if (escape_characters == 0)
984 {
985 output[0] = '\"';
986 memcpy(output + 1, input, output_length);
987 output[output_length + 1] = '\"';
988 output[output_length + 2] = '\0';
989
990 return true;
991 }
992
993 output[0] = '\"';
994 output_pointer = output + 1;
995 /* copy the string */
996 for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
997 {
998 if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
999 {
1000 /* normal character, copy */
1001 *output_pointer = *input_pointer;
1002 }
1003 else
1004 {
1005 /* character needs to be escaped */
1006 *output_pointer++ = '\\';
1007 switch (*input_pointer)
1008 {
1009 case '\\':
1010 *output_pointer = '\\';
1011 break;
1012 case '\"':
1013 *output_pointer = '\"';
1014 break;
1015 case '\b':
1016 *output_pointer = 'b';
1017 break;
1018 case '\f':
1019 *output_pointer = 'f';
1020 break;
1021 case '\n':
1022 *output_pointer = 'n';
1023 break;
1024 case '\r':
1025 *output_pointer = 'r';
1026 break;
1027 case '\t':
1028 *output_pointer = 't';
1029 break;
1030 default:
1031 /* escape and print as unicode codepoint */
1032 sprintf((char*)output_pointer, "u%04x", *input_pointer);
1033 output_pointer += 4;
1034 break;
1035 }
1036 }
1037 }
1038 output[output_length + 1] = '\"';
1039 output[output_length + 2] = '\0';
1040
1041 return true;
1042}
1043
1044/* Invoke print_string_ptr (which is useful) on an item. */
1045static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
1046{
1047 return print_string_ptr((unsigned char*)item->valuestring, p);
1048}
1049
1050/* Predeclare these prototypes. */
1051static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
1052static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
1053static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
1054static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
1055static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
1056static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
1057
1058/* Utility to jump whitespace and cr/lf */
1059static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
1060{
1061 if ((buffer == NULL) || (buffer->content == NULL))
1062 {
1063 return NULL;
1064 }
1065
1066 if (cannot_access_at_index(buffer, 0))
1067 {
1068 return buffer;
1069 }
1070
1071 while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
1072 {
1073 buffer->offset++;
1074 }
1075
1076 if (buffer->offset == buffer->length)
1077 {
1078 buffer->offset--;
1079 }
1080
1081 return buffer;
1082}
1083
1084/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
1085static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
1086{
1087 if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
1088 {
1089 return NULL;
1090 }
1091
1092 if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
1093 {
1094 buffer->offset += 3;
1095 }
1096
1097 return buffer;
1098}
1099
1100CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
1101{
1102 size_t buffer_length;
1103
1104 if (NULL == value)
1105 {
1106 return NULL;
1107 }
1108
1109 /* Adding null character size due to require_null_terminated. */
1110 buffer_length = strlen(value) + sizeof("");
1111
1112 return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
1113}
1114
1115/* Parse an object - create a new root, and populate. */
1116CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
1117{
1118 parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
1119 cJSON *item = NULL;
1120
1121 /* reset error position */
1122 global_error.json = NULL;
1123 global_error.position = 0;
1124
1125 if (value == NULL || 0 == buffer_length)
1126 {
1127 goto fail;
1128 }
1129
1130 buffer.content = (const unsigned char*)value;
1131 buffer.length = buffer_length;
1132 buffer.offset = 0;
1133 buffer.hooks = global_hooks;
1134
1135 item = cJSON_New_Item(&global_hooks);
1136 if (item == NULL) /* memory fail */
1137 {
1138 goto fail;
1139 }
1140
1141 if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
1142 {
1143 /* parse failure. ep is set. */
1144 goto fail;
1145 }
1146
1147 /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
1148 if (require_null_terminated)
1149 {
1150 buffer_skip_whitespace(&buffer);
1151 if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
1152 {
1153 goto fail;
1154 }
1155 }
1156 if (return_parse_end)
1157 {
1158 *return_parse_end = (const char*)buffer_at_offset(&buffer);
1159 }
1160
1161 return item;
1162
1163fail:
1164 if (item != NULL)
1165 {
1166 cJSON_Delete(item);
1167 }
1168
1169 if (value != NULL)
1170 {
1171 error local_error;
1172 local_error.json = (const unsigned char*)value;
1173 local_error.position = 0;
1174
1175 if (buffer.offset < buffer.length)
1176 {
1177 local_error.position = buffer.offset;
1178 }
1179 else if (buffer.length > 0)
1180 {
1181 local_error.position = buffer.length - 1;
1182 }
1183
1184 if (return_parse_end != NULL)
1185 {
1186 *return_parse_end = (const char*)local_error.json + local_error.position;
1187 }
1188
1189 global_error = local_error;
1190 }
1191
1192 return NULL;
1193}
1194
1195/* Default options for cJSON_Parse */
1196CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
1197{
1198 return cJSON_ParseWithOpts(value, 0, 0);
1199}
1200
1201CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
1202{
1203 return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
1204}
1205
1206#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
1207
1208static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
1209{
1210 static const size_t default_buffer_size = 256;
1211 printbuffer buffer[1];
1212 unsigned char *printed = NULL;
1213
1214 memset(buffer, 0, sizeof(buffer));
1215
1216 /* create buffer */
1217 buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
1218 buffer->length = default_buffer_size;
1219 buffer->format = format;
1220 buffer->hooks = *hooks;
1221 if (buffer->buffer == NULL)
1222 {
1223 goto fail;
1224 }
1225
1226 /* print the value */
1227 if (!print_value(item, buffer))
1228 {
1229 goto fail;
1230 }
1231 update_offset(buffer);
1232
1233 /* check if reallocate is available */
1234 if (hooks->reallocate != NULL)
1235 {
1236 printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
1237 if (printed == NULL) {
1238 goto fail;
1239 }
1240 buffer->buffer = NULL;
1241 }
1242 else /* otherwise copy the JSON over to a new buffer */
1243 {
1244 printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
1245 if (printed == NULL)
1246 {
1247 goto fail;
1248 }
1249 memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
1250 printed[buffer->offset] = '\0'; /* just to be sure */
1251
1252 /* free the buffer */
1253 hooks->deallocate(buffer->buffer);
1254 buffer->buffer = NULL;
1255 }
1256
1257 return printed;
1258
1259fail:
1260 if (buffer->buffer != NULL)
1261 {
1262 hooks->deallocate(buffer->buffer);
1263 buffer->buffer = NULL;
1264 }
1265
1266 if (printed != NULL)
1267 {
1268 hooks->deallocate(printed);
1269 printed = NULL;
1270 }
1271
1272 return NULL;
1273}
1274
1275/* Render a cJSON item/entity/structure to text. */
1276CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
1277{
1278 return (char*)print(item, true, &global_hooks);
1279}
1280
1281CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
1282{
1283 return (char*)print(item, false, &global_hooks);
1284}
1285
1286CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
1287{
1288 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1289
1290 if (prebuffer < 0)
1291 {
1292 return NULL;
1293 }
1294
1295 p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
1296 if (!p.buffer)
1297 {
1298 return NULL;
1299 }
1300
1301 p.length = (size_t)prebuffer;
1302 p.offset = 0;
1303 p.noalloc = false;
1304 p.format = fmt;
1305 p.hooks = global_hooks;
1306
1307 if (!print_value(item, &p))
1308 {
1309 global_hooks.deallocate(p.buffer);
1310 p.buffer = NULL;
1311 return NULL;
1312 }
1313
1314 return (char*)p.buffer;
1315}
1316
1317CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
1318{
1319 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1320
1321 if ((length < 0) || (buffer == NULL))
1322 {
1323 return false;
1324 }
1325
1326 p.buffer = (unsigned char*)buffer;
1327 p.length = (size_t)length;
1328 p.offset = 0;
1329 p.noalloc = true;
1330 p.format = format;
1331 p.hooks = global_hooks;
1332
1333 return print_value(item, &p);
1334}
1335
1336/* Parser core - when encountering text, process appropriately. */
1337static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
1338{
1339 if ((input_buffer == NULL) || (input_buffer->content == NULL))
1340 {
1341 return false; /* no input */
1342 }
1343
1344 /* parse the different types of values */
1345 /* null */
1346 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
1347 {
1348 item->type = cJSON_NULL;
1349 input_buffer->offset += 4;
1350 return true;
1351 }
1352 /* false */
1353 if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
1354 {
1355 item->type = cJSON_False;
1356 input_buffer->offset += 5;
1357 return true;
1358 }
1359 /* true */
1360 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
1361 {
1362 item->type = cJSON_True;
1363 item->valueint = 1;
1364 input_buffer->offset += 4;
1365 return true;
1366 }
1367 /* string */
1368 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
1369 {
1370 return parse_string(item, input_buffer);
1371 }
1372 /* number */
1373 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'))))
1374 {
1375 return parse_number(item, input_buffer);
1376 }
1377 /* array */
1378 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
1379 {
1380 return parse_array(item, input_buffer);
1381 }
1382 /* object */
1383 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
1384 {
1385 return parse_object(item, input_buffer);
1386 }
1387
1388 return false;
1389}
1390
1391/* Render a value to text. */
1392static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
1393{
1394 unsigned char *output = NULL;
1395
1396 if ((item == NULL) || (output_buffer == NULL))
1397 {
1398 return false;
1399 }
1400
1401 switch ((item->type) & 0xFF)
1402 {
1403 case cJSON_NULL:
1404 output = ensure(output_buffer, 5);
1405 if (output == NULL)
1406 {
1407 return false;
1408 }
1409 strcpy((char*)output, "null");
1410 return true;
1411
1412 case cJSON_False:
1413 output = ensure(output_buffer, 6);
1414 if (output == NULL)
1415 {
1416 return false;
1417 }
1418 strcpy((char*)output, "false");
1419 return true;
1420
1421 case cJSON_True:
1422 output = ensure(output_buffer, 5);
1423 if (output == NULL)
1424 {
1425 return false;
1426 }
1427 strcpy((char*)output, "true");
1428 return true;
1429
1430 case cJSON_Number:
1431 return print_number(item, output_buffer);
1432
1433 case cJSON_Raw:
1434 {
1435 size_t raw_length = 0;
1436 if (item->valuestring == NULL)
1437 {
1438 return false;
1439 }
1440
1441 raw_length = strlen(item->valuestring) + sizeof("");
1442 output = ensure(output_buffer, raw_length);
1443 if (output == NULL)
1444 {
1445 return false;
1446 }
1447 memcpy(output, item->valuestring, raw_length);
1448 return true;
1449 }
1450
1451 case cJSON_String:
1452 return print_string(item, output_buffer);
1453
1454 case cJSON_Array:
1455 return print_array(item, output_buffer);
1456
1457 case cJSON_Object:
1458 return print_object(item, output_buffer);
1459
1460 default:
1461 return false;
1462 }
1463}
1464
1465/* Build an array from input text. */
1466static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
1467{
1468 cJSON *head = NULL; /* head of the linked list */
1469 cJSON *current_item = NULL;
1470
1471 if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1472 {
1473 return false; /* to deeply nested */
1474 }
1475 input_buffer->depth++;
1476
1477 if (buffer_at_offset(input_buffer)[0] != '[')
1478 {
1479 /* not an array */
1480 goto fail;
1481 }
1482
1483 input_buffer->offset++;
1484 buffer_skip_whitespace(input_buffer);
1485 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
1486 {
1487 /* empty array */
1488 goto success;
1489 }
1490
1491 /* check if we skipped to the end of the buffer */
1492 if (cannot_access_at_index(input_buffer, 0))
1493 {
1494 input_buffer->offset--;
1495 goto fail;
1496 }
1497
1498 /* step back to character in front of the first element */
1499 input_buffer->offset--;
1500 /* loop through the comma separated array elements */
1501 do
1502 {
1503 /* allocate next item */
1504 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1505 if (new_item == NULL)
1506 {
1507 goto fail; /* allocation failure */
1508 }
1509
1510 /* attach next item to list */
1511 if (head == NULL)
1512 {
1513 /* start the linked list */
1514 current_item = head = new_item;
1515 }
1516 else
1517 {
1518 /* add to the end and advance */
1519 current_item->next = new_item;
1520 new_item->prev = current_item;
1521 current_item = new_item;
1522 }
1523
1524 /* parse next value */
1525 input_buffer->offset++;
1526 buffer_skip_whitespace(input_buffer);
1527 if (!parse_value(current_item, input_buffer))
1528 {
1529 goto fail; /* failed to parse value */
1530 }
1531 buffer_skip_whitespace(input_buffer);
1532 }
1533 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1534
1535 if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
1536 {
1537 goto fail; /* expected end of array */
1538 }
1539
1540success:
1541 input_buffer->depth--;
1542
1543 if (head != NULL) {
1544 head->prev = current_item;
1545 }
1546
1547 item->type = cJSON_Array;
1548 item->child = head;
1549
1550 input_buffer->offset++;
1551
1552 return true;
1553
1554fail:
1555 if (head != NULL)
1556 {
1557 cJSON_Delete(head);
1558 }
1559
1560 return false;
1561}
1562
1563/* Render an array to text */
1564static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
1565{
1566 unsigned char *output_pointer = NULL;
1567 size_t length = 0;
1568 cJSON *current_element = item->child;
1569
1570 if (output_buffer == NULL)
1571 {
1572 return false;
1573 }
1574
1575 /* Compose the output array. */
1576 /* opening square bracket */
1577 output_pointer = ensure(output_buffer, 1);
1578 if (output_pointer == NULL)
1579 {
1580 return false;
1581 }
1582
1583 *output_pointer = '[';
1584 output_buffer->offset++;
1585 output_buffer->depth++;
1586
1587 while (current_element != NULL)
1588 {
1589 if (!print_value(current_element, output_buffer))
1590 {
1591 return false;
1592 }
1593 update_offset(output_buffer);
1594 if (current_element->next)
1595 {
1596 length = (size_t) (output_buffer->format ? 2 : 1);
1597 output_pointer = ensure(output_buffer, length + 1);
1598 if (output_pointer == NULL)
1599 {
1600 return false;
1601 }
1602 *output_pointer++ = ',';
1603 if(output_buffer->format)
1604 {
1605 *output_pointer++ = ' ';
1606 }
1607 *output_pointer = '\0';
1608 output_buffer->offset += length;
1609 }
1610 current_element = current_element->next;
1611 }
1612
1613 output_pointer = ensure(output_buffer, 2);
1614 if (output_pointer == NULL)
1615 {
1616 return false;
1617 }
1618 *output_pointer++ = ']';
1619 *output_pointer = '\0';
1620 output_buffer->depth--;
1621
1622 return true;
1623}
1624
1625/* Build an object from the text. */
1626static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
1627{
1628 cJSON *head = NULL; /* linked list head */
1629 cJSON *current_item = NULL;
1630
1631 if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1632 {
1633 return false; /* to deeply nested */
1634 }
1635 input_buffer->depth++;
1636
1637 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
1638 {
1639 goto fail; /* not an object */
1640 }
1641
1642 input_buffer->offset++;
1643 buffer_skip_whitespace(input_buffer);
1644 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
1645 {
1646 goto success; /* empty object */
1647 }
1648
1649 /* check if we skipped to the end of the buffer */
1650 if (cannot_access_at_index(input_buffer, 0))
1651 {
1652 input_buffer->offset--;
1653 goto fail;
1654 }
1655
1656 /* step back to character in front of the first element */
1657 input_buffer->offset--;
1658 /* loop through the comma separated array elements */
1659 do
1660 {
1661 /* allocate next item */
1662 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1663 if (new_item == NULL)
1664 {
1665 goto fail; /* allocation failure */
1666 }
1667
1668 /* attach next item to list */
1669 if (head == NULL)
1670 {
1671 /* start the linked list */
1672 current_item = head = new_item;
1673 }
1674 else
1675 {
1676 /* add to the end and advance */
1677 current_item->next = new_item;
1678 new_item->prev = current_item;
1679 current_item = new_item;
1680 }
1681
1682 if (cannot_access_at_index(input_buffer, 1))
1683 {
1684 goto fail; /* nothing comes after the comma */
1685 }
1686
1687 /* parse the name of the child */
1688 input_buffer->offset++;
1689 buffer_skip_whitespace(input_buffer);
1690 if (!parse_string(current_item, input_buffer))
1691 {
1692 goto fail; /* failed to parse name */
1693 }
1694 buffer_skip_whitespace(input_buffer);
1695
1696 /* swap valuestring and string, because we parsed the name */
1697 current_item->string = current_item->valuestring;
1698 current_item->valuestring = NULL;
1699
1700 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
1701 {
1702 goto fail; /* invalid object */
1703 }
1704
1705 /* parse the value */
1706 input_buffer->offset++;
1707 buffer_skip_whitespace(input_buffer);
1708 if (!parse_value(current_item, input_buffer))
1709 {
1710 goto fail; /* failed to parse value */
1711 }
1712 buffer_skip_whitespace(input_buffer);
1713 }
1714 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1715
1716 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
1717 {
1718 goto fail; /* expected end of object */
1719 }
1720
1721success:
1722 input_buffer->depth--;
1723
1724 if (head != NULL) {
1725 head->prev = current_item;
1726 }
1727
1728 item->type = cJSON_Object;
1729 item->child = head;
1730
1731 input_buffer->offset++;
1732 return true;
1733
1734fail:
1735 if (head != NULL)
1736 {
1737 cJSON_Delete(head);
1738 }
1739
1740 return false;
1741}
1742
1743/* Render an object to text. */
1744static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
1745{
1746 unsigned char *output_pointer = NULL;
1747 size_t length = 0;
1748 cJSON *current_item = item->child;
1749
1750 if (output_buffer == NULL)
1751 {
1752 return false;
1753 }
1754
1755 /* Compose the output: */
1756 length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
1757 output_pointer = ensure(output_buffer, length + 1);
1758 if (output_pointer == NULL)
1759 {
1760 return false;
1761 }
1762
1763 *output_pointer++ = '{';
1764 output_buffer->depth++;
1765 if (output_buffer->format)
1766 {
1767 *output_pointer++ = '\n';
1768 }
1769 output_buffer->offset += length;
1770
1771 while (current_item)
1772 {
1773 if (output_buffer->format)
1774 {
1775 size_t i;
1776 output_pointer = ensure(output_buffer, output_buffer->depth);
1777 if (output_pointer == NULL)
1778 {
1779 return false;
1780 }
1781 for (i = 0; i < output_buffer->depth; i++)
1782 {
1783 *output_pointer++ = '\t';
1784 }
1785 output_buffer->offset += output_buffer->depth;
1786 }
1787
1788 /* print key */
1789 if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
1790 {
1791 return false;
1792 }
1793 update_offset(output_buffer);
1794
1795 length = (size_t) (output_buffer->format ? 2 : 1);
1796 output_pointer = ensure(output_buffer, length);
1797 if (output_pointer == NULL)
1798 {
1799 return false;
1800 }
1801 *output_pointer++ = ':';
1802 if (output_buffer->format)
1803 {
1804 *output_pointer++ = '\t';
1805 }
1806 output_buffer->offset += length;
1807
1808 /* print value */
1809 if (!print_value(current_item, output_buffer))
1810 {
1811 return false;
1812 }
1813 update_offset(output_buffer);
1814
1815 /* print comma if not last */
1816 length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
1817 output_pointer = ensure(output_buffer, length + 1);
1818 if (output_pointer == NULL)
1819 {
1820 return false;
1821 }
1822 if (current_item->next)
1823 {
1824 *output_pointer++ = ',';
1825 }
1826
1827 if (output_buffer->format)
1828 {
1829 *output_pointer++ = '\n';
1830 }
1831 *output_pointer = '\0';
1832 output_buffer->offset += length;
1833
1834 current_item = current_item->next;
1835 }
1836
1837 output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
1838 if (output_pointer == NULL)
1839 {
1840 return false;
1841 }
1842 if (output_buffer->format)
1843 {
1844 size_t i;
1845 for (i = 0; i < (output_buffer->depth - 1); i++)
1846 {
1847 *output_pointer++ = '\t';
1848 }
1849 }
1850 *output_pointer++ = '}';
1851 *output_pointer = '\0';
1852 output_buffer->depth--;
1853
1854 return true;
1855}
1856
1857/* Get Array size/item / object item. */
1858CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
1859{
1860 cJSON *child = NULL;
1861 size_t size = 0;
1862
1863 if (array == NULL)
1864 {
1865 return 0;
1866 }
1867
1868 child = array->child;
1869
1870 while(child != NULL)
1871 {
1872 size++;
1873 child = child->next;
1874 }
1875
1876 /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
1877
1878 return (int)size;
1879}
1880
1881static cJSON* get_array_item(const cJSON *array, size_t index)
1882{
1883 cJSON *current_child = NULL;
1884
1885 if (array == NULL)
1886 {
1887 return NULL;
1888 }
1889
1890 current_child = array->child;
1891 while ((current_child != NULL) && (index > 0))
1892 {
1893 index--;
1894 current_child = current_child->next;
1895 }
1896
1897 return current_child;
1898}
1899
1900CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
1901{
1902 if (index < 0)
1903 {
1904 return NULL;
1905 }
1906
1907 return get_array_item(array, (size_t)index);
1908}
1909
1910static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
1911{
1912 cJSON *current_element = NULL;
1913
1914 if ((object == NULL) || (name == NULL))
1915 {
1916 return NULL;
1917 }
1918
1919 current_element = object->child;
1920 if (case_sensitive)
1921 {
1922 while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
1923 {
1924 current_element = current_element->next;
1925 }
1926 }
1927 else
1928 {
1929 while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
1930 {
1931 current_element = current_element->next;
1932 }
1933 }
1934
1935 if ((current_element == NULL) || (current_element->string == NULL)) {
1936 return NULL;
1937 }
1938
1939 return current_element;
1940}
1941
1942CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
1943{
1944 return get_object_item(object, string, false);
1945}
1946
1947CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
1948{
1949 return get_object_item(object, string, true);
1950}
1951
1952CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
1953{
1954 return cJSON_GetObjectItem(object, string) ? 1 : 0;
1955}
1956
1957/* Utility for array list handling. */
1958static void suffix_object(cJSON *prev, cJSON *item)
1959{
1960 prev->next = item;
1961 item->prev = prev;
1962}
1963
1964/* Utility for handling references. */
1965static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
1966{
1967 cJSON *reference = NULL;
1968 if (item == NULL)
1969 {
1970 return NULL;
1971 }
1972
1973 reference = cJSON_New_Item(hooks);
1974 if (reference == NULL)
1975 {
1976 return NULL;
1977 }
1978
1979 memcpy(reference, item, sizeof(cJSON));
1980 reference->string = NULL;
1981 reference->type |= cJSON_IsReference;
1982 reference->next = reference->prev = NULL;
1983 return reference;
1984}
1985
1986static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
1987{
1988 cJSON *child = NULL;
1989
1990 if ((item == NULL) || (array == NULL) || (array == item))
1991 {
1992 return false;
1993 }
1994
1995 child = array->child;
1996 /*
1997 * To find the last item in array quickly, we use prev in array
1998 */
1999 if (child == NULL)
2000 {
2001 /* list is empty, start new one */
2002 array->child = item;
2003 item->prev = item;
2004 item->next = NULL;
2005 }
2006 else
2007 {
2008 /* append to the end */
2009 if (child->prev)
2010 {
2011 suffix_object(child->prev, item);
2012 array->child->prev = item;
2013 }
2014 }
2015
2016 return true;
2017}
2018
2019/* Add item to array/object. */
2020CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
2021{
2022 return add_item_to_array(array, item);
2023}
2024
2025#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2026 #pragma GCC diagnostic push
2027#endif
2028#ifdef __GNUC__
2029#pragma GCC diagnostic ignored "-Wcast-qual"
2030#endif
2031/* helper function to cast away const */
2032static void* cast_away_const(const void* string)
2033{
2034 return (void*)string;
2035}
2036#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2037 #pragma GCC diagnostic pop
2038#endif
2039
2040
2041static 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)
2042{
2043 char *new_key = NULL;
2044 int new_type = cJSON_Invalid;
2045
2046 if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
2047 {
2048 return false;
2049 }
2050
2051 if (constant_key)
2052 {
2053 new_key = (char*)cast_away_const(string);
2054 new_type = item->type | cJSON_StringIsConst;
2055 }
2056 else
2057 {
2058 new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
2059 if (new_key == NULL)
2060 {
2061 return false;
2062 }
2063
2064 new_type = item->type & ~cJSON_StringIsConst;
2065 }
2066
2067 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
2068 {
2069 hooks->deallocate(item->string);
2070 }
2071
2072 item->string = new_key;
2073 item->type = new_type;
2074
2075 return add_item_to_array(object, item);
2076}
2077
2078CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
2079{
2080 return add_item_to_object(object, string, item, &global_hooks, false);
2081}
2082
2083/* Add an item to an object with constant string as key */
2084CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
2085{
2086 return add_item_to_object(object, string, item, &global_hooks, true);
2087}
2088
2089CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
2090{
2091 if (array == NULL)
2092 {
2093 return false;
2094 }
2095
2096 return add_item_to_array(array, create_reference(item, &global_hooks));
2097}
2098
2099CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
2100{
2101 if ((object == NULL) || (string == NULL))
2102 {
2103 return false;
2104 }
2105
2106 return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
2107}
2108
2109CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
2110{
2111 cJSON *null = cJSON_CreateNull();
2112 if (add_item_to_object(object, name, null, &global_hooks, false))
2113 {
2114 return null;
2115 }
2116
2117 cJSON_Delete(null);
2118 return NULL;
2119}
2120
2121CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
2122{
2123 cJSON *true_item = cJSON_CreateTrue();
2124 if (add_item_to_object(object, name, true_item, &global_hooks, false))
2125 {
2126 return true_item;
2127 }
2128
2129 cJSON_Delete(true_item);
2130 return NULL;
2131}
2132
2133CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
2134{
2135 cJSON *false_item = cJSON_CreateFalse();
2136 if (add_item_to_object(object, name, false_item, &global_hooks, false))
2137 {
2138 return false_item;
2139 }
2140
2141 cJSON_Delete(false_item);
2142 return NULL;
2143}
2144
2145CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
2146{
2147 cJSON *bool_item = cJSON_CreateBool(boolean);
2148 if (add_item_to_object(object, name, bool_item, &global_hooks, false))
2149 {
2150 return bool_item;
2151 }
2152
2153 cJSON_Delete(bool_item);
2154 return NULL;
2155}
2156
2157CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
2158{
2159 cJSON *number_item = cJSON_CreateNumber(number);
2160 if (add_item_to_object(object, name, number_item, &global_hooks, false))
2161 {
2162 return number_item;
2163 }
2164
2165 cJSON_Delete(number_item);
2166 return NULL;
2167}
2168
2169CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
2170{
2171 cJSON *string_item = cJSON_CreateString(string);
2172 if (add_item_to_object(object, name, string_item, &global_hooks, false))
2173 {
2174 return string_item;
2175 }
2176
2177 cJSON_Delete(string_item);
2178 return NULL;
2179}
2180
2181CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
2182{
2183 cJSON *raw_item = cJSON_CreateRaw(raw);
2184 if (add_item_to_object(object, name, raw_item, &global_hooks, false))
2185 {
2186 return raw_item;
2187 }
2188
2189 cJSON_Delete(raw_item);
2190 return NULL;
2191}
2192
2193CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
2194{
2195 cJSON *object_item = cJSON_CreateObject();
2196 if (add_item_to_object(object, name, object_item, &global_hooks, false))
2197 {
2198 return object_item;
2199 }
2200
2201 cJSON_Delete(object_item);
2202 return NULL;
2203}
2204
2205CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
2206{
2207 cJSON *array = cJSON_CreateArray();
2208 if (add_item_to_object(object, name, array, &global_hooks, false))
2209 {
2210 return array;
2211 }
2212
2213 cJSON_Delete(array);
2214 return NULL;
2215}
2216
2217CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
2218{
2219 if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL))
2220 {
2221 return NULL;
2222 }
2223
2224 if (item != parent->child)
2225 {
2226 /* not the first element */
2227 item->prev->next = item->next;
2228 }
2229 if (item->next != NULL)
2230 {
2231 /* not the last element */
2232 item->next->prev = item->prev;
2233 }
2234
2235 if (item == parent->child)
2236 {
2237 /* first element */
2238 parent->child = item->next;
2239 }
2240 else if (item->next == NULL)
2241 {
2242 /* last element */
2243 parent->child->prev = item->prev;
2244 }
2245
2246 /* make sure the detached item doesn't point anywhere anymore */
2247 item->prev = NULL;
2248 item->next = NULL;
2249
2250 return item;
2251}
2252
2253CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
2254{
2255 if (which < 0)
2256 {
2257 return NULL;
2258 }
2259
2260 return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
2261}
2262
2263CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
2264{
2265 cJSON_Delete(cJSON_DetachItemFromArray(array, which));
2266}
2267
2268CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
2269{
2270 cJSON *to_detach = cJSON_GetObjectItem(object, string);
2271
2272 return cJSON_DetachItemViaPointer(object, to_detach);
2273}
2274
2275CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
2276{
2277 cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
2278
2279 return cJSON_DetachItemViaPointer(object, to_detach);
2280}
2281
2282CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
2283{
2284 cJSON_Delete(cJSON_DetachItemFromObject(object, string));
2285}
2286
2287CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
2288{
2289 cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
2290}
2291
2292/* Replace array/object items with new ones. */
2293CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
2294{
2295 cJSON *after_inserted = NULL;
2296
2297 if (which < 0 || newitem == NULL)
2298 {
2299 return false;
2300 }
2301
2302 after_inserted = get_array_item(array, (size_t)which);
2303 if (after_inserted == NULL)
2304 {
2305 return add_item_to_array(array, newitem);
2306 }
2307
2308 if (after_inserted != array->child && after_inserted->prev == NULL) {
2309 /* return false if after_inserted is a corrupted array item */
2310 return false;
2311 }
2312
2313 newitem->next = after_inserted;
2314 newitem->prev = after_inserted->prev;
2315 after_inserted->prev = newitem;
2316 if (after_inserted == array->child)
2317 {
2318 array->child = newitem;
2319 }
2320 else
2321 {
2322 newitem->prev->next = newitem;
2323 }
2324 return true;
2325}
2326
2327CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
2328{
2329 if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
2330 {
2331 return false;
2332 }
2333
2334 if (replacement == item)
2335 {
2336 return true;
2337 }
2338
2339 replacement->next = item->next;
2340 replacement->prev = item->prev;
2341
2342 if (replacement->next != NULL)
2343 {
2344 replacement->next->prev = replacement;
2345 }
2346 if (parent->child == item)
2347 {
2348 if (parent->child->prev == parent->child)
2349 {
2350 replacement->prev = replacement;
2351 }
2352 parent->child = replacement;
2353 }
2354 else
2355 { /*
2356 * To find the last item in array quickly, we use prev in array.
2357 * We can't modify the last item's next pointer where this item was the parent's child
2358 */
2359 if (replacement->prev != NULL)
2360 {
2361 replacement->prev->next = replacement;
2362 }
2363 if (replacement->next == NULL)
2364 {
2365 parent->child->prev = replacement;
2366 }
2367 }
2368
2369 item->next = NULL;
2370 item->prev = NULL;
2371 cJSON_Delete(item);
2372
2373 return true;
2374}
2375
2376CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
2377{
2378 if (which < 0)
2379 {
2380 return false;
2381 }
2382
2383 return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
2384}
2385
2386static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
2387{
2388 if ((replacement == NULL) || (string == NULL))
2389 {
2390 return false;
2391 }
2392
2393 /* replace the name in the replacement */
2394 if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
2395 {
2396 cJSON_free(replacement->string);
2397 }
2398 replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2399 if (replacement->string == NULL)
2400 {
2401 return false;
2402 }
2403
2404 replacement->type &= ~cJSON_StringIsConst;
2405
2406 return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
2407}
2408
2409CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
2410{
2411 return replace_item_in_object(object, string, newitem, false);
2412}
2413
2414CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
2415{
2416 return replace_item_in_object(object, string, newitem, true);
2417}
2418
2419/* Create basic types: */
2420CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
2421{
2422 cJSON *item = cJSON_New_Item(&global_hooks);
2423 if(item)
2424 {
2425 item->type = cJSON_NULL;
2426 }
2427
2428 return item;
2429}
2430
2431CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
2432{
2433 cJSON *item = cJSON_New_Item(&global_hooks);
2434 if(item)
2435 {
2436 item->type = cJSON_True;
2437 }
2438
2439 return item;
2440}
2441
2442CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
2443{
2444 cJSON *item = cJSON_New_Item(&global_hooks);
2445 if(item)
2446 {
2447 item->type = cJSON_False;
2448 }
2449
2450 return item;
2451}
2452
2453CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
2454{
2455 cJSON *item = cJSON_New_Item(&global_hooks);
2456 if(item)
2457 {
2458 item->type = boolean ? cJSON_True : cJSON_False;
2459 }
2460
2461 return item;
2462}
2463
2464CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
2465{
2466 cJSON *item = cJSON_New_Item(&global_hooks);
2467 if(item)
2468 {
2469 item->type = cJSON_Number;
2470 item->valuedouble = num;
2471
2472 /* use saturation in case of overflow */
2473 if (num >= INT_MAX)
2474 {
2475 item->valueint = INT_MAX;
2476 }
2477 else if (num <= (double)INT_MIN)
2478 {
2479 item->valueint = INT_MIN;
2480 }
2481 else
2482 {
2483 item->valueint = (int)num;
2484 }
2485 }
2486
2487 return item;
2488}
2489
2490CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
2491{
2492 cJSON *item = cJSON_New_Item(&global_hooks);
2493 if(item)
2494 {
2495 item->type = cJSON_String;
2496 item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2497 if(!item->valuestring)
2498 {
2499 cJSON_Delete(item);
2500 return NULL;
2501 }
2502 }
2503
2504 return item;
2505}
2506
2507CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
2508{
2509 cJSON *item = cJSON_New_Item(&global_hooks);
2510 if (item != NULL)
2511 {
2512 item->type = cJSON_String | cJSON_IsReference;
2513 item->valuestring = (char*)cast_away_const(string);
2514 }
2515
2516 return item;
2517}
2518
2519CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
2520{
2521 cJSON *item = cJSON_New_Item(&global_hooks);
2522 if (item != NULL) {
2523 item->type = cJSON_Object | cJSON_IsReference;
2524 item->child = (cJSON*)cast_away_const(child);
2525 }
2526
2527 return item;
2528}
2529
2530CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
2531 cJSON *item = cJSON_New_Item(&global_hooks);
2532 if (item != NULL) {
2533 item->type = cJSON_Array | cJSON_IsReference;
2534 item->child = (cJSON*)cast_away_const(child);
2535 }
2536
2537 return item;
2538}
2539
2540CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
2541{
2542 cJSON *item = cJSON_New_Item(&global_hooks);
2543 if(item)
2544 {
2545 item->type = cJSON_Raw;
2546 item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
2547 if(!item->valuestring)
2548 {
2549 cJSON_Delete(item);
2550 return NULL;
2551 }
2552 }
2553
2554 return item;
2555}
2556
2557CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
2558{
2559 cJSON *item = cJSON_New_Item(&global_hooks);
2560 if(item)
2561 {
2562 item->type=cJSON_Array;
2563 }
2564
2565 return item;
2566}
2567
2568CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
2569{
2570 cJSON *item = cJSON_New_Item(&global_hooks);
2571 if (item)
2572 {
2573 item->type = cJSON_Object;
2574 }
2575
2576 return item;
2577}
2578
2579/* Create Arrays: */
2580CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
2581{
2582 size_t i = 0;
2583 cJSON *n = NULL;
2584 cJSON *p = NULL;
2585 cJSON *a = NULL;
2586
2587 if ((count < 0) || (numbers == NULL))
2588 {
2589 return NULL;
2590 }
2591
2592 a = cJSON_CreateArray();
2593
2594 for(i = 0; a && (i < (size_t)count); i++)
2595 {
2596 n = cJSON_CreateNumber(numbers[i]);
2597 if (!n)
2598 {
2599 cJSON_Delete(a);
2600 return NULL;
2601 }
2602 if(!i)
2603 {
2604 a->child = n;
2605 }
2606 else
2607 {
2608 suffix_object(p, n);
2609 }
2610 p = n;
2611 }
2612
2613 if (a && a->child) {
2614 a->child->prev = n;
2615 }
2616
2617 return a;
2618}
2619
2620CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
2621{
2622 size_t i = 0;
2623 cJSON *n = NULL;
2624 cJSON *p = NULL;
2625 cJSON *a = NULL;
2626
2627 if ((count < 0) || (numbers == NULL))
2628 {
2629 return NULL;
2630 }
2631
2632 a = cJSON_CreateArray();
2633
2634 for(i = 0; a && (i < (size_t)count); i++)
2635 {
2636 n = cJSON_CreateNumber((double)numbers[i]);
2637 if(!n)
2638 {
2639 cJSON_Delete(a);
2640 return NULL;
2641 }
2642 if(!i)
2643 {
2644 a->child = n;
2645 }
2646 else
2647 {
2648 suffix_object(p, n);
2649 }
2650 p = n;
2651 }
2652
2653 if (a && a->child) {
2654 a->child->prev = n;
2655 }
2656
2657 return a;
2658}
2659
2660CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
2661{
2662 size_t i = 0;
2663 cJSON *n = NULL;
2664 cJSON *p = NULL;
2665 cJSON *a = NULL;
2666
2667 if ((count < 0) || (numbers == NULL))
2668 {
2669 return NULL;
2670 }
2671
2672 a = cJSON_CreateArray();
2673
2674 for(i = 0; a && (i < (size_t)count); i++)
2675 {
2676 n = cJSON_CreateNumber(numbers[i]);
2677 if(!n)
2678 {
2679 cJSON_Delete(a);
2680 return NULL;
2681 }
2682 if(!i)
2683 {
2684 a->child = n;
2685 }
2686 else
2687 {
2688 suffix_object(p, n);
2689 }
2690 p = n;
2691 }
2692
2693 if (a && a->child) {
2694 a->child->prev = n;
2695 }
2696
2697 return a;
2698}
2699
2700CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
2701{
2702 size_t i = 0;
2703 cJSON *n = NULL;
2704 cJSON *p = NULL;
2705 cJSON *a = NULL;
2706
2707 if ((count < 0) || (strings == NULL))
2708 {
2709 return NULL;
2710 }
2711
2712 a = cJSON_CreateArray();
2713
2714 for (i = 0; a && (i < (size_t)count); i++)
2715 {
2716 n = cJSON_CreateString(strings[i]);
2717 if(!n)
2718 {
2719 cJSON_Delete(a);
2720 return NULL;
2721 }
2722 if(!i)
2723 {
2724 a->child = n;
2725 }
2726 else
2727 {
2728 suffix_object(p,n);
2729 }
2730 p = n;
2731 }
2732
2733 if (a && a->child) {
2734 a->child->prev = n;
2735 }
2736
2737 return a;
2738}
2739
2740/* Duplication */
2741cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
2742
2743CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
2744{
2745 return cJSON_Duplicate_rec(item, 0, recurse );
2746}
2747
2748cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
2749{
2750 cJSON *newitem = NULL;
2751 cJSON *child = NULL;
2752 cJSON *next = NULL;
2753 cJSON *newchild = NULL;
2754
2755 /* Bail on bad ptr */
2756 if (!item)
2757 {
2758 goto fail;
2759 }
2760 /* Create new item */
2761 newitem = cJSON_New_Item(&global_hooks);
2762 if (!newitem)
2763 {
2764 goto fail;
2765 }
2766 /* Copy over all vars */
2767 newitem->type = item->type & (~cJSON_IsReference);
2768 newitem->valueint = item->valueint;
2769 newitem->valuedouble = item->valuedouble;
2770 if (item->valuestring)
2771 {
2772 newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
2773 if (!newitem->valuestring)
2774 {
2775 goto fail;
2776 }
2777 }
2778 if (item->string)
2779 {
2780 newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
2781 if (!newitem->string)
2782 {
2783 goto fail;
2784 }
2785 }
2786 /* If non-recursive, then we're done! */
2787 if (!recurse)
2788 {
2789 return newitem;
2790 }
2791 /* Walk the ->next chain for the child. */
2792 child = item->child;
2793 while (child != NULL)
2794 {
2795 if(depth >= CJSON_CIRCULAR_LIMIT) {
2796 goto fail;
2797 }
2798 newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */
2799 if (!newchild)
2800 {
2801 goto fail;
2802 }
2803 if (next != NULL)
2804 {
2805 /* If newitem->child already set, then crosswire ->prev and ->next and move on */
2806 next->next = newchild;
2807 newchild->prev = next;
2808 next = newchild;
2809 }
2810 else
2811 {
2812 /* Set newitem->child and move to it */
2813 newitem->child = newchild;
2814 next = newchild;
2815 }
2816 child = child->next;
2817 }
2818 if (newitem && newitem->child)
2819 {
2820 newitem->child->prev = newchild;
2821 }
2822
2823 return newitem;
2824
2825fail:
2826 if (newitem != NULL)
2827 {
2828 cJSON_Delete(newitem);
2829 }
2830
2831 return NULL;
2832}
2833
2834static void skip_oneline_comment(char **input)
2835{
2836 *input += static_strlen("//");
2837
2838 for (; (*input)[0] != '\0'; ++(*input))
2839 {
2840 if ((*input)[0] == '\n') {
2841 *input += static_strlen("\n");
2842 return;
2843 }
2844 }
2845}
2846
2847static void skip_multiline_comment(char **input)
2848{
2849 *input += static_strlen("/*");
2850
2851 for (; (*input)[0] != '\0'; ++(*input))
2852 {
2853 if (((*input)[0] == '*') && ((*input)[1] == '/'))
2854 {
2855 *input += static_strlen("*/");
2856 return;
2857 }
2858 }
2859}
2860
2861static void minify_string(char **input, char **output) {
2862 (*output)[0] = (*input)[0];
2863 *input += static_strlen("\"");
2864 *output += static_strlen("\"");
2865
2866
2867 for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
2868 (*output)[0] = (*input)[0];
2869
2870 if ((*input)[0] == '\"') {
2871 (*output)[0] = '\"';
2872 *input += static_strlen("\"");
2873 *output += static_strlen("\"");
2874 return;
2875 } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
2876 (*output)[1] = (*input)[1];
2877 *input += static_strlen("\"");
2878 *output += static_strlen("\"");
2879 }
2880 }
2881}
2882
2883CJSON_PUBLIC(void) cJSON_Minify(char *json)
2884{
2885 char *into = json;
2886
2887 if (json == NULL)
2888 {
2889 return;
2890 }
2891
2892 while (json[0] != '\0')
2893 {
2894 switch (json[0])
2895 {
2896 case ' ':
2897 case '\t':
2898 case '\r':
2899 case '\n':
2900 json++;
2901 break;
2902
2903 case '/':
2904 if (json[1] == '/')
2905 {
2906 skip_oneline_comment(&json);
2907 }
2908 else if (json[1] == '*')
2909 {
2910 skip_multiline_comment(&json);
2911 } else {
2912 json++;
2913 }
2914 break;
2915
2916 case '\"':
2917 minify_string(&json, (char**)&into);
2918 break;
2919
2920 default:
2921 into[0] = json[0];
2922 json++;
2923 into++;
2924 }
2925 }
2926
2927 /* and null-terminate. */
2928 *into = '\0';
2929}
2930
2931CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
2932{
2933 if (item == NULL)
2934 {
2935 return false;
2936 }
2937
2938 return (item->type & 0xFF) == cJSON_Invalid;
2939}
2940
2941CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
2942{
2943 if (item == NULL)
2944 {
2945 return false;
2946 }
2947
2948 return (item->type & 0xFF) == cJSON_False;
2949}
2950
2951CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
2952{
2953 if (item == NULL)
2954 {
2955 return false;
2956 }
2957
2958 return (item->type & 0xff) == cJSON_True;
2959}
2960
2961
2962CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
2963{
2964 if (item == NULL)
2965 {
2966 return false;
2967 }
2968
2969 return (item->type & (cJSON_True | cJSON_False)) != 0;
2970}
2971CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
2972{
2973 if (item == NULL)
2974 {
2975 return false;
2976 }
2977
2978 return (item->type & 0xFF) == cJSON_NULL;
2979}
2980
2981CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
2982{
2983 if (item == NULL)
2984 {
2985 return false;
2986 }
2987
2988 return (item->type & 0xFF) == cJSON_Number;
2989}
2990
2991CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
2992{
2993 if (item == NULL)
2994 {
2995 return false;
2996 }
2997
2998 return (item->type & 0xFF) == cJSON_String;
2999}
3000
3001CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
3002{
3003 if (item == NULL)
3004 {
3005 return false;
3006 }
3007
3008 return (item->type & 0xFF) == cJSON_Array;
3009}
3010
3011CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
3012{
3013 if (item == NULL)
3014 {
3015 return false;
3016 }
3017
3018 return (item->type & 0xFF) == cJSON_Object;
3019}
3020
3021CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
3022{
3023 if (item == NULL)
3024 {
3025 return false;
3026 }
3027
3028 return (item->type & 0xFF) == cJSON_Raw;
3029}
3030
3031CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
3032{
3033 if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
3034 {
3035 return false;
3036 }
3037
3038 /* check if type is valid */
3039 switch (a->type & 0xFF)
3040 {
3041 case cJSON_False:
3042 case cJSON_True:
3043 case cJSON_NULL:
3044 case cJSON_Number:
3045 case cJSON_String:
3046 case cJSON_Raw:
3047 case cJSON_Array:
3048 case cJSON_Object:
3049 break;
3050
3051 default:
3052 return false;
3053 }
3054
3055 /* identical objects are equal */
3056 if (a == b)
3057 {
3058 return true;
3059 }
3060
3061 switch (a->type & 0xFF)
3062 {
3063 /* in these cases and equal type is enough */
3064 case cJSON_False:
3065 case cJSON_True:
3066 case cJSON_NULL:
3067 return true;
3068
3069 case cJSON_Number:
3070 if (compare_double(a->valuedouble, b->valuedouble))
3071 {
3072 return true;
3073 }
3074 return false;
3075
3076 case cJSON_String:
3077 case cJSON_Raw:
3078 if ((a->valuestring == NULL) || (b->valuestring == NULL))
3079 {
3080 return false;
3081 }
3082 if (strcmp(a->valuestring, b->valuestring) == 0)
3083 {
3084 return true;
3085 }
3086
3087 return false;
3088
3089 case cJSON_Array:
3090 {
3091 cJSON *a_element = a->child;
3092 cJSON *b_element = b->child;
3093
3094 for (; (a_element != NULL) && (b_element != NULL);)
3095 {
3096 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3097 {
3098 return false;
3099 }
3100
3101 a_element = a_element->next;
3102 b_element = b_element->next;
3103 }
3104
3105 /* one of the arrays is longer than the other */
3106 if (a_element != b_element) {
3107 return false;
3108 }
3109
3110 return true;
3111 }
3112
3113 case cJSON_Object:
3114 {
3115 cJSON *a_element = NULL;
3116 cJSON *b_element = NULL;
3117 cJSON_ArrayForEach(a_element, a)
3118 {
3119 /* TODO This has O(n^2) runtime, which is horrible! */
3120 b_element = get_object_item(b, a_element->string, case_sensitive);
3121 if (b_element == NULL)
3122 {
3123 return false;
3124 }
3125
3126 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3127 {
3128 return false;
3129 }
3130 }
3131
3132 /* doing this twice, once on a and b to prevent true comparison if a subset of b
3133 * TODO: Do this the proper way, this is just a fix for now */
3134 cJSON_ArrayForEach(b_element, b)
3135 {
3136 a_element = get_object_item(a, b_element->string, case_sensitive);
3137 if (a_element == NULL)
3138 {
3139 return false;
3140 }
3141
3142 if (!cJSON_Compare(b_element, a_element, case_sensitive))
3143 {
3144 return false;
3145 }
3146 }
3147
3148 return true;
3149 }
3150
3151 default:
3152 return false;
3153 }
3154}
3155
3156CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
3157{
3158 return global_hooks.allocate(size);
3159}
3160
3161CJSON_PUBLIC(void) cJSON_free(void *object)
3162{
3163 global_hooks.deallocate(object);
3164 object = NULL;
3165}
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 @@
1/*
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21*/
22
23#ifndef cJSON__h
24#define cJSON__h
25
26#ifdef __cplusplus
27extern "C"
28{
29#endif
30
31#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
32#define __WINDOWS__
33#endif
34
35#ifdef __WINDOWS__
36
37/* 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:
38
39CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
40CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
41CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
42
43For *nix builds that support visibility attribute, you can define similar behavior by
44
45setting default visibility to hidden by adding
46-fvisibility=hidden (for gcc)
47or
48-xldscope=hidden (for sun cc)
49to CFLAGS
50
51then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
52
53*/
54
55#define CJSON_CDECL __cdecl
56#define CJSON_STDCALL __stdcall
57
58/* export symbols by default, this is necessary for copy pasting the C and header file */
59#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
60#define CJSON_EXPORT_SYMBOLS
61#endif
62
63#if defined(CJSON_HIDE_SYMBOLS)
64#define CJSON_PUBLIC(type) type CJSON_STDCALL
65#elif defined(CJSON_EXPORT_SYMBOLS)
66#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
67#elif defined(CJSON_IMPORT_SYMBOLS)
68#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
69#endif
70#else /* !__WINDOWS__ */
71#define CJSON_CDECL
72#define CJSON_STDCALL
73
74#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
75#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
76#else
77#define CJSON_PUBLIC(type) type
78#endif
79#endif
80
81/* project version */
82#define CJSON_VERSION_MAJOR 1
83#define CJSON_VERSION_MINOR 7
84#define CJSON_VERSION_PATCH 18
85
86#include <stddef.h>
87
88/* cJSON Types: */
89#define cJSON_Invalid (0)
90#define cJSON_False (1 << 0)
91#define cJSON_True (1 << 1)
92#define cJSON_NULL (1 << 2)
93#define cJSON_Number (1 << 3)
94#define cJSON_String (1 << 4)
95#define cJSON_Array (1 << 5)
96#define cJSON_Object (1 << 6)
97#define cJSON_Raw (1 << 7) /* raw json */
98
99#define cJSON_IsReference 256
100#define cJSON_StringIsConst 512
101
102/* The cJSON structure: */
103typedef struct cJSON
104{
105 /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
106 struct cJSON *next;
107 struct cJSON *prev;
108 /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
109 struct cJSON *child;
110
111 /* The type of the item, as above. */
112 int type;
113
114 /* The item's string, if type==cJSON_String and type == cJSON_Raw */
115 char *valuestring;
116 /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
117 int valueint;
118 /* The item's number, if type==cJSON_Number */
119 double valuedouble;
120
121 /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
122 char *string;
123} cJSON;
124
125typedef struct cJSON_Hooks
126{
127 /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
128 void *(CJSON_CDECL *malloc_fn)(size_t sz);
129 void (CJSON_CDECL *free_fn)(void *ptr);
130} cJSON_Hooks;
131
132typedef int cJSON_bool;
133
134/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
135 * This is to prevent stack overflows. */
136#ifndef CJSON_NESTING_LIMIT
137#define CJSON_NESTING_LIMIT 1000
138#endif
139
140/* Limits the length of circular references can be before cJSON rejects to parse them.
141 * This is to prevent stack overflows. */
142#ifndef CJSON_CIRCULAR_LIMIT
143#define CJSON_CIRCULAR_LIMIT 10000
144#endif
145
146/* returns the version of cJSON as a string */
147CJSON_PUBLIC(const char*) cJSON_Version(void);
148
149/* Supply malloc, realloc and free functions to cJSON */
150CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
151
152/* 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. */
153/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
154CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
155CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
156/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
157/* 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(). */
158CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
159CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
160
161/* Render a cJSON entity to text for transfer/storage. */
162CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
163/* Render a cJSON entity to text for transfer/storage without any formatting. */
164CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
165/* 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 */
166CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
167/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
168/* 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 */
169CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
170/* Delete a cJSON entity and all subentities. */
171CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
172
173/* Returns the number of items in an array (or object). */
174CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
175/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
176CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
177/* Get item "string" from object. Case insensitive. */
178CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
179CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
180CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
181/* 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. */
182CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
183
184/* Check item type and return its value */
185CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
186CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
187
188/* These functions check the type of an item */
189CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
190CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
191CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
192CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
193CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
194CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
195CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
196CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
197CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
198CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
199
200/* These calls create a cJSON item of the appropriate type. */
201CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
202CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
203CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
204CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
205CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
206CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
207/* raw json */
208CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
209CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
210CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
211
212/* Create a string where valuestring references a string so
213 * it will not be freed by cJSON_Delete */
214CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
215/* Create an object/array that only references it's elements so
216 * they will not be freed by cJSON_Delete */
217CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
218CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
219
220/* These utilities create an Array of count items.
221 * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
222CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
223CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
224CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
225CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
226
227/* Append item to the specified array/object. */
228CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
229CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
230/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
231 * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
232 * writing to `item->string` */
233CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
234/* 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. */
235CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
236CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
237
238/* Remove/Detach items from Arrays/Objects. */
239CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
240CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
241CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
242CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
243CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
244CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
245CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
246
247/* Update array items. */
248CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
249CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
250CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
251CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
252CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
253
254/* Duplicate a cJSON item */
255CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
256/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
257 * need to be released. With recurse!=0, it will duplicate any children connected to the item.
258 * The item->next and ->prev pointers are always zero on return from Duplicate. */
259/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
260 * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
261CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
262
263/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
264 * The input pointer json cannot point to a read-only address area, such as a string constant,
265 * but should point to a readable and writable address area. */
266CJSON_PUBLIC(void) cJSON_Minify(char *json);
267
268/* Helper functions for creating and adding items to an object at the same time.
269 * They return the added item or NULL on failure. */
270CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
271CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
272CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
273CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
274CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
275CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
276CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
277CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
278CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
279
280/* When assigning an integer value, it needs to be propagated to valuedouble too. */
281#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
282/* helper for the cJSON_SetNumberValue macro */
283CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
284#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
285/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
286CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
287
288/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
289#define cJSON_SetBoolValue(object, boolValue) ( \
290 (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
291 (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
292 cJSON_Invalid\
293)
294
295/* Macro for iterating over an array or object */
296#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
297
298/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
299CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
300CJSON_PUBLIC(void) cJSON_free(void *object);
301
302#ifdef __cplusplus
303}
304#endif
305
306#endif