'echo -' should output a dash instead of treating it as options

Fixes #1459
This commit is contained in:
ridiculousfish 2014-05-16 15:19:07 +08:00
parent c7aca5cc35
commit 3cbace98a7
3 changed files with 53 additions and 39 deletions

View file

@ -1610,62 +1610,70 @@ static int builtin_echo(parser_t &parser, wchar_t **argv)
if (! *argv++) if (! *argv++)
return STATUS_BUILTIN_ERROR; return STATUS_BUILTIN_ERROR;
/* Process options */ /* Process options. Options must come at the beginning - the first non-option kicks us out. */
bool print_newline = true, print_spaces = true, interpret_special_chars = false; bool print_newline = true, print_spaces = true, interpret_special_chars = false;
while (*argv) size_t option_idx = 0;
for (option_idx = 0; argv[option_idx] != NULL; option_idx++)
{ {
wchar_t *s = *argv, c = *s; const wchar_t *arg = argv[option_idx];
if (c == L'-') assert(arg != NULL);
bool arg_is_valid_option = false;
if (arg[0] == L'-')
{ {
/* Ensure that option is valid */ // We have a leading dash. Ensure that every subseqnent character is a valid option.
for (++s, c = *s; c != L'\0'; c = *(++s)) size_t i = 1;
while (arg[i] != L'\0' && wcschr(L"nesE", arg[i]) != NULL)
{ {
if (c != L'n' && c != L'e' && c != L's' && c != L'E') i++;
{
goto invalid_echo_option;
}
}
/* Parse option */
for (s = *argv, ++s, c = *s; c != L'\0'; c = *(++s))
{
switch (c)
{
case L'n':
print_newline = false;
break;
case L'e':
interpret_special_chars = true;
break;
case L's':
// fish-specific extension,
// which we should try to nix
print_spaces = false;
break;
case L'E':
interpret_special_chars = false;
break;
}
} }
// We must have at least two characters to be a valid option, and have consumed the whole string
arg_is_valid_option = (i >= 2 && arg[i] == L'\0');
} }
else
if (! arg_is_valid_option)
{ {
invalid_echo_option: // This argument is not an option, so there are no more options
break; break;
} }
argv++;
// Ok, we are sure the argument is an option. Parse it.
assert(arg_is_valid_option);
for (size_t i=1; arg[i] != L'\0'; i++)
{
switch (arg[i])
{
case L'n':
print_newline = false;
break;
case L'e':
interpret_special_chars = true;
break;
case L's':
print_spaces = false;
break;
case L'E':
interpret_special_chars = false;
break;
default:
assert(0 && "Unexpected character in builtin_echo argument");
break;
}
}
} }
/* The special character \c can be used to indicate no more output */ /* The special character \c can be used to indicate no more output */
bool continue_output = true; bool continue_output = true;
for (size_t idx = 0; continue_output && argv[idx] != NULL; idx++) /* Skip over the options */
const wchar_t * const *args_to_echo = argv + option_idx;
for (size_t idx = 0; continue_output && args_to_echo[idx] != NULL; idx++)
{ {
if (print_spaces && idx > 0) if (print_spaces && idx > 0)
{
stdout_buffer.push_back(' '); stdout_buffer.push_back(' ');
}
const wchar_t *str = argv[idx]; const wchar_t *str = args_to_echo[idx];
for (size_t j=0; continue_output && str[j]; j++) for (size_t j=0; continue_output && str[j]; j++)
{ {
if (! interpret_special_chars || str[j] != L'\\') if (! interpret_special_chars || str[j] != L'\\')
@ -1736,12 +1744,16 @@ invalid_echo_option:
j += consumed; j += consumed;
if (continue_output) if (continue_output)
{
stdout_buffer.push_back(wc); stdout_buffer.push_back(wc);
}
} }
} }
} }
if (print_newline && continue_output) if (print_newline && continue_output)
{
stdout_buffer.push_back('\n'); stdout_buffer.push_back('\n');
}
return STATUS_BUILTIN_OK; return STATUS_BUILTIN_OK;
} }

View file

@ -116,6 +116,7 @@ echo -e 'abc\121def'
echo -e 'abc\1212def' echo -e 'abc\1212def'
echo -e 'abc\cdef' # won't output a newline! echo -e 'abc\cdef' # won't output a newline!
echo '' echo ''
echo -
echo -e Catch your breath echo -e Catch your breath

View file

@ -33,6 +33,7 @@ abc!def
abcQdef abcQdef
abcQ2def abcQ2def
abc abc
-
Catch your breath Catch your breath
abc!def abc!def
abc!1def abc!1def