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