From 74027e7297009092d1adc301892b65523698c893 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Tue, 20 Jan 2026 21:27:58 +0800 Subject: [PATCH] fix: fix linux runtime bugs - use std::filesystem::read_symlink for symlink reading instead of complex homemade linux-syscall-based function. std function is more robust than I written. - fix linux command line argument getter issue. --- src/yycc/env.cpp | 92 +++++------------------------------------------- 1 file changed, 8 insertions(+), 84 deletions(-) diff --git a/src/yycc/env.cpp b/src/yycc/env.cpp index 59e8ee8..4a9351f 100644 --- a/src/yycc/env.cpp +++ b/src/yycc/env.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #if defined(YYCC_OS_WINDOWS) #include "encoding/windows.hpp" @@ -241,84 +242,6 @@ namespace yycc::env { return std::filesystem::current_path(); } -#if defined(YYCC_OS_LINUX) - - /// @brief All possible error occurs when reading Linux symlink target. - enum class ReadSymlinkTargetError { - SysCall, ///< Fail to call system call. - Truncated, ///< Expected path to target was truncated. - Others, ///< Any other errors - }; - - /** - * @brief An utility function for convenient reading symlink target on Linux. - * @param[in] link The path to symlink where the target is read. - * @return The read path to to target, or error occurs. - */ - static std::expected read_symlink_target(const std::string_view &link) { - // Reference: https://www.man7.org/linux/man-pages/man2/readlink.2.html - - // String view is not NUL terminated. - // Create an string container for it. - std::string path(link); - - // Get the expected size. - // Query this symlink info first. - struct stat sb; - if (lstat(path.c_str(), &sb) != 0) { - return std::unexpected(ReadSymlinkTargetError::SysCall); - } - // Fetch the size of target path in gotten struct. - // And cast it into expected type. - auto expected_size = SAFECAST::try_to(sb.st_size); - if (!expected_size.has_value()) { - return std::unexpected(ReadSymlinkTargetError::Others); - } - auto buf_size = expected_size.value(); - // Some magic symlinks under (for example) /proc and /sys report 'st_size' as zero. - // In that case, take PATH_MAX as a "good enough" estimate. - if (buf_size == 0) { - buf_size = PATH_MAX; - } - - // Prepare return value and allocate it with previous gotten size. - std::u8string rv(u8'\0', buf_size); - - // Copy data into result value. - // Add one to the link size, so that we can determine whether - // the buffer returned by readlink() was truncated. - // Also, due to the add operation, we need do overflow checks. - auto passed_size = SAFEOP::checked_add(buf_size, 1); - if (!passed_size.has_value()) { - return std::unexpected(ReadSymlinkTargetError::Others); - } - // Read data into result value. - ssize_t nbytes = readlink(path.c_str(), REINTERPRET::as_ordinary(rv.data()), passed_size.value()); - if (nbytes < 0) { - return std::unexpected(ReadSymlinkTargetError::SysCall); - } - - // Check written size - // Cast it type into expected type. - auto written_size = SAFECAST::try_to(nbytes); - if (!written_size.has_value()) { - return std::unexpected(ReadSymlinkTargetError::Others); - } - // If the return value was equal to the buffer size, then - // the link target was larger than expected (perhaps because the - // target was changed between the call to lstat() and the call to - // readlink()). Return error instead. - if (written_size.value() != buf_size) { - // TODO: There must be a better solution to this truncated issue than simply return error. - return std::unexpected(ReadSymlinkTargetError::Truncated); - } - - // Everything is okey - return rv; - } - -#endif - PathResult current_exe() { std::u8string rv; @@ -328,9 +251,10 @@ namespace yycc::env { else return std::unexpected(PathError::SysCall); #elif defined(YYCC_OS_LINUX) // Reference: https://www.man7.org/linux/man-pages/man5/proc_pid_exe.5.html - auto target = read_symlink_target("/proc/self/exe"); - if (target.has_value()) return rv = std::move(target.value()); - else return std::unexpected(PathError::SysCall); + std::error_code ec; + auto target = std::filesystem::read_symlink(std::filesystem::path("/proc/self/exe"), ec); + if (ec) return std::unexpected(PathError::SysCall); + else rv = REINTERPRET::as_utf8(target.string()); #elif defined(YYCC_OS_MACOS) // TODO: This is AI generated and don't have test and reference. std::string buffer(PATH_MAX, '\0'); @@ -437,10 +361,10 @@ namespace yycc::env { while (true) { // We use NUL as delimiter std::getline(cmdline, arg, '\0'); + // Check whether we reach EOF + if (cmdline.eof()) break; // Check whether reading is okey. - if (!cmdline.good()) return std::unexpected(ArgError::Others); - // If return string is empty, it means that we reach the tail. - if (arg.empty()) break; + if (cmdline.fail()) return std::unexpected(ArgError::Others); // Push this argument into result. rv.emplace_back(REINTERPRET::as_utf8(arg));