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