--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * json_escape - Escape sequences for JSON strings
+ *
+ * This code helps you format strings into forms useful for JSON.
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ * License: BSD-MIT
+ * Example:
+ * // Print arguments as a JSON array.
+ * #include <ccan/json_escape/json_escape.h>
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * printf("[");
+ * for (int i = 1; i < argc; i++) {
+ * struct json_escape *e = json_escape(NULL, argv[i]);
+ * printf("%s\"%s\"", i == 1 ? "" : ",", e->s);
+ * }
+ * printf("]\n");
+ * return 0;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/tal\n");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/json_escape/json_escape.h>
+#include <stdio.h>
+
+struct json_escape *json_escape_string_(const tal_t *ctx,
+ const void *bytes, size_t len)
+{
+ struct json_escape *esc;
+
+ esc = (void *)tal_arr_label(ctx, char, len + 1,
+ TAL_LABEL(struct json_escape, ""));
+ memcpy(esc->s, bytes, len);
+ esc->s[len] = '\0';
+ return esc;
+}
+
+bool json_escape_eq(const struct json_escape *a, const struct json_escape *b)
+{
+ return streq(a->s, b->s);
+}
+
+static struct json_escape *escape(const tal_t *ctx,
+ const char *str TAKES,
+ size_t len,
+ bool partial)
+{
+ struct json_escape *esc;
+ size_t i, n;
+
+ /* Worst case: all \uXXXX */
+ esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
+
+ for (i = n = 0; i < len; i++, n++) {
+ char escape = 0;
+ switch (str[i]) {
+ case '\n':
+ escape = 'n';
+ break;
+ case '\b':
+ escape = 'b';
+ break;
+ case '\f':
+ escape = 'f';
+ break;
+ case '\t':
+ escape = 't';
+ break;
+ case '\r':
+ escape = 'r';
+ break;
+ case '\\':
+ if (partial) {
+ /* Don't double-escape standard escapes. */
+ if (str[i+1] == 'n'
+ || str[i+1] == 'b'
+ || str[i+1] == 'f'
+ || str[i+1] == 't'
+ || str[i+1] == 'r'
+ || str[i+1] == '/'
+ || str[i+1] == '\\'
+ || str[i+1] == '"') {
+ escape = str[i+1];
+ i++;
+ break;
+ }
+ if (str[i+1] == 'u'
+ && cisxdigit(str[i+2])
+ && cisxdigit(str[i+3])
+ && cisxdigit(str[i+4])
+ && cisxdigit(str[i+5])) {
+ memcpy(esc->s + n, str + i, 6);
+ n += 5;
+ i += 5;
+ continue;
+ }
+ } /* fall thru */
+ case '"':
+ escape = str[i];
+ break;
+ default:
+ if ((unsigned)str[i] < ' ' || str[i] == 127) {
+ snprintf(esc->s + n, 7, "\\u%04X", str[i]);
+ n += 5;
+ continue;
+ }
+ }
+ if (escape) {
+ esc->s[n++] = '\\';
+ esc->s[n] = escape;
+ } else
+ esc->s[n] = str[i];
+ }
+
+ esc->s[n] = '\0';
+ if (taken(str))
+ tal_free(str);
+ return esc;
+}
+
+struct json_escape *json_partial_escape(const tal_t *ctx, const char *str TAKES)
+{
+ return escape(ctx, str, strlen(str), true);
+}
+
+struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES)
+{
+ return escape(ctx, str, strlen(str), false);
+}
+
+struct json_escape *json_escape_len(const tal_t *ctx, const char *str TAKES,
+ size_t len)
+{
+ return escape(ctx, str, len, false);
+}
+
+/* By policy, we don't handle \u. Use UTF-8. */
+const char *json_escape_unescape(const tal_t *ctx, const struct json_escape *esc)
+{
+ char *unesc = tal_arr(ctx, char, strlen(esc->s) + 1);
+ size_t i, n;
+
+ for (i = n = 0; esc->s[i]; i++, n++) {
+ if (esc->s[i] != '\\') {
+ unesc[n] = esc->s[i];
+ continue;
+ }
+
+ i++;
+ switch (esc->s[i]) {
+ case 'n':
+ unesc[n] = '\n';
+ break;
+ case 'b':
+ unesc[n] = '\b';
+ break;
+ case 'f':
+ unesc[n] = '\f';
+ break;
+ case 't':
+ unesc[n] = '\t';
+ break;
+ case 'r':
+ unesc[n] = '\r';
+ break;
+ case '/':
+ case '\\':
+ case '"':
+ unesc[n] = esc->s[i];
+ break;
+ default:
+ return tal_free(unesc);
+ }
+ }
+
+ unesc[n] = '\0';
+ return unesc;
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_JSON_ESCAPE_H
+#define CCAN_JSON_ESCAPE_H
+#include "config.h"
+#include <ccan/tal/tal.h>
+
+/* Type differentiation for a correctly-escaped JSON string */
+struct json_escape {
+ /* NUL terminated string. */
+ char s[1];
+};
+
+/**
+ * json_escape - escape a valid UTF-8 string.
+ * @ctx: tal context to allocate from.
+ * @str: the string to escape.
+ *
+ * Allocates and returns a valid JSON string (without surrounding quotes).
+ */
+struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES);
+
+/* Version with @len */
+struct json_escape *json_escape_len(const tal_t *ctx,
+ const char *str TAKES, size_t len);
+
+/* @str is a valid UTF-8 string which may already contain escapes. */
+struct json_escape *json_partial_escape(const tal_t *ctx,
+ const char *str TAKES);
+
+/* Extract a JSON-escaped string. */
+
+/* Are two escape json strings identical? */
+bool json_escape_eq(const struct json_escape *a,
+ const struct json_escape *b);
+
+/* Internal routine for creating json_escape from bytes. */
+struct json_escape *json_escape_string_(const tal_t *ctx,
+ const void *bytes, size_t len);
+
+/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */
+const char *json_escape_unescape(const tal_t *ctx,
+ const struct json_escape *esc);
+#endif /* CCAN_JSON_ESCAPE_H */
--- /dev/null
+#include <ccan/json_escape/json_escape.h>
+/* Include the C files directly. */
+#include <ccan/json_escape/json_escape.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ const tal_t *ctx = tal(NULL, char);
+
+ /* This is how many tests you plan to run */
+ plan_tests(21);
+
+ ok1(!strcmp(json_partial_escape(ctx, "\\")->s, "\\\\"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\\\")->s, "\\\\"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\n")->s, "\\n"));
+ ok1(!strcmp(json_partial_escape(ctx, "\n")->s, "\\n"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\\"")->s, "\\\""));
+ ok1(!strcmp(json_partial_escape(ctx, "\"")->s, "\\\""));
+ ok1(!strcmp(json_partial_escape(ctx, "\\t")->s, "\\t"));
+ ok1(!strcmp(json_partial_escape(ctx, "\t")->s, "\\t"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\b")->s, "\\b"));
+ ok1(!strcmp(json_partial_escape(ctx, "\b")->s, "\\b"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\r")->s, "\\r"));
+ ok1(!strcmp(json_partial_escape(ctx, "\r")->s, "\\r"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\f")->s, "\\f"));
+ ok1(!strcmp(json_partial_escape(ctx, "\f")->s, "\\f"));
+ /* You're allowed to escape / according to json.org. */
+ ok1(!strcmp(json_partial_escape(ctx, "\\/")->s, "\\/"));
+ ok1(!strcmp(json_partial_escape(ctx, "/")->s, "/"));
+
+ ok1(!strcmp(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF"));
+ ok1(!strcmp(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx"));
+
+ /* Unknown escapes should be escaped. */
+ ok1(!strcmp(json_partial_escape(ctx, "\\x")->s, "\\\\x"));
+ tal_free(ctx);
+
+ return 0;
+}
--- /dev/null
+#include <ccan/json_escape/json_escape.h>
+/* Include the C files directly. */
+#include <ccan/json_escape/json_escape.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ const tal_t *ctx = tal(NULL, char);
+ struct json_escape *e;
+
+ /* This is how many tests you plan to run */
+ plan_tests(6);
+
+ e = json_escape(ctx, "Hello");
+ ok1(!strcmp(e->s, "Hello"));
+ ok1(!strcmp(json_escape_unescape(ctx, e),
+ "Hello"));
+
+ e = json_escape(ctx,
+ "\\\b\f\n\r\t\""
+ "\\\\\\b\\f\\n\\r\\t\\\"");
+ ok1(!strcmp(e->s,
+ "\\\\\\b\\f\\n\\r\\t\\\""
+ "\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\""));
+ ok1(!strcmp(json_escape_unescape(ctx, e),
+ "\\\b\f\n\r\t\""
+ "\\\\\\b\\f\\n\\r\\t\\\""));
+
+ /* This one doesn't escape the already-escaped chars */
+ e = json_partial_escape(ctx,
+ "\\\b\f\n\r\t\""
+ "\\\\\\b\\f\\n\\r\\t\\\"");
+ ok1(!strcmp(e->s,
+ "\\\\\\b\\f\\n\\r\\t\\\""
+ "\\\\\\b\\f\\n\\r\\t\\\""));
+ ok1(!strcmp(json_escape_unescape(ctx, e),
+ "\\\b\f\n\r\t\""
+ "\\\b\f\n\r\t\""));
+
+ tal_free(ctx);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}