@@ -1297,7 +1297,10 @@ F: cmd/luks.c
F: doc/usage/cmd/luks.rst
F: drivers/block/luks.c
F: include/luks.h
+F: include/json.h
+F: lib/json.c
F: test/boot/luks.c
+F: test/lib/json.c
MALI DISPLAY PROCESSORS
M: Liviu Dudau <liviu.dudau@foss.arm.com>
new file mode 100644
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * JSON utilities
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+#ifndef __JSON_H__
+#define __JSON_H__
+
+/**
+ * json_print_pretty() - Print JSON with indentation
+ *
+ * This function takes a JSON string and prints it with proper indentation,
+ * making it more human-readable. It handles nested objects and arrays.
+ *
+ * @json: JSON string to print (may be nul terminated before @len)
+ * @len: Length of JSON string
+ */
+void json_print_pretty(const char *json, int len);
+
+#endif /* __JSON_H__ */
@@ -976,6 +976,13 @@ config GETOPT
help
This enables functions for parsing command-line options.
+config JSON
+ bool "Enable JSON parsing and printing"
+ help
+ This enables JSON (JavaScript Object Notation) parsing and pretty-
+ printing functions. JSON is used for structured data representation,
+ such as LUKS2 metadata.
+
config OF_LIBFDT
bool "Enable the FDT library"
default y if OF_CONTROL
@@ -39,6 +39,7 @@ endif
obj-y += crc8.o
obj-$(CONFIG_ERRNO_STR) += errno_str.o
+obj-$(CONFIG_JSON) += json.o
obj-$(CONFIG_FIT) += fdtdec_common.o
obj-$(CONFIG_TEST_FDTDEC) += fdtdec_test.o
obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o
new file mode 100644
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * JSON pretty-printer
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+#include <ctype.h>
+#include <log.h>
+
+/**
+ * print_indent() - Print indentation spaces
+ *
+ * @indent: Indentation level (each level is 2 spaces)
+ */
+static void print_indent(int indent)
+{
+ for (int i = 0; i < indent * 2; i++)
+ putc(' ');
+}
+
+void json_print_pretty(const char *json, int len)
+{
+ int indent = 0;
+ bool in_string = false;
+ bool escaped = false;
+ bool after_open = false;
+ int i;
+
+ for (i = 0; i < len && json[i]; i++) {
+ char c = json[i];
+
+ /* Handle escape sequences */
+ if (escaped) {
+ putc(c);
+ escaped = false;
+ continue;
+ }
+
+ if (c == '\\') {
+ putc(c);
+ escaped = true;
+ continue;
+ }
+
+ /* Track whether we're inside a string */
+ if (c == '"') {
+ in_string = !in_string;
+ if (after_open) {
+ print_indent(indent);
+ after_open = false;
+ }
+ putc(c);
+ continue;
+ }
+
+ /* Don't format inside strings */
+ if (in_string) {
+ putc(c);
+ continue;
+ }
+
+ /* Format structural characters */
+ switch (c) {
+ case '{':
+ case '[':
+ if (after_open) {
+ print_indent(indent);
+ after_open = false;
+ }
+ putc(c);
+ putc('\n');
+ indent++;
+ after_open = true;
+ break;
+
+ case '}':
+ case ']':
+ if (!after_open) {
+ putc('\n');
+ indent--;
+ print_indent(indent);
+ } else {
+ indent--;
+ }
+ putc(c);
+ after_open = false;
+ break;
+
+ case ',':
+ putc(c);
+ putc('\n');
+ print_indent(indent);
+ after_open = false;
+ break;
+
+ case ':':
+ putc(c);
+ putc(' ');
+ after_open = false;
+ break;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ /* Skip whitespace outside strings */
+ break;
+
+ default:
+ if (after_open) {
+ print_indent(indent);
+ after_open = false;
+ }
+ putc(c);
+ break;
+ }
+ }
+
+ putc('\n');
+}
@@ -12,6 +12,7 @@ obj-$(CONFIG_EFI_LOADER) += efi_device_path.o
obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o
obj-$(CONFIG_EFI_LOG) += efi_log.o
obj-y += hexdump.o
+obj-$(CONFIG_JSON) += json.o
obj-$(CONFIG_SANDBOX) += kconfig.o
obj-y += lmb.o
obj-$(CONFIG_HAVE_SETJMP) += longjmp.o
new file mode 100644
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for JSON pretty-printer
+ *
+ * Copyright (C) 2025 Canonical Ltd
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+#include <json.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_json_simple_object(struct unit_test_state *uts)
+{
+ const char *json = "{\"name\":\"value\"}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"name\": \"value\"");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_simple_object, UTF_CONSOLE);
+
+static int lib_test_json_simple_array(struct unit_test_state *uts)
+{
+ const char *json = "[1,2,3]";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("[");
+ ut_assert_nextline(" 1,");
+ ut_assert_nextline(" 2,");
+ ut_assert_nextline(" 3");
+ ut_assert_nextline("]");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_simple_array, UTF_CONSOLE);
+
+static int lib_test_json_nested_object(struct unit_test_state *uts)
+{
+ const char *json = "{\"outer\":{\"inner\":\"value\"}}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"outer\": {");
+ ut_assert_nextline(" \"inner\": \"value\"");
+ ut_assert_nextline(" }");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_nested_object, UTF_CONSOLE);
+
+static int lib_test_json_nested_array(struct unit_test_state *uts)
+{
+ const char *json = "[[1,2],[3,4]]";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("[");
+ ut_assert_nextline(" [");
+ ut_assert_nextline(" 1,");
+ ut_assert_nextline(" 2");
+ ut_assert_nextline(" ],");
+ ut_assert_nextline(" [");
+ ut_assert_nextline(" 3,");
+ ut_assert_nextline(" 4");
+ ut_assert_nextline(" ]");
+ ut_assert_nextline("]");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_nested_array, UTF_CONSOLE);
+
+static int lib_test_json_mixed_nested(struct unit_test_state *uts)
+{
+ const char *json = "{\"array\":[1,{\"nested\":\"obj\"}]}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"array\": [");
+ ut_assert_nextline(" 1,");
+ ut_assert_nextline(" {");
+ ut_assert_nextline(" \"nested\": \"obj\"");
+ ut_assert_nextline(" }");
+ ut_assert_nextline(" ]");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_mixed_nested, UTF_CONSOLE);
+
+static int lib_test_json_string_with_colon(struct unit_test_state *uts)
+{
+ const char *json = "{\"url\":\"http://example.com\"}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"url\": \"http://example.com\"");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_string_with_colon, UTF_CONSOLE);
+
+static int lib_test_json_string_with_comma(struct unit_test_state *uts)
+{
+ const char *json = "{\"name\":\"last, first\"}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"name\": \"last, first\"");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_string_with_comma, UTF_CONSOLE);
+
+static int lib_test_json_string_with_braces(struct unit_test_state *uts)
+{
+ const char *json = "{\"text\":\"some {braces} here\"}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"text\": \"some {braces} here\"");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_string_with_braces, UTF_CONSOLE);
+
+static int lib_test_json_escaped_quote(struct unit_test_state *uts)
+{
+ const char *json = "{\"quote\":\"He said \\\"hello\\\"\"}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"quote\": \"He said \\\"hello\\\"\"");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_escaped_quote, UTF_CONSOLE);
+
+static int lib_test_json_multiple_fields(struct unit_test_state *uts)
+{
+ const char *json = "{\"name\":\"test\",\"age\":25,\"active\":true}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"name\": \"test\",");
+ ut_assert_nextline(" \"age\": 25,");
+ ut_assert_nextline(" \"active\": true");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_multiple_fields, UTF_CONSOLE);
+
+static int lib_test_json_empty_object(struct unit_test_state *uts)
+{
+ const char *json = "{}";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_empty_object, UTF_CONSOLE);
+
+static int lib_test_json_empty_array(struct unit_test_state *uts)
+{
+ const char *json = "[]";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("[");
+ ut_assert_nextline("]");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_empty_array, UTF_CONSOLE);
+
+static int lib_test_json_whitespace(struct unit_test_state *uts)
+{
+ const char *json = "{ \"name\" : \"value\" , \"num\" : 42 }";
+
+ json_print_pretty(json, strlen(json));
+ ut_assert_nextline("{");
+ ut_assert_nextline(" \"name\": \"value\",");
+ ut_assert_nextline(" \"num\": 42");
+ ut_assert_nextline("}");
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_json_whitespace, UTF_CONSOLE);