Append a JSON array of strings: ,"key":["a","b","c"]. Atomic like * append_json_string: emitted only if the whole array fits. */
| 201 | /* Append a JSON array of strings: ,"key":["a","b","c"]. Atomic like |
| 202 | * append_json_string: emitted only if the whole array fits. */ |
| 203 | static void append_json_str_array(char *buf, size_t bufsize, size_t *pos, const char *key, |
| 204 | const char **arr) { |
| 205 | if (!arr || !arr[0] || *pos >= bufsize - PD_JSON_MARGIN) { |
| 206 | return; |
| 207 | } |
| 208 | /* ,"key":[ + per item "<escaped>" + separating commas + ] */ |
| 209 | size_t required = strlen(key) + PD_JSON_FIELD_OVERHEAD; |
| 210 | for (int i = 0; arr[i]; i++) { |
| 211 | required += def_json_escaped_len(arr[i]) + PD_ESC_SPACE + (i > 0 ? SKIP_ONE : 0); |
| 212 | } |
| 213 | if (*pos + required + PD_ESC_SPACE > bufsize) { |
| 214 | return; /* whole array would not fit — skip it atomically */ |
| 215 | } |
| 216 | size_t p = *pos; |
| 217 | int n = snprintf(buf + p, bufsize - p, ",\"%s\":[", key); |
| 218 | if (n <= 0 || p + (size_t)n >= bufsize - PD_ESC_SPACE) { |
| 219 | return; |
| 220 | } |
| 221 | p += (size_t)n; |
| 222 | for (int i = 0; arr[i]; i++) { |
| 223 | if (i > 0 && p < bufsize - SKIP_ONE) { |
| 224 | buf[p++] = ','; |
| 225 | } |
| 226 | if (p < bufsize - SKIP_ONE) { |
| 227 | buf[p++] = '"'; |
| 228 | } |
| 229 | /* Full escaping (not just quote/backslash): items like C param types |
| 230 | * sliced from multi-line declarations carry raw \n/\t bytes, which are |
| 231 | * invalid inside JSON strings. */ |
| 232 | for (const char *s = arr[i]; *s && p < bufsize - PD_ESC_SPACE; s++) { |
| 233 | p += (size_t)def_json_escape_char(buf + p, bufsize - p - PD_ESC_SPACE, *s); |
| 234 | } |
| 235 | if (p < bufsize - SKIP_ONE) { |
| 236 | buf[p++] = '"'; |
| 237 | } |
| 238 | } |
| 239 | if (p < bufsize - SKIP_ONE) { |
| 240 | buf[p++] = ']'; |
| 241 | } |
| 242 | buf[p] = '\0'; |
| 243 | *pos = p; |
| 244 | } |
| 245 | |
| 246 | /* Build properties JSON for a definition node. */ |
| 247 | static void build_def_props(char *buf, size_t bufsize, const CBMDefinition *def) { |
no test coverage detected