aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-03-12 18:58:24 +0100
committerGitHub Enterprise <[email protected]>2025-03-12 18:58:24 +0100
commit908f99b749fbbf9754f9485d680914792034334c (patch)
tree2d4e70fa49d3c95c509f230cb2dc5269358a3b59 /src
parentRemove spurious '4' in conditional code block (diff)
downloadzen-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.cpp19
-rw-r--r--src/zen/cmds/admin_cmd.h8
-rw-r--r--src/zen/cmds/builds_cmd.cpp119
-rw-r--r--src/zen/cmds/builds_cmd.h38
-rw-r--r--src/zen/cmds/status_cmd.cpp9
-rw-r--r--src/zen/cmds/status_cmd.h6
-rw-r--r--src/zen/cmds/up_cmd.cpp43
-rw-r--r--src/zen/cmds/up_cmd.h28
-rw-r--r--src/zen/cmds/workspaces_cmd.cpp91
-rw-r--r--src/zen/cmds/workspaces_cmd.h34
-rw-r--r--src/zen/zen.cpp18
-rw-r--r--src/zencore/filesystem.cpp13
-rw-r--r--src/zencore/include/zencore/filesystem.h2
-rw-r--r--src/zencore/include/zencore/process.h3
-rw-r--r--src/zencore/process.cpp142
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