diff --git a/strbuf_helpers.c b/strbuf_helpers.c index f57a82de..341aba87 100644 --- a/strbuf_helpers.c +++ b/strbuf_helpers.c @@ -459,6 +459,31 @@ strbuf strbuf_append_quoted_string(strbuf sb, const char *str) return sb; } +static void _html_char(strbuf sb, char c) +{ + if (c == '&') + strbuf_puts(sb, "&"); + else if (c == '<') + strbuf_puts(sb, "<"); + else if (c == '>') + strbuf_puts(sb, ">"); + else if (c == '"') + strbuf_puts(sb, """); + else if (c == '\'') + strbuf_puts(sb, "'"); + else if (iscntrl(c)) + strbuf_sprintf(sb, "&#%u;", (unsigned char) c); + else + strbuf_putc(sb, c); +} + +strbuf strbuf_html_escape(strbuf sb, const char *str, size_t strlen) +{ + for (; strlen; --strlen, ++str) + _html_char(sb, *str); + return sb; +} + strbuf strbuf_json_null(strbuf sb) { strbuf_puts(sb, "null"); @@ -529,6 +554,60 @@ strbuf strbuf_json_hex(strbuf sb, const unsigned char *buf, size_t len) return sb; } +strbuf strbuf_json_atom(strbuf sb, const struct json_atom *atom) +{ + switch (atom->type) { + case JSON_NULL: + return strbuf_json_null(sb); + case JSON_BOOLEAN: + return strbuf_json_boolean(sb, atom->u.boolean); + case JSON_INTEGER: + strbuf_sprintf(sb, "%"PRId64, atom->u.integer); + return sb; + case JSON_STRING_NULTERM: + return strbuf_json_string(sb, atom->u.string.content); + case JSON_STRING_LENGTH: + return strbuf_json_string_len(sb, atom->u.string.content, atom->u.string.length); + } + abort(); +} + +strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *atom) +{ + switch (atom->type) { + case JSON_NULL: + return strbuf_json_null(sb); + case JSON_BOOLEAN: + return strbuf_puts(sb, atom->u.boolean ? "True" : "False"); + case JSON_INTEGER: + strbuf_sprintf(sb, "%"PRId64, atom->u.integer); + return sb; + case JSON_STRING_NULTERM: + return strbuf_puts(sb, atom->u.string.content); + case JSON_STRING_LENGTH: + return strbuf_ncat(sb, atom->u.string.content, atom->u.string.length); + } + abort(); +} + +strbuf strbuf_json_atom_as_html(strbuf sb, const struct json_atom *atom) +{ + switch (atom->type) { + case JSON_NULL: + return strbuf_json_null(sb); + case JSON_BOOLEAN: + return strbuf_json_boolean(sb, atom->u.boolean); + case JSON_INTEGER: + strbuf_sprintf(sb, "%"PRId64, atom->u.integer); + return sb; + case JSON_STRING_NULTERM: + return strbuf_html_escape(sb, atom->u.string.content, strlen(atom->u.string.content)); + case JSON_STRING_LENGTH: + return strbuf_html_escape(sb, atom->u.string.content, atom->u.string.length); + } + abort(); +} + strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels) { unsigned i; diff --git a/strbuf_helpers.h b/strbuf_helpers.h index 1aa82e0c..09ef461b 100644 --- a/strbuf_helpers.h +++ b/strbuf_helpers.h @@ -162,6 +162,11 @@ strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt); */ strbuf strbuf_append_quoted_string(strbuf sb, const char *str); +/* Escape HTML entities. + * @author Andrew Bettison + */ +strbuf strbuf_html_escape(strbuf sb, const char *, size_t); + /* Append various JSON elements. * @author Andrew Bettison */ @@ -170,6 +175,20 @@ strbuf strbuf_json_boolean(strbuf sb, int boolean); strbuf strbuf_json_string(strbuf sb, const char *str); // str can be NULL strbuf strbuf_json_string_len(strbuf sb, const char *str, size_t strlen); // str cannot be NULL strbuf strbuf_json_hex(strbuf sb, const unsigned char *buf, size_t len); +struct json_atom { + enum json_atomic_type { JSON_NULL, JSON_BOOLEAN, JSON_INTEGER, JSON_STRING_NULTERM, JSON_STRING_LENGTH } type; + union { + int boolean; + int64_t integer; + struct { + const char *content; + size_t length; + } string; + } u; +}; +strbuf strbuf_json_atom(strbuf sb, const struct json_atom *); +strbuf strbuf_json_atom_as_html(strbuf sb, const struct json_atom *); +strbuf strbuf_json_atom_as_text(strbuf sb, const struct json_atom *); /* Append a representation of a struct http_range[] array. * @author Andrew Bettison