/** returns true if the specified path exists and is a directory */ bool Path_IsDirectory(const std::string & sPath) { std::string sFixedPath = Path_FixSlashes(sPath); if (sFixedPath.empty()) return false; char cLast = sFixedPath[sFixedPath.length() - 1]; if (cLast == '/' || cLast == '\\') sFixedPath.erase(sFixedPath.end() - 1, sFixedPath.end()); // see if the specified path actually exists. #if defined(POSIX) struct stat buf; if (stat(sFixedPath.c_str(), &buf) == -1) { return false; } #if defined( LINUX ) || defined( OSX ) return S_ISDIR(buf.st_mode); #else return (buf.st_mode & _S_IFDIR) != 0; #endif #else struct _stat buf; std::wstring wsFixedPath = UTF8to16(sFixedPath.c_str()); if (_wstat(wsFixedPath.c_str(), &buf) == -1) { return false; } return (buf.st_mode & _S_IFDIR) != 0; #endif }
/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the * specified path has a broken number of directories for its number of ..s */ std::string Path_Compact(const std::string & sRawPath, char slash) { if (slash == 0) slash = Path_GetSlash(); std::string sPath = Path_FixSlashes(sRawPath, slash); std::string sSlashString(1, slash); // strip out all /./ for (std::string::size_type i = 0; (i + 3) < sPath.length(); ) { if (sPath[i] == slash && sPath[i + 1] == '.' && sPath[i + 2] == slash) { sPath.replace(i, 3, sSlashString); } else { ++i; } } // get rid of trailing /. but leave the path separator if (sPath.length() > 2) { std::string::size_type len = sPath.length(); if (sPath[len - 1] == '.' && sPath[len - 2] == slash) { // sPath.pop_back(); sPath[len - 1] = 0; // for now, at least } } // get rid of leading ./ if (sPath.length() > 2) { if (sPath[0] == '.' && sPath[1] == slash) { sPath.replace(0, 2, ""); } } // each time we encounter .. back up until we've found the previous directory name // then get rid of both std::string::size_type i = 0; while (i < sPath.length()) { if (i > 0 && sPath.length() - i >= 2 && sPath[i] == '.' && sPath[i + 1] == '.' && (i + 2 == sPath.length() || sPath[i + 2] == slash) && sPath[i - 1] == slash) { // check if we've hit the start of the string and have a bogus path if (i == 1) return ""; // find the separator before i-1 std::string::size_type iDirStart = i - 2; while (iDirStart > 0 && sPath[iDirStart - 1] != slash) --iDirStart; // remove everything from iDirStart to i+2 sPath.replace(iDirStart, (i - iDirStart) + 3, ""); // start over i = 0; } else { ++i; } } return sPath; }
TEST( PathTools, FixSlashes ) { EXPECT_STREQ( "foo/bar/baz", Path_FixSlashes( "foo\\bar/baz", '/' ).c_str() ); EXPECT_STREQ( "/foo/bar/baz", Path_FixSlashes( "/foo\\bar/baz", '/' ).c_str() ); EXPECT_STREQ( "/foo/bar/baz", Path_FixSlashes( "\\foo\\bar/baz", '/' ).c_str() ); }