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