1 /* 2 * Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 #include <inttypes.h> 17 #include "blobmsg.h" 18 #include "blobmsg_json.h" 19 20 #include <json.h> 21 22 bool blobmsg_add_object(struct blob_buf *b, json_object *obj) 23 { 24 json_object_object_foreach(obj, key, val) { 25 if (!blobmsg_add_json_element(b, key, val)) 26 return false; 27 } 28 return true; 29 } 30 31 static bool blobmsg_add_array(struct blob_buf *b, struct array_list *a) 32 { 33 size_t i, len; 34 35 for (i = 0, len = array_list_length(a); i < len; i++) { 36 if (!blobmsg_add_json_element(b, NULL, array_list_get_idx(a, i))) 37 return false; 38 } 39 40 return true; 41 } 42 43 bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object *obj) 44 { 45 bool ret = true; 46 void *c; 47 48 switch (json_object_get_type(obj)) { 49 case json_type_object: 50 c = blobmsg_open_table(b, name); 51 ret = blobmsg_add_object(b, obj); 52 blobmsg_close_table(b, c); 53 break; 54 case json_type_array: 55 c = blobmsg_open_array(b, name); 56 ret = blobmsg_add_array(b, json_object_get_array(obj)); 57 blobmsg_close_array(b, c); 58 break; 59 case json_type_string: 60 blobmsg_add_string(b, name, json_object_get_string(obj)); 61 break; 62 case json_type_boolean: 63 blobmsg_add_u8(b, name, json_object_get_boolean(obj)); 64 break; 65 case json_type_int: { 66 int64_t i64 = json_object_get_int64(obj); 67 if (i64 >= INT32_MIN && i64 <= INT32_MAX) { 68 blobmsg_add_u32(b, name, (uint32_t)i64); 69 } else { 70 blobmsg_add_u64(b, name, (uint64_t)i64); 71 } 72 break; 73 } 74 case json_type_double: 75 blobmsg_add_double(b, name, json_object_get_double(obj)); 76 break; 77 case json_type_null: 78 blobmsg_add_field(b, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); 79 break; 80 default: 81 return false; 82 } 83 return ret; 84 } 85 86 static bool __blobmsg_add_json(struct blob_buf *b, json_object *obj) 87 { 88 bool ret = false; 89 90 if (!obj) 91 return false; 92 93 if (json_object_get_type(obj) != json_type_object) 94 goto out; 95 96 ret = blobmsg_add_object(b, obj); 97 98 out: 99 json_object_put(obj); 100 return ret; 101 } 102 103 bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file) 104 { 105 return __blobmsg_add_json(b, json_object_from_file(file)); 106 } 107 108 bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str) 109 { 110 return __blobmsg_add_json(b, json_tokener_parse(str)); 111 } 112 113 114 struct strbuf { 115 size_t len; 116 size_t pos; 117 char *buf; 118 119 blobmsg_json_format_t custom_format; 120 void *priv; 121 bool indent; 122 int indent_level; 123 }; 124 125 /* 126 * Minimum and growth slack for the JSON strbuf. The minimum size 127 * keeps malloc(0) out of setup_strbuf() and is large enough to hold 128 * any short scalar serialisation ("null", "true", "false", small 129 * numbers) without an immediate realloc. The same value is added on 130 * each grow in blobmsg_puts() so that successive small writes are 131 * amortised across a few extra bytes per realloc. 132 */ 133 #define STRBUF_MIN_SIZE 16 134 135 static bool blobmsg_puts(struct strbuf *s, const char *c, size_t len) 136 { 137 size_t new_len; 138 char *new_buf; 139 140 if (!len) 141 return true; 142 143 if (s->len - s->pos <= len) { 144 if (len > SIZE_MAX - STRBUF_MIN_SIZE - s->len) 145 return false; 146 new_len = s->len + STRBUF_MIN_SIZE + len; 147 new_buf = realloc(s->buf, new_len); 148 if (!new_buf) 149 return false; 150 151 s->len = new_len; 152 s->buf = new_buf; 153 } 154 155 memcpy(s->buf + s->pos, c, len); 156 s->pos += len; 157 return true; 158 } 159 160 static void add_separator(struct strbuf *s) 161 { 162 const char indent_chars[] = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; 163 size_t len; 164 165 if (!s->indent) 166 return; 167 168 len = s->indent_level + 1; 169 if (len > sizeof(indent_chars) - 1) 170 len = sizeof(indent_chars) - 1; 171 172 blobmsg_puts(s, indent_chars, len); 173 } 174 175 176 static void blobmsg_format_string(struct strbuf *s, const char *str) 177 { 178 const unsigned char *p, *last, *end; 179 char buf[8] = "\\u00"; 180 181 end = (unsigned char *) str + strlen(str); 182 blobmsg_puts(s, "\"", 1); 183 for (p = (unsigned char *) str, last = p; *p; p++) { 184 char escape = '\0'; 185 size_t len; 186 187 switch(*p) { 188 case '\b': 189 escape = 'b'; 190 break; 191 case '\n': 192 escape = 'n'; 193 break; 194 case '\t': 195 escape = 't'; 196 break; 197 case '\r': 198 escape = 'r'; 199 break; 200 case '"': 201 case '\\': 202 escape = *p; 203 break; 204 default: 205 if (*p < ' ') 206 escape = 'u'; 207 break; 208 } 209 210 if (!escape) 211 continue; 212 213 if (p > last) 214 blobmsg_puts(s, (char *) last, p - last); 215 last = p + 1; 216 buf[1] = escape; 217 218 if (escape == 'u') { 219 snprintf(buf + 4, sizeof(buf) - 4, "%02x", (unsigned char) *p); 220 len = 6; 221 } else { 222 len = 2; 223 } 224 blobmsg_puts(s, buf, len); 225 } 226 227 blobmsg_puts(s, (char *) last, end - last); 228 blobmsg_puts(s, "\"", 1); 229 } 230 231 static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, size_t len, bool array); 232 233 static void blobmsg_format_element(struct strbuf *s, struct blob_attr *attr, bool without_name, bool head) 234 { 235 const char *data_str; 236 char buf[317]; 237 void *data; 238 size_t len; 239 240 if (!blobmsg_check_attr(attr, false)) 241 return; 242 243 if (!without_name && blobmsg_name(attr)[0]) { 244 blobmsg_format_string(s, blobmsg_name(attr)); 245 blobmsg_puts(s, ": ", s->indent ? 2 : 1); 246 } 247 248 data = blobmsg_data(attr); 249 len = blobmsg_data_len(attr); 250 251 if (!head && s->custom_format) { 252 data_str = s->custom_format(s->priv, attr); 253 if (data_str) 254 goto out; 255 } 256 257 data_str = buf; 258 switch(blob_id(attr)) { 259 case BLOBMSG_TYPE_UNSPEC: 260 snprintf(buf, sizeof(buf), "null"); 261 break; 262 case BLOBMSG_TYPE_BOOL: 263 snprintf(buf, sizeof(buf), "%s", *(uint8_t *)data ? "true" : "false"); 264 break; 265 case BLOBMSG_TYPE_INT16: 266 snprintf(buf, sizeof(buf), "%" PRId16, (int16_t) be16_to_cpu(*(uint16_t *)data)); 267 break; 268 case BLOBMSG_TYPE_INT32: 269 snprintf(buf, sizeof(buf), "%" PRId32, (int32_t) be32_to_cpu(*(uint32_t *)data)); 270 break; 271 case BLOBMSG_TYPE_INT64: 272 snprintf(buf, sizeof(buf), "%" PRId64, (int64_t) be64_to_cpu(*(uint64_t *)data)); 273 break; 274 case BLOBMSG_TYPE_DOUBLE: 275 snprintf(buf, sizeof(buf), "%.17g", blobmsg_get_double(attr)); 276 break; 277 case BLOBMSG_TYPE_STRING: 278 blobmsg_format_string(s, data); 279 return; 280 case BLOBMSG_TYPE_ARRAY: 281 blobmsg_format_json_list(s, data, len, true); 282 return; 283 case BLOBMSG_TYPE_TABLE: 284 blobmsg_format_json_list(s, data, len, false); 285 return; 286 } 287 288 out: 289 blobmsg_puts(s, data_str, strlen(data_str)); 290 } 291 292 static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, size_t len, bool array) 293 { 294 struct blob_attr *pos; 295 bool first = true; 296 size_t rem = len; 297 298 blobmsg_puts(s, (array ? "[" : "{" ), 1); 299 s->indent_level++; 300 add_separator(s); 301 __blob_for_each_attr(pos, attr, rem) { 302 if (!first) { 303 blobmsg_puts(s, ",", 1); 304 add_separator(s); 305 } 306 307 blobmsg_format_element(s, pos, array, false); 308 first = false; 309 } 310 s->indent_level--; 311 add_separator(s); 312 blobmsg_puts(s, (array ? "]" : "}"), 1); 313 } 314 315 static void setup_strbuf(struct strbuf *s, struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent) 316 { 317 s->len = blob_len(attr); 318 if (s->len < STRBUF_MIN_SIZE) 319 s->len = STRBUF_MIN_SIZE; 320 s->buf = malloc(s->len); 321 s->pos = 0; 322 s->custom_format = cb; 323 s->priv = priv; 324 s->indent = false; 325 326 if (indent >= 0) { 327 s->indent = true; 328 s->indent_level = indent; 329 } 330 } 331 332 char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, blobmsg_json_format_t cb, void *priv, int indent) 333 { 334 struct strbuf s = {0}; 335 bool array; 336 char *ret; 337 338 setup_strbuf(&s, attr, cb, priv, indent); 339 if (!s.buf) 340 return NULL; 341 342 array = blob_is_extended(attr) && 343 blobmsg_type(attr) == BLOBMSG_TYPE_ARRAY; 344 345 if (list) 346 blobmsg_format_json_list(&s, blobmsg_data(attr), blobmsg_data_len(attr), array); 347 else 348 blobmsg_format_element(&s, attr, false, false); 349 350 if (!s.pos) { 351 free(s.buf); 352 return NULL; 353 } 354 355 ret = realloc(s.buf, s.pos + 1); 356 if (!ret) { 357 free(s.buf); 358 return NULL; 359 } 360 361 ret[s.pos] = 0; 362 363 return ret; 364 } 365 366 char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent) 367 { 368 struct strbuf s = {0}; 369 char *ret; 370 371 setup_strbuf(&s, attr, cb, priv, indent); 372 if (!s.buf) 373 return NULL; 374 375 blobmsg_format_element(&s, attr, true, false); 376 377 if (!s.pos) { 378 free(s.buf); 379 return NULL; 380 } 381 382 ret = realloc(s.buf, s.pos + 1); 383 if (!ret) { 384 free(s.buf); 385 return NULL; 386 } 387 388 ret[s.pos] = 0; 389 390 return ret; 391 } 392
This page was automatically generated by LXR 0.3.1. • OpenWrt