jcs's openbsd hax
openbsd
at jcs 325 lines 6.1 kB view raw
1/* $OpenBSD: json.c,v 1.11 2025/04/02 09:15:04 tb 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 <ctype.h> 20#include <err.h> 21#include <stdarg.h> 22#include <stdint.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26 27#include "json.h" 28 29#define JSON_MAX_STACK 16 30 31enum json_type { 32 NONE, 33 START, 34 ARRAY, 35 OBJECT 36}; 37 38static struct json_stack { 39 const char *name; 40 unsigned int count; 41 int compact; 42 enum json_type type; 43} stack[JSON_MAX_STACK]; 44 45static char indent[JSON_MAX_STACK + 1]; 46static int level; 47static int eb; 48static FILE *jsonfh; 49 50static void 51do_comma_indent(void) 52{ 53 char sp = '\n'; 54 55 if (stack[level].compact) 56 sp = ' '; 57 58 if (stack[level].count++ > 0) { 59 if (!eb) 60 eb = fprintf(jsonfh, ",%c", sp) < 0; 61 } 62 63 if (stack[level].compact) 64 return; 65 if (!eb) 66 eb = fprintf(jsonfh, "\t%.*s", level, indent) < 0; 67} 68 69static void 70do_name(const char *name) 71{ 72 if (stack[level].type == ARRAY) 73 return; 74 if (!eb) 75 eb = fprintf(jsonfh, "\"%s\": ", name) < 0; 76} 77 78static int 79do_find(enum json_type type, const char *name) 80{ 81 int i; 82 83 for (i = level; i > 0; i--) 84 if (type == stack[i].type && 85 strcmp(name, stack[i].name) == 0) 86 return i; 87 88 /* not found */ 89 return -1; 90} 91 92void 93json_do_start(FILE *fh) 94{ 95 memset(indent, '\t', JSON_MAX_STACK); 96 memset(stack, 0, sizeof(stack)); 97 level = 0; 98 stack[level].type = START; 99 jsonfh = fh; 100 eb = 0; 101 102 eb = fprintf(jsonfh, "{\n") < 0; 103} 104 105int 106json_do_finish(void) 107{ 108 while (level > 0) 109 json_do_end(); 110 if (!eb) 111 eb = fprintf(jsonfh, "\n}\n") < 0; 112 113 return -eb; 114} 115 116void 117json_do_array(const char *name) 118{ 119 int i, l; 120 char sp = '\n'; 121 122 if ((l = do_find(ARRAY, name)) > 0) { 123 /* array already in use, close element and move on */ 124 for (i = level - l; i > 0; i--) 125 json_do_end(); 126 return; 127 } 128 /* Do not stack arrays, while allowed this is not needed */ 129 if (stack[level].type == ARRAY) 130 json_do_end(); 131 132 if (stack[level].compact) 133 sp = ' '; 134 do_comma_indent(); 135 do_name(name); 136 if (!eb) 137 eb = fprintf(jsonfh, "[%c", sp) < 0; 138 139 if (++level >= JSON_MAX_STACK) 140 errx(1, "json stack too deep"); 141 142 stack[level].name = name; 143 stack[level].type = ARRAY; 144 stack[level].count = 0; 145 /* inherit compact setting from above level */ 146 stack[level].compact = stack[level - 1].compact; 147} 148 149void 150json_do_object(const char *name, int compact) 151{ 152 int i, l; 153 char sp = '\n'; 154 155 if ((l = do_find(OBJECT, name)) > 0) { 156 /* roll back to that object and close it */ 157 for (i = level - l; i >= 0; i--) 158 json_do_end(); 159 } 160 161 if (compact) 162 sp = ' '; 163 do_comma_indent(); 164 do_name(name); 165 if (!eb) 166 eb = fprintf(jsonfh, "{%c", sp) < 0; 167 168 if (++level >= JSON_MAX_STACK) 169 errx(1, "json stack too deep"); 170 171 stack[level].name = name; 172 stack[level].type = OBJECT; 173 stack[level].count = 0; 174 stack[level].compact = compact; 175} 176 177void 178json_do_end(void) 179{ 180 char c; 181 182 if (stack[level].type == ARRAY) 183 c = ']'; 184 else if (stack[level].type == OBJECT) 185 c = '}'; 186 else 187 errx(1, "json bad stack state"); 188 189 if (!stack[level].compact) { 190 if (!eb) 191 eb = fprintf(jsonfh, "\n%.*s%c", level, indent, c) < 0; 192 } else { 193 if (!eb) 194 eb = fprintf(jsonfh, " %c", c) < 0; 195 } 196 197 stack[level].name = NULL; 198 stack[level].type = NONE; 199 stack[level].count = 0; 200 stack[level].compact = 0; 201 202 if (level-- <= 0) 203 errx(1, "json stack underflow"); 204 205 stack[level].count++; 206} 207 208void 209json_do_printf(const char *name, const char *fmt, ...) 210{ 211 va_list ap; 212 char *str; 213 214 va_start(ap, fmt); 215 if (!eb) { 216 if (vasprintf(&str, fmt, ap) == -1) 217 errx(1, "json printf failed"); 218 json_do_string(name, str); 219 free(str); 220 } 221 va_end(ap); 222} 223 224void 225json_do_string(const char *name, const char *v) 226{ 227 unsigned char c; 228 229 do_comma_indent(); 230 do_name(name); 231 if (!eb) 232 eb = fprintf(jsonfh, "\"") < 0; 233 while ((c = *v++) != '\0' && !eb) { 234 /* skip escaping '/' since our use case does not require it */ 235 switch (c) { 236 case '"': 237 eb = fprintf(jsonfh, "\\\"") < 0; 238 break; 239 case '\\': 240 eb = fprintf(jsonfh, "\\\\") < 0; 241 break; 242 case '\b': 243 eb = fprintf(jsonfh, "\\b") < 0; 244 break; 245 case '\f': 246 eb = fprintf(jsonfh, "\\f") < 0; 247 break; 248 case '\n': 249 eb = fprintf(jsonfh, "\\n") < 0; 250 break; 251 case '\r': 252 eb = fprintf(jsonfh, "\\r") < 0; 253 break; 254 case '\t': 255 eb = fprintf(jsonfh, "\\t") < 0; 256 break; 257 default: 258 if (iscntrl(c)) 259 eb = fprintf(jsonfh, "\\u00%02x", c) < 0; 260 else 261 eb = putc(c, jsonfh) == EOF; 262 break; 263 } 264 } 265 if (!eb) 266 eb = fprintf(jsonfh, "\"") < 0; 267} 268 269void 270json_do_hexdump(const char *name, void *buf, size_t len) 271{ 272 uint8_t *data = buf; 273 size_t i; 274 275 do_comma_indent(); 276 do_name(name); 277 if (!eb) 278 eb = fprintf(jsonfh, "\"") < 0; 279 for (i = 0; i < len; i++) 280 if (!eb) 281 eb = fprintf(jsonfh, "%02x", *(data + i)) < 0; 282 if (!eb) 283 eb = fprintf(jsonfh, "\"") < 0; 284} 285 286void 287json_do_bool(const char *name, int v) 288{ 289 do_comma_indent(); 290 do_name(name); 291 if (v) { 292 if (!eb) 293 eb = fprintf(jsonfh, "true") < 0; 294 } else { 295 if (!eb) 296 eb = fprintf(jsonfh, "false") < 0; 297 } 298} 299 300void 301json_do_uint(const char *name, unsigned long long v) 302{ 303 do_comma_indent(); 304 do_name(name); 305 if (!eb) 306 eb = fprintf(jsonfh, "%llu", v) < 0; 307} 308 309void 310json_do_int(const char *name, long long v) 311{ 312 do_comma_indent(); 313 do_name(name); 314 if (!eb) 315 eb = fprintf(jsonfh, "%lld", v) < 0; 316} 317 318void 319json_do_double(const char *name, double v) 320{ 321 do_comma_indent(); 322 do_name(name); 323 if (!eb) 324 eb = fprintf(jsonfh, "%f", v) < 0; 325}