diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7e38e13 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "googletest"] + path = googletest + url = https://github.com/google/googletest diff --git a/fileop.cpp b/fileop.cpp index 0e42672..e503901 100644 --- a/fileop.cpp +++ b/fileop.cpp @@ -419,7 +419,6 @@ bool fileop::mkdirs(std::string path, int mode, bool allow_exists) { } bool fileop::get_file_size(std::string path, size_t& size) { - if (!exists(path)) return false; #if _WIN32 UINT cp[] = { CP_UTF8, CP_OEMCP, CP_ACP }; int i; @@ -545,3 +544,126 @@ int fileop::fcloseall() { return 0; #endif } + +std::string fileop::relpath(std::string path, std::string start) { + // 如果 start 为空,使用当前目录 + if (start.empty()) { + start = "."; + } + + // 将 Windows 风格的路径分隔符统一转换为 '/' + auto normalize_path = [](std::string& p) { + std::string result = p; + for (auto& c : result) { + if (c == '\\') c = '/'; + } + // 确保路径末尾没有斜杠 + if (!result.empty() && result.back() == '/') { + result.pop_back(); + } + return result; + }; + + std::string norm_path = normalize_path(path); + std::string norm_start = normalize_path(start); + + // 如果两个路径相同,返回当前目录 + if (norm_path == norm_start) { + return "."; + } + + // 如果 path 是绝对路径但 start 不是,或者反之,无法计算相对路径 +#ifdef _WIN32 + bool path_is_abs = isabs(path); + bool start_is_abs = isabs(start); + + // 在 Windows 上,还要检查驱动器号是否相同 + if (path_is_abs && start_is_abs) { + if (norm_path.length() >= 2 && norm_start.length() >= 2) { + // 如果驱动器号不同,无法计算相对路径 + if (tolower(norm_path[0]) != tolower(norm_start[0]) || norm_path[1] != ':') { + return path; // 返回原路径 + } + } + } +#else + bool path_is_abs = !norm_path.empty() && norm_path[0] == '/'; + bool start_is_abs = !norm_start.empty() && norm_start[0] == '/'; +#endif + + if (path_is_abs != start_is_abs) { + return path; // 无法计算相对路径,返回原路径 + } + + // 分割路径为组件 + auto split_path = [](const std::string& p) { + std::vector components; + std::string::size_type start = 0; + std::string::size_type end = 0; + + while ((end = p.find('/', start)) != std::string::npos) { + if (end != start) { + components.push_back(p.substr(start, end - start)); + } + start = end + 1; + } + + if (start < p.length()) { + components.push_back(p.substr(start)); + } + + return components; + }; + + auto path_components = split_path(norm_path); + auto start_components = split_path(norm_start); + + // 找到公共前缀 + size_t i = 0; + while (i < path_components.size() && i < start_components.size()) { +#ifdef _WIN32 + // Windows 路径不区分大小写 + if (str_util::tolower(path_components[i]) != str_util::tolower(start_components[i])) { + break; + } +#else + if (path_components[i] != start_components[i]) { + break; + } +#endif + i++; + } + + // 构建相对路径 + std::string result; + + // 添加 ".." 以表示上级目录 + for (size_t j = i; j < start_components.size(); j++) { + if (!result.empty()) { + result += "/"; + } + result += ".."; + } + + // 添加目标路径的非共享部分 + for (size_t j = i; j < path_components.size(); j++) { + if (!result.empty()) { + result += "/"; + } + result += path_components[j]; + } + + // 如果结果为空,表示当前目录 + if (result.empty()) { + return "."; + } + +#ifdef _WIN32 + // 在 Windows 上,将斜杠转换回反斜杠 + for (auto& c : result) { + if (c == '/') c = '\\'; + } +#endif + + return result; +} diff --git a/fileop.h b/fileop.h index 9daeda2..4996e68 100644 --- a/fileop.h +++ b/fileop.h @@ -175,5 +175,12 @@ namespace fileop { * @return 0(>=0) succeed. EOF error happened. */ int fcloseall(); + /** + * @brief Return a relative path + * @param path Path + * @param start Start path + * @return Result + */ + std::string relpath(std::string path, std::string start = ""); } #endif diff --git a/googletest b/googletest index 1d17ea1..0bdccf4 160000 --- a/googletest +++ b/googletest @@ -1 +1 @@ -Subproject commit 1d17ea141d2c11b8917d2c7d029f1c4e2b9769b2 +Subproject commit 0bdccf4aa2f5c67af967193caf31d42d5c49bde2 diff --git a/str_util.cpp b/str_util.cpp index 2d28d60..ede0d9c 100644 --- a/str_util.cpp +++ b/str_util.cpp @@ -30,6 +30,18 @@ bool str_util::touppercase(std::string ori, std::string& result) { } } +std::string str_util::tolower(std::string ori) { + std::string result; + tolowercase(ori, result); + return result; +} + +std::string str_util::toupper(std::string ori) { + std::string result; + touppercase(ori, result); + return result; +} + std::string str_util::str_replace(std::string input, std::string pattern, std::string new_content) { auto loc = input.find(pattern, 0); auto len = pattern.length(); diff --git a/str_util.h b/str_util.h index 320a884..b798767 100644 --- a/str_util.h +++ b/str_util.h @@ -19,6 +19,8 @@ namespace str_util { * @return true if successed. */ bool touppercase(std::string ori, std::string& result); + std::string tolower(std::string ori); + std::string toupper(std::string ori); /** * @brief Replace all pattern to new_content * @param input Input string