Skip to content

Commit

Permalink
Fix #76; parse directory token more carefully.
Browse files Browse the repository at this point in the history
`cd ../../` was getting misinterpreted because it was being passed
directly to GetFileAttributesW, which correctly parses it into an empty
string internally.

Clink must parse the input line to ensure it is a single token before
passing it to GetFileAttributesW.  And some commands such as `cd` defeat
the directory shortcut feature (if not quoted) even if there's a path
that contains them (such as an atual path named "cd\foo").
  • Loading branch information
chrisant996 committed Feb 23, 2021
1 parent df32920 commit af92b5d
Showing 1 changed file with 85 additions and 22 deletions.
107 changes: 85 additions & 22 deletions clink/app/src/host/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,89 @@ static void write_line_feed()
WriteConsoleW(handle, L"\n", 1, &written, nullptr);
}

//------------------------------------------------------------------------------
static bool parse_line_token(str_base& out, const char* line)
{
out.clear();

// Skip leading whitespace.
while (*line == ' ' || *line == '\t')
line++;

// Parse the line text.
bool first_component = true;
for (bool quoted = false; true; line++)
{
// Spaces are acceptable when quoted, otherwise it's the end of the
// token and any subsequent text defeats the directory shortcut feature.
if (*line == ' ' || *line == '\t')
{
if (!quoted)
{
// Skip trailing whitespace.
while (*line == ' ' || *line == '\t')
line++;
// Parse fails if input is more than a single token.
if (*line)
return false;
}
}

// Parse succeeds if input is one token.
if (!*line)
return out.length();

switch (*line)
{
// These characters defeat the directory shortcut feature.
case '^':
case '<':
case '|':
case '>':
case '%':
return false;

// These characters are acceptable when quoted.
case '@':
case '(':
case ')':
case '&':
case '+':
case '=':
case ';':
case ',':
if (!quoted)
return false;
break;

// Quotes toggle quote mode.
case '"':
first_component = false;
quoted = !quoted;
continue;

// These characters end a component.
case '.':
case '/':
case '\\':
if (first_component)
{
// Some commands are special and defeat the directory shortcut
// feature even if they're legitimately part of an actual path,
// unless they are quoted.
static const char* const c_commands[] = { "call", "cd", "chdir", "dir", "echo", "md", "mkdir", "popd", "pushd" };
for (const char* name : c_commands)
if (out.iequals(name))
return false;
first_component = false;
}
break;
}

out.concat(line, 1);
}
}

//------------------------------------------------------------------------------
static bool intercept_directory(str_base& inout)
{
Expand All @@ -250,29 +333,9 @@ static bool intercept_directory(str_base& inout)
return true;
}

// Skip leading whitespace.
while (*line == ' ' || *line == '\t')
line++;

// Strip all quotes (it may be surprising, but this is what CMD.EXE does).
// Parse the input for a single token.
str<> tmp;
while (*line)
{
if (*line != '\"')
tmp.concat(line, 1);
line++;
}

// Truncate trailing whitespcae.
while (tmp.length())
{
char ch = tmp.c_str()[tmp.length() - 1];
if (ch != ' ' && ch != '\t')
break;
tmp.truncate(tmp.length() - 1);
}

if (!tmp.length())
if (!parse_line_token(tmp, line))
return false;

// If all dots, convert into valid path syntax moving N-1 levels.
Expand Down

0 comments on commit af92b5d

Please sign in to comment.