webxos commited on
Commit
a9842d5
Β·
verified Β·
1 Parent(s): 847343d

Upload 7 files

Browse files
Files changed (7) hide show
  1. Makefile +36 -0
  2. cJSON.c +3174 -0
  3. cJSON.h +290 -0
  4. interpreter.c +214 -0
  5. interpreter.h +40 -0
  6. shadowclaw.c +1383 -0
  7. start.sh +153 -0
Makefile ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Makefile for Shadowclaw
2
+
3
+ CC = gcc
4
+ CFLAGS = -Wall -Wextra -O2 -D_GNU_SOURCE
5
+ LDFLAGS = -lpthread -lcurl -lm
6
+
7
+ # Dependency checks
8
+ CHECK_CURL = $(shell pkg-config --exists libcurl && echo yes)
9
+ ifeq ($(CHECK_CURL),)
10
+ $(error libcurl development package not found (install libcurl4-openssl-dev or equivalent))
11
+ endif
12
+
13
+ # Source files
14
+ OBJS = shadowclaw.o interpreter.o cJSON.o
15
+
16
+ # Default target
17
+ shadowclaw: $(OBJS)
18
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
19
+
20
+ # Automatic header dependency tracking
21
+ DEPDIR = .deps
22
+ df = $(DEPDIR)/$(*F)
23
+
24
+ %.o: %.c
25
+ @mkdir -p $(DEPDIR)
26
+ $(CC) $(CFLAGS) -MMD -MF $(df).d -c $< -o $@
27
+ @cp $(df).d $(df).P; sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; rm -f $(df).d
28
+
29
+ # Include generated dependencies
30
+ -include $(OBJS:%.o=$(DEPDIR)/%.P)
31
+
32
+ # Clean
33
+ clean:
34
+ rm -f shadowclaw $(OBJS) $(DEPDIR)/*.P
35
+
36
+ .PHONY: clean
cJSON.c ADDED
@@ -0,0 +1,3174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 <string.h>
41
+ #include <stdio.h>
42
+ #include <math.h>
43
+ #include <stdlib.h>
44
+ #include <limits.h>
45
+ #include <ctype.h>
46
+ #include <float.h>
47
+
48
+ #ifdef ENABLE_LOCALES
49
+ #include <locale.h>
50
+ #endif
51
+
52
+ #if defined(_MSC_VER)
53
+ #pragma warning (pop)
54
+ #endif
55
+ #ifdef __GNUC__
56
+ #pragma GCC visibility pop
57
+ #endif
58
+
59
+ #include "cJSON.h"
60
+
61
+ /* define our own boolean type */
62
+ #ifdef true
63
+ #undef true
64
+ #endif
65
+ #define true ((cJSON_bool)1)
66
+
67
+ #ifdef false
68
+ #undef false
69
+ #endif
70
+ #define false ((cJSON_bool)0)
71
+
72
+ /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
73
+ #ifndef isinf
74
+ #define isinf(d) (isnan((d - d)) && !isnan(d))
75
+ #endif
76
+ #ifndef isnan
77
+ #define isnan(d) (d != d)
78
+ #endif
79
+
80
+ #ifndef NAN
81
+ #ifdef _WIN32
82
+ #define NAN sqrt(-1.0)
83
+ #else
84
+ #define NAN 0.0/0.0
85
+ #endif
86
+ #endif
87
+
88
+ typedef struct {
89
+ const unsigned char *json;
90
+ size_t position;
91
+ } error;
92
+ static error global_error = { NULL, 0 };
93
+
94
+ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
95
+ {
96
+ return (const char*) (global_error.json + global_error.position);
97
+ }
98
+
99
+ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
100
+ {
101
+ if (!cJSON_IsString(item))
102
+ {
103
+ return NULL;
104
+ }
105
+
106
+ return item->valuestring;
107
+ }
108
+
109
+ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
110
+ {
111
+ if (!cJSON_IsNumber(item))
112
+ {
113
+ return (double) NAN;
114
+ }
115
+
116
+ return item->valuedouble;
117
+ }
118
+
119
+ /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
120
+ #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
121
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
122
+ #endif
123
+
124
+ CJSON_PUBLIC(const char*) cJSON_Version(void)
125
+ {
126
+ static char version[15];
127
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
128
+
129
+ return version;
130
+ }
131
+
132
+ /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
133
+ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
134
+ {
135
+ if ((string1 == NULL) || (string2 == NULL))
136
+ {
137
+ return 1;
138
+ }
139
+
140
+ if (string1 == string2)
141
+ {
142
+ return 0;
143
+ }
144
+
145
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
146
+ {
147
+ if (*string1 == '\0')
148
+ {
149
+ return 0;
150
+ }
151
+ }
152
+
153
+ return tolower(*string1) - tolower(*string2);
154
+ }
155
+
156
+ typedef struct internal_hooks
157
+ {
158
+ void *(CJSON_CDECL *allocate)(size_t size);
159
+ void (CJSON_CDECL *deallocate)(void *pointer);
160
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
161
+ } internal_hooks;
162
+
163
+ #if defined(_MSC_VER)
164
+ /* work around MSVC error C2322: '...' address of dllimport '...' is not static */
165
+ static void * CJSON_CDECL internal_malloc(size_t size)
166
+ {
167
+ return malloc(size);
168
+ }
169
+ static void CJSON_CDECL internal_free(void *pointer)
170
+ {
171
+ free(pointer);
172
+ }
173
+ static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
174
+ {
175
+ return realloc(pointer, size);
176
+ }
177
+ #else
178
+ #define internal_malloc malloc
179
+ #define internal_free free
180
+ #define internal_realloc realloc
181
+ #endif
182
+
183
+ /* strlen of character literals resolved at compile time */
184
+ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
185
+
186
+ static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
187
+
188
+ static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
189
+ {
190
+ size_t length = 0;
191
+ unsigned char *copy = NULL;
192
+
193
+ if (string == NULL)
194
+ {
195
+ return NULL;
196
+ }
197
+
198
+ length = strlen((const char*)string) + sizeof("");
199
+ copy = (unsigned char*)hooks->allocate(length);
200
+ if (copy == NULL)
201
+ {
202
+ return NULL;
203
+ }
204
+ memcpy(copy, string, length);
205
+
206
+ return copy;
207
+ }
208
+
209
+ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
210
+ {
211
+ if (hooks == NULL)
212
+ {
213
+ /* Reset hooks */
214
+ global_hooks.allocate = malloc;
215
+ global_hooks.deallocate = free;
216
+ global_hooks.reallocate = realloc;
217
+ return;
218
+ }
219
+
220
+ global_hooks.allocate = malloc;
221
+ if (hooks->malloc_fn != NULL)
222
+ {
223
+ global_hooks.allocate = hooks->malloc_fn;
224
+ }
225
+
226
+ global_hooks.deallocate = free;
227
+ if (hooks->free_fn != NULL)
228
+ {
229
+ global_hooks.deallocate = hooks->free_fn;
230
+ }
231
+
232
+ /* use realloc only if both free and malloc are used */
233
+ global_hooks.reallocate = NULL;
234
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
235
+ {
236
+ global_hooks.reallocate = realloc;
237
+ }
238
+ }
239
+
240
+ /* Internal constructor. */
241
+ static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
242
+ {
243
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
244
+ if (node)
245
+ {
246
+ memset(node, '\0', sizeof(cJSON));
247
+ }
248
+
249
+ return node;
250
+ }
251
+
252
+ /* Delete a cJSON structure. */
253
+ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
254
+ {
255
+ cJSON *next = NULL;
256
+ while (item != NULL)
257
+ {
258
+ next = item->next;
259
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
260
+ {
261
+ cJSON_Delete(item->child);
262
+ }
263
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
264
+ {
265
+ global_hooks.deallocate(item->valuestring);
266
+ item->valuestring = NULL;
267
+ }
268
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
269
+ {
270
+ global_hooks.deallocate(item->string);
271
+ item->string = NULL;
272
+ }
273
+ global_hooks.deallocate(item);
274
+ item = next;
275
+ }
276
+ }
277
+
278
+ /* get the decimal point character of the current locale */
279
+ static unsigned char get_decimal_point(void)
280
+ {
281
+ #ifdef ENABLE_LOCALES
282
+ struct lconv *lconv = localeconv();
283
+ return (unsigned char) lconv->decimal_point[0];
284
+ #else
285
+ return '.';
286
+ #endif
287
+ }
288
+
289
+ typedef struct
290
+ {
291
+ const unsigned char *content;
292
+ size_t length;
293
+ size_t offset;
294
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
295
+ internal_hooks hooks;
296
+ } parse_buffer;
297
+
298
+ /* check if the given size is left to read in a given parse buffer (starting with 1) */
299
+ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
300
+ /* check if the buffer can be accessed at the given index (starting with 0) */
301
+ #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
302
+ #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
303
+ /* get a pointer to the buffer at the position */
304
+ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
305
+
306
+ /* Parse the input text to generate a number, and populate the result into item. */
307
+ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
308
+ {
309
+ double number = 0;
310
+ unsigned char *after_end = NULL;
311
+ unsigned char number_c_string[64];
312
+ unsigned char decimal_point = get_decimal_point();
313
+ size_t i = 0;
314
+
315
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
316
+ {
317
+ return false;
318
+ }
319
+
320
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
321
+ * of the current locale (for strtod)
322
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
323
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
324
+ {
325
+ switch (buffer_at_offset(input_buffer)[i])
326
+ {
327
+ case '0':
328
+ case '1':
329
+ case '2':
330
+ case '3':
331
+ case '4':
332
+ case '5':
333
+ case '6':
334
+ case '7':
335
+ case '8':
336
+ case '9':
337
+ case '+':
338
+ case '-':
339
+ case 'e':
340
+ case 'E':
341
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
342
+ break;
343
+
344
+ case '.':
345
+ number_c_string[i] = decimal_point;
346
+ break;
347
+
348
+ default:
349
+ goto loop_end;
350
+ }
351
+ }
352
+ loop_end:
353
+ number_c_string[i] = '\0';
354
+
355
+ number = strtod((const char*)number_c_string, (char**)&after_end);
356
+ if (number_c_string == after_end)
357
+ {
358
+ return false; /* parse_error */
359
+ }
360
+
361
+ item->valuedouble = number;
362
+
363
+ /* use saturation in case of overflow */
364
+ if (number >= INT_MAX)
365
+ {
366
+ item->valueint = INT_MAX;
367
+ }
368
+ else if (number <= (double)INT_MIN)
369
+ {
370
+ item->valueint = INT_MIN;
371
+ }
372
+ else
373
+ {
374
+ item->valueint = (int)number;
375
+ }
376
+
377
+ item->type = cJSON_Number;
378
+
379
+ input_buffer->offset += (size_t)(after_end - number_c_string);
380
+ return true;
381
+ }
382
+
383
+ /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
384
+ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
385
+ {
386
+ if (number >= INT_MAX)
387
+ {
388
+ object->valueint = INT_MAX;
389
+ }
390
+ else if (number <= (double)INT_MIN)
391
+ {
392
+ object->valueint = INT_MIN;
393
+ }
394
+ else
395
+ {
396
+ object->valueint = (int)number;
397
+ }
398
+
399
+ return object->valuedouble = number;
400
+ }
401
+
402
+ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
403
+ {
404
+ char *copy = NULL;
405
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
406
+ if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
407
+ {
408
+ return NULL;
409
+ }
410
+ /* return NULL if the object is corrupted */
411
+ if (object->valuestring == NULL)
412
+ {
413
+ return NULL;
414
+ }
415
+ if (strlen(valuestring) <= strlen(object->valuestring))
416
+ {
417
+ strcpy(object->valuestring, valuestring);
418
+ return object->valuestring;
419
+ }
420
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
421
+ if (copy == NULL)
422
+ {
423
+ return NULL;
424
+ }
425
+ if (object->valuestring != NULL)
426
+ {
427
+ cJSON_free(object->valuestring);
428
+ }
429
+ object->valuestring = copy;
430
+
431
+ return copy;
432
+ }
433
+
434
+ typedef struct
435
+ {
436
+ unsigned char *buffer;
437
+ size_t length;
438
+ size_t offset;
439
+ size_t depth; /* current nesting depth (for formatted printing) */
440
+ cJSON_bool noalloc;
441
+ cJSON_bool format; /* is this print a formatted print */
442
+ internal_hooks hooks;
443
+ } printbuffer;
444
+
445
+ /* realloc printbuffer if necessary to have at least "needed" bytes more */
446
+ static unsigned char* ensure(printbuffer * const p, size_t needed)
447
+ {
448
+ unsigned char *newbuffer = NULL;
449
+ size_t newsize = 0;
450
+
451
+ if ((p == NULL) || (p->buffer == NULL))
452
+ {
453
+ return NULL;
454
+ }
455
+
456
+ if ((p->length > 0) && (p->offset >= p->length))
457
+ {
458
+ /* make sure that offset is valid */
459
+ return NULL;
460
+ }
461
+
462
+ if (needed > INT_MAX)
463
+ {
464
+ /* sizes bigger than INT_MAX are currently not supported */
465
+ return NULL;
466
+ }
467
+
468
+ needed += p->offset + 1;
469
+ if (needed <= p->length)
470
+ {
471
+ return p->buffer + p->offset;
472
+ }
473
+
474
+ if (p->noalloc) {
475
+ return NULL;
476
+ }
477
+
478
+ /* calculate new buffer size */
479
+ if (needed > (INT_MAX / 2))
480
+ {
481
+ /* overflow of int, use INT_MAX if possible */
482
+ if (needed <= INT_MAX)
483
+ {
484
+ newsize = INT_MAX;
485
+ }
486
+ else
487
+ {
488
+ return NULL;
489
+ }
490
+ }
491
+ else
492
+ {
493
+ newsize = needed * 2;
494
+ }
495
+
496
+ if (p->hooks.reallocate != NULL)
497
+ {
498
+ /* reallocate with realloc if available */
499
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
500
+ if (newbuffer == NULL)
501
+ {
502
+ p->hooks.deallocate(p->buffer);
503
+ p->length = 0;
504
+ p->buffer = NULL;
505
+
506
+ return NULL;
507
+ }
508
+ }
509
+ else
510
+ {
511
+ /* otherwise reallocate manually */
512
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
513
+ if (!newbuffer)
514
+ {
515
+ p->hooks.deallocate(p->buffer);
516
+ p->length = 0;
517
+ p->buffer = NULL;
518
+
519
+ return NULL;
520
+ }
521
+
522
+ memcpy(newbuffer, p->buffer, p->offset + 1);
523
+ p->hooks.deallocate(p->buffer);
524
+ }
525
+ p->length = newsize;
526
+ p->buffer = newbuffer;
527
+
528
+ return newbuffer + p->offset;
529
+ }
530
+
531
+ /* calculate the new length of the string in a printbuffer and update the offset */
532
+ static void update_offset(printbuffer * const p)
533
+ {
534
+ const unsigned char *buffer = NULL;
535
+ if ((p == NULL) || (p->buffer == NULL))
536
+ {
537
+ return;
538
+ }
539
+ buffer = p->buffer + p->offset;
540
+
541
+ p->offset += strlen((const char*)buffer);
542
+ }
543
+
544
+ /* securely comparison of floating-point variables */
545
+ static cJSON_bool compare_double(double a, double b)
546
+ {
547
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
548
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
549
+ }
550
+
551
+ /* Render the number nicely from the given item into a string. */
552
+ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
553
+ {
554
+ unsigned char *output_pointer = NULL;
555
+ double d = item->valuedouble;
556
+ int length = 0;
557
+ size_t i = 0;
558
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
559
+ unsigned char decimal_point = get_decimal_point();
560
+ double test = 0.0;
561
+
562
+ if (output_buffer == NULL)
563
+ {
564
+ return false;
565
+ }
566
+
567
+ /* This checks for NaN and Infinity */
568
+ if (isnan(d) || isinf(d))
569
+ {
570
+ length = sprintf((char*)number_buffer, "null");
571
+ }
572
+ else if (d == (double)item->valueint)
573
+ {
574
+ length = sprintf((char*)number_buffer, "%d", item->valueint);
575
+ }
576
+ else
577
+ {
578
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
579
+ length = sprintf((char*)number_buffer, "%1.15g", d);
580
+
581
+ /* Check whether the original double can be recovered */
582
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
583
+ {
584
+ /* If not, print with 17 decimal places of precision */
585
+ length = sprintf((char*)number_buffer, "%1.17g", d);
586
+ }
587
+ }
588
+
589
+ /* sprintf failed or buffer overrun occurred */
590
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
591
+ {
592
+ return false;
593
+ }
594
+
595
+ /* reserve appropriate space in the output */
596
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
597
+ if (output_pointer == NULL)
598
+ {
599
+ return false;
600
+ }
601
+
602
+ /* copy the printed number to the output and replace locale
603
+ * dependent decimal point with '.' */
604
+ for (i = 0; i < ((size_t)length); i++)
605
+ {
606
+ if (number_buffer[i] == decimal_point)
607
+ {
608
+ output_pointer[i] = '.';
609
+ continue;
610
+ }
611
+
612
+ output_pointer[i] = number_buffer[i];
613
+ }
614
+ output_pointer[i] = '\0';
615
+
616
+ output_buffer->offset += (size_t)length;
617
+
618
+ return true;
619
+ }
620
+
621
+ /* parse 4 digit hexadecimal number */
622
+ static unsigned parse_hex4(const unsigned char * const input)
623
+ {
624
+ unsigned int h = 0;
625
+ size_t i = 0;
626
+
627
+ for (i = 0; i < 4; i++)
628
+ {
629
+ /* parse digit */
630
+ if ((input[i] >= '0') && (input[i] <= '9'))
631
+ {
632
+ h += (unsigned int) input[i] - '0';
633
+ }
634
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
635
+ {
636
+ h += (unsigned int) 10 + input[i] - 'A';
637
+ }
638
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
639
+ {
640
+ h += (unsigned int) 10 + input[i] - 'a';
641
+ }
642
+ else /* invalid */
643
+ {
644
+ return 0;
645
+ }
646
+
647
+ if (i < 3)
648
+ {
649
+ /* shift left to make place for the next nibble */
650
+ h = h << 4;
651
+ }
652
+ }
653
+
654
+ return h;
655
+ }
656
+
657
+ /* converts a UTF-16 literal to UTF-8
658
+ * A literal can be one or two sequences of the form \uXXXX */
659
+ static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
660
+ {
661
+ long unsigned int codepoint = 0;
662
+ unsigned int first_code = 0;
663
+ const unsigned char *first_sequence = input_pointer;
664
+ unsigned char utf8_length = 0;
665
+ unsigned char utf8_position = 0;
666
+ unsigned char sequence_length = 0;
667
+ unsigned char first_byte_mark = 0;
668
+
669
+ if ((input_end - first_sequence) < 6)
670
+ {
671
+ /* input ends unexpectedly */
672
+ goto fail;
673
+ }
674
+
675
+ /* get the first utf16 sequence */
676
+ first_code = parse_hex4(first_sequence + 2);
677
+
678
+ /* check that the code is valid */
679
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
680
+ {
681
+ goto fail;
682
+ }
683
+
684
+ /* UTF16 surrogate pair */
685
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
686
+ {
687
+ const unsigned char *second_sequence = first_sequence + 6;
688
+ unsigned int second_code = 0;
689
+ sequence_length = 12; /* \uXXXX\uXXXX */
690
+
691
+ if ((input_end - second_sequence) < 6)
692
+ {
693
+ /* input ends unexpectedly */
694
+ goto fail;
695
+ }
696
+
697
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
698
+ {
699
+ /* missing second half of the surrogate pair */
700
+ goto fail;
701
+ }
702
+
703
+ /* get the second utf16 sequence */
704
+ second_code = parse_hex4(second_sequence + 2);
705
+ /* check that the code is valid */
706
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
707
+ {
708
+ /* invalid second half of the surrogate pair */
709
+ goto fail;
710
+ }
711
+
712
+
713
+ /* calculate the unicode codepoint from the surrogate pair */
714
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
715
+ }
716
+ else
717
+ {
718
+ sequence_length = 6; /* \uXXXX */
719
+ codepoint = first_code;
720
+ }
721
+
722
+ /* encode as UTF-8
723
+ * takes at maximum 4 bytes to encode:
724
+ * 0 - 0x7F: 0xxxxxxx
725
+ * 0x80 - 0x7FF: 110xxxxx 10xxxxxx
726
+ * 0x800 - 0xFFFF: 1110xxxx 10xxxxxx 10xxxxxx
727
+ * 0x10000 - 0x10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
728
+
729
+ if (codepoint < 0x80)
730
+ {
731
+ /* one byte symbol */
732
+ utf8_length = 1;
733
+ }
734
+ else if (codepoint < 0x800)
735
+ {
736
+ /* two byte symbol */
737
+ utf8_length = 2;
738
+ }
739
+ else if (codepoint < 0x10000)
740
+ {
741
+ /* three byte symbol */
742
+ utf8_length = 3;
743
+ }
744
+ else if (codepoint <= 0x10FFFF)
745
+ {
746
+ /* four byte symbol */
747
+ utf8_length = 4;
748
+ }
749
+ else
750
+ {
751
+ /* invalid unicode codepoint */
752
+ goto fail;
753
+ }
754
+
755
+ /* encode as utf8 */
756
+ switch (utf8_length)
757
+ {
758
+ case 1:
759
+ /* first byte */
760
+ (*output_pointer)[0] = (unsigned char)(codepoint);
761
+ break;
762
+ case 2:
763
+ first_byte_mark = 0xC0; /* 11000000 */
764
+ (*output_pointer)[0] = (unsigned char)(codepoint >> 6) | first_byte_mark;
765
+ (*output_pointer)[1] = (unsigned char)(codepoint & 0x3F) | 0x80;
766
+ break;
767
+ case 3:
768
+ first_byte_mark = 0xE0; /* 11100000 */
769
+ (*output_pointer)[0] = (unsigned char)(codepoint >> 12) | first_byte_mark;
770
+ (*output_pointer)[1] = (unsigned char)((codepoint >> 6) & 0x3F) | 0x80;
771
+ (*output_pointer)[2] = (unsigned char)(codepoint & 0x3F) | 0x80;
772
+ break;
773
+ case 4:
774
+ first_byte_mark = 0xF0; /* 11110000 */
775
+ (*output_pointer)[0] = (unsigned char)(codepoint >> 18) | first_byte_mark;
776
+ (*output_pointer)[1] = (unsigned char)((codepoint >> 12) & 0x3F) | 0x80;
777
+ (*output_pointer)[2] = (unsigned char)((codepoint >> 6) & 0x3F) | 0x80;
778
+ (*output_pointer)[3] = (unsigned char)(codepoint & 0x3F) | 0x80;
779
+ break;
780
+ default:
781
+ /* Should not execute */
782
+ goto fail;
783
+ }
784
+
785
+ utf8_position = utf8_length;
786
+ /* skip the \u characters */
787
+ sequence_length = (sequence_length > 0) ? sequence_length : 0;
788
+
789
+ *output_pointer += utf8_position;
790
+ return sequence_length;
791
+
792
+ fail:
793
+ return 0;
794
+ }
795
+
796
+ /* Parse the input text into an unescaped cinput, and populate item. */
797
+ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
798
+ {
799
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
800
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
801
+ unsigned char *output_pointer = NULL;
802
+ unsigned char *output = NULL;
803
+
804
+ /* not a string */
805
+ if (buffer_at_offset(input_buffer)[0] != '\"')
806
+ {
807
+ goto fail;
808
+ }
809
+
810
+ {
811
+ /* calculate approximate size of the output (overestimate) */
812
+ size_t allocation_length = 0;
813
+ size_t skipped_bytes = 0;
814
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
815
+ {
816
+ /* Is escape sequence */
817
+ if (input_end[0] == '\\')
818
+ {
819
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
820
+ {
821
+ /* prevent buffer overflow when last input character is a backslash */
822
+ goto fail;
823
+ }
824
+ skipped_bytes++;
825
+ input_end++;
826
+ }
827
+ input_end++;
828
+ }
829
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
830
+ {
831
+ goto fail; /* string ended unexpectedly */
832
+ }
833
+
834
+ /* This is at most how much we need for the output */
835
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
836
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
837
+ if (output == NULL)
838
+ {
839
+ goto fail; /* allocation failure */
840
+ }
841
+ }
842
+
843
+ output_pointer = output;
844
+ /* loop through the string literal */
845
+ while (input_pointer < input_end)
846
+ {
847
+ if (*input_pointer != '\\')
848
+ {
849
+ *output_pointer++ = *input_pointer++;
850
+ }
851
+ /* escape sequence */
852
+ else
853
+ {
854
+ unsigned char sequence_length = 2;
855
+ if ((input_end - input_pointer) < 1)
856
+ {
857
+ goto fail;
858
+ }
859
+
860
+ switch (input_pointer[1])
861
+ {
862
+ case 'b':
863
+ *output_pointer++ = '\b';
864
+ break;
865
+ case 'f':
866
+ *output_pointer++ = '\f';
867
+ break;
868
+ case 'n':
869
+ *output_pointer++ = '\n';
870
+ break;
871
+ case 'r':
872
+ *output_pointer++ = '\r';
873
+ break;
874
+ case 't':
875
+ *output_pointer++ = '\t';
876
+ break;
877
+ case '\"':
878
+ case '\\':
879
+ case '/':
880
+ *output_pointer++ = input_pointer[1];
881
+ break;
882
+
883
+ /* UTF-16 literal */
884
+ case 'u':
885
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
886
+ if (sequence_length == 0)
887
+ {
888
+ /* failed to convert UTF-16-literal to UTF-8 */
889
+ goto fail;
890
+ }
891
+ break;
892
+
893
+ default:
894
+ goto fail;
895
+ }
896
+ input_pointer += sequence_length;
897
+ }
898
+ }
899
+
900
+ /* zero terminate the output */
901
+ *output_pointer = '\0';
902
+
903
+ item->type = cJSON_String;
904
+ item->valuestring = (char*)output;
905
+
906
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
907
+ input_buffer->offset++;
908
+
909
+ return true;
910
+
911
+ fail:
912
+ if (output != NULL)
913
+ {
914
+ input_buffer->hooks.deallocate(output);
915
+ output = NULL;
916
+ }
917
+
918
+ if (input_pointer != NULL)
919
+ {
920
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
921
+ }
922
+
923
+ return false;
924
+ }
925
+
926
+ /* Render the cstring provided to an escaped version that can be printed. */
927
+ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
928
+ {
929
+ const unsigned char *input_pointer = NULL;
930
+ unsigned char *output = NULL;
931
+ unsigned char *output_pointer = NULL;
932
+ size_t output_length = 0;
933
+ /* numbers of additional characters needed for escaping */
934
+ size_t escape_characters = 0;
935
+
936
+ if (output_buffer == NULL)
937
+ {
938
+ return false;
939
+ }
940
+
941
+ /* empty string */
942
+ if (input == NULL)
943
+ {
944
+ output = ensure(output_buffer, sizeof("\"\""));
945
+ if (output == NULL)
946
+ {
947
+ return false;
948
+ }
949
+ strcpy((char*)output, "\"\"");
950
+
951
+ output_buffer->offset += 2;
952
+
953
+ return true;
954
+ }
955
+
956
+ /* calculate "escape" characters and final string length */
957
+ for (input_pointer = input; *input_pointer; input_pointer++)
958
+ {
959
+ switch (*input_pointer)
960
+ {
961
+ case '\"':
962
+ case '\\':
963
+ case '\b':
964
+ case '\f':
965
+ case '\n':
966
+ case '\r':
967
+ case '\t':
968
+ /* one character escape needed */
969
+ escape_characters++;
970
+ break;
971
+ default:
972
+ if (*input_pointer < 32)
973
+ {
974
+ /* UTF-16 escape sequence \uXXXX should be printed */
975
+ escape_characters += 5;
976
+ }
977
+ break;
978
+ }
979
+ }
980
+ output_length = (size_t)(input_pointer - input) + escape_characters;
981
+
982
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
983
+ if (output == NULL)
984
+ {
985
+ return false;
986
+ }
987
+
988
+ /* no characters have to be escaped */
989
+ if (escape_characters == 0)
990
+ {
991
+ output[0] = '\"';
992
+ memcpy(output + 1, input, output_length);
993
+ output[output_length + 1] = '\"';
994
+ output[output_length + 2] = '\0';
995
+
996
+ output_buffer->offset += output_length + 2;
997
+
998
+ return true;
999
+ }
1000
+
1001
+ output[0] = '\"';
1002
+ output_pointer = output + 1;
1003
+ /* copy the string */
1004
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
1005
+ {
1006
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
1007
+ {
1008
+ /* normal character, copy */
1009
+ *output_pointer = *input_pointer;
1010
+ }
1011
+ else
1012
+ {
1013
+ /* character needs to be escaped */
1014
+ *output_pointer++ = '\\';
1015
+ switch (*input_pointer)
1016
+ {
1017
+ case '\\':
1018
+ *output_pointer = '\\';
1019
+ break;
1020
+ case '\"':
1021
+ *output_pointer = '\"';
1022
+ break;
1023
+ case '\b':
1024
+ *output_pointer = 'b';
1025
+ break;
1026
+ case '\f':
1027
+ *output_pointer = 'f';
1028
+ break;
1029
+ case '\n':
1030
+ *output_pointer = 'n';
1031
+ break;
1032
+ case '\r':
1033
+ *output_pointer = 'r';
1034
+ break;
1035
+ case '\t':
1036
+ *output_pointer = 't';
1037
+ break;
1038
+ default:
1039
+ /* escape and print as unicode codepoint */
1040
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
1041
+ output_pointer += 4;
1042
+ break;
1043
+ }
1044
+ }
1045
+ }
1046
+ output[output_length + 1] = '\"';
1047
+ output[output_length + 2] = '\0';
1048
+
1049
+ output_buffer->offset += output_length + 2;
1050
+
1051
+ return true;
1052
+ }
1053
+
1054
+ /* Invoke print_string_ptr (which is useful) on an item. */
1055
+ static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
1056
+ {
1057
+ return print_string_ptr((unsigned char*)item->valuestring, p);
1058
+ }
1059
+
1060
+ /* Predeclare these prototypes. */
1061
+ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
1062
+ static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
1063
+ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
1064
+ static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
1065
+ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
1066
+ static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
1067
+
1068
+ /* Utility to jump whitespace and cr/lf */
1069
+ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
1070
+ {
1071
+ if ((buffer == NULL) || (buffer->content == NULL))
1072
+ {
1073
+ return NULL;
1074
+ }
1075
+
1076
+ if (cannot_access_at_index(buffer, 0))
1077
+ {
1078
+ return buffer;
1079
+ }
1080
+
1081
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
1082
+ {
1083
+ buffer->offset++;
1084
+ }
1085
+
1086
+ if (buffer->offset == buffer->length)
1087
+ {
1088
+ buffer->offset--;
1089
+ }
1090
+
1091
+ return buffer;
1092
+ }
1093
+
1094
+ /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
1095
+ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
1096
+ {
1097
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
1098
+ {
1099
+ return NULL;
1100
+ }
1101
+
1102
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
1103
+ {
1104
+ buffer->offset += 3;
1105
+ }
1106
+
1107
+ return buffer;
1108
+ }
1109
+
1110
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
1111
+ {
1112
+ size_t buffer_length;
1113
+
1114
+ if (NULL == value)
1115
+ {
1116
+ return NULL;
1117
+ }
1118
+
1119
+ /* Adding null character detection due to parse_parse_string and the way cJSON works */
1120
+ buffer_length = strlen(value) + sizeof("");
1121
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
1122
+ }
1123
+
1124
+ /* Parse an object - create a new root, and populate. */
1125
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
1126
+ {
1127
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
1128
+ cJSON *item = NULL;
1129
+
1130
+ /* reset error position */
1131
+ global_error.json = NULL;
1132
+ global_error.position = 0;
1133
+
1134
+ if (value == NULL || 0 == buffer_length)
1135
+ {
1136
+ goto fail;
1137
+ }
1138
+
1139
+ buffer.content = (const unsigned char*)value;
1140
+ buffer.length = buffer_length;
1141
+ buffer.offset = 0;
1142
+ buffer.hooks = global_hooks;
1143
+
1144
+ item = cJSON_New_Item(&global_hooks);
1145
+ if (item == NULL) /* memory fail */
1146
+ {
1147
+ goto fail;
1148
+ }
1149
+
1150
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
1151
+ {
1152
+ /* parse failure. ep is set. */
1153
+ goto fail;
1154
+ }
1155
+
1156
+ /* if we require null-terminated JSON without appended garbage, skip then and check for a null terminator */
1157
+ if (require_null_terminated)
1158
+ {
1159
+ buffer_skip_whitespace(&buffer);
1160
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
1161
+ {
1162
+ goto fail;
1163
+ }
1164
+ }
1165
+ if (return_parse_end)
1166
+ {
1167
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
1168
+ }
1169
+
1170
+ return item;
1171
+
1172
+ fail:
1173
+ if (item != NULL)
1174
+ {
1175
+ cJSON_Delete(item);
1176
+ }
1177
+
1178
+ if (value != NULL)
1179
+ {
1180
+ error local_error;
1181
+ local_error.json = (const unsigned char*)value;
1182
+ local_error.position = 0;
1183
+
1184
+ if (buffer.offset < buffer.length)
1185
+ {
1186
+ local_error.position = buffer.offset;
1187
+ }
1188
+ else if (buffer.length > 0)
1189
+ {
1190
+ local_error.position = buffer.length - 1;
1191
+ }
1192
+
1193
+ if (return_parse_end != NULL)
1194
+ {
1195
+ *return_parse_end = (const char*)local_error.json + local_error.position;
1196
+ }
1197
+
1198
+ global_error = local_error;
1199
+ }
1200
+
1201
+ return NULL;
1202
+ }
1203
+
1204
+ /* Default options for cJSON_Parse */
1205
+ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
1206
+ {
1207
+ return cJSON_ParseWithOpts(value, 0, 0);
1208
+ }
1209
+
1210
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
1211
+ {
1212
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
1213
+ }
1214
+
1215
+ #define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
1216
+
1217
+ static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
1218
+ {
1219
+ static const size_t default_buffer_size = 256;
1220
+ printbuffer buffer[1];
1221
+ unsigned char *printed = NULL;
1222
+
1223
+ memset(buffer, 0, sizeof(buffer));
1224
+
1225
+ /* create buffer */
1226
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
1227
+ buffer->length = default_buffer_size;
1228
+ buffer->format = format;
1229
+ buffer->hooks = *hooks;
1230
+ if (buffer->buffer == NULL)
1231
+ {
1232
+ goto fail;
1233
+ }
1234
+
1235
+ /* print the value */
1236
+ if (!print_value(item, buffer))
1237
+ {
1238
+ goto fail;
1239
+ }
1240
+ update_offset(buffer);
1241
+
1242
+ /* check if reallocate is available */
1243
+ if (hooks->reallocate != NULL)
1244
+ {
1245
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
1246
+ if (printed == NULL) {
1247
+ goto fail;
1248
+ }
1249
+ buffer->buffer = NULL;
1250
+ }
1251
+ else /* otherwise copy the JSON over to a new buffer */
1252
+ {
1253
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
1254
+ if (printed == NULL)
1255
+ {
1256
+ goto fail;
1257
+ }
1258
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
1259
+ printed[buffer->offset] = '\0'; /* just to be sure */
1260
+
1261
+ /* free the buffer */
1262
+ hooks->deallocate(buffer->buffer);
1263
+ buffer->buffer = NULL;
1264
+ }
1265
+
1266
+ return printed;
1267
+
1268
+ fail:
1269
+ if (buffer->buffer != NULL)
1270
+ {
1271
+ hooks->deallocate(buffer->buffer);
1272
+ buffer->buffer = NULL;
1273
+ }
1274
+
1275
+ if (printed != NULL)
1276
+ {
1277
+ hooks->deallocate(printed);
1278
+ printed = NULL;
1279
+ }
1280
+
1281
+ return NULL;
1282
+ }
1283
+
1284
+ /* Render a cJSON item/entity/structure to text. */
1285
+ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
1286
+ {
1287
+ return (char*)print(item, true, &global_hooks);
1288
+ }
1289
+
1290
+ CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
1291
+ {
1292
+ return (char*)print(item, false, &global_hooks);
1293
+ }
1294
+
1295
+ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
1296
+ {
1297
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1298
+
1299
+ if (prebuffer < 0)
1300
+ {
1301
+ return NULL;
1302
+ }
1303
+
1304
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
1305
+ if (!p.buffer)
1306
+ {
1307
+ return NULL;
1308
+ }
1309
+
1310
+ p.length = (size_t)prebuffer;
1311
+ p.offset = 0;
1312
+ p.noalloc = false;
1313
+ p.format = fmt;
1314
+ p.hooks = global_hooks;
1315
+
1316
+ if (!print_value(item, &p))
1317
+ {
1318
+ global_hooks.deallocate(p.buffer);
1319
+ p.buffer = NULL;
1320
+ return NULL;
1321
+ }
1322
+
1323
+ update_offset(&p);
1324
+
1325
+ return (char*)p.buffer;
1326
+ }
1327
+
1328
+ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
1329
+ {
1330
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1331
+
1332
+ if ((length < 0) || (buffer == NULL))
1333
+ {
1334
+ return false;
1335
+ }
1336
+
1337
+ p.buffer = (unsigned char*)buffer;
1338
+ p.length = (size_t)length;
1339
+ p.offset = 0;
1340
+ p.noalloc = true;
1341
+ p.format = format;
1342
+ p.hooks = global_hooks;
1343
+
1344
+ return print_value(item, &p);
1345
+ }
1346
+
1347
+ /* Parser core - when encountering text, process appropriately. */
1348
+ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
1349
+ {
1350
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
1351
+ {
1352
+ return false; /* no input */
1353
+ }
1354
+
1355
+ /* parse the different types of values */
1356
+ /* null */
1357
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
1358
+ {
1359
+ item->type = cJSON_NULL;
1360
+ input_buffer->offset += 4;
1361
+ return true;
1362
+ }
1363
+ /* false */
1364
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
1365
+ {
1366
+ item->type = cJSON_False;
1367
+ input_buffer->offset += 5;
1368
+ return true;
1369
+ }
1370
+ /* true */
1371
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
1372
+ {
1373
+ item->type = cJSON_True;
1374
+ item->valueint = 1;
1375
+ input_buffer->offset += 4;
1376
+ return true;
1377
+ }
1378
+ /* string */
1379
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
1380
+ {
1381
+ return parse_string(item, input_buffer);
1382
+ }
1383
+ /* number */
1384
+ 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'))))
1385
+ {
1386
+ return parse_number(item, input_buffer);
1387
+ }
1388
+ /* array */
1389
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
1390
+ {
1391
+ return parse_array(item, input_buffer);
1392
+ }
1393
+ /* object */
1394
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
1395
+ {
1396
+ return parse_object(item, input_buffer);
1397
+ }
1398
+
1399
+ return false;
1400
+ }
1401
+
1402
+ /* Render a value to text. */
1403
+ static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
1404
+ {
1405
+ unsigned char *output = NULL;
1406
+
1407
+ if ((item == NULL) || (output_buffer == NULL))
1408
+ {
1409
+ return false;
1410
+ }
1411
+
1412
+ switch ((item->type) & 0xFF)
1413
+ {
1414
+ case cJSON_NULL:
1415
+ output = ensure(output_buffer, 5);
1416
+ if (output == NULL)
1417
+ {
1418
+ return false;
1419
+ }
1420
+ strcpy((char*)output, "null");
1421
+ output_buffer->offset += 4;
1422
+ return true;
1423
+
1424
+ case cJSON_False:
1425
+ output = ensure(output_buffer, 6);
1426
+ if (output == NULL)
1427
+ {
1428
+ return false;
1429
+ }
1430
+ strcpy((char*)output, "false");
1431
+ output_buffer->offset += 5;
1432
+ return true;
1433
+
1434
+ case cJSON_True:
1435
+ output = ensure(output_buffer, 5);
1436
+ if (output == NULL)
1437
+ {
1438
+ return false;
1439
+ }
1440
+ strcpy((char*)output, "true");
1441
+ output_buffer->offset += 4;
1442
+ return true;
1443
+
1444
+ case cJSON_Number:
1445
+ return print_number(item, output_buffer);
1446
+
1447
+ case cJSON_Raw:
1448
+ {
1449
+ size_t raw_length = 0;
1450
+ if (item->valuestring == NULL)
1451
+ {
1452
+ return false;
1453
+ }
1454
+
1455
+ raw_length = strlen(item->valuestring) + sizeof("");
1456
+ output = ensure(output_buffer, raw_length);
1457
+ if (output == NULL)
1458
+ {
1459
+ return false;
1460
+ }
1461
+ memcpy(output, item->valuestring, raw_length);
1462
+ output_buffer->offset += raw_length - 1;
1463
+ return true;
1464
+ }
1465
+
1466
+ case cJSON_String:
1467
+ return print_string(item, output_buffer);
1468
+
1469
+ case cJSON_Array:
1470
+ return print_array(item, output_buffer);
1471
+
1472
+ case cJSON_Object:
1473
+ return print_object(item, output_buffer);
1474
+
1475
+ default:
1476
+ return false;
1477
+ }
1478
+ }
1479
+
1480
+ /* Build an array from input text. */
1481
+ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
1482
+ {
1483
+ cJSON *head = NULL; /* head of the linked list */
1484
+ cJSON *current_item = NULL;
1485
+
1486
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1487
+ {
1488
+ return false; /* to deeply nested */
1489
+ }
1490
+ input_buffer->depth++;
1491
+
1492
+ if (buffer_at_offset(input_buffer)[0] != '[')
1493
+ {
1494
+ /* not an array */
1495
+ goto fail;
1496
+ }
1497
+
1498
+ input_buffer->offset++;
1499
+ buffer_skip_whitespace(input_buffer);
1500
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
1501
+ {
1502
+ /* empty array */
1503
+ goto success;
1504
+ }
1505
+
1506
+ /* check if we skipped to the end of the buffer */
1507
+ if (cannot_access_at_index(input_buffer, 0))
1508
+ {
1509
+ input_buffer->offset--;
1510
+ goto fail;
1511
+ }
1512
+
1513
+ /* step back to character in front of the first element */
1514
+ input_buffer->offset--;
1515
+ /* loop through the comma separated array elements */
1516
+ do
1517
+ {
1518
+ /* allocate next item */
1519
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1520
+ if (new_item == NULL)
1521
+ {
1522
+ goto fail; /* allocation failure */
1523
+ }
1524
+
1525
+ /* attach next item to list */
1526
+ if (head == NULL)
1527
+ {
1528
+ /* start the linked list */
1529
+ current_item = head = new_item;
1530
+ }
1531
+ else
1532
+ {
1533
+ /* add to the end and advance */
1534
+ current_item->next = new_item;
1535
+ new_item->prev = current_item;
1536
+ current_item = new_item;
1537
+ }
1538
+
1539
+ /* parse next value */
1540
+ input_buffer->offset++;
1541
+ buffer_skip_whitespace(input_buffer);
1542
+ if (!parse_value(current_item, input_buffer))
1543
+ {
1544
+ goto fail; /* failed to parse value */
1545
+ }
1546
+ buffer_skip_whitespace(input_buffer);
1547
+ }
1548
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1549
+
1550
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
1551
+ {
1552
+ goto fail; /* expected end of array */
1553
+ }
1554
+
1555
+ success:
1556
+ input_buffer->depth--;
1557
+
1558
+ if (head != NULL) {
1559
+ head->prev = current_item;
1560
+ }
1561
+
1562
+ item->type = cJSON_Array;
1563
+ item->child = head;
1564
+
1565
+ input_buffer->offset++;
1566
+
1567
+ return true;
1568
+
1569
+ fail:
1570
+ if (head != NULL)
1571
+ {
1572
+ cJSON_Delete(head);
1573
+ }
1574
+
1575
+ return false;
1576
+ }
1577
+
1578
+ /* Render an array to text */
1579
+ static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
1580
+ {
1581
+ unsigned char *output_pointer = NULL;
1582
+ size_t length = 0;
1583
+ cJSON *current_element = item->child;
1584
+
1585
+ if (output_buffer == NULL)
1586
+ {
1587
+ return false;
1588
+ }
1589
+
1590
+ /* Compose the output array. */
1591
+ /* opening square bracket */
1592
+ output_pointer = ensure(output_buffer, 1);
1593
+ if (output_pointer == NULL)
1594
+ {
1595
+ return false;
1596
+ }
1597
+
1598
+ *output_pointer = '[';
1599
+ output_buffer->offset++;
1600
+ output_buffer->depth++;
1601
+
1602
+ while (current_element != NULL)
1603
+ {
1604
+ if (!print_value(current_element, output_buffer))
1605
+ {
1606
+ return false;
1607
+ }
1608
+ update_offset(output_buffer);
1609
+ if (current_element->next)
1610
+ {
1611
+ length = (size_t) (output_buffer->format ? 2 : 1);
1612
+ output_pointer = ensure(output_buffer, length + 1);
1613
+ if (output_pointer == NULL)
1614
+ {
1615
+ return false;
1616
+ }
1617
+ *output_pointer++ = ',';
1618
+ if(output_buffer->format)
1619
+ {
1620
+ *output_pointer++ = ' ';
1621
+ }
1622
+ *output_pointer = '\0';
1623
+ output_buffer->offset += length;
1624
+ }
1625
+ current_element = current_element->next;
1626
+ }
1627
+
1628
+ output_pointer = ensure(output_buffer, 2);
1629
+ if (output_pointer == NULL)
1630
+ {
1631
+ return false;
1632
+ }
1633
+ *output_pointer++ = ']';
1634
+ *output_pointer = '\0';
1635
+ output_buffer->depth--;
1636
+
1637
+ return true;
1638
+ }
1639
+
1640
+ /* Build an object from the text. */
1641
+ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
1642
+ {
1643
+ cJSON *head = NULL; /* linked list head */
1644
+ cJSON *current_item = NULL;
1645
+
1646
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1647
+ {
1648
+ return false; /* to deeply nested */
1649
+ }
1650
+ input_buffer->depth++;
1651
+
1652
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
1653
+ {
1654
+ goto fail; /* not an object */
1655
+ }
1656
+
1657
+ input_buffer->offset++;
1658
+ buffer_skip_whitespace(input_buffer);
1659
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
1660
+ {
1661
+ goto success; /* empty object */
1662
+ }
1663
+
1664
+ /* check if we skipped to the end of the buffer */
1665
+ if (cannot_access_at_index(input_buffer, 0))
1666
+ {
1667
+ input_buffer->offset--;
1668
+ goto fail;
1669
+ }
1670
+
1671
+ /* step back to character in front of the first element */
1672
+ input_buffer->offset--;
1673
+ /* loop through the comma separated array elements */
1674
+ do
1675
+ {
1676
+ /* allocate next item */
1677
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1678
+ if (new_item == NULL)
1679
+ {
1680
+ goto fail; /* allocation failure */
1681
+ }
1682
+
1683
+ /* attach next item to list */
1684
+ if (head == NULL)
1685
+ {
1686
+ /* start the linked list */
1687
+ current_item = head = new_item;
1688
+ }
1689
+ else
1690
+ {
1691
+ /* add to the end and advance */
1692
+ current_item->next = new_item;
1693
+ new_item->prev = current_item;
1694
+ current_item = new_item;
1695
+ }
1696
+
1697
+ if (cannot_access_at_index(input_buffer, 1))
1698
+ {
1699
+ goto fail; /* nothing comes after the comma */
1700
+ }
1701
+
1702
+ /* parse the name of the child */
1703
+ input_buffer->offset++;
1704
+ buffer_skip_whitespace(input_buffer);
1705
+ if (!parse_string(current_item, input_buffer))
1706
+ {
1707
+ goto fail; /* failed to parse name */
1708
+ }
1709
+ buffer_skip_whitespace(input_buffer);
1710
+
1711
+ /* swap valuestring and string, because we parsed the name */
1712
+ current_item->string = current_item->valuestring;
1713
+ current_item->valuestring = NULL;
1714
+
1715
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
1716
+ {
1717
+ goto fail; /* invalid object */
1718
+ }
1719
+
1720
+ /* parse the value */
1721
+ input_buffer->offset++;
1722
+ buffer_skip_whitespace(input_buffer);
1723
+ if (!parse_value(current_item, input_buffer))
1724
+ {
1725
+ goto fail; /* failed to parse value */
1726
+ }
1727
+ buffer_skip_whitespace(input_buffer);
1728
+ }
1729
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1730
+
1731
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
1732
+ {
1733
+ goto fail; /* expected end of object */
1734
+ }
1735
+
1736
+ success:
1737
+ input_buffer->depth--;
1738
+
1739
+ if (head != NULL) {
1740
+ head->prev = current_item;
1741
+ }
1742
+
1743
+ item->type = cJSON_Object;
1744
+ item->child = head;
1745
+
1746
+ input_buffer->offset++;
1747
+
1748
+ return true;
1749
+
1750
+ fail:
1751
+ if (head != NULL)
1752
+ {
1753
+ cJSON_Delete(head);
1754
+ }
1755
+
1756
+ return false;
1757
+ }
1758
+
1759
+ /* Render an object to text. */
1760
+ static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
1761
+ {
1762
+ unsigned char *output_pointer = NULL;
1763
+ size_t length = 0;
1764
+ cJSON *current_item = item->child;
1765
+
1766
+ if (output_buffer == NULL)
1767
+ {
1768
+ return false;
1769
+ }
1770
+
1771
+ /* Compose the output: */
1772
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
1773
+ output_pointer = ensure(output_buffer, length + 1);
1774
+ if (output_pointer == NULL)
1775
+ {
1776
+ return false;
1777
+ }
1778
+
1779
+ *output_pointer++ = '{';
1780
+ output_buffer->depth++;
1781
+ if (output_buffer->format)
1782
+ {
1783
+ *output_pointer++ = '\n';
1784
+ }
1785
+ output_buffer->offset += length;
1786
+
1787
+ while (current_item)
1788
+ {
1789
+ if (output_buffer->format)
1790
+ {
1791
+ size_t i;
1792
+ output_pointer = ensure(output_buffer, output_buffer->depth);
1793
+ if (output_pointer == NULL)
1794
+ {
1795
+ return false;
1796
+ }
1797
+ for (i = 0; i < output_buffer->depth; i++)
1798
+ {
1799
+ *output_pointer++ = '\t';
1800
+ }
1801
+ output_buffer->offset += output_buffer->depth;
1802
+ }
1803
+
1804
+ /* print key */
1805
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
1806
+ {
1807
+ return false;
1808
+ }
1809
+ update_offset(output_buffer);
1810
+
1811
+ length = (size_t) (output_buffer->format ? 2 : 1);
1812
+ output_pointer = ensure(output_buffer, length);
1813
+ if (output_pointer == NULL)
1814
+ {
1815
+ return false;
1816
+ }
1817
+ *output_pointer++ = ':';
1818
+ if (output_buffer->format)
1819
+ {
1820
+ *output_pointer++ = '\t';
1821
+ }
1822
+ output_buffer->offset += length;
1823
+
1824
+ /* print value */
1825
+ if (!print_value(current_item, output_buffer))
1826
+ {
1827
+ return false;
1828
+ }
1829
+ update_offset(output_buffer);
1830
+
1831
+ /* print comma if not last */
1832
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
1833
+ output_pointer = ensure(output_buffer, length + 1);
1834
+ if (output_pointer == NULL)
1835
+ {
1836
+ return false;
1837
+ }
1838
+ if (current_item->next)
1839
+ {
1840
+ *output_pointer++ = ',';
1841
+ }
1842
+
1843
+ if (output_buffer->format)
1844
+ {
1845
+ *output_pointer++ = '\n';
1846
+ }
1847
+ *output_pointer = '\0';
1848
+ output_buffer->offset += length;
1849
+
1850
+ current_item = current_item->next;
1851
+ }
1852
+
1853
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
1854
+ if (output_pointer == NULL)
1855
+ {
1856
+ return false;
1857
+ }
1858
+ if (output_buffer->format)
1859
+ {
1860
+ size_t i;
1861
+ for (i = 0; i < (output_buffer->depth - 1); i++)
1862
+ {
1863
+ *output_pointer++ = '\t';
1864
+ }
1865
+ }
1866
+ *output_pointer++ = '}';
1867
+ *output_pointer = '\0';
1868
+ output_buffer->depth--;
1869
+
1870
+ return true;
1871
+ }
1872
+
1873
+ /* Get Array size/item / object item. */
1874
+ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
1875
+ {
1876
+ cJSON *child = NULL;
1877
+ size_t size = 0;
1878
+
1879
+ if (array == NULL)
1880
+ {
1881
+ return 0;
1882
+ }
1883
+
1884
+ child = array->child;
1885
+
1886
+ while(child != NULL)
1887
+ {
1888
+ size++;
1889
+ child = child->next;
1890
+ }
1891
+
1892
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
1893
+
1894
+ return (int)size;
1895
+ }
1896
+
1897
+ static cJSON* get_array_item(const cJSON *array, size_t index)
1898
+ {
1899
+ cJSON *current_child = NULL;
1900
+
1901
+ if (array == NULL)
1902
+ {
1903
+ return NULL;
1904
+ }
1905
+
1906
+ current_child = array->child;
1907
+ while ((current_child != NULL) && (index > 0))
1908
+ {
1909
+ index--;
1910
+ current_child = current_child->next;
1911
+ }
1912
+
1913
+ return current_child;
1914
+ }
1915
+
1916
+ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
1917
+ {
1918
+ if (index < 0)
1919
+ {
1920
+ return NULL;
1921
+ }
1922
+
1923
+ return get_array_item(array, (size_t)index);
1924
+ }
1925
+
1926
+ static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
1927
+ {
1928
+ cJSON *current_element = NULL;
1929
+
1930
+ if ((object == NULL) || (name == NULL))
1931
+ {
1932
+ return NULL;
1933
+ }
1934
+
1935
+ current_element = object->child;
1936
+ if (case_sensitive)
1937
+ {
1938
+ while (current_element != NULL)
1939
+ {
1940
+ if (current_element->string && (strcmp(name, current_element->string) == 0))
1941
+ {
1942
+ return current_element;
1943
+ }
1944
+ current_element = current_element->next;
1945
+ }
1946
+ }
1947
+ else
1948
+ {
1949
+ while (current_element != NULL)
1950
+ {
1951
+ if (current_element->string && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) == 0))
1952
+ {
1953
+ return current_element;
1954
+ }
1955
+ current_element = current_element->next;
1956
+ }
1957
+ }
1958
+
1959
+ return NULL;
1960
+ }
1961
+
1962
+ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
1963
+ {
1964
+ return get_object_item(object, string, false);
1965
+ }
1966
+
1967
+ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
1968
+ {
1969
+ return get_object_item(object, string, true);
1970
+ }
1971
+
1972
+ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
1973
+ {
1974
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
1975
+ }
1976
+
1977
+ /* Utility for array list handling. */
1978
+ static void suffix_object(cJSON *prev, cJSON *item)
1979
+ {
1980
+ prev->next = item;
1981
+ item->prev = prev;
1982
+ }
1983
+
1984
+ /* Utility for handling references. */
1985
+ static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
1986
+ {
1987
+ cJSON *reference = NULL;
1988
+ if (item == NULL)
1989
+ {
1990
+ return NULL;
1991
+ }
1992
+
1993
+ reference = cJSON_New_Item(hooks);
1994
+ if (reference == NULL)
1995
+ {
1996
+ return NULL;
1997
+ }
1998
+
1999
+ memcpy(reference, item, sizeof(cJSON));
2000
+ reference->string = NULL;
2001
+ reference->type |= cJSON_IsReference;
2002
+ reference->next = reference->prev = NULL;
2003
+ return reference;
2004
+ }
2005
+
2006
+ static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
2007
+ {
2008
+ cJSON *child = NULL;
2009
+
2010
+ if ((item == NULL) || (array == NULL) || (array == item))
2011
+ {
2012
+ return false;
2013
+ }
2014
+
2015
+ child = array->child;
2016
+ /*
2017
+ * To find the last item in array quickly, we use prev in array
2018
+ */
2019
+ if (child == NULL)
2020
+ {
2021
+ /* list is empty, start new one */
2022
+ array->child = item;
2023
+ item->prev = item;
2024
+ item->next = NULL;
2025
+ }
2026
+ else
2027
+ {
2028
+ /* append to the end */
2029
+ if (child->prev)
2030
+ {
2031
+ suffix_object(child->prev, item);
2032
+ array->child->prev = item;
2033
+ }
2034
+ }
2035
+
2036
+ return true;
2037
+ }
2038
+
2039
+ /* Add item to array/object. */
2040
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
2041
+ {
2042
+ return add_item_to_array(array, item);
2043
+ }
2044
+
2045
+ #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2046
+ #pragma GCC diagnostic push
2047
+ #endif
2048
+ #ifdef __GNUC__
2049
+ #pragma GCC diagnostic ignored "-Wcast-qual"
2050
+ #endif
2051
+ /* helper function to cast away const */
2052
+ static void* cast_away_const(const void* string)
2053
+ {
2054
+ return (void*)string;
2055
+ }
2056
+ #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2057
+ #pragma GCC diagnostic pop
2058
+ #endif
2059
+
2060
+
2061
+ static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
2062
+ {
2063
+ char *new_key = NULL;
2064
+ int new_type = cJSON_Invalid;
2065
+
2066
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
2067
+ {
2068
+ return false;
2069
+ }
2070
+
2071
+ if (constant_key)
2072
+ {
2073
+ new_key = (char*)cast_away_const(string);
2074
+ new_type = item->type | cJSON_StringIsConst;
2075
+ }
2076
+ else
2077
+ {
2078
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
2079
+ if (new_key == NULL)
2080
+ {
2081
+ return false;
2082
+ }
2083
+
2084
+ new_type = item->type & ~cJSON_StringIsConst;
2085
+ }
2086
+
2087
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
2088
+ {
2089
+ hooks->deallocate(item->string);
2090
+ }
2091
+
2092
+ item->string = new_key;
2093
+ item->type = new_type;
2094
+
2095
+ return add_item_to_array(object, item);
2096
+ }
2097
+
2098
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
2099
+ {
2100
+ return add_item_to_object(object, string, item, &global_hooks, false);
2101
+ }
2102
+
2103
+ /* Add an item to an object with constant string as key */
2104
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
2105
+ {
2106
+ return add_item_to_object(object, string, item, &global_hooks, true);
2107
+ }
2108
+
2109
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
2110
+ {
2111
+ if (array == NULL)
2112
+ {
2113
+ return false;
2114
+ }
2115
+
2116
+ return add_item_to_array(array, create_reference(item, &global_hooks));
2117
+ }
2118
+
2119
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
2120
+ {
2121
+ if ((object == NULL) || (string == NULL))
2122
+ {
2123
+ return false;
2124
+ }
2125
+
2126
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
2127
+ }
2128
+
2129
+ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
2130
+ {
2131
+ cJSON *null = cJSON_CreateNull();
2132
+ if (add_item_to_object(object, name, null, &global_hooks, false))
2133
+ {
2134
+ return null;
2135
+ }
2136
+
2137
+ cJSON_Delete(null);
2138
+ return NULL;
2139
+ }
2140
+
2141
+ CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
2142
+ {
2143
+ cJSON *true_item = cJSON_CreateTrue();
2144
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
2145
+ {
2146
+ return true_item;
2147
+ }
2148
+
2149
+ cJSON_Delete(true_item);
2150
+ return NULL;
2151
+ }
2152
+
2153
+ CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
2154
+ {
2155
+ cJSON *false_item = cJSON_CreateFalse();
2156
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
2157
+ {
2158
+ return false_item;
2159
+ }
2160
+
2161
+ cJSON_Delete(false_item);
2162
+ return NULL;
2163
+ }
2164
+
2165
+ CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
2166
+ {
2167
+ cJSON *bool_item = cJSON_CreateBool(boolean);
2168
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
2169
+ {
2170
+ return bool_item;
2171
+ }
2172
+
2173
+ cJSON_Delete(bool_item);
2174
+ return NULL;
2175
+ }
2176
+
2177
+ CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
2178
+ {
2179
+ cJSON *number_item = cJSON_CreateNumber(number);
2180
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
2181
+ {
2182
+ return number_item;
2183
+ }
2184
+
2185
+ cJSON_Delete(number_item);
2186
+ return NULL;
2187
+ }
2188
+
2189
+ CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
2190
+ {
2191
+ cJSON *string_item = cJSON_CreateString(string);
2192
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
2193
+ {
2194
+ return string_item;
2195
+ }
2196
+
2197
+ cJSON_Delete(string_item);
2198
+ return NULL;
2199
+ }
2200
+
2201
+ CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
2202
+ {
2203
+ cJSON *raw_item = cJSON_CreateRaw(raw);
2204
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
2205
+ {
2206
+ return raw_item;
2207
+ }
2208
+
2209
+ cJSON_Delete(raw_item);
2210
+ return NULL;
2211
+ }
2212
+
2213
+ CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
2214
+ {
2215
+ cJSON *object_item = cJSON_CreateObject();
2216
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
2217
+ {
2218
+ return object_item;
2219
+ }
2220
+
2221
+ cJSON_Delete(object_item);
2222
+ return NULL;
2223
+ }
2224
+
2225
+ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
2226
+ {
2227
+ cJSON *array = cJSON_CreateArray();
2228
+ if (add_item_to_object(object, name, array, &global_hooks, false))
2229
+ {
2230
+ return array;
2231
+ }
2232
+
2233
+ cJSON_Delete(array);
2234
+ return NULL;
2235
+ }
2236
+
2237
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
2238
+ {
2239
+ if ((parent == NULL) || (item == NULL))
2240
+ {
2241
+ return NULL;
2242
+ }
2243
+
2244
+ if (item != parent->child)
2245
+ {
2246
+ /* not the first element */
2247
+ item->prev->next = item->next;
2248
+ }
2249
+ if (item->next != NULL)
2250
+ {
2251
+ /* not the last element */
2252
+ item->next->prev = item->prev;
2253
+ }
2254
+
2255
+ if (item == parent->child)
2256
+ {
2257
+ /* first element */
2258
+ parent->child = item->next;
2259
+ }
2260
+ else if (item->next == NULL)
2261
+ {
2262
+ /* last element */
2263
+ parent->child->prev = item->prev;
2264
+ }
2265
+
2266
+ /* make sure the detached item doesn't point anywhere anymore */
2267
+ item->prev = NULL;
2268
+ item->next = NULL;
2269
+
2270
+ return item;
2271
+ }
2272
+
2273
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
2274
+ {
2275
+ if (which < 0)
2276
+ {
2277
+ return NULL;
2278
+ }
2279
+
2280
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
2281
+ }
2282
+
2283
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
2284
+ {
2285
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
2286
+ }
2287
+
2288
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
2289
+ {
2290
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
2291
+
2292
+ return cJSON_DetachItemViaPointer(object, to_detach);
2293
+ }
2294
+
2295
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
2296
+ {
2297
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
2298
+
2299
+ return cJSON_DetachItemViaPointer(object, to_detach);
2300
+ }
2301
+
2302
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
2303
+ {
2304
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
2305
+ }
2306
+
2307
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
2308
+ {
2309
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
2310
+ }
2311
+
2312
+ /* Replace array/object items with new ones. */
2313
+ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
2314
+ {
2315
+ cJSON *after_inserted = NULL;
2316
+
2317
+ if (which < 0 || newitem == NULL)
2318
+ {
2319
+ return false;
2320
+ }
2321
+
2322
+ after_inserted = get_array_item(array, (size_t)which);
2323
+ if (after_inserted == NULL)
2324
+ {
2325
+ return add_item_to_array(array, newitem);
2326
+ }
2327
+
2328
+ if (after_inserted != array->child && after_inserted->prev == NULL) {
2329
+ /* return false if after_inserted is a corrupted array item */
2330
+ return false;
2331
+ }
2332
+
2333
+ newitem->next = after_inserted;
2334
+ newitem->prev = after_inserted->prev;
2335
+ after_inserted->prev = newitem;
2336
+ if (after_inserted == array->child)
2337
+ {
2338
+ array->child = newitem;
2339
+ }
2340
+ else
2341
+ {
2342
+ newitem->prev->next = newitem;
2343
+ }
2344
+ return true;
2345
+ }
2346
+
2347
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
2348
+ {
2349
+ if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
2350
+ {
2351
+ return false;
2352
+ }
2353
+
2354
+ if (replacement == item)
2355
+ {
2356
+ return true;
2357
+ }
2358
+
2359
+ replacement->next = item->next;
2360
+ replacement->prev = item->prev;
2361
+
2362
+ if (replacement->next != NULL)
2363
+ {
2364
+ replacement->next->prev = replacement;
2365
+ }
2366
+ if (parent->child == item)
2367
+ {
2368
+ if (parent->child->prev == parent->child)
2369
+ {
2370
+ replacement->prev = replacement;
2371
+ }
2372
+ parent->child = replacement;
2373
+ }
2374
+ else
2375
+ { /*
2376
+ * To find the last item in array quickly, we use prev in array.
2377
+ * We can't modify the last item's prev pointer from pointer to pointer conversion.
2378
+ */
2379
+ if (replacement->prev != NULL)
2380
+ {
2381
+ replacement->prev->next = replacement;
2382
+ }
2383
+ if (replacement->next == NULL)
2384
+ {
2385
+ parent->child->prev = replacement;
2386
+ }
2387
+ }
2388
+
2389
+ item->next = NULL;
2390
+ item->prev = NULL;
2391
+ cJSON_Delete(item);
2392
+
2393
+ return true;
2394
+ }
2395
+
2396
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
2397
+ {
2398
+ if (which < 0)
2399
+ {
2400
+ return false;
2401
+ }
2402
+
2403
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
2404
+ }
2405
+
2406
+ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
2407
+ {
2408
+ if ((replacement == NULL) || (string == NULL))
2409
+ {
2410
+ return false;
2411
+ }
2412
+
2413
+ /* replace the name in the replacement */
2414
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
2415
+ {
2416
+ cJSON_free(replacement->string);
2417
+ }
2418
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2419
+ if (replacement->string == NULL)
2420
+ {
2421
+ return false;
2422
+ }
2423
+
2424
+ replacement->type &= ~cJSON_StringIsConst;
2425
+
2426
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
2427
+ }
2428
+
2429
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
2430
+ {
2431
+ return replace_item_in_object(object, string, newitem, false);
2432
+ }
2433
+
2434
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
2435
+ {
2436
+ return replace_item_in_object(object, string, newitem, true);
2437
+ }
2438
+
2439
+ /* Create basic types: */
2440
+ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
2441
+ {
2442
+ cJSON *item = cJSON_New_Item(&global_hooks);
2443
+ if(item)
2444
+ {
2445
+ item->type = cJSON_NULL;
2446
+ }
2447
+
2448
+ return item;
2449
+ }
2450
+
2451
+ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
2452
+ {
2453
+ cJSON *item = cJSON_New_Item(&global_hooks);
2454
+ if(item)
2455
+ {
2456
+ item->type = cJSON_True;
2457
+ }
2458
+
2459
+ return item;
2460
+ }
2461
+
2462
+ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
2463
+ {
2464
+ cJSON *item = cJSON_New_Item(&global_hooks);
2465
+ if(item)
2466
+ {
2467
+ item->type = cJSON_False;
2468
+ }
2469
+
2470
+ return item;
2471
+ }
2472
+
2473
+ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
2474
+ {
2475
+ cJSON *item = cJSON_New_Item(&global_hooks);
2476
+ if(item)
2477
+ {
2478
+ item->type = boolean ? cJSON_True : cJSON_False;
2479
+ }
2480
+
2481
+ return item;
2482
+ }
2483
+
2484
+ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
2485
+ {
2486
+ cJSON *item = cJSON_New_Item(&global_hooks);
2487
+ if(item)
2488
+ {
2489
+ item->type = cJSON_Number;
2490
+ item->valuedouble = num;
2491
+
2492
+ /* use saturation in case of overflow */
2493
+ if (num >= INT_MAX)
2494
+ {
2495
+ item->valueint = INT_MAX;
2496
+ }
2497
+ else if (num <= (double)INT_MIN)
2498
+ {
2499
+ item->valueint = INT_MIN;
2500
+ }
2501
+ else
2502
+ {
2503
+ item->valueint = (int)num;
2504
+ }
2505
+ }
2506
+
2507
+ return item;
2508
+ }
2509
+
2510
+ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
2511
+ {
2512
+ cJSON *item = cJSON_New_Item(&global_hooks);
2513
+ if(item)
2514
+ {
2515
+ item->type = cJSON_String;
2516
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2517
+ if(!item->valuestring)
2518
+ {
2519
+ cJSON_Delete(item);
2520
+ return NULL;
2521
+ }
2522
+ }
2523
+
2524
+ return item;
2525
+ }
2526
+
2527
+ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
2528
+ {
2529
+ cJSON *item = cJSON_New_Item(&global_hooks);
2530
+ if (item != NULL)
2531
+ {
2532
+ item->type = cJSON_String | cJSON_IsReference;
2533
+ item->valuestring = (char*)cast_away_const(string);
2534
+ }
2535
+
2536
+ return item;
2537
+ }
2538
+
2539
+ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
2540
+ {
2541
+ cJSON *item = cJSON_New_Item(&global_hooks);
2542
+ if (item != NULL) {
2543
+ item->type = cJSON_Object | cJSON_IsReference;
2544
+ item->child = (cJSON*)cast_away_const(child);
2545
+ }
2546
+
2547
+ return item;
2548
+ }
2549
+
2550
+ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
2551
+ cJSON *item = cJSON_New_Item(&global_hooks);
2552
+ if (item != NULL) {
2553
+ item->type = cJSON_Array | cJSON_IsReference;
2554
+ item->child = (cJSON*)cast_away_const(child);
2555
+ }
2556
+
2557
+ return item;
2558
+ }
2559
+
2560
+ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
2561
+ {
2562
+ cJSON *item = cJSON_New_Item(&global_hooks);
2563
+ if(item)
2564
+ {
2565
+ item->type = cJSON_Raw;
2566
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
2567
+ if(!item->valuestring)
2568
+ {
2569
+ cJSON_Delete(item);
2570
+ return NULL;
2571
+ }
2572
+ }
2573
+
2574
+ return item;
2575
+ }
2576
+
2577
+ CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
2578
+ {
2579
+ cJSON *item = cJSON_New_Item(&global_hooks);
2580
+ if(item)
2581
+ {
2582
+ item->type=cJSON_Array;
2583
+ }
2584
+
2585
+ return item;
2586
+ }
2587
+
2588
+ CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
2589
+ {
2590
+ cJSON *item = cJSON_New_Item(&global_hooks);
2591
+ if(item)
2592
+ {
2593
+ item->type = cJSON_Object;
2594
+ }
2595
+
2596
+ return item;
2597
+ }
2598
+
2599
+ /* Create Arrays: */
2600
+ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
2601
+ {
2602
+ size_t i = 0;
2603
+ cJSON *n = NULL;
2604
+ cJSON *p = NULL;
2605
+ cJSON *a = NULL;
2606
+
2607
+ if ((count < 0) || (numbers == NULL))
2608
+ {
2609
+ return NULL;
2610
+ }
2611
+
2612
+ a = cJSON_CreateArray();
2613
+
2614
+ for(i = 0; a && (i < (size_t)count); i++)
2615
+ {
2616
+ n = cJSON_CreateNumber((double)numbers[i]);
2617
+ if (!n)
2618
+ {
2619
+ cJSON_Delete(a);
2620
+ return NULL;
2621
+ }
2622
+ if(!i)
2623
+ {
2624
+ a->child = n;
2625
+ }
2626
+ else
2627
+ {
2628
+ suffix_object(p, n);
2629
+ }
2630
+ p = n;
2631
+ }
2632
+
2633
+ if (a && a->child) {
2634
+ a->child->prev = n;
2635
+ }
2636
+
2637
+ return a;
2638
+ }
2639
+
2640
+ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
2641
+ {
2642
+ size_t i = 0;
2643
+ cJSON *n = NULL;
2644
+ cJSON *p = NULL;
2645
+ cJSON *a = NULL;
2646
+
2647
+ if ((count < 0) || (numbers == NULL))
2648
+ {
2649
+ return NULL;
2650
+ }
2651
+
2652
+ a = cJSON_CreateArray();
2653
+
2654
+ for(i = 0; a && (i < (size_t)count); i++)
2655
+ {
2656
+ n = cJSON_CreateNumber((double)numbers[i]);
2657
+ if(!n)
2658
+ {
2659
+ cJSON_Delete(a);
2660
+ return NULL;
2661
+ }
2662
+ if(!i)
2663
+ {
2664
+ a->child = n;
2665
+ }
2666
+ else
2667
+ {
2668
+ suffix_object(p, n);
2669
+ }
2670
+ p = n;
2671
+ }
2672
+
2673
+ if (a && a->child) {
2674
+ a->child->prev = n;
2675
+ }
2676
+
2677
+ return a;
2678
+ }
2679
+
2680
+ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
2681
+ {
2682
+ size_t i = 0;
2683
+ cJSON *n = NULL;
2684
+ cJSON *p = NULL;
2685
+ cJSON *a = NULL;
2686
+
2687
+ if ((count < 0) || (numbers == NULL))
2688
+ {
2689
+ return NULL;
2690
+ }
2691
+
2692
+ a = cJSON_CreateArray();
2693
+
2694
+ for(i = 0; a && (i < (size_t)count); i++)
2695
+ {
2696
+ n = cJSON_CreateNumber(numbers[i]);
2697
+ if(!n)
2698
+ {
2699
+ cJSON_Delete(a);
2700
+ return NULL;
2701
+ }
2702
+ if(!i)
2703
+ {
2704
+ a->child = n;
2705
+ }
2706
+ else
2707
+ {
2708
+ suffix_object(p, n);
2709
+ }
2710
+ p = n;
2711
+ }
2712
+
2713
+ if (a && a->child) {
2714
+ a->child->prev = n;
2715
+ }
2716
+
2717
+ return a;
2718
+ }
2719
+
2720
+ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
2721
+ {
2722
+ size_t i = 0;
2723
+ cJSON *n = NULL;
2724
+ cJSON *p = NULL;
2725
+ cJSON *a = NULL;
2726
+
2727
+ if ((count < 0) || (strings == NULL))
2728
+ {
2729
+ return NULL;
2730
+ }
2731
+
2732
+ a = cJSON_CreateArray();
2733
+
2734
+ for(i = 0; a && (i < (size_t)count); i++)
2735
+ {
2736
+ n = cJSON_CreateString(strings[i]);
2737
+ if(!n)
2738
+ {
2739
+ cJSON_Delete(a);
2740
+ return NULL;
2741
+ }
2742
+ if(!i)
2743
+ {
2744
+ a->child = n;
2745
+ }
2746
+ else
2747
+ {
2748
+ suffix_object(p, n);
2749
+ }
2750
+ p = n;
2751
+ }
2752
+
2753
+ if (a && a->child) {
2754
+ a->child->prev = n;
2755
+ }
2756
+
2757
+ return a;
2758
+ }
2759
+
2760
+ /* Duplication */
2761
+ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
2762
+ {
2763
+ cJSON *newitem = NULL;
2764
+ cJSON *child = NULL;
2765
+ cJSON *next = NULL;
2766
+ cJSON *newchild = NULL;
2767
+
2768
+ /* Bail on bad ptr */
2769
+ if (!item)
2770
+ {
2771
+ goto fail;
2772
+ }
2773
+ /* Create new item */
2774
+ newitem = cJSON_New_Item(&global_hooks);
2775
+ if (!newitem)
2776
+ {
2777
+ goto fail;
2778
+ }
2779
+ /* Copy over all vars */
2780
+ newitem->type = item->type & (~cJSON_IsReference);
2781
+ newitem->valueint = item->valueint;
2782
+ newitem->valuedouble = item->valuedouble;
2783
+ if (item->valuestring)
2784
+ {
2785
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
2786
+ if (!newitem->valuestring)
2787
+ {
2788
+ goto fail;
2789
+ }
2790
+ }
2791
+ if (item->string)
2792
+ {
2793
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
2794
+ if (!newitem->string)
2795
+ {
2796
+ goto fail;
2797
+ }
2798
+ }
2799
+ /* If non recurse, then we're done! */
2800
+ if (!recurse)
2801
+ {
2802
+ return newitem;
2803
+ }
2804
+ /* Walk the ->next chain for the child. */
2805
+ child = item->child;
2806
+ while (child != NULL)
2807
+ {
2808
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the chain */
2809
+ if (!newchild)
2810
+ {
2811
+ goto fail;
2812
+ }
2813
+ if (next != NULL)
2814
+ {
2815
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
2816
+ next->next = newchild;
2817
+ newchild->prev = next;
2818
+ next = newchild;
2819
+ }
2820
+ else
2821
+ {
2822
+ /* Set newitem->child and move to it */
2823
+ newitem->child = newchild;
2824
+ next = newchild;
2825
+ }
2826
+ child = child->next;
2827
+ }
2828
+ if (newitem && newitem->child)
2829
+ {
2830
+ newitem->child->prev = newchild;
2831
+ }
2832
+
2833
+ return newitem;
2834
+
2835
+ fail:
2836
+ if (newitem != NULL)
2837
+ {
2838
+ cJSON_Delete(newitem);
2839
+ }
2840
+
2841
+ return NULL;
2842
+ }
2843
+
2844
+ static void skip_oneline_comment(char **input)
2845
+ {
2846
+ *input += static_strlen("//");
2847
+
2848
+ for (; (*input)[0] != '\0'; ++(*input))
2849
+ {
2850
+ if ((*input)[0] == '\n') {
2851
+ *input += static_strlen("\n");
2852
+ return;
2853
+ }
2854
+ }
2855
+ }
2856
+
2857
+ static void skip_multiline_comment(char **input)
2858
+ {
2859
+ *input += static_strlen("/*");
2860
+
2861
+ for (; (*input)[0] != '\0'; ++(*input))
2862
+ {
2863
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
2864
+ {
2865
+ *input += static_strlen("*/");
2866
+ return;
2867
+ }
2868
+ }
2869
+ }
2870
+
2871
+ static void minify_string(char **input, char **output) {
2872
+ (*output)[0] = (*input)[0];
2873
+ *input += static_strlen("\"");
2874
+ *output += static_strlen("\"");
2875
+
2876
+
2877
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
2878
+ (*output)[0] = (*input)[0];
2879
+
2880
+ if ((*input)[0] == '\"') {
2881
+ (*output)[0] = '\"';
2882
+ *input += static_strlen("\"");
2883
+ *output += static_strlen("\"");
2884
+ return;
2885
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
2886
+ (*output)[1] = (*input)[1];
2887
+ *input += static_strlen("\"");
2888
+ *output += static_strlen("\"");
2889
+ }
2890
+ }
2891
+ }
2892
+
2893
+ CJSON_PUBLIC(void) cJSON_Minify(char *json)
2894
+ {
2895
+ char *into = json;
2896
+
2897
+ if (json == NULL)
2898
+ {
2899
+ return;
2900
+ }
2901
+
2902
+ while (json[0] != '\0')
2903
+ {
2904
+ switch (json[0])
2905
+ {
2906
+ case ' ':
2907
+ case '\t':
2908
+ case '\r':
2909
+ case '\n':
2910
+ json++;
2911
+ break;
2912
+
2913
+ case '/':
2914
+ if (json[1] == '/')
2915
+ {
2916
+ skip_oneline_comment(&json);
2917
+ }
2918
+ else if (json[1] == '*')
2919
+ {
2920
+ skip_multiline_comment(&json);
2921
+ } else {
2922
+ json++;
2923
+ }
2924
+ break;
2925
+
2926
+ case '\"':
2927
+ minify_string(&json, (char**)&into);
2928
+ break;
2929
+
2930
+ default:
2931
+ into[0] = json[0];
2932
+ json++;
2933
+ into++;
2934
+ }
2935
+ }
2936
+
2937
+ /* and null-terminate. */
2938
+ *into = '\0';
2939
+ }
2940
+
2941
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
2942
+ {
2943
+ if (item == NULL)
2944
+ {
2945
+ return false;
2946
+ }
2947
+
2948
+ return (item->type & 0xFF) == cJSON_Invalid;
2949
+ }
2950
+
2951
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
2952
+ {
2953
+ if (item == NULL)
2954
+ {
2955
+ return false;
2956
+ }
2957
+
2958
+ return (item->type & 0xFF) == cJSON_False;
2959
+ }
2960
+
2961
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
2962
+ {
2963
+ if (item == NULL)
2964
+ {
2965
+ return false;
2966
+ }
2967
+
2968
+ return (item->type & 0xff) == cJSON_True;
2969
+ }
2970
+
2971
+
2972
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
2973
+ {
2974
+ if (item == NULL)
2975
+ {
2976
+ return false;
2977
+ }
2978
+
2979
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
2980
+ }
2981
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
2982
+ {
2983
+ if (item == NULL)
2984
+ {
2985
+ return false;
2986
+ }
2987
+
2988
+ return (item->type & 0xFF) == cJSON_NULL;
2989
+ }
2990
+
2991
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
2992
+ {
2993
+ if (item == NULL)
2994
+ {
2995
+ return false;
2996
+ }
2997
+
2998
+ return (item->type & 0xFF) == cJSON_Number;
2999
+ }
3000
+
3001
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
3002
+ {
3003
+ if (item == NULL)
3004
+ {
3005
+ return false;
3006
+ }
3007
+
3008
+ return (item->type & 0xFF) == cJSON_String;
3009
+ }
3010
+
3011
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
3012
+ {
3013
+ if (item == NULL)
3014
+ {
3015
+ return false;
3016
+ }
3017
+
3018
+ return (item->type & 0xFF) == cJSON_Array;
3019
+ }
3020
+
3021
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
3022
+ {
3023
+ if (item == NULL)
3024
+ {
3025
+ return false;
3026
+ }
3027
+
3028
+ return (item->type & 0xFF) == cJSON_Object;
3029
+ }
3030
+
3031
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
3032
+ {
3033
+ if (item == NULL)
3034
+ {
3035
+ return false;
3036
+ }
3037
+
3038
+ return (item->type & 0xFF) == cJSON_Raw;
3039
+ }
3040
+
3041
+ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
3042
+ {
3043
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
3044
+ {
3045
+ return false;
3046
+ }
3047
+
3048
+ /* check if type is valid */
3049
+ switch (a->type & 0xFF)
3050
+ {
3051
+ case cJSON_False:
3052
+ case cJSON_True:
3053
+ case cJSON_NULL:
3054
+ case cJSON_Number:
3055
+ case cJSON_String:
3056
+ case cJSON_Raw:
3057
+ case cJSON_Array:
3058
+ case cJSON_Object:
3059
+ break;
3060
+
3061
+ default:
3062
+ return false;
3063
+ }
3064
+
3065
+ /* identical objects are equal */
3066
+ if (a == b)
3067
+ {
3068
+ return true;
3069
+ }
3070
+
3071
+ switch (a->type & 0xFF)
3072
+ {
3073
+ /* in these cases and equal type is enough */
3074
+ case cJSON_False:
3075
+ case cJSON_True:
3076
+ case cJSON_NULL:
3077
+ return true;
3078
+
3079
+ case cJSON_Number:
3080
+ if (compare_double(a->valuedouble, b->valuedouble))
3081
+ {
3082
+ return true;
3083
+ }
3084
+ return false;
3085
+
3086
+ case cJSON_String:
3087
+ case cJSON_Raw:
3088
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
3089
+ {
3090
+ return false;
3091
+ }
3092
+ if (strcmp(a->valuestring, b->valuestring) == 0)
3093
+ {
3094
+ return true;
3095
+ }
3096
+
3097
+ return false;
3098
+
3099
+ case cJSON_Array:
3100
+ {
3101
+ cJSON *a_element = a->child;
3102
+ cJSON *b_element = b->child;
3103
+
3104
+ for (; (a_element != NULL) && (b_element != NULL);)
3105
+ {
3106
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
3107
+ {
3108
+ return false;
3109
+ }
3110
+
3111
+ a_element = a_element->next;
3112
+ b_element = b_element->next;
3113
+ }
3114
+
3115
+ /* one of the arrays is longer than the other */
3116
+ if (a_element != b_element) {
3117
+ return false;
3118
+ }
3119
+
3120
+ return true;
3121
+ }
3122
+
3123
+ case cJSON_Object:
3124
+ {
3125
+ cJSON *a_element = NULL;
3126
+ cJSON *b_element = NULL;
3127
+ cJSON_ArrayForEach(a_element, a)
3128
+ {
3129
+ /* TODO This has O(n^2) runtime, which is horrible! */
3130
+ b_element = get_object_item(b, a_element->string, case_sensitive);
3131
+ if (b_element == NULL)
3132
+ {
3133
+ return false;
3134
+ }
3135
+
3136
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
3137
+ {
3138
+ return false;
3139
+ }
3140
+ }
3141
+
3142
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
3143
+ * TODO: Do this the proper way. */
3144
+ cJSON_ArrayForEach(b_element, b)
3145
+ {
3146
+ a_element = get_object_item(a, b_element->string, case_sensitive);
3147
+ if (a_element == NULL)
3148
+ {
3149
+ return false;
3150
+ }
3151
+
3152
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
3153
+ {
3154
+ return false;
3155
+ }
3156
+ }
3157
+
3158
+ return true;
3159
+ }
3160
+
3161
+ default:
3162
+ return false;
3163
+ }
3164
+ }
3165
+
3166
+ CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
3167
+ {
3168
+ return global_hooks.allocate(size);
3169
+ }
3170
+
3171
+ CJSON_PUBLIC(void) cJSON_free(void *object)
3172
+ {
3173
+ global_hooks.deallocate(object);
3174
+ }
cJSON.h ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
27
+ extern "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
+
39
+ CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
40
+ CJSON_EXPORT_SYMBOLS - Define this on build when you want to dllexport symbols (default)
41
+ CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
42
+
43
+ For *nix builds that support visibility attribute, you can define similar behavior by setting default visibility to hidden by adding
44
+ -fvisibility=hidden (for gcc) or -xldscope=hidden (for sun cc) to CFLAGS
45
+ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
46
+
47
+ */
48
+
49
+ #define CJSON_CDECL __cdecl
50
+ #define CJSON_STDCALL __stdcall
51
+
52
+ /* export symbols by default, this is necessary for copy pasting the C and header file */
53
+ #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
54
+ #define CJSON_EXPORT_SYMBOLS
55
+ #endif
56
+
57
+ #if defined(CJSON_HIDE_SYMBOLS)
58
+ #define CJSON_PUBLIC(type) type CJSON_STDCALL
59
+ #elif defined(CJSON_EXPORT_SYMBOLS)
60
+ #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
61
+ #elif defined(CJSON_IMPORT_SYMBOLS)
62
+ #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
63
+ #endif
64
+ #else /* !__WINDOWS__ */
65
+ #define CJSON_CDECL
66
+ #define CJSON_STDCALL
67
+
68
+ #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
69
+ #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
70
+ #else
71
+ #define CJSON_PUBLIC(type) type
72
+ #endif
73
+ #endif
74
+
75
+ /* project version */
76
+ #define CJSON_VERSION_MAJOR 1
77
+ #define CJSON_VERSION_MINOR 7
78
+ #define CJSON_VERSION_PATCH 18
79
+
80
+ #include <stddef.h>
81
+
82
+ /* cJSON Types: */
83
+ #define cJSON_Invalid (0)
84
+ #define cJSON_False (1 << 0)
85
+ #define cJSON_True (1 << 1)
86
+ #define cJSON_NULL (1 << 2)
87
+ #define cJSON_Number (1 << 3)
88
+ #define cJSON_String (1 << 4)
89
+ #define cJSON_Array (1 << 5)
90
+ #define cJSON_Object (1 << 6)
91
+ #define cJSON_Raw (1 << 7) /* raw json */
92
+
93
+ #define cJSON_IsReference 256
94
+ #define cJSON_StringIsConst 512
95
+
96
+ /* The cJSON structure: */
97
+ typedef struct cJSON
98
+ {
99
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
100
+ struct cJSON *next;
101
+ struct cJSON *prev;
102
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
103
+ struct cJSON *child;
104
+
105
+ /* The type of the item, as above. */
106
+ int type;
107
+
108
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
109
+ char *valuestring;
110
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
111
+ int valueint;
112
+ /* The item's number, if type==cJSON_Number */
113
+ double valuedouble;
114
+
115
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
116
+ char *string;
117
+ } cJSON;
118
+
119
+ typedef struct cJSON_Hooks
120
+ {
121
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
122
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
123
+ void (CJSON_CDECL *free_fn)(void *ptr);
124
+ } cJSON_Hooks;
125
+
126
+ typedef int cJSON_bool;
127
+
128
+ /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
129
+ * This is to prevent stack overflows. */
130
+ #ifndef CJSON_NESTING_LIMIT
131
+ #define CJSON_NESTING_LIMIT 1000
132
+ #endif
133
+
134
+ /* returns the version of cJSON as a string */
135
+ CJSON_PUBLIC(const char*) cJSON_Version(void);
136
+
137
+ /* Supply malloc, realloc and free functions to cJSON */
138
+ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
139
+
140
+ /* 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. */
141
+ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
142
+ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
143
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
144
+ /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
145
+ /* 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(). */
146
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
147
+ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
148
+
149
+ /* Render a cJSON entity to text for transfer/storage. */
150
+ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
151
+ /* Render a cJSON entity to text for transfer/storage without any formatting. */
152
+ CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
153
+ /* 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 */
154
+ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
155
+ /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
156
+ /* 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 */
157
+ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
158
+ /* Delete a cJSON entity and all subentities. */
159
+ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
160
+
161
+ /* Returns the number of items in an array (or object). */
162
+ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
163
+ /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
164
+ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
165
+ /* Get item "string" from object. Case insensitive. */
166
+ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
167
+ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
168
+ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
169
+ /* 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 no error. */
170
+ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
171
+
172
+ /* Check item type */
173
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
174
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
175
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
176
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
177
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
178
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
179
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
180
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
181
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
182
+ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
183
+
184
+ /* These calls create a cJSON item of the appropriate type. */
185
+ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
186
+ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
187
+ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
188
+ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
189
+ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
190
+ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
191
+ /* raw json */
192
+ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
193
+ CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
194
+ CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
195
+
196
+ /* Create a string where valuestring references a string so
197
+ * it will not be freed by cJSON_Delete */
198
+ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
199
+ /* Create an object/array that only references it's elements so
200
+ * they will not be freed by cJSON_Delete */
201
+ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
202
+ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
203
+
204
+ /* These utilities create an Array of count items.
205
+ * The parameter count cannot be greater than the number of elements in the number list, otherwise array access will be out of bounds.*/
206
+ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
207
+ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
208
+ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
209
+ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
210
+
211
+ /* Append item to the specified array/object. */
212
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
213
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
214
+ /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
215
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
216
+ * writing to `item->string` */
217
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
218
+ /* 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 item. */
219
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
220
+ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
221
+
222
+ /* Remove/Detach items from Arrays/Objects. */
223
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
224
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
225
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
226
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
227
+ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
228
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
229
+ CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
230
+
231
+ /* Update array items. */
232
+ CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
233
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
234
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
235
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
236
+ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem);
237
+
238
+ /* Duplicate a cJSON item */
239
+ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
240
+ /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
241
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
242
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
243
+ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
244
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
245
+ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
246
+
247
+ /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
248
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
249
+ * but should point to a readable and writable address area. */
250
+ CJSON_PUBLIC(void) cJSON_Minify(char *json);
251
+
252
+ /* Helper functions for creating and adding items to an object at the same time.
253
+ * They return the added item or NULL on failure. */
254
+ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
255
+ CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
256
+ CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
257
+ CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
258
+ CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
259
+ CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
260
+ CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
261
+ CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
262
+ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
263
+
264
+ /* When assigning an integer value, it needs to be propagated to valuedouble too. */
265
+ #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
266
+ /* helper for the cJSON_SetNumberValue macro */
267
+ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
268
+ #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
269
+ /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
270
+ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
271
+
272
+ /* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
273
+ #define cJSON_SetBoolValue(object, boolValue) ( \
274
+ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
275
+ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
276
+ cJSON_Invalid\
277
+ )
278
+
279
+ /* Macro for iterating over an array or object */
280
+ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
281
+
282
+ /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
283
+ CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
284
+ CJSON_PUBLIC(void) cJSON_free(void *object);
285
+
286
+ #ifdef __cplusplus
287
+ }
288
+ #endif
289
+
290
+ #endif
interpreter.c ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "interpreter.h"
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+ #include <ctype.h>
6
+ #include <stdint.h>
7
+
8
+ /* Global sync request flag (set by commands that modify state) */
9
+ static int sync_requested = 0;
10
+
11
+ void interpreter_request_sync(void) {
12
+ sync_requested = 1;
13
+ }
14
+
15
+ /* Command table – add your own commands here */
16
+ static Command commands[] = {
17
+ { "boot", boot_disk },
18
+ { "diag", run_diagnostic },
19
+ { "status", get_status },
20
+ { "replan", cmd_replan }, /* new command */
21
+ { NULL, NULL } /* sentinel */
22
+ };
23
+
24
+ /* ------------------------------------------------------------------
25
+ * Dynamic string builder (internal use)
26
+ * ------------------------------------------------------------------ */
27
+ typedef struct {
28
+ char *str;
29
+ size_t len;
30
+ size_t cap;
31
+ } string_builder;
32
+
33
+ static void sb_init(string_builder *sb) {
34
+ sb->str = NULL;
35
+ sb->len = 0;
36
+ sb->cap = 0;
37
+ }
38
+
39
+ static int sb_append_char(string_builder *sb, char c) {
40
+ if (sb->len + 2 > sb->cap) {
41
+ size_t new_cap = sb->cap ? sb->cap * 2 : 32;
42
+ char *new_str = realloc(sb->str, new_cap);
43
+ if (!new_str) return -1;
44
+ sb->str = new_str;
45
+ sb->cap = new_cap;
46
+ }
47
+ sb->str[sb->len++] = c;
48
+ sb->str[sb->len] = '\0';
49
+ return 0;
50
+ }
51
+
52
+ static int sb_append_str(string_builder *sb, const char *s) {
53
+ while (*s) {
54
+ if (sb_append_char(sb, *s++) < 0) return -1;
55
+ }
56
+ return 0;
57
+ }
58
+
59
+ static void sb_free(string_builder *sb) {
60
+ free(sb->str);
61
+ sb->str = NULL;
62
+ sb->len = sb->cap = 0;
63
+ }
64
+
65
+ /* ------------------------------------------------------------------
66
+ * Split quoted string into argv (handles escaped quotes and backslashes)
67
+ * Returns NULL on error (memory or unterminated quote).
68
+ * Caller must free each argv[i] and then free argv.
69
+ * ------------------------------------------------------------------ */
70
+ static char** split_quoted(const char* input, int* argc) {
71
+ char **argv = NULL;
72
+ int capacity = 0;
73
+ *argc = 0;
74
+ const char *p = input;
75
+
76
+ while (*p) {
77
+ while (isspace(*p)) p++;
78
+ if (!*p) break;
79
+
80
+ if (*argc >= capacity) {
81
+ capacity = capacity ? capacity * 2 : 8;
82
+ char **new_argv = realloc(argv, capacity * sizeof(char*));
83
+ if (!new_argv) goto error;
84
+ argv = new_argv;
85
+ }
86
+
87
+ string_builder sb;
88
+ sb_init(&sb);
89
+ char quote = 0;
90
+
91
+ if (*p == '"' || *p == '\'') {
92
+ quote = *p++;
93
+ }
94
+
95
+ int escaped = 0;
96
+ while (*p) {
97
+ if (escaped) {
98
+ if (sb_append_char(&sb, *p++) < 0) goto error;
99
+ escaped = 0;
100
+ } else if (*p == '\\') {
101
+ escaped = 1;
102
+ p++;
103
+ } else if (quote && *p == quote) {
104
+ p++; /* closing quote */
105
+ break;
106
+ } else if (!quote && isspace(*p)) {
107
+ break;
108
+ } else {
109
+ if (sb_append_char(&sb, *p++) < 0) goto error;
110
+ }
111
+ }
112
+
113
+ if (quote && !*p) { /* unterminated quote */
114
+ sb_free(&sb);
115
+ goto error;
116
+ }
117
+
118
+ argv[(*argc)++] = sb.str; /* sb.str is now owned by argv */
119
+ }
120
+
121
+ return argv;
122
+
123
+ error:
124
+ for (int i = 0; i < *argc; i++) free(argv[i]);
125
+ free(argv);
126
+ return NULL;
127
+ }
128
+
129
+ /* ------------------------------------------------------------------
130
+ * Main interpreter
131
+ * ------------------------------------------------------------------ */
132
+ char* interpret_command(const char* input) {
133
+ if (!input || *input == '\0')
134
+ return strdup("No input provided.");
135
+
136
+ int argc = 0;
137
+ char **argv = split_quoted(input, &argc);
138
+ if (!argv) return strdup("Memory error or unterminated quote.");
139
+
140
+ if (argc == 0) {
141
+ free(argv);
142
+ return strdup("");
143
+ }
144
+
145
+ char *result = NULL;
146
+ for (Command *c = commands; c->name; c++) {
147
+ if (strcmp(c->name, argv[0]) == 0) {
148
+ /* Build argument string safely */
149
+ string_builder sb;
150
+ sb_init(&sb);
151
+ for (int i = 1; i < argc; i++) {
152
+ if (i > 1) {
153
+ if (sb_append_char(&sb, ' ') < 0) {
154
+ sb_free(&sb);
155
+ for (int j = 0; j < argc; j++) free(argv[j]);
156
+ free(argv);
157
+ return strdup("Memory error");
158
+ }
159
+ }
160
+ if (sb_append_str(&sb, argv[i]) < 0) {
161
+ sb_free(&sb);
162
+ for (int j = 0; j < argc; j++) free(argv[j]);
163
+ free(argv);
164
+ return strdup("Memory error");
165
+ }
166
+ }
167
+ result = c->fn(sb.str ? sb.str : "");
168
+ sb_free(&sb);
169
+ break;
170
+ }
171
+ }
172
+
173
+ for (int i = 0; i < argc; i++) free(argv[i]);
174
+ free(argv);
175
+
176
+ if (!result) result = strdup("Unknown command.");
177
+ return result;
178
+ }
179
+
180
+ /* ------------------------------------------------------------------
181
+ * Example command implementations
182
+ * ------------------------------------------------------------------ */
183
+ char* boot_disk(const char* args) {
184
+ size_t len = strlen("Booting disk with args: ") + strlen(args) + 1;
185
+ char *buf = malloc(len);
186
+ if (!buf) return strdup("Memory error");
187
+ snprintf(buf, len, "Booting disk with args: %s", args);
188
+ return buf;
189
+ }
190
+
191
+ char* run_diagnostic(const char* args) {
192
+ (void)args;
193
+ return strdup("Running diagnostic...");
194
+ }
195
+
196
+ char* get_status(const char* args) {
197
+ (void)args;
198
+ return strdup("Status: All systems nominal.");
199
+ }
200
+
201
+ /* ------------------------------------------------------------------
202
+ * New command: /replan – forces a fresh LLM call
203
+ * Returns a special marker that the main loop will recognize.
204
+ * ------------------------------------------------------------------ */
205
+ char* cmd_replan(const char* args) {
206
+ (void)args;
207
+ /* Request a soul sync (though this command doesn't modify state) */
208
+ interpreter_request_sync();
209
+ /* Return the replan marker */
210
+ char *marker = malloc(strlen(INTERPRETER_MARKER_REPLAN) + 1);
211
+ if (!marker) return strdup("Memory error");
212
+ strcpy(marker, INTERPRETER_MARKER_REPLAN);
213
+ return marker;
214
+ }
interpreter.h ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ifndef INTERPRETER_H
2
+ #define INTERPRETER_H
3
+
4
+ /* Special return value prefixes (for communication with main loop) */
5
+ #define INTERPRETER_MARKER_REPLAN "REPLAN"
6
+ #define INTERPRETER_MARKER_TOOL_RESULT "TOOL_RESULT:"
7
+
8
+ /* Command handler signature:
9
+ * Takes arguments string, returns malloc'd response.
10
+ * Caller is responsible for freeing the returned string.
11
+ * If the returned string starts with one of the INTERPRETER_MARKER_*
12
+ * prefixes, the main loop will handle it specially.
13
+ */
14
+ typedef char* (*CommandFn)(const char* args);
15
+
16
+ /* Registered command entry */
17
+ typedef struct {
18
+ const char* name;
19
+ CommandFn fn;
20
+ } Command;
21
+
22
+ /* Main interpreter entry point.
23
+ * Input: user command line (may contain quoted arguments).
24
+ * Returns: malloc'd result string (caller must free).
25
+ */
26
+ char* interpret_command(const char* input);
27
+
28
+ /* Call this after executing any command that modifies persistent state
29
+ * (e.g., adding/removing skills, cron jobs, webhooks, core memory).
30
+ * The main loop will then sync the soul file.
31
+ */
32
+ void interpreter_request_sync(void);
33
+
34
+ /* Example command implementations – replace with real logic */
35
+ char* boot_disk(const char* args);
36
+ char* run_diagnostic(const char* args);
37
+ char* get_status(const char* args);
38
+ char* cmd_replan(const char* args); /* new: forces a fresh LLM call */
39
+
40
+ #endif /* INTERPRETER_H */
shadowclaw.c ADDED
@@ -0,0 +1,1383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #define _GNU_SOURCE
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <stdint.h>
5
+ #include <string.h>
6
+ #include <stdbool.h>
7
+ #include <unistd.h>
8
+ #include <time.h>
9
+ #include <curl/curl.h>
10
+ #include <dirent.h>
11
+ #include <errno.h>
12
+ #include <signal.h>
13
+ #include <sys/stat.h>
14
+ #include <limits.h>
15
+ #include <ctype.h>
16
+ #include "cJSON.h"
17
+ #include "interpreter.h"
18
+
19
+ // ------------------------------------------------------------------
20
+ // Version & Colors
21
+ // ------------------------------------------------------------------
22
+ #define SHADOWCLAW_VERSION "3.4"
23
+ #define COLOR_RESET "\033[0m"
24
+ #define COLOR_RED "\033[31m"
25
+ #define COLOR_GREEN "\033[32m"
26
+ #define COLOR_YELLOW "\033[33m"
27
+ #define COLOR_BLUE "\033[34m"
28
+ #define COLOR_CYAN "\033[36m"
29
+ #define COLOR_BOLD "\033[1m"
30
+
31
+ // ------------------------------------------------------------------
32
+ // Blob Storage (linked list, no fragmentation)
33
+ // ------------------------------------------------------------------
34
+ typedef enum {
35
+ BLOB_SYSTEM = 1,
36
+ BLOB_USER,
37
+ BLOB_ASSISTANT,
38
+ BLOB_TOOL_CALL,
39
+ BLOB_TOOL_RESULT,
40
+ BLOB_CORE_MEMORY,
41
+ BLOB_SKILL,
42
+ BLOB_CRONJOB,
43
+ BLOB_WEBHOOK
44
+ } BlobKind;
45
+
46
+ typedef struct Blob {
47
+ uint32_t kind;
48
+ uint64_t id;
49
+ size_t size;
50
+ char *data;
51
+ struct Blob *next;
52
+ } Blob;
53
+
54
+ static Blob *blob_list = NULL;
55
+ static uint64_t next_id = 1;
56
+ static int dirty = 0;
57
+
58
+ void blob_append(BlobKind kind, const char *data) {
59
+ Blob *b = malloc(sizeof(Blob));
60
+ if (!b) abort();
61
+ b->kind = kind;
62
+ b->id = next_id++;
63
+ b->size = strlen(data);
64
+ b->data = malloc(b->size + 1);
65
+ if (!b->data) abort();
66
+ strcpy(b->data, data);
67
+ b->next = blob_list;
68
+ blob_list = b;
69
+ dirty = 1;
70
+ }
71
+
72
+ void blob_foreach_reverse(void (*cb)(Blob*, void*), void *user) {
73
+ // reverse list to get chronological order
74
+ Blob *prev = NULL, *curr = blob_list, *next;
75
+ while (curr) {
76
+ next = curr->next;
77
+ curr->next = prev;
78
+ prev = curr;
79
+ curr = next;
80
+ }
81
+ blob_list = prev;
82
+ curr = blob_list;
83
+ while (curr) {
84
+ cb(curr, user);
85
+ curr = curr->next;
86
+ }
87
+ // restore original order
88
+ prev = NULL;
89
+ curr = blob_list;
90
+ while (curr) {
91
+ next = curr->next;
92
+ curr->next = prev;
93
+ prev = curr;
94
+ curr = next;
95
+ }
96
+ blob_list = prev;
97
+ }
98
+
99
+ void blob_clear(void) {
100
+ Blob *b = blob_list;
101
+ while (b) {
102
+ Blob *next = b->next;
103
+ free(b->data);
104
+ free(b);
105
+ b = next;
106
+ }
107
+ blob_list = NULL;
108
+ next_id = 1;
109
+ dirty = 1;
110
+ }
111
+
112
+ int save_state(const char *filename) {
113
+ FILE *f = fopen(filename, "wb");
114
+ if (!f) return -1;
115
+ uint32_t magic = 0x53484C42; // "SHLB"
116
+ uint32_t version = 1;
117
+ fwrite(&magic, 4, 1, f);
118
+ fwrite(&version, 4, 1, f);
119
+ fwrite(&next_id, 8, 1, f);
120
+ // Write blobs in order (oldest first)
121
+ Blob *prev = NULL, *curr = blob_list, *next;
122
+ while (curr) {
123
+ next = curr->next;
124
+ curr->next = prev;
125
+ prev = curr;
126
+ curr = next;
127
+ }
128
+ blob_list = prev;
129
+ curr = blob_list;
130
+ while (curr) {
131
+ fwrite(&curr->kind, 4, 1, f);
132
+ fwrite(&curr->id, 8, 1, f);
133
+ fwrite(&curr->size, 8, 1, f);
134
+ fwrite(curr->data, 1, curr->size + 1, f);
135
+ curr = curr->next;
136
+ }
137
+ // restore
138
+ prev = NULL;
139
+ curr = blob_list;
140
+ while (curr) {
141
+ next = curr->next;
142
+ curr->next = prev;
143
+ prev = curr;
144
+ curr = next;
145
+ }
146
+ blob_list = prev;
147
+ fclose(f);
148
+ dirty = 0;
149
+ return 0;
150
+ }
151
+
152
+ int load_state(const char *filename) {
153
+ FILE *f = fopen(filename, "rb");
154
+ if (!f) return -1;
155
+ uint32_t magic, version;
156
+ if (fread(&magic, 4, 1, f) != 1 || magic != 0x53484C42) {
157
+ fclose(f);
158
+ return -1;
159
+ }
160
+ if (fread(&version, 4, 1, f) != 1 || version != 1) {
161
+ fclose(f);
162
+ return -1;
163
+ }
164
+ if (fread(&next_id, 8, 1, f) != 1) {
165
+ fclose(f);
166
+ return -1;
167
+ }
168
+ blob_clear();
169
+ while (!feof(f)) {
170
+ uint32_t kind;
171
+ uint64_t id;
172
+ size_t size;
173
+ if (fread(&kind, 4, 1, f) != 1) break;
174
+ if (fread(&id, 8, 1, f) != 1) break;
175
+ if (fread(&size, 8, 1, f) != 1) break;
176
+ char *data = malloc(size + 1);
177
+ if (!data) { fclose(f); return -1; }
178
+ if (fread(data, 1, size + 1, f) != size + 1) {
179
+ free(data);
180
+ fclose(f);
181
+ return -1;
182
+ }
183
+ Blob *b = malloc(sizeof(Blob));
184
+ if (!b) { free(data); fclose(f); return -1; }
185
+ b->kind = kind;
186
+ b->id = id;
187
+ b->size = size;
188
+ b->data = data;
189
+ b->next = blob_list;
190
+ blob_list = b;
191
+ }
192
+ fclose(f);
193
+ dirty = 0;
194
+ return 0;
195
+ }
196
+
197
+ void sync_soul_file(const char *soul_file) {
198
+ FILE *f = fopen(soul_file, "w");
199
+ if (!f) return;
200
+ fprintf(f, "# ShadowSoul - Unified Memory\n");
201
+ time_t now = time(NULL);
202
+ fprintf(f, "Last updated: %s", ctime(&now));
203
+ fprintf(f, "\n## Core Memory\n");
204
+ Blob *b = blob_list;
205
+ while (b) {
206
+ if (b->kind == BLOB_CORE_MEMORY)
207
+ fprintf(f, "```json\n%s\n```\n\n", b->data);
208
+ b = b->next;
209
+ }
210
+ fprintf(f, "## Skills\n");
211
+ b = blob_list;
212
+ while (b) {
213
+ if (b->kind == BLOB_SKILL)
214
+ fprintf(f, "- %s\n", b->data);
215
+ b = b->next;
216
+ }
217
+ fprintf(f, "\n## Cron Jobs\n");
218
+ b = blob_list;
219
+ while (b) {
220
+ if (b->kind == BLOB_CRONJOB)
221
+ fprintf(f, "- %s\n", b->data);
222
+ b = b->next;
223
+ }
224
+ fprintf(f, "\n## Webhooks\n");
225
+ b = blob_list;
226
+ while (b) {
227
+ if (b->kind == BLOB_WEBHOOK)
228
+ fprintf(f, "- %s\n", b->data);
229
+ b = b->next;
230
+ }
231
+ fprintf(f, "\n## Conversation Log\n");
232
+ // Print in chronological order
233
+ Blob *prev = NULL, *curr = blob_list, *next;
234
+ while (curr) {
235
+ next = curr->next;
236
+ curr->next = prev;
237
+ prev = curr;
238
+ curr = next;
239
+ }
240
+ blob_list = prev;
241
+ curr = blob_list;
242
+ while (curr) {
243
+ if (curr->kind == BLOB_USER || curr->kind == BLOB_ASSISTANT ||
244
+ curr->kind == BLOB_TOOL_CALL || curr->kind == BLOB_TOOL_RESULT) {
245
+ const char *role = "";
246
+ switch (curr->kind) {
247
+ case BLOB_USER: role = "User"; break;
248
+ case BLOB_ASSISTANT: role = "Assistant"; break;
249
+ case BLOB_TOOL_CALL: role = "Tool Call"; break;
250
+ case BLOB_TOOL_RESULT: role = "Tool Result"; break;
251
+ default: break;
252
+ }
253
+ fprintf(f, "### %s\n%s\n\n", role, curr->data);
254
+ }
255
+ curr = curr->next;
256
+ }
257
+ // restore
258
+ prev = NULL;
259
+ curr = blob_list;
260
+ while (curr) {
261
+ next = curr->next;
262
+ curr->next = prev;
263
+ prev = curr;
264
+ curr = next;
265
+ }
266
+ blob_list = prev;
267
+ fclose(f);
268
+ }
269
+
270
+ // ------------------------------------------------------------------
271
+ // Tools (with runtime filtering)
272
+ // ------------------------------------------------------------------
273
+ typedef struct Tool {
274
+ const char *name;
275
+ char *(*func)(const char *args);
276
+ const char *description;
277
+ int enabled;
278
+ } Tool;
279
+
280
+ // Forward declarations
281
+ char* tool_shell(const char *args);
282
+ char* tool_file_read(const char *args);
283
+ char* tool_file_write(const char *args);
284
+ char* tool_http_get(const char *args);
285
+ char* tool_math(const char *args);
286
+ char* tool_list_dir(const char *args);
287
+ char* tool_webhook_add(const char *args);
288
+ char* tool_cron_add(const char *args);
289
+ char* tool_cron_list(const char *args);
290
+ char* tool_cron_remove(const char *args);
291
+ char* tool_skill_add(const char *args);
292
+ char* tool_skill_run(const char *args);
293
+ char* tool_list_skills(const char *args);
294
+ char* tool_update_core_memory(const char *args);
295
+ char* tool_recall(const char *args);
296
+ char* tool_heartbeat(const char *args);
297
+
298
+ // Implementations (simplified but functional)
299
+ char* tool_shell(const char *args) {
300
+ (void)args;
301
+ return strdup("Shell tool is disabled for security.");
302
+ }
303
+
304
+ #define MAX_FILE_SIZE (10 * 1024 * 1024)
305
+ #define MAX_TOOL_RESULT 2000
306
+
307
+ char* tool_file_read(const char *args) {
308
+ FILE *f = fopen(args, "rb");
309
+ if (!f) return strdup("Error: cannot open file");
310
+ if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return strdup("Error seeking file"); }
311
+ long size = ftell(f);
312
+ if (size == -1 || size > MAX_FILE_SIZE) { fclose(f); return strdup("File too large"); }
313
+ rewind(f);
314
+ char *buf = malloc(size + 1);
315
+ if (!buf) { fclose(f); return strdup("Memory error"); }
316
+ size_t read = fread(buf, 1, size, f);
317
+ buf[read] = '\0';
318
+ fclose(f);
319
+ if (strlen(buf) > MAX_TOOL_RESULT) buf[MAX_TOOL_RESULT] = '\0';
320
+ return buf;
321
+ }
322
+
323
+ char* tool_file_write(const char *args) {
324
+ const char *space = strchr(args, ' ');
325
+ if (!space) return strdup("Invalid: need 'filename content'");
326
+ size_t fname_len = space - args;
327
+ char *fname = malloc(fname_len + 1);
328
+ if (!fname) return strdup("Memory error");
329
+ memcpy(fname, args, fname_len);
330
+ fname[fname_len] = '\0';
331
+ const char *content = space + 1;
332
+ FILE *f = fopen(fname, "w");
333
+ free(fname);
334
+ if (!f) return strdup("Cannot write file");
335
+ fprintf(f, "%s", content);
336
+ fclose(f);
337
+ return strdup("File written");
338
+ }
339
+
340
+ static size_t http_write_cb(void *ptr, size_t size, size_t nmemb, void *user) {
341
+ size_t total = size * nmemb;
342
+ char **resp = (char**)user;
343
+ size_t old_len = *resp ? strlen(*resp) : 0;
344
+ char *new = realloc(*resp, old_len + total + 1);
345
+ if (!new) return 0;
346
+ *resp = new;
347
+ memcpy(*resp + old_len, ptr, total);
348
+ (*resp)[old_len + total] = '\0';
349
+ return total;
350
+ }
351
+
352
+ char* tool_http_get(const char *args) {
353
+ CURL *curl = curl_easy_init();
354
+ if (!curl) return strdup("curl init failed");
355
+ char *response = NULL;
356
+ curl_easy_setopt(curl, CURLOPT_URL, args);
357
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_write_cb);
358
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
359
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
360
+ CURLcode res = curl_easy_perform(curl);
361
+ curl_easy_cleanup(curl);
362
+ if (res != CURLE_OK) {
363
+ free(response);
364
+ return strdup("HTTP request failed");
365
+ }
366
+ if (response && strlen(response) > MAX_TOOL_RESULT) response[MAX_TOOL_RESULT] = '\0';
367
+ return response ? response : strdup("");
368
+ }
369
+
370
+ char* tool_math(const char *args) {
371
+ // simple eval using bc
372
+ char cmd[512];
373
+ snprintf(cmd, sizeof(cmd), "echo '%s' | bc 2>/dev/null", args);
374
+ FILE *fp = popen(cmd, "r");
375
+ if (!fp) return strdup("Math error");
376
+ char buf[256];
377
+ char *result = NULL;
378
+ size_t len = 0;
379
+ while (fgets(buf, sizeof(buf), fp)) {
380
+ size_t l = strlen(buf);
381
+ char *new = realloc(result, len + l + 1);
382
+ if (!new) { free(result); pclose(fp); return strdup("Memory error"); }
383
+ result = new;
384
+ memcpy(result + len, buf, l + 1);
385
+ len += l;
386
+ }
387
+ pclose(fp);
388
+ if (!result) result = strdup("");
389
+ if (strlen(result) > MAX_TOOL_RESULT) result[MAX_TOOL_RESULT] = '\0';
390
+ return result;
391
+ }
392
+
393
+ char* tool_list_dir(const char *args) {
394
+ DIR *d = opendir(args);
395
+ if (!d) return strdup("Cannot open directory");
396
+ char *result = NULL;
397
+ size_t len = 0;
398
+ FILE *out = open_memstream(&result, &len);
399
+ if (!out) { closedir(d); return strdup("Memory error"); }
400
+ struct dirent *entry;
401
+ while ((entry = readdir(d)) != NULL) fprintf(out, "%s\n", entry->d_name);
402
+ closedir(d);
403
+ fclose(out);
404
+ if (!result) result = strdup("");
405
+ if (strlen(result) > MAX_TOOL_RESULT) result[MAX_TOOL_RESULT] = '\0';
406
+ return result;
407
+ }
408
+
409
+ char* tool_webhook_add(const char *args) {
410
+ cJSON *j = cJSON_Parse(args);
411
+ if (!j) return strdup("Invalid JSON");
412
+ cJSON *url = cJSON_GetObjectItem(j, "url");
413
+ cJSON *event = cJSON_GetObjectItem(j, "event");
414
+ if (!cJSON_IsString(url) || !cJSON_IsString(event)) {
415
+ cJSON_Delete(j);
416
+ return strdup("Missing url or event");
417
+ }
418
+ cJSON *obj = cJSON_CreateObject();
419
+ cJSON_AddStringToObject(obj, "url", url->valuestring);
420
+ cJSON_AddStringToObject(obj, "event", event->valuestring);
421
+ char *entry = cJSON_PrintUnformatted(obj);
422
+ cJSON_Delete(obj);
423
+ cJSON_Delete(j);
424
+ if (entry) {
425
+ blob_append(BLOB_WEBHOOK, entry);
426
+ free(entry);
427
+ return strdup("Webhook added");
428
+ }
429
+ return strdup("Memory error");
430
+ }
431
+
432
+ char* tool_cron_add(const char *args) {
433
+ cJSON *j = cJSON_Parse(args);
434
+ if (!j) return strdup("Invalid JSON");
435
+ cJSON *sched = cJSON_GetObjectItem(j, "schedule");
436
+ cJSON *tool = cJSON_GetObjectItem(j, "tool");
437
+ cJSON *tool_args = cJSON_GetObjectItem(j, "args");
438
+ if (!cJSON_IsString(sched) || !cJSON_IsString(tool) || !cJSON_IsString(tool_args)) {
439
+ cJSON_Delete(j);
440
+ return strdup("Missing schedule, tool, or args");
441
+ }
442
+ cJSON *obj = cJSON_CreateObject();
443
+ cJSON_AddStringToObject(obj, "schedule", sched->valuestring);
444
+ cJSON_AddStringToObject(obj, "tool", tool->valuestring);
445
+ cJSON_AddStringToObject(obj, "args", tool_args->valuestring);
446
+ cJSON_AddNumberToObject(obj, "last_run", 0);
447
+ char *entry = cJSON_PrintUnformatted(obj);
448
+ cJSON_Delete(obj);
449
+ cJSON_Delete(j);
450
+ if (entry) {
451
+ blob_append(BLOB_CRONJOB, entry);
452
+ free(entry);
453
+ return strdup("Cron job added");
454
+ }
455
+ return strdup("Memory error");
456
+ }
457
+
458
+ char* tool_cron_list(const char *args) {
459
+ (void)args;
460
+ char *result = NULL;
461
+ size_t len = 0;
462
+ FILE *out = open_memstream(&result, &len);
463
+ if (!out) return strdup("Memory error");
464
+ Blob *b = blob_list;
465
+ while (b) {
466
+ if (b->kind == BLOB_CRONJOB) fprintf(out, "%s\n", b->data);
467
+ b = b->next;
468
+ }
469
+ fclose(out);
470
+ if (!result) result = strdup("");
471
+ if (strlen(result) > MAX_TOOL_RESULT) result[MAX_TOOL_RESULT] = '\0';
472
+ return result;
473
+ }
474
+
475
+ char* tool_cron_remove(const char *args) {
476
+ Blob *prev = NULL, *curr = blob_list;
477
+ int removed = 0;
478
+ while (curr) {
479
+ if (curr->kind == BLOB_CRONJOB && strstr(curr->data, args)) {
480
+ Blob *next = curr->next;
481
+ if (prev) prev->next = next;
482
+ else blob_list = next;
483
+ free(curr->data);
484
+ free(curr);
485
+ curr = next;
486
+ removed++;
487
+ dirty = 1;
488
+ } else {
489
+ prev = curr;
490
+ curr = curr->next;
491
+ }
492
+ }
493
+ char msg[64];
494
+ snprintf(msg, sizeof(msg), "Removed %d cron job(s)", removed);
495
+ return strdup(msg);
496
+ }
497
+
498
+ char* tool_skill_add(const char *args) {
499
+ cJSON *j = cJSON_Parse(args);
500
+ if (!j) return strdup("Invalid JSON");
501
+ cJSON *name = cJSON_GetObjectItem(j, "name");
502
+ cJSON *desc = cJSON_GetObjectItem(j, "desc");
503
+ cJSON *steps = cJSON_GetObjectItem(j, "steps");
504
+ if (!cJSON_IsString(name) || !cJSON_IsString(desc) || !cJSON_IsArray(steps)) {
505
+ cJSON_Delete(j);
506
+ return strdup("Missing name, desc, or steps array");
507
+ }
508
+ cJSON *obj = cJSON_CreateObject();
509
+ cJSON_AddStringToObject(obj, "name", name->valuestring);
510
+ cJSON_AddStringToObject(obj, "desc", desc->valuestring);
511
+ cJSON_AddItemToObject(obj, "steps", cJSON_Duplicate(steps, 1));
512
+ char *entry = cJSON_PrintUnformatted(obj);
513
+ cJSON_Delete(obj);
514
+ cJSON_Delete(j);
515
+ if (entry) {
516
+ blob_append(BLOB_SKILL, entry);
517
+ free(entry);
518
+ return strdup("Skill added");
519
+ }
520
+ return strdup("Memory error");
521
+ }
522
+
523
+ char* tool_skill_run(const char *args) {
524
+ char *copy = strdup(args);
525
+ if (!copy) return strdup("Memory error");
526
+ char *space = strchr(copy, ' ');
527
+ if (space) *space = '\0';
528
+ const char *skill_name = copy;
529
+ const char *skill_args = space ? space + 1 : "";
530
+ char *result = NULL;
531
+ asprintf(&result, "RUN_SKILL:%s:%s", skill_name, skill_args);
532
+ free(copy);
533
+ return result ? result : strdup("");
534
+ }
535
+
536
+ char* tool_list_skills(const char *args) {
537
+ (void)args;
538
+ char *result = NULL;
539
+ size_t len = 0;
540
+ FILE *out = open_memstream(&result, &len);
541
+ if (!out) return strdup("Memory error");
542
+ Blob *b = blob_list;
543
+ while (b) {
544
+ if (b->kind == BLOB_SKILL) {
545
+ cJSON *j = cJSON_Parse(b->data);
546
+ if (j) {
547
+ cJSON *name = cJSON_GetObjectItem(j, "name");
548
+ if (cJSON_IsString(name)) fprintf(out, "%s\n", name->valuestring);
549
+ cJSON_Delete(j);
550
+ }
551
+ }
552
+ b = b->next;
553
+ }
554
+ fclose(out);
555
+ if (!result) result = strdup("");
556
+ if (strlen(result) > MAX_TOOL_RESULT) result[MAX_TOOL_RESULT] = '\0';
557
+ return result;
558
+ }
559
+
560
+ char* tool_update_core_memory(const char *args) {
561
+ cJSON *updates = cJSON_Parse(args);
562
+ if (!updates) return strdup("Invalid JSON");
563
+ // Find existing core memory
564
+ cJSON *core = NULL;
565
+ Blob *b = blob_list;
566
+ while (b) {
567
+ if (b->kind == BLOB_CORE_MEMORY) {
568
+ core = cJSON_Parse(b->data);
569
+ break;
570
+ }
571
+ b = b->next;
572
+ }
573
+ if (!core) core = cJSON_CreateObject();
574
+ // Merge updates
575
+ cJSON *child = NULL;
576
+ cJSON_ArrayForEach(child, updates) {
577
+ cJSON_AddItemToObject(core, child->string, cJSON_Duplicate(child, 1));
578
+ }
579
+ char *new_core = cJSON_PrintUnformatted(core);
580
+ cJSON_Delete(core);
581
+ cJSON_Delete(updates);
582
+ if (!new_core) return strdup("Memory error");
583
+ // Delete old core blob
584
+ Blob *prev = NULL, *curr = blob_list;
585
+ while (curr) {
586
+ if (curr->kind == BLOB_CORE_MEMORY) {
587
+ if (prev) prev->next = curr->next;
588
+ else blob_list = curr->next;
589
+ free(curr->data);
590
+ free(curr);
591
+ break;
592
+ }
593
+ prev = curr;
594
+ curr = curr->next;
595
+ }
596
+ blob_append(BLOB_CORE_MEMORY, new_core);
597
+ free(new_core);
598
+ dirty = 1;
599
+ return strdup("Core memory updated");
600
+ }
601
+
602
+ char* tool_recall(const char *args) {
603
+ char *result = NULL;
604
+ size_t len = 0;
605
+ FILE *out = open_memstream(&result, &len);
606
+ if (!out) return strdup("Memory error");
607
+ Blob *b = blob_list;
608
+ while (b) {
609
+ if ((b->kind == BLOB_USER || b->kind == BLOB_ASSISTANT) && strcasestr(b->data, args)) {
610
+ const char *role = (b->kind == BLOB_USER) ? "User" : "Assistant";
611
+ fprintf(out, "[%s] %s\n", role, b->data);
612
+ }
613
+ b = b->next;
614
+ }
615
+ fclose(out);
616
+ if (!result) result = strdup("");
617
+ if (strlen(result) > MAX_TOOL_RESULT) result[MAX_TOOL_RESULT] = '\0';
618
+ return result;
619
+ }
620
+
621
+ char* tool_heartbeat(const char *args) {
622
+ (void)args;
623
+ return strdup("Heartbeat");
624
+ }
625
+
626
+ static Tool tools[] = {
627
+ {"shell", tool_shell, "Execute shell command (disabled)", 0},
628
+ {"file_read", tool_file_read, "Read a file", 1},
629
+ {"file_write", tool_file_write, "Write a file", 1},
630
+ {"http_get", tool_http_get, "HTTP GET request", 1},
631
+ {"math", tool_math, "Evaluate arithmetic expression", 1},
632
+ {"list_dir", tool_list_dir, "List directory contents", 1},
633
+ {"webhook_add", tool_webhook_add, "Add a webhook", 1},
634
+ {"cron_add", tool_cron_add, "Add a cron job", 1},
635
+ {"cron_list", tool_cron_list, "List cron jobs", 1},
636
+ {"cron_remove", tool_cron_remove, "Remove cron jobs", 1},
637
+ {"skill_add", tool_skill_add, "Add a skill", 1},
638
+ {"skill_run", tool_skill_run, "Run a skill", 1},
639
+ {"list_skills", tool_list_skills, "List skills", 1},
640
+ {"update_core_memory", tool_update_core_memory, "Update core memory", 1},
641
+ {"recall", tool_recall, "Search conversation history", 1},
642
+ {"heartbeat", tool_heartbeat, "Internal heartbeat", 1},
643
+ {NULL, NULL, NULL, 0}
644
+ };
645
+
646
+ char* execute_tool(const char *name, const char *args) {
647
+ for (Tool *t = tools; t->name; t++) {
648
+ if (strcmp(t->name, name) == 0 && t->enabled) {
649
+ return t->func(args);
650
+ }
651
+ }
652
+ return strdup("Unknown or disabled tool");
653
+ }
654
+
655
+ // ------------------------------------------------------------------
656
+ // LLM Communication
657
+ // ------------------------------------------------------------------
658
+ static int no_llm_mode = 0;
659
+ static int dry_run = 0;
660
+ static char state_filename[256] = "shadowclaw.bin";
661
+ static char soul_filename[256] = "shadowclaw_data/shadowsoul.md";
662
+ static const char *ollama_endpoint = "http://localhost:11434";
663
+ static const char *ollama_model = "qwen2.5:0.5b";
664
+ static long llm_connect_timeout = 15;
665
+ static long llm_total_timeout = 180;
666
+ static int llm_retry_attempts = 5;
667
+ static int shell_enabled = 0;
668
+
669
+ static int ollama_healthy(void) {
670
+ CURL *curl = curl_easy_init();
671
+ if (!curl) return 0;
672
+ curl_easy_setopt(curl, CURLOPT_URL, ollama_endpoint);
673
+ curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
674
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2L);
675
+ CURLcode res = curl_easy_perform(curl);
676
+ curl_easy_cleanup(curl);
677
+ return (res == CURLE_OK);
678
+ }
679
+
680
+ typedef struct {
681
+ char *data;
682
+ size_t len;
683
+ } RespBuf;
684
+
685
+ static size_t llm_write_cb(void *ptr, size_t size, size_t nmemb, void *stream) {
686
+ RespBuf *buf = (RespBuf*)stream;
687
+ size_t total = size * nmemb;
688
+ char *new = realloc(buf->data, buf->len + total + 1);
689
+ if (!new) return 0;
690
+ buf->data = new;
691
+ memcpy(buf->data + buf->len, ptr, total);
692
+ buf->len += total;
693
+ buf->data[buf->len] = '\0';
694
+ return total;
695
+ }
696
+
697
+ static char* call_llm(const char *prompt) {
698
+ if (!ollama_healthy()) {
699
+ fprintf(stderr, COLOR_YELLOW "Ollama unreachable. Switching to no-llm mode.\n" COLOR_RESET);
700
+ no_llm_mode = 1;
701
+ return NULL;
702
+ }
703
+ CURL *curl = curl_easy_init();
704
+ if (!curl) return NULL;
705
+ char url[256];
706
+ snprintf(url, sizeof(url), "%s/api/generate", ollama_endpoint);
707
+ cJSON *req = cJSON_CreateObject();
708
+ cJSON_AddStringToObject(req, "model", ollama_model);
709
+ cJSON_AddStringToObject(req, "prompt", prompt);
710
+ cJSON_AddBoolToObject(req, "stream", 0);
711
+ char *req_str = cJSON_PrintUnformatted(req);
712
+ cJSON_Delete(req);
713
+ struct curl_slist *headers = NULL;
714
+ headers = curl_slist_append(headers, "Content-Type: application/json");
715
+ RespBuf resp = {0};
716
+ curl_easy_setopt(curl, CURLOPT_URL, url);
717
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req_str);
718
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
719
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, llm_write_cb);
720
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
721
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, llm_connect_timeout);
722
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, llm_total_timeout);
723
+ CURLcode res = curl_easy_perform(curl);
724
+ free(req_str);
725
+ curl_slist_free_all(headers);
726
+ curl_easy_cleanup(curl);
727
+ if (res != CURLE_OK) {
728
+ fprintf(stderr, COLOR_RED "LLM request failed: %s\n" COLOR_RESET, curl_easy_strerror(res));
729
+ free(resp.data);
730
+ return NULL;
731
+ }
732
+ cJSON *root = cJSON_Parse(resp.data);
733
+ free(resp.data);
734
+ if (!root) return NULL;
735
+ cJSON *response = cJSON_GetObjectItem(root, "response");
736
+ char *result = response && cJSON_IsString(response) ? strdup(response->valuestring) : NULL;
737
+ cJSON_Delete(root);
738
+ return result;
739
+ }
740
+
741
+ static char* build_simple_prompt(const char *user_msg) {
742
+ // Build list of available tools (filter by enabled)
743
+ char tools_list[1024] = "";
744
+ for (Tool *t = tools; t->name; t++) {
745
+ if (t->enabled) {
746
+ strcat(tools_list, t->name);
747
+ strcat(tools_list, ", ");
748
+ }
749
+ }
750
+ if (strlen(tools_list) > 2) tools_list[strlen(tools_list)-2] = '\0';
751
+ // Collect core memory
752
+ char *core = strdup("{}");
753
+ Blob *b = blob_list;
754
+ while (b) {
755
+ if (b->kind == BLOB_CORE_MEMORY) {
756
+ free(core);
757
+ core = strdup(b->data);
758
+ break;
759
+ }
760
+ b = b->next;
761
+ }
762
+ // Collect last 5 conversation turns (chronological)
763
+ char history[4096] = "";
764
+ Blob *prev = NULL, *curr = blob_list, *next;
765
+ while (curr) {
766
+ next = curr->next;
767
+ curr->next = prev;
768
+ prev = curr;
769
+ curr = next;
770
+ }
771
+ blob_list = prev;
772
+ int count = 0;
773
+ curr = blob_list;
774
+ while (curr && count < 10) { // 5 pairs
775
+ if (curr->kind == BLOB_USER || curr->kind == BLOB_ASSISTANT ||
776
+ curr->kind == BLOB_TOOL_CALL || curr->kind == BLOB_TOOL_RESULT) {
777
+ const char *role = "";
778
+ switch (curr->kind) {
779
+ case BLOB_USER: role = "User"; break;
780
+ case BLOB_ASSISTANT: role = "Assistant"; break;
781
+ case BLOB_TOOL_CALL: role = "Tool call"; break;
782
+ case BLOB_TOOL_RESULT: role = "Tool result"; break;
783
+ }
784
+ char line[512];
785
+ snprintf(line, sizeof(line), "%s: %s\n", role, curr->data);
786
+ strcat(history, line);
787
+ count++;
788
+ }
789
+ curr = curr->next;
790
+ }
791
+ // restore list
792
+ prev = NULL;
793
+ curr = blob_list;
794
+ while (curr) {
795
+ next = curr->next;
796
+ curr->next = prev;
797
+ prev = curr;
798
+ curr = next;
799
+ }
800
+ blob_list = prev;
801
+ char *prompt;
802
+ asprintf(&prompt,
803
+ "You are Shadowclaw, an AI assistant with access to tools.\n"
804
+ "Core memory: %s\n\n"
805
+ "Recent conversation:\n%s\n\n"
806
+ "Available tools: %s\n"
807
+ "To use a tool, output exactly:\n```tool\n{\"tool\":\"name\",\"args\":\"arguments\"}\n```\n"
808
+ "Otherwise, respond normally.\n\n"
809
+ "User: %s\n",
810
+ core, history, tools_list, user_msg);
811
+ free(core);
812
+ return prompt;
813
+ }
814
+
815
+ static char* extract_tool_json(const char *response) {
816
+ const char *start = strstr(response, "```tool");
817
+ if (!start) return NULL;
818
+ start += 7;
819
+ while (*start == ' ' || *start == '\n') start++;
820
+ const char *end = strstr(start, "```");
821
+ if (!end) return NULL;
822
+ size_t len = end - start;
823
+ char *json = malloc(len + 1);
824
+ if (!json) return NULL;
825
+ memcpy(json, start, len);
826
+ json[len] = '\0';
827
+ return json;
828
+ }
829
+
830
+ static int parse_tool_call(const char *json_str, char **tool_name, char **tool_args) {
831
+ cJSON *root = cJSON_Parse(json_str);
832
+ if (!root) return 0;
833
+ cJSON *name = cJSON_GetObjectItem(root, "tool");
834
+ cJSON *args = cJSON_GetObjectItem(root, "args");
835
+ if (!cJSON_IsString(name)) {
836
+ cJSON_Delete(root);
837
+ return 0;
838
+ }
839
+ *tool_name = strdup(name->valuestring);
840
+ if (cJSON_IsString(args)) {
841
+ *tool_args = strdup(args->valuestring);
842
+ } else if (cJSON_IsArray(args)) {
843
+ int size = cJSON_GetArraySize(args);
844
+ size_t total = 0;
845
+ for (int i = 0; i < size; i++) {
846
+ cJSON *elem = cJSON_GetArrayItem(args, i);
847
+ if (cJSON_IsString(elem)) total += strlen(elem->valuestring) + 1;
848
+ }
849
+ *tool_args = malloc(total + 1);
850
+ if (*tool_args) {
851
+ (*tool_args)[0] = '\0';
852
+ for (int i = 0; i < size; i++) {
853
+ cJSON *elem = cJSON_GetArrayItem(args, i);
854
+ if (cJSON_IsString(elem)) {
855
+ if (i > 0) strcat(*tool_args, " ");
856
+ strcat(*tool_args, elem->valuestring);
857
+ }
858
+ }
859
+ } else {
860
+ *tool_args = strdup("");
861
+ }
862
+ } else {
863
+ *tool_args = strdup("");
864
+ }
865
+ cJSON_Delete(root);
866
+ return 1;
867
+ }
868
+
869
+ // Webhook retry
870
+ static int http_post_with_retry(const char *url, const char *payload) {
871
+ int delays[] = {1, 2, 4};
872
+ for (int i = 0; i < 3; i++) {
873
+ CURL *curl = curl_easy_init();
874
+ if (!curl) continue;
875
+ struct curl_slist *headers = NULL;
876
+ headers = curl_slist_append(headers, "Content-Type: application/json");
877
+ curl_easy_setopt(curl, CURLOPT_URL, url);
878
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
879
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
880
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
881
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); // discard
882
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
883
+ CURLcode res = curl_easy_perform(curl);
884
+ curl_easy_cleanup(curl);
885
+ curl_slist_free_all(headers);
886
+ if (res == CURLE_OK) return 0;
887
+ sleep(delays[i]);
888
+ }
889
+ fprintf(stderr, COLOR_RED "Webhook POST failed after retries\n" COLOR_RESET);
890
+ return -1;
891
+ }
892
+
893
+ static void trigger_webhooks(const char *event, const char *data) {
894
+ Blob *b = blob_list;
895
+ while (b) {
896
+ if (b->kind == BLOB_WEBHOOK) {
897
+ cJSON *j = cJSON_Parse(b->data);
898
+ if (j) {
899
+ cJSON *url = cJSON_GetObjectItem(j, "url");
900
+ cJSON *ev = cJSON_GetObjectItem(j, "event");
901
+ if (cJSON_IsString(url) && cJSON_IsString(ev) && strcmp(ev->valuestring, event) == 0) {
902
+ cJSON *payload = cJSON_CreateObject();
903
+ cJSON_AddStringToObject(payload, "event", event);
904
+ cJSON_AddStringToObject(payload, "data", data);
905
+ char *payload_str = cJSON_PrintUnformatted(payload);
906
+ cJSON_Delete(payload);
907
+ if (payload_str) {
908
+ http_post_with_retry(url->valuestring, payload_str);
909
+ free(payload_str);
910
+ }
911
+ }
912
+ cJSON_Delete(j);
913
+ }
914
+ }
915
+ b = b->next;
916
+ }
917
+ }
918
+
919
+ // Cron processing in main loop
920
+ static void process_cron_jobs(void) {
921
+ time_t now = time(NULL);
922
+ Blob *b = blob_list;
923
+ while (b) {
924
+ if (b->kind == BLOB_CRONJOB) {
925
+ cJSON *j = cJSON_Parse(b->data);
926
+ if (!j) { b = b->next; continue; }
927
+ cJSON *sched = cJSON_GetObjectItem(j, "schedule");
928
+ cJSON *tool = cJSON_GetObjectItem(j, "tool");
929
+ cJSON *args = cJSON_GetObjectItem(j, "args");
930
+ cJSON *last_run_json = cJSON_GetObjectItem(j, "last_run");
931
+ if (cJSON_IsString(sched) && cJSON_IsString(tool) && cJSON_IsString(args) && cJSON_IsNumber(last_run_json)) {
932
+ time_t last_run = (time_t)last_run_json->valuedouble;
933
+ int interval = 0;
934
+ if (strcmp(sched->valuestring, "@heartbeat") == 0) interval = 120;
935
+ else if (strncmp(sched->valuestring, "@every ", 7) == 0) {
936
+ int val = atoi(sched->valuestring + 7);
937
+ interval = val;
938
+ } else if (strcmp(sched->valuestring, "@hourly") == 0) interval = 3600;
939
+ else if (strcmp(sched->valuestring, "@daily") == 0) interval = 86400;
940
+ else if (strcmp(sched->valuestring, "@weekly") == 0) interval = 604800;
941
+ if (interval > 0 && (now - last_run) >= interval) {
942
+ if (strcmp(tool->valuestring, "heartbeat") == 0) {
943
+ // run heartbeat immediately
944
+ char *result = execute_tool("heartbeat", "");
945
+ printf(COLOR_CYAN "[Cron] Heartbeat: %s\n" COLOR_RESET, result);
946
+ free(result);
947
+ } else {
948
+ // queue? For simplicity, just execute now
949
+ char *result = execute_tool(tool->valuestring, args->valuestring);
950
+ printf(COLOR_CYAN "[Cron] %s(%s) -> %s\n" COLOR_RESET, tool->valuestring, args->valuestring, result);
951
+ free(result);
952
+ }
953
+ // update last_run in blob
954
+ cJSON_SetNumberValue(last_run_json, (double)now);
955
+ char *new_data = cJSON_PrintUnformatted(j);
956
+ if (new_data) {
957
+ // replace blob
958
+ Blob *prev = NULL, *curr = blob_list;
959
+ while (curr) {
960
+ if (curr == b) {
961
+ if (prev) prev->next = curr->next;
962
+ else blob_list = curr->next;
963
+ free(curr->data);
964
+ free(curr);
965
+ break;
966
+ }
967
+ prev = curr;
968
+ curr = curr->next;
969
+ }
970
+ blob_append(BLOB_CRONJOB, new_data);
971
+ free(new_data);
972
+ dirty = 1;
973
+ break; // restart iteration
974
+ }
975
+ }
976
+ }
977
+ cJSON_Delete(j);
978
+ }
979
+ b = b->next;
980
+ }
981
+ }
982
+
983
+ // ------------------------------------------------------------------
984
+ // Slash Commands (restored full menu)
985
+ // ------------------------------------------------------------------
986
+ static void print_help(void) {
987
+ printf(COLOR_BOLD "\nShadowclaw v%s commands:\n" COLOR_RESET, SHADOWCLAW_VERSION);
988
+ printf(" %-12s %s\n", "/help", "Show this help");
989
+ printf(" %-12s %s\n", "/tools", "List available tools");
990
+ printf(" %-12s %s\n", "/state", "Show arena memory stats and soul file info");
991
+ printf(" %-12s %s\n", "/clear", "Clear conversation history");
992
+ printf(" %-12s %s\n", "/exit", "Exit Shadowclaw");
993
+ printf(" %-12s %s\n", "/loop", "Schedule a recurring task: /loop <schedule> <tool> [args...]");
994
+ printf(" %-12s %s\n", "/crons", "List scheduled cron jobs");
995
+ printf(" %-12s %s\n", "/webhooks", "List registered webhooks");
996
+ printf(" %-12s %s\n", "/skills", "List dynamic skills");
997
+ printf(" %-12s %s\n", "/compact", "Manually compact arena (remove deleted blobs)");
998
+ printf(" %-12s %s\n", "/soul", "Show path to shadowsoul.md and last update");
999
+ printf(" %-12s %s\n", "/reconnect", "Reconnect to Ollama (if LLM mode is off)");
1000
+
1001
+ printf(COLOR_BOLD "\nBuilt-in tools:\n" COLOR_RESET);
1002
+ for (Tool *t = tools; t->name; t++) printf(" %s\n", t->name);
1003
+
1004
+ int skill_count = 0;
1005
+ Blob *b = blob_list;
1006
+ while (b) {
1007
+ if (b->kind == BLOB_SKILL) {
1008
+ cJSON *j = cJSON_Parse(b->data);
1009
+ if (j) {
1010
+ cJSON *name = cJSON_GetObjectItem(j, "name");
1011
+ cJSON *desc = cJSON_GetObjectItem(j, "desc");
1012
+ if (cJSON_IsString(name) && cJSON_IsString(desc)) {
1013
+ if (skill_count == 0) printf(COLOR_BOLD "\nDynamic skills:\n" COLOR_RESET);
1014
+ printf(" %-12s %s\n", name->valuestring, desc->valuestring);
1015
+ skill_count++;
1016
+ }
1017
+ cJSON_Delete(j);
1018
+ }
1019
+ }
1020
+ b = b->next;
1021
+ }
1022
+ if (skill_count == 0) printf("\nNo dynamic skills loaded. Use skill_add to create one.\n");
1023
+
1024
+ struct stat st;
1025
+ if (stat(soul_filename, &st) == 0) {
1026
+ char timebuf[128];
1027
+ struct tm *tm_info = localtime(&st.st_mtime);
1028
+ strftime(timebuf, sizeof(timebuf), "%c", tm_info);
1029
+ printf("\nSoul file: %s (%ld bytes, last modified: %s\n", soul_filename, st.st_size, timebuf);
1030
+ } else {
1031
+ printf("\nSoul file not yet created.\n");
1032
+ }
1033
+ printf("\n");
1034
+ }
1035
+
1036
+ static void list_tools(void) {
1037
+ printf(COLOR_BOLD "Enabled tools:\n" COLOR_RESET);
1038
+ for (Tool *t = tools; t->name; t++) {
1039
+ if (t->enabled)
1040
+ printf(" %-12s %s\n", t->name, t->description);
1041
+ }
1042
+ }
1043
+
1044
+ static void show_state(void) {
1045
+ size_t total_size = 0;
1046
+ Blob *b = blob_list;
1047
+ while (b) {
1048
+ total_size += sizeof(Blob) + b->size + 1;
1049
+ b = b->next;
1050
+ }
1051
+ printf("Blob count: %llu (next id: %llu)\n", (unsigned long long)(next_id - 1), (unsigned long long)next_id);
1052
+ printf("Estimated memory usage: ~%zu bytes\n", total_size);
1053
+ struct stat st;
1054
+ if (stat(soul_filename, &st) == 0) {
1055
+ char timebuf[128];
1056
+ struct tm *tm_info = localtime(&st.st_mtime);
1057
+ strftime(timebuf, sizeof(timebuf), "%c", tm_info);
1058
+ printf("Soul file: %s (%ld bytes, last modified: %s\n", soul_filename, st.st_size, timebuf);
1059
+ } else {
1060
+ printf("Soul file not yet created.\n");
1061
+ }
1062
+ }
1063
+
1064
+ static void clear_conversation(void) {
1065
+ // Keep system, core memory, skills, cron jobs, webhooks
1066
+ Blob *new_list = NULL;
1067
+ Blob *b = blob_list;
1068
+ while (b) {
1069
+ Blob *next = b->next;
1070
+ if (b->kind == BLOB_SYSTEM || b->kind == BLOB_CORE_MEMORY ||
1071
+ b->kind == BLOB_SKILL || b->kind == BLOB_CRONJOB || b->kind == BLOB_WEBHOOK) {
1072
+ // move to new list (preserve order)
1073
+ b->next = new_list;
1074
+ new_list = b;
1075
+ } else {
1076
+ free(b->data);
1077
+ free(b);
1078
+ }
1079
+ b = next;
1080
+ }
1081
+ blob_list = new_list;
1082
+ dirty = 1;
1083
+ printf("Conversation history cleared (system, core memory, skills, cron, webhooks preserved).\n");
1084
+ }
1085
+
1086
+ static void compact_arena(void) {
1087
+ // Rebuild blob list without any deleted markers (we don't use deleted markers, so just reorder?)
1088
+ // Actually we want to reorder by id to remove gaps and keep chronological?
1089
+ // For simplicity, we just leave as is; compaction is automatic via save/load.
1090
+ // Calling save and reload compacts.
1091
+ if (save_state(state_filename) == 0) {
1092
+ load_state(state_filename);
1093
+ printf("Arena compacted (state saved and reloaded).\n");
1094
+ } else {
1095
+ printf("Compaction failed.\n");
1096
+ }
1097
+ }
1098
+
1099
+ static const char* parse_schedule_string(const char *sched) {
1100
+ static char buf[32];
1101
+ if (strcmp(sched, "hourly") == 0) return "@hourly";
1102
+ if (strcmp(sched, "daily") == 0) return "@daily";
1103
+ if (strcmp(sched, "weekly") == 0) return "@weekly";
1104
+ char *end;
1105
+ long val = strtol(sched, &end, 10);
1106
+ if (end != sched && *end) {
1107
+ if (strcmp(end, "s") == 0) { snprintf(buf, sizeof(buf), "@every %lds", val); return buf; }
1108
+ if (strcmp(end, "m") == 0) { snprintf(buf, sizeof(buf), "@every %ldm", val); return buf; }
1109
+ if (strcmp(end, "h") == 0) { snprintf(buf, sizeof(buf), "@every %ldh", val); return buf; }
1110
+ }
1111
+ return NULL;
1112
+ }
1113
+
1114
+ // ------------------------------------------------------------------
1115
+ // Main
1116
+ // ------------------------------------------------------------------
1117
+ int main(int argc, char **argv) {
1118
+ for (int i = 1; i < argc; i++) {
1119
+ if (strcmp(argv[i], "--no-llm") == 0) no_llm_mode = 1;
1120
+ else if (strcmp(argv[i], "--dry-run") == 0) dry_run = 1;
1121
+ else if (strcmp(argv[i], "--enable-shell") == 0) {
1122
+ shell_enabled = 1;
1123
+ for (Tool *t = tools; t->name; t++)
1124
+ if (strcmp(t->name, "shell") == 0) t->enabled = 1;
1125
+ } else if (strcmp(argv[i], "--model") == 0 && i+1 < argc) ollama_model = argv[++i];
1126
+ else if (strcmp(argv[i], "-f") == 0 && i+1 < argc) strncpy(state_filename, argv[++i], 255);
1127
+ }
1128
+
1129
+ curl_global_init(CURL_GLOBAL_ALL);
1130
+
1131
+ // Ensure soul directory exists
1132
+ char *soul_dir = strdup(soul_filename);
1133
+ char *last_slash = strrchr(soul_dir, '/');
1134
+ if (last_slash) {
1135
+ *last_slash = '\0';
1136
+ mkdir(soul_dir, 0755);
1137
+ }
1138
+ free(soul_dir);
1139
+
1140
+ if (load_state(state_filename) != 0) {
1141
+ printf("No saved state found. Starting fresh.\n");
1142
+ blob_append(BLOB_SYSTEM, "You are Shadowclaw, a helpful AI assistant with access to tools.");
1143
+ blob_append(BLOB_CORE_MEMORY, "{}");
1144
+ save_state(state_filename);
1145
+ }
1146
+ sync_soul_file(soul_filename);
1147
+
1148
+ printf(COLOR_BOLD "\nShadowclaw v%s – Ollama-Enhanced\n" COLOR_RESET, SHADOWCLAW_VERSION);
1149
+ if (no_llm_mode) printf(COLOR_YELLOW "LLM mode disabled. Using local interpreter.\n" COLOR_RESET);
1150
+ else printf(COLOR_GREEN "LLM mode enabled (model: %s)\n" COLOR_RESET, ollama_model);
1151
+
1152
+ // Ensure heartbeat cron job exists
1153
+ int has_heartbeat = 0;
1154
+ Blob *b = blob_list;
1155
+ while (b) {
1156
+ if (b->kind == BLOB_CRONJOB && strstr(b->data, "\"tool\":\"heartbeat\"")) {
1157
+ has_heartbeat = 1;
1158
+ break;
1159
+ }
1160
+ b = b->next;
1161
+ }
1162
+ if (!has_heartbeat) {
1163
+ char *heartbeat_json = NULL;
1164
+ asprintf(&heartbeat_json, "{\"schedule\":\"@every 120s\",\"tool\":\"heartbeat\",\"args\":\"\",\"last_run\":0}");
1165
+ if (heartbeat_json) {
1166
+ blob_append(BLOB_CRONJOB, heartbeat_json);
1167
+ free(heartbeat_json);
1168
+ dirty = 1;
1169
+ }
1170
+ }
1171
+
1172
+ char line[4096];
1173
+ while (1) {
1174
+ process_cron_jobs();
1175
+ printf(COLOR_GREEN "> " COLOR_RESET);
1176
+ fflush(stdout);
1177
+ if (!fgets(line, sizeof(line), stdin)) break;
1178
+ line[strcspn(line, "\n")] = 0;
1179
+ if (strlen(line) == 0) continue;
1180
+
1181
+ if (line[0] == '/') {
1182
+ if (strcmp(line, "/help") == 0) {
1183
+ print_help();
1184
+ } else if (strcmp(line, "/tools") == 0) {
1185
+ list_tools();
1186
+ } else if (strcmp(line, "/state") == 0) {
1187
+ show_state();
1188
+ } else if (strcmp(line, "/clear") == 0) {
1189
+ clear_conversation();
1190
+ save_state(state_filename);
1191
+ sync_soul_file(soul_filename);
1192
+ } else if (strcmp(line, "/exit") == 0) {
1193
+ break;
1194
+ } else if (strncmp(line, "/loop", 5) == 0) {
1195
+ char *p = line + 5;
1196
+ while (*p == ' ') p++;
1197
+ if (*p == '\0') {
1198
+ printf("Usage: /loop <schedule> <tool> [args...]\n"
1199
+ "Examples:\n /loop 1h http_get https://example.com/news\n"
1200
+ " /loop daily file_read ~/notes.txt\n");
1201
+ continue;
1202
+ }
1203
+ char *sched_start = p;
1204
+ while (*p && !isspace(*p)) p++;
1205
+ if (!*p) { printf("Missing tool after schedule.\n"); continue; }
1206
+ *p++ = '\0';
1207
+ const char *sched_str = parse_schedule_string(sched_start);
1208
+ if (!sched_str) {
1209
+ printf("Unrecognized schedule format. Use e.g., 1h, 30m, daily, hourly, weekly.\n");
1210
+ continue;
1211
+ }
1212
+ while (*p == ' ') p++;
1213
+ if (*p == '\0') { printf("Missing tool name.\n"); continue; }
1214
+ char *tool_start = p;
1215
+ while (*p && !isspace(*p)) p++;
1216
+ if (*p) *p++ = '\0';
1217
+ while (*p == ' ') p++;
1218
+ char *args = p;
1219
+
1220
+ cJSON *obj = cJSON_CreateObject();
1221
+ cJSON_AddStringToObject(obj, "schedule", sched_str);
1222
+ cJSON_AddStringToObject(obj, "tool", tool_start);
1223
+ cJSON_AddStringToObject(obj, "args", args);
1224
+ cJSON_AddNumberToObject(obj, "last_run", 0);
1225
+ char *json = cJSON_PrintUnformatted(obj);
1226
+ cJSON_Delete(obj);
1227
+ if (json) {
1228
+ blob_append(BLOB_CRONJOB, json);
1229
+ free(json);
1230
+ save_state(state_filename);
1231
+ sync_soul_file(soul_filename);
1232
+ printf("Scheduled: %s every %s\n", tool_start, sched_str);
1233
+ } else {
1234
+ printf("Failed to create cron job.\n");
1235
+ }
1236
+ } else if (strcmp(line, "/crons") == 0) {
1237
+ Blob *b = blob_list;
1238
+ printf("Scheduled cron jobs:\n");
1239
+ int count = 0;
1240
+ while (b) {
1241
+ if (b->kind == BLOB_CRONJOB) {
1242
+ printf(" %s\n", b->data);
1243
+ count++;
1244
+ }
1245
+ b = b->next;
1246
+ }
1247
+ if (count == 0) printf(" None\n");
1248
+ } else if (strcmp(line, "/webhooks") == 0) {
1249
+ Blob *b = blob_list;
1250
+ printf("Registered webhooks:\n");
1251
+ int count = 0;
1252
+ while (b) {
1253
+ if (b->kind == BLOB_WEBHOOK) {
1254
+ printf(" %s\n", b->data);
1255
+ count++;
1256
+ }
1257
+ b = b->next;
1258
+ }
1259
+ if (count == 0) printf(" None\n");
1260
+ } else if (strcmp(line, "/skills") == 0) {
1261
+ Blob *b = blob_list;
1262
+ printf("Dynamic skills:\n");
1263
+ int count = 0;
1264
+ while (b) {
1265
+ if (b->kind == BLOB_SKILL) {
1266
+ cJSON *j = cJSON_Parse(b->data);
1267
+ if (j) {
1268
+ cJSON *name = cJSON_GetObjectItem(j, "name");
1269
+ cJSON *desc = cJSON_GetObjectItem(j, "desc");
1270
+ if (cJSON_IsString(name) && cJSON_IsString(desc)) {
1271
+ printf(" %s: %s\n", name->valuestring, desc->valuestring);
1272
+ count++;
1273
+ }
1274
+ cJSON_Delete(j);
1275
+ }
1276
+ }
1277
+ b = b->next;
1278
+ }
1279
+ if (count == 0) printf(" None\n");
1280
+ } else if (strcmp(line, "/compact") == 0) {
1281
+ compact_arena();
1282
+ sync_soul_file(soul_filename);
1283
+ } else if (strcmp(line, "/soul") == 0) {
1284
+ struct stat st;
1285
+ if (stat(soul_filename, &st) == 0) {
1286
+ char timebuf[128];
1287
+ struct tm *tm_info = localtime(&st.st_mtime);
1288
+ strftime(timebuf, sizeof(timebuf), "%c", tm_info);
1289
+ printf("Soul file: %s (%ld bytes, last modified: %s\n", soul_filename, st.st_size, timebuf);
1290
+ } else {
1291
+ printf("Soul file not found.\n");
1292
+ }
1293
+ } else if (strcmp(line, "/reconnect") == 0) {
1294
+ if (ollama_healthy()) {
1295
+ no_llm_mode = 0;
1296
+ printf("Reconnected to Ollama. LLM mode enabled.\n");
1297
+ } else {
1298
+ printf("Ollama still unreachable.\n");
1299
+ }
1300
+ } else {
1301
+ // Fallback to interpreter for other slash commands
1302
+ char *resp = interpret_command(line);
1303
+ printf("%s\n", resp);
1304
+ free(resp);
1305
+ }
1306
+ if (dirty) {
1307
+ save_state(state_filename);
1308
+ sync_soul_file(soul_filename);
1309
+ dirty = 0;
1310
+ }
1311
+ continue;
1312
+ }
1313
+
1314
+ if (no_llm_mode) {
1315
+ char *resp = interpret_command(line);
1316
+ printf(COLOR_BLUE "%s\n" COLOR_RESET, resp);
1317
+ free(resp);
1318
+ continue;
1319
+ }
1320
+
1321
+ blob_append(BLOB_USER, line);
1322
+ sync_soul_file(soul_filename);
1323
+ int max_iter = 3;
1324
+ char *last_response = NULL;
1325
+ for (int iter = 0; iter < max_iter; iter++) {
1326
+ char *prompt = build_simple_prompt(line);
1327
+ char *llm_out = call_llm(prompt);
1328
+ free(prompt);
1329
+ if (!llm_out) {
1330
+ printf("LLM call failed. Try again or use /reconnect.\n");
1331
+ break;
1332
+ }
1333
+ char *tool_json = extract_tool_json(llm_out);
1334
+ if (!tool_json) {
1335
+ // final response
1336
+ printf(COLOR_BLUE "Assistant: %s\n" COLOR_RESET, llm_out);
1337
+ blob_append(BLOB_ASSISTANT, llm_out);
1338
+ last_response = llm_out;
1339
+ break;
1340
+ }
1341
+ char *tool_name = NULL, *tool_args = NULL;
1342
+ if (parse_tool_call(tool_json, &tool_name, &tool_args)) {
1343
+ if (dry_run) {
1344
+ printf(COLOR_YELLOW "[DRY RUN] would execute %s(%s)\n" COLOR_RESET, tool_name, tool_args);
1345
+ } else {
1346
+ char *result = execute_tool(tool_name, tool_args);
1347
+ printf(COLOR_YELLOW "\n[Tool %s] β†’ %s\n" COLOR_RESET, tool_name, result);
1348
+ blob_append(BLOB_TOOL_CALL, tool_json);
1349
+ blob_append(BLOB_TOOL_RESULT, result);
1350
+ trigger_webhooks(tool_name, result);
1351
+ // feed result back to LLM and continue loop
1352
+ char *next_prompt;
1353
+ asprintf(&next_prompt, "Tool result: %s\nContinue.", result);
1354
+ free(llm_out);
1355
+ llm_out = call_llm(next_prompt);
1356
+ free(next_prompt);
1357
+ free(result);
1358
+ if (!llm_out) break;
1359
+ // use llm_out as next response candidate, loop continues
1360
+ free(tool_json);
1361
+ free(tool_name);
1362
+ free(tool_args);
1363
+ continue;
1364
+ }
1365
+ } else {
1366
+ printf(COLOR_RED "Invalid tool JSON: %s\n" COLOR_RESET, tool_json);
1367
+ blob_append(BLOB_ASSISTANT, llm_out);
1368
+ last_response = llm_out;
1369
+ }
1370
+ free(tool_json);
1371
+ free(tool_name);
1372
+ free(tool_args);
1373
+ break;
1374
+ }
1375
+ if (last_response) free(last_response);
1376
+ save_state(state_filename);
1377
+ sync_soul_file(soul_filename);
1378
+ }
1379
+
1380
+ save_state(state_filename);
1381
+ curl_global_cleanup();
1382
+ return 0;
1383
+ }
start.sh ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Shadowclaw Startup Script v3.2+ (Fixed)
3
+ # Location: /home/kali/shadowclaw-v2/start.sh
4
+
5
+ set -e
6
+
7
+ # ---------- Configuration ----------
8
+ PROGRAM_NAME="shadowclaw"
9
+ PROGRAM_PATH="./$PROGRAM_NAME"
10
+ STATE_FILE="shadowclaw.bin"
11
+ DATA_DIR="shadowclaw_data"
12
+ SOUL_FILE="$DATA_DIR/shadowsoul.md"
13
+
14
+ # Ollama settings (can be overridden by environment)
15
+ OLLAMA_ENDPOINT="${OLLAMA_ENDPOINT:-http://localhost:11434}"
16
+ OLLAMA_MODEL="${OLLAMA_MODEL:-qwen2.5:0.5b}"
17
+
18
+ # Colors for output
19
+ RED='\033[0;31m'
20
+ GREEN='\033[0;32m'
21
+ YELLOW='\033[0;33m'
22
+ CYAN='\033[0;36m'
23
+ BOLD='\033[1m'
24
+ NC='\033[0m' # No Color
25
+
26
+ # ---------- Helper Functions ----------
27
+ print_status() { echo -e "${CYAN}[*]${NC} $1"; }
28
+ print_success() { echo -e "${GREEN}[βœ“]${NC} $1"; }
29
+ print_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
30
+ print_error() { echo -e "${RED}[βœ—]${NC} $1"; }
31
+
32
+ # ---------- Environment Variables for Timeouts ----------
33
+ export SHADOWCLAW_CONNECT_TIMEOUT="${SHADOWCLAW_CONNECT_TIMEOUT:-15}"
34
+ export SHADOWCLAW_TOTAL_TIMEOUT="${SHADOWCLAW_TOTAL_TIMEOUT:-180}"
35
+ export SHADOWCLAW_RETRY_ATTEMPTS="${SHADOWCLAW_RETRY_ATTEMPTS:-5}"
36
+
37
+ print_status "Timeout settings: connect=${SHADOWCLAW_CONNECT_TIMEOUT}s, total=${SHADOWCLAW_TOTAL_TIMEOUT}s, retries=${SHADOWCLAW_RETRY_ATTEMPTS}"
38
+
39
+ # ---------- Dependency Checks ----------
40
+ print_status "Checking dependencies..."
41
+
42
+ if ! command -v curl &> /dev/null; then
43
+ print_error "curl not found. Please install curl (sudo apt install curl)."
44
+ exit 1
45
+ fi
46
+
47
+ # ---------- Ollama Availability & Model Check ----------
48
+ NO_LLM_MODE=0
49
+ FORCE_LLM=0
50
+
51
+ for arg in "$@"; do
52
+ if [ "$arg" = "--force-llm" ]; then
53
+ FORCE_LLM=1
54
+ fi
55
+ done
56
+
57
+ check_ollama() {
58
+ if ! curl -s --max-time 2 "$OLLAMA_ENDPOINT/api/tags" > /dev/null 2>&1; then
59
+ return 1
60
+ fi
61
+ if ! curl -s "$OLLAMA_ENDPOINT/api/tags" 2>/dev/null | grep -q "\"name\":\"$OLLAMA_MODEL\""; then
62
+ return 2
63
+ fi
64
+ return 0
65
+ }
66
+
67
+ if [ $FORCE_LLM -eq 1 ]; then
68
+ print_warning "--force-llm flag set: will attempt to use LLM even if checks fail."
69
+ NO_LLM_MODE=0
70
+ else
71
+ check_ollama
72
+ case $? in
73
+ 0)
74
+ print_success "Ollama is reachable and model '$OLLAMA_MODEL' is available."
75
+ ;;
76
+ 1)
77
+ print_warning "Ollama not reachable at $OLLAMA_ENDPOINT. Auto-enabling --no-llm mode."
78
+ NO_LLM_MODE=1
79
+ ;;
80
+ 2)
81
+ print_warning "Model '$OLLAMA_MODEL' not found in Ollama. Auto-enabling --no-llm mode."
82
+ print_status "Pull it with: ollama pull $OLLAMA_MODEL"
83
+ NO_LLM_MODE=1
84
+ ;;
85
+ esac
86
+ fi
87
+
88
+ # ---------- Binary Existence & Permissions ----------
89
+ cd "$(dirname "$0")" || { print_error "Failed to change to script directory."; exit 1; }
90
+
91
+ if [ ! -f "$PROGRAM_PATH" ]; then
92
+ print_error "Binary '$PROGRAM_NAME' not found in current directory."
93
+ print_status "Compiling Shadowclaw..."
94
+ if [ -f "shadowclaw.c" ] && [ -f "interpreter.c" ] && [ -f "cJSON.c" ]; then
95
+ make clean 2>/dev/null || true
96
+ if make; then
97
+ print_success "Compilation successful."
98
+ else
99
+ print_error "Compilation failed. Check dependencies (libcurl, libpthread, readline)."
100
+ exit 1
101
+ fi
102
+ else
103
+ print_error "Source files missing. Please ensure shadowclaw.c, interpreter.c, cJSON.c are present."
104
+ exit 1
105
+ fi
106
+ fi
107
+
108
+ if [ ! -x "$PROGRAM_PATH" ]; then
109
+ print_warning "Binary not executable. Fixing permissions..."
110
+ chmod +x "$PROGRAM_PATH"
111
+ fi
112
+
113
+ # ---------- Environment Setup ----------
114
+ if [ ! -d "$DATA_DIR" ]; then
115
+ mkdir -p "$DATA_DIR"
116
+ print_success "Created data directory: $DATA_DIR"
117
+ fi
118
+
119
+ # ---------- Display Information (Full ASCII Banner) ----------
120
+ clear
121
+ echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}"
122
+ echo -e "${BOLD}β•‘ ____ _ _ _ β•‘${NC}"
123
+ echo -e "${BOLD}β•‘ / ___|| |__ __ _ __| | _____ _____| | __ ___ __ β•‘${NC}"
124
+ echo -e "${BOLD}β•‘ \___ \| '_ \ / _\` |/ _\` |/ _ \ \ /\ / / __| |/ _\` \ \ /\ / / β•‘${NC}"
125
+ echo -e "${BOLD}β•‘ ___) | | | | (_| | (_| | (_) \ V V / (__| | (_| |\ V V / β•‘${NC}"
126
+ echo -e "${BOLD}β•‘ |____/|_| |_|\__,_|\__,_|\___/ \_/\_/ \___|_|\__,_| \_/\_/ β•‘${NC}"
127
+ echo -e "${BOLD}β•‘ Shadowclaw AI Agent v3.4 β•‘${NC}"
128
+ echo -e "${BOLD}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${NC}"
129
+ echo ""
130
+ print_status "Working directory: $(pwd)"
131
+ print_status "State file: $STATE_FILE"
132
+ print_status "Soul memory: $SOUL_FILE"
133
+ if [ $NO_LLM_MODE -eq 1 ]; then
134
+ print_warning "Starting in NO-LLM mode (local interpreter only)."
135
+ else
136
+ print_success "LLM mode enabled (model: $OLLAMA_MODEL)."
137
+ fi
138
+ echo ""
139
+
140
+ # ---------- Build final argument list ----------
141
+ ARGS=()
142
+ if [ $NO_LLM_MODE -eq 1 ]; then
143
+ ARGS+=("--no-llm")
144
+ fi
145
+ for arg in "$@"; do
146
+ if [ "$arg" != "--force-llm" ]; then
147
+ ARGS+=("$arg")
148
+ fi
149
+ done
150
+
151
+ # ---------- Launch ----------
152
+ print_success "Launching Shadowclaw..."
153
+ exec "$PROGRAM_PATH" "${ARGS[@]}"