From 79632dd3c68bcc12900262234e00603deba58f3b Mon Sep 17 00:00:00 2001 From: captain0xff Date: Mon, 26 Jan 2026 18:15:28 +0530 Subject: [PATCH] App: handle symlinks in Application::processFiles & DocInfo::getDocPath --- src/App/Application.cpp | 13 ++++++++++++- src/App/PropertyLinks.cpp | 3 ++- src/Base/FileInfo.cpp | 31 +++++++++++++++++++++++++++++++ src/Base/FileInfo.h | 8 ++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 60e18528fa..8c1851eabb 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -925,7 +925,7 @@ Document* Application::openDocumentPrivate(const char * FileName, if (!File.exists()) { std::stringstream str; str << "File '" << FileName << "' does not exist!"; - throw Base::FileSystemError(str.str().c_str()); + throw Base::FileSystemError(str.str()); } // Before creating a new document we check whether the document is already open @@ -2803,7 +2803,18 @@ std::list Application::processFiles(const std::list& f std::list processed; Base::Console().log("Init: Processing command line files\n"); for (const auto & it : files) { + Base::FileInfo file(it); + // Can we safely remove the isSymlink check and directly query the canonical + // path for every string? The reason for avoiding it currently is that + // getCannonicalPath will log an error if the file doesn't exist + if (file.isSymlink()) { + if (auto cannonicalPath = file.getCannonicalPath()) { + file = Base::FileInfo(*cannonicalPath); + } else { + Base::Console().error("Failed to process symlink file: %s\n", file.filePath()); + } + } Base::Console().log("Init: Processing file: %s\n",file.filePath().c_str()); diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index f7d65a4187..5cd4ff2d98 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -3349,7 +3349,8 @@ public: throw Base::RuntimeError("Owner document not saved"); } - QDir docDir(QFileInfo(QString::fromUtf8(docPath)).absoluteDir()); + QFileInfo docFileInfo{QString::fromUtf8(docPath)}; + QDir docDir(docFileInfo.canonicalPath()); if (!absolute) { path = QDir::cleanPath(docDir.absoluteFilePath(path)); if (fullPath) { diff --git a/src/Base/FileInfo.cpp b/src/Base/FileInfo.cpp index 05983c8956..fddedc1c0e 100644 --- a/src/Base/FileInfo.cpp +++ b/src/Base/FileInfo.cpp @@ -425,6 +425,16 @@ bool FileInfo::isDir() const return false; } +bool FileInfo::isSymlink() const +{ + fs::path path = stringToPath(FileName); + if (fs::exists(path)) { + return fs::is_symlink(path); + } + + return false; +} + unsigned int FileInfo::size() const { unsigned int bytes {}; @@ -566,3 +576,24 @@ std::vector FileInfo::getDirectoryContent() const return list; } + +std::optional FileInfo::getSymlinkTarget() +{ + fs::path path = stringToPath(FileName); + if (isSymlink()) { + return pathToString(fs::read_symlink(path)); + } + return std::nullopt; +} + +std::optional FileInfo::getCannonicalPath() +{ + try { + fs::path path = stringToPath(FileName); + return pathToString(fs::canonical(path)); + } + catch (const fs::filesystem_error& e) { + std::clog << e.what() << '\n'; + return std::nullopt; + } +} diff --git a/src/Base/FileInfo.h b/src/Base/FileInfo.h index 9d551c4f19..e9796aef21 100644 --- a/src/Base/FileInfo.h +++ b/src/Base/FileInfo.h @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -125,6 +126,8 @@ public: bool isFile() const; /// Checks if it is a directory (not a file) bool isDir() const; + /// Checks if it is a symbolic link (returns false if the file doesn't exist) + bool isSymlink() const; /// The size of the file unsigned int size() const; /// Returns the time when the file was last modified. @@ -153,6 +156,11 @@ public: /// Rename the file bool copyTo(const char* NewName) const; + /// Returns the folder or directory the symlink points + std::optional getSymlinkTarget(); + /// Returns the absolute path without any "..", "." or symlinks + std::optional getCannonicalPath(); + /** @name Tools */ //@{ /// Get a unique File Name in the given or (if 0) in the temp path