Make wildcards beginning with dots not match . and ..

https://github.com/fish-shell/fish-shell/issues/270
This commit is contained in:
ridiculousfish 2012-10-15 18:16:47 -07:00
parent 3d5a3f03fa
commit 833abc27cc
5 changed files with 59 additions and 26 deletions

View file

@ -512,7 +512,7 @@ bool contains_internal( const wchar_t *a, ... )
{ {
const wchar_t *arg; const wchar_t *arg;
va_list va; va_list va;
int res = 0; bool res = false;
CHECK( a, 0 ); CHECK( a, 0 );
@ -521,7 +521,7 @@ bool contains_internal( const wchar_t *a, ... )
{ {
if( wcscmp( a,arg) == 0 ) if( wcscmp( a,arg) == 0 )
{ {
res=1; res = true;
break; break;
} }

View file

@ -175,7 +175,7 @@ extern const wchar_t *program_name;
#define N_(wstr) wstr #define N_(wstr) wstr
/** /**
Check if the specified stringelement is a part of the specified string list Check if the specified string element is a part of the specified string list
*/ */
#define contains( str,... ) contains_internal( str, __VA_ARGS__, NULL ) #define contains( str,... ) contains_internal( str, __VA_ARGS__, NULL )

View file

@ -527,7 +527,13 @@ static int expand_test( const wchar_t *in, int flags, ... )
} }
#if 0
for (size_t idx=0; idx < output.size(); idx++)
{
printf("%ls\n", output.at(idx).completion.c_str());
}
#endif
va_start( va, flags ); va_start( va, flags );
while( (arg=va_arg(va, wchar_t *) )!= 0 ) while( (arg=va_arg(va, wchar_t *) )!= 0 )
@ -573,7 +579,22 @@ static void test_expand()
{ {
err( L"Cannot skip wildcard expansion" ); err( L"Cannot skip wildcard expansion" );
} }
if (system("mkdir -p /tmp/fish_expand_test/")) err(L"mkdir failed");
if (system("touch /tmp/fish_expand_test/.foo")) err(L"touch failed");
if (system("touch /tmp/fish_expand_test/bar")) err(L"touch failed");
// This is checking that .* does NOT match . and .. (https://github.com/fish-shell/fish-shell/issues/270). But it does have to match literal components (e.g. "./*" has to match the same as "*"
if (! expand_test( L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0 ))
{
err( L"Expansion not correctly handling dotfiles" );
}
if (! expand_test( L"/tmp/fish_expand_test/./.*", 0, L"/tmp/fish_expand_test/.foo", 0 ))
{
err( L"Expansion not correctly handling literal path components in dotfiles" );
}
//system("rm -Rf /tmp/fish_expand_test");
} }
/** Test path functions */ /** Test path functions */

View file

@ -143,29 +143,36 @@ int wildcard_has( const wchar_t *str, int internal )
\param wc The wildcard. \param wc The wildcard.
\param is_first Whether files beginning with dots should not be matched against wildcards. \param is_first Whether files beginning with dots should not be matched against wildcards.
*/ */
static int wildcard_match2( const wchar_t *str, static bool wildcard_match2(const wchar_t *str,
const wchar_t *wc, const wchar_t *wc,
int is_first ) bool is_first )
{ {
if( *str == 0 && *wc==0 ) if( *str == 0 && *wc==0 )
return 1; return true;
/* Hackish fix for https://github.com/fish-shell/fish-shell/issues/270. Prevent wildcards from matching . or .., but we must still allow literal matches. */
if (is_first && contains(str, L".", L".."))
{
/* The string is '.' or '..'. Return true if the wildcard exactly matches. */
return ! wcscmp(str, wc);
}
if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE)
{ {
/* Ignore hidden file */ /* Ignore hidden file */
if( is_first && *str == L'.' ) if( is_first && *str == L'.' )
{ {
return 0; return false;
} }
/* Try all submatches */ /* Try all submatches */
do do
{ {
if( wildcard_match2( str, wc+1, 0 ) ) if( wildcard_match2( str, wc+1, false ) )
return 1; return true;
} }
while( *(str++) != 0 ); while( *(str++) != 0 );
return 0; return false;
} }
else if( *str == 0 ) else if( *str == 0 )
{ {
@ -173,23 +180,23 @@ static int wildcard_match2( const wchar_t *str,
End of string, but not end of wildcard, and the next wildcard End of string, but not end of wildcard, and the next wildcard
element is not a '*', so this is not a match. element is not a '*', so this is not a match.
*/ */
return 0; return false;
} }
if( *wc == ANY_CHAR ) if( *wc == ANY_CHAR )
{ {
if( is_first && *str == L'.' ) if( is_first && *str == L'.' )
{ {
return 0; return false;
} }
return wildcard_match2( str+1, wc+1, 0 ); return wildcard_match2( str+1, wc+1, false );
} }
if( *wc == *str ) if( *wc == *str )
return wildcard_match2( str+1, wc+1, 0 ); return wildcard_match2( str+1, wc+1, false );
return 0; return false;
} }
/** /**
@ -303,9 +310,9 @@ bool wildcard_complete(const wcstring &str,
} }
int wildcard_match( const wcstring &str, const wcstring &wc ) bool wildcard_match( const wcstring &str, const wcstring &wc )
{ {
return wildcard_match2( str.c_str(), wc.c_str(), 1 ); return wildcard_match2( str.c_str(), wc.c_str(), true );
} }
/** /**
@ -684,7 +691,11 @@ typedef std::pair<dev_t, ino_t> file_id_t;
This function traverses the relevant directory tree looking for This function traverses the relevant directory tree looking for
matches, and recurses when needed to handle wildcrards spanning matches, and recurses when needed to handle wildcrards spanning
multiple components and recursive wildcards. multiple components and recursive wildcards.
Because this function calls itself recursively with substrings,
it's important that the parameters be raw pointers instead of wcstring,
which would be too expensive to construct for all substrings.
*/ */
static int wildcard_expand_internal( const wchar_t *wc, static int wildcard_expand_internal( const wchar_t *wc,
const wchar_t *base_dir, const wchar_t *base_dir,
@ -709,7 +720,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
/* Variables for testing for presense of recursive wildcards */ /* Variables for testing for presense of recursive wildcards */
const wchar_t *wc_recursive; const wchar_t *wc_recursive;
int is_recursive; bool is_recursive;
/* Slightly mangled version of base_dir */ /* Slightly mangled version of base_dir */
const wchar_t *dir_string; const wchar_t *dir_string;
@ -843,7 +854,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
} }
else else
{ {
if( wildcard_match2( name, wc, 1 ) ) if( wildcard_match2( name, wc, true ) )
{ {
const wcstring long_name = make_path(base_dir, next); const wcstring long_name = make_path(base_dir, next);
int skip = 0; int skip = 0;
@ -938,7 +949,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
Test if the file/directory name matches the whole Test if the file/directory name matches the whole
wildcard element, i.e. regular matching. wildcard element, i.e. regular matching.
*/ */
int whole_match = wildcard_match2( name, wc_str, 1 ); int whole_match = wildcard_match2( name, wc_str, true );
int partial_match = 0; int partial_match = 0;
/* /*
@ -951,7 +962,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
{ {
const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE ); const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE );
wchar_t *wc_sub = wcsndup( wc, end-wc+1); wchar_t *wc_sub = wcsndup( wc, end-wc+1);
partial_match = wildcard_match2( name, wc_sub, 1 ); partial_match = wildcard_match2( name, wc_sub, true );
free( wc_sub ); free( wc_sub );
} }
@ -974,8 +985,9 @@ static int wildcard_expand_internal( const wchar_t *wc,
{ {
// Insert a "file ID" into visited_files // Insert a "file ID" into visited_files
// If the insertion fails, we've already visited this file (i.e. a symlink loop) // If the insertion fails, we've already visited this file (i.e. a symlink loop)
// If we're not recursive, insert anyways (in case we loop back around in a future recursive segment), but continue on; the idea being that literal path components should still work
const file_id_t file_id(buf.st_dev, buf.st_ino); const file_id_t file_id(buf.st_dev, buf.st_ino);
if( S_ISDIR(buf.st_mode) && visited_files.insert(file_id).second) if( S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive))
{ {
size_t new_len = wcslen( new_dir ); size_t new_len = wcslen( new_dir );
new_dir[new_len] = L'/'; new_dir[new_len] = L'/';

View file

@ -75,7 +75,7 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_
\param wc The wildcard to test against \param wc The wildcard to test against
\return true if the wildcard matched \return true if the wildcard matched
*/ */
int wildcard_match( const wcstring &str, const wcstring &wc ); bool wildcard_match( const wcstring &str, const wcstring &wc );
/** /**