w.deng | e87b500 | 2025-08-20 10:43:03 +0800 | [diff] [blame] | 1 | /* |
| 2 | * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ |
| 3 | * |
| 4 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. |
| 5 | * Michael Clark <michael@metaparadigm.com> |
| 6 | * |
| 7 | * This library is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the MIT license. See COPYING for details. |
| 9 | * |
| 10 | */ |
| 11 | |
| 12 | #include "config.h" |
| 13 | #undef realloc |
| 14 | |
| 15 | #include "strerror_override.h" |
| 16 | |
| 17 | #include <limits.h> |
| 18 | #include <stdarg.h> |
| 19 | #include <stddef.h> |
| 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <string.h> |
| 23 | |
| 24 | #ifdef HAVE_SYS_TYPES_H |
| 25 | #include <sys/types.h> |
| 26 | #endif /* HAVE_SYS_TYPES_H */ |
| 27 | |
| 28 | #ifdef HAVE_SYS_STAT_H |
| 29 | #include <sys/stat.h> |
| 30 | #endif /* HAVE_SYS_STAT_H */ |
| 31 | |
| 32 | #ifdef HAVE_FCNTL_H |
| 33 | #include <fcntl.h> |
| 34 | #endif /* HAVE_FCNTL_H */ |
| 35 | |
| 36 | #ifdef HAVE_UNISTD_H |
| 37 | #include <unistd.h> |
| 38 | #endif /* HAVE_UNISTD_H */ |
| 39 | |
| 40 | #ifdef WIN32 |
| 41 | #define WIN32_LEAN_AND_MEAN |
| 42 | #include <io.h> |
| 43 | #include <windows.h> |
| 44 | #endif /* defined(WIN32) */ |
| 45 | |
| 46 | #if !defined(HAVE_OPEN) && defined(WIN32) |
| 47 | #define open _open |
| 48 | #endif |
| 49 | |
| 50 | #include "snprintf_compat.h" |
| 51 | |
| 52 | #include "debug.h" |
| 53 | #include "json_inttypes.h" |
| 54 | #include "json_object.h" |
| 55 | #include "json_tokener.h" |
| 56 | #include "json_util.h" |
| 57 | #include "printbuf.h" |
| 58 | |
| 59 | static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename); |
| 60 | |
| 61 | static char _last_err[256] = ""; |
| 62 | |
| 63 | const char *json_util_get_last_err(void) |
| 64 | { |
| 65 | if (_last_err[0] == '\0') |
| 66 | return NULL; |
| 67 | return _last_err; |
| 68 | } |
| 69 | |
| 70 | void _json_c_set_last_err(const char *err_fmt, ...) |
| 71 | { |
| 72 | va_list ap; |
| 73 | va_start(ap, err_fmt); |
| 74 | // Ignore (attempted) overruns from snprintf |
| 75 | (void)vsnprintf(_last_err, sizeof(_last_err), err_fmt, ap); |
| 76 | va_end(ap); |
| 77 | } |
| 78 | |
| 79 | struct json_object *json_object_from_fd(int fd) |
| 80 | { |
| 81 | return json_object_from_fd_ex(fd, -1); |
| 82 | } |
| 83 | struct json_object *json_object_from_fd_ex(int fd, int in_depth) |
| 84 | { |
| 85 | struct printbuf *pb; |
| 86 | struct json_object *obj; |
| 87 | char buf[JSON_FILE_BUF_SIZE]; |
| 88 | ssize_t ret; |
| 89 | int depth = JSON_TOKENER_DEFAULT_DEPTH; |
| 90 | json_tokener *tok; |
| 91 | |
| 92 | if (!(pb = printbuf_new())) |
| 93 | { |
| 94 | _json_c_set_last_err("json_object_from_fd_ex: printbuf_new failed\n"); |
| 95 | return NULL; |
| 96 | } |
| 97 | |
| 98 | if (in_depth != -1) |
| 99 | depth = in_depth; |
| 100 | tok = json_tokener_new_ex(depth); |
| 101 | if (!tok) |
| 102 | { |
| 103 | _json_c_set_last_err( |
| 104 | "json_object_from_fd_ex: unable to allocate json_tokener(depth=%d): %s\n", |
| 105 | depth, strerror(errno)); |
| 106 | printbuf_free(pb); |
| 107 | return NULL; |
| 108 | } |
| 109 | |
| 110 | while ((ret = read(fd, buf, sizeof(buf))) > 0) |
| 111 | { |
| 112 | if (printbuf_memappend(pb, buf, ret) < 0) |
| 113 | { |
| 114 | #if JSON_FILE_BUF_SIZE > INT_MAX |
| 115 | #error "Can't append more than INT_MAX bytes at a time" |
| 116 | #endif |
| 117 | _json_c_set_last_err( |
| 118 | "json_object_from_fd_ex: failed to printbuf_memappend after reading %d+%d bytes: %s", printbuf_length(pb), (int)ret, strerror(errno)); |
| 119 | json_tokener_free(tok); |
| 120 | printbuf_free(pb); |
| 121 | return NULL; |
| 122 | } |
| 123 | } |
| 124 | if (ret < 0) |
| 125 | { |
| 126 | _json_c_set_last_err("json_object_from_fd_ex: error reading fd %d: %s\n", fd, |
| 127 | strerror(errno)); |
| 128 | json_tokener_free(tok); |
| 129 | printbuf_free(pb); |
| 130 | return NULL; |
| 131 | } |
| 132 | |
| 133 | obj = json_tokener_parse_ex(tok, pb->buf, printbuf_length(pb)); |
| 134 | if (obj == NULL) |
| 135 | _json_c_set_last_err("json_tokener_parse_ex failed: %s\n", |
| 136 | json_tokener_error_desc(json_tokener_get_error(tok))); |
| 137 | |
| 138 | json_tokener_free(tok); |
| 139 | printbuf_free(pb); |
| 140 | return obj; |
| 141 | } |
| 142 | |
| 143 | struct json_object *json_object_from_file(const char *filename) |
| 144 | { |
| 145 | struct json_object *obj; |
| 146 | int fd; |
| 147 | |
| 148 | if ((fd = open(filename, O_RDONLY)) < 0) |
| 149 | { |
| 150 | _json_c_set_last_err("json_object_from_file: error opening file %s: %s\n", |
| 151 | filename, strerror(errno)); |
| 152 | return NULL; |
| 153 | } |
| 154 | obj = json_object_from_fd(fd); |
| 155 | close(fd); |
| 156 | return obj; |
| 157 | } |
| 158 | |
| 159 | /* extended "format and write to file" function */ |
| 160 | |
| 161 | int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) |
| 162 | { |
| 163 | int fd, ret; |
| 164 | int saved_errno; |
| 165 | |
| 166 | if (!obj) |
| 167 | { |
| 168 | _json_c_set_last_err("json_object_to_file_ext: object is null\n"); |
| 169 | return -1; |
| 170 | } |
| 171 | |
| 172 | if ((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) |
| 173 | { |
| 174 | _json_c_set_last_err("json_object_to_file_ext: error opening file %s: %s\n", |
| 175 | filename, strerror(errno)); |
| 176 | return -1; |
| 177 | } |
| 178 | ret = _json_object_to_fd(fd, obj, flags, filename); |
| 179 | saved_errno = errno; |
| 180 | close(fd); |
| 181 | errno = saved_errno; |
| 182 | return ret; |
| 183 | } |
| 184 | |
| 185 | int json_object_to_fd(int fd, struct json_object *obj, int flags) |
| 186 | { |
| 187 | if (!obj) |
| 188 | { |
| 189 | _json_c_set_last_err("json_object_to_fd: object is null\n"); |
| 190 | return -1; |
| 191 | } |
| 192 | |
| 193 | return _json_object_to_fd(fd, obj, flags, NULL); |
| 194 | } |
| 195 | static int _json_object_to_fd(int fd, struct json_object *obj, int flags, const char *filename) |
| 196 | { |
| 197 | ssize_t ret; |
| 198 | const char *json_str; |
| 199 | size_t wpos, wsize; |
| 200 | |
| 201 | filename = filename ? filename : "(fd)"; |
| 202 | |
| 203 | if (!(json_str = json_object_to_json_string_ext(obj, flags))) |
| 204 | { |
| 205 | return -1; |
| 206 | } |
| 207 | |
| 208 | wsize = strlen(json_str); |
| 209 | wpos = 0; |
| 210 | while (wpos < wsize) |
| 211 | { |
| 212 | if ((ret = write(fd, json_str + wpos, wsize - wpos)) < 0) |
| 213 | { |
| 214 | _json_c_set_last_err("json_object_to_fd: error writing file %s: %s\n", |
| 215 | filename, strerror(errno)); |
| 216 | return -1; |
| 217 | } |
| 218 | |
| 219 | /* because of the above check for ret < 0, we can safely cast and add */ |
| 220 | wpos += (size_t)ret; |
| 221 | } |
| 222 | |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | // backwards compatible "format and write to file" function |
| 227 | |
| 228 | int json_object_to_file(const char *filename, struct json_object *obj) |
| 229 | { |
| 230 | return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); |
| 231 | } |
| 232 | |
| 233 | // Deprecated json_parse_double function. See json_tokener_parse_double instead. |
| 234 | int json_parse_double(const char *buf, double *retval) |
| 235 | { |
| 236 | char *end; |
| 237 | *retval = strtod(buf, &end); |
| 238 | return end == buf ? 1 : 0; |
| 239 | } |
| 240 | |
| 241 | int json_parse_int64(const char *buf, int64_t *retval) |
| 242 | { |
| 243 | char *end = NULL; |
| 244 | int64_t val; |
| 245 | |
| 246 | errno = 0; |
| 247 | val = strtoll(buf, &end, 10); |
| 248 | if (end != buf) |
| 249 | *retval = val; |
| 250 | if ((val == 0 && errno != 0) || (end == buf)) |
| 251 | { |
| 252 | errno = EINVAL; |
| 253 | return 1; |
| 254 | } |
| 255 | return 0; |
| 256 | } |
| 257 | |
| 258 | int json_parse_uint64(const char *buf, uint64_t *retval) |
| 259 | { |
| 260 | char *end = NULL; |
| 261 | uint64_t val; |
| 262 | |
| 263 | errno = 0; |
| 264 | while (*buf == ' ') |
| 265 | buf++; |
| 266 | if (*buf == '-') |
| 267 | return 1; /* error: uint cannot be negative */ |
| 268 | |
| 269 | val = strtoull(buf, &end, 10); |
| 270 | if (end != buf) |
| 271 | *retval = val; |
| 272 | if ((val == 0 && errno != 0) || (end == buf)) |
| 273 | { |
| 274 | errno = EINVAL; |
| 275 | return 1; |
| 276 | } |
| 277 | return 0; |
| 278 | } |
| 279 | |
| 280 | #ifndef HAVE_REALLOC |
| 281 | void *rpl_realloc(void *p, size_t n) |
| 282 | { |
| 283 | if (n == 0) |
| 284 | n = 1; |
| 285 | if (p == 0) |
| 286 | return malloc(n); |
| 287 | return realloc(p, n); |
| 288 | } |
| 289 | #endif |
| 290 | |
| 291 | #define NELEM(a) (sizeof(a) / sizeof(a[0])) |
| 292 | /* clang-format off */ |
| 293 | static const char *json_type_name[] = { |
| 294 | /* If you change this, be sure to update the enum json_type definition too */ |
| 295 | "null", |
| 296 | "boolean", |
| 297 | "double", |
| 298 | "int", |
| 299 | "object", |
| 300 | "array", |
| 301 | "string", |
| 302 | }; |
| 303 | /* clang-format on */ |
| 304 | |
| 305 | const char *json_type_to_name(enum json_type o_type) |
| 306 | { |
| 307 | int o_type_int = (int)o_type; |
| 308 | if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) |
| 309 | { |
| 310 | _json_c_set_last_err("json_type_to_name: type %d is out of range [0,%u]\n", o_type, |
| 311 | (unsigned)NELEM(json_type_name)); |
| 312 | return NULL; |
| 313 | } |
| 314 | return json_type_name[o_type]; |
| 315 | } |