1 /* sdb - MIT - Copyright 2012-2021 - pancake */
2 
3 #include <stdarg.h>
4 #include "sdb.h"
5 #include "json/rangstr.c"
6 #include "json/js0n.c"
7 #include "json/path.c"
8 #include "json/api.c"
9 #include "json/indent.c"
10 
sdb_json_get_str(const char * json,const char * path)11 SDB_API char *sdb_json_get_str (const char *json, const char *path) {
12 	Rangstr rs = json_get (json, path);
13 	return rangstr_dup (&rs);
14 }
15 
sdb_json_get_bool(const char * json,const char * path)16 SDB_API bool sdb_json_get_bool(const char *json, const char *path) {
17 	Rangstr rs = json_get (json, path);
18 	const char *p = rs.p + rs.f;
19 	return (rangstr_length (&rs) == 4 && !strncmp (p, "true", 4));
20 }
21 
sdb_json_get(Sdb * s,const char * k,const char * p,ut32 * cas)22 SDB_API char *sdb_json_get(Sdb *s, const char *k, const char *p, ut32 *cas) {
23 	Rangstr rs;
24 	char *u, *v = sdb_get (s, k, cas);
25 	if (!v) {
26 		return NULL;
27 	}
28 	rs = json_get (v, p);
29 	u = rangstr_dup (&rs);
30 	free (v);
31 	return u;
32 }
33 
sdb_json_num_inc(Sdb * s,const char * k,const char * p,int n,ut32 cas)34 SDB_API int sdb_json_num_inc(Sdb *s, const char *k, const char *p, int n, ut32 cas) {
35 	ut32 c;
36 	int cur = sdb_json_num_get (s, k, p, &c);
37 	if (cas && c != cas) {
38 		return 0;
39 	}
40 	sdb_json_num_set (s, k, p, cur + n, cas);
41 	return cur + n;
42 }
43 
sdb_json_num_dec(Sdb * s,const char * k,const char * p,int n,ut32 cas)44 SDB_API int sdb_json_num_dec(Sdb *s, const char *k, const char *p, int n, ut32 cas) {
45 	ut32 c;
46 	int cur = sdb_json_num_get (s, k, p, &c);
47 	if (cas && c != cas) {
48 		return 0;
49 	}
50 	sdb_json_num_set (s, k, p, cur - n, cas);
51 	return cur - n;
52 }
53 
sdb_json_num_get(Sdb * s,const char * k,const char * p,ut32 * cas)54 SDB_API int sdb_json_num_get(Sdb *s, const char *k, const char *p, ut32 *cas) {
55 	char *v = sdb_get (s, k, cas);
56 	if (v) {
57 		Rangstr rs = json_get (v, p);
58 		int ret = rangstr_int (&rs);
59 		free (v);
60 		return ret;
61 	}
62 	return 0;
63 }
64 
findkey(Rangstr * rs)65 static int findkey(Rangstr *rs) {
66 	int i;
67 	for (i = rs->f; i > 0; i--) {
68 		// Find the quote after the key
69 		if (rs->p[i] == '"') {
70 			for (--i; i > 0; i--) {
71 				// Find the quote before the key
72 				if (rs->p[i] == '"') {
73 					return i;
74 				}
75 			}
76 		}
77 	}
78 	return -1;
79 }
80 
isstring(const char * s)81 static bool isstring(const char *s) {
82 	if (!strcmp (s, "true")) {
83 		return false;
84 	}
85 	if (!strcmp (s, "false")) {
86 		return false;
87 	}
88 	for (; *s; s++) {
89 		if (*s < '0' || *s > '9') {
90 			return true;
91 		}
92 	}
93 	return false;
94 }
95 
96 // JSON only supports base16 numbers
sdb_json_num_set(Sdb * s,const char * k,const char * p,int v,ut32 cas)97 SDB_API int sdb_json_num_set(Sdb *s, const char *k, const char *p, int v, ut32 cas) {
98 	char *_str, str[64];
99 	_str = sdb_itoa (v, str, 10);
100 	return sdb_json_set (s, k, p, _str, cas);
101 }
102 
sdb_json_unset(Sdb * s,const char * k,const char * p,ut32 cas)103 SDB_API int sdb_json_unset(Sdb *s, const char *k, const char *p, ut32 cas) {
104 	return sdb_json_set (s, k, p, NULL, cas);
105 }
106 
sdb_json_set(Sdb * s,const char * k,const char * p,const char * v,ut32 cas)107 SDB_API bool sdb_json_set(Sdb *s, const char *k, const char *p, const char *v, ut32 cas) {
108 	int l, idx, len[3], jslen = 0;
109 	char *b, *str = NULL;
110 	const char *beg[3];
111 	const char *end[3];
112 	const char *js;
113 	Rangstr rs;
114 	ut32 c;
115 
116 	if (!s || !k || !v) {
117 		return false;
118 	}
119 	js = sdb_const_get_len (s, k, &jslen, &c);
120 	if (!js) {
121 		const int v_len = strlen (v);
122 		const int p_len = strlen (p);
123 		b = malloc (p_len + v_len + 8);
124 		if (b) {
125 			int is_str = isstring (v);
126 			const char *q = is_str? "\"": "";
127 			sprintf (b, "{\"%s\":%s%s%s}", p, q, v, q);
128 #if 0
129 			/* disabled because it memleaks */
130 			sdb_set_owned (s, k, b, cas);
131 #else
132 			sdb_set (s, k, b, cas);
133 			free (b);
134 #endif
135 			return true;
136 		}
137 		return false;
138 	}
139 	jslen++;
140 	if (cas && c != cas) {
141 		return false;
142 	}
143 	rs = json_get (js, p);
144 	if (!rs.p) {
145 		// jslen already comprehends the NULL-terminator and is
146 		// ensured to be positive by sdb_const_get_len
147 		// 7 corresponds to the length of '{"":"",'
148 		size_t buf_len = jslen + strlen (p) + strlen (v) + 7;
149 		char *buf = malloc (buf_len);
150 		if (buf) {
151 			int curlen, is_str = isstring (v);
152 			const char *quote = is_str ? "\"" : "";
153 			const char *end = ""; // XX: or comma
154 			if (js[0] && js[1] != '}') {
155 				end = ",";
156 			}
157 			curlen = sprintf (buf, "{\"%s\":%s%s%s%s",
158 				p, quote, v, quote, end);
159 			strcpy (buf + curlen, js + 1);
160 			// transfer ownership
161 			sdb_set_owned (s, k, buf, cas);
162 			return true;
163 		}
164 		// invalid json?
165 		return false;
166 	}
167 
168 	// rs.p and js point to the same memory location
169 	beg[0] = js;
170 	end[0] = rs.p + rs.f;
171 	len[0] = WLEN (0);
172 
173 	if (*v) {
174 		beg[1] = v;
175 		end[1] = v + strlen (v);
176 		len[1] = WLEN (1);
177 	}
178 
179 	beg[2] = rs.p + rs.t;
180 	end[2] = js + jslen;
181 	len[2] = WLEN (2);
182 
183 	// TODO: accelerate with small buffer in stack for small jsons
184 	if (*v) {
185 		int is_str = isstring (v);
186 		// 2 is the maximum amount of quotes that can be inserted
187 		int msz = len[0] + len[1] + len[2] + strlen (v) + 2;
188 		if (msz < 1) {
189 			return false;
190 		}
191 		str = malloc (msz);
192 		if (!str) {
193 			return false;
194 		}
195 		idx = len[0];
196 		memcpy (str, beg[0], idx);
197 		if (is_str) {
198 			if (beg[2][0] != '"') {
199 				str[idx] = '"';
200 				idx++;
201 			}
202 		} else {
203 			if (beg[2][0] == '"') {
204 				beg[2]++;
205 				len[2]--;
206 			}
207 		}
208 		l = len[1];
209 		memcpy (str + idx, beg[1], l);
210 		idx += len[1];
211 		if (is_str) {
212 			// TODO: add quotes
213 			if (beg[2][0] != '"') {
214 				str[idx] = '"';
215 				idx++;
216 			}
217 		} else {
218 			if (beg[2][0] == '"') {
219 				beg[2]++;
220 				len[2]--;
221 			}
222 		}
223 		l = len[2];
224 		memcpy (str + idx, beg[2], l);
225 		str[idx + l] = 0;
226 	} else {
227 		int kidx;
228 		// DELETE KEY
229 		rs.f -= 2;
230 		kidx = findkey (&rs);
231 		len[0] = R_MAX (1, kidx - 1);
232 
233 		// Delete quote if deleted value was a string
234 		if (beg[2][0] == '"') {
235 			beg[2]++;
236 			len[2]--;
237 		}
238 
239 		// If not the last key, delete comma
240 		if (len[2] != 2) {
241 			beg[2]++;
242 			len[2]--;
243 		}
244 
245 		str = malloc (len[0] + len[2] + 1);
246 		if (!str) {
247 			return false;
248 		}
249 
250 		memcpy (str, beg[0], len[0]);
251 		memcpy (str + len[0], beg[2], len[2]);
252 		str[len[0] + len[2]] = 0;
253 	}
254 	sdb_set_owned (s, k, str, cas);
255 	return true;
256 }
257 
sdb_json_format(SdbJsonString * s,const char * fmt,...)258 SDB_API const char *sdb_json_format(SdbJsonString *s, const char *fmt, ...) {
259 	char *arg_s, *x, tmp[128];
260 	ut64 arg_l;
261 	int i, arg_i;
262 	double arg_f;
263 	va_list ap;
264 #define JSONSTR_ALLOCATE(y)\
265 	if (s->len + y > s->blen) {\
266 		s->blen *= 2;\
267 		x = realloc (s->buf, s->blen);\
268 		if (!x) {\
269 			va_end (ap);\
270 			return NULL;\
271 		}\
272 		s->buf = x;\
273 	}
274 	if (!s) {
275 		return NULL;
276 	}
277 	if (!s->buf) {
278 		s->blen = 1024;
279 		s->buf = malloc (s->blen);
280 		if (!s->buf) {
281 			return NULL;
282 		}
283 		*s->buf = 0;
284 	}
285 	if (!fmt || !*fmt) {
286 		return s->buf;
287 	}
288 	va_start (ap, fmt);
289 	for (; *fmt; fmt++) {
290 		if (*fmt == '%') {
291 			fmt++;
292 			switch (*fmt) {
293 			case 'b':
294 				JSONSTR_ALLOCATE (32);
295 				arg_i = va_arg (ap, int);
296 				arg_i = arg_i? 4: 5;
297 				memcpy (s->buf + s->len, (arg_i == 4)? "true": "false", 5);
298 				s->len += arg_i;
299 				break;
300 			case 'f':
301 				JSONSTR_ALLOCATE (32);
302 				arg_f = va_arg (ap, double);
303 				snprintf (tmp, sizeof (tmp), "%f", arg_f);
304 				memcpy (s->buf + s->len, tmp, strlen (tmp));
305 				s->len += strlen (tmp);
306 				break;
307 			case 'l':
308 				JSONSTR_ALLOCATE (32);
309 				arg_l = va_arg (ap, ut64);
310 				snprintf (tmp, sizeof (tmp), "0x%"ULLFMT "x", arg_l);
311 				memcpy (s->buf + s->len, tmp, strlen (tmp));
312 				s->len += strlen (tmp);
313 				break;
314 			case 'd':
315 			case 'i':
316 				JSONSTR_ALLOCATE (32);
317 				arg_i = va_arg (ap, int);
318 				snprintf (tmp, sizeof (tmp), "%d", arg_i);
319 				memcpy (s->buf + s->len, tmp, strlen (tmp));
320 				s->len += strlen (tmp);
321 				break;
322 			case 's':
323 				arg_s = va_arg (ap, char *);
324 				JSONSTR_ALLOCATE (strlen (arg_s) + 3);
325 				s->buf[s->len++] = '"';
326 				for (i = 0; arg_s[i]; i++) {
327 					if (arg_s[i] == '"') {
328 						s->buf[s->len++] = '\\';
329 					}
330 					s->buf[s->len++] = arg_s[i];
331 				}
332 				s->buf[s->len++] = '"';
333 				break;
334 			}
335 		} else {
336 			JSONSTR_ALLOCATE (10);
337 			s->buf[s->len++] = *fmt;
338 		}
339 		s->buf[s->len] = 0;
340 	}
341 	va_end (ap);
342 	return s->buf;
343 }
344 
345 #if 0
346 int main () {
347 	SdbJsonString s = {
348 		0
349 	};
350 	sdb_json_format (&s, "[{%s:%d},%b]", "Hello \"world\"", 1024, 3);
351 	printf ("%s\n", sdb_json_format (&s, 0));
352 	sdb_json_format_free (&s);
353 	return 0;
354 }
355 #endif
356