Simplify handling of escape sequences in echo -e

This commit is contained in:
ridiculousfish 2012-10-17 17:08:45 -07:00
parent 57de1388e8
commit e52cf09bc1

View file

@ -1463,8 +1463,10 @@ static int builtin_functions( parser_t &parser, wchar_t **argv )
return res; return res;
} }
static unsigned int builtin_echo_octal_digit(wchar_t wc) static unsigned int builtin_echo_digit(wchar_t wc, unsigned int base)
{ {
// base must be hex or octal
assert(base == 8 || base == 16);
switch (wc) switch (wc)
{ {
case L'0': return 0; case L'0': return 0;
@ -1475,22 +1477,20 @@ static unsigned int builtin_echo_octal_digit(wchar_t wc)
case L'5': return 5; case L'5': return 5;
case L'6': return 6; case L'6': return 6;
case L'7': return 7; case L'7': return 7;
default: return UINT_MAX;
} }
}
static unsigned int builtin_echo_hex_digit(wchar_t wc) if (base == 16) switch (wc)
{
switch (wc)
{ {
case L'8': return 8;
case L'9': return 9;
case L'a': case L'A': return 10; case L'a': case L'A': return 10;
case L'b': case L'B': return 11; case L'b': case L'B': return 11;
case L'c': case L'C': return 12; case L'c': case L'C': return 12;
case L'd': case L'D': return 13; case L'd': case L'D': return 13;
case L'e': case L'E': return 14; case L'e': case L'E': return 14;
case L'f': case L'F': return 15; case L'f': case L'F': return 15;
default: return builtin_echo_octal_digit(wc);
} }
return UINT_MAX;
} }
/* Parse a numeric escape sequence in str, returning whether we succeeded. /* Parse a numeric escape sequence in str, returning whether we succeeded.
@ -1504,41 +1504,46 @@ static unsigned int builtin_echo_hex_digit(wchar_t wc)
static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *consumed, unsigned char *out_val) static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *consumed, unsigned char *out_val)
{ {
bool success = false; bool success = false;
unsigned char val = 0; unsigned char val = 0; //resulting character
size_t idx = 0; unsigned int start = 0; //the first character of the numeric part of the sequence
if (builtin_echo_octal_digit(str[0]) != UINT_MAX)
unsigned int base = 0, max_digits = 0;
if (builtin_echo_digit(str[0], 8) != UINT_MAX)
{ {
// Octal escape
base = 8;
// If the first digit is a 0, we allow four digits (including that zero) // If the first digit is a 0, we allow four digits (including that zero)
// Otherwise we allow 3. // Otherwise we allow 3.
unsigned int max_digits = (str[0] == L'0' ? 4 : 3); max_digits = (str[0] == L'0' ? 4 : 3);
for (idx = 0; idx < max_digits; idx++)
{
unsigned int digit = builtin_echo_octal_digit(str[idx]);
if (digit == UINT_MAX)
break;
val = val * 8 + digit;
}
// Can't fail, since we know we had at least one octal digit at str[1]
success = true;
} }
else if (str[0] == L'x') else if (str[0] == L'x')
{ {
// Hex escape // Hex escape
for (idx = 1; idx < 3; idx++) base = 16;
{ max_digits = 2;
unsigned int digit = builtin_echo_hex_digit(str[idx]);
if (digit == UINT_MAX) // Skip the x
break; start = 1;
val = val * 16 + digit;
}
// Requires at least one digit
success = (idx > 1);
} }
if (success) if (base != 0)
{ {
*consumed = idx; unsigned int idx;
*out_val = val; for (idx = start; idx < start + max_digits; idx++)
{
unsigned int digit = builtin_echo_digit(str[idx], base);
if (digit == UINT_MAX) break;
val = val * base + digit;
}
// We succeeded if we consumed at least one digit
if (idx > start)
{
*consumed = idx;
*out_val = val;
success = true;
}
} }
return success; return success;
} }