diff options
Diffstat (limited to 'tests/json.c')
-rw-r--r-- | tests/json.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/tests/json.c b/tests/json.c new file mode 100644 index 0000000..f2c2380 --- /dev/null +++ b/tests/json.c @@ -0,0 +1,286 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2012, 2015 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include "grecs.h" +#include "json.h" + +size_t indent; +int pretty_print; +int precision = -1; + +static void +usage(const char *arg, FILE *fp, int code) +{ + fprintf(fp, + "usage: %s [-file=FILE][-indent=N][-precision=N] [expr]\n", + arg); + exit(code); +} + +static void +json_indent(FILE *fp, size_t level) +{ + level *= indent; + while (level--) + fputc(' ', fp); +} + +static void +json_format_delim(FILE *fp, size_t level) +{ + fputc(',', fp); + if (indent) { + fputc('\n', fp); + json_indent(fp, level); + } else + fputc(' ', fp); +} + +static int +escape(char c, char *o) +{ + static char transtab[] = "\\\\\"\"//b\bf\fn\nr\rt\t"; + char *p; + + for (p = transtab; *p; p += 2) { + if (p[1] == c) { + *o = p[0]; + return 0; + } + } + return -1; +} + +static void +json_format_string(FILE *fp, const char *s) +{ + fputc('"', fp); + for (; *s; s++) { + char c; + if (!escape(*s, &c)) { + fputc('\\', fp); + fputc(c, fp); + } else + fputc(*s, fp); + } + fputc('"', fp); +} + +static int +collect_keypairs(void *sym, void *data) +{ + struct json_pair *p = sym; + struct json_pair ***kp = data; + **kp = p; + ++*kp; + return 0; +} + +static void json_format_value(FILE *fp, struct json_value *obj, size_t level); + +static int +keypair_cmp_name(const void *a, const void *b) +{ + struct json_pair const * const *kpa = a; + struct json_pair const * const *kpb = b; + return strcmp((*kpa)->k, (*kpb)->k); +} + +static void +json_format_obj(FILE *fp, struct json_value *obj, size_t level) +{ + size_t count, i; + struct json_pair **keypairs, **kp; + + count = grecs_symtab_count_entries(obj->v.o); + keypairs = grecs_calloc(count, sizeof(*keypairs)); + kp = keypairs; + grecs_symtab_enumerate(obj->v.o, collect_keypairs, &kp); + qsort(keypairs, count, sizeof(*keypairs), keypair_cmp_name); + + fputc('{', fp); + if (count) { + if (indent) + fputc('\n', fp); + for (i = 0; i < count; i++) { + (i ? json_format_delim : json_indent)(fp, level); + json_format_string(fp, keypairs[i]->k); + fputc(':', fp); + if (indent) + fputc(' ', fp); + json_format_value(fp, keypairs[i]->v, level); + } + if (indent) { + fputc('\n', fp); + json_indent(fp, level-1); + } + } + fputc('}', fp); +} + +static void +json_format_array(FILE *fp, struct json_value *obj, size_t level) +{ + size_t i; + + fputc('[', fp); + if (obj->v.a->oc) { + if (indent) + fputc('\n', fp); + for (i = 0; i < obj->v.a->oc; i++) { + (i ? json_format_delim : json_indent)(fp, level); + json_format_value(fp, obj->v.a->ov[i], level); + } + if (indent) { + fputc('\n', fp); + json_indent(fp, level-1); + } + } + fputc(']', fp); +} + +static void +json_format_value(FILE *fp, struct json_value *obj, size_t level) +{ + if (!obj) { + fprintf(fp, "null"); + return; + } + + ++level; + switch (obj->type) { + case json_null: + fprintf(fp, "null"); + break; + + case json_bool: + fprintf(fp, "%s", obj->v.b ? "true" : "false"); + break; + + case json_number: + if (precision == -1) + fprintf(fp, "%e", obj->v.n); + else + fprintf(fp, "%.*f", precision, obj->v.n); + break; + + case json_string: + json_format_string(fp, obj->v.s); + break; + + case json_arr: + json_format_array(fp, obj, level); + break; + + case json_object: + json_format_obj(fp, obj, level); + break; + } +} + + +int +main(int argc, char **argv) +{ + char *progname = argv[0]; + char *file = NULL; + char *input; + size_t size; + struct json_value *obj; + char *key = NULL; + + while (--argc) { + char *arg = *++argv; + if (strncmp(arg, "-file=", 6) == 0) + file = arg + 6; + else if (strncmp(arg, "-indent=", 8) == 0) + indent = atoi(arg + 8); + else if (strncmp(arg, "-search=", 8) == 0) + key = arg + 8; + else if (strncmp(arg, "-precision=", 11) == 0) + precision = atoi(arg + 11); + else if (arg[0] == '-') + usage(progname, stderr, 1); + else + break; + } + + if (file) { + struct stat st; + int fd; + ssize_t n; + + if (argc != 0) + usage(progname, stderr, 1); + + fd = open(file, O_RDONLY); + if (fd == -1) { + perror(file); + return 2; + } + if (fstat(fd, &st)) { + perror("fstat"); + return 2; + } + size = (size_t) st.st_size; + if (size != st.st_size) + abort(); + input = grecs_malloc(size + 1); + n = read(fd, input, size); + if (n == -1) { + perror("read"); + return 2; + } + if (n != size) { + fprintf(stderr, "%s: short read from %s\n", + progname, file); + return 2; + } + input[n] = 0; + close(fd); + } else if (argc == 1) { + if (file) + usage(progname, stderr, 1); + input = *argv; + size = strlen(input); + } else + usage(progname, stderr, 1); + + obj = json_parse_string(input, size); + if (!obj) { + json_err_locus.beg.file = json_err_locus.end.file = + file ? file : "input"; + grecs_error(&json_err_locus, 0, "%s", json_err_diag); + return 3; + } + if (key) { + struct json_value *p = json_value_lookup(obj, key); + if (!p) + return 4; + obj = p; + } + json_format_value(stdout, obj, 0); + putchar('\n'); + return 0; +} |