Graph names and paths originate in the repository/index and are data, never * hook instructions. Keep valid UTF-8 sequences, collapse ASCII controls to a * space, and bound each field without splitting a multibyte sequence. */
| 266 | * hook instructions. Keep valid UTF-8 sequences, collapse ASCII controls to a |
| 267 | * space, and bound each field without splitting a multibyte sequence. */ |
| 268 | static void ha_sanitize_metadata(const char *input, char *output, size_t output_size) { |
| 269 | if (!output || output_size == 0U) { |
| 270 | return; |
| 271 | } |
| 272 | output[0] = '\0'; |
| 273 | if (!input) { |
| 274 | return; |
| 275 | } |
| 276 | size_t used = 0U; |
| 277 | bool previous_space = false; |
| 278 | size_t input_length = strlen(input); |
| 279 | for (size_t pos = 0U; pos < input_length && used + 1U < output_size;) { |
| 280 | unsigned char ch = (unsigned char)input[pos]; |
| 281 | if (ch < 0x20U || ch == 0x7fU) { |
| 282 | if (!previous_space && used > 0U) { |
| 283 | output[used++] = ' '; |
| 284 | previous_space = true; |
| 285 | } |
| 286 | pos++; |
| 287 | continue; |
| 288 | } |
| 289 | size_t sequence = |
| 290 | ha_utf8_sequence_length((const unsigned char *)input + pos, input_length - pos); |
| 291 | if (sequence == 0U) { |
| 292 | output[used++] = '?'; |
| 293 | previous_space = false; |
| 294 | pos++; |
| 295 | continue; |
| 296 | } |
| 297 | if (used + sequence >= output_size) { |
| 298 | break; |
| 299 | } |
| 300 | memcpy(output + used, input + pos, sequence); |
| 301 | used += sequence; |
| 302 | pos += sequence; |
| 303 | previous_space = ch == ' '; |
| 304 | } |
| 305 | while (used > 0U && output[used - 1U] == ' ') { |
| 306 | used--; |
| 307 | } |
| 308 | output[used] = '\0'; |
| 309 | } |
| 310 | |
| 311 | #ifdef CBM_CLI_ENABLE_TEST_API |
| 312 | void cbm_hook_sanitize_metadata_for_testing(const char *input, char *output, size_t output_size) { |
no test coverage detected