• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/libubox/blobmsg_json.c

  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