diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2015-12-20 16:41:10 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2015-12-20 16:41:10 +0200 |
commit | ffd39f82123a582b97fdbaf61ebcb932ecc6682e (patch) | |
tree | 03f5709a2b1fe90b714b7180ae0dba4341713a60 | |
parent | 88e00fd054cc07cb9ede045c10ebf41795e144ad (diff) | |
download | grecs-ffd39f82123a582b97fdbaf61ebcb932ecc6682e.tar.gz grecs-ffd39f82123a582b97fdbaf61ebcb932ecc6682e.tar.bz2 |
Implement function for formatting JSON values
* src/Make.am: Add jsonfmt.c
* src/json.h (json_format): New struct.
(json_format_value): New proto.
* src/jsonfmt.c: New file.
* tests/json.c: Use json_format_value.
-rw-r--r-- | src/Make.am | 2 | ||||
-rw-r--r-- | src/json.h | 10 | ||||
-rw-r--r-- | src/jsonfmt.c | 216 | ||||
-rw-r--r-- | tests/json.c | 180 |
4 files changed, 239 insertions, 169 deletions
diff --git a/src/Make.am b/src/Make.am index 15e0075..e39a44a 100644 --- a/src/Make.am +++ b/src/Make.am @@ -40,7 +40,7 @@ if GRECS_COND_GIT_PARSER endif if GRECS_COND_JSON - GRECS_JSON = json-gram.y json-lex.l + GRECS_JSON = json-gram.y json-lex.l jsonfmt.c GRECS_EXTRA_JSON = json-gram.h endif @@ -65,4 +65,14 @@ struct json_value *json_parse_string(char const *input, size_t len); struct json_value *json_value_lookup(struct json_value *obj, const char *ident); + +struct json_format +{ + size_t indent; + int precision; + void (*write) (void *, char const *, size_t); + void *data; +}; + +void json_format_value(struct json_value *obj, struct json_format *fmt); diff --git a/src/jsonfmt.c b/src/jsonfmt.c new file mode 100644 index 0000000..75c8ac7 --- /dev/null +++ b/src/jsonfmt.c @@ -0,0 +1,216 @@ +/* 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 <string.h> +#include "grecs.h" +#include "json.h" + +static void +json_writez(struct json_format *fmt, char const *str) +{ + size_t len = strlen(str); + fmt->write(fmt->data, str, len); +} + +static void +json_writec(struct json_format *fmt, char c) +{ + fmt->write(fmt->data, &c, 1); +} + +static void +json_indent(struct json_format *fmt, size_t level) +{ + level *= fmt->indent; + while (level--) + json_writec(fmt, ' '); +} + +static void +json_format_delim(struct json_format *fmt, size_t level) +{ + json_writec(fmt, ','); + if (fmt->indent) { + json_writec(fmt, '\n'); + json_indent(fmt, level); + } else + json_writec(fmt, ' '); +} + +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(struct json_format *fmt, const char *s) +{ + json_writec(fmt, '"'); + for (; *s; s++) { + char c; + if (!escape(*s, &c)) { + json_writec(fmt, '\\'); + json_writec(fmt, c); + } else + json_writec(fmt, *s); + } + json_writec(fmt, '"'); +} + +static void +json_format_number(struct json_format *fmt, double n) +{ + char *buffer = NULL; + size_t size = 0; + if (fmt->precision == -1) + grecs_asprintf(&buffer, &size, "%e", n); + else + grecs_asprintf(&buffer, &size, "%.*f", fmt->precision, n); + json_writez(fmt, buffer); + free(buffer); +} + +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_formatter(struct json_format *fmt, 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(struct json_format *fmt, 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); + + json_writec(fmt, '{'); + if (count) { + if (fmt->indent) + json_writec(fmt, '\n'); + for (i = 0; i < count; i++) { + (i ? json_format_delim : json_indent)(fmt, level); + json_format_string(fmt, keypairs[i]->k); + json_writec(fmt, ':'); + if (fmt->indent) + json_writec(fmt, ' '); + json_formatter(fmt, keypairs[i]->v, level); + } + if (fmt->indent) { + json_writec(fmt, '\n'); + json_indent(fmt, level-1); + } + } + json_writec(fmt, '}'); +} + +static void +json_format_array(struct json_format *fmt, struct json_value *obj, + size_t level) +{ + size_t i; + + json_writec(fmt, '['); + if (obj->v.a->oc) { + if (fmt->indent) + json_writec(fmt, '\n'); + for (i = 0; i < obj->v.a->oc; i++) { + (i ? json_format_delim : json_indent)(fmt, level); + json_formatter(fmt, obj->v.a->ov[i], level); + } + if (fmt->indent) { + json_writec(fmt, '\n'); + json_indent(fmt, level-1); + } + } + json_writec(fmt, ']'); +} + +static void +json_formatter(struct json_format *fmt, struct json_value *obj, size_t level) +{ + if (!obj) { + json_writez(fmt, "null"); + return; + } + + ++level; + switch (obj->type) { + case json_null: + json_writez(fmt, "null"); + break; + + case json_bool: + json_writez(fmt, obj->v.b ? "true" : "false"); + break; + + case json_number: + json_format_number(fmt, obj->v.n); + break; + + case json_string: + json_format_string(fmt, obj->v.s); + break; + + case json_arr: + json_format_array(fmt, obj, level); + break; + + case json_object: + json_format_obj(fmt, obj, level); + break; + } +} + +void +json_format_value(struct json_value *obj, struct json_format *fmt) +{ + json_formatter(fmt, obj, 0); +} + + diff --git a/tests/json.c b/tests/json.c index f2c2380..60e264c 100644 --- a/tests/json.c +++ b/tests/json.c @@ -25,9 +25,14 @@ #include "grecs.h" #include "json.h" -size_t indent; -int pretty_print; -int precision = -1; +static void +printer(void *d, char const *buf, size_t size) +{ + FILE *fp = d; + fwrite(buf, size, 1, fp); +} + +struct json_format fmt = { 0, -1, printer, NULL }; static void usage(const char *arg, FILE *fp, int code) @@ -38,168 +43,6 @@ usage(const char *arg, FILE *fp, int code) 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) { @@ -215,11 +58,11 @@ main(int argc, char **argv) if (strncmp(arg, "-file=", 6) == 0) file = arg + 6; else if (strncmp(arg, "-indent=", 8) == 0) - indent = atoi(arg + 8); + fmt.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); + fmt.precision = atoi(arg + 11); else if (arg[0] == '-') usage(progname, stderr, 1); else @@ -280,7 +123,8 @@ main(int argc, char **argv) return 4; obj = p; } - json_format_value(stdout, obj, 0); + fmt.data = stdout; + json_format_value(obj, &fmt); putchar('\n'); return 0; } |