]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/client/json_writer.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx>
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include <sys/queue.h>
28 #include "../compat/compat.h"
39 struct element
*parent
; /* Parent (if any) */
40 TAILQ_ENTRY(element
) next
; /* Sibling (if any) */
41 char *key
; /* Key if parent is an object */
42 enum tag tag
; /* Kind of element */
44 char *string
; /* STRING */
45 int boolean
; /* BOOL */
46 TAILQ_HEAD(, element
) children
; /* ARRAY or OBJECT */
50 struct json_writer_private
{
53 struct element
*current
; /* should always be an object */
56 /* Create a new element. If a parent is provided, it will also be attached to
58 static struct element
*
59 json_element_new(struct element
*parent
, const char *key
, enum tag tag
)
61 struct element
*child
= malloc(sizeof(*child
));
62 if (child
== NULL
) fatal(NULL
, NULL
);
63 child
->parent
= parent
;
64 child
->key
= key
?strdup(key
):NULL
;
66 TAILQ_INIT(&child
->children
);
67 if (parent
) TAILQ_INSERT_TAIL(&parent
->children
, child
, next
);
71 /* Free the element content (but not the element itself) */
73 json_element_free(struct element
*current
)
75 struct element
*el
, *el_next
;
76 switch (current
->tag
) {
78 free(current
->string
);
84 for (el
= TAILQ_FIRST(¤t
->children
);
87 el_next
= TAILQ_NEXT(el
, next
);
88 json_element_free(el
);
89 TAILQ_REMOVE(¤t
->children
, el
, next
);
90 if (current
->tag
== OBJECT
) free(el
->key
);
98 json_free(struct json_writer_private
*p
)
100 json_element_free(p
->root
);
105 json_string_dump(FILE *fh
, const char *s
)
112 case '"': fprintf(fh
, "\\\""); s
++; break;
113 case '\\': fprintf(fh
, "\\\\"); s
++; break;
114 case '\b': fprintf(fh
, "\\\b"); s
++; break;
115 case '\f': fprintf(fh
, "\\\f"); s
++; break;
116 case '\n': fprintf(fh
, "\\\n"); s
++; break;
117 case '\r': fprintf(fh
, "\\\r"); s
++; break;
118 case '\t': fprintf(fh
, "\\\t"); s
++; break;
120 len
= utf8_validate_cz(s
);
122 /* Not a valid UTF-8 char, use a
123 * replacement character */
124 fprintf(fh
, "\\uFFFD");
126 } else if (c
< 0x1f) {
127 /* 7-bit ASCII character */
128 fprintf(fh
, "\\u%04X", c
);
131 /* UTF-8, write as is */
132 while (len
--) fprintf(fh
, "%c", *s
++);
140 /* Dump an element to the specified file handle. */
142 json_element_dump(FILE *fh
, struct element
*current
, int indent
)
144 static const char pairs
[2][2] = { "{}", "[]" };
146 switch (current
->tag
) {
148 json_string_dump(fh
, current
->string
);
151 fprintf(fh
, current
->boolean
?"true":"false");
155 fprintf(fh
, "%c\n%*s", pairs
[(current
->tag
== ARRAY
)][0],
157 TAILQ_FOREACH(el
, ¤t
->children
, next
) {
158 if (current
->tag
== OBJECT
)
159 fprintf(fh
, "\"%s\": ", el
->key
);
160 json_element_dump(fh
, el
, indent
+ 2);
161 if (TAILQ_NEXT(el
, next
))
162 fprintf(fh
, ",\n%*s", indent
+ 2, "");
164 fprintf(fh
, "\n%*c", indent
+ 1,
165 pairs
[(current
->tag
== ARRAY
)][1]);
171 json_dump(struct json_writer_private
*p
)
173 json_element_dump(p
->fh
, p
->root
, 0);
174 fprintf(p
->fh
, "\n");
178 json_start(struct writer
*w
, const char *tag
,
181 struct json_writer_private
*p
= w
->priv
;
182 struct element
*child
;
185 /* Look for the tag in the current object. */
186 TAILQ_FOREACH(child
, &p
->current
->children
, next
) {
187 if (!strcmp(child
->key
, tag
)) break;
190 child
= json_element_new(p
->current
, tag
, ARRAY
);
192 /* Queue the new element. */
193 new = json_element_new(child
, NULL
, OBJECT
);
198 json_attr(struct writer
*w
, const char *tag
,
199 const char *descr
, const char *value
)
201 struct json_writer_private
*p
= w
->priv
;
202 struct element
*new = json_element_new(p
->current
, tag
, STRING
);
203 if (value
&& (!strcmp(value
, "yes") || !strcmp(value
, "on"))) {
206 } else if (value
&& (!strcmp(value
, "no") || !strcmp(value
, "off"))) {
210 new->string
= strdup(value
?value
:"");
215 json_data(struct writer
*w
, const char *data
)
217 struct json_writer_private
*p
= w
->priv
;
218 struct element
*new = json_element_new(p
->current
, "value", STRING
);
219 new->string
= strdup(data
?data
:"");
222 /* When an array has only one member, just remove the array. When an object has
223 * `value` as the only key, remove the object. Moreover, for an object, move the
224 * `name` key outside (inside a new object). This is a recursive function. We
225 * think the depth will be limited. Also, the provided element can be
226 * destroyed. Don't use it after this function!
228 * During the cleaning process, we will generate array of 1-size objects that
229 * could be turned into an object. We don't do that since people may rely on
230 * this format. Another problem is the format is changing depending on the
231 * number of interfaces or the number of neighbors.
234 json_element_cleanup(struct element
*el
)
237 struct element
*child
, *child_next
;
239 /* If array with one element, steal the content. Object with only one
240 * value whose key is "value", steal the content. */
241 if ((el
->tag
== ARRAY
|| el
->tag
== OBJECT
) &&
242 (child
= TAILQ_FIRST(&el
->children
)) &&
243 !TAILQ_NEXT(child
, next
) &&
244 (el
->tag
== ARRAY
|| !strcmp(child
->key
, "value"))) {
246 child
->key
= el
->key
;
247 child
->parent
= el
->parent
;
248 TAILQ_INSERT_BEFORE(el
, child
, next
);
249 TAILQ_REMOVE(&el
->parent
->children
, el
, next
);
251 json_element_cleanup(child
);
255 /* Other kind of arrays, recursively clean */
256 if (el
->tag
== ARRAY
) {
257 for (child
= TAILQ_FIRST(&el
->children
);
259 child
= child_next
) {
260 child_next
= TAILQ_NEXT(child
, next
);
261 json_element_cleanup(child
);
266 /* Other kind of objects, recursively clean, but if one key is "name",
267 * use it's value as a key for a new object stealing the existing
269 if (el
->tag
== OBJECT
) {
270 struct element
*name_child
= NULL
;
271 for (child
= TAILQ_FIRST(&el
->children
);
273 child
= child_next
) {
274 child_next
= TAILQ_NEXT(child
, next
);
275 json_element_cleanup(child
);
277 /* Redo a check to find if we have a "name" key now */
278 for (child
= TAILQ_FIRST(&el
->children
);
280 child
= child_next
) {
281 child_next
= TAILQ_NEXT(child
, next
);
282 if (!strcmp(child
->key
, "name") &&
283 child
->tag
== STRING
) {
288 struct element
*new_el
= json_element_new(NULL
, NULL
, OBJECT
);
289 /* Replace el by new_el in parent object/array */
290 new_el
->parent
= el
->parent
;
291 TAILQ_INSERT_BEFORE(el
, new_el
, next
);
292 TAILQ_REMOVE(&el
->parent
->children
, el
, next
);
293 new_el
->key
= el
->key
;
295 /* new_el is parent of el */
297 el
->key
= name_child
->string
; /* stolen */
298 TAILQ_INSERT_TAIL(&new_el
->children
, el
, next
);
300 /* Remove "name" child */
301 TAILQ_REMOVE(&el
->children
, name_child
, next
);
302 free(name_child
->key
);
311 json_cleanup(struct json_writer_private
*p
)
313 json_element_cleanup(p
->root
);
317 json_end(struct writer
*w
)
319 struct json_writer_private
*p
= w
->priv
;
320 while ((p
->current
= p
->current
->parent
) != NULL
&& p
->current
->tag
!= OBJECT
);
321 if (p
->current
== NULL
) {
322 fatalx("lldpctl", "unbalanced tags");
326 /* Display current object if last one */
327 if (p
->current
== p
->root
) {
333 p
->root
= p
->current
= json_element_new(NULL
, NULL
, OBJECT
);
338 json_finish(struct writer
*w
)
340 struct json_writer_private
*p
= w
->priv
;
341 if (p
->current
!= p
->root
)
342 log_warnx("lldpctl", "unbalanced tags");
351 struct writer
*result
;
352 struct json_writer_private
*priv
;
354 priv
= malloc(sizeof(*priv
));
355 if (priv
== NULL
) fatal(NULL
, NULL
);
358 priv
->root
= priv
->current
= json_element_new(NULL
, NULL
, OBJECT
);
360 result
= malloc(sizeof(*result
));
361 if (result
== NULL
) fatal(NULL
, NULL
);
364 result
->start
= json_start
;
365 result
->attr
= json_attr
;
366 result
->data
= json_data
;
367 result
->end
= json_end
;
368 result
->finish
= json_finish
;