diff options
| author | Dan Engelbrecht <[email protected]> | 2025-03-12 18:58:24 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-03-12 18:58:24 +0100 |
| commit | 908f99b749fbbf9754f9485d680914792034334c (patch) | |
| tree | 2d4e70fa49d3c95c509f230cb2dc5269358a3b59 /src | |
| parent | Remove spurious '4' in conditional code block (diff) | |
| download | zen-908f99b749fbbf9754f9485d680914792034334c.tar.xz zen-908f99b749fbbf9754f9485d680914792034334c.zip | |
fix quoted command lines arguments (#306)
Handling of quotes and quotes with leading backslash for command line parsing - UE-231677
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/admin_cmd.cpp | 19 | ||||
| -rw-r--r-- | src/zen/cmds/admin_cmd.h | 8 | ||||
| -rw-r--r-- | src/zen/cmds/builds_cmd.cpp | 119 | ||||
| -rw-r--r-- | src/zen/cmds/builds_cmd.h | 38 | ||||
| -rw-r--r-- | src/zen/cmds/status_cmd.cpp | 9 | ||||
| -rw-r--r-- | src/zen/cmds/status_cmd.h | 6 | ||||
| -rw-r--r-- | src/zen/cmds/up_cmd.cpp | 43 | ||||
| -rw-r--r-- | src/zen/cmds/up_cmd.h | 28 | ||||
| -rw-r--r-- | src/zen/cmds/workspaces_cmd.cpp | 91 | ||||
| -rw-r--r-- | src/zen/cmds/workspaces_cmd.h | 34 | ||||
| -rw-r--r-- | src/zen/zen.cpp | 18 | ||||
| -rw-r--r-- | src/zencore/filesystem.cpp | 13 | ||||
| -rw-r--r-- | src/zencore/include/zencore/filesystem.h | 2 | ||||
| -rw-r--r-- | src/zencore/include/zencore/process.h | 3 | ||||
| -rw-r--r-- | src/zencore/process.cpp | 142 |
15 files changed, 391 insertions, 182 deletions
diff --git a/src/zen/cmds/admin_cmd.cpp b/src/zen/cmds/admin_cmd.cpp index 995ed4136..835e01151 100644 --- a/src/zen/cmds/admin_cmd.cpp +++ b/src/zen/cmds/admin_cmd.cpp @@ -714,26 +714,29 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) throw OptionParseException("data path must be given"); } - if (!std::filesystem::is_directory(m_DataPath)) + std::filesystem::path DataPath = StringToPath(m_DataPath); + std::filesystem::path TargetPath = StringToPath(m_TargetPath); + + if (!std::filesystem::is_directory(DataPath)) { throw OptionParseException("data path must exist"); } - if (m_TargetPath.empty()) + if (TargetPath.empty()) { throw OptionParseException("target path must be given"); } - std::filesystem::path RootManifestPath = m_DataPath / "root_manifest"; - std::filesystem::path TargetRootManifestPath = m_TargetPath / "root_manifest"; + std::filesystem::path RootManifestPath = DataPath / "root_manifest"; + std::filesystem::path TargetRootManifestPath = TargetPath / "root_manifest"; if (!TryCopy(RootManifestPath, TargetRootManifestPath)) { throw OptionParseException("data path is invalid, missing root_manifest"); } - std::filesystem::path CachePath = m_DataPath / "cache"; - std::filesystem::path TargetCachePath = m_TargetPath / "cache"; + std::filesystem::path CachePath = DataPath / "cache"; + std::filesystem::path TargetCachePath = TargetPath / "cache"; // Copy cache state DirectoryContent CacheDirectoryContent; @@ -778,8 +781,8 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } - std::filesystem::path CasPath = m_DataPath / "cas"; - std::filesystem::path TargetCasPath = m_TargetPath / "cas"; + std::filesystem::path CasPath = DataPath / "cas"; + std::filesystem::path TargetCasPath = TargetPath / "cas"; { std::filesystem::path UCasRootPath = CasPath / ".ucas_root"; diff --git a/src/zen/cmds/admin_cmd.h b/src/zen/cmds/admin_cmd.h index c593b2cac..8b6d3e258 100644 --- a/src/zen/cmds/admin_cmd.h +++ b/src/zen/cmds/admin_cmd.h @@ -155,10 +155,10 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"copy-state", "Copy zen server disk state"}; - std::filesystem::path m_DataPath; - std::filesystem::path m_TargetPath; - bool m_SkipLogs = false; + cxxopts::Options m_Options{"copy-state", "Copy zen server disk state"}; + std::string m_DataPath; + std::string m_TargetPath; + bool m_SkipLogs = false; }; } // namespace zen diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp index d3536768b..f0ee4904e 100644 --- a/src/zen/cmds/builds_cmd.cpp +++ b/src/zen/cmds/builds_cmd.cpp @@ -6689,7 +6689,7 @@ BuildsCommand::BuildsCommand() m_Options.add_options()("h,help", "Print help"); auto AddAuthOptions = [this](cxxopts::Options& Ops) { - Ops.add_option("", "", "system-dir", "Specify system root", cxxopts::value<std::filesystem::path>(m_SystemRootDir), "<systemdir>"); + Ops.add_option("", "", "system-dir", "Specify system root", cxxopts::value<std::string>(m_SystemRootDir), "<systemdir>"); // Direct access token (may expire) Ops.add_option("auth-token", @@ -7025,7 +7025,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) auto CreateAuthMgr = [&]() { if (!Auth) { - std::filesystem::path DataRoot = m_SystemRootDir.empty() ? PickDefaultSystemRootDirectory() : m_SystemRootDir; + std::filesystem::path DataRoot = m_SystemRootDir.empty() ? PickDefaultSystemRootDirectory() : StringToPath(m_SystemRootDir); if (m_EncryptionKey.empty()) { @@ -7147,8 +7147,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else if (!m_StoragePath.empty()) { - ZEN_CONSOLE("Querying builds in folder '{}'.", m_StoragePath); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + ZEN_CONSOLE("Querying builds in folder '{}'.", StoragePath); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); } else { @@ -7199,9 +7200,11 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } + std::filesystem::path Path = StringToPath(m_Path); + if (m_BuildPartName.empty()) { - m_BuildPartName = m_Path.filename().string(); + m_BuildPartName = Path.filename().string(); } const bool GeneratedBuildId = m_BuildId.empty(); @@ -7241,26 +7244,27 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("Uploading '{}' from '{}' to cloud endpoint '{}'. SessionId: '{}'. Namespace '{}', Bucket '{}', {}BuildId '{}'", m_BuildPartName, - m_Path, + Path, m_BuildsUrl, Http.GetSessionId(), m_Namespace, m_Bucket, GeneratedBuildId ? "Generated " : "", BuildId); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } else if (!m_StoragePath.empty()) { + std::filesystem::path StoragePath = StringToPath(m_StoragePath); ZEN_CONSOLE("Uploading '{}' from '{}' to folder '{}'. {}BuildId '{}'", m_BuildPartName, - m_Path, - m_StoragePath, + Path, + StoragePath, GeneratedBuildId ? "Generated " : "", BuildId); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, m_WriteMetadataAsJson, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, m_WriteMetadataAsJson, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { @@ -7303,7 +7307,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildId, BuildPartId, m_BuildPartName, - m_Path, + Path, m_ManifestPath, m_BlockReuseMinPercentLimit, m_AllowMultiparts, @@ -7372,6 +7376,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } + std::filesystem::path Path = StringToPath(m_Path); + BuildStorage::Statistics StorageStats; std::unique_ptr<BuildStorage> Storage; std::string StorageName; @@ -7379,20 +7385,21 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("Downloading '{}' to '{}' from cloud endpoint {}. SessionId: '{}'. Namespace '{}', Bucket '{}', BuildId '{}'", BuildId, - m_Path, + Path, m_BuildsUrl, Http.GetSessionId(), m_Namespace, m_Bucket, BuildId); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } else if (!m_StoragePath.empty()) { - ZEN_CONSOLE("Downloading '{}' to '{}' from folder {}. BuildId '{}'", BuildId, m_Path, m_StoragePath, BuildId); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + ZEN_CONSOLE("Downloading '{}' to '{}' from folder {}. BuildId '{}'", BuildId, Path, StoragePath, BuildId); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { @@ -7403,7 +7410,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildId, BuildPartIds, m_BuildPartNames, - m_Path, + Path, m_AllowMultiparts, m_AllowPartialBlockRequests, m_Clean, @@ -7442,7 +7449,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { throw zen::OptionParseException(fmt::format("compare-path is required\n{}", m_DownloadOptions.help())); } - DiffFolders(m_Path, m_DiffPath, m_OnlyChunked); + std::filesystem::path Path = StringToPath(m_Path); + DiffFolders(Path, m_DiffPath, m_OnlyChunked); return AbortFlag ? 11 : 0; } @@ -7467,6 +7475,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) // "07d3964f919d577a321a1fdd", // "07d396a6ce875004e16b9528"}; + std::filesystem::path Path = StringToPath(m_Path); + BuildStorage::Statistics StorageStats; std::unique_ptr<BuildStorage> Storage; std::string StorageName; @@ -7474,19 +7484,20 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("Downloading {} to '{}' from cloud endpoint {}. SessionId: '{}'. Namespace '{}', Bucket '{}'", FormatArray<std::string>(m_BuildIds, " "sv), - m_Path, + Path, m_BuildsUrl, Http.GetSessionId(), m_Namespace, m_Bucket); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } else if (!m_StoragePath.empty()) { - ZEN_CONSOLE("Downloading {}'to '{}' from folder {}", FormatArray<std::string>(m_BuildIds, " "sv), m_Path, m_StoragePath); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + ZEN_CONSOLE("Downloading {}'to '{}' from folder {}", FormatArray<std::string>(m_BuildIds, " "sv), Path, StoragePath); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { @@ -7504,7 +7515,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildId, {}, {}, - m_Path, + Path, m_AllowMultiparts, m_AllowPartialBlockRequests, BuildIdString == m_BuildIds.front(), @@ -7531,8 +7542,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) throw zen::OptionParseException(fmt::format("local-path is required\n{}", m_DownloadOptions.help())); } + std::filesystem::path Path = StringToPath(m_Path); + m_BuildId = Oid::NewOid().ToString(); - m_BuildPartName = m_Path.filename().string(); + m_BuildPartName = Path.filename().string(); m_BuildPartId = Oid::NewOid().ToString(); m_CreateBuild = true; @@ -7542,16 +7555,18 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::unique_ptr<BuildStorage> Storage; std::string StorageName; - if (m_BuildsUrl.empty() && m_StoragePath.empty()) + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + + if (m_BuildsUrl.empty() && StoragePath.empty()) { - m_StoragePath = GetRunningExecutablePath().parent_path() / ".tmpstore"; - CreateDirectories(m_StoragePath); - CleanDirectory(m_StoragePath, {}); + m_StoragePath = (GetRunningExecutablePath().parent_path() / ".tmpstore").generic_string(); + CreateDirectories(StoragePath); + CleanDirectory(StoragePath, {}); } auto _ = MakeGuard([&]() { - if (m_BuildsUrl.empty() && m_StoragePath.empty()) + if (m_BuildsUrl.empty() && StoragePath.empty()) { - DeleteDirectories(m_StoragePath); + DeleteDirectories(StoragePath); } }); @@ -7559,24 +7574,24 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("Using '{}' to '{}' from cloud endpoint {}. SessionId: '{}'. Namespace '{}', Bucket '{}', BuildId '{}'", m_BuildPartName.empty() ? m_BuildPartId : m_BuildPartName, - m_Path, + Path, m_BuildsUrl, Http.GetSessionId(), m_Namespace, m_Bucket, BuildId); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } - else if (!m_StoragePath.empty()) + else if (!StoragePath.empty()) { ZEN_CONSOLE("Using '{}' to '{}' from folder {}. BuildId '{}'", m_BuildPartName.empty() ? m_BuildPartId : m_BuildPartName, - m_Path, - m_StoragePath, + Path, + StoragePath, BuildId); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { @@ -7608,7 +7623,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildId, BuildPartId, m_BuildPartName, - m_Path, + Path, {}, m_BlockReuseMinPercentLimit, m_AllowMultiparts, @@ -7622,7 +7637,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 11; } - const std::filesystem::path DownloadPath = m_Path.parent_path() / (m_BuildPartName + "_download"); + const std::filesystem::path DownloadPath = Path.parent_path() / (m_BuildPartName + "_download"); ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}'", BuildId, BuildPartId, m_BuildPartName, DownloadPath); DownloadFolder(*Storage, BuildId, {BuildPartId}, {}, DownloadPath, m_AllowMultiparts, m_AllowPartialBlockRequests, true, true); if (AbortFlag) @@ -7846,6 +7861,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::unique_ptr<BuildStorage> Storage; std::string StorageName; + std::filesystem::path Path = StringToPath(m_Path); + if (!m_BuildsUrl.empty()) { ZEN_CONSOLE("Using from cloud endpoint {}. SessionId: '{}'. Namespace '{}', Bucket '{}', BuildId '{}'", @@ -7854,14 +7871,15 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_Namespace, m_Bucket, BuildId); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } else if (!m_StoragePath.empty()) { - ZEN_CONSOLE("Using folder {}. BuildId '{}'", m_StoragePath, BuildId); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + ZEN_CONSOLE("Using folder {}. BuildId '{}'", StoragePath, BuildId); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { @@ -7913,6 +7931,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::unique_ptr<BuildStorage> Storage; std::string StorageName; + std::filesystem::path Path = StringToPath(m_Path); + if (!m_BuildsUrl.empty()) { ZEN_CONSOLE("Using from cloud endpoint {}. SessionId: '{}'. Namespace '{}', Bucket '{}', BuildId '{}'", @@ -7921,14 +7941,15 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_Namespace, m_Bucket, BuildId); - Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, m_Path / ZenTempStorageFolderName); + Storage = CreateJupiterBuildStorage(Log(), Http, StorageStats, m_Namespace, m_Bucket, Path / ZenTempStorageFolderName); StorageName = "Cloud DDC"; } else if (!m_StoragePath.empty()) { - ZEN_CONSOLE("Using folder {}. BuildId '{}'", m_StoragePath, BuildId); - Storage = CreateFileBuildStorage(m_StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); - StorageName = fmt::format("Disk {}", m_StoragePath.stem()); + std::filesystem::path StoragePath = StringToPath(m_StoragePath); + ZEN_CONSOLE("Using folder {}. BuildId '{}'", StoragePath, BuildId); + Storage = CreateFileBuildStorage(StoragePath, StorageStats, false, DefaultLatency, DefaultDelayPerKBSec); + StorageName = fmt::format("Disk {}", StoragePath.stem()); } else { diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h index 167a5d29f..60953efad 100644 --- a/src/zen/cmds/builds_cmd.h +++ b/src/zen/cmds/builds_cmd.h @@ -25,7 +25,7 @@ public: private: cxxopts::Options m_Options{Name, Description}; - std::filesystem::path m_SystemRootDir; + std::string m_SystemRootDir; bool m_PlainProgress = false; bool m_Verbose = false; @@ -37,20 +37,20 @@ private: std::string m_Bucket; // file storage - std::filesystem::path m_StoragePath; - bool m_WriteMetadataAsJson = false; - - std::string m_BuildId; - bool m_CreateBuild = false; - std::string m_BuildMetadataPath; - std::string m_BuildMetadata; - std::string m_BuildPartName; // Defaults to name of leaf folder in m_Path - std::string m_BuildPartId; // Defaults to a generated id when creating part, looked up when downloading using m_BuildPartName - bool m_Clean = false; - uint8_t m_BlockReuseMinPercentLimit = 85; - bool m_AllowMultiparts = true; - bool m_AllowPartialBlockRequests = true; - std::filesystem::path m_ManifestPath; + std::string m_StoragePath; + bool m_WriteMetadataAsJson = false; + + std::string m_BuildId; + bool m_CreateBuild = false; + std::string m_BuildMetadataPath; + std::string m_BuildMetadata; + std::string m_BuildPartName; // Defaults to name of leaf folder in m_Path + std::string m_BuildPartId; // Defaults to a generated id when creating part, looked up when downloading using m_BuildPartName + bool m_Clean = false; + uint8_t m_BlockReuseMinPercentLimit = 85; + bool m_AllowMultiparts = true; + bool m_AllowPartialBlockRequests = true; + std::string m_ManifestPath; // Direct access token (may expire) std::string m_AccessToken; @@ -76,7 +76,7 @@ private: cxxopts::Options m_ListOptions{"list", "List available builds"}; - std::filesystem::path m_Path; + std::string m_Path; cxxopts::Options m_UploadOptions{"upload", "Upload a folder"}; bool m_PostUploadVerify = false; @@ -86,9 +86,9 @@ private: std::vector<std::string> m_BuildPartIds; bool m_PostDownloadVerify = false; - cxxopts::Options m_DiffOptions{"diff", "Compare two local folders"}; - std::filesystem::path m_DiffPath; - bool m_OnlyChunked = false; + cxxopts::Options m_DiffOptions{"diff", "Compare two local folders"}; + std::string m_DiffPath; + bool m_OnlyChunked = false; cxxopts::Options m_TestOptions{"test", "Test upload and download with verify"}; diff --git a/src/zen/cmds/status_cmd.cpp b/src/zen/cmds/status_cmd.cpp index 16754e747..4d1534e05 100644 --- a/src/zen/cmds/status_cmd.cpp +++ b/src/zen/cmds/status_cmd.cpp @@ -32,16 +32,17 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) uint16_t EffectivePort = 0; if (!m_DataDir.empty()) { - if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + std::filesystem::path DataDir = StringToPath(m_DataDir); + if (!std::filesystem::is_regular_file(DataDir / ".lock")) { - ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir); return 1; } - LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock"))); std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason); return 1; } EffectivePort = Info.EffectiveListenPort; diff --git a/src/zen/cmds/status_cmd.h b/src/zen/cmds/status_cmd.h index 46bda9ee6..00ad0e758 100644 --- a/src/zen/cmds/status_cmd.h +++ b/src/zen/cmds/status_cmd.h @@ -20,9 +20,9 @@ public: private: int GetLockFileEffectivePort() const; - cxxopts::Options m_Options{"status", "Show zen status"}; - uint16_t m_Port = 0; - std::filesystem::path m_DataDir; + cxxopts::Options m_Options{"status", "Show zen status"}; + uint16_t m_Port = 0; + std::string m_DataDir; }; } // namespace zen diff --git a/src/zen/cmds/up_cmd.cpp b/src/zen/cmds/up_cmd.cpp index ac2f42a86..44a41146c 100644 --- a/src/zen/cmds/up_cmd.cpp +++ b/src/zen/cmds/up_cmd.cpp @@ -77,13 +77,15 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } - if (m_ProgramBaseDir.empty()) + std::filesystem::path ProgramBaseDir = StringToPath(m_ProgramBaseDir); + + if (ProgramBaseDir.empty()) { std::filesystem::path ExePath = zen::GetRunningExecutablePath(); - m_ProgramBaseDir = ExePath.parent_path(); + ProgramBaseDir = ExePath.parent_path(); } ZenServerEnvironment ServerEnvironment; - ServerEnvironment.Initialize(m_ProgramBaseDir); + ServerEnvironment.Initialize(ProgramBaseDir); ZenServerInstance Server(ServerEnvironment); std::string ServerArguments = GlobalOptions.PassthroughCommandLine; if ((m_Port != 0) && (ServerArguments.find("--port"sv) == std::string::npos)) @@ -153,18 +155,20 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Instance.Sweep(); ZenServerState::ZenServerEntry* Entry = Instance.Lookup(m_Port); - if (!m_DataDir.empty()) + std::filesystem::path DataDir = StringToPath(m_DataDir); + + if (!DataDir.empty()) { - if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + if (!std::filesystem::is_regular_file(DataDir / ".lock")) { - ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir); return 1; } - LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock"))); std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason); return 1; } Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); @@ -214,24 +218,27 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Instance.Initialize(); ZenServerState::ZenServerEntry* Entry = Instance.Lookup(m_Port); - if (m_ProgramBaseDir.empty()) + std::filesystem::path ProgramBaseDir = StringToPath(m_ProgramBaseDir); + if (ProgramBaseDir.empty()) { - std::filesystem::path ExePath = zen::GetRunningExecutablePath(); - m_ProgramBaseDir = ExePath.parent_path(); + std::filesystem::path ExePath = GetRunningExecutablePath(); + ProgramBaseDir = ExePath.parent_path(); } - if (!m_DataDir.empty()) + std::filesystem::path DataDir = StringToPath(m_DataDir); + + if (!DataDir.empty()) { - if (!std::filesystem::is_regular_file(m_DataDir / ".lock")) + if (!std::filesystem::is_regular_file(DataDir / ".lock")) { - ZEN_CONSOLE("lock file does not exist in directory '{}'", m_DataDir); + ZEN_CONSOLE("lock file does not exist in directory '{}'", DataDir); return 1; } - LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(m_DataDir / ".lock"))); + LockFileInfo Info = ReadLockFilePayload(LoadCompactBinaryObject(IoBufferBuilder::MakeFromFile(DataDir / ".lock"))); std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); + ZEN_CONSOLE("lock file in directory '{}' is not valid. Reason: '{}'", DataDir, Reason); return 1; } Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); @@ -244,7 +251,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) try { ZenServerEnvironment ServerEnvironment; - ServerEnvironment.Initialize(m_ProgramBaseDir); + ServerEnvironment.Initialize(ProgramBaseDir); ZenServerInstance Server(ServerEnvironment); Server.AttachToRunningServer(EntryPort); @@ -309,7 +316,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (m_ForceTerminate) { // Try to find the running executable by path name - std::filesystem::path ServerExePath = m_ProgramBaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL; + std::filesystem::path ServerExePath = ProgramBaseDir / "zenserver" ZEN_EXE_SUFFIX_LITERAL; ProcessHandle RunningProcess; if (std::error_code Ec = FindProcess(ServerExePath, RunningProcess); !Ec) { diff --git a/src/zen/cmds/up_cmd.h b/src/zen/cmds/up_cmd.h index c9af16749..32d8ddab3 100644 --- a/src/zen/cmds/up_cmd.h +++ b/src/zen/cmds/up_cmd.h @@ -18,11 +18,11 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"up", "Bring up zen service"}; - uint16_t m_Port = 0; - bool m_ShowConsole = false; - bool m_ShowLog = false; - std::filesystem::path m_ProgramBaseDir; + cxxopts::Options m_Options{"up", "Bring up zen service"}; + uint16_t m_Port = 0; + bool m_ShowConsole = false; + bool m_ShowLog = false; + std::string m_ProgramBaseDir; }; class AttachCommand : public ZenCmdBase @@ -35,10 +35,10 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"}; - uint16_t m_Port = 0; - int m_OwnerPid = 0; - std::filesystem::path m_DataDir; + cxxopts::Options m_Options{"attach", "Add a sponsor process to a running zen service"}; + uint16_t m_Port = 0; + int m_OwnerPid = 0; + std::string m_DataDir; }; class DownCommand : public ZenCmdBase @@ -51,11 +51,11 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"down", "Bring down zen service"}; - uint16_t m_Port = 0; - bool m_ForceTerminate = false; - std::filesystem::path m_ProgramBaseDir; - std::filesystem::path m_DataDir; + cxxopts::Options m_Options{"down", "Bring down zen service"}; + uint16_t m_Port = 0; + bool m_ForceTerminate = false; + std::string m_ProgramBaseDir; + std::string m_DataDir; }; } // namespace zen diff --git a/src/zen/cmds/workspaces_cmd.cpp b/src/zen/cmds/workspaces_cmd.cpp index 166d4218d..5f3f8f7ca 100644 --- a/src/zen/cmds/workspaces_cmd.cpp +++ b/src/zen/cmds/workspaces_cmd.cpp @@ -25,16 +25,7 @@ namespace { if (!Path.empty()) { std::u8string PathString = Path.u8string(); - if (PathString.ends_with(std::filesystem::path::preferred_separator) || PathString.ends_with('/')) - { - PathString.pop_back(); - Path = std::filesystem::path(PathString); - } - // Special case if user gives a path with quotes and includes a backslash at the end: - // ="path\" cxxopts strips the leading quote only but not the trailing. - // As we expect paths here and we don't want trailing slashes we strip away the quote - // manually if the string does not start with a quote UE-231677 - else if (PathString[0] != '\"' && PathString[PathString.length() - 1] == '\"') + if (PathString.ends_with(std::filesystem::path::preferred_separator) || PathString.starts_with('/')) { PathString.pop_back(); Path = std::filesystem::path(PathString); @@ -96,7 +87,7 @@ WorkspaceCommand::WorkspaceCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>"); - m_Options.add_options()("system-dir", "Specify system root", cxxopts::value<std::filesystem::path>(m_SystemRootDir)); + m_Options.add_options()("system-dir", "Specify system root", cxxopts::value(m_SystemRootDir)); m_Options.add_option("", "v", "verb", "Verb for workspace - create, remove, info", cxxopts::value(m_Verb), "<verb>"); m_Options.parse_positional({"verb"}); m_Options.positional_help("verb"); @@ -148,16 +139,18 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_HostName = ResolveTargetHostSpec(m_HostName); - if (m_SystemRootDir.empty()) + std::filesystem::path SystemRootDir = StringToPath(m_SystemRootDir); + + if (SystemRootDir.empty()) { - m_SystemRootDir = PickDefaultSystemRootDirectory(); - if (m_SystemRootDir.empty()) + SystemRootDir = PickDefaultSystemRootDirectory(); + if (SystemRootDir.empty()) { throw zen::OptionParseException("unable to resolve system root directory"); } } - std::filesystem::path StatePath = m_SystemRootDir / "workspaces"; + std::filesystem::path StatePath = SystemRootDir / "workspaces"; if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { @@ -171,12 +164,12 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) throw zen::OptionParseException(fmt::format("path is required\n{}", m_CreateOptions.help())); } - RemoveTrailingPathSeparator(m_Path); + std::filesystem::path Path = StringToPath(m_Path); if (m_Id.empty()) { - m_Id = Workspaces::PathToId(m_Path).ToString(); - ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_Id, m_Path); + m_Id = Workspaces::PathToId(Path).ToString(); + ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_Id, Path); } if (Oid::TryFromHexString(m_Id) == Oid::Zero) @@ -187,7 +180,7 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Workspaces::AddWorkspace( Log(), StatePath, - {.Id = Oid::FromHexString(m_Id), .RootPath = m_Path, .AllowShareCreationFromHttp = m_AllowShareCreationFromHttp})) + {.Id = Oid::FromHexString(m_Id), .RootPath = Path, .AllowShareCreationFromHttp = m_AllowShareCreationFromHttp})) { if (!m_HostName.empty()) { @@ -287,7 +280,7 @@ WorkspaceShareCommand::WorkspaceShareCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>"); - m_Options.add_options()("system-dir", "Specify system root", cxxopts::value<std::filesystem::path>(m_SystemRootDir)); + m_Options.add_options()("system-dir", "Specify system root", cxxopts::value(m_SystemRootDir)); m_Options.add_option("", "v", "verb", "Verb for workspace - create, remove, info", cxxopts::value(m_Verb), "<verb>"); m_Options.parse_positional({"verb"}); m_Options.positional_help("verb"); @@ -399,16 +392,18 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** m_HostName = ResolveTargetHostSpec(m_HostName); - if (m_SystemRootDir.empty()) + std::filesystem::path SystemRootDir = StringToPath(m_SystemRootDir); + + if (SystemRootDir.empty()) { - m_SystemRootDir = PickDefaultSystemRootDirectory(); - if (m_SystemRootDir.empty()) + SystemRootDir = PickDefaultSystemRootDirectory(); + if (SystemRootDir.empty()) { throw zen::OptionParseException("unable to resolve system root directory"); } } - std::filesystem::path StatePath = m_SystemRootDir / "workspaces"; + std::filesystem::path StatePath = SystemRootDir / "workspaces"; if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { @@ -417,7 +412,8 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (SubOption == &m_CreateOptions) { - if (m_WorkspaceRoot.empty()) + std::filesystem::path WorkspaceRoot = StringToPath(m_WorkspaceRoot); + if (WorkspaceRoot.empty()) { if (m_WorkspaceId.empty()) { @@ -436,15 +432,15 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId); return 0; } - m_WorkspaceRoot = WorkspaceConfig.RootPath; + WorkspaceRoot = WorkspaceConfig.RootPath; } else { - RemoveTrailingPathSeparator(m_WorkspaceRoot); + RemoveTrailingPathSeparator(WorkspaceRoot); if (m_WorkspaceId.empty()) { - m_WorkspaceId = Workspaces::PathToId(m_WorkspaceRoot).ToString(); - ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_WorkspaceId, m_WorkspaceRoot); + m_WorkspaceId = Workspaces::PathToId(WorkspaceRoot).ToString(); + ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_WorkspaceId, WorkspaceRoot); } else { @@ -453,23 +449,25 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_WorkspaceId)); } } - if (Workspaces::AddWorkspace(Log(), StatePath, {.Id = Oid::FromHexString(m_WorkspaceId), .RootPath = m_WorkspaceRoot})) + if (Workspaces::AddWorkspace(Log(), StatePath, {.Id = Oid::FromHexString(m_WorkspaceId), .RootPath = WorkspaceRoot})) { - ZEN_CONSOLE("Created workspace {} using root path '{}'", m_WorkspaceId, m_WorkspaceRoot); + ZEN_CONSOLE("Created workspace {} using root path '{}'", m_WorkspaceId, WorkspaceRoot); } else { - ZEN_CONSOLE("Using existing workspace {} with root path '{}'", m_WorkspaceId, m_WorkspaceRoot); + ZEN_CONSOLE("Using existing workspace {} with root path '{}'", m_WorkspaceId, WorkspaceRoot); } } - RemoveTrailingPathSeparator(m_SharePath); - RemoveLeadingPathSeparator(m_SharePath); + std::filesystem::path SharePath = StringToPath(m_SharePath); + + RemoveLeadingPathSeparator(SharePath); + RemoveTrailingPathSeparator(SharePath); if (m_ShareId.empty()) { - m_ShareId = Workspaces::PathToId(m_SharePath).ToString(); - ZEN_CONSOLE("Using generated share id {}, for path '{}'", m_ShareId, m_SharePath); + m_ShareId = Workspaces::PathToId(SharePath).ToString(); + ZEN_CONSOLE("Using generated share id {}, for path '{}'", m_ShareId, SharePath); } if (Oid::TryFromHexString(m_ShareId) == Oid::Zero) @@ -478,8 +476,8 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** } if (Workspaces::AddWorkspaceShare(Log(), - m_WorkspaceRoot, - {.Id = Oid::FromHexString(m_ShareId), .SharePath = m_SharePath, .Alias = m_Alias})) + WorkspaceRoot, + {.Id = Oid::FromHexString(m_ShareId), .SharePath = SharePath, .Alias = m_Alias})) { if (!m_HostName.empty()) { @@ -531,7 +529,8 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId); return 0; } - m_WorkspaceRoot = WorkspaceConfig.RootPath; + + std::filesystem::path WorkspaceRoot = WorkspaceConfig.RootPath; if (m_ShareId.empty()) { @@ -543,8 +542,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_ShareId)); } - Workspaces::WorkspaceShareConfiguration Share = - Workspaces::FindWorkspaceShare(Log(), m_WorkspaceRoot, Oid::FromHexString(m_ShareId)); + Workspaces::WorkspaceShareConfiguration Share = Workspaces::FindWorkspaceShare(Log(), WorkspaceRoot, Oid::FromHexString(m_ShareId)); if (Share.Id == Oid::Zero) { ZEN_CONSOLE("Workspace share {} does not exist", m_ShareId); @@ -556,6 +554,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (SubOption == &m_RemoveOptions) { + std::filesystem::path WorkspaceRoot; if (!m_Alias.empty()) { Workspaces::WorkspaceConfiguration WorkspaceConfig; @@ -566,9 +565,9 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ZEN_CONSOLE("Workspace share with alias {} does not exist", m_Alias); return 0; } - m_ShareId = ShareConfig.Id.ToString(); - m_WorkspaceId = WorkspaceConfig.Id.ToString(); - m_WorkspaceRoot = WorkspaceConfig.RootPath; + m_ShareId = ShareConfig.Id.ToString(); + m_WorkspaceId = WorkspaceConfig.Id.ToString(); + WorkspaceRoot = WorkspaceConfig.RootPath; } if (m_WorkspaceId.empty()) @@ -587,7 +586,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId); return 0; } - m_WorkspaceRoot = WorkspaceConfig.RootPath; + WorkspaceRoot = WorkspaceConfig.RootPath; if (m_ShareId.empty()) { @@ -599,7 +598,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_ShareId)); } - if (Workspaces::RemoveWorkspaceShare(Log(), m_WorkspaceRoot, Oid::FromHexString(m_ShareId))) + if (Workspaces::RemoveWorkspaceShare(Log(), WorkspaceRoot, Oid::FromHexString(m_ShareId))) { if (!m_HostName.empty()) { diff --git a/src/zen/cmds/workspaces_cmd.h b/src/zen/cmds/workspaces_cmd.h index de0edd061..86452e25e 100644 --- a/src/zen/cmds/workspaces_cmd.h +++ b/src/zen/cmds/workspaces_cmd.h @@ -21,17 +21,17 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{Name, Description}; - std::string m_HostName; - std::filesystem::path m_SystemRootDir; + cxxopts::Options m_Options{Name, Description}; + std::string m_HostName; + std::string m_SystemRootDir; std::string m_Verb; // create, info, remove std::string m_Id; - cxxopts::Options m_CreateOptions{"create", "Create a workspace"}; - std::filesystem::path m_Path; - bool m_AllowShareCreationFromHttp = false; + cxxopts::Options m_CreateOptions{"create", "Create a workspace"}; + std::string m_Path; + bool m_AllowShareCreationFromHttp = false; cxxopts::Options m_InfoOptions{"info", "Info about a workspace"}; @@ -53,17 +53,17 @@ public: virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{Name, Description}; - std::string m_HostName; - std::filesystem::path m_SystemRootDir; - std::string m_WorkspaceId; - std::filesystem::path m_WorkspaceRoot; - std::string m_Verb; // create, info, remove - std::string m_ShareId; - std::string m_Alias; - - cxxopts::Options m_CreateOptions{"create", "Create a workspace share"}; - std::filesystem::path m_SharePath; + cxxopts::Options m_Options{Name, Description}; + std::string m_HostName; + std::string m_SystemRootDir; + std::string m_WorkspaceId; + std::string m_WorkspaceRoot; + std::string m_Verb; // create, info, remove + std::string m_ShareId; + std::string m_Alias; + + cxxopts::Options m_CreateOptions{"create", "Create a workspace share"}; + std::string m_SharePath; bool m_Refresh = false; diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 9d0eab7dc..6f831349b 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -29,6 +29,7 @@ #include <zencore/filesystem.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> +#include <zencore/process.h> #include <zencore/scopeguard.h> #include <zencore/string.h> #include <zencore/trace.h> @@ -414,6 +415,23 @@ ProgressBar::HasActiveTask() const int main(int argc, char** argv) { + std::vector<std::string> Args; +#if ZEN_PLATFORM_WINDOWS + LPWSTR RawCommandLine = GetCommandLine(); + std::string CommandLine = zen::WideToUtf8(RawCommandLine); + Args = zen::ParseCommandLine(CommandLine); +#else + Args.reserve(argc); + for (int I = 0; I < argc; I++) + { + Args.push_back(std::string(argv[I])); + } +#endif + std::vector<char*> RawArgs = zen::StripCommandlineQuotes(Args); + + argc = gsl::narrow<int>(RawArgs.size()); + argv = RawArgs.data(); + using namespace zen; using namespace std::literals; diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index 9f3f4f7fc..05e2bf049 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -2043,6 +2043,19 @@ SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly) return false; } +std::filesystem::path +StringToPath(const std::string_view& Path) +{ + if (Path.length() > 2 && Path.front() == '\"' && Path.back() == '\"') + { + return std::filesystem::path(Path.substr(1, Path.length() - 2)).make_preferred(); + } + else + { + return std::filesystem::path(Path).make_preferred(); + } +} + ////////////////////////////////////////////////////////////////////////// // // Testing related code follows... diff --git a/src/zencore/include/zencore/filesystem.h b/src/zencore/include/zencore/filesystem.h index e020668fc..9a2b15d1d 100644 --- a/src/zencore/include/zencore/filesystem.h +++ b/src/zencore/include/zencore/filesystem.h @@ -292,6 +292,8 @@ uint32_t MakeFileModeReadOnly(uint32_t FileMode, bool ReadOnly); bool SetFileReadOnly(const std::filesystem::path& Filename, bool ReadOnly); +std::filesystem::path StringToPath(const std::string_view& Path); + ////////////////////////////////////////////////////////////////////////// void filesystem_forcelink(); // internal diff --git a/src/zencore/include/zencore/process.h b/src/zencore/include/zencore/process.h index d1394cd9a..0c5931ba0 100644 --- a/src/zencore/include/zencore/process.h +++ b/src/zencore/include/zencore/process.h @@ -100,6 +100,9 @@ int GetProcessId(CreateProcResult ProcId); std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc); std::error_code FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle); +std::vector<std::string> ParseCommandLine(std::string_view CommandLine); +std::vector<char*> StripCommandlineQuotes(std::vector<std::string>& InOutArgs); + void process_forcelink(); // internal } // namespace zen diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp index c51e8f69d..0761521dc 100644 --- a/src/zencore/process.cpp +++ b/src/zencore/process.cpp @@ -1047,6 +1047,118 @@ FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHand #endif // ZEN_PLATFORM_LINUX } +std::vector<std::string> +ParseCommandLine(std::string_view CommandLine) +{ + auto IsWhitespaceOrEnd = [](std::string_view CommandLine, std::string::size_type Pos) { + if (Pos == CommandLine.length()) + { + return true; + } + if (CommandLine[Pos] == ' ') + { + return true; + } + return false; + }; + + bool IsParsingArg = false; + bool IsInQuote = false; + + std::string::size_type Pos = 0; + std::string::size_type ArgStart = 0; + std::vector<std::string> Args; + while (Pos < CommandLine.length()) + { + if (IsInQuote) + { + if (CommandLine[Pos] == '"' && IsWhitespaceOrEnd(CommandLine, Pos + 1)) + { + Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart + 1))); + Pos++; + IsInQuote = false; + IsParsingArg = false; + } + else + { + Pos++; + } + } + else if (IsParsingArg) + { + ZEN_ASSERT(Pos > ArgStart); + if (CommandLine[Pos] == ' ') + { + Args.push_back(std::string(CommandLine.substr(ArgStart, Pos - ArgStart))); + Pos++; + IsParsingArg = false; + } + else if (CommandLine[Pos] == '"') + { + IsInQuote = true; + Pos++; + } + else + { + Pos++; + } + } + else if (CommandLine[Pos] == '"') + { + IsInQuote = true; + IsParsingArg = true; + ArgStart = Pos; + Pos++; + } + else if (CommandLine[Pos] != ' ') + { + IsParsingArg = true; + ArgStart = Pos; + Pos++; + } + else + { + Pos++; + } + } + if (IsParsingArg) + { + ZEN_ASSERT(Pos > ArgStart); + Args.push_back(std::string(CommandLine.substr(ArgStart))); + } + + return Args; +} + +std::vector<char*> +StripCommandlineQuotes(std::vector<std::string>& InOutArgs) +{ + std::vector<char*> RawArgs; + RawArgs.reserve(InOutArgs.size()); + for (std::string& Arg : InOutArgs) + { + std::string::size_type EscapedQuotePos = Arg.find("\\\"", 1); + while (EscapedQuotePos != std::string::npos && Arg.rfind('\"', EscapedQuotePos - 1) != std::string::npos) + { + Arg.erase(EscapedQuotePos, 1); + EscapedQuotePos = Arg.find("\\\"", EscapedQuotePos); + } + + if (Arg.starts_with("\"")) + { + if (Arg.find('"', 1) == Arg.length() - 1) + { + if (Arg.find(' ', 1) == std::string::npos) + { + Arg = Arg.substr(1, Arg.length() - 2); + } + } + } + RawArgs.push_back(const_cast<char*>(Arg.c_str())); + } + return RawArgs; +} + #if ZEN_WITH_TESTS void @@ -1123,6 +1235,36 @@ TEST_CASE("BuildArgV") } } +TEST_CASE("CommandLine") +{ + std::vector<std::string> v1 = ParseCommandLine("c:\\my\\exe.exe \"quoted arg\" \"one\",two,\"three\\\""); + CHECK_EQ(v1[0], "c:\\my\\exe.exe"); + CHECK_EQ(v1[1], "\"quoted arg\""); + CHECK_EQ(v1[2], "\"one\",two,\"three\\\""); + + std::vector<std::string> v2 = ParseCommandLine( + "--tracehost 127.0.0.1 builds download --url=https://jupiter.devtools.epicgames.com --namespace=ue.oplog " + "--bucket=citysample.packaged-build.fortnite-main.windows \"c:\\just\\a\\path\" " + "--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\" \"D:\\Dev\\Spaced Folder\\Target\\\" " + "--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\\\" 07dn23ifiwesnvoasjncasab --build-part-name win64,linux,ps5"); + + std::vector<char*> v2Stripped = StripCommandlineQuotes(v2); + CHECK_EQ(v2Stripped[0], std::string("--tracehost")); + CHECK_EQ(v2Stripped[1], std::string("127.0.0.1")); + CHECK_EQ(v2Stripped[2], std::string("builds")); + CHECK_EQ(v2Stripped[3], std::string("download")); + CHECK_EQ(v2Stripped[4], std::string("--url=https://jupiter.devtools.epicgames.com")); + CHECK_EQ(v2Stripped[5], std::string("--namespace=ue.oplog")); + CHECK_EQ(v2Stripped[6], std::string("--bucket=citysample.packaged-build.fortnite-main.windows")); + CHECK_EQ(v2Stripped[7], std::string("c:\\just\\a\\path")); + CHECK_EQ(v2Stripped[8], std::string("--access-token-path=\"C:\\Users\\dan.engelbrecht\\jupiter-token.json\"")); + CHECK_EQ(v2Stripped[9], std::string("\"D:\\Dev\\Spaced Folder\\Target\"")); + CHECK_EQ(v2Stripped[10], std::string("--alt-path=\"D:\\Dev\\Spaced Folder2\\Target\"")); + CHECK_EQ(v2Stripped[11], std::string("07dn23ifiwesnvoasjncasab")); + CHECK_EQ(v2Stripped[12], std::string("--build-part-name")); + CHECK_EQ(v2Stripped[13], std::string("win64,linux,ps5")); +} + TEST_SUITE_END(/* core.process */); #endif |