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