| 442 | } |
| 443 | |
| 444 | std::string EscapeInternal(absl::string_view src, bool escape_all_bytes, |
| 445 | char escape_quote_char) { |
| 446 | std::string dest; |
| 447 | // Worst case size is every byte has to be hex escaped, so 4 char for every |
| 448 | // byte. |
| 449 | dest.reserve(src.size() * 4); |
| 450 | bool last_hex_escape = false; // true if last output char was \xNN. |
| 451 | const char* p = src.data(); |
| 452 | const char* end = p + src.size(); |
| 453 | for (; p < end; ++p) { |
| 454 | unsigned char c = static_cast<unsigned char>(*p); |
| 455 | bool is_hex_escape = false; |
| 456 | switch (c) { |
| 457 | case '\n': |
| 458 | dest.append("\\n"); |
| 459 | break; |
| 460 | case '\r': |
| 461 | dest.append("\\r"); |
| 462 | break; |
| 463 | case '\t': |
| 464 | dest.append("\\t"); |
| 465 | break; |
| 466 | case '\\': |
| 467 | dest.append("\\\\"); |
| 468 | break; |
| 469 | case '\'': |
| 470 | ABSL_FALLTHROUGH_INTENDED; |
| 471 | case '\"': |
| 472 | ABSL_FALLTHROUGH_INTENDED; |
| 473 | case '`': |
| 474 | // Escape only quote chars that match escape_quote_char. |
| 475 | if (escape_quote_char == 0 || c == escape_quote_char) { |
| 476 | dest.push_back('\\'); |
| 477 | } |
| 478 | dest.push_back(c); |
| 479 | break; |
| 480 | default: |
| 481 | // Note that if we emit \xNN and the src character after that is a hex |
| 482 | // digit then that digit must be escaped too to prevent it being |
| 483 | // interpreted as part of the character code by C. |
| 484 | if ((!escape_all_bytes || c < 0x80) && |
| 485 | (!absl::ascii_isprint(c) || |
| 486 | (last_hex_escape && absl::ascii_isxdigit(c)))) { |
| 487 | dest.append("\\x"); |
| 488 | dest.push_back(kHexTable[c / 16]); |
| 489 | dest.push_back(kHexTable[c % 16]); |
| 490 | is_hex_escape = true; |
| 491 | } else { |
| 492 | dest.push_back(c); |
| 493 | break; |
| 494 | } |
| 495 | } |
| 496 | last_hex_escape = is_hex_escape; |
| 497 | } |
| 498 | dest.shrink_to_fit(); |
| 499 | return dest; |
| 500 | } |
| 501 |
no test coverage detected