util-linux/lib/jsonwrt.c

225 lines
4.8 KiB
C

/*
* JSON output formatting functions.
*
* No copyright is claimed. This code is in the public domain; do with
* it what you wish.
*
* Written by Karel Zak <kzak@redhat.com>
*/
#include <stdio.h>
#include <inttypes.h>
#include <ctype.h>
#include <cctype.h>
#include "c.h"
#include "jsonwrt.h"
/*
* Requirements enumerated via testing (V8, Firefox, IE11):
*
* var charsToEscape = [];
* for (var i = 0; i < 65535; i += 1) {
* try {
* JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
* } catch (e) {
* charsToEscape.push(i);
* }
* }
*/
static void fputs_quoted_case_json(const char *data, FILE *out, int dir)
{
const char *p;
fputc('"', out);
for (p = data; p && *p; p++) {
const unsigned int c = (unsigned int) *p;
/* From http://www.json.org
*
* The double-quote and backslashes would break out a string or
* init an escape sequence if not escaped.
*
* Note that single-quotes and forward slashes, while they're
* in the JSON spec, don't break double-quoted strings.
*/
if (c == '"' || c == '\\') {
fputc('\\', out);
fputc(c, out);
continue;
}
/* All non-control characters OK; do the case swap as required. */
if (c >= 0x20) {
/*
* Don't use locale sensitive ctype.h functions for regular
* ASCII chars, because for example with Turkish locale
* (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'.
*/
if (c <= 127)
fputc(dir == 1 ? c_toupper(c) :
dir == -1 ? c_tolower(c) : *p, out);
else
fputc(dir == 1 ? toupper(c) :
dir == -1 ? tolower(c) : *p, out);
continue;
}
/* In addition, all chars under ' ' break Node's/V8/Chrome's, and
* Firefox's JSON.parse function
*/
switch (c) {
/* Handle short-hand cases to reduce output size. C
* has most of the same stuff here, so if there's an
* "Escape for C" function somewhere in the STL, we
* should probably be using it.
*/
case '\b':
fputs("\\b", out);
break;
case '\t':
fputs("\\t", out);
break;
case '\n':
fputs("\\n", out);
break;
case '\f':
fputs("\\f", out);
break;
case '\r':
fputs("\\r", out);
break;
default:
/* Other assorted control characters */
fprintf(out, "\\u00%02x", c);
break;
}
}
fputc('"', out);
}
#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0)
#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
{
fmt->out = out;
fmt->indent = indent;
fmt->after_close = 0;
}
void ul_jsonwrt_indent(struct ul_jsonwrt *fmt)
{
int i;
for (i = 0; i < fmt->indent; i++)
fputs(" ", fmt->out);
}
void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
{
if (name) {
if (fmt->after_close)
fputs(",\n", fmt->out);
ul_jsonwrt_indent(fmt);
fputs_quoted_json_lower(name, fmt->out);
} else {
if (fmt->after_close)
fputs(",", fmt->out);
else
ul_jsonwrt_indent(fmt);
}
switch (type) {
case UL_JSON_OBJECT:
fputs(name ? ": {\n" : "{\n", fmt->out);
fmt->indent++;
break;
case UL_JSON_ARRAY:
fputs(name ? ": [\n" : "[\n", fmt->out);
fmt->indent++;
break;
case UL_JSON_VALUE:
fputs(name ? ": " : " ", fmt->out);
break;
}
fmt->after_close = 0;
}
void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
{
if (fmt->indent == 1) {
fputs("\n}\n", fmt->out);
fmt->indent--;
fmt->after_close = 1;
return;
}
assert(fmt->indent > 0);
switch (type) {
case UL_JSON_OBJECT:
fmt->indent--;
fputc('\n', fmt->out);
ul_jsonwrt_indent(fmt);
fputs("}", fmt->out);
break;
case UL_JSON_ARRAY:
fmt->indent--;
fputc('\n', fmt->out);
ul_jsonwrt_indent(fmt);
fputs("]", fmt->out);
break;
case UL_JSON_VALUE:
break;
}
fmt->after_close = 1;
}
void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
const char *name, const char *data)
{
ul_jsonwrt_value_open(fmt, name);
if (data && *data)
fputs(data, fmt->out);
else
fputs("null", fmt->out);
ul_jsonwrt_value_close(fmt);
}
void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
const char *name, const char *data)
{
ul_jsonwrt_value_open(fmt, name);
if (data && *data)
fputs_quoted_json(data, fmt->out);
else
fputs("null", fmt->out);
ul_jsonwrt_value_close(fmt);
}
void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
const char *name, uint64_t data)
{
ul_jsonwrt_value_open(fmt, name);
fprintf(fmt->out, "%"PRIu64, data);
ul_jsonwrt_value_close(fmt);
}
void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
const char *name, int data)
{
ul_jsonwrt_value_open(fmt, name);
fputs(data ? "true" : "false", fmt->out);
ul_jsonwrt_value_close(fmt);
}
void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt,
const char *name)
{
ul_jsonwrt_value_open(fmt, name);
fputs("null", fmt->out);
ul_jsonwrt_value_close(fmt);
}