xref: /openbsd-src/usr.sbin/bgpctl/json.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: json.c,v 1.3 2022/08/31 12:13:59 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <err.h>
20 #include <stdarg.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "json.h"
26 
27 #define JSON_MAX_STACK	16
28 
29 enum json_type {
30 	NONE,
31 	START,
32 	ARRAY,
33 	OBJECT
34 };
35 
36 struct json_stack {
37 	const char	*name;
38 	unsigned int	count;
39 	enum json_type	type;
40 } stack[JSON_MAX_STACK];
41 
42 char indent[JSON_MAX_STACK + 1];
43 int level;
44 
45 static void
46 do_comma_indent(void)
47 {
48 	if (stack[level].count++ > 0)
49 		printf(",\n");
50 	printf("\t%.*s", level, indent);
51 }
52 
53 static void
54 do_name(const char *name)
55 {
56 	if (stack[level].type == ARRAY)
57 		return;
58 	printf("\"%s\": ", name);
59 }
60 
61 static int
62 do_find(enum json_type type, const char *name)
63 {
64 	int i;
65 
66 	for (i = level; i > 0; i--)
67 		if (type == stack[i].type &&
68 		    strcmp(name, stack[i].name) == 0)
69 			return i;
70 
71 	/* not found */
72 	return -1;
73 }
74 
75 void
76 json_do_start(void)
77 {
78 	memset(indent, '\t', JSON_MAX_STACK);
79 	memset(stack, 0, sizeof(stack));
80 	level = 0;
81 	stack[level].type = START;
82 
83 	printf("{\n");
84 }
85 
86 void
87 json_do_finish(void)
88 {
89 	while (level > 0)
90 		json_do_end();
91 	printf("\n}\n");
92 }
93 
94 void
95 json_do_array(const char *name)
96 {
97 	int i, l;
98 
99 	if ((l = do_find(ARRAY, name)) > 0) {
100 		/* array already in use, close element and move on */
101 		for (i = level - l; i > 0; i--)
102 			json_do_end();
103 		return;
104 	}
105 	/* Do not stack arrays, while allowed this is not needed */
106 	if (stack[level].type == ARRAY)
107 		json_do_end();
108 
109 	do_comma_indent();
110 	do_name(name);
111 	printf("[\n");
112 
113 	if (++level >= JSON_MAX_STACK)
114 		errx(1, "json stack too deep");
115 
116 	stack[level].name = name;
117 	stack[level].type = ARRAY;
118 	stack[level].count = 0;
119 }
120 
121 void
122 json_do_object(const char *name)
123 {
124 	int i, l;
125 
126 	if ((l = do_find(OBJECT, name)) > 0) {
127 		/* roll back to that object and close it */
128 		for (i = level - l; i >= 0; i--)
129 			json_do_end();
130 	}
131 
132 	do_comma_indent();
133 	do_name(name);
134 	printf("{\n");
135 
136 	if (++level >= JSON_MAX_STACK)
137 		errx(1, "json stack too deep");
138 
139 	stack[level].name = name;
140 	stack[level].type = OBJECT;
141 	stack[level].count = 0;
142 }
143 
144 void
145 json_do_end(void)
146 {
147 	if (stack[level].type == ARRAY)
148 		printf("\n%.*s]", level, indent);
149 	else if (stack[level].type == OBJECT)
150 		printf("\n%.*s}", level, indent);
151 	else
152 		errx(1, "json bad stack state");
153 
154 	stack[level].name = NULL;
155 	stack[level].type = NONE;
156 	stack[level].count = 0;
157 
158 	if (level-- <= 0)
159 		errx(1, "json stack underflow");
160 
161 	stack[level].count++;
162 }
163 
164 void
165 json_do_printf(const char *name, const char *fmt, ...)
166 {
167 	va_list ap;
168 
169 	do_comma_indent();
170 
171 	do_name(name);
172 	printf("\"");
173 	va_start(ap, fmt);
174 	vprintf(fmt, ap);
175 	va_end(ap);
176 	printf("\"");
177 }
178 
179 void
180 json_do_hexdump(const char *name, void *buf, size_t len)
181 {
182 	uint8_t *data = buf;
183 	size_t i;
184 
185 	do_comma_indent();
186 	do_name(name);
187 	printf("\"");
188 	for (i=0; i < len; i++)
189 		printf("%02x", *(data+i));
190 	printf("\"");
191 }
192 
193 void
194 json_do_bool(const char *name, int v)
195 {
196 	do_comma_indent();
197 	do_name(name);
198 	if (v)
199 		printf("true");
200 	else
201 		printf("false");
202 }
203 
204 void
205 json_do_uint(const char *name, unsigned long long v)
206 {
207 	do_comma_indent();
208 	do_name(name);
209 	printf("%llu", v);
210 }
211 
212 void
213 json_do_int(const char *name, long long v)
214 {
215 	do_comma_indent();
216 	do_name(name);
217 	printf("%lld", v);
218 }
219 
220 void
221 json_do_double(const char *name, double v)
222 {
223 	do_comma_indent();
224 	do_name(name);
225 	printf("%f", v);
226 }
227