Improve fs::PathStartsWith to handle empty strings (#14877)

`""` does not refer to a proper path, and `fs::PathStartsWith(path, "")` should just return `false`. This is also the case in libraries in other languages where I looked, seems to be common.

The new behavior:
* check early, if `prefix` is empty - return if path is empty or not,
* no special processing for when `path` is empty, the function meets characters in `prefix` and returns false anyway.
This commit is contained in:
asrelo 2024-08-11 21:19:14 +03:00 committed by GitHub
parent 5b19d315b3
commit cfa9c83d33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 11 deletions

@ -697,32 +697,43 @@ bool MoveDir(const std::string &source, const std::string &target)
bool PathStartsWith(const std::string &path, const std::string &prefix) bool PathStartsWith(const std::string &path, const std::string &prefix)
{ {
if (prefix.empty())
return path.empty();
size_t pathsize = path.size(); size_t pathsize = path.size();
size_t pathpos = 0; size_t pathpos = 0;
size_t prefixsize = prefix.size(); size_t prefixsize = prefix.size();
size_t prefixpos = 0; size_t prefixpos = 0;
for(;;){ for(;;){
// Test if current characters at path and prefix are delimiter OR EOS
bool delim1 = pathpos == pathsize bool delim1 = pathpos == pathsize
|| IsDirDelimiter(path[pathpos]); || IsDirDelimiter(path[pathpos]);
bool delim2 = prefixpos == prefixsize bool delim2 = prefixpos == prefixsize
|| IsDirDelimiter(prefix[prefixpos]); || IsDirDelimiter(prefix[prefixpos]);
// Return false if it's delimiter/EOS in one path but not in the other
if(delim1 != delim2) if(delim1 != delim2)
return false; return false;
if(delim1){ if(delim1){
// Skip consequent delimiters in path, in prefix
while(pathpos < pathsize && while(pathpos < pathsize &&
IsDirDelimiter(path[pathpos])) IsDirDelimiter(path[pathpos]))
++pathpos; ++pathpos;
while(prefixpos < prefixsize && while(prefixpos < prefixsize &&
IsDirDelimiter(prefix[prefixpos])) IsDirDelimiter(prefix[prefixpos]))
++prefixpos; ++prefixpos;
// Return true if prefix has ended (at delimiter/EOS)
if(prefixpos == prefixsize) if(prefixpos == prefixsize)
return true; return true;
// Return false if path has ended (at delimiter/EOS)
// while prefix did not.
if(pathpos == pathsize) if(pathpos == pathsize)
return false; return false;
} }
else{ else{
// Skip pairwise-equal characters in path and prefix until
// delimiter/EOS in path or prefix.
// Return false if differing characters are met.
size_t len = 0; size_t len = 0;
do{ do{
char pathchar = path[pathpos+len]; char pathchar = path[pathpos+len];

@ -113,6 +113,7 @@ void TestFileSys::testPathStartsWith()
}; };
/* /*
expected fs::PathStartsWith results expected fs::PathStartsWith results
(row for every path, column for every prefix)
0 = returns false 0 = returns false
1 = returns true 1 = returns true
2 = returns false on windows, true elsewhere 2 = returns false on windows, true elsewhere
@ -122,17 +123,17 @@ void TestFileSys::testPathStartsWith()
*/ */
int expected_results[numpaths][numpaths] = { int expected_results[numpaths][numpaths] = {
{1,2,0,0,0,0,0,0,0,0,0,0}, {1,2,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0}, {0,1,1,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0}, {0,1,1,1,0,0,0,0,0,0,0,0},
{1,1,0,0,1,0,0,0,0,0,0,0}, {0,1,0,0,1,0,0,0,0,0,0,0},
{1,1,0,0,0,1,0,0,1,1,0,0}, {0,1,0,0,0,1,0,0,1,1,0,0},
{1,1,0,0,0,0,1,4,1,0,0,0}, {0,1,0,0,0,0,1,4,1,0,0,0},
{1,1,0,0,0,0,4,1,4,0,0,0}, {0,1,0,0,0,0,4,1,4,0,0,0},
{1,1,0,0,0,0,0,0,1,0,0,0}, {0,1,0,0,0,0,0,0,1,0,0,0},
{1,1,0,0,0,0,0,0,1,1,0,0}, {0,1,0,0,0,0,0,0,1,1,0,0},
{1,1,0,0,0,0,0,0,0,0,1,0}, {0,1,0,0,0,0,0,0,0,0,1,0},
{1,1,0,0,0,0,0,0,0,0,0,1}, {0,1,0,0,0,0,0,0,0,0,0,1},
}; };
for (int i = 0; i < numpaths; i++) for (int i = 0; i < numpaths; i++)