static char eat_escape_code(void) { int c, coded_char; c = input(); switch (c) { case 0: /* i.e., EOF */ unput(c); return(c); case '\n': return(0); case 'n': return('\n'); case 't': return('\t'); case 'b': return('\b'); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': coded_char = c - '0'; c = input(); if (!is_octal_digit(c)) { unput(c); return(coded_char); } coded_char = coded_char*8 + c-'0'; c = input(); if (!is_octal_digit(c)) { unput(c); return(coded_char); } return(coded_char*8 + c-'0'); default: return(c); } }
/* Print a \ escape sequence starting at ESCSTART. Return the number of characters in the escape sequence besides the backslash. If OCTAL_0 is nonzero, octal escapes are of the form \0ooo, where o is an octal digit; otherwise they are of the form \ooo. */ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) { const wchar_t *p = escstart + 1; int esc_value = 0; /* Value of \nnn escape. */ int esc_length; /* Length of \nnn escape. */ if (*p == L'x') { /* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */ for (esc_length = 0, ++p; esc_length < 2 && is_hex_digit(*p); ++esc_length, ++p) esc_value = esc_value * 16 + hex_to_bin(*p); if (esc_length == 0) this->fatal_error(_(L"missing hexadecimal number in escape")); this->append_format_output(L"%lc", esc_value); } else if (is_octal_digit(*p)) { /* Parse \0ooo (if octal_0 && *p == L'0') or \ooo (otherwise). Allow \ooo if octal_0 && *p != L'0'; this is an undocumented extension to POSIX that is compatible with Bash 2.05b. */ for (esc_length = 0, p += octal_0 && *p == L'0'; esc_length < 3 && is_octal_digit(*p); ++esc_length, ++p) esc_value = esc_value * 8 + octal_to_bin(*p); this->append_format_output(L"%c", esc_value); } else if (*p && wcschr(L"\"\\abcfnrtv", *p)) print_esc_char(*p++); else if (*p == L'u' || *p == L'U') { wchar_t esc_char = *p; p++; uint32_t uni_value = 0; for (size_t esc_length = 0; esc_length < (esc_char == L'u' ? 4 : 8); esc_length++) { if (! is_hex_digit(*p)) { /* Escape sequence must be done. Complain if we didn't get anything */ if (esc_length == 0) { this->fatal_error(_(L"Missing hexadecimal number in Unicode escape")); } break; } uni_value = uni_value * 16 + hex_to_bin(*p); p++; } /* PCA GNU printf respects the limitations described in ISO N717, about which universal characters "shall not" be specified. I believe this limitation is for the benefit of compilers; I see no reason to impose it in builtin_printf. If __STDC_ISO_10646__ is defined, then it means wchar_t can and does hold Unicode code points, so just use that. If not defined, use the %lc printf conversion; this probably won't do anything good if your wide character set is not Unicode, but such platforms are exceedingly rare. */ if (uni_value > 0x10FFFF) { this->fatal_error(_(L"Unicode character out of range: \\%c%0*x"), esc_char, (esc_char == L'u' ? 4 : 8), uni_value); } else { #if defined(__STDC_ISO_10646__) this->append_output(uni_value); #else this->append_format_output(L"%lc", uni_value); } #endif } else { this->append_format_output(L"%lc", L'\\'); if (*p) { this->append_format_output(L"%lc", *p); p++; } } return p - escstart - 1; }
static bool is_escape_char(char const *& s, unsigned& result) { unsigned d1, d2, d3; if (*s != '\\' || *(s + 1) == 0) { return false; } if (*(s + 1) == 'x' && is_hex_digit(*(s + 2), d1) && is_hex_digit(*(s + 3), d2)) { result = d1*16 + d2; s += 4; return true; } /* C-standard octal escapes: either 1, 2, or 3 octal digits, * stopping either at 3 digits or at the first non-digit character. */ /* 1 octal digit */ if (is_octal_digit(*(s + 1), d1) && !is_octal_digit(*(s + 2), d2)) { result = d1; s += 2; return true; } /* 2 octal digits */ if (is_octal_digit(*(s + 1), d1) && is_octal_digit(*(s + 2), d2) && !is_octal_digit(*(s + 3), d3)) { result = d1 * 8 + d2; s += 3; return true; } /* 3 octal digits */ if (is_octal_digit(*(s + 1), d1) && is_octal_digit(*(s + 2), d2) && is_octal_digit(*(s + 3), d3)) { result = d1*64 + d2*8 + d3; s += 4; return true; } switch (*(s + 1)) { case 'a': result = '\a'; s += 2; return true; case 'b': result = '\b'; s += 2; return true; #if 0 case 'e': result = '\e'; s += 2; return true; #endif case 'f': result = '\f'; s += 2; return true; case 'n': result = '\n'; s += 2; return true; case 'r': result = '\r'; s += 2; return true; case 't': result = '\t'; s += 2; return true; case 'v': result = '\v'; s += 2; return true; default: result = *(s + 1); s += 2; return true; } return false; }