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