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