aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2015-12-20 16:41:10 +0200
committerSergey Poznyakoff <gray@gnu.org>2015-12-20 16:41:10 +0200
commitffd39f82123a582b97fdbaf61ebcb932ecc6682e (patch)
tree03f5709a2b1fe90b714b7180ae0dba4341713a60
parent88e00fd054cc07cb9ede045c10ebf41795e144ad (diff)
downloadgrecs-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.am2
-rw-r--r--src/json.h10
-rw-r--r--src/jsonfmt.c216
-rw-r--r--tests/json.c180
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
diff --git a/src/json.h b/src/json.h
index e089692..698d56b 100644
--- a/src/json.h
+++ b/src/json.h
@@ -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;
}

Return to:

Send suggestions and report system problems to the System administrator.