xref: /openbsd-src/usr.sbin/bgpctl/json.c (revision db68842d154ba8ae8decd12f838983aba1366e92)
1 /*	$OpenBSD: json.c,v 1.6 2023/04/26 20:53:17 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 static struct json_stack {
37 	const char	*name;
38 	unsigned int	count;
39 	enum json_type	type;
40 } stack[JSON_MAX_STACK];
41 
42 static char indent[JSON_MAX_STACK + 1];
43 static int level;
44 static int eb;
45 static FILE *jsonfh;
46 
47 static void
48 do_comma_indent(void)
49 {
50 	if (stack[level].count++ > 0)
51 		if (!eb)
52 			eb = fprintf(jsonfh, ",\n") == -1;
53 	if (!eb)
54 		eb = fprintf(jsonfh, "\t%.*s", level, indent) == -1;
55 }
56 
57 static void
58 do_name(const char *name)
59 {
60 	if (stack[level].type == ARRAY)
61 		return;
62 	if (!eb)
63 		eb = fprintf(jsonfh, "\"%s\": ", name) == -1;
64 }
65 
66 static int
67 do_find(enum json_type type, const char *name)
68 {
69 	int i;
70 
71 	for (i = level; i > 0; i--)
72 		if (type == stack[i].type &&
73 		    strcmp(name, stack[i].name) == 0)
74 			return i;
75 
76 	/* not found */
77 	return -1;
78 }
79 
80 void
81 json_do_start(FILE *fh)
82 {
83 	memset(indent, '\t', JSON_MAX_STACK);
84 	memset(stack, 0, sizeof(stack));
85 	level = 0;
86 	stack[level].type = START;
87 	jsonfh = fh;
88 	eb = 0;
89 
90 	eb = fprintf(jsonfh, "{\n") == -1;
91 }
92 
93 int
94 json_do_finish(void)
95 {
96 	while (level > 0)
97 		json_do_end();
98 	if (!eb)
99 		eb = fprintf(jsonfh, "\n}\n") == -1;
100 
101 	return -eb;
102 }
103 
104 void
105 json_do_array(const char *name)
106 {
107 	int i, l;
108 
109 	if ((l = do_find(ARRAY, name)) > 0) {
110 		/* array already in use, close element and move on */
111 		for (i = level - l; i > 0; i--)
112 			json_do_end();
113 		return;
114 	}
115 	/* Do not stack arrays, while allowed this is not needed */
116 	if (stack[level].type == ARRAY)
117 		json_do_end();
118 
119 	do_comma_indent();
120 	do_name(name);
121 	if (!eb)
122 		eb = fprintf(jsonfh, "[\n") == -1;
123 
124 	if (++level >= JSON_MAX_STACK)
125 		errx(1, "json stack too deep");
126 
127 	stack[level].name = name;
128 	stack[level].type = ARRAY;
129 	stack[level].count = 0;
130 }
131 
132 void
133 json_do_object(const char *name)
134 {
135 	int i, l;
136 
137 	if ((l = do_find(OBJECT, name)) > 0) {
138 		/* roll back to that object and close it */
139 		for (i = level - l; i >= 0; i--)
140 			json_do_end();
141 	}
142 
143 	do_comma_indent();
144 	do_name(name);
145 	if (!eb)
146 		eb = fprintf(jsonfh, "{\n") == -1;
147 
148 	if (++level >= JSON_MAX_STACK)
149 		errx(1, "json stack too deep");
150 
151 	stack[level].name = name;
152 	stack[level].type = OBJECT;
153 	stack[level].count = 0;
154 }
155 
156 void
157 json_do_end(void)
158 {
159 	if (stack[level].type == ARRAY) {
160 		if (!eb)
161 			eb = fprintf(jsonfh, "\n%.*s]", level, indent) == -1;
162 	} else if (stack[level].type == OBJECT) {
163 		if (!eb)
164 			eb = fprintf(jsonfh, "\n%.*s}", level, indent) == -1;
165 	} else {
166 		errx(1, "json bad stack state");
167 	}
168 	stack[level].name = NULL;
169 	stack[level].type = NONE;
170 	stack[level].count = 0;
171 
172 	if (level-- <= 0)
173 		errx(1, "json stack underflow");
174 
175 	stack[level].count++;
176 }
177 
178 void
179 json_do_printf(const char *name, const char *fmt, ...)
180 {
181 	va_list ap;
182 
183 	do_comma_indent();
184 
185 	do_name(name);
186 	if (!eb)
187 		eb = fprintf(jsonfh, "\"") == -1;
188 	va_start(ap, fmt);
189 	if (!eb)
190 		eb = vfprintf(jsonfh, fmt, ap) == -1;
191 	va_end(ap);
192 	if (!eb)
193 		eb = fprintf(jsonfh, "\"") == -1;
194 }
195 
196 void
197 json_do_hexdump(const char *name, void *buf, size_t len)
198 {
199 	uint8_t *data = buf;
200 	size_t i;
201 
202 	do_comma_indent();
203 	do_name(name);
204 	if (!eb)
205 		eb = fprintf(jsonfh, "\"") == -1;
206 	for (i = 0; i < len; i++)
207 		if (!eb)
208 			eb = fprintf(jsonfh, "%02x", *(data + i)) == -1;
209 	if (!eb)
210 		eb = fprintf(jsonfh, "\"") == -1;
211 }
212 
213 void
214 json_do_bool(const char *name, int v)
215 {
216 	do_comma_indent();
217 	do_name(name);
218 	if (v) {
219 		if (!eb)
220 			eb = fprintf(jsonfh, "true") == -1;
221 	} else {
222 		if (!eb)
223 			eb = fprintf(jsonfh, "false") == -1;
224 	}
225 }
226 
227 void
228 json_do_uint(const char *name, unsigned long long v)
229 {
230 	do_comma_indent();
231 	do_name(name);
232 	if (!eb)
233 		eb = fprintf(jsonfh, "%llu", v) == -1;
234 }
235 
236 void
237 json_do_int(const char *name, long long v)
238 {
239 	do_comma_indent();
240 	do_name(name);
241 	if (!eb)
242 		eb = fprintf(jsonfh, "%lld", v) == -1;
243 }
244 
245 void
246 json_do_double(const char *name, double v)
247 {
248 	do_comma_indent();
249 	do_name(name);
250 	if (!eb)
251 		eb = fprintf(jsonfh, "%f", v) == -1;
252 }
253