14namespace fs = std::filesystem;
27#define mkdir(path, mode) _mkdir(path)
29#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
42 mWorkspacePath = GetPortableWorkspacePath();
44 mWorkspacePath = GetDocumentsPath() +
"/pebl-exp." +
PEBL_VERSION;
58 if (FileExists(
"./STANDALONE.txt") || FileExists(
"../STANDALONE.txt") || FileExists(
"../../STANDALONE.txt")) {
62 if (FileExists(
"./PORTABLE.txt") || FileExists(
"../PORTABLE.txt") || FileExists(
"../../PORTABLE.txt")) {
67 const char* portableEnv = getenv(
"PEBL_PORTABLE");
68 if (portableEnv && strcmp(portableEnv,
"1") == 0) {
75std::string WorkspaceManager::GetPortableWorkspacePath()
const {
103 if (FileExists(
"../../STANDALONE.txt") || FileExists(
"../../PORTABLE.txt")) {
108 if (FileExists(
"../STANDALONE.txt") || FileExists(
"../PORTABLE.txt")) {
114 if (FileExists(
"./STANDALONE.txt") || FileExists(
"./PORTABLE.txt")) {
129 if (!DirectoryExists(mWorkspacePath)) {
134 std::string studiesPath = mWorkspacePath +
"/my_studies";
135 return !DirectoryExists(studiesPath);
146 if (!DirectoryExists(mWorkspacePath)) {
147 if (!CreateDir(mWorkspacePath)) {
153 std::vector<std::string> subdirs = {
163 for (
const auto& subdir : subdirs) {
164 std::string fullPath = mWorkspacePath + subdir;
165 if (!DirectoryExists(fullPath)) {
166 if (!CreateDir(fullPath)) {
178 std::cout <<
"CopyResources: skipped (empty path or portable mode)" << std::endl;
182 std::cout <<
"CopyResources: installation path = " << installationPath << std::endl;
183 std::cout <<
"CopyResources: workspace path = " << mWorkspacePath << std::endl;
186 std::string docDest = mWorkspacePath +
"/doc";
189 std::vector<std::string> docFiles = {
190 std::string(
"doc/pman/PEBLManual") +
PEBL_VERSION +
".pdf",
191 "doc/ReleaseNotes.txt",
195 std::cout <<
"Copying documentation files..." << std::endl;
196 for (
const auto& relPath : docFiles) {
197 std::string srcFile = installationPath +
"/" + relPath;
199 std::string filename = relPath;
200 size_t lastSlash = filename.find_last_of(
'/');
201 if (lastSlash != std::string::npos) {
202 filename = filename.substr(lastSlash + 1);
204 std::string destFile = docDest +
"/" + filename;
206 if (CopyFileContents(srcFile, destFile)) {
207 std::cout <<
" ✓ Copied " << filename << std::endl;
209 std::cout <<
" ✗ Failed to copy " << relPath << std::endl;
214 std::string demoSource = installationPath +
"/demo";
215 std::string demoDest = mWorkspacePath +
"/demo";
216 std::cout <<
"Checking for demo: " << demoSource << std::endl;
217 if (DirectoryExists(demoSource)) {
218 std::cout <<
"Copying demos from " << demoSource <<
" to " << demoDest <<
" (excluding tests/)" << std::endl;
219 std::vector<std::string> excludeDirs = {
"tests"};
220 if (CopyDirectory(demoSource, demoDest,
false, excludeDirs)) {
221 std::cout <<
" ✓ Demos copied successfully" << std::endl;
223 std::cout <<
" ✗ Failed to copy demos" << std::endl;
226 std::cout <<
" Demo source not found" << std::endl;
230 std::string tutorialSource = installationPath +
"/tutorials";
231 std::string tutorialDest = mWorkspacePath +
"/tutorials";
232 std::cout <<
"Checking for tutorials: " << tutorialSource << std::endl;
233 if (DirectoryExists(tutorialSource)) {
234 std::cout <<
"Copying tutorials from " << tutorialSource <<
" to " << tutorialDest << std::endl;
235 if (CopyDirectory(tutorialSource, tutorialDest,
false)) {
236 std::cout <<
" ✓ Tutorials copied successfully" << std::endl;
238 std::cout <<
" ✗ Failed to copy tutorials" << std::endl;
241 std::cout <<
" Tutorials source not found" << std::endl;
248 return mInitialized || DirectoryExists(mWorkspacePath);
252 std::vector<std::string> studies;
256 for (
const auto& entry : fs::directory_iterator(studiesPath)) {
257 if (!entry.is_directory()) {
261 std::string dirName = entry.path().filename().string();
262 std::string fullPath = entry.path().string();
265 std::string studyInfoPath = fullPath +
"/study-info.json";
266 struct stat fileInfo;
267 if (stat(studyInfoPath.c_str(), &fileInfo) == 0) {
268 studies.push_back(dirName);
271 }
catch (
const fs::filesystem_error&) {
279 std::vector<std::string> snapshots;
283 for (
const auto& entry : fs::directory_iterator(snapshotsPath)) {
284 if (!entry.is_directory()) {
288 std::string dirName = entry.path().filename().string();
289 std::string fullPath = entry.path().string();
292 std::string studyInfoPath = fullPath +
"/study-info.json";
293 struct stat fileInfo;
294 if (stat(studyInfoPath.c_str(), &fileInfo) == 0) {
295 snapshots.push_back(dirName);
298 }
catch (
const fs::filesystem_error&) {
308 if (DirectoryExists(studyPath)) {
313 if (!CreateDir(studyPath)) {
318 CreateDir(studyPath +
"/chains");
319 CreateDir(studyPath +
"/tests");
320 CreateDir(studyPath +
"/data");
328 if (DirectoryExists(snapshotPath)) {
333 return CopyDirectory(studyPath, snapshotPath,
true);
339 if (DirectoryExists(studyPath)) {
344 return CopyDirectory(snapshotPath, studyPath,
false);
347bool WorkspaceManager::CreateDir(
const std::string& path) {
349 if (stat(path.c_str(), &info) == 0) {
350 return (info.st_mode & S_IFDIR) != 0;
354 if (mkdir(path.c_str(), 0755) == 0) {
359 size_t pos = path.rfind(
'/');
360 if (pos != std::string::npos) {
361 std::string parentPath = path.substr(0, pos);
362 if (!parentPath.empty() && CreateDir(parentPath)) {
363 return mkdir(path.c_str(), 0755) == 0;
370bool WorkspaceManager::DirectoryExists(
const std::string& path)
const {
372 return (stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR));
375bool WorkspaceManager::FileExists(
const std::string& path)
const {
377 return (stat(path.c_str(), &info) == 0 && !(info.st_mode & S_IFDIR));
380bool WorkspaceManager::CopyDirectory(
const std::string& source,
const std::string& dest,
bool excludeData,
const std::vector<std::string>& excludeDirs) {
382 if (!CreateDir(dest)) {
389 for (
const auto& entry : fs::directory_iterator(source)) {
390 std::string name = entry.path().filename().string();
393 if (excludeData && name ==
"data" && entry.is_directory()) {
398 bool skipDir =
false;
399 for (
const auto& excludeDir : excludeDirs) {
400 if (name == excludeDir) {
409 std::string sourcePath = entry.path().string();
410 std::string destPath = dest +
"/" + name;
412 if (entry.is_directory()) {
414 if (!CopyDirectory(sourcePath, destPath, excludeData, excludeDirs)) {
420 if (!CopyFileContents(sourcePath, destPath)) {
426 }
catch (
const fs::filesystem_error&) {
433bool WorkspaceManager::CopyFileContents(
const std::string& source,
const std::string& dest) {
434 std::ifstream src(source, std::ios::binary);
435 if (!src.is_open()) {
439 std::ofstream dst(dest, std::ios::binary);
440 if (!dst.is_open()) {
452std::string WorkspaceManager::GetDocumentsPath()
const {
456 if (SUCCEEDED(SHGetFolderPathA(
NULL, CSIDL_PERSONAL,
NULL, 0, path))) {
457 return std::string(path);
460 const char* userProfile = getenv(
"USERPROFILE");
462 return std::string(userProfile) +
"\\Documents";
464 return "C:\\Users\\Public\\Documents";
467 const char* home = getenv(
"HOME");
470 std::string xdgConfigPath = std::string(home) +
"/.config/user-dirs.dirs";
471 std::ifstream xdgConfig(xdgConfigPath);
472 if (xdgConfig.is_open()) {
474 while (std::getline(xdgConfig, line)) {
475 if (line.find(
"XDG_DOCUMENTS_DIR=") == 0) {
476 size_t start = line.find(
'"');
477 size_t end = line.rfind(
'"');
478 if (start != std::string::npos && end != std::string::npos && end > start) {
479 std::string docPath = line.substr(start + 1, end - start - 1);
481 size_t homePos = docPath.find(
"$HOME");
482 if (homePos != std::string::npos) {
483 docPath.replace(homePos, 5, home);
492 return std::string(home) +
"/Documents";
497 return "/tmp/pebl-workspace";
bool CreateSnapshot(const std::string &studyPath, const std::string &snapshotName)
bool CopyResources(const std::string &installationPath)
bool ImportSnapshot(const std::string &snapshotPath, const std::string &newStudyName)
std::string GetStudiesPath() const
bool IsInitialized() const
bool IsPortableMode() const
std::vector< std::string > GetSnapshotDirectories() const
std::vector< std::string > GetStudyDirectories() const
std::string GetSnapshotsPath() const
bool CreateStudyDirectory(const std::string &studyName)