We use an array of strings to represnt a JSONPath. Add a new %pJP format
specifier that can be used to print them. This is useful for error
messages when the lookup fails.

Signed-off-by: Ahmad Fatoum <[email protected]>
---
 lib/vsprintf.c     | 44 ++++++++++++++++++++++++++++++++++++++++++++
 test/self/printf.c | 17 +++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 693f3c0cc0c8..cdb3906ea2ff 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -197,6 +197,27 @@ static char *string(char *buf, const char *end, const char 
*s, int field_width,
        return trailing_spaces(buf, end, len, &field_width, flags);
 }
 
+static __maybe_unused char *string_array(char *buf, const char *end, char 
*const *s,
+                                        int field_width, int precision, int 
flags,
+                                        const char *separator)
+{
+       size_t i, len = strlen(separator);
+
+       while (*s) {
+               buf = string(buf, end, *s, field_width, precision, flags);
+               if (!*++s)
+                       break;
+
+               for (i = 0; i < len; ++i) {
+                       if (buf < end)
+                               *buf = separator[i];
+                       ++buf;
+               }
+       }
+
+       return buf;
+}
+
 static char *wstring(char *buf, const char *end, const wchar_t *s, int 
field_width,
                     int precision, int flags)
 {
@@ -368,6 +389,26 @@ char *hex_string(char *buf, const char *end, const u8 
*addr, int field_width,
        return buf;
 }
 
+static noinline_for_stack
+char *jsonpath_string(char *buf, const char *end, char *const *path, int 
field_width,
+                int precision, int flags, const char *fmt)
+{
+       if ((unsigned long)path < PAGE_SIZE)
+               return string(buf, end, "<NULL>", field_width, precision, 
flags);
+
+       if (buf < end)
+               *buf = '$';
+       ++buf;
+
+       if (*path) {
+               if (buf < end)
+                       *buf = '.';
+               ++buf;
+       }
+
+       return string_array(buf, end, path, field_width, precision, flags, ".");
+}
+
 static noinline_for_stack
 char *address_val(char *buf, const char *end, const void *addr,
                  int field_width, int precision, int flags, const char *fmt)
@@ -458,6 +499,9 @@ static char *pointer(const char *fmt, char *buf, const char 
*end, const void *pt
                if (IS_ENABLED(CONFIG_PRINTF_HEXSTR))
                        return hex_string(buf, end, ptr, field_width, 
precision, flags, fmt);
                break;
+       case 'J':
+               if (fmt[1] == 'P' && IS_ENABLED(CONFIG_JSMN))
+                       return jsonpath_string(buf, end, ptr, field_width, 
precision, flags, fmt);
        }
 
        return raw_pointer(buf, end, ptr, field_width, precision, flags);
diff --git a/test/self/printf.c b/test/self/printf.c
index 7a74660868c2..eae40ed242c1 100644
--- a/test/self/printf.c
+++ b/test/self/printf.c
@@ -305,6 +305,22 @@ test_pointer(void)
        errptr();
 }
 
+static void __init
+test_jsonpath(void)
+{
+       if (!IS_ENABLED(CONFIG_JSMN)) {
+               pr_info("skipping jsonpath tests: CONFIG_JSMN disabled in 
config\n");
+               skipped_tests += 5;
+               return;
+       }
+
+       test("<NULL>", "%pJP",  NULL);
+       test("$", "%pJP",  &(char *[]){ NULL });
+       test("$.1", "%pJP",  &(char *[]){ "1", NULL });
+       test("$.1.23", "%pJP",  &(char *[]){ "1", "23", NULL });
+       test("$.1.23.456", "%pJP",  &(char *[]){ "1", "23", "456", NULL });
+}
+
 static void __init test_printf(void)
 {
        alloced_buffer = malloc(BUF_SIZE + 2*PAD_SIZE);
@@ -317,6 +333,7 @@ static void __init test_printf(void)
        test_string();
        test_pointer();
        test_hexstr();
+       test_jsonpath();
 
        free(alloced_buffer);
 }
-- 
2.30.2


Reply via email to