JSON strings require certain characters to be encoded, either by using a single reverse solidus character "\" for a set of popular characters, or by using a Unicode representation of "\uXXXXX". The jsmn library doesn't handle unescaping for us, so we must implement this functionality for ourselves.
Add a new function `grub_json_unescape ()` that takes a potentially escaped JSON string as input and returns a new unescaped string. Signed-off-by: Patrick Steinhardt <p...@pks.im> --- grub-core/lib/json/json.c | 98 +++++++++++++++++++++++++++++++++++++++ grub-core/lib/json/json.h | 12 +++++ 2 files changed, 110 insertions(+) diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c index 1c20c75ea..3e5e25f79 100644 --- a/grub-core/lib/json/json.c +++ b/grub-core/lib/json/json.c @@ -262,3 +262,101 @@ grub_json_getint64 (grub_int64_t *out, const grub_json_t *parent, const char *ke return GRUB_ERR_NONE; } + +grub_err_t +grub_json_unescape (char **out, size_t *outlen, const char *in, size_t inlen) +{ + grub_err_t ret = GRUB_ERR_NONE; + size_t inpos, resultpos; + char *result; + + result = grub_calloc (1, inlen + 1); + + for (inpos = resultpos = 0; inpos < inlen; inpos++) + { + if (in[inpos] == '\\') + { + inpos++; + if (inpos >= inlen) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Expected escaped character"); + goto err; + } + + switch (in[inpos]) + { + case '"': + result[resultpos++] = '"'; break; + case '/': + result[resultpos++] = '/'; break; + case '\\': + result[resultpos++] = '\\'; break; + case 'b': + result[resultpos++] = '\b'; break; + case 'f': + result[resultpos++] = '\f'; break; + case 'r': + result[resultpos++] = '\r'; break; + case 'n': + result[resultpos++] = '\n'; break; + case 't': + result[resultpos++] = '\t'; break; + case 'u': + { + unsigned char values[4] = {0}; + int i; + + inpos++; + if (inpos + ARRAY_SIZE(values) > inlen) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unicode sequence too short"); + goto err; + } + + for (i = 0; i < 4; i++) + { + char c = in[inpos++]; + + if (c >= '0' && c <= '9') + values[i] = c - '0'; + else if (c >= 'A' && c <= 'F') + values[i] = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + values[i] = c - 'a' + 10; + else + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unicode sequence with invalid character '%c'", c); + goto err; + } + } + + if (values[0] != 0 || values[1] != 0) + result[resultpos++] = values[0] << 4 | values[1]; + result[resultpos++] = values[2] << 4 | values[3]; + + /* Offset the increment that's coming in via the loop increment. */ + inpos--; + + break; + } + default: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unrecognized escaped character '%c'", in[inpos]); + goto err; + } + } + else + { + result[resultpos++] = in[inpos]; + } + } + + *out = result; + *outlen = resultpos; + +err: + if (ret != GRUB_ERR_NONE) + grub_free (result); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/json/json.h b/grub-core/lib/json/json.h index 4ea2a22d8..37a5a5f57 100644 --- a/grub-core/lib/json/json.h +++ b/grub-core/lib/json/json.h @@ -125,4 +125,16 @@ extern grub_err_t EXPORT_FUNC(grub_json_getint64) (grub_int64_t *out, const grub_json_t *parent, const char *key); +/* + * Unescape escaped characters and Unicode sequences in the + * given JSON-encoded string. Returns a newly allocated string + * passed back via the `out` parameter that has a length of + * `*outlen`. + * + * See https://datatracker.ietf.org/doc/html/rfc8259#section-7 for more + * information on escaping in JSON. + */ +extern grub_err_t EXPORT_FUNC(grub_json_unescape) (char **out, size_t *outlen, + const char *in, size_t inlen); + #endif -- 2.36.1
signature.asc
Description: PGP signature
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel