| 1484 | } |
| 1485 | |
| 1486 | static size_t append_args_json(char *buf, size_t bufsize, size_t pos, const CBMCall *call) { |
| 1487 | if (call->arg_count == 0 || pos >= bufsize - PP_ARGS_MARGIN) { |
| 1488 | return pos; |
| 1489 | } |
| 1490 | int n = snprintf(buf + pos, bufsize - pos, ",\"args\":["); |
| 1491 | if (n <= 0) { |
| 1492 | return pos; |
| 1493 | } |
| 1494 | pos += (size_t)n; |
| 1495 | for (int i = 0; i < call->arg_count && pos < bufsize - CBM_ARG_JSON_GUARD; i++) { |
| 1496 | const CBMCallArg *a = &call->args[i]; |
| 1497 | size_t mark = pos; /* rollback point (before the separator) */ |
| 1498 | if (i > 0 && pos < bufsize - SKIP_ONE) { |
| 1499 | buf[pos++] = ','; |
| 1500 | } |
| 1501 | char expr_buf[CBM_SZ_128]; |
| 1502 | sanitize_expr(expr_buf, a->expr); |
| 1503 | n = format_call_arg(buf + pos, bufsize - pos, a, expr_buf); |
| 1504 | /* snprintf returns the UNtruncated length: if the arg did not fully |
| 1505 | * fit, advancing pos by n would push it past buf and the buf[pos] |
| 1506 | * writes below would overflow. Drop the arg whole (atomic field — |
| 1507 | * keeps the array valid) and stop appending. */ |
| 1508 | if (n <= 0 || (size_t)n >= bufsize - pos) { |
| 1509 | pos = mark; |
| 1510 | break; |
| 1511 | } |
| 1512 | pos += (size_t)n; |
| 1513 | } |
| 1514 | if (pos < bufsize - SKIP_ONE) { |
| 1515 | buf[pos++] = ']'; |
| 1516 | } |
| 1517 | buf[pos] = '\0'; |
| 1518 | return pos; |
| 1519 | } |
| 1520 | |
| 1521 | /* Scan call args for a URL-like route path and handler reference. */ |
| 1522 | static bool is_path_keyword(const char *keyword) { |
no test coverage detected