OSDN Git Service

Merge branch 'develop' into macos-develop
[hengbandforosx/hengbandosx.git] / src / main / angband-initializer.cpp
index 31313c4..e365a5a 100644 (file)
@@ -33,7 +33,9 @@
 #include "term/term-color-types.h"
 #include "time.h"
 #include "util/angband-files.h"
+#include "util/string-processor.h"
 #include "world/world.h"
+#include <vector>
 #include <chrono>
 
 /*!
@@ -71,25 +73,29 @@ static void remove_old_debug_savefiles()
 /*!
  * @brief 各データファイルを読み取るためのパスを取得する.
  * @param libpath 各PCのインストール環境における"lib/" を表す絶対パス
+ * @param varpath Is the base path for directories that have files which
+ * are not read-only: ANGBAND_DIR_APEX, ANGBAND_DIR_BONE, ANGBAND_DIR_DATA,
+ * and ANGBAND_DIR_SAVE.  If the PRIVATE_USER_PATH preprocessor macro has
+ * not been set, it is also used as the base path for ANGBAND_DIR_USER.
  */
-void init_file_paths(const std::filesystem::path &libpath)
+void init_file_paths(const std::filesystem::path &libpath, const std::filesystem::path &varpath)
 {
     ANGBAND_DIR = std::filesystem::path(libpath);
-    ANGBAND_DIR_APEX = std::filesystem::path(libpath).append("apex");
-    ANGBAND_DIR_BONE = std::filesystem::path(libpath).append("bone");
-    ANGBAND_DIR_DATA = std::filesystem::path(libpath).append("data");
+    ANGBAND_DIR_APEX = std::filesystem::path(varpath).append("apex");
+    ANGBAND_DIR_BONE = std::filesystem::path(varpath).append("bone");
+    ANGBAND_DIR_DATA = std::filesystem::path(varpath).append("data");
     ANGBAND_DIR_EDIT = std::filesystem::path(libpath).append("edit");
     ANGBAND_DIR_SCRIPT = std::filesystem::path(libpath).append("script");
     ANGBAND_DIR_FILE = std::filesystem::path(libpath).append("file");
     ANGBAND_DIR_HELP = std::filesystem::path(libpath).append("help");
     ANGBAND_DIR_INFO = std::filesystem::path(libpath).append("info");
     ANGBAND_DIR_PREF = std::filesystem::path(libpath).append("pref");
-    ANGBAND_DIR_SAVE = std::filesystem::path(libpath).append("save");
+    ANGBAND_DIR_SAVE = std::filesystem::path(varpath).append("save");
     ANGBAND_DIR_DEBUG_SAVE = std::filesystem::path(ANGBAND_DIR_SAVE).append("log");
 #ifdef PRIVATE_USER_PATH
-    ANGBAND_DIR_USER = std::filesystem::path(PRIVATE_USER_PATH).append(VARIANT_NAME);
+    ANGBAND_DIR_USER = path_parse(PRIVATE_USER_PATH).append(VARIANT_NAME);
 #else
-    ANGBAND_DIR_USER = std::filesystem::path(libpath).append("user");
+    ANGBAND_DIR_USER = std::filesystem::path(varpath).append("user");
 #endif
     ANGBAND_DIR_XTRA = std::filesystem::path(libpath).append("xtra");
 
@@ -101,6 +107,87 @@ void init_file_paths(const std::filesystem::path &libpath)
     remove_old_debug_savefiles();
 }
 
+/*
+ * Helper function for create_needed_dirs().  Copied over from PosChengband.
+ */
+static bool dir_exists(const std::filesystem::path path)
+{
+    struct stat buf;
+    if (stat(path.native().data(), &buf) != 0)
+       return false;
+#ifdef WIN32
+    else if (buf.st_mode & S_IFDIR)
+#else
+    else if (S_ISDIR(buf.st_mode))
+#endif
+       return true;
+    else
+       return false;
+}
+
+
+/*
+ * Helper function for create_needed_dirs().  Copied over from PosChengband
+ * but use the global definition for the path separator rather than a local
+ * one in PosChengband's code and check for paths that end with the path
+ * separator.
+ */
+static bool dir_create(const std::filesystem::path path)
+{
+#ifdef WIN32
+    /* If the directory already exists then we're done */
+    if (dir_exists(path)) return true;
+    return false;
+#else
+    std::vector<std::filesystem::path> missing;
+    std::filesystem::path next_path = path;
+
+    while (1) {
+        if (dir_exists(next_path)) {
+            break;
+        }
+        missing.push_back(next_path);
+        if (!next_path.has_relative_path()) {
+               break;
+       }
+       next_path = next_path.parent_path();
+    }
+    for (; !missing.empty(); missing.pop_back()) {
+        if (mkdir(missing.back().native().data(), 0755) != 0) {
+            return false;
+        }
+    }
+    return true;
+#endif
+}
+
+
+/*
+ * Create any missing directories. We create only those dirs which may be
+ * empty (user/, save/, apex/, bone/, data/). Only user/ is created when
+ * the PRIVATE_USER_PATH preprocessor maccro has been set. The others are
+ * assumed to contain required files and therefore must exist at startup
+ * (edit/, pref/, file/, xtra/).
+ *
+ * ToDo: Only create the directories when actually writing files.
+ * Copied over from PosChengband to support main-cocoa.m.  Dropped
+ * creation of help/ (and removed it and info/ in the comment)
+ * since init_file_paths() puts those in libpath which may not be writable
+ * by the user running the application.  Added bone/ since
+ * init_file_paths() puts that in varpath.
+ */
+void create_needed_dirs(void)
+{
+    if (!dir_create(ANGBAND_DIR_USER)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_USER.native().data());
+#ifndef PRIVATE_USER_PATH
+    if (!dir_create(ANGBAND_DIR_SAVE)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_SAVE.native().data());
+    if (!dir_create(ANGBAND_DIR_DEBUG_SAVE)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_DEBUG_SAVE.native().data());
+    if (!dir_create(ANGBAND_DIR_APEX)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_APEX.native().data());
+    if (!dir_create(ANGBAND_DIR_BONE)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_BONE.native().data());
+    if (!dir_create(ANGBAND_DIR_DATA)) quit_fmt("Cannot create '%s'", ANGBAND_DIR_DATA.native().data());
+#endif /* ndef PRIVATE_USER_PATH */
+}
+
 /*!
  * @brief 画面左下にシステムメッセージを表示する / Take notes on line 23
  * @param str 初期化中のコンテンツ文字列