Eric Blake <ebl...@redhat.com> writes: > Pull out a new qstring_append_json_string() helper, so that all > JSON output producers can use the same output escaping rules. > > While it appears that vmstate's use of the simpler qjson.c > formatter is not currently encountering any string that needs > escapes to be valid JSON, it is better to be safe than sorry, > and this substitution is not adding any potential asserts > during migration. > > Adding a return value will let callers that care diagnose the > situation where an attempt was made to output invalid JSON (we > use substitute characters, and in fact guarantee that our > output is ASCII via escapes even if the input was UTF-8). None > of the current callers care, but a future patch wants to make > it possible to turn this situation into an error. > > Signed-off-by: Eric Blake <ebl...@redhat.com> [...] > @@ -290,3 +238,68 @@ QString *qobject_to_json_pretty(const QObject *obj) > > return str; > } > + > +/** > + * Append a JSON string to @qstring that encodes the C string @str. > + * > + * The JSON string is enclosed in double quotes and has funny > + * characters escaped. Returns -1 if encoding errors were replaced, > + * or 0 if the input was already valid UTF-8. > + */
Suggest: * @str is in UTF-8. The JSON string is enclosed in double quotes * and has funny characters escaped. Invalid UTF-8 sequences are * replaced by (properly escaped) U+0xFFFD replacement characters. * Return -1 if invalid sequences were replaced, else 0. > +int qstring_append_json_string(QString *qstring, const char *str) > +{ > + const char *ptr; > + int cp; > + char buf[16]; > + char *end; > + int result = 0; > + > + qstring_append(qstring, "\""); > + > + for (ptr = str; *ptr; ptr = end) { > + cp = mod_utf8_codepoint(ptr, 6, &end); > + switch (cp) { > + case '\"': > + qstring_append(qstring, "\\\""); > + break; > + case '\\': > + qstring_append(qstring, "\\\\"); > + break; > + case '\b': > + qstring_append(qstring, "\\b"); > + break; > + case '\f': > + qstring_append(qstring, "\\f"); > + break; > + case '\n': > + qstring_append(qstring, "\\n"); > + break; > + case '\r': > + qstring_append(qstring, "\\r"); > + break; > + case '\t': > + qstring_append(qstring, "\\t"); > + break; > + default: > + if (cp < 0) { > + cp = 0xFFFD; /* replacement character */ > + result = -1; > + } > + if (cp > 0xFFFF) { > + /* beyond BMP; need a surrogate pair */ > + snprintf(buf, sizeof(buf), "\\u%04X\\u%04X", > + 0xD800 + ((cp - 0x10000) >> 10), > + 0xDC00 + ((cp - 0x10000) & 0x3FF)); > + } else if (cp < 0x20 || cp >= 0x7F) { > + snprintf(buf, sizeof(buf), "\\u%04X", cp); > + } else { > + buf[0] = cp; > + buf[1] = 0; > + } > + qstring_append(qstring, buf); > + } > + }; > + > + qstring_append(qstring, "\""); > + return result; > +}