diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1d127930e..b1dc3de0b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2365,6 +2365,7 @@ static const LuaWrapper::FunctionReg dfhack_filesystem_module[] = { WRAPM(Filesystem, getcwd), WRAPM(Filesystem, chdir), WRAPM(Filesystem, mkdir), + WRAPM(Filesystem, mkdir_recursive), WRAPM(Filesystem, rmdir), WRAPM(Filesystem, exists), WRAPM(Filesystem, isfile), diff --git a/library/include/modules/Filesystem.h b/library/include/modules/Filesystem.h index bc7a02337..bc0953d27 100644 --- a/library/include/modules/Filesystem.h +++ b/library/include/modules/Filesystem.h @@ -149,6 +149,7 @@ namespace DFHack { DFHACK_EXPORT bool chdir (std::string path); DFHACK_EXPORT std::string getcwd (); DFHACK_EXPORT bool mkdir (std::string path); + DFHACK_EXPORT bool mkdir_recursive (std::string path); DFHACK_EXPORT bool rmdir (std::string path); DFHACK_EXPORT bool stat (std::string path, STAT_STRUCT &info); DFHACK_EXPORT bool exists (std::string path); diff --git a/library/modules/Filesystem.cpp b/library/modules/Filesystem.cpp index 430e6351e..373b403c8 100644 --- a/library/modules/Filesystem.cpp +++ b/library/modules/Filesystem.cpp @@ -82,6 +82,37 @@ bool Filesystem::mkdir (std::string path) return fail == 0; } +static bool mkdir_recursive_impl (std::string path) +{ + size_t last_slash = path.find_last_of("/"); + if (last_slash != std::string::npos) + { + std::string parent_path = path.substr(0, last_slash); + bool parent_exists = mkdir_recursive_impl(parent_path); + if (!parent_exists) + { + return false; + } + } + return Filesystem::mkdir(path) || errno == EEXIST; +} + +bool Filesystem::mkdir_recursive (std::string path) +{ +#ifdef _WIN32 + // normalize to forward slashes + std::replace(path.begin(), path.end(), '\\', '/'); +#endif + + if (path.size() > PATH_MAX) + { + // path too long + return false; + } + + return mkdir_recursive_impl(path); +} + bool Filesystem::rmdir (std::string path) { int fail; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3dc461ee9..678b7eec2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -93,8 +93,7 @@ if(BUILD_SUPPORTED) dfhack_plugin(automaterial automaterial.cpp) dfhack_plugin(automelt automelt.cpp) dfhack_plugin(autotrade autotrade.cpp) - # stdc++fs required for std::experimental::filesystem; once we move to c++17 this can be removed since it's in the STL there - dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua stdc++fs) + dfhack_plugin(blueprint blueprint.cpp LINK_LIBRARIES lua) dfhack_plugin(burrows burrows.cpp LINK_LIBRARIES lua) dfhack_plugin(building-hacks building-hacks.cpp LINK_LIBRARIES lua) dfhack_plugin(buildingplan buildingplan.cpp LINK_LIBRARIES buildingplan-lib) diff --git a/plugins/blueprint.cpp b/plugins/blueprint.cpp index d7ce19334..4653b469c 100644 --- a/plugins/blueprint.cpp +++ b/plugins/blueprint.cpp @@ -3,7 +3,6 @@ //Translates a region of tiles specified by the cursor and arguments/prompts into a series of blueprint files suitable for digfort/buildingplan/quickfort #include -#include // This is just in c++17, but we're currently compiling with c++11 #include #include @@ -11,6 +10,7 @@ #include "LuaTools.h" #include "modules/Buildings.h" +#include "modules/Filesystem.h" #include "modules/Gui.h" #include "modules/MapCache.h" @@ -35,8 +35,6 @@ using std::pair; using namespace DFHack; using namespace df::enums; -namespace filesystem = std::experimental::filesystem; - DFHACK_PLUGIN("blueprint"); enum phase {DIG=1, BUILD=2, PLACE=4, QUERY=8}; @@ -562,13 +560,11 @@ string get_tile_query(df::building* b) return " "; } -void init_stream(ofstream &out, filesystem::path basename, std::string target) +void init_stream(ofstream &out, std::string basename, std::string target) { - filesystem::path out_path(basename); - out_path += "-"; - out_path += target; - out_path += ".csv"; - out.open(out_path, ofstream::trunc); + std::ostringstream out_path; + out_path << basename << "-" << target << ".csv"; + out.open(out_path.str(), ofstream::trunc); out << "#" << target << endl; } @@ -576,16 +572,21 @@ command_result do_transform(DFCoord start, DFCoord end, string name, uint32_t ph { ofstream dig, build, place, query; - filesystem::path basename = "blueprints"; - basename /= name; - basename.make_preferred(); + std::string basename = "blueprints/" + name; + +#ifdef _WIN32 + // normalize to forward slashes + std::replace(basename.begin(), basename.end(), '\\', '/'); +#endif + + size_t last_slash = basename.find_last_of("/"); + std::string parent_path = basename.substr(0, last_slash); // create output directory if it doesn't already exist std::error_code ec; - if (!filesystem::create_directories(basename.parent_path(), ec) && ec) + if (!Filesystem::mkdir_recursive(parent_path)) { - err << "could not create output directory: " << basename.parent_path() - << " (" << ec.message() << ")"; + err << "could not create output directory: '" << parent_path << "'"; return CR_FAILURE; }