From 10e185c65654a4edb84ffc857851ecac15ba998f Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Sun, 8 Mar 2020 23:23:41 +0100 Subject: [PATCH] Heightfield to mesh conversion (#890) --- apps/CMakeLists.txt | 20 +- apps/yheightfieldproc/CMakeLists.txt | 6 + .../ext/filesystem.hpp | 0 apps/yheightfieldproc/yheightfieldproc.cpp | 141 + apps/yimageproc/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../yimageproc.cpp} | 4 +- apps/yimageview/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../yimageview.cpp} | 0 apps/yimageviews/CMakeLists.txt | 5 + .../yimageviews.cpp} | 0 apps/yimgproc/CMakeLists.txt | 5 - apps/yimgview/CMakeLists.txt | 5 - apps/yimgviews/CMakeLists.txt | 5 - apps/yimshproc/CMakeLists.txt | 5 - apps/yimshproc/yimshproc.cpp | 163 - apps/yimshproc/yimshproc.h | 374 -- apps/ymshproc/CMakeLists.txt | 6 - apps/ysceneitrace/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../ysceneitrace.cpp} | 0 apps/ysceneitraces/CMakeLists.txt | 5 + .../ysceneitraces.cpp} | 0 apps/ysceneproc/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../ysceneproc.cpp} | 0 apps/yscenetrace/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../yscenetrace.cpp} | 0 apps/ysceneview/CMakeLists.txt | 5 + .../ext/filesystem.hpp | 0 .../ysceneview.cpp} | 0 apps/yscnitrace/CMakeLists.txt | 5 - apps/yscnitraces/CMakeLists.txt | 5 - apps/yscnproc/CMakeLists.txt | 5 - apps/yscntrace/CMakeLists.txt | 5 - apps/yscnview/CMakeLists.txt | 5 - apps/yshapeproc/CMakeLists.txt | 6 + apps/yshapeproc/ext/filesystem.hpp | 5114 +++++++++++++++++ .../yshapeproc.cpp} | 30 +- docs/readme.md | 18 +- libs/yocto/yocto_image.cpp | 43 + libs/yocto/yocto_image.h | 33 +- libs/yocto/yocto_math.h | 25 +- libs/yocto/yocto_ply.h | 9 + libs/yocto/yocto_sceneio.cpp | 4 + libs/yocto/yocto_shape.cpp | 37 + libs/yocto/yocto_shape.h | 14 + readme.md | 18 +- scripts/scenes.py | 8 +- 51 files changed, 5506 insertions(+), 652 deletions(-) create mode 100644 apps/yheightfieldproc/CMakeLists.txt rename apps/{yimgproc => yheightfieldproc}/ext/filesystem.hpp (100%) create mode 100644 apps/yheightfieldproc/yheightfieldproc.cpp create mode 100644 apps/yimageproc/CMakeLists.txt rename apps/{yimgview => yimageproc}/ext/filesystem.hpp (100%) rename apps/{yimgproc/yimgproc.cpp => yimageproc/yimageproc.cpp} (99%) create mode 100644 apps/yimageview/CMakeLists.txt rename apps/{ymshproc => yimageview}/ext/filesystem.hpp (100%) rename apps/{yimgview/yimgview.cpp => yimageview/yimageview.cpp} (100%) create mode 100644 apps/yimageviews/CMakeLists.txt rename apps/{yimgviews/yimgviews.cpp => yimageviews/yimageviews.cpp} (100%) delete mode 100644 apps/yimgproc/CMakeLists.txt delete mode 100644 apps/yimgview/CMakeLists.txt delete mode 100644 apps/yimgviews/CMakeLists.txt delete mode 100644 apps/yimshproc/CMakeLists.txt delete mode 100644 apps/yimshproc/yimshproc.cpp delete mode 100644 apps/yimshproc/yimshproc.h delete mode 100644 apps/ymshproc/CMakeLists.txt create mode 100644 apps/ysceneitrace/CMakeLists.txt rename apps/{yscnitrace => ysceneitrace}/ext/filesystem.hpp (100%) rename apps/{yscnitrace/yscnitrace.cpp => ysceneitrace/ysceneitrace.cpp} (100%) create mode 100644 apps/ysceneitraces/CMakeLists.txt rename apps/{yscnitraces/yscnitraces.cpp => ysceneitraces/ysceneitraces.cpp} (100%) create mode 100644 apps/ysceneproc/CMakeLists.txt rename apps/{yscnproc => ysceneproc}/ext/filesystem.hpp (100%) rename apps/{yscnproc/yscnproc.cpp => ysceneproc/ysceneproc.cpp} (100%) create mode 100644 apps/yscenetrace/CMakeLists.txt rename apps/{yscntrace => yscenetrace}/ext/filesystem.hpp (100%) rename apps/{yscntrace/yscntrace.cpp => yscenetrace/yscenetrace.cpp} (100%) create mode 100644 apps/ysceneview/CMakeLists.txt rename apps/{yscnview => ysceneview}/ext/filesystem.hpp (100%) rename apps/{yscnview/yscnview.cpp => ysceneview/ysceneview.cpp} (100%) delete mode 100644 apps/yscnitrace/CMakeLists.txt delete mode 100644 apps/yscnitraces/CMakeLists.txt delete mode 100644 apps/yscnproc/CMakeLists.txt delete mode 100644 apps/yscntrace/CMakeLists.txt delete mode 100644 apps/yscnview/CMakeLists.txt create mode 100644 apps/yshapeproc/CMakeLists.txt create mode 100644 apps/yshapeproc/ext/filesystem.hpp rename apps/{ymshproc/ymshproc.cpp => yshapeproc/yshapeproc.cpp} (100%) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 9dd3dceea..228112b6d 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,13 +1,13 @@ -add_subdirectory(yscntrace) -add_subdirectory(yscnproc) -add_subdirectory(yimgproc) -add_subdirectory(ymshproc) +add_subdirectory(yscenetrace) +add_subdirectory(ysceneproc) +add_subdirectory(yimageproc) +add_subdirectory(yshapeproc) +add_subdirectory(yheightfieldproc) if(YOCTO_OPENGL) -add_subdirectory(yscnview) -add_subdirectory(yimshproc) -add_subdirectory(yscnitrace) -add_subdirectory(yscnitraces) -add_subdirectory(yimgview) -add_subdirectory(yimgviews) +add_subdirectory(ysceneview) +add_subdirectory(ysceneitrace) +add_subdirectory(ysceneitraces) +add_subdirectory(yimageview) +add_subdirectory(yimageviews) endif(YOCTO_OPENGL) diff --git a/apps/yheightfieldproc/CMakeLists.txt b/apps/yheightfieldproc/CMakeLists.txt new file mode 100644 index 000000000..43bf91c92 --- /dev/null +++ b/apps/yheightfieldproc/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(yheightfieldproc yheightfieldproc.cpp) + +set_target_properties(yheightfieldproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yheightfieldproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yheightfieldproc yocto) + diff --git a/apps/yimgproc/ext/filesystem.hpp b/apps/yheightfieldproc/ext/filesystem.hpp similarity index 100% rename from apps/yimgproc/ext/filesystem.hpp rename to apps/yheightfieldproc/ext/filesystem.hpp diff --git a/apps/yheightfieldproc/yheightfieldproc.cpp b/apps/yheightfieldproc/yheightfieldproc.cpp new file mode 100644 index 000000000..0225dfcdd --- /dev/null +++ b/apps/yheightfieldproc/yheightfieldproc.cpp @@ -0,0 +1,141 @@ +// +// LICENSE: +// +// Copyright (c) 2016 -- 2020 Fabio Pellacini +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +using namespace yocto::math; +namespace shp = yocto::shape; +namespace img = yocto::image; +namespace cli = yocto::commonio; + +#include "ext/filesystem.hpp" +namespace sfs = ghc::filesystem; + +using std::string; +using std::vector; +using namespace std::string_literals; + +int main(int argc, const char* argv[]) { + // command line parameters + auto smooth = false; + auto scale = vec3f{1}; + auto uscale = 1.0f; + auto height = 1.0f; + auto rotate = zero3f; + auto translate = zero3f; + auto info = false; + auto output = "out.ply"s; + auto filename = "heightfield.png"s; + + // parse command line + auto cli = cli::make_cli( + "yheightfieldproc", "Makes a mesh from a heightfield"); + add_option(cli, "--height,-h", height, "Height scale"); + add_option(cli, "--smooth", smooth, "Compute smooth normals"); + add_option(cli, "--rotatey,-ry", rotate.y, "Rotate around y axis"); + add_option(cli, "--rotatex,-rx", rotate.x, "Rotate around x axis"); + add_option(cli, "--rotatez,-rz", rotate.z, "Rotate around z axis"); + add_option(cli, "--translatey,-ty", translate.y, "Translate along y axis"); + add_option(cli, "--translatex,-tx", translate.x, "Translate along x axis"); + add_option(cli, "--translatez,-tz", translate.z, "Translate along z axis"); + add_option(cli, "--scale,-s", uscale, "Scale along xyz axes"); + add_option(cli, "--scaley,-sy", scale.y, "Scale along y axis"); + add_option(cli, "--scalex,-sx", scale.x, "Scale along x axis"); + add_option(cli, "--scalez,-sz", scale.z, "Scale along z axis"); + add_option(cli, "--info,-i", info, "print mesh info"); + add_option(cli, "--output,-o", output, "output mesh"); + add_option(cli, "mesh", filename, "input heightfield", true); + parse_cli(cli, argc, argv); + + // mesh data + auto positions = std::vector{}; + auto normals = std::vector{}; + auto texcoords = std::vector{}; + auto quads = std::vector{}; + + // image data + auto heightfield = img::image{}; + + // load mesh + auto ioerror = ""s; + cli::print_progress("load image", 0, 1); + if (img::is_hdr_filename(filename)) { + if (!img::load_image(filename, heightfield, ioerror)) + cli::print_fatal(ioerror); + } else { + auto heightfield16 = img::image{}; + if (!img::load_image(filename, heightfield16, ioerror)) + cli::print_fatal(ioerror); + heightfield = img::ushort_to_float(heightfield16); + } + cli::print_progress("load shape", 1, 1); + + // adjust height + if (height != 1) { + for (auto& pixel : heightfield) pixel *= height; + } + + // create heightfield + shp::make_heightfield(quads, positions, normals, texcoords, + heightfield.size(), heightfield.data_vector()); + if (!smooth) normals.clear(); + + // print info + if (info) { + cli::print_info("shape stats ------------"); + auto stats = shp::shape_stats( + {}, {}, {}, quads, {}, {}, {}, positions, normals, texcoords, {}, {}); + for (auto& stat : stats) cli::print_info(stat); + } + + // transform + if (uscale != 1) scale *= uscale; + if (translate != zero3f || rotate != zero3f || scale != vec3f{1}) { + cli::print_progress("transform shape", 0, 1); + auto xform = translation_frame(translate) * scaling_frame(scale) * + rotation_frame({1, 0, 0}, radians(rotate.x)) * + rotation_frame({0, 0, 1}, radians(rotate.z)) * + rotation_frame({0, 1, 0}, radians(rotate.y)); + for (auto& p : positions) p = transform_point(xform, p); + for (auto& n : normals) + n = transform_normal(xform, n, max(scale) != min(scale)); + cli::print_progress("transform shape", 1, 1); + } + + // save mesh + cli::print_progress("save shape", 0, 1); + if (!shp::save_shape(output, {}, {}, {}, quads, positions, normals, texcoords, + {}, {}, ioerror)) + cli::print_fatal(ioerror); + cli::print_progress("save shape", 1, 1); + + // done + return 0; +} diff --git a/apps/yimageproc/CMakeLists.txt b/apps/yimageproc/CMakeLists.txt new file mode 100644 index 000000000..7135ef236 --- /dev/null +++ b/apps/yimageproc/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(yimageproc yimageproc.cpp) + +set_target_properties(yimageproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yimageproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yimageproc yocto) diff --git a/apps/yimgview/ext/filesystem.hpp b/apps/yimageproc/ext/filesystem.hpp similarity index 100% rename from apps/yimgview/ext/filesystem.hpp rename to apps/yimageproc/ext/filesystem.hpp diff --git a/apps/yimgproc/yimgproc.cpp b/apps/yimageproc/yimageproc.cpp similarity index 99% rename from apps/yimgproc/yimgproc.cpp rename to apps/yimageproc/yimageproc.cpp index 7cc30fff3..2cbb95ef1 100644 --- a/apps/yimgproc/yimgproc.cpp +++ b/apps/yimageproc/yimageproc.cpp @@ -225,9 +225,9 @@ bool make_image_preset( make_fbmmap(img, size); img = srgb_to_rgb(img); } else if (type == "test-checker-opacity") { - make_checker(img, size, 1, {1,1,1,1}, {0,0,0,0}); + make_checker(img, size, 1, {1, 1, 1, 1}, {0, 0, 0, 0}); } else if (type == "test-grid-opacity") { - make_grid(img, size, 1, {1,1,1,1}, {0,0,0,0}); + make_grid(img, size, 1, {1, 1, 1, 1}, {0, 0, 0, 0}); } else { error = "unknown preset"; img = {}; diff --git a/apps/yimageview/CMakeLists.txt b/apps/yimageview/CMakeLists.txt new file mode 100644 index 000000000..251a32870 --- /dev/null +++ b/apps/yimageview/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(yimageview yimageview.cpp) + +set_target_properties(yimageview PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yimageview PUBLIC ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yimageview yocto yocto_gui) diff --git a/apps/ymshproc/ext/filesystem.hpp b/apps/yimageview/ext/filesystem.hpp similarity index 100% rename from apps/ymshproc/ext/filesystem.hpp rename to apps/yimageview/ext/filesystem.hpp diff --git a/apps/yimgview/yimgview.cpp b/apps/yimageview/yimageview.cpp similarity index 100% rename from apps/yimgview/yimgview.cpp rename to apps/yimageview/yimageview.cpp diff --git a/apps/yimageviews/CMakeLists.txt b/apps/yimageviews/CMakeLists.txt new file mode 100644 index 000000000..ec5433169 --- /dev/null +++ b/apps/yimageviews/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(yimageviews yimageviews.cpp) + +set_target_properties(yimageviews PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yimageviews PUBLIC ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yimageviews yocto yocto_gui) diff --git a/apps/yimgviews/yimgviews.cpp b/apps/yimageviews/yimageviews.cpp similarity index 100% rename from apps/yimgviews/yimgviews.cpp rename to apps/yimageviews/yimageviews.cpp diff --git a/apps/yimgproc/CMakeLists.txt b/apps/yimgproc/CMakeLists.txt deleted file mode 100644 index 4a856a210..000000000 --- a/apps/yimgproc/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yimgproc yimgproc.cpp) - -set_target_properties(yimgproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yimgproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yimgproc yocto) diff --git a/apps/yimgview/CMakeLists.txt b/apps/yimgview/CMakeLists.txt deleted file mode 100644 index a9ad5642e..000000000 --- a/apps/yimgview/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yimgview yimgview.cpp) - -set_target_properties(yimgview PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yimgview PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yimgview yocto yocto_gui) diff --git a/apps/yimgviews/CMakeLists.txt b/apps/yimgviews/CMakeLists.txt deleted file mode 100644 index d9ed74415..000000000 --- a/apps/yimgviews/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yimgviews yimgviews.cpp) - -set_target_properties(yimgviews PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yimgviews PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yimgviews yocto yocto_gui) diff --git a/apps/yimshproc/CMakeLists.txt b/apps/yimshproc/CMakeLists.txt deleted file mode 100644 index 0e6bcacaf..000000000 --- a/apps/yimshproc/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yimshproc yimshproc.cpp) - -set_target_properties(yimshproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yimshproc PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yimshproc yocto yocto_gui ${GL_EXTRA_LIBRARIES}) diff --git a/apps/yimshproc/yimshproc.cpp b/apps/yimshproc/yimshproc.cpp deleted file mode 100644 index b8412803f..000000000 --- a/apps/yimshproc/yimshproc.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "yimshproc.h" - -struct my_data { - std::vector face_adjacency; - std::vector> vertex_adjacency; - shp::geodesic_solver solver; - std::vector scalar_field; - std::vector vector_field; - - int num_samples = 500; - std::vector vertex_selection = {}; -}; - -void my_init(my_data& data, app_state* app) { - data.face_adjacency = shp::face_adjacencies(app->shape.triangles); - data.vertex_adjacency = shp::vertex_adjacencies( - app->shape.triangles, data.face_adjacency); - data.solver = shp::make_geodesic_solver( - app->shape.triangles, data.face_adjacency, app->shape.positions); -} - -void my_keycallback(my_data& data, app_state* app, int key, bool pressing) { - // Ignore release. - if (!pressing) return; - - cli::print_info( - "press: " + std::to_string((char)key) + " [" + std::to_string(key) + "]"); - auto enter = 257; - auto esc = 256; - - if (key == enter) { - cli::print_info("Enter pressed!"); - } - - if (key == esc) { - cli::print_info("Esc pressed!"); - init_camera(app); - set_frame(app->glcamera, app->camera.frame); - set_lens( - app->glcamera, app->camera.lens, app->camera.aspect, app->camera.film); - } - - if (key == 'z') { - cli::print_info("Z pressed!"); - } -} - -void my_click_callback(my_data& data, app_state* app, int face, const vec2f& uv, - int vertex, float distance) { - cli::print_info("clicked vertex: " + std::to_string(vertex)); - data.vertex_selection.push_back(vertex); - - auto positions = std::vector(data.vertex_selection.size()); - for (int i = 0; i < positions.size(); ++i) { - positions[i] = app->shape.positions[data.vertex_selection[i]]; - } - update_glpoints(app, positions); -} - -void my_draw_glwidgets(my_data& data, app_state* app, gui::window* win) { - if (draw_button(win, "Geodesic gradient field")) { - if (data.vertex_selection.size() > 1) { - data.scalar_field = compute_geodesic_distances( - data.solver, data.vertex_selection); - - data.vector_field = std::vector(app->shape.triangles.size()); - for (int i = 0; i < app->shape.triangles.size(); ++i) { - data.vector_field[i] = shp::compute_gradient( - app->shape.triangles[i], app->shape.positions, data.scalar_field); - } - update_glvector_field(app, data.vector_field, 100); - } - } - - if (draw_button(win, "Geodesic distance field")) { - if (data.vertex_selection.size()) { - data.scalar_field = compute_geodesic_distances( - data.solver, data.vertex_selection); - auto colors = std::vector(data.scalar_field.size()); - for (int i = 0; i < colors.size(); ++i) { - colors[i] = vec3f(data.scalar_field[i]); - } - set_colors(app->glshapes, colors); - } - } - - if (draw_button(win, "Compute geodesic path")) { - if (data.vertex_selection.size() > 1) { - auto positions = std::vector(); - auto n = data.vertex_selection.size(); - for (int i = 0; i < n - (n < 3); ++i) { - auto from = data.vertex_selection[i]; - auto to = data.vertex_selection[(i + 1) % n]; - auto field = compute_geodesic_distances(data.solver, {to}); - for (auto& f : field) f = -f; - - // @Speed: Remove tags from function api to avoid this. - auto dummy_tags = std::vector(app->shape.triangles.size(), 0); - auto path = shp::integrate_field(app->shape.triangles, - app->shape.positions, data.face_adjacency, dummy_tags, 0, field, - from, to); - - auto ppositions = shp::make_positions_from_path( - path, app->shape.positions); - positions.insert(positions.end(), ppositions.begin(), ppositions.end()); - } - update_glpolyline(app, positions); - } - } - - if (draw_slider(win, "Sample points", data.num_samples, 0, 10000)) { - data.vertex_selection = sample_vertices_poisson( - data.solver, data.num_samples); - auto positions = std::vector(data.vertex_selection.size()); - for (int i = 0; i < positions.size(); ++i) - positions[i] = app->shape.positions[data.vertex_selection[i]]; - update_glpoints(app, positions); - } - - if (draw_checkbox(win, "Show mesh edges", app->show_edges)) { - if (app->show_edges) - show_edges(app); - else - hide_edges(app); - } - - if (draw_button(win, "Clear")) { - data.vertex_selection.clear(); - clear(app); - } -} - -int main(int argc, const char* argv[]) { - std::string input_filename = "model.obj"; - - // Parse command line. - auto cli = cli::make_cli( - "yimshproc", "interactive viewer for mesh processing"); - add_option(cli, "model", input_filename, "model filenames", true); - parse_cli(cli, argc, argv); - - auto data = my_data{}; - - // Create callbacks that interface with yimshproc. - auto init = [&data](app_state* app) { - cli::print_info("init my data"); - my_init(data, app); - }; - auto key_callback = [&data](app_state* app, int key, bool pressing) { - my_keycallback(data, app, key, pressing); - }; - auto click_callback = [&data](app_state* a, int f, vec2f uv, int v, float d) { - my_click_callback(data, a, f, uv, v, d); - }; - auto draw_widgets = [&data](app_state* app, gui::window* win) { - my_draw_glwidgets(data, app, win); - }; - - yimshproc(input_filename, init, key_callback, click_callback, draw_widgets); - - // done - return 0; -} diff --git a/apps/yimshproc/yimshproc.h b/apps/yimshproc/yimshproc.h deleted file mode 100644 index e92d41e3a..000000000 --- a/apps/yimshproc/yimshproc.h +++ /dev/null @@ -1,374 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace yocto::math; -namespace sio = yocto::sceneio; -namespace shp = yocto::shape; -namespace cli = yocto::commonio; -namespace trc = yocto::trace; -namespace gui = yocto::gui; - -#include -using std::function; -using std::string; -using std::vector; -using namespace std::string_literals; - -struct app_state { - // Callbacks available for user to build its own behaviors - function init; - function key_callback; - function click_callback; - function draw_widgets; - - // Geometry data - sio::shape shape; - - // OpenGL data - gui::scene* glscene = new gui::scene{}; - gui::scene_params opengl_options = {}; - - // Interaction data - float time = 0; - bool show_edges = false; - sio::camera camera; - float camera_focus; - shp::bvh_tree bvh; - - // Internal handles - gui::camera* glcamera = nullptr; - gui::shape* glshapes = nullptr; - gui::shape* glpoints = nullptr; - gui::shape* glvfields = nullptr; - gui::shape* gledges = nullptr; - gui::shape* glpolylines = nullptr; - gui::material* glshapem = nullptr; - gui::material* glpointm = nullptr; - gui::material* glvfieldm = nullptr; - gui::material* gledgem = nullptr; - gui::material* glpolylinem = nullptr; - gui::object* glshapeo = nullptr; - gui::object* glpointo = nullptr; - gui::object* glvfieldo = nullptr; - gui::object* gledgeo = nullptr; - gui::object* glpolylineo = nullptr; - - // cleanup - ~app_state() { - if (glscene) delete glscene; - } -}; - -void update_glshape(app_state* app) { - // @Issue: This app is specialized for a model that is a triangle mesh. - // Loading a generic shape is unsafe, maybe we should load only - // triangle meshes here... - auto& shape = app->shape; - set_points(app->glshapes, shape.points); - set_lines(app->glshapes, shape.lines); - set_triangles(app->glshapes, shape.triangles); - set_quads(app->glshapes, shape.quads); - set_positions(app->glshapes, shape.positions); - set_normals(app->glshapes, shape.normals); - set_texcoords(app->glshapes, shape.texcoords); - set_colors(app->glshapes, shape.colors); -} - -void update_glpolyline(app_state* app, const std::vector& vertices) { - if (vertices.size()) { - auto elements = std::vector(vertices.size() - 1); - for (int i = 0; i < elements.size(); i++) elements[i] = {i, i + 1}; - set_positions(app->glpolylines, vertices); - set_lines(app->glpolylines, elements); - } -} - -void update_glpoints(app_state* app, const std::vector& points) { - if (points.size()) { - auto elements = std::vector(points.size()); - for (int i = 0; i < elements.size(); i++) elements[i] = i; - auto normals = std::vector(points.size(), {0, 0, 1}); - set_positions(app->glpolylines, points); - set_points(app->glpolylines, elements); - } -} - -void update_glvector_field(app_state* app, - const std::vector& vector_field, float scale = 0.01) { - auto perface = vector_field.size() == app->shape.triangles.size(); - auto pervertex = vector_field.size() == app->shape.positions.size(); - - if (!perface && !pervertex) { - throw std::runtime_error("input vector field has wrong size\n"); - } - - auto size = perface ? app->shape.triangles.size() - : app->shape.positions.size(); - auto positions = std::vector(size * 2); - - // Per-face vector field - if (perface) { - for (int i = 0; i < app->shape.triangles.size(); i++) { - auto x = app->shape.positions[app->shape.triangles[i].x]; - auto y = app->shape.positions[app->shape.triangles[i].y]; - auto z = app->shape.positions[app->shape.triangles[i].z]; - auto normal = triangle_normal(x, y, z); - normal *= 0.0001; - auto center = (x + y + z) / 3; - auto from = center + normal; - auto to = from + (scale * vector_field[i]) + normal; - positions[i * 2] = from; - positions[i * 2 + 1] = to; - } - } else { - for (int i = 0; i < app->shape.positions.size(); i++) { - auto from = app->shape.positions[i]; - auto to = from + scale * vector_field[i]; - positions[i * 2] = from; - positions[i * 2 + 1] = to; - } - } - - auto elements = std::vector(size); - for (int i = 0; i < elements.size(); i++) { - elements[i] = {2 * i, 2 * i + 1}; - } - - set_positions(app->glvfields, positions); - set_lines(app->glvfields, elements); -} - -void update_gledges(app_state* app) { - auto positions = app->shape.positions; - for (int i = 0; i < positions.size(); i++) { - positions[i] += app->shape.normals[i] * 0.0001; - } - - auto elements = std::vector(); - elements.reserve(app->shape.triangles.size() * 3); - for (int i = 0; i < app->shape.triangles.size(); i++) { - for (int k = 0; k < 3; k++) { - auto a = app->shape.triangles[i][k]; - auto b = app->shape.triangles[i][(k + 1) % 3]; - if (a < b) { - elements.push_back({a, b}); - } - } - } - set_positions(app->gledges, positions); - set_lines(app->gledges, elements); -} - -void init_camera(app_state* app, const vec3f& from = vec3f{0, 0.5, 1.5}, - const vec3f& to = {0, 0, 0}) { - app->camera = sio::camera{}; - auto up = vec3f{0, 1, 0}; - app->camera.lens = 0.02f; - app->camera.orthographic = false; - app->camera.aperture = 0; - app->camera.frame = lookat_frame(from, to, up); - app->camera.film = 0.036f; - app->camera.aspect = 0.036f / 0.015f; - app->camera.focus = length(to - from); - app->camera_focus = app->camera.focus; -} - -void init_bvh(app_state* app) { - make_triangles_bvh( - app->bvh, app->shape.triangles, app->shape.positions, app->shape.radius); -} - -void hide_edges(app_state* app) { - app->show_edges = false; - set_hidden(app->gledgeo, true); -} -void show_edges(app_state* app) { - app->show_edges = true; - set_hidden(app->gledgeo, false); -} - -void init_opengl_scene(app_state* app) { - init_glscene(app->glscene); - app->glcamera = add_camera(app->glscene); - set_frame(app->glcamera, app->camera.frame); - set_lens( - app->glcamera, app->camera.lens, app->camera.aspect, app->camera.film); - set_nearfar(app->glcamera, 0.001, 10000); - - // The model. - app->glshapes = add_shape(app->glscene); - app->glshapem = add_material(app->glscene); - set_color(app->glshapem, {1, 0.2, 0}); - set_specular(app->glshapem, 1); - set_roughness(app->glshapem, 0.3); - app->glshapeo = add_object(app->glscene); - set_shape(app->glshapeo, app->glshapes); - set_material(app->glshapeo, app->glshapem); - update_glshape(app); - - // The points. - app->glpoints = add_shape(app->glscene); - app->glpointm = add_material(app->glscene); - set_emission(app->glpointm, {1, 1, 1}); - set_roughness(app->glpointm, 0.0); - app->glpointo = add_object(app->glscene); - set_shape(app->glpointo, app->glpoints); - set_material(app->glpointo, app->glpointm); - - // The vector field. - app->glvfields = add_shape(app->glscene); - app->glvfieldm = add_material(app->glscene); - set_emission(app->glvfieldm, {1, 1, 1}); - set_roughness(app->glvfieldm, 0.0); - app->glvfieldo = add_object(app->glscene); - set_shape(app->glvfieldo, app->glvfields); - set_material(app->glvfieldo, app->glvfieldm); - - // The edges. - app->gledges = add_shape(app->glscene); - app->gledgem = add_material(app->glscene); - set_emission(app->gledgem, {1, 1, 1}); - set_roughness(app->gledgem, 0.0); - app->gledgeo = add_object(app->glscene); - set_shape(app->gledgeo, app->gledges); - set_material(app->gledgeo, app->glvfieldm); - update_gledges(app); - - // The polyline. - app->glpolylines = add_shape(app->glscene); - app->glpolylinem = add_material(app->glscene); - set_emission(app->glpolylinem, {1, 1, 1}); - set_roughness(app->glpolylinem, 0.0); - app->glpolylineo = add_object(app->glscene); - set_shape(app->glpolylineo, app->glpolylines); - set_material(app->glpolylineo, app->glpolylinem); - - // Hide edges. - if (!app->show_edges) hide_edges(app); - - // Add lights. - set_light(add_light(app->glscene), {5, 5, 5}, {30, 30, 30}, false); - set_light(add_light(app->glscene), {-5, 5, 5}, {30, 30, 30}, false); - set_light(add_light(app->glscene), {0, 5, -5}, {30, 30, 30}, false); -} - -void clear(app_state* app) { - // TODO: not sure how this works - // TODO: fix me - // for (int i = 0; i < app->scene.shapes.size(); i++) { - // if (i == app->glshape_id) continue; - // if (i == app->gledges_id) continue; - // delete_glshape(app->scene.shapes[i]); - // } - // delete_glarraybuffer(app->glshapes().colors); - // init_glarraybuffer(app->glshapes().colors, - // std::vector(app->shape.positions.size(), {1, 1, 1, 1})); -} - -void yimshproc(const std::string& input_filename, - function init, - function key_callback, - function click_callback, - function draw_widgets) { - auto app_guard = std::make_unique(); - auto app = app_guard.get(); - - // init shape - auto ioerror = ""s; - if (!shp::load_shape(input_filename, app->shape.points, app->shape.lines, - app->shape.triangles, app->shape.quads, app->shape.positions, - app->shape.normals, app->shape.texcoords, app->shape.colors, - app->shape.radius, ioerror)) - cli::print_fatal(ioerror); - init_bvh(app); - init_camera(app); - - app->init = init; - app->key_callback = key_callback; - app->click_callback = click_callback; - app->draw_widgets = draw_widgets; // @Issue: win not needed for widgets - - app->init(app); - - // Init window. - auto win_guard = std::make_unique(); - auto win = win_guard.get(); - init_window(win, {1280 + 320, 720}, "yimshproc", true); - init_opengl_scene(app); - - // callbacks - set_draw_callback(win, [app](gui::window* win, const gui::input& input) { - draw_scene(app->glscene, app->glcamera, input.framebuffer_viewport, - app->opengl_options); - }); - set_widgets_callback( - win, [app, draw_widgets](gui::window* win, const gui::input& input) { - draw_widgets(app, win); - }); - set_click_callback(win, [app](gui::window* win, bool left, bool press, - const gui::input& input) { - auto mouse = input.mouse_pos / - vec2f{(float)input.window_size.x, (float)input.window_size.y}; - - // Ray trace camera ray. - if (!left && press) { - auto ray = camera_ray(app->camera.frame, app->camera.lens, - app->camera.aspect >= 1 - ? vec2f{app->camera.film, app->camera.film / app->camera.aspect} - : vec2f{app->camera.film * app->camera.aspect, app->camera.film}, - mouse + 0.5f); - auto intersection = intersect_triangles_bvh( - app->bvh, app->shape.triangles, app->shape.positions, ray); - - if (intersection.hit) { - auto uvw = vec3f{intersection.uv.x, intersection.uv.y, - 1 - intersection.uv.x - intersection.uv.y}; - int k = 0; - if (uvw.x > uvw.y && uvw.x > uvw.z) k = 1; - if (uvw.y > uvw.x && uvw.y > uvw.z) k = 2; - auto vertex = app->shape.triangles[intersection.element][k]; - app->click_callback(app, intersection.element, intersection.uv, vertex, - intersection.distance); - } - } - }); - set_scroll_callback( - win, [app](gui::window* win, float yoffset, const gui::input& input) { - float zoom = yoffset > 0 ? 0.1 : -0.1; - update_turntable( - app->camera.frame, app->camera.focus, zero2f, zoom, zero2f); - set_frame(app->glcamera, app->camera.frame); - set_lens(app->glcamera, app->camera.lens, app->camera.aspect, - app->camera.film); - }); - set_key_callback(win, - [app](gui::window* win, int key, bool pressing, const gui::input& input) { - app->key_callback(app, key, pressing); - }); - set_uiupdate_callback(win, [app](gui::window* win, const gui::input& input) { - // Handle mouse and keyboard for navigation. - if ((input.mouse_left || input.mouse_right) && !input.modifier_alt && - !input.widgets_active) { - auto dolly = 0.0f; - auto pan = zero2f; - auto rotate = zero2f; - if (input.mouse_left && !input.modifier_shift) - rotate = (input.mouse_pos - input.mouse_last) / 100.0f; - if (input.mouse_left && input.modifier_shift) - pan = (input.mouse_pos - input.mouse_last) / 100.0f; - rotate.y = -rotate.y; - pan.x = -pan.x; - update_turntable( - app->camera.frame, app->camera.focus, rotate, dolly, pan); - set_frame(app->glcamera, app->camera.frame); - set_lens(app->glcamera, app->camera.lens, app->camera.aspect, - app->camera.film); - } - }); - - // cleanup - clear_window(win); -} diff --git a/apps/ymshproc/CMakeLists.txt b/apps/ymshproc/CMakeLists.txt deleted file mode 100644 index da8a25bff..000000000 --- a/apps/ymshproc/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_executable(ymshproc ymshproc.cpp) - -set_target_properties(ymshproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(ymshproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(ymshproc yocto) - diff --git a/apps/ysceneitrace/CMakeLists.txt b/apps/ysceneitrace/CMakeLists.txt new file mode 100644 index 000000000..30efd15a1 --- /dev/null +++ b/apps/ysceneitrace/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(ysceneitrace ysceneitrace.cpp) + +set_target_properties(ysceneitrace PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(ysceneitrace PUBLIC ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(ysceneitrace yocto yocto_gui) diff --git a/apps/yscnitrace/ext/filesystem.hpp b/apps/ysceneitrace/ext/filesystem.hpp similarity index 100% rename from apps/yscnitrace/ext/filesystem.hpp rename to apps/ysceneitrace/ext/filesystem.hpp diff --git a/apps/yscnitrace/yscnitrace.cpp b/apps/ysceneitrace/ysceneitrace.cpp similarity index 100% rename from apps/yscnitrace/yscnitrace.cpp rename to apps/ysceneitrace/ysceneitrace.cpp diff --git a/apps/ysceneitraces/CMakeLists.txt b/apps/ysceneitraces/CMakeLists.txt new file mode 100644 index 000000000..058f6b254 --- /dev/null +++ b/apps/ysceneitraces/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(ysceneitraces ysceneitraces.cpp) + +set_target_properties(ysceneitraces PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(ysceneitraces PUBLIC ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(ysceneitraces yocto yocto_gui) diff --git a/apps/yscnitraces/yscnitraces.cpp b/apps/ysceneitraces/ysceneitraces.cpp similarity index 100% rename from apps/yscnitraces/yscnitraces.cpp rename to apps/ysceneitraces/ysceneitraces.cpp diff --git a/apps/ysceneproc/CMakeLists.txt b/apps/ysceneproc/CMakeLists.txt new file mode 100644 index 000000000..28a7e8c3e --- /dev/null +++ b/apps/ysceneproc/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(ysceneproc ysceneproc.cpp) + +set_target_properties(ysceneproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(ysceneproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(ysceneproc yocto) diff --git a/apps/yscnproc/ext/filesystem.hpp b/apps/ysceneproc/ext/filesystem.hpp similarity index 100% rename from apps/yscnproc/ext/filesystem.hpp rename to apps/ysceneproc/ext/filesystem.hpp diff --git a/apps/yscnproc/yscnproc.cpp b/apps/ysceneproc/ysceneproc.cpp similarity index 100% rename from apps/yscnproc/yscnproc.cpp rename to apps/ysceneproc/ysceneproc.cpp diff --git a/apps/yscenetrace/CMakeLists.txt b/apps/yscenetrace/CMakeLists.txt new file mode 100644 index 000000000..7eeece485 --- /dev/null +++ b/apps/yscenetrace/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(yscenetrace yscenetrace.cpp) + +set_target_properties(yscenetrace PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yscenetrace PRIVATE ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yscenetrace yocto) diff --git a/apps/yscntrace/ext/filesystem.hpp b/apps/yscenetrace/ext/filesystem.hpp similarity index 100% rename from apps/yscntrace/ext/filesystem.hpp rename to apps/yscenetrace/ext/filesystem.hpp diff --git a/apps/yscntrace/yscntrace.cpp b/apps/yscenetrace/yscenetrace.cpp similarity index 100% rename from apps/yscntrace/yscntrace.cpp rename to apps/yscenetrace/yscenetrace.cpp diff --git a/apps/ysceneview/CMakeLists.txt b/apps/ysceneview/CMakeLists.txt new file mode 100644 index 000000000..693430a34 --- /dev/null +++ b/apps/ysceneview/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(ysceneview ysceneview.cpp) + +set_target_properties(ysceneview PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(ysceneview PUBLIC ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(ysceneview yocto yocto_gui) diff --git a/apps/yscnview/ext/filesystem.hpp b/apps/ysceneview/ext/filesystem.hpp similarity index 100% rename from apps/yscnview/ext/filesystem.hpp rename to apps/ysceneview/ext/filesystem.hpp diff --git a/apps/yscnview/yscnview.cpp b/apps/ysceneview/ysceneview.cpp similarity index 100% rename from apps/yscnview/yscnview.cpp rename to apps/ysceneview/ysceneview.cpp diff --git a/apps/yscnitrace/CMakeLists.txt b/apps/yscnitrace/CMakeLists.txt deleted file mode 100644 index 72c9e9c6c..000000000 --- a/apps/yscnitrace/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yscnitrace yscnitrace.cpp) - -set_target_properties(yscnitrace PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yscnitrace PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yscnitrace yocto yocto_gui) diff --git a/apps/yscnitraces/CMakeLists.txt b/apps/yscnitraces/CMakeLists.txt deleted file mode 100644 index 2f2627d51..000000000 --- a/apps/yscnitraces/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yscnitraces yscnitraces.cpp) - -set_target_properties(yscnitraces PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yscnitraces PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yscnitraces yocto yocto_gui) diff --git a/apps/yscnproc/CMakeLists.txt b/apps/yscnproc/CMakeLists.txt deleted file mode 100644 index f565e509c..000000000 --- a/apps/yscnproc/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yscnproc yscnproc.cpp) - -set_target_properties(yscnproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yscnproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yscnproc yocto) diff --git a/apps/yscntrace/CMakeLists.txt b/apps/yscntrace/CMakeLists.txt deleted file mode 100644 index a09d8e1bc..000000000 --- a/apps/yscntrace/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yscntrace yscntrace.cpp) - -set_target_properties(yscntrace PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yscntrace PRIVATE ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yscntrace yocto) diff --git a/apps/yscnview/CMakeLists.txt b/apps/yscnview/CMakeLists.txt deleted file mode 100644 index f0e01004b..000000000 --- a/apps/yscnview/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(yscnview yscnview.cpp) - -set_target_properties(yscnview PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_include_directories(yscnview PUBLIC ${CMAKE_SOURCE_DIR}/libs) -target_link_libraries(yscnview yocto yocto_gui) diff --git a/apps/yshapeproc/CMakeLists.txt b/apps/yshapeproc/CMakeLists.txt new file mode 100644 index 000000000..90e2ab744 --- /dev/null +++ b/apps/yshapeproc/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(yshapeproc yshapeproc.cpp) + +set_target_properties(yshapeproc PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_include_directories(yshapeproc PRIVATE ${CMAKE_SOURCE_DIR}/libs) +target_link_libraries(yshapeproc yocto) + diff --git a/apps/yshapeproc/ext/filesystem.hpp b/apps/yshapeproc/ext/filesystem.hpp new file mode 100644 index 000000000..3b79c9fb3 --- /dev/null +++ b/apps/yshapeproc/ext/filesystem.hpp @@ -0,0 +1,5114 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++147/C++17 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//--------------------------------------------------------------------------------------- +// +// To dynamically select std::filesystem where available, you could use: +// +// #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +// #include +// namespace fs = std::filesystem; +// #else +// #include +// namespace fs = ghc::filesystem; +// #endif +// +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_H +#define GHC_FILESYSTEM_H + +#if defined(__APPLE__) && defined(__MACH__) +#define GHC_OS_MACOS +#elif defined(__linux__) +#define GHC_OS_LINUX +#elif defined(_WIN64) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN64 +#elif defined(_WIN32) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN32 +#else +#error "Operating system currently not supported!" +#endif + +#if defined(GHC_FILESYSTEM_IMPLEMENTATION) +#define GHC_EXPAND_IMPL +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#define GHC_FS_API +#define GHC_FS_API_CLASS +#else +#define GHC_FS_API __attribute__((visibility("default"))) +#define GHC_FS_API_CLASS __attribute__((visibility("default"))) +#endif +#elif defined(GHC_FILESYSTEM_FWD) +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#define GHC_FS_API extern +#define GHC_FS_API_CLASS +#else +#define GHC_FS_API extern +#define GHC_FS_API_CLASS +#endif +#else +#define GHC_EXPAND_IMPL +#define GHC_INLINE inline +#define GHC_FS_API +#define GHC_FS_API_CLASS +#endif + +#ifdef GHC_EXPAND_IMPL + +#ifdef GHC_OS_WINDOWS +#define NOMINMAX +#include +// additional includes +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__ANDROID__) +#define GHC_OS_ANDROID +#include +#endif +#endif +#ifdef GHC_OS_MACOS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else // GHC_EXPAND_IMPL +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_WINDOWS +#include +#endif +#endif // GHC_EXPAND_IMPL + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +// configure LWG conformance () +#define LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superceded by P1164R1, so only activate if really needed +// #define LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) +#define LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// UTF8-Everywhere is the original behaviour of ghc::filesystem. With this define you can +// enable the more standard conforming implementation option that uses wstring on Windows +// as ghc::filesystem::string_type. +// #define GHC_WIN_WSTRING_STRING_TYPE +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Rais errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, +// instead of replacing them with the unicode replacement character (U+FFFD). +// #define GHC_RAISE_UNICODE_ERRORS +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) +#define GHC_FILESYSTEM_VERSION 10200L + +namespace ghc { +namespace filesystem { + +// temporary existing exception type for yet unimplemented parts +class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error +{ +public: + not_implemented_exception() + : std::logic_error("function not implemented yet.") + { + } +}; + +// 30.10.8 class path +class GHC_FS_API_CLASS path +{ +public: +#ifdef GHC_OS_WINDOWS +#ifdef GHC_WIN_WSTRING_STRING_TYPE +#define GHC_USE_WCHAR_T + using value_type = std::wstring::value_type; +#else + using value_type = std::string::value_type; +#endif + using string_type = std::basic_string; + static constexpr value_type preferred_separator = '\\'; +#else + using value_type = std::string::value_type; + using string_type = std::basic_string; + static constexpr value_type preferred_separator = '/'; +#endif + // 30.10.10.1 enumeration format + /// The path format in wich the constructor argument is given. + enum format { + generic_format, ///< The generic format, internally used by + ///< ghc::filesystem::path with slashes + native_format, ///< The format native to the current platform this code + ///< is build for + auto_format, ///< Try to auto-detect the format, fallback to native + }; + + template + struct _is_basic_string : std::false_type + { + }; + template + struct _is_basic_string> : std::true_type + { + }; +#ifdef __cpp_lib_string_view + // template + // struct _is_basic_string> : std::true_type + // { + // }; +#endif + + template + using path_type = typename std::enable_if::value, path>::type; +#ifdef GHC_USE_WCHAR_T + template + using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value, path>::type; +#else + template + using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value, path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#endif + // 30.10.8.4.1 constructors and destructor + path() noexcept; + path(const path& p); + path(path&& p) noexcept; + path(string_type&& source, format fmt = auto_format); + template > + path(const Source& source, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, format fmt = auto_format); + template > + path(const Source& source, const std::locale& loc, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); + ~path(); + + // 30.10.8.4.2 assignments + path& operator=(const path& p); + path& operator=(path&& p) noexcept; + path& operator=(string_type&& source); + path& assign(string_type&& source); + template + path& operator=(const Source& source); + template + path& assign(const Source& source); + template + path& assign(InputIterator first, InputIterator last); + + // 30.10.8.4.3 appends + path& operator/=(const path& p); + template + path& operator/=(const Source& source); + template + path& append(const Source& source); + template + path& append(InputIterator first, InputIterator last); + + // 30.10.8.4.4 concatenation + path& operator+=(const path& x); + path& operator+=(const string_type& x); +#ifdef __cpp_lib_string_view + // path& operator+=(std::basic_string_view x); +#endif + path& operator+=(const value_type* x); + path& operator+=(value_type x); + template + path_from_string& operator+=(const Source& x); + template + path_type_EcharT& operator+=(EcharT x); + template + path& concat(const Source& x); + template + path& concat(InputIterator first, InputIterator last); + + // 30.10.8.4.5 modifiers + void clear() noexcept; + path& make_preferred(); + path& remove_filename(); + path& replace_filename(const path& replacement); + path& replace_extension(const path& replacement = path()); + void swap(path& rhs) noexcept; + + // 30.10.8.4.6 native format observers + const string_type& native() const; // this implementation doesn't support noexcept for native() + const value_type* c_str() const; // this implementation doesn't support noexcept for c_str() + operator string_type() const; + template , class Allocator = std::allocator> + std::basic_string string(const Allocator& a = Allocator()) const; + std::string string() const; + std::wstring wstring() const; + std::string u8string() const; + std::u16string u16string() const; + std::u32string u32string() const; + + // 30.10.8.4.7 generic format observers + template , class Allocator = std::allocator> + std::basic_string generic_string(const Allocator& a = Allocator()) const; + const std::string& generic_string() const; // this is different from the standard, that returns by value + std::wstring generic_wstring() const; + std::string generic_u8string() const; + std::u16string generic_u16string() const; + std::u32string generic_u32string() const; + + // 30.10.8.4.8 compare + int compare(const path& p) const noexcept; + int compare(const string_type& s) const; +#ifdef __cpp_lib_string_view + // int compare(std::basic_string_view s) const; +#endif + int compare(const value_type* s) const; + + // 30.10.8.4.9 decomposition + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; + path parent_path() const; + path filename() const; + path stem() const; + path extension() const; + + // 30.10.8.4.10 query + bool empty() const noexcept; + bool has_root_name() const; + bool has_root_directory() const; + bool has_root_path() const; + bool has_relative_path() const; + bool has_parent_path() const; + bool has_filename() const; + bool has_stem() const; + bool has_extension() const; + bool is_absolute() const; + bool is_relative() const; + + // 30.10.8.4.11 generation + path lexically_normal() const; + path lexically_relative(const path& base) const; + path lexically_proximate(const path& base) const; + + // 30.10.8.5 iterators + class iterator; + using const_iterator = iterator; + iterator begin() const; + iterator end() const; + +private: + using impl_value_type = std::string::value_type; + using impl_string_type = std::basic_string; + friend class directory_iterator; + void append_name(const char* name); + static constexpr impl_value_type generic_separator = '/'; + template + class input_iterator_range + { + public: + typedef InputIterator iterator; + typedef InputIterator const_iterator; + typedef typename InputIterator::difference_type difference_type; + + input_iterator_range(const InputIterator& first, const InputIterator& last) + : _first(first) + , _last(last) + { + } + + InputIterator begin() const { return _first; } + InputIterator end() const { return _last; } + + private: + InputIterator _first; + InputIterator _last; + }; + friend void swap(path& lhs, path& rhs) noexcept; + friend size_t hash_value(const path& p) noexcept; + static void postprocess_path_with_format(impl_string_type& p, format fmt); + impl_string_type _path; +#ifdef GHC_OS_WINDOWS + impl_string_type native_impl() const; + mutable string_type _native_cache; +#else + const impl_string_type& native_impl() const; +#endif +}; + +// 30.10.8.6 path non-member functions +GHC_FS_API void swap(path& lhs, path& rhs) noexcept; +GHC_FS_API size_t hash_value(const path& p) noexcept; +GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; + +GHC_FS_API path operator/(const path& lhs, const path& rhs); + +// 30.10.8.6.1 path inserter and extractor +template +std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); +template +std::basic_istream& operator>>(std::basic_istream& is, path& p); + +// 30.10.8.6.2 path factory functions +template > +path u8path(const Source& source); +template +path u8path(InputIterator first, InputIterator last); + +// 30.10.9 class filesystem_error +class GHC_FS_API_CLASS filesystem_error : public std::system_error +{ +public: + filesystem_error(const std::string& what_arg, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); + const path& path1() const noexcept; + const path& path2() const noexcept; + const char* what() const noexcept override; + +private: + std::string _what_arg; + std::error_code _ec; + path _p1, _p2; +}; + +class GHC_FS_API_CLASS path::iterator +{ +public: + using value_type = const path; + using difference_type = std::ptrdiff_t; + using pointer = const path*; + using reference = const path&; + using iterator_category = std::bidirectional_iterator_tag; + + iterator(); + iterator(const impl_string_type::const_iterator& first, const impl_string_type::const_iterator& last, const impl_string_type::const_iterator& pos); + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + reference operator*() const; + pointer operator->() const; + +private: + impl_string_type::const_iterator increment(const std::string::const_iterator& pos) const; + impl_string_type::const_iterator decrement(const std::string::const_iterator& pos) const; + void updateCurrent(); + impl_string_type::const_iterator _first; + impl_string_type::const_iterator _last; + impl_string_type::const_iterator _root; + impl_string_type::const_iterator _iter; + path _current; +}; + +struct space_info +{ + uintmax_t capacity; + uintmax_t free; + uintmax_t available; +}; + +// 30.10.10, enumerations +enum class file_type { + none, + not_found, + regular, + directory, + symlink, + block, + character, + fifo, + socket, + unknown, +}; + +enum class perms : uint16_t { + none = 0, + + owner_read = 0400, + owner_write = 0200, + owner_exec = 0100, + owner_all = 0700, + + group_read = 040, + group_write = 020, + group_exec = 010, + group_all = 070, + + others_read = 04, + others_write = 02, + others_exec = 01, + others_all = 07, + + all = 0777, + set_uid = 04000, + set_gid = 02000, + sticky_bit = 01000, + + mask = 07777, + unknown = 0xffff +}; + +enum class perm_options : uint16_t { + replace = 3, + add = 1, + remove = 2, + nofollow = 4, +}; + +enum class copy_options : uint16_t { + none = 0, + + skip_existing = 1, + overwrite_existing = 2, + update_existing = 4, + + recursive = 8, + + copy_symlinks = 0x10, + skip_symlinks = 0x20, + + directories_only = 0x40, + create_symlinks = 0x80, + create_hard_links = 0x100 +}; + +enum class directory_options : uint16_t { + none = 0, + follow_directory_symlink = 1, + skip_permission_denied = 2, +}; + +// 30.10.11 class file_status +class GHC_FS_API_CLASS file_status +{ +public: + // 30.10.11.1 constructors and destructor + file_status() noexcept; + explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; + file_status(const file_status&) noexcept; + file_status(file_status&&) noexcept; + ~file_status(); + // assignments: + file_status& operator=(const file_status&) noexcept; + file_status& operator=(file_status&&) noexcept; + // 30.10.11.3 modifiers + void type(file_type ft) noexcept; + void permissions(perms prms) noexcept; + // 30.10.11.2 observers + file_type type() const noexcept; + perms permissions() const noexcept; + +private: + file_type _type; + perms _perms; +}; + +using file_time_type = std::chrono::time_point; + +// 30.10.12 Class directory_entry +class GHC_FS_API_CLASS directory_entry +{ +public: + // 30.10.12.1 constructors and destructor + directory_entry() noexcept = default; + directory_entry(const directory_entry&) = default; + directory_entry(directory_entry&&) noexcept = default; + explicit directory_entry(const path& p); + directory_entry(const path& p, std::error_code& ec); + ~directory_entry(); + + // assignments: + directory_entry& operator=(const directory_entry&) = default; + directory_entry& operator=(directory_entry&&) noexcept = default; + + // 30.10.12.2 modifiers + void assign(const path& p); + void assign(const path& p, std::error_code& ec); + void replace_filename(const path& p); + void replace_filename(const path& p, std::error_code& ec); + void refresh(); + void refresh(std::error_code& ec) noexcept; + + // 30.10.12.3 observers + const filesystem::path& path() const noexcept; + operator const filesystem::path&() const noexcept; + bool exists() const; + bool exists(std::error_code& ec) const noexcept; + bool is_block_file() const; + bool is_block_file(std::error_code& ec) const noexcept; + bool is_character_file() const; + bool is_character_file(std::error_code& ec) const noexcept; + bool is_directory() const; + bool is_directory(std::error_code& ec) const noexcept; + bool is_fifo() const; + bool is_fifo(std::error_code& ec) const noexcept; + bool is_other() const; + bool is_other(std::error_code& ec) const noexcept; + bool is_regular_file() const; + bool is_regular_file(std::error_code& ec) const noexcept; + bool is_socket() const; + bool is_socket(std::error_code& ec) const noexcept; + bool is_symlink() const; + bool is_symlink(std::error_code& ec) const noexcept; + uintmax_t file_size() const; + uintmax_t file_size(std::error_code& ec) const noexcept; + uintmax_t hard_link_count() const; + uintmax_t hard_link_count(std::error_code& ec) const noexcept; + file_time_type last_write_time() const; + file_time_type last_write_time(std::error_code& ec) const noexcept; + + file_status status() const; + file_status status(std::error_code& ec) const noexcept; + + file_status symlink_status() const; + file_status symlink_status(std::error_code& ec) const noexcept; + bool operator<(const directory_entry& rhs) const noexcept; + bool operator==(const directory_entry& rhs) const noexcept; + bool operator!=(const directory_entry& rhs) const noexcept; + bool operator<=(const directory_entry& rhs) const noexcept; + bool operator>(const directory_entry& rhs) const noexcept; + bool operator>=(const directory_entry& rhs) const noexcept; + +private: + friend class directory_iterator; + filesystem::path _path; + file_status _status; + file_status _symlink_status; + uintmax_t _file_size = 0; +#ifndef GHC_OS_WINDOWS + uintmax_t _hard_link_count; +#endif + time_t _last_write_time = 0; +}; + +// 30.10.13 Class directory_iterator +class GHC_FS_API_CLASS directory_iterator +{ +public: + class GHC_FS_API_CLASS proxy + { + public: + const directory_entry& operator*() const& noexcept { return _dir_entry; } + directory_entry operator*() && noexcept { return std::move(_dir_entry); } + + private: + explicit proxy(const directory_entry& dir_entry) + : _dir_entry(dir_entry) + { + } + friend class directory_iterator; + friend class recursive_directory_iterator; + directory_entry _dir_entry; + }; + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // 30.10.13.1 member functions + directory_iterator() noexcept; + explicit directory_iterator(const path& p); + directory_iterator(const path& p, directory_options options); + directory_iterator(const path& p, std::error_code& ec) noexcept; + directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + directory_iterator(const directory_iterator& rhs); + directory_iterator(directory_iterator&& rhs) noexcept; + ~directory_iterator(); + directory_iterator& operator=(const directory_iterator& rhs); + directory_iterator& operator=(directory_iterator&& rhs) noexcept; + const directory_entry& operator*() const; + const directory_entry* operator->() const; + directory_iterator& operator++(); + directory_iterator& increment(std::error_code& ec) noexcept; + + // other members as required by 27.2.3, input iterators + proxy operator++(int) + { + proxy proxy{**this}; + ++*this; + return proxy; + } + bool operator==(const directory_iterator& rhs) const; + bool operator!=(const directory_iterator& rhs) const; + +private: + friend class recursive_directory_iterator; + class impl; + std::shared_ptr _impl; +}; + +// 30.10.13.2 directory_iterator non-member functions +GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; +GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; + +// 30.10.14 class recursive_directory_iterator +class GHC_FS_API_CLASS recursive_directory_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // 30.10.14.1 constructors and destructor + recursive_directory_iterator() noexcept; + explicit recursive_directory_iterator(const path& p); + recursive_directory_iterator(const path& p, directory_options options); + recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; + recursive_directory_iterator(const recursive_directory_iterator& rhs); + recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; + ~recursive_directory_iterator(); + + // 30.10.14.1 observers + directory_options options() const; + int depth() const; + bool recursion_pending() const; + + const directory_entry& operator*() const; + const directory_entry* operator->() const; + + // 30.10.14.1 modifiers recursive_directory_iterator& + recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); + recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; + recursive_directory_iterator& operator++(); + recursive_directory_iterator& increment(std::error_code& ec) noexcept; + + void pop(); + void pop(std::error_code& ec); + void disable_recursion_pending(); + + // other members as required by 27.2.3, input iterators + directory_iterator::proxy operator++(int) + { + directory_iterator::proxy proxy{**this}; + ++*this; + return proxy; + } + bool operator==(const recursive_directory_iterator& rhs) const; + bool operator!=(const recursive_directory_iterator& rhs) const; + +private: + struct recursive_directory_iterator_impl + { + directory_options _options; + bool _recursion_pending; + std::stack _dir_iter_stack; + recursive_directory_iterator_impl(directory_options options, bool recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) + { + } + }; + std::shared_ptr _impl; +}; + +// 30.10.14.2 directory_iterator non-member functions +GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; +GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; + +// 30.10.15 filesystem operations +GHC_FS_API path absolute(const path& p); +GHC_FS_API path absolute(const path& p, std::error_code& ec); + +GHC_FS_API path canonical(const path& p); +GHC_FS_API path canonical(const path& p, std::error_code& ec); + +GHC_FS_API void copy(const path& from, const path& to); +GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void copy(const path& from, const path& to, copy_options options); +GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; + +GHC_FS_API bool copy_file(const path& from, const path& to); +GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; + +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; + +GHC_FS_API bool create_directories(const path& p); +GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool create_directory(const path& p); +GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool create_directory(const path& p, const path& attributes); +GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; + +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; + +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; + +GHC_FS_API void create_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; + +GHC_FS_API path current_path(); +GHC_FS_API path current_path(std::error_code& ec); +GHC_FS_API void current_path(const path& p); +GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool exists(file_status s) noexcept; +GHC_FS_API bool exists(const path& p); +GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool equivalent(const path& p1, const path& p2); +GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; + +GHC_FS_API uintmax_t file_size(const path& p); +GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API uintmax_t hard_link_count(const path& p); +GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool is_block_file(file_status s) noexcept; +GHC_FS_API bool is_block_file(const path& p); +GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_character_file(file_status s) noexcept; +GHC_FS_API bool is_character_file(const path& p); +GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_directory(file_status s) noexcept; +GHC_FS_API bool is_directory(const path& p); +GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_empty(const path& p); +GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_fifo(file_status s) noexcept; +GHC_FS_API bool is_fifo(const path& p); +GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_other(file_status s) noexcept; +GHC_FS_API bool is_other(const path& p); +GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_regular_file(file_status s) noexcept; +GHC_FS_API bool is_regular_file(const path& p); +GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_socket(file_status s) noexcept; +GHC_FS_API bool is_socket(const path& p); +GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_symlink(file_status s) noexcept; +GHC_FS_API bool is_symlink(const path& p); +GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API file_time_type last_write_time(const path& p); +GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void last_write_time(const path& p, file_time_type new_time); +GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; + +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); +GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec); + +GHC_FS_API path proximate(const path& p, std::error_code& ec); +GHC_FS_API path proximate(const path& p, const path& base = current_path()); +GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); + +GHC_FS_API path read_symlink(const path& p); +GHC_FS_API path read_symlink(const path& p, std::error_code& ec); + +GHC_FS_API path relative(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, const path& base = current_path()); +GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); + +GHC_FS_API bool remove(const path& p); +GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API uintmax_t remove_all(const path& p); +GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API void rename(const path& from, const path& to); +GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; + +GHC_FS_API void resize_file(const path& p, uintmax_t size); +GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; + +GHC_FS_API space_info space(const path& p); +GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API file_status status(const path& p); +GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API bool status_known(file_status s) noexcept; + +GHC_FS_API file_status symlink_status(const path& p); +GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; + +GHC_FS_API path temp_directory_path(); +GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; + +GHC_FS_API path weakly_canonical(const path& p); +GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; + +// Non-C++17 add-on std::fstream wrappers with path +template > +class basic_filebuf : public std::basic_filebuf +{ +public: + basic_filebuf() {} + ~basic_filebuf() override {} + basic_filebuf(const basic_filebuf&) = delete; + const basic_filebuf& operator=(const basic_filebuf&) = delete; + basic_filebuf* open(const path& p, std::ios_base::openmode mode) + { +#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) + return std::basic_filebuf::open(p.wstring().c_str(), mode) ? this : 0; +#else + return std::basic_filebuf::open(p.string().c_str(), mode) ? this : 0; +#endif + } +}; + +template > +class basic_ifstream : public std::basic_ifstream +{ +public: + basic_ifstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.string().c_str(), mode); } +#endif + basic_ifstream(const basic_ifstream&) = delete; + const basic_ifstream& operator=(const basic_ifstream&) = delete; + ~basic_ifstream() override {} +}; + +template > +class basic_ofstream : public std::basic_ofstream +{ +public: + basic_ofstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.string().c_str(), mode); } +#endif + basic_ofstream(const basic_ofstream&) = delete; + const basic_ofstream& operator=(const basic_ofstream&) = delete; + ~basic_ofstream() override {} +}; + +template > +class basic_fstream : public std::basic_fstream +{ +public: + basic_fstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.string().c_str(), mode); } +#endif + basic_fstream(const basic_fstream&) = delete; + const basic_fstream& operator=(const basic_fstream&) = delete; + ~basic_fstream() override {} +}; + +typedef basic_filebuf filebuf; +typedef basic_filebuf wfilebuf; +typedef basic_ifstream ifstream; +typedef basic_ifstream wifstream; +typedef basic_ofstream ofstream; +typedef basic_ofstream wofstream; +typedef basic_fstream fstream; +typedef basic_fstream wfstream; + +class GHC_FS_API_CLASS u8arguments +{ +public: + u8arguments(int& argc, char**& argv); + ~u8arguments() + { + _refargc = _argc; + _refargv = _argv; + } + + bool valid() const { return _isvalid; } + +private: + int _argc; + char** _argv; + int& _refargc; + char**& _refargv; + bool _isvalid; +#ifdef GHC_OS_WINDOWS + std::vector _args; + std::vector _argp; +#endif +}; + +//------------------------------------------------------------------------------------------------- +// Implementation +//------------------------------------------------------------------------------------------------- + +namespace detail { +// GHC_FS_API void postprocess_path_with_format(path::impl_string_type& p, path::format fmt); +enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; +GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); +GHC_FS_API bool is_surrogate(uint32_t c); +GHC_FS_API bool is_high_surrogate(uint32_t c); +GHC_FS_API bool is_low_surrogate(uint32_t c); +GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); +enum class portable_error { + none = 0, + exists, + not_found, + not_supported, + not_implemented, + invalid_argument, + is_a_directory, +}; +GHC_FS_API std::error_code make_error_code(portable_error err); +} // namespace detail + +namespace detail { + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::error_code make_error_code(portable_error err) +{ +#ifdef GHC_OS_WINDOWS + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); + case portable_error::not_found: + return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); + case portable_error::not_supported: + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); + case portable_error::is_a_directory: +#ifdef ERROR_DIRECTORY_NOT_SUPPORTED + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#else + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); +#endif + } +#else + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(EEXIST, std::system_category()); + case portable_error::not_found: + return std::error_code(ENOENT, std::system_category()); + case portable_error::not_supported: + return std::error_code(ENOTSUP, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ENOSYS, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(EINVAL, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(EISDIR, std::system_category()); + } +#endif + return std::error_code(); +} + +#endif // GHC_EXPAND_IMPL + +template +using EnableBitmask = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, Enum>::type; +} // namespace detail + +template +detail::EnableBitmask operator&(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) & static_cast(Y)); +} + +template +detail::EnableBitmask operator|(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) | static_cast(Y)); +} + +template +detail::EnableBitmask operator^(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) ^ static_cast(Y)); +} + +template +detail::EnableBitmask operator~(Enum X) +{ + using underlying = typename std::underlying_type::type; + return static_cast(~static_cast(X)); +} + +template +detail::EnableBitmask& operator&=(Enum& X, Enum Y) +{ + X = X & Y; + return X; +} + +template +detail::EnableBitmask& operator|=(Enum& X, Enum Y) +{ + X = X | Y; + return X; +} + +template +detail::EnableBitmask& operator^=(Enum& X, Enum Y) +{ + X = X ^ Y; + return X; +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) +{ + return ((uint32_t)(c - lo) < (hi - lo + 1)); +} + +GHC_INLINE bool is_surrogate(uint32_t c) +{ + return in_range(c, 0xd800, 0xdfff); +} + +GHC_INLINE bool is_high_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xd800; +} + +GHC_INLINE bool is_low_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xdc00; +} + +GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) +{ + if (unicode <= 0x7f) { + str.push_back(static_cast(unicode)); + } + else if (unicode >= 0x80 && unicode <= 0x7ff) { + str.push_back(static_cast((unicode >> 6) + 192)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { + str.push_back(static_cast((unicode >> 12) + 224)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if (unicode >= 0x10000 && unicode <= 0x10ffff) { + str.push_back(static_cast((unicode >> 18) + 240)); + str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(str, 0xfffd); +#endif + } +} + +// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) +// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; +// Generating debugging and shrinking my own DFA from scratch was a day of fun! +GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) +{ + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; + uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3f) : (0xff >> category) & fragment); + return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +GHC_INLINE bool validUtf8(const std::string& utf8String) +{ + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_RJCT) { + return false; + } + } + if (utf8_state) { + return false; + } + return true; +} + +} // namespace detail + +#endif + +namespace detail { + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { + if (codepoint <= 0xffff) { + result += (typename StringType::value_type)codepoint; + } + else { + codepoint -= 0x10000; + result += (typename StringType::value_type)((codepoint >> 10) + 0xd800); + result += (typename StringType::value_type)((codepoint & 0x3ff) + 0xdc00); + } + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += (typename StringType::value_type)0xfffd; + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += (typename StringType::value_type)0xfffd; +#endif + } + return result; +} + +template ::type* = nullptr> +inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { + result += codepoint; + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += (typename StringType::value_type)0xfffd; + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += (typename StringType::value_type)0xfffd; +#endif + } + return result; +} + +template ::type size = 1> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + return std::string(unicodeString.begin(), unicodeString.end()); +} + +template ::type size = 2> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + std::string result; + for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { + char32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { + appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(result, 0xfffd); + if(iter == unicodeString.end()) { + break; + } +#endif + } + } + else { + appendUTF8(result, c); + } + } + return result; +} + +template ::type size = 4> +inline std::string toUtf8(const std::basic_string& unicodeString) +{ + std::string result; + for (auto c : unicodeString) { + appendUTF8(result, c); + } + return result; +} + +template +inline std::string toUtf8(const charT* unicodeString) +{ + return toUtf8(std::basic_string>(unicodeString)); +} + +} // namespace detail + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool startsWith(const std::string& what, const std::string& with) +{ + return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); +} + +} // namespace detail + +GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, path::format fmt) +{ +#ifdef GHC_RAISE_UNICODE_ERRORS + if(!detail::validUtf8(p)) { + path t; + t._path = p; + throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); + } +#endif + switch (fmt) { +#ifndef GHC_OS_WINDOWS + case path::auto_format: + case path::native_format: +#endif + case path::generic_format: + // nothing to do + break; +#ifdef GHC_OS_WINDOWS + case path::auto_format: + case path::native_format: + if (detail::startsWith(p, std::string("\\\\?\\"))) { + // remove Windows long filename marker + p.erase(0, 4); + if (detail::startsWith(p, std::string("UNC\\"))) { + p.erase(0, 2); + p[0] = '\\'; + } + } + for (auto& c : p) { + if (c == '\\') { + c = '/'; + } + } + break; +#endif + } + if (p.length() > 2 && p[0] == '/' && p[1] == '/' && p[2] != '/') { + std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); + p.erase(new_end, p.end()); + } + else { + std::string::iterator new_end = std::unique(p.begin(), p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); + p.erase(new_end, p.end()); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path(const Source& source, format fmt) + : _path(detail::toUtf8(source)) +{ + postprocess_path_with_format(_path, fmt); +} +template <> +inline path::path(const std::wstring& source, format fmt) +{ + _path = detail::toUtf8(source); + postprocess_path_with_format(_path, fmt); +} +template <> +inline path::path(const std::u16string& source, format fmt) +{ + _path = detail::toUtf8(source); + postprocess_path_with_format(_path, fmt); +} +template <> +inline path::path(const std::u32string& source, format fmt) +{ + _path = detail::toUtf8(source); + postprocess_path_with_format(_path, fmt); +} + +template +inline path u8path(const Source& source) +{ + return path(source); +} +template +inline path u8path(InputIterator first, InputIterator last) +{ + return path(first, last); +} + +template +inline path::path(InputIterator first, InputIterator last, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + // delegated +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) +{ +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { + if (*str1++ == 0) + return true; + } + return false; +#else + return 0 == ::_stricmp(str1, str2); +#endif +#else + return 0 == ::strcasecmp(str1, str2); +#endif +} + +template +GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) +{ +#if defined(GHC_OS_WINDOWS) + LPVOID msgBuf; + DWORD dw = code ? code : ::GetLastError(); + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); + std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); + LocalFree(msgBuf); + return msg; +#elif defined(GHC_OS_MACOS) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !defined(_GNU_SOURCE)) || (defined(GHC_OS_ANDROID) && __ANDROID_API__ < 23) + char buffer[512]; + int rc = strerror_r(code ? code : errno, buffer, sizeof(buffer)); + return rc == 0 ? (const char*)buffer : "Error in strerror_r!"; +#else + char buffer[512]; + char* msg = strerror_r(code ? code : errno, buffer, sizeof(buffer)); + return msg ? msg : buffer; +#endif +} + +#ifdef GHC_OS_WINDOWS +using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); +using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) +{ + std::error_code tec; + auto fs = status(target_name, tec); + if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { + ec = detail::make_error_code(detail::portable_error::not_supported); + return; + } + static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); + if (api_call) { + if (api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) { + auto result = ::GetLastError(); + if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), to_directory ? 3 : 2) != 0) { + return; + } + ec = std::error_code(result, std::system_category()); + } + } + else { + ec = std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + } +} + +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ + static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); + if (api_call) { + if (api_call(detail::fromUtf8(new_hardlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), NULL) == 0) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + } + else { + ec = std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + } +} +#else +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) +{ + if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { + ec = std::error_code(errno, std::system_category()); + } +} + +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ + if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { + ec = std::error_code(errno, std::system_category()); + } +} +#endif + +template +GHC_INLINE file_status file_status_from_st_mode(T mode) +{ +#ifdef GHC_OS_WINDOWS + file_type ft = file_type::unknown; + if ((mode & _S_IFDIR) == _S_IFDIR) { + ft = file_type::directory; + } + else if ((mode & _S_IFREG) == _S_IFREG) { + ft = file_type::regular; + } + else if ((mode & _S_IFCHR) == _S_IFCHR) { + ft = file_type::character; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#else + file_type ft = file_type::unknown; + if (S_ISDIR(mode)) { + ft = file_type::directory; + } + else if (S_ISREG(mode)) { + ft = file_type::regular; + } + else if (S_ISCHR(mode)) { + ft = file_type::character; + } + else if (S_ISBLK(mode)) { + ft = file_type::block; + } + else if (S_ISFIFO(mode)) { + ft = file_type::fifo; + } + else if (S_ISLNK(mode)) { + ft = file_type::symlink; + } + else if (S_ISSOCK(mode)) { + ft = file_type::socket; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#endif +} + +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS +#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE + typedef struct _REPARSE_DATA_BUFFER + { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; + } REPARSE_DATA_BUFFER; +#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) +#endif +#endif + + std::shared_ptr file(CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + if (file.get() == INVALID_HANDLE_VALUE) { + ec = std::error_code(::GetLastError(), std::system_category()); + return path(); + } + + std::shared_ptr reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free); + ULONG bufferUsed; + path result; + if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { + if (IsReparseTagMicrosoft(reparseData->ReparseTag)) { + switch (reparseData->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: + result = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); + break; + case IO_REPARSE_TAG_MOUNT_POINT: + result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)); + break; + default: + break; + } + } + } + else { + ec = std::error_code(::GetLastError(), std::system_category()); + } + return result; +#else + size_t bufferSize = 256; + while (true) { + std::vector buffer(bufferSize, (char)0); + auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); + if (rc < 0) { + ec = std::error_code(errno, std::system_category()); + return path(); + } + else if (rc < static_cast(bufferSize)) { + return path(std::string(buffer.data(), rc)); + } + bufferSize *= 2; + } + return path(); +#endif +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) +{ + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + return ull.QuadPart / 10000000ULL - 11644473600ULL; +} + +GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) +{ + LONGLONG ll; + ll = Int32x32To64(t, 10000000) + 116444736000000000; + ft.dwLowDateTime = (DWORD)ll; + ft.dwHighDateTime = ll >> 32; +} + +template +GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) +{ + return static_cast(-1); +} + +template <> +GHC_INLINE uintmax_t hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION* info) +{ + return info->nNumberOfLinks; +} + +template +GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) noexcept +{ + file_type ft = file_type::unknown; + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + ft = file_type::symlink; + } + else { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ft = file_type::directory; + } + else { + ft = file_type::regular; + } + } + perms prms = perms::owner_read | perms::group_read | perms::others_read; + if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { + prms = prms | perms::owner_write | perms::group_write | perms::others_write; + } + std::string ext = p.extension().generic_string(); + if (equals_simple_insensitive(ext.c_str(), ".exe") || equals_simple_insensitive(ext.c_str(), ".cmd") || equals_simple_insensitive(ext.c_str(), ".bat") || equals_simple_insensitive(ext.c_str(), ".com")) { + prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; + } + if (sz) { + *sz = static_cast(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; + } + if (lwt) { + *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); + } + return file_status(ft, prms); +} + +#endif + +GHC_INLINE bool is_not_found_error(std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; +#else + return ec.value() == ENOENT || ec.value() == ENOTDIR; +#endif +} + +GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept +{ +#ifdef GHC_OS_WINDOWS + file_status fs; + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(detail::fromUtf8(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + else { + ec.clear(); + fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); + if (nhl) { + *nhl = 0; + } + if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + fs.type(file_type::symlink); + } + } + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return ec ? file_status(file_type::none) : fs; +#else + (void)sz; + (void)nhl; + (void)lwt; + struct ::stat fs; + auto result = ::lstat(p.c_str(), &fs); + if (result == 0) { + ec.clear(); + file_status f_s = detail::file_status_from_st_mode(fs.st_mode); + return f_s; + } + ec = std::error_code(errno, std::system_category()); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); +#endif +} + +GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (recurse_count > 16) { + ec = std::error_code(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/, std::system_category()); + return file_status(file_type::unknown); + } + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!::GetFileAttributesExW(p.wstring().c_str(), GetFileExInfoStandard, &attr)) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + path target = resolveSymlink(p, ec); + file_status result; + if (!ec && !target.empty()) { + if (sls) { + *sls = status_from_INFO(p, &attr, ec); + } + return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); + } + return file_status(file_type::unknown); + } + if (ec) { + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return file_status(file_type::none); + } + if (nhl) { + *nhl = 0; + } + return detail::status_from_INFO(p, &attr, ec, sz, lwt); +#else + (void)recurse_count; + struct ::stat st; + auto result = ::lstat(p.c_str(), &st); + if (result == 0) { + ec.clear(); + file_status fs = detail::file_status_from_st_mode(st.st_mode); + if (fs.type() == file_type::symlink) { + result = ::stat(p.c_str(), &st); + if (result == 0) { + if (sls) { + *sls = fs; + } + fs = detail::file_status_from_st_mode(st.st_mode); + } + } + if (sz) { + *sz = st.st_size; + } + if (nhl) { + *nhl = st.st_nlink; + } + if (lwt) { + *lwt = st.st_mtime; + } + return fs; + } + else { + ec = std::error_code(errno, std::system_category()); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } +#endif +} + +} // namespace detail + +GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) + : _argc(argc) + , _argv(argv) + , _refargc(argc) + , _refargv(argv) + , _isvalid(false) +{ +#ifdef GHC_OS_WINDOWS + LPWSTR* p; + p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + _args.reserve(argc); + _argp.reserve(argc); + for (size_t i = 0; i < static_cast(argc); ++i) { + _args.push_back(detail::toUtf8(std::wstring(p[i]))); + _argp.push_back((char*)_args[i].data()); + } + argv = _argp.data(); + ::LocalFree(p); + _isvalid = true; +#else + std::setlocale(LC_ALL, ""); +#if defined(__ANDROID__) && __ANDROID_API__ < 26 + _isvalid = true; +#else + if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { + _isvalid = true; + } +#endif +#endif +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.1 constructors and destructor + +GHC_INLINE path::path() noexcept {} + +GHC_INLINE path::path(const path& p) + : _path(p._path) +{ +} + +GHC_INLINE path::path(path&& p) noexcept + : _path(std::move(p._path)) +{ +} + +GHC_INLINE path::path(string_type&& source, format fmt) +#ifdef GHC_USE_WCHAR_T + : _path(detail::toUtf8(source)) +#else + : _path(std::move(source)) +#endif +{ + postprocess_path_with_format(_path, fmt); +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path(const Source& source, const std::locale& loc, format fmt) + : path(source, fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} + +template +inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE path::~path() {} + +//----------------------------------------------------------------------------- +// 30.10.8.4.2 assignments + +GHC_INLINE path& path::operator=(const path& p) +{ + _path = p._path; + return *this; +} + +GHC_INLINE path& path::operator=(path&& p) noexcept +{ + _path = std::move(p._path); + return *this; +} + +GHC_INLINE path& path::operator=(path::string_type&& source) +{ + return assign(source); +} + +GHC_INLINE path& path::assign(path::string_type&& source) +{ +#ifdef GHC_USE_WCHAR_T + _path = detail::toUtf8(source); +#else + _path = std::move(source); +#endif + postprocess_path_with_format(_path, native_format); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator=(const Source& source) +{ + return assign(source); +} + +template +inline path& path::assign(const Source& source) +{ + _path.assign(detail::toUtf8(source)); + postprocess_path_with_format(_path, native_format); + return *this; +} + +template <> +inline path& path::assign(const path& source) +{ + _path = source._path; + return *this; +} + +template +inline path& path::assign(InputIterator first, InputIterator last) +{ + _path.assign(first, last); + postprocess_path_with_format(_path, native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.8.4.3 appends + +GHC_INLINE path& path::operator/=(const path& p) +{ + if (p.empty()) { + // was: if ((!has_root_directory() && is_absolute()) || has_filename()) + if (!_path.empty() && _path[_path.length() - 1] != '/' && _path[_path.length() - 1] != ':') { + _path += '/'; + } + return *this; + } + if ((p.is_absolute() && (_path != root_name() || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { + assign(p); + return *this; + } + if (p.has_root_directory()) { + assign(root_name()); + } + else if ((!has_root_directory() && is_absolute()) || has_filename()) { + _path += '/'; + } + auto iter = p.begin(); + bool first = true; + if (p.has_root_name()) { + ++iter; + } + while (iter != p.end()) { + if (!first && !(!_path.empty() && _path[_path.length() - 1] == '/')) { + _path += '/'; + } + first = false; + _path += (*iter++).generic_string(); + } + return *this; +} + +GHC_INLINE void path::append_name(const char* name) +{ + if (_path.empty()) { + this->operator/=(path(name)); + } + else { + if (_path.back() != path::generic_separator) { + _path.push_back(path::generic_separator); + } + _path += name; + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator/=(const Source& source) +{ + return append(source); +} + +template +inline path& path::append(const Source& source) +{ + return this->operator/=(path(detail::toUtf8(source))); +} + +template <> +inline path& path::append(const path& p) +{ + return this->operator/=(p); +} + +template +inline path& path::append(InputIterator first, InputIterator last) +{ + std::basic_string::value_type> part(first, last); + return append(part); +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.8.4.4 concatenation + +GHC_INLINE path& path::operator+=(const path& x) +{ + return concat(x._path); +} + +GHC_INLINE path& path::operator+=(const string_type& x) +{ + return concat(x); +} + +#ifdef __cpp_lib_string_view +// GHC_INLINE path& path::operator+=(std::basic_string_view x) +// { +// return concat(x); +// } +#endif + +GHC_INLINE path& path::operator+=(const value_type* x) +{ + return concat(string_type(x)); +} + +GHC_INLINE path& path::operator+=(value_type x) +{ +#ifdef GHC_OS_WINDOWS + if (x == '\\') { + x = generic_separator; + } +#endif + if (_path.empty() || _path.back() != generic_separator) { +#ifdef GHC_USE_WCHAR_T + _path += detail::toUtf8(string_type(1, x)); +#else + _path += x; +#endif + } + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path_from_string& path::operator+=(const Source& x) +{ + return concat(x); +} + +template +inline path::path_type_EcharT& path::operator+=(EcharT x) +{ + std::basic_string part(1, x); + concat(detail::toUtf8(part)); + return *this; +} + +template +inline path& path::concat(const Source& x) +{ + path p(x); + postprocess_path_with_format(p._path, native_format); + _path += p._path; + return *this; +} +template +inline path& path::concat(InputIterator first, InputIterator last) +{ + _path.append(first, last); + postprocess_path_with_format(_path, native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.8.4.5 modifiers +GHC_INLINE void path::clear() noexcept +{ + _path.clear(); +} + +GHC_INLINE path& path::make_preferred() +{ + // as this filesystem implementation only uses generic_format + // internally, this must be a no-op + return *this; +} + +GHC_INLINE path& path::remove_filename() +{ + if (has_filename()) { + _path.erase(_path.size() - filename()._path.size()); + } + return *this; +} + +GHC_INLINE path& path::replace_filename(const path& replacement) +{ + remove_filename(); + return append(replacement); +} + +GHC_INLINE path& path::replace_extension(const path& replacement) +{ + if (has_extension()) { + _path.erase(_path.size() - extension()._path.size()); + } + if (!replacement.empty() && replacement._path[0] != '.') { + _path += '.'; + } + return concat(replacement); +} + +GHC_INLINE void path::swap(path& rhs) noexcept +{ + _path.swap(rhs._path); +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.6, native format observers +#ifdef GHC_OS_WINDOWS +GHC_INLINE path::impl_string_type path::native_impl() const +{ + impl_string_type result; + if (is_absolute() && _path.length() > MAX_PATH - 10) { + // expand long Windows filenames with marker + if (has_root_name() && _path[0] == '/') { + result = "\\\\?\\UNC" + _path.substr(1); + } + else { + result = "\\\\?\\" + _path; + } + } + else { + result = _path; + } + /*if (has_root_name() && root_name()._path[0] == '/') { + return _path; + }*/ + for (auto& c : result) { + if (c == '/') { + c = '\\'; + } + } + return result; +} +#else +GHC_INLINE const path::impl_string_type& path::native_impl() const +{ + return _path; +} +#endif + +GHC_INLINE const path::string_type& path::native() const +{ +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + _native_cache = detail::fromUtf8(native_impl()); +#else + _native_cache = native_impl(); +#endif + return _native_cache; +#else + return _path; +#endif +} + +GHC_INLINE const path::value_type* path::c_str() const +{ + return native().c_str(); +} + +GHC_INLINE path::operator path::string_type() const +{ + return native(); +} + +#endif // GHC_EXPAND_IMPL + +template +inline std::basic_string path::string(const Allocator& a) const +{ + return detail::fromUtf8>(native_impl(), a); +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::string() const +{ + return native_impl(); +} + +GHC_INLINE std::wstring path::wstring() const +{ +#ifdef GHC_USE_WCHAR_T + return native(); +#else + return detail::fromUtf8(native()); +#endif +} + +GHC_INLINE std::string path::u8string() const +{ + return native_impl(); +} + +GHC_INLINE std::u16string path::u16string() const +{ + return detail::fromUtf8(native_impl()); +} + +GHC_INLINE std::u32string path::u32string() const +{ + return detail::fromUtf8(native_impl()); +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.8.4.7, generic format observers +template +inline std::basic_string path::generic_string(const Allocator& a) const +{ + return detail::fromUtf8>(_path, a); +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE const std::string& path::generic_string() const +{ + return _path; +} + +GHC_INLINE std::wstring path::generic_wstring() const +{ + return detail::fromUtf8(_path); +} + +GHC_INLINE std::string path::generic_u8string() const +{ + return _path; +} + +GHC_INLINE std::u16string path::generic_u16string() const +{ + return detail::fromUtf8(_path); +} + +GHC_INLINE std::u32string path::generic_u32string() const +{ + return detail::fromUtf8(_path); +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.8, compare +GHC_INLINE int path::compare(const path& p) const noexcept +{ + return native().compare(p.native()); +} + +GHC_INLINE int path::compare(const string_type& s) const +{ + return native().compare(path(s).native()); +} + +#ifdef __cpp_lib_string_view +// GHC_INLINE int path::compare(std::basic_string_view s) const +// { +// return native().compare(path(s).native()); +// } +#endif + +GHC_INLINE int path::compare(const value_type* s) const +{ + return native().compare(path(s).native()); +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.9, decomposition +GHC_INLINE path path::root_name() const +{ +#ifdef GHC_OS_WINDOWS + if (_path.length() >= 2 && std::toupper(static_cast(_path[0])) >= 'A' && std::toupper(static_cast(_path[0])) <= 'Z' && _path[1] == ':') { + return path(_path.substr(0, 2)); + } +#endif + if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) { + impl_string_type::size_type pos = _path.find_first_of("/\\", 3); + if (pos == impl_string_type::npos) { + return path(_path); + } + else { + return path(_path.substr(0, pos)); + } + } + return path(); +} + +GHC_INLINE path path::root_directory() const +{ + path root = root_name(); + if (_path.length() > root._path.length() && _path[root._path.length()] == '/') { + return path("/"); + } + return path(); +} + +GHC_INLINE path path::root_path() const +{ + return root_name().generic_string() + root_directory().generic_string(); +} + +GHC_INLINE path path::relative_path() const +{ + std::string root = root_path()._path; + return path(_path.substr((std::min)(root.length(), _path.length())), generic_format); +} + +GHC_INLINE path path::parent_path() const +{ + if (has_relative_path()) { + if (empty() || begin() == --end()) { + return path(); + } + else { + path pp; + for (const string_type& s : input_iterator_range(begin(), --end())) { + if (s == "/") { + // don't use append to join a path- + pp += s; + } + else { + pp /= s; + } + } + return pp; + } + } + else { + return *this; + } +} + +GHC_INLINE path path::filename() const +{ + return relative_path().empty() ? path() : path(*--end()); +} + +GHC_INLINE path path::stem() const +{ + impl_string_type fn = filename().string(); + if (fn != "." && fn != "..") { + impl_string_type::size_type n = fn.rfind("."); + if (n != impl_string_type::npos && n != 0) { + return fn.substr(0, n); + } + } + return fn; +} + +GHC_INLINE path path::extension() const +{ + impl_string_type fn = filename().string(); + impl_string_type::size_type pos = fn.find_last_of('.'); + if (pos == std::string::npos || pos == 0) { + return ""; + } + return fn.substr(pos); +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.10, query +GHC_INLINE bool path::empty() const noexcept +{ + return _path.empty(); +} + +GHC_INLINE bool path::has_root_name() const +{ + return !root_name().empty(); +} + +GHC_INLINE bool path::has_root_directory() const +{ + return !root_directory().empty(); +} + +GHC_INLINE bool path::has_root_path() const +{ + return !root_path().empty(); +} + +GHC_INLINE bool path::has_relative_path() const +{ + return !relative_path().empty(); +} + +GHC_INLINE bool path::has_parent_path() const +{ + return !parent_path().empty(); +} + +GHC_INLINE bool path::has_filename() const +{ + return !filename().empty(); +} + +GHC_INLINE bool path::has_stem() const +{ + return !stem().empty(); +} + +GHC_INLINE bool path::has_extension() const +{ + return !extension().empty(); +} + +GHC_INLINE bool path::is_absolute() const +{ +#ifdef GHC_OS_WINDOWS + return has_root_name() && has_root_directory(); +#else + return has_root_directory(); +#endif +} + +GHC_INLINE bool path::is_relative() const +{ + return !is_absolute(); +} + +//----------------------------------------------------------------------------- +// 30.10.8.4.11, generation +GHC_INLINE path path::lexically_normal() const +{ + path dest; + for (const string_type& s : *this) { + if (s == ".") { + dest /= ""; + continue; + } + else if (s == ".." && !dest.empty()) { + auto root = root_path(); + if (dest == root) { + continue; + } + else if (*(--dest.end()) != "..") { + if (dest._path.back() == generic_separator) { + dest._path.pop_back(); + } + dest.remove_filename(); + continue; + } + } + dest /= s; + } + if (dest.empty()) { + dest = "."; + } + return dest; +} + +GHC_INLINE path path::lexically_relative(const path& base) const +{ + if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { + return path(); + } + const_iterator a = begin(), b = base.begin(); + while (a != end() && b != base.end() && *a == *b) { + ++a; + ++b; + } + if (a == end() && b == base.end()) { + return path("."); + } + int count = 0; + for (const auto& element : input_iterator_range(b, base.end())) { + if (element != "." && element != "..") { + ++count; + } + else if (element == "..") { + --count; + } + } + if (count < 0) { + return path(); + } + path result; + for (int i = 0; i < count; ++i) { + result /= ".."; + } + for (const auto& element : input_iterator_range(a, end())) { + result /= element; + } + return result; +} + +GHC_INLINE path path::lexically_proximate(const path& base) const +{ + path result = lexically_relative(base); + return result.empty() ? *this : result; +} + +//----------------------------------------------------------------------------- +// 30.10.8.5, iterators +GHC_INLINE path::iterator::iterator() {} + +GHC_INLINE path::iterator::iterator(const path::impl_string_type::const_iterator& first, const path::impl_string_type::const_iterator& last, const path::impl_string_type::const_iterator& pos) + : _first(first) + , _last(last) + , _iter(pos) +{ + updateCurrent(); + // find the position of a potential root directory slash +#ifdef GHC_OS_WINDOWS + if (_last - _first >= 3 && std::toupper(static_cast(*first)) >= 'A' && std::toupper(static_cast(*first)) <= 'Z' && *(first + 1) == ':' && *(first + 2) == '/') { + _root = _first + 2; + } + else +#endif + { + if (_first != _last && *_first == '/') { + if (_last - _first >= 2 && *(_first + 1) == '/' && !(_last - _first >= 3 && *(_first + 2) == '/')) { + _root = increment(_first); + } + else { + _root = _first; + } + } + else { + _root = _last; + } + } +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + bool fromStart = i == _first; + if (i != _last) { + // we can only sit on a slash if it is a network name or a root + if (*i++ == '/') { + if (i != _last && *i == '/') { + if (fromStart && !(i + 1 != _last && *(i + 1) == '/')) { + // leadind double slashes detected, treat this and the + // following until a slash as one unit + i = std::find(++i, _last, '/'); + } + else { + // skip redundant slashes + while (i != _last && *i == '/') { + ++i; + } + } + } + } + else { + if (fromStart && i != _last && *i == ':') { + ++i; + } + else { + i = std::find(i, _last, '/'); + } + } + } + return i; +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + if (i != _first) { + --i; + // if this is now the root slash or the trailing slash, we are done, + // else check for network name + if (i != _root && (pos != _last || *i != '/')) { +#ifdef GHC_OS_WINDOWS + static const std::string seps = "/:"; + i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); + if (i > _first && *i == ':') { + i++; + } +#else + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), '/').base(); +#endif + // Now we have to check if this is a network name + if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') { + i -= 2; + } + } + } + return i; +} + +GHC_INLINE void path::iterator::updateCurrent() +{ + if (_iter != _first && _iter != _last && (*_iter == '/' && _iter != _root) && (_iter + 1 == _last)) { + _current = ""; + } + else { + _current.assign(_iter, increment(_iter)); + if (_current.generic_string().size() > 1 && _current.generic_string()[0] == '/' && _current.generic_string()[_current.generic_string().size() - 1] == '/') { + // shrink successive slashes to one + _current = "/"; + } + } +} + +GHC_INLINE path::iterator& path::iterator::operator++() +{ + _iter = increment(_iter); + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position + *_iter == '/' && // we are on a slash + (_iter + 1) != _last // the slash is not the last char + ) { + ++_iter; + } + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator++(int) +{ + path::iterator i{*this}; + ++(*this); + return i; +} + +GHC_INLINE path::iterator& path::iterator::operator--() +{ + _iter = decrement(_iter); + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator--(int) +{ + auto i = *this; + --(*this); + return i; +} + +GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const +{ + return _iter == other._iter; +} + +GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const +{ + return _iter != other._iter; +} + +GHC_INLINE path::iterator::reference path::iterator::operator*() const +{ + return _current; +} + +GHC_INLINE path::iterator::pointer path::iterator::operator->() const +{ + return &_current; +} + +GHC_INLINE path::iterator path::begin() const +{ + return iterator(_path.begin(), _path.end(), _path.begin()); +} + +GHC_INLINE path::iterator path::end() const +{ + return iterator(_path.begin(), _path.end(), _path.end()); +} + +//----------------------------------------------------------------------------- +// 30.10.8.6, path non-member functions +GHC_INLINE void swap(path& lhs, path& rhs) noexcept +{ + swap(lhs._path, rhs._path); +} + +GHC_INLINE size_t hash_value(const path& p) noexcept +{ + return std::hash()(p.generic_string()); +} + +GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() == rhs.generic_string(); +} + +GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() != rhs.generic_string(); +} + +GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() < rhs.generic_string(); +} + +GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() <= rhs.generic_string(); +} + +GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() > rhs.generic_string(); +} + +GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept +{ + return lhs.generic_string() >= rhs.generic_string(); +} + +GHC_INLINE path operator/(const path& lhs, const path& rhs) +{ + path result(lhs); + result /= rhs; + return result; +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.8.6.1 path inserter and extractor +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) +{ + os << "\""; + auto ps = p.string(); + for (auto c : ps) { + if (c == '"' || c == '\\') { + os << '\\'; + } + os << c; + } + os << "\""; + return os; +} + +template +inline std::basic_istream& operator>>(std::basic_istream& is, path& p) +{ + std::basic_string tmp; + auto c = is.get(); + if (c == '"') { + auto sf = is.flags(); + is >> std::noskipws; + while (is) { + c = is.get(); + if (is) { + if (c == '\\') { + c = is.get(); + if (is) { + tmp += static_cast(c); + } + } + else if (c == '"') { + break; + } + else { + tmp += static_cast(c); + } + } + } + if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { + is >> std::skipws; + } + p = path(tmp); + } + else { + is >> tmp; + p = path(static_cast(c) + tmp); + } + return is; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// 30.10.9 Class filesystem_error +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) +{ +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.u8string() + "'"; + } +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) + , _p2(p2) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.u8string() + "'"; + } + if (!_p2.empty()) { + _what_arg += ", '" + _p2.u8string() + "'"; + } +} + +GHC_INLINE const path& filesystem_error::path1() const noexcept +{ + return _p1; +} + +GHC_INLINE const path& filesystem_error::path2() const noexcept +{ + return _p2; +} + +GHC_INLINE const char* filesystem_error::what() const noexcept +{ + return _what_arg.c_str(); +} + +//----------------------------------------------------------------------------- +// 30.10.15, filesystem operations +GHC_INLINE path absolute(const path& p) +{ + std::error_code ec; + path result = absolute(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE path absolute(const path& p, std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (p.empty()) { + return absolute(current_path(ec), ec) / ""; + } + ULONG size = ::GetFullPathNameW(p.wstring().c_str(), 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(p.wstring().c_str(), size, buf.data(), nullptr); + if (s2 && s2 < size) { + path result = path(std::wstring(buf.data(), s2)); + if (p.filename() == ".") { + result /= "."; + } + return result; + } + } + ec = std::error_code(::GetLastError(), std::system_category()); + return path(); +#else + path base = current_path(ec); + if (!ec) { + if (p.empty()) { + return base / p; + } + if (p.has_root_name()) { + if (p.has_root_directory()) { + return p; + } + else { + return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); + } + } + else { + if (p.has_root_directory()) { + return base.root_name() / p; + } + else { + return base / p; + } + } + } + ec = std::error_code(errno, std::system_category()); + return path(); +#endif +} + +GHC_INLINE path canonical(const path& p) +{ + std::error_code ec; + auto result = canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE path canonical(const path& p, std::error_code& ec) +{ + if (p.empty()) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + path work = p.is_absolute() ? p : absolute(p, ec); + path root = work.root_path(); + path result; + + auto fs = status(work, ec); + if (ec) { + return path(); + } + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + bool redo; + do { + redo = false; + result.clear(); + for (auto pe : work) { + if (pe.empty() || pe == ".") { + continue; + } + else if (pe == "..") { + result = result.parent_path(); + continue; + } + else if ((result / pe).string().length() <= root.string().length()) { + result /= pe; + continue; + } + auto sls = symlink_status(result / pe, ec); + if (ec) { + return path(); + } + if (is_symlink(sls)) { + redo = true; + auto target = read_symlink(result / pe, ec); + if (ec) { + return path(); + } + if (target.is_absolute()) { + result = target; + continue; + } + else { + result /= target; + continue; + } + } + else { + result /= pe; + } + } + work = result; + } while (redo); + ec.clear(); + return result; +} + +GHC_INLINE void copy(const path& from, const path& to) +{ + copy(from, to, copy_options::none); +} + +GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept +{ + copy(from, to, copy_options::none, ec); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options) +{ + std::error_code ec; + copy(from, to, options, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tec; + file_status fs_from, fs_to; + ec.clear(); + if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_from = symlink_status(from, ec); + } + else { + fs_from = status(from, ec); + } + if (!exists(fs_from)) { + if (!ec) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return; + } + if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_to = symlink_status(to, tec); + } + else { + fs_to = status(to, tec); + } + if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + else if (is_symlink(fs_from)) { + if ((options & copy_options::skip_symlinks) == copy_options::none) { + if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { + copy_symlink(from, to, ec); + } + else { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + } + } + else if (is_regular_file(fs_from)) { + if ((options & copy_options::directories_only) == copy_options::none) { + if ((options & copy_options::create_symlinks) != copy_options::none) { + create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); + } + else if ((options & copy_options::create_hard_links) != copy_options::none) { + create_hard_link(from, to, ec); + } + else if (is_directory(fs_to)) { + copy_file(from, to / from.filename(), options, ec); + } + else { + copy_file(from, to, ec); + } + } + } +#ifdef LWG_2682_BEHAVIOUR + else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + ec = detail::make_error_code(detail::portable_error::is_a_directory); + } +#endif + else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { + if (!exists(fs_to)) { + create_directory(to, from, ec); + if (ec) { + return; + } + } + for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { + if (!ec) { + copy(iter->path(), to / iter->path().filename(), options | static_cast(0x8000), ec); + } + if (ec) { + return; + } + } + } + return; +} + +GHC_INLINE bool copy_file(const path& from, const path& to) +{ + return copy_file(from, to, copy_options::none); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept +{ + return copy_file(from, to, copy_options::none, ec); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) +{ + std::error_code ec; + auto result = copy_file(from, to, option, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } + return result; +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tecf, tect; + auto sf = status(from, tecf); + auto st = status(to, tect); + bool overwrite = false; + ec.clear(); + if (!is_regular_file(sf)) { + ec = tecf; + return false; + } + if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } + if (exists(st)) { + if ((options & copy_options::update_existing) == copy_options::update_existing) { + auto from_time = last_write_time(from, ec); + if (ec) { + ec = std::error_code(errno, std::system_category()); + return false; + } + auto to_time = last_write_time(to, ec); + if (ec) { + ec = std::error_code(errno, std::system_category()); + return false; + } + if (from_time <= to_time) { + return false; + } + } + overwrite = true; + } +#ifdef GHC_OS_WINDOWS + if (!::CopyFileW(detail::fromUtf8(from.u8string()).c_str(), detail::fromUtf8(to.u8string()).c_str(), !overwrite)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return false; + } + return true; +#else + std::vector buffer(16384, '\0'); + int in = -1, out = -1; + if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { + ec = std::error_code(errno, std::system_category()); + return false; + } + std::shared_ptr guard_in(nullptr, [in](void*) { ::close(in); }); + int mode = O_CREAT | O_WRONLY | O_TRUNC; + if (!overwrite) { + mode |= O_EXCL; + } + if ((out = ::open(to.c_str(), mode, static_cast(sf.permissions() & perms::all))) < 0) { + ec = std::error_code(errno, std::system_category()); + return false; + } + std::shared_ptr guard_out(nullptr, [out](void*) { ::close(out); }); + ssize_t br, bw; + while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + int offset = 0; + do { + if ((bw = ::write(out, buffer.data() + offset, br)) > 0) { + br -= bw; + offset += bw; + } + else if (bw < 0) { + ec = std::error_code(errno, std::system_category()); + return false; + } + } while (br); + } + return true; +#endif +} + +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) +{ + std::error_code ec; + copy_symlink(existing_symlink, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); + } +} + +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept +{ + ec.clear(); + auto to = read_symlink(existing_symlink, ec); + if (!ec) { + if (exists(to, ec) && is_directory(to, ec)) { + create_directory_symlink(to, new_symlink, ec); + } + else { + create_symlink(to, new_symlink, ec); + } + } +} + +GHC_INLINE bool create_directories(const path& p) +{ + std::error_code ec; + auto result = create_directories(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept +{ + path current; + ec.clear(); + for (const path::string_type& part : p) { + current /= part; + if (current != p.root_name() && current != p.root_path()) { + std::error_code tec; + auto fs = status(current, tec); + if (tec && fs.type() != file_type::not_found) { + ec = tec; + return false; + } + if (!exists(fs)) { + create_directory(current, ec); + if (ec) { + return false; + } + } +#ifndef LWG_2935_BEHAVIOUR + else if (!is_directory(fs)) { + ec = detail::make_error_code(detail::portable_error::exists); + return false; + } +#endif + } + } + return true; +} + +GHC_INLINE bool create_directory(const path& p) +{ + std::error_code ec; + auto result = create_directory(p, path(), ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept +{ + return create_directory(p, path(), ec); +} + +GHC_INLINE bool create_directory(const path& p, const path& attributes) +{ + std::error_code ec; + auto result = create_directory(p, attributes, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept +{ + std::error_code tec; + ec.clear(); + auto fs = status(p, tec); +#ifdef LWG_2935_BEHAVIOUR + if (status_known(fs) && exists(fs)) { + return false; + } +#else + if (status_known(fs) && exists(fs) && is_directory(fs)) { + return false; + } +#endif +#ifdef GHC_OS_WINDOWS + if (!attributes.empty()) { + if (!::CreateDirectoryExW(detail::fromUtf8(attributes.u8string()).c_str(), detail::fromUtf8(p.u8string()).c_str(), NULL)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return false; + } + } + else if (!::CreateDirectoryW(detail::fromUtf8(p.u8string()).c_str(), NULL)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return false; + } +#else + ::mode_t attribs = static_cast(perms::all); + if (!attributes.empty()) { + struct ::stat fileStat; + if (::stat(attributes.c_str(), &fileStat) != 0) { + ec = std::error_code(errno, std::system_category()); + return false; + } + attribs = fileStat.st_mode; + } + if (::mkdir(p.c_str(), attribs) != 0) { + ec = std::error_code(errno, std::system_category()); + return false; + } +#endif + return true; +} + +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_directory_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} + +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, true, ec); +} + +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) +{ + std::error_code ec; + create_hard_link(to, new_hard_link, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); + } +} + +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept +{ + detail::create_hardlink(to, new_hard_link, ec); +} + +GHC_INLINE void create_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} + +GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, false, ec); +} + +GHC_INLINE path current_path() +{ + std::error_code ec; + auto result = current_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} + +GHC_INLINE path current_path(std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + DWORD pathlen = ::GetCurrentDirectoryW(0, 0); + std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); + if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { + ec = std::error_code(::GetLastError(), std::system_category()); + return path(); + } + return path(std::wstring(buffer.get()), path::native_format); +#else + size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); + std::unique_ptr buffer(new char[pathlen + 1]); + if (::getcwd(buffer.get(), pathlen) == NULL) { + ec = std::error_code(errno, std::system_category()); + return path(); + } + return path(buffer.get()); +#endif +} + +GHC_INLINE void current_path(const path& p) +{ + std::error_code ec; + current_path(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} + +GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (!::SetCurrentDirectoryW(detail::fromUtf8(p.u8string()).c_str())) { + ec = std::error_code(::GetLastError(), std::system_category()); + } +#else + if (::chdir(p.string().c_str()) == -1) { + ec = std::error_code(errno, std::system_category()); + } +#endif +} + +GHC_INLINE bool exists(file_status s) noexcept +{ + return status_known(s) && s.type() != file_type::not_found; +} + +GHC_INLINE bool exists(const path& p) +{ + return exists(status(p)); +} + +GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept +{ + file_status s = status(p, ec); + if (status_known(s)) { + ec.clear(); + } + return exists(s); +} + +GHC_INLINE bool equivalent(const path& p1, const path& p2) +{ + std::error_code ec; + bool result = equivalent(p1, p2, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); + } + return result; +} + +GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + std::shared_ptr file1(::CreateFileW(p1.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + auto e1 = ::GetLastError(); + std::shared_ptr file2(::CreateFileW(p2.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) { +#ifdef LWG_2937_BEHAVIOUR + ec = std::error_code(e1 ? e1 : ::GetLastError(), std::system_category()); +#else + if (file1 == file2) { + ec = std::error_code(e1 ? e1 : ::GetLastError(), std::system_category()); + } +#endif + return false; + } + BY_HANDLE_FILE_INFORMATION inf1, inf2; + if (!::GetFileInformationByHandle(file1.get(), &inf1)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return false; + } + if (!::GetFileInformationByHandle(file2.get(), &inf2)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return false; + } + return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow && + inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber; +#else + struct ::stat s1, s2; + auto rc1 = ::stat(p1.c_str(), &s1); + auto e1 = errno; + auto rc2 = ::stat(p2.c_str(), &s2); + if (rc1 || rc2) { +#ifdef LWG_2937_BEHAVIOUR + ec = std::error_code(e1 ? e1 : errno, std::system_category()); +#else + if (rc1 && rc2) { + ec = std::error_code(e1 ? e1 : errno, std::system_category()); + } +#endif + return false; + } + return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime; +#endif +} + +GHC_INLINE uintmax_t file_size(const path& p) +{ + std::error_code ec; + auto result = file_size(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(detail::fromUtf8(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return static_cast(-1); + } + return static_cast(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; +#else + struct ::stat fileStat; + if (::stat(p.c_str(), &fileStat) == -1) { + ec = std::error_code(errno, std::system_category()); + return static_cast(-1); + } + return static_cast(fileStat.st_size); +#endif +} + +GHC_INLINE uintmax_t hard_link_count(const path& p) +{ + std::error_code ec; + auto result = hard_link_count(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + uintmax_t result = static_cast(-1); + std::shared_ptr file(::CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + BY_HANDLE_FILE_INFORMATION inf; + if (file.get() == INVALID_HANDLE_VALUE) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + else { + if (!::GetFileInformationByHandle(file.get(), &inf)) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + else { + result = inf.nNumberOfLinks; + } + } + return result; +#else + uintmax_t result = 0; + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr); + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return ec ? static_cast(-1) : result; +#endif +} + +GHC_INLINE bool is_block_file(file_status s) noexcept +{ + return s.type() == file_type::block; +} + +GHC_INLINE bool is_block_file(const path& p) +{ + return is_block_file(status(p)); +} + +GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept +{ + return is_block_file(status(p, ec)); +} + +GHC_INLINE bool is_character_file(file_status s) noexcept +{ + return s.type() == file_type::character; +} + +GHC_INLINE bool is_character_file(const path& p) +{ + return is_character_file(status(p)); +} + +GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept +{ + return is_character_file(status(p, ec)); +} + +GHC_INLINE bool is_directory(file_status s) noexcept +{ + return s.type() == file_type::directory; +} + +GHC_INLINE bool is_directory(const path& p) +{ + return is_directory(status(p)); +} + +GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept +{ + return is_directory(status(p, ec)); +} + +GHC_INLINE bool is_empty(const path& p) +{ + if (is_directory(p)) { + return directory_iterator(p) == directory_iterator(); + } + else { + return file_size(p) == 0; + } +} + +GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept +{ + auto fs = status(p, ec); + if (ec) { + return false; + } + if (is_directory(fs)) { + directory_iterator iter(p, ec); + if (ec) { + return false; + } + return iter == directory_iterator(); + } + else { + auto sz = file_size(p, ec); + if (ec) { + return false; + } + return sz == 0; + } +} + +GHC_INLINE bool is_fifo(file_status s) noexcept +{ + return s.type() == file_type::fifo; +} + +GHC_INLINE bool is_fifo(const path& p) +{ + return is_fifo(status(p)); +} + +GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept +{ + return is_fifo(status(p, ec)); +} + +GHC_INLINE bool is_other(file_status s) noexcept +{ + return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); +} + +GHC_INLINE bool is_other(const path& p) +{ + return is_other(status(p)); +} + +GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept +{ + return is_other(status(p, ec)); +} + +GHC_INLINE bool is_regular_file(file_status s) noexcept +{ + return s.type() == file_type::regular; +} + +GHC_INLINE bool is_regular_file(const path& p) +{ + return is_regular_file(status(p)); +} + +GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept +{ + return is_regular_file(status(p, ec)); +} + +GHC_INLINE bool is_socket(file_status s) noexcept +{ + return s.type() == file_type::socket; +} + +GHC_INLINE bool is_socket(const path& p) +{ + return is_socket(status(p)); +} + +GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept +{ + return is_socket(status(p, ec)); +} + +GHC_INLINE bool is_symlink(file_status s) noexcept +{ + return s.type() == file_type::symlink; +} + +GHC_INLINE bool is_symlink(const path& p) +{ + return is_symlink(symlink_status(p)); +} + +GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept +{ + return is_symlink(symlink_status(p, ec)); +} + +GHC_INLINE file_time_type last_write_time(const path& p) +{ + std::error_code ec; + auto result = last_write_time(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept +{ + time_t result = 0; + ec.clear(); + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); + return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); +} + +GHC_INLINE void last_write_time(const path& p, file_time_type new_time) +{ + std::error_code ec; + last_write_time(p, new_time, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} + +GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept +{ + ec.clear(); + auto d = new_time.time_since_epoch(); +#ifdef GHC_OS_WINDOWS + std::shared_ptr file(::CreateFileW(p.wstring().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle); + FILETIME ft; + auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; + ft.dwLowDateTime = (unsigned long)tt; + ft.dwHighDateTime = tt >> 32; + if (!::SetFileTime(file.get(), 0, 0, &ft)) { + ec = std::error_code(::GetLastError(), std::system_category()); + } +#elif defined(GHC_OS_MACOS) +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + struct ::stat fs; + if (::stat(p.c_str(), &fs) == 0) { + struct ::timeval tv[2]; + tv[0].tv_sec = fs.st_atimespec.tv_sec; + tv[0].tv_usec = static_cast(fs.st_atimespec.tv_nsec / 1000); + tv[1].tv_sec = std::chrono::duration_cast(d).count(); + tv[1].tv_usec = static_cast(std::chrono::duration_cast(d).count() % 1000000); + if (::utimes(p.c_str(), tv) == 0) { + return; + } + } + ec = std::error_code(errno, std::system_category()); + return; +#else + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = std::chrono::duration_cast(d).count(); + times[1].tv_nsec = std::chrono::duration_cast(d).count() % 1000000000; + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + ec = std::error_code(errno, std::system_category()); + } + return; +#endif +#endif +#else + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = std::chrono::duration_cast(d).count(); + times[1].tv_nsec = std::chrono::duration_cast(d).count() % 1000000000; + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + ec = std::error_code(errno, std::system_category()); + } + return; +#endif +} + +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) +{ + std::error_code ec; + permissions(p, prms, opts, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} + +GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept +{ + permissions(p, prms, perm_options::replace, ec); +} + +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) +{ + if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return; + } + auto fs = symlink_status(p, ec); + if ((opts & perm_options::replace) != perm_options::replace) { + if ((opts & perm_options::add) == perm_options::add) { + prms = fs.permissions() | prms; + } + else { + prms = fs.permissions() & ~prms; + } + } +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + auto oldAttr = GetFileAttributesW(p.wstring().c_str()); + if (oldAttr != INVALID_FILE_ATTRIBUTES) { + DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~FILE_ATTRIBUTE_READONLY : oldAttr | FILE_ATTRIBUTE_READONLY; + if (oldAttr == newAttr || SetFileAttributesW(p.wstring().c_str(), newAttr)) { + return; + } + } + ec = std::error_code(::GetLastError(), std::system_category()); +#else + int mode = 0; + if ((prms & perms::owner_read) == perms::owner_read) { + mode |= _S_IREAD; + } + if ((prms & perms::owner_write) == perms::owner_write) { + mode |= _S_IWRITE; + } + if (::_wchmod(p.wstring().c_str(), mode) != 0) { + ec = std::error_code(::GetLastError(), std::system_category()); + } +#endif +#else + if ((opts & perm_options::nofollow) != perm_options::nofollow) { + if (::chmod(p.c_str(), static_cast(prms)) != 0) { + ec = std::error_code(errno, std::system_category()); + } + } +#endif +} + +GHC_INLINE path proximate(const path& p, std::error_code& ec) +{ + return proximate(p, current_path(), ec); +} + +GHC_INLINE path proximate(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); +} + +GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); +} + +GHC_INLINE path read_symlink(const path& p) +{ + std::error_code ec; + auto result = read_symlink(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE path read_symlink(const path& p, std::error_code& ec) +{ + file_status fs = symlink_status(p, ec); + if (fs.type() != file_type::symlink) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return path(); + } + auto result = detail::resolveSymlink(p, ec); + return ec ? path() : result; +} + +GHC_INLINE path relative(const path& p, std::error_code& ec) +{ + return relative(p, current_path(ec), ec); +} + +GHC_INLINE path relative(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_relative(weakly_canonical(base)); +} + +GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); +} + +GHC_INLINE bool remove(const path& p) +{ + std::error_code ec; + auto result = remove(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + std::wstring np = detail::fromUtf8(p.u8string()); + DWORD attr = GetFileAttributesW(np.c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { + auto error = ::GetLastError(); + if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { + return false; + } + ec = std::error_code(error, std::system_category()); + } + if (!ec) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + if (!RemoveDirectoryW(np.c_str())) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + } + else { + if (!DeleteFileW(np.c_str())) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + } + } +#else + if (::remove(p.c_str()) == -1) { + auto error = errno; + if (error == ENOENT) { + return false; + } + ec = std::error_code(errno, std::system_category()); + } +#endif + return ec ? false : true; +} + +GHC_INLINE uintmax_t remove_all(const path& p) +{ + std::error_code ec; + auto result = remove_all(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); + uintmax_t count = 0; + if (p == "/") { + ec = detail::make_error_code(detail::portable_error::not_supported); + return static_cast(-1); + } + std::error_code tec; + auto fs = status(p, tec); + if (exists(fs) && is_directory(fs)) { + for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { + if (ec) { + break; + } + if (!iter->is_symlink() && iter->is_directory()) { + count += remove_all(iter->path(), ec); + if (ec) { + return static_cast(-1); + } + } + else { + remove(iter->path(), ec); + if (ec) { + return static_cast(-1); + } + ++count; + } + } + } + if (!ec) { + if (remove(p, ec)) { + ++count; + } + } + if (ec) { + return static_cast(-1); + } + return count; +} + +GHC_INLINE void rename(const path& from, const path& to) +{ + std::error_code ec; + rename(from, to, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} + +GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (from != to) { + if (!MoveFileW(detail::fromUtf8(from.u8string()).c_str(), detail::fromUtf8(to.u8string()).c_str())) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + } +#else + if (from != to) { + if (::rename(from.c_str(), to.c_str()) != 0) { + ec = std::error_code(errno, std::system_category()); + } + } +#endif +} + +GHC_INLINE void resize_file(const path& p, uintmax_t size) +{ + std::error_code ec; + resize_file(p, size, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} + +GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + LARGE_INTEGER lisize; + lisize.QuadPart = size; + std::shared_ptr file(CreateFileW(detail::fromUtf8(p.u8string()).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle); + if (file.get() == INVALID_HANDLE_VALUE) { + ec = std::error_code(::GetLastError(), std::system_category()); + } + else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { + ec = std::error_code(::GetLastError(), std::system_category()); + } +#else + if (::truncate(p.c_str(), size) != 0) { + ec = std::error_code(errno, std::system_category()); + } +#endif +} + +GHC_INLINE space_info space(const path& p) +{ + std::error_code ec; + auto result = space(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + ULARGE_INTEGER freeBytesAvailableToCaller = {0}; + ULARGE_INTEGER totalNumberOfBytes = {0}; + ULARGE_INTEGER totalNumberOfFreeBytes = {0}; + if (!GetDiskFreeSpaceExW(detail::fromUtf8(p.u8string()).c_str(), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { + ec = std::error_code(::GetLastError(), std::system_category()); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(totalNumberOfBytes.QuadPart), static_cast(totalNumberOfFreeBytes.QuadPart), static_cast(freeBytesAvailableToCaller.QuadPart)}; +#elif !defined(__ANDROID__) || __ANDROID_API__ >= 19 + struct ::statvfs sfs; + if (::statvfs(p.c_str(), &sfs) != 0) { + ec = std::error_code(errno, std::system_category()); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(sfs.f_blocks * sfs.f_frsize), static_cast(sfs.f_bfree * sfs.f_frsize), static_cast(sfs.f_bavail * sfs.f_frsize)}; +#else + ec = detail::make_error_code(detail::portable_error::not_supported); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; +#endif +} + +GHC_INLINE file_status status(const path& p) +{ + std::error_code ec; + auto result = status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept +{ + return detail::status_ex(p, ec); +} + +GHC_INLINE bool status_known(file_status s) noexcept +{ + return s.type() != file_type::none; +} + +GHC_INLINE file_status symlink_status(const path& p) +{ + std::error_code ec; + auto result = symlink_status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} + +GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept +{ + return detail::symlink_status_ex(p, ec); +} + +GHC_INLINE path temp_directory_path() +{ + std::error_code ec; + path result = temp_directory_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} + +GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + wchar_t buffer[512]; + int rc = GetTempPathW(511, buffer); + if (!rc || rc > 511) { + ec = std::error_code(::GetLastError(), std::system_category()); + return path(); + } + return path(std::wstring(buffer)); +#else + static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + const char* temp_path = nullptr; + for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { + temp_path = std::getenv(*temp_name); + if (temp_path) { + return path(temp_path); + } + } + return path("/tmp"); +#endif +} + +GHC_INLINE path weakly_canonical(const path& p) +{ + std::error_code ec; + auto result = weakly_canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} + +GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept +{ + path result; + ec.clear(); + bool scan = true; + for (auto pe : p) { + if (scan) { + std::error_code tec; + if (exists(result / pe, tec)) { + result /= pe; + } + else { + if (ec) { + return path(); + } + scan = false; + if (!result.empty()) { + result = canonical(result, ec) / pe; + if (ec) { + break; + } + } + else { + result /= pe; + } + } + } + else { + result /= pe; + } + } + if (scan) { + if (!result.empty()) { + result = canonical(result, ec); + } + } + return ec ? path() : result.lexically_normal(); +} + +//----------------------------------------------------------------------------- +// 30.10.11 class file_status +// 30.10.11.1 constructors and destructor +GHC_INLINE file_status::file_status() noexcept + : file_status(file_type::none) +{ +} + +GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept + : _type(ft) + , _perms(prms) +{ +} + +GHC_INLINE file_status::file_status(const file_status& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::file_status(file_status&& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::~file_status() {} + +// assignments: +GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +// 30.10.11.3 modifiers +GHC_INLINE void file_status::type(file_type ft) noexcept +{ + _type = ft; +} + +GHC_INLINE void file_status::permissions(perms prms) noexcept +{ + _perms = prms; +} + +// 30.10.11.2 observers +GHC_INLINE file_type file_status::type() const noexcept +{ + return _type; +} + +GHC_INLINE perms file_status::permissions() const noexcept +{ + return _perms; +} + +//----------------------------------------------------------------------------- +// 30.10.12 class directory_entry +// 30.10.12.1 constructors and destructor +// directory_entry::directory_entry() noexcept = default; +// directory_entry::directory_entry(const directory_entry&) = default; +// directory_entry::directory_entry(directory_entry&&) noexcept = default; +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) + : _path(p) + , _file_size(0) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(0) +#endif + , _last_write_time(0) +{ + refresh(); +} + +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) + : _path(p) + , _file_size(0) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(0) +#endif + , _last_write_time(0) +{ + refresh(ec); +} + +GHC_INLINE directory_entry::~directory_entry() {} + +// assignments: +// directory_entry& directory_entry::operator=(const directory_entry&) = default; +// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; + +// 30.10.12.2 directory_entry modifiers +GHC_INLINE void directory_entry::assign(const filesystem::path& p) +{ + _path = p; + refresh(); +} + +GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) +{ + _path = p; + refresh(ec); +} + +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) +{ + _path.replace_filename(p); + refresh(); +} + +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) +{ + _path.replace_filename(p); + refresh(ec); +} + +GHC_INLINE void directory_entry::refresh() +{ + std::error_code ec; + refresh(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); + } +} + +GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept +{ +#ifdef GHC_OS_WINDOWS + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); +#else + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); +#endif +} + +// 30.10.12.3 directory_entry observers +GHC_INLINE const filesystem::path& directory_entry::path() const noexcept +{ + return _path; +} + +GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept +{ + return _path; +} + +GHC_INLINE bool directory_entry::exists() const +{ + return filesystem::exists(status()); +} + +GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept +{ + return filesystem::exists(status(ec)); +} + +GHC_INLINE bool directory_entry::is_block_file() const +{ + return filesystem::is_block_file(status()); +} +GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept +{ + return filesystem::is_block_file(status(ec)); +} + +GHC_INLINE bool directory_entry::is_character_file() const +{ + return filesystem::is_character_file(status()); +} + +GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept +{ + return filesystem::is_character_file(status(ec)); +} + +GHC_INLINE bool directory_entry::is_directory() const +{ + return filesystem::is_directory(status()); +} + +GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept +{ + return filesystem::is_directory(status(ec)); +} + +GHC_INLINE bool directory_entry::is_fifo() const +{ + return filesystem::is_fifo(status()); +} + +GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept +{ + return filesystem::is_fifo(status(ec)); +} + +GHC_INLINE bool directory_entry::is_other() const +{ + return filesystem::is_other(status()); +} + +GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept +{ + return filesystem::is_other(status(ec)); +} + +GHC_INLINE bool directory_entry::is_regular_file() const +{ + return filesystem::is_regular_file(status()); +} + +GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept +{ + return filesystem::is_regular_file(status(ec)); +} + +GHC_INLINE bool directory_entry::is_socket() const +{ + return filesystem::is_socket(status()); +} + +GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept +{ + return filesystem::is_socket(status(ec)); +} + +GHC_INLINE bool directory_entry::is_symlink() const +{ + return filesystem::is_symlink(symlink_status()); +} + +GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept +{ + return filesystem::is_symlink(symlink_status(ec)); +} + +GHC_INLINE uintmax_t directory_entry::file_size() const +{ + if (_status.type() != file_type::none) { + return _file_size; + } + return filesystem::file_size(path()); +} + +GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + return _file_size; + } + return filesystem::file_size(path(), ec); +} + +GHC_INLINE uintmax_t directory_entry::hard_link_count() const +{ +#ifndef GHC_OS_WINDOWS + if (_status.type() != file_type::none) { + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path()); +} + +GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept +{ +#ifndef GHC_OS_WINDOWS + if (_status.type() != file_type::none) { + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path(), ec); +} + +GHC_INLINE file_time_type directory_entry::last_write_time() const +{ + if (_status.type() != file_type::none) { + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path()); +} + +GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path(), ec); +} + +GHC_INLINE file_status directory_entry::status() const +{ + if (_status.type() != file_type::none) { + return _status; + } + return filesystem::status(path()); +} + +GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + return _status; + } + return filesystem::status(path(), ec); +} + +GHC_INLINE file_status directory_entry::symlink_status() const +{ + if (_symlink_status.type() != file_type::none) { + return _symlink_status; + } + return filesystem::symlink_status(path()); +} + +GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none) { + return _symlink_status; + } + return filesystem::symlink_status(path(), ec); +} + +GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept +{ + return _path < rhs._path; +} + +GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept +{ + return _path == rhs._path; +} + +GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept +{ + return _path != rhs._path; +} + +GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept +{ + return _path <= rhs._path; +} + +GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept +{ + return _path > rhs._path; +} + +GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept +{ + return _path >= rhs._path; +} + +//----------------------------------------------------------------------------- +// 30.10.13 class directory_iterator + +#ifdef GHC_OS_WINDOWS +class directory_iterator::impl +{ +public: + impl(const path& p, directory_options options) + : _base(p) + , _options(options) + , _findData{0} + , _dirHandle(INVALID_HANDLE_VALUE) + { + if (!_base.empty()) { + ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); + if ((_dirHandle = FindFirstFileW(detail::fromUtf8((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) { + if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { + increment(_ec); + } + else { + _current = _base / std::wstring(_findData.cFileName); + copyToDirEntry(_ec); + } + } + else { + auto error = ::GetLastError(); + _base = filesystem::path(); + if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = std::error_code(::GetLastError(), std::system_category()); + } + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + } + } + void increment(std::error_code& ec) + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + do { + if (FindNextFileW(_dirHandle, &_findData)) { + _current = _base; + try { + _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); + } + catch(filesystem_error& fe) { + ec = fe.code(); + return; + } + copyToDirEntry(ec); + } + else { + auto err = ::GetLastError(); + if(err != ERROR_NO_MORE_FILES) { + _ec = ec = std::error_code(err, std::system_category()); + } + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + _current = filesystem::path(); + break; + } + } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); + } + else { + ec = _ec; + } + } + void copyToDirEntry(std::error_code& ec) + { + _dir_entry._path = _current; + if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + _dir_entry._status = detail::status_ex(_current, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); + } + else { + _dir_entry._status = detail::status_from_INFO(_current, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); + _dir_entry._symlink_status = _dir_entry._status; + } + if (ec) { + if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { + ec.clear(); + } + else { + _dir_entry._file_size = static_cast(-1); + _dir_entry._last_write_time = 0; + } + } + } + path _base; + directory_options _options; + WIN32_FIND_DATAW _findData; + HANDLE _dirHandle; + path _current; + directory_entry _dir_entry; + std::error_code _ec; +}; +#else +// POSIX implementation +class directory_iterator::impl +{ +public: + impl(const path& path, directory_options options) + : _base(path) + , _options(options) + , _dir(nullptr) + , _entry(nullptr) + { + if (!path.empty()) { + _dir = ::opendir(path.native().c_str()); + } + if (!path.empty()) { + if (!_dir) { + auto error = errno; + _base = filesystem::path(); + if (error != EACCES || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = std::error_code(errno, std::system_category()); + } + } + else { + increment(_ec); + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dir) { + ::closedir(_dir); + } + } + void increment(std::error_code& ec) + { + if (_dir) { + do { + errno = 0; + _entry = readdir(_dir); + if (_entry) { + _current = _base; + _current.append_name(_entry->d_name); + _dir_entry = directory_entry(_current, ec); + } + else { + ::closedir(_dir); + _dir = nullptr; + _current = path(); + if (errno) { + ec = std::error_code(errno, std::system_category()); + } + break; + } + } while (std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); + } + } + path _base; + directory_options _options; + path _current; + DIR* _dir; + struct ::dirent* _entry; + directory_entry _dir_entry; + std::error_code _ec; +}; +#endif + +// 30.10.13.1 member functions +GHC_INLINE directory_iterator::directory_iterator() noexcept + : _impl(new impl(path(), directory_options::none)) +{ +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p) + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } + _impl->_ec.clear(); +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE directory_iterator::~directory_iterator() {} + +GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +GHC_INLINE const directory_entry& directory_iterator::operator*() const +{ + return _impl->_dir_entry; +} + +GHC_INLINE const directory_entry* directory_iterator::operator->() const +{ + return &_impl->_dir_entry; +} + +GHC_INLINE directory_iterator& directory_iterator::operator++() +{ + std::error_code ec; + _impl->increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_current, ec); + } + return *this; +} + +GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept +{ + _impl->increment(ec); + return *this; +} + +GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const +{ + return _impl->_current == rhs._impl->_current; +} + +GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const +{ + return _impl->_current != rhs._impl->_current; +} + +// 30.10.13.2 directory_iterator non-member functions + +GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE directory_iterator end(const directory_iterator&) noexcept +{ + return directory_iterator(); +} + +//----------------------------------------------------------------------------- +// 30.10.14 class recursive_directory_iterator + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator()); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} + +// 30.10.14.1 observers +GHC_INLINE directory_options recursive_directory_iterator::options() const +{ + return _impl->_options; +} + +GHC_INLINE int recursive_directory_iterator::depth() const +{ + return static_cast(_impl->_dir_iter_stack.size() - 1); +} + +GHC_INLINE bool recursive_directory_iterator::recursion_pending() const +{ + return _impl->_recursion_pending; +} + +GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const +{ + return *(_impl->_dir_iter_stack.top()); +} + +GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const +{ + return &(*(_impl->_dir_iter_stack.top())); +} + +// 30.10.14.1 modifiers recursive_directory_iterator& +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() +{ + std::error_code ec; + increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept +{ + if (recursion_pending() && is_directory((*this)->status()) && (!is_symlink((*this)->symlink_status()) || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { + _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); + } + else { + _impl->_dir_iter_stack.top().increment(ec); + } + if (!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if (!_impl->_dir_iter_stack.empty()) { + _impl->_dir_iter_stack.pop(); + } + _impl->_recursion_pending = true; + return *this; +} + +GHC_INLINE void recursive_directory_iterator::pop() +{ + std::error_code ec; + pop(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } +} + +GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) +{ + if (depth() == 0) { + *this = recursive_directory_iterator(); + } + else { + do { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); + } +} + +GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() +{ + _impl->_recursion_pending = false; +} + +// other members as required by 27.2.3, input iterators +GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); +} + +GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); +} + +// 30.10.14.2 directory_iterator non-member functions +GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept +{ + return recursive_directory_iterator(); +} + +#endif // GHC_EXPAND_IMPL + +} // namespace filesystem +} // namespace ghc + +#endif // GHC_FILESYSTEM_H diff --git a/apps/ymshproc/ymshproc.cpp b/apps/yshapeproc/yshapeproc.cpp similarity index 100% rename from apps/ymshproc/ymshproc.cpp rename to apps/yshapeproc/yshapeproc.cpp index c8c4f69b3..8d85c6f37 100644 --- a/apps/ymshproc/ymshproc.cpp +++ b/apps/yshapeproc/yshapeproc.cpp @@ -241,13 +241,6 @@ bool make_shape_preset(vector& quadspos, std::vector& quadsnorm, int main(int argc, const char* argv[]) { // command line parameters - auto geodesic_source = -1; - int p0 = -1; - int p1 = -1; - int p2 = -1; - auto num_geodesic_samples = 0; - auto geodesic_scale = 30.0f; - auto slice = false; auto facevarying = false; auto positiononly = false; auto trianglesonly = false; @@ -257,19 +250,18 @@ int main(int argc, const char* argv[]) { auto uscale = 1.0f; auto translate = zero3f; auto info = false; + auto geodesic_source = -1; + int p0 = -1; + int p1 = -1; + int p2 = -1; + auto num_geodesic_samples = 0; + auto geodesic_scale = 30.0f; + auto slice = false; auto output = "out.ply"s; auto filename = "mesh.ply"s; // parse command line auto cli = cli::make_cli("ymshproc", "Applies operations on a triangle mesh"); - add_option(cli, "--geodesic-source,-g", geodesic_source, "Geodesic source"); - add_option(cli, "--path-vertex0,-p0", p0, "Path vertex 0"); - add_option(cli, "--path-vertex1,-p1", p1, "Path vertex 1"); - add_option(cli, "--path-vertex2,-p2", p2, "Path vertex 2"); - add_option(cli, "--num-geodesic-samples", num_geodesic_samples, - "Number of sampled geodesic sources"); - add_option(cli, "--geodesic-scale", geodesic_scale, "Geodesic scale"); - add_option(cli, "--slice", slice, "Slice mesh along field isolines"); add_option(cli, "--facevarying", facevarying, "Preserve facevarying"); add_option(cli, "--positiononly", positiononly, "Remove all but positions"); add_option(cli, "--trianglesonly", trianglesonly, "Remove all but triangles"); @@ -285,6 +277,14 @@ int main(int argc, const char* argv[]) { add_option(cli, "--scalex,-sx", scale.x, "Scale along x axis"); add_option(cli, "--scalez,-sz", scale.z, "Scale along z axis"); add_option(cli, "--info,-i", info, "print mesh info"); + add_option(cli, "--geodesic-source,-g", geodesic_source, "Geodesic source"); + add_option(cli, "--path-vertex0,-p0", p0, "Path vertex 0"); + add_option(cli, "--path-vertex1,-p1", p1, "Path vertex 1"); + add_option(cli, "--path-vertex2,-p2", p2, "Path vertex 2"); + add_option(cli, "--num-geodesic-samples", num_geodesic_samples, + "Number of sampled geodesic sources"); + add_option(cli, "--geodesic-scale", geodesic_scale, "Geodesic scale"); + add_option(cli, "--slice", slice, "Slice mesh along field isolines"); add_option(cli, "--output,-o", output, "output mesh"); add_option(cli, "mesh", filename, "input mesh", true); parse_cli(cli, argc, argv); diff --git a/docs/readme.md b/docs/readme.md index 7ba6be75e..d5349a210 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -80,15 +80,15 @@ See each header file for documentation. You can see Yocto/GL in action in the following applications written to test the library: -- `apps/yscntrace.cpp`: command-line path-tracer -- `apps/yscnitrace.cpp`: interactive path-tracer -- `apps/yscnitraces.cpp`: simpler version of `apps/yscnitrace.cpp` for demos -- `apps/yscnproc.cpp`: command-line scene manipulation and conversion -- `apps/ymshproc.cpp`: command-line mesh manipulation and conversion -- `apps/yimgview.cpp`: Hdr/Ldr image viewer with tonemapping and color grading -- `apps/yimgviews.cpp`: simpler version of `apps/yimgview.cpp` for demos -- `apps/yimgproc.cpp`: command-line image manipulation -- `apps/yscnview.cpp`: simple OpenGL viewer +- `apps/yscenetrace.cpp`: command-line path-tracer +- `apps/ysceneitrace.cpp`: interactive path-tracer +- `apps/ysceneitraces.cpp`: simpler version of `apps/ysceneitrace.cpp` for demos +- `apps/ysceneproc.cpp`: command-line scene manipulation and conversion +- `apps/yshapeproc.cpp`: command-line mesh manipulation and conversion +- `apps/yimageview.cpp`: Hdr/Ldr image viewer with tonemapping and color grading +- `apps/yimageviews.cpp`: simpler version of `apps/yimageview.cpp` for demos +- `apps/yimageproc.cpp`: command-line image manipulation +- `apps/ysceneview.cpp`: simple OpenGL viewer Here are some test images rendered with the path tracer. More images are included in the [project site](https://xelatihy.github.io/yocto-gl/). diff --git a/libs/yocto/yocto_image.cpp b/libs/yocto/yocto_image.cpp index a253dbe89..fbfbbb0b4 100644 --- a/libs/yocto/yocto_image.cpp +++ b/libs/yocto/yocto_image.cpp @@ -602,6 +602,18 @@ image float_to_byte(const image& fl) { return bt; } +// Conversion from/to floats. +image ushort_to_float(const image& bt) { + auto fl = image{bt.size()}; + for (auto i = 0ull; i < fl.count(); i++) fl[i] = math::ushort_to_float(bt[i]); + return fl; +} +image float_to_ushort(const image& fl) { + auto bt = image{fl.size()}; + for (auto i = 0ull; i < bt.count(); i++) bt[i] = math::float_to_ushort(fl[i]); + return bt; +} + // Conversion between linear and gamma-encoded images. image srgb_to_rgb(const image& srgb) { auto rgb = image{srgb.size()}; @@ -2028,6 +2040,37 @@ bool is_hdr_filename(const std::string& filename) { } } +// Loads a 16 bit image. +[[nodiscard]] bool load_image( + const std::string& filename, image& img, std::string& error) { + auto format_error = [filename, &error]() { + error = filename + ": unknown format"; + return false; + }; + auto read_error = [filename, &error]() { + error = filename + ": read error"; + return false; + }; + + auto ext = get_extension(filename); + if (ext == ".png" || ext == ".PNG" || ext == ".jpg" || ext == ".JPG" || + ext == ".tga" || ext == ".TGA" || ext == ".bmp" || ext == ".BMP") { + auto width = 0, height = 0, ncomp = 0; + auto pixels = stbi_load_16(filename.c_str(), &width, &height, &ncomp, 1); + if (!pixels) return read_error(); + img = image{{width, height}, (const uint16_t*)pixels}; + free(pixels); + return true; + } else if (is_hdr_filename(filename)) { + auto imgf = image{}; + if (!load_image(filename, imgf, error)) return false; + img = float_to_ushort(imgf); + return true; + } else { + return format_error(); + } +} + } // namespace yocto::image // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_image.h b/libs/yocto/yocto_image.h index 11233f2ed..431f24186 100644 --- a/libs/yocto/yocto_image.h +++ b/libs/yocto/yocto_image.h @@ -112,6 +112,7 @@ namespace yocto::image { using math::byte; using math::mat3f; using math::pif; +using math::ushort; using math::vec2f; using math::vec2i; using math::vec3b; @@ -166,6 +167,10 @@ struct image { const T* begin() const; const T* end() const; + // [experimental] data access as vector --- will be replaced by views + std::vector& data_vector(); + const std::vector& data_vector() const; + private: // data vec2i extent = {0, 0}; @@ -207,12 +212,14 @@ vec3f eval_image(const image& img, const vec2f& uv, bool as_linear, namespace yocto::image { // Conversion from/to floats. -image byte_to_float(const image& bt); -image float_to_byte(const image& fl); -image byte_to_float(const image& bt); -image float_to_byte(const image& fl); -image byte_to_float(const image& bt); -image float_to_byte(const image& fl); +image byte_to_float(const image& bt); +image float_to_byte(const image& fl); +image byte_to_float(const image& bt); +image float_to_byte(const image& fl); +image byte_to_float(const image& bt); +image float_to_byte(const image& fl); +image ushort_to_float(const image& bt); +image float_to_ushort(const image& fl); // Conversion between linear and gamma-encoded images. image srgb_to_rgb(const image& srgb); @@ -323,6 +330,10 @@ bool load_image( bool save_image( const std::string& filename, const image& img, std::string& error); +// Load/saves a 16 bit image in linear color space. +bool load_image( + const std::string& filename, image& img, std::string& error); + } // namespace yocto::image // ----------------------------------------------------------------------------- @@ -628,6 +639,16 @@ inline const T* image::end() const { return pixels.data() + pixels.size(); } +// data access as vector +template +inline std::vector& image::data_vector() { + return pixels; +} +template +inline const std::vector& image::data_vector() const { + return pixels; +} + // equality template inline bool operator==(const image& a, const image& b) { diff --git a/libs/yocto/yocto_math.h b/libs/yocto/yocto_math.h index 1cdefc9b6..730f19116 100644 --- a/libs/yocto/yocto_math.h +++ b/libs/yocto/yocto_math.h @@ -207,8 +207,9 @@ // ----------------------------------------------------------------------------- namespace yocto::math { -using byte = unsigned char; -using uint = unsigned int; +using byte = unsigned char; +using uint = unsigned int; +using ushort = unsigned short; inline const double pi = 3.14159265358979323846; inline const float pif = (float)pi; @@ -1333,12 +1334,14 @@ inline bool overlap_bbox(const bbox3f& bbox1, const bbox3f& bbox2); namespace yocto::math { // Conversion between flots and bytes -inline vec3b float_to_byte(const vec3f& a); -inline vec3f byte_to_float(const vec3b& a); -inline vec4b float_to_byte(const vec4f& a); -inline vec4f byte_to_float(const vec4b& a); -inline byte float_to_byte(float a); -inline float byte_to_float(byte a); +inline vec3b float_to_byte(const vec3f& a); +inline vec3f byte_to_float(const vec3b& a); +inline vec4b float_to_byte(const vec4f& a); +inline vec4f byte_to_float(const vec4b& a); +inline byte float_to_byte(float a); +inline float byte_to_float(byte a); +inline ushort float_to_ushort(float a); +inline float ushort_to_float(ushort a); // Luminance inline float luminance(const vec3f& a); @@ -3683,7 +3686,11 @@ inline vec4f byte_to_float(const vec4b& a) { return {a.x / 255.0f, a.y / 255.0f, a.z / 255.0f, a.w / 255.0f}; } inline byte float_to_byte(float a) { return (byte)clamp(int(a * 256), 0, 255); } -inline float byte_to_float(byte a) { return a / 255.0f; } +inline float byte_to_float(byte a) { return a / 255.0f; } +inline ushort float_to_ushort(float a) { + return (ushort)clamp(int(a * 65536), 0, 65535); +} +inline float ushort_to_float(ushort a) { return a / 65535.0f; } // Luminance inline float luminance(const vec3f& a) { diff --git a/libs/yocto/yocto_ply.h b/libs/yocto/yocto_ply.h index ff1dabbf2..4d56ef8b7 100644 --- a/libs/yocto/yocto_ply.h +++ b/libs/yocto/yocto_ply.h @@ -1220,6 +1220,7 @@ inline bool add_values(ply::model* ply, const float* values, size_t count, inline bool add_value(ply::model* ply, const std::string& element, const std::string& property, const std::vector& values) { + if (values.empty()) return false; auto properties = std::vector{property}; return add_values( ply, (float*)values.data(), values.size(), element, properties.data(), 1); @@ -1227,24 +1228,28 @@ inline bool add_value(ply::model* ply, const std::string& element, inline bool add_values(ply::model* ply, const std::string& element, const std::array& properties, const std::vector& values) { + if (values.empty()) return false; return add_values( ply, (float*)values.data(), values.size(), element, properties.data(), 2); } inline bool add_values(ply::model* ply, const std::string& element, const std::array& properties, const std::vector& values) { + if (values.empty()) return false; return add_values( ply, (float*)values.data(), values.size(), element, properties.data(), 3); } inline bool add_values(ply::model* ply, const std::string& element, const std::array& properties, const std::vector& values) { + if (values.empty()) return false; return add_values( ply, (float*)values.data(), values.size(), element, properties.data(), 4); } inline bool add_values(ply::model* ply, const std::string& element, const std::array& properties, const std::vector& values) { + if (values.empty()) return false; return add_values(ply, (float*)values.data(), values.size(), element, properties.data(), properties.size()); } @@ -1288,20 +1293,24 @@ inline bool add_lists(ply::model* ply, const int* values, size_t count, } inline bool add_lists(ply::model* ply, const std::string& element, const std::string& property, const std::vector& values) { + if (values.empty()) return false; return add_lists(ply, values.data(), values.size(), 1, element, property); } inline bool add_lists(ply::model* ply, const std::string& element, const std::string& property, const std::vector& values) { + if (values.empty()) return false; return add_lists( ply, (int*)values.data(), values.size(), 2, element, property); } inline bool add_lists(ply::model* ply, const std::string& element, const std::string& property, const std::vector& values) { + if (values.empty()) return false; return add_lists( ply, (int*)values.data(), values.size(), 3, element, property); } inline bool add_lists(ply::model* ply, const std::string& element, const std::string& property, const std::vector& values) { + if (values.empty()) return false; return add_lists( ply, (int*)values.data(), values.size(), 4, element, property); } diff --git a/libs/yocto/yocto_sceneio.cpp b/libs/yocto/yocto_sceneio.cpp index 754e2fa0b..ff3a6a3c0 100644 --- a/libs/yocto/yocto_sceneio.cpp +++ b/libs/yocto/yocto_sceneio.cpp @@ -1925,6 +1925,10 @@ static bool load_ply_scene(const std::string& filename, scn::model* scene, shape->colors, shape->radius, error)) return false; + // create object + auto object = add_object(scene); + object->shape = shape; + // fix scene add_cameras(scene); add_radius(scene); diff --git a/libs/yocto/yocto_shape.cpp b/libs/yocto/yocto_shape.cpp index e6c368141..4654b6033 100644 --- a/libs/yocto/yocto_shape.cpp +++ b/libs/yocto/yocto_shape.cpp @@ -4003,6 +4003,31 @@ void make_rounded_uvcylinder(std::vector& quads, } } +// Make a quad. +void make_yrect(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& steps, const vec2f& scale, const vec2f& uvscale) { + make_rect(quads, positions, normals, texcoords, steps, scale, uvscale); + for (auto& p : positions) { + std::swap(p.y, p.z); + p.z = -p.z; + } + for (auto& n : normals) std::swap(n.y, n.z); +} + +void make_bulged_yrect(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& steps, const vec2f& scale, const vec2f& uvscale, + float height) { + make_bulged_rect( + quads, positions, normals, texcoords, steps, scale, uvscale, height); + for (auto& p : positions) { + std::swap(p.y, p.z); + p.z = -p.z; + } + for (auto& n : normals) std::swap(n.y, n.z); +} + // Generate lines set along a quad. void make_lines(std::vector& lines, std::vector& positions, std::vector& normals, std::vector& texcoords, @@ -4762,6 +4787,18 @@ void make_shell(std::vector& quads, std::vector& positions, inner_positions, inner_normals, inner_texturecoords); } +// Make a heightfield mesh. +void make_heightfield(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& size, const std::vector& height) { + make_yrect( + quads, positions, normals, texcoords, size - 1, (vec2f)size / max(size)); + for (auto j = 0; j < size.y; j++) + for (auto i = 0; i < size.x; i++) + positions[j * size.x + i].y = height[j * size.x + i]; + normals = compute_normals(quads, positions); +} + } // namespace yocto::shape // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_shape.h b/libs/yocto/yocto_shape.h index 198a460c8..1b6d610fe 100644 --- a/libs/yocto/yocto_shape.h +++ b/libs/yocto/yocto_shape.h @@ -857,6 +857,15 @@ void make_rounded_uvcylinder(std::vector& quads, std::vector& texcoords, const vec3i& steps = {32, 32, 32}, const vec2f& scale = {1, 1}, const vec3f& uvscale = {1, 1, 1}, float radius = 0.3); +// Make a plane in the xz plane. +void make_yrect(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& steps = {1, 1}, const vec2f& scale = {1, 1}, + const vec2f& uvscale = {1, 1}); +void make_bulged_yrect(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& steps = {1, 1}, const vec2f& scale = {1, 1}, + const vec2f& uvscale = {1, 1}, float radius = 0.3); // Make a facevarying rect void make_fvrect(std::vector& quadspos, std::vector& quadsnorm, @@ -936,6 +945,11 @@ void make_shell(std::vector& quads, std::vector& positions, std::vector& normals, std::vector& texcoords, float thickness); +// Make a heightfield mesh. +void make_heightfield(std::vector& quads, std::vector& positions, + std::vector& normals, std::vector& texcoords, + const vec2i& size, const std::vector& height); + } // namespace yocto::shape // ----------------------------------------------------------------------------- diff --git a/readme.md b/readme.md index eaa6398cd..5d4525b48 100644 --- a/readme.md +++ b/readme.md @@ -38,15 +38,15 @@ See each header file for documentation. You can see Yocto/GL in action in the following applications written to test the library: -- `apps/yscntrace.cpp`: command-line path-tracer -- `apps/yscnitrace.cpp`: interactive path-tracer -- `apps/yscnitraces.cpp`: simpler version of `apps/yscnitrace.cpp` for demos -- `apps/yscnproc.cpp`: command-line scene manipulation and conversion -- `apps/ymshproc.cpp`: command-line mesh manipulation and conversion -- `apps/yimgview.cpp`: Hdr/Ldr image viewer with tonemapping and color grading -- `apps/yimgviews.cpp`: simpler version of `apps/yimgview.cpp` for demos -- `apps/yimgproc.cpp`: command-line image manipulation -- `apps/yscnview.cpp`: simple OpenGL viewer +- `apps/yscenetrace.cpp`: command-line path-tracer +- `apps/ysceneitrace.cpp`: interactive path-tracer +- `apps/ysceneitraces.cpp`: simpler version of `apps/ysceneitrace.cpp` for demos +- `apps/ysceneproc.cpp`: command-line scene manipulation and conversion +- `apps/yshapeproc.cpp`: command-line mesh manipulation and conversion +- `apps/yimageview.cpp`: Hdr/Ldr image viewer with tonemapping and color grading +- `apps/yimageviews.cpp`: simpler version of `apps/yimageview.cpp` for demos +- `apps/yimageproc.cpp`: command-line image manipulation +- `apps/ysceneview.cpp`: simple OpenGL viewer Here are some test images rendered with the path tracer. More images are included in the [project site](https://xelatihy.github.io/yocto-gl/). diff --git a/scripts/scenes.py b/scripts/scenes.py index 04f548d28..2c05e74b8 100755 --- a/scripts/scenes.py +++ b/scripts/scenes.py @@ -27,7 +27,7 @@ def itrace(directory='mcguire',scene='*',format='json',mode='path'): if format == 'pbrt': with open(filename) as f: if 'WorldBegin' not in f.read(): continue - cmd = f'../yocto-gl/bin/yscnitrace {options} {filename}' + cmd = f'../yocto-gl/bin/ysceneitrace {options} {filename}' print(cmd, file=sys.stderr) os.system(cmd) @@ -88,12 +88,12 @@ def trace(directory='mcguire',scene='*',format='json',mode='path'): basename = os.path.basename(filename).replace(f'.{format}','') os.system(f'mkdir -p {directory}/{outprefix}-{format}') imagename = f'{directory}/{outprefix}-{format}/{basename}.{outformat}' - cmd = f'../yocto-gl/bin/yscntrace -o {imagename} {options} {filename}' + cmd = f'../yocto-gl/bin/yscenetrace -o {imagename} {options} {filename}' print(cmd, file=sys.stderr) os.system(cmd) for cam in extracams: imagename = f'{directory}/{outprefix}-{format}/{basename}-c{cam}.{outformat}' - cmd = f'../yocto-gl/bin/yscntrace -o {imagename} --camera {cam} {options} {filename}' + cmd = f'../yocto-gl/bin/yscenetrace -o {imagename} --camera {cam} {options} {filename}' print(cmd, file=sys.stderr) os.system(cmd) @@ -202,7 +202,7 @@ def convert(directory='mcguire',scene='*',format='obj',outformat="json",mode='pa if 'WorldBegin' not in f.read(): continue outname = filename.replace(f'/source/',f'/{outformat}/').replace(f'.{format}',f'.{outformat}') if format != 'dijson': - cmd = f'../yocto-gl/bin/yscnproc -o {outname} {options} {obj_options} {filename} {copyright_options}' + cmd = f'../yocto-gl/bin/ysceneproc -o {outname} {options} {obj_options} {filename} {copyright_options}' print(cmd, file=sys.stderr) os.system(cmd) else: