diff --git a/common.cpp b/common.cpp index 0e349f9e8..b8dc4ceb7 100644 --- a/common.cpp +++ b/common.cpp @@ -512,7 +512,7 @@ bool contains_internal( const wchar_t *a, ... ) { const wchar_t *arg; va_list va; - int res = 0; + bool res = false; CHECK( a, 0 ); @@ -521,7 +521,7 @@ bool contains_internal( const wchar_t *a, ... ) { if( wcscmp( a,arg) == 0 ) { - res=1; + res = true; break; } diff --git a/common.h b/common.h index 3413d69ca..313ffba75 100644 --- a/common.h +++ b/common.h @@ -175,7 +175,7 @@ extern const wchar_t *program_name; #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 ) diff --git a/fish_tests.cpp b/fish_tests.cpp index 438254b4b..cc39d3a2c 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -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 ); while( (arg=va_arg(va, wchar_t *) )!= 0 ) @@ -573,7 +579,22 @@ static void test_expand() { 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 */ diff --git a/wildcard.cpp b/wildcard.cpp index db42fadc8..d32c8e01b 100644 --- a/wildcard.cpp +++ b/wildcard.cpp @@ -143,29 +143,36 @@ int wildcard_has( const wchar_t *str, int internal ) \param wc The wildcard. \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, - int is_first ) + bool is_first ) { 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) { /* Ignore hidden file */ if( is_first && *str == L'.' ) { - return 0; + return false; } /* Try all submatches */ do { - if( wildcard_match2( str, wc+1, 0 ) ) - return 1; + if( wildcard_match2( str, wc+1, false ) ) + return true; } while( *(str++) != 0 ); - return 0; + return false; } 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 element is not a '*', so this is not a match. */ - return 0; + return false; } if( *wc == ANY_CHAR ) { 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 ) - 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 file_id_t; This function traverses the relevant directory tree looking for 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, 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 */ const wchar_t *wc_recursive; - int is_recursive; + bool is_recursive; /* Slightly mangled version of base_dir */ const wchar_t *dir_string; @@ -843,7 +854,7 @@ static int wildcard_expand_internal( const wchar_t *wc, } else { - if( wildcard_match2( name, wc, 1 ) ) + if( wildcard_match2( name, wc, true ) ) { const wcstring long_name = make_path(base_dir, next); 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 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; /* @@ -951,7 +962,7 @@ static int wildcard_expand_internal( const wchar_t *wc, { const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE ); 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 ); } @@ -974,8 +985,9 @@ static int wildcard_expand_internal( const wchar_t *wc, { // Insert a "file ID" into visited_files // 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); - 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 ); new_dir[new_len] = L'/'; diff --git a/wildcard.h b/wildcard.h index 40830f0cc..9380375c5 100644 --- a/wildcard.h +++ b/wildcard.h @@ -75,7 +75,7 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_ \param wc The wildcard to test against \return true if the wildcard matched */ -int wildcard_match( const wcstring &str, const wcstring &wc ); +bool wildcard_match( const wcstring &str, const wcstring &wc ); /**