From 7bdf972c004c3d0efb94a29339d6f49f9119bd95 Mon Sep 17 00:00:00 2001 From: Matteo Paonessa Date: Tue, 20 Dec 2022 13:38:05 +0100 Subject: [PATCH] Rust switch --- .github/workflows/cmake.yml | 35 - .gitignore | 188 +-- .idea/.gitignore | 2 + .idea/.name | 1 - .idea/caesium-clt.iml | 2 - .idea/cclt.iml | 15 + .idea/inspectionProfiles/Project_Default.xml | 125 -- .idea/misc.xml | 12 - .idea/modules.xml | 2 +- .idea/scopes/Inspection.xml | 3 - .idea/vcs.xml | 2 +- CMakeLists.txt | 21 - Cargo.lock | 1174 ++++++++++++++++++ Cargo.toml | 17 + INSTALL.md | 22 - README.md | 46 +- README.zh-CN.md | 44 +- install.sh | 6 - src/CMakeLists.txt | 12 - src/config.h.in | 4 - src/error.c | 79 -- src/error.h | 31 - src/helper.c | 351 ------ src/helper.h | 78 -- src/logger.rs | 16 + src/main.c | 78 -- src/main.rs | 264 ++++ src/options.rs | 93 ++ src/scanfiles.rs | 110 ++ src/shared.c | 19 - src/shared.h | 23 - src/utils.c | 354 ------ src/utils.h | 49 - src/vendor/optparse.c | 264 ---- src/vendor/optparse.h | 102 -- src/vendor/tinydir.h | 804 ------------ 36 files changed, 1737 insertions(+), 2711 deletions(-) delete mode 100644 .github/workflows/cmake.yml create mode 100644 .idea/.gitignore delete mode 100644 .idea/.name delete mode 100644 .idea/caesium-clt.iml create mode 100644 .idea/cclt.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/scopes/Inspection.xml delete mode 100644 CMakeLists.txt create mode 100644 Cargo.lock create mode 100644 Cargo.toml delete mode 100644 INSTALL.md delete mode 100644 install.sh delete mode 100644 src/CMakeLists.txt delete mode 100644 src/config.h.in delete mode 100644 src/error.c delete mode 100644 src/error.h delete mode 100644 src/helper.c delete mode 100644 src/helper.h create mode 100644 src/logger.rs delete mode 100755 src/main.c create mode 100644 src/main.rs create mode 100644 src/options.rs create mode 100644 src/scanfiles.rs delete mode 100644 src/shared.c delete mode 100644 src/shared.h delete mode 100644 src/utils.c delete mode 100644 src/utils.h delete mode 100644 src/vendor/optparse.c delete mode 100644 src/vendor/optparse.h delete mode 100644 src/vendor/tinydir.h diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml deleted file mode 100644 index e97e882..0000000 --- a/.github/workflows/cmake.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CMake - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally - # well on Windows or Mac. You can convert this to a matrix build if you need - # cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install requirements - run: chmod +x ./install.sh && ./install.sh - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBCAESIUM_PATH=./libcaesium/target/release - - - name: Build - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - diff --git a/.gitignore b/.gitignore index 2e0414c..c057c10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,185 +1,3 @@ -# Folder view configuration files -.DS_Store -Desktop.ini - -# Thumbnail cache files -._* -Thumbs.db - -# Files that might appear on external disks -.Spotlight-V100 -.Trashes - -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-* - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties -### macOS template -# General -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.TemporaryItems -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk -### Linux template -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* -### Windows template -# Windows thumbnail cache files -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk -### C template -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps -build \ No newline at end of file +/target +**/*.rs.bk +test_output diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 572ef0b..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -caesiumclt \ No newline at end of file diff --git a/.idea/caesium-clt.iml b/.idea/caesium-clt.iml deleted file mode 100644 index f08604b..0000000 --- a/.idea/caesium-clt.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/cclt.iml b/.idea/cclt.iml new file mode 100644 index 0000000..944a1fa --- /dev/null +++ b/.idea/cclt.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index ecc50f5..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,125 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 81967a4..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 96d27b3..a51762a 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/scopes/Inspection.xml b/.idea/scopes/Inspection.xml deleted file mode 100644 index 7eb5c79..0000000 --- a/.idea/scopes/Inspection.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index ccd0de0..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(caesiumclt) - -# The version number. -set(VERSION_MAJOR 0) -set(VERSION_MINOR 18) -set(VERSION_PATCH 0) - -configure_file( - "src/config.h.in" - "${PROJECT_BINARY_DIR}/config.h" -) -if (NOT DEFINED LIBCAESIUM_PATH) - message(FATAL_ERROR "LIBCAESIUM_PATH is not defined. Cannot find libcaesium.") -endif () - -link_directories(${LIBCAESIUM_PATH}) - -include_directories("${PROJECT_BINARY_DIR}") - -add_subdirectory(src) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2f4f481 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1174 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytemuck" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "caesiumclt" +version = "0.19.0" +dependencies = [ + "human_bytes", + "indicatif", + "infer 0.11.0", + "libcaesium", + "num_cpus", + "rand", + "rayon", + "structopt", + "walkdir", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.1", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cloudflare-zlib" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfcefb5df07f146eb15756342a135eb7d76b8bb609eff9c111f7539d060f94d" +dependencies = [ + "cloudflare-zlib-sys", +] + +[[package]] +name = "cloudflare-zlib-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2040b6d1edfee6d75f172d81e2d2a7807534f3f294ce18184c70e7bb0105cd6f" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "console" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + +[[package]] +name = "dunce" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "fallible_collections" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c195cf4b2285d3c993eb887b4dc56b0d5728bbe1d0f9a99c0ac6bec2da3e4d85" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "filetime" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide 0.5.1", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gifsicle" +version = "1.92.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36998a2316aad26c8bfd74c82a8809eaf12e2216a2938bc4dca5b83c59fb3e1a" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "human_bytes" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b528196c838e8b3da8b665e08c30958a6f2ede91d79f2ffcd0d4664b9c64eb" + +[[package]] +name = "image" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "imagequant" +version = "4.0.2" +source = "git+https://github.com/Lymphatus/libimagequant?rev=67f1686#67f1686bfe55b9dda06760089f2f6720f3f7eeeb" +dependencies = [ + "arrayvec", + "noisy_float", + "once_cell", + "rayon", + "rgb", + "thread_local", +] + +[[package]] +name = "img-parts" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19358258d99a5fc34466fed27a5318f92ae636c3e36165cf9b1e87b5b6701f0" +dependencies = [ + "bytes", + "crc32fast", + "miniz_oxide 0.5.1", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", + "rayon", +] + +[[package]] +name = "indicatif" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc42b206e70d86ec03285b123e65a5458c92027d1fb2ae3555878b8113b3ddf" +dependencies = [ + "console", + "number_prefix", + "unicode-width", +] + +[[package]] +name = "infer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f178e61cdbfe084aa75a2f4f7a25a5bb09701a47ae1753608f194b15783c937a" +dependencies = [ + "cfb", +] + +[[package]] +name = "infer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6c16b11a665b26aeeb9b1d7f954cdeb034be38dd00adab4f2ae921a8fee804" +dependencies = [ + "cfb", +] + +[[package]] +name = "iter-read" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c397ca3ea05ad509c4ec451fea28b4771236a376ca1c69fd5143aae0cf8f93c4" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744c24117572563a98a7e9168a5ac1ee4a1ca7f702211258797bbe0ed0346c3c" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libcaesium" +version = "0.10.0" +source = "git+https://github.com/Lymphatus/libcaesium?rev=eca05e2#eca05e2eb8870c873118a9e43c2e9b60f83b12b8" +dependencies = [ + "bytes", + "gifsicle", + "image", + "imagequant", + "img-parts", + "infer 0.9.0", + "libc", + "lodepng", + "mozjpeg-sys", + "oxipng", + "webp", +] + +[[package]] +name = "libdeflate-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43afa5b192ff058426ba20a4f35c290ef402478d6045ac934ac15aa947a3898d" +dependencies = [ + "cc", +] + +[[package]] +name = "libdeflater" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e656b7960ec49e864badc7ad1b810427a7ac8b78511a699ce5cdc3ead0b32e5b" +dependencies = [ + "libdeflate-sys", +] + +[[package]] +name = "libwebp-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439fd1885aa28937e7edcd68d2e793cb4a22f8733460d2519fbafd2b215672bf" +dependencies = [ + "cc", +] + +[[package]] +name = "lodepng" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff45534ec797452c044fcd47861059eddb501e30a8fd9fdadea7957cdff3ebc7" +dependencies = [ + "crc32fast", + "fallible_collections", + "flate2", + "libc", + "rgb", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mozjpeg-sys" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac69196a2b59950122d25194985c8070f9633ac01414dce0a48925346c70c3de" +dependencies = [ + "cc", + "dunce", + "libc", + "nasm-rs", +] + +[[package]] +name = "nasm-rs" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce095842aee9aa3ecbda7a5d2a4df680375fd128a8596b6b56f8e497e231f483" +dependencies = [ + "rayon", +] + +[[package]] +name = "noisy_float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978fe6e6ebc0bf53de533cd456ca2d9de13de13856eda1518a285d7705a213af" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "oxipng" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40d437cd5308cba163907008d4c91a0280fc3b1ec1265dd20820e739002f4d9" +dependencies = [ + "bit-vec", + "clap 3.2.22", + "cloudflare-zlib", + "crc", + "crossbeam-channel", + "filetime", + "image", + "indexmap", + "itertools", + "libdeflater", + "log", + "miniz_oxide 0.6.2", + "rayon", + "rgb", + "rustc_version", + "stderrlog", + "wild", + "zopfli", +] + +[[package]] +name = "png" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.5.1", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rgb" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f3aac57ee7f3272d8395c6e4f502f434f0e289fcd62876f70daa008c20dcabe" + +[[package]] +name = "stderrlog" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af95cb8a5f79db5b2af2a46f44da7594b5adbcbb65cbf87b8da0959bfdd82460" +dependencies = [ + "atty", + "log", + "termcolor", + "thread_local", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.33.3", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typed-arena" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "uuid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "webp" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf022f821f166079a407d000ab57e84de020e66ffbbf4edde999bc7d6e371cae" +dependencies = [ + "image", + "libwebp-sys", +] + +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + +[[package]] +name = "wild" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b116685a6be0c52f5a103334cbff26db643826c7b3735fc0a3ba9871310a74" +dependencies = [ + "glob", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "zopfli" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e0d16c30236860686a8f03d36b384dc2fc0675a8916367d2f9a1ecd795eab6" +dependencies = [ + "adler32", + "byteorder", + "crc", + "iter-read", + "log", + "typed-arena", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..116ed62 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "caesiumclt" +version = "0.19.0" +authors = ["Matteo Paonessa "] +edition = "2021" + + +[dependencies] +structopt = "0.3" +indicatif = "0.17" +walkdir = "2.3" +num_cpus = "1.13" +infer = "0.11" +rayon = "1.5" +rand = "0.8" +human_bytes = { version = "0.4", default-features = false } +libcaesium = { git = "https://github.com/Lymphatus/libcaesium", rev = "eca05e2" } \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index c5f2042..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,22 +0,0 @@ -# Install - -## Requirements -CaesiumCLT is based on [libcaesium](https://github.com/Lymphatus/libcaesium) and requires it to be compiled and installed. -Please refer to its own documentation. -You will also need cmake if you want to compile it from source. - -## Instructions - -Download the latest release package from [here](https://github.com/Lymphatus/caesium-clt/releases) or clone from git. -Then run: - - $ cd caesium-clt - $ mkdir build && cd build - $ cmake .. -DLIBCAESIUM_PATH=/your/libcaesium/path/dir - $ make - $ sudo make install - -This will compile and install caesiumclt in your `PATH`. -You can verify everything went ok by running `caesiumclt -v`. - - \ No newline at end of file diff --git a/README.md b/README.md index 424d12a..a52ed1e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,8 @@ ## Caesium CommandLineTools -###### caesium-clt - v0.18.0-beta (build 20221106) +###### caesium-clt - v0.19.0 (build 20221114) ###### REQUIREMENTS -* [libcaesium](https://github.com/Lymphatus/libcaesium) >= 0.9.3 - -###### Included libraries -* [optparse](https://github.com/skeeto/optparse) -* [tinydir](https://github.com/cxong/tinydir) - +* [Rust](https://www.rust-lang.org/tools/install) ---------- ###### TESTED PLATFORMS @@ -18,7 +13,7 @@ ---------- ###### COMPILATION -See INSTALL.md for more details. +`cargo build --release` ---------- @@ -37,7 +32,7 @@ See INSTALL.md for more details. - `-S, --keep-structure` If the input is a folder, and the `-R` option is set, caesiumclt will compress all the files keeping the original folder structure. - `-O, --overwrite` - Sets the overwrite policy: `all` will overwrite any existing file, `prompt` will ask each time before overwriting, `bigger` will overwrite bigger files only, and `none` will silently skip existing files. + Sets overwrite policy: `all` will overwrite any existing file, `prompt` will ask each time before overwriting, `bigger` will overwrite bigger files only, and `none` will silently skip existing files. - `-d, --dry-run` If this option is set, no files will be compressed, but the entire process will just be simulated. Useful for checking if all the files will be correctly handled. @@ -81,19 +76,20 @@ $ caesiumclt -q 0 -RS -o ~/output/ ~/Pictures ---------- ###### CHANGELOG -* 0.18.0-beta - Fixed Windows build + libcaesium 0.9.3 -* 0.17.0-beta - libcaesium 0.9.2 -* 0.16.0-beta - Using libcaesium Rust library -* 0.15.2-beta - Fixed Windows -RS bug -* 0.15.1-beta - Fixed rename bug on Windows + "Compressing..." message -* 0.15.0-beta - Support for libcaesium 0.5.0 -* 0.14.0-beta - Added --quiet option -* 0.13.1-beta - Bugfix -* 0.13.0-beta - Bugfix -* 0.12.1-beta - Bugfix -* 0.12.0-beta - Resizing (experimental) -* 0.11.0-beta - Fixing paths issues and dry-run option -* 0.10.2-beta - Bugfixes & full Windows support -* 0.10.1-beta - All features are available -* 0.10.0-beta - Switched to cmake build system and libcaesium -* 0.9.1-beta - Initial development stage +* 0.19.0 - Rust migration +* 0.18.0 - Fixed Windows build + libcaesium 0.9.3 +* 0.17.0 - libcaesium 0.9.2 +* 0.16.0 - Using libcaesium Rust library +* 0.15.2 - Fixed Windows -RS bug +* 0.15.1 - Fixed rename bug on Windows + "Compressing..." message +* 0.15.0 - Support for libcaesium 0.5.0 +* 0.14.0 - Added --quiet option +* 0.13.1 - Bugfix +* 0.13.0 - Bugfix +* 0.12.1 - Bugfix +* 0.12.0 - Resizing (experimental) +* 0.11.0 - Fixing paths issues and dry-run option +* 0.10.2 - Bugfixes & full Windows support +* 0.10.1 - All features are available +* 0.10.0 - Switched to cmake build system and libcaesium +* 0.9.1 - Initial development stage diff --git a/README.zh-CN.md b/README.zh-CN.md index 1fc0eeb..be46c52 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,13 +1,8 @@ ## Caesium 命令行工具 -###### caesium-clt - v0.18.0-beta (build 20221106) +###### caesium-clt - v0.19.0 (build 20221114) ###### 依赖 -* [libcaesium](https://github.com/Lymphatus/libcaesium) >= 0.9.3 - -###### 已包含的库 -* [optparse](https://github.com/skeeto/optparse) -* [tinydir](https://github.com/cxong/tinydir) - +* [Rust](https://www.rust-lang.org/tools/install) ---------- ###### 已通过测试的平台 @@ -18,7 +13,7 @@ ---------- ###### 编译 -详情见 INSTALL.md 。 +`cargo build --release` ---------- @@ -80,19 +75,20 @@ $ caesiumclt -q 0 -RS -o ~/output/ ~/Pictures ---------- ###### 变更日志 -* 0.18.0-beta - Fixed Windows build + libcaesium 0.9.3 -* 0.17.0-beta - libcaesium 0.9.2 -* 0.16.0-beta - Using libcaesium Rust library -* 0.15.2-beta - Fixed Windows -RS bug -* 0.15.1-beta - Fixed rename bug on Windows + "Compressing..." message -* 0.15.0-beta - Support for libcaesium 0.5.0 -* 0.14.0-beta - Added --quiet option -* 0.13.1-beta - Bugfix -* 0.13.0-beta - Bugfix -* 0.12.1-beta - Bugfix -* 0.12.0-beta - Resizing (experimental) -* 0.11.0-beta - Fixing paths issues and dry-run option -* 0.10.2-beta - Bugfixes & full Windows support -* 0.10.1-beta - All features are available -* 0.10.0-beta - Switched to cmake build system and libcaesium -* 0.9.1-beta - Initial development stage +* 0.19.0 - Rust migration +* 0.18.0 - Fixed Windows build + libcaesium 0.9.3 +* 0.17.0 - libcaesium 0.9.2 +* 0.16.0 - Using libcaesium Rust library +* 0.15.2 - Fixed Windows -RS bug +* 0.15.1 - Fixed rename bug on Windows + "Compressing..." message +* 0.15.0 - Support for libcaesium 0.5.0 +* 0.14.0 - Added --quiet option +* 0.13.1 - Bugfix +* 0.13.0 - Bugfix +* 0.12.1 - Bugfix +* 0.12.0 - Resizing (experimental) +* 0.11.0 - Fixing paths issues and dry-run option +* 0.10.2 - Bugfixes & full Windows support +* 0.10.1 - All features are available +* 0.10.0 - Switched to cmake build system and libcaesium +* 0.9.1 - Initial development stage diff --git a/install.sh b/install.sh deleted file mode 100644 index 8649a7a..0000000 --- a/install.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -#libcaesium -git clone https://github.com/Lymphatus/libcaesium.git -cd libcaesium || exit -cargo build --release diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 038ea09..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(CMAKE_C_FLAGS "--std=gnu99 -Wno-nullability-completeness") - -FILE(GLOB CSources *.c) -FILE(GLOB CVendorSources vendor/*.c) -FILE(GLOB CHeaders *.h) -FILE(GLOB CVendorHeaders vendor/*.h) - -add_executable(caesiumclt ${CVendorSources} ${CVendorHeaders} ${CSources} ${CHeaders}) - -target_link_libraries(caesiumclt LINK_PUBLIC caesium m) - -install(TARGETS caesiumclt DESTINATION bin) \ No newline at end of file diff --git a/src/config.h.in b/src/config.h.in deleted file mode 100644 index bc68323..0000000 --- a/src/config.h.in +++ /dev/null @@ -1,4 +0,0 @@ -#define VERSION_MAJOR @VERSION_MAJOR@ -#define VERSION_MINOR @VERSION_MINOR@ -#define VERSION_PATCH @VERSION_PATCH@ - diff --git a/src/error.c b/src/error.c deleted file mode 100644 index 8508a23..0000000 --- a/src/error.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include "error.h" -#include "utils.h" -#include "shared.h" - -void display_error(error_level level, int code) { - char *error_level = ((level) ? "[WARNING]" : "[ERROR]"); - - print_to_console(stderr, verbose, "%s %s (code: %d)\n", - error_level, - get_error_message(code), - code); - - if (level == CS_ERROR) { - exit(-code); - } -} - -const char *get_error_message(int code) { - switch (code) { - //Generic errors - case 1: - return "Invalid quality value. Must be between [0-100]."; - case 2: - return "Unrecognized option."; - case 3: - return "Empty input folder."; - case 4: - return "Cannot keep folder structure providing multiple input files."; - case 5: - return "Cannot create output folder."; - case 6: - return "Cannot check if is a directory."; - case 7: - return "Cannot calculate file size"; - case 8: - return "Input folder provided. Skipping all other inputs."; - case 9: - return "Input files provided. Cannot mix them with a folder."; - case 10: - return "-R has no effects on files."; - case 11: - return "-S has no effect without -R."; - case 12: - return "Cannot set output folder inside the input one."; - case 13: - return "Scale factor must be between (0, 1.0]. Setting it to 1.0."; - case 14: - return "Scale factor parsing error."; - case 15: - return "Overwrite policy value is invalid. Using 'bigger'."; - case 16: - return "Cannot get the full output path."; - case 17: - return "Cannot create the output folder."; - - default: - return "Unrecognized error."; - } -} \ No newline at end of file diff --git a/src/error.h b/src/error.h deleted file mode 100644 index a058f19..0000000 --- a/src/error.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef CAESIUMCLT_ERROR_H -#define CAESIUMCLT_ERROR_H - -typedef enum error_level -{ - CS_ERROR = 0, - CS_WARNING = 1 -} error_level; - -void display_error(error_level level, int code); - -const char *get_error_message(int code); - -#endif //CAESIUMCLT_ERROR_H diff --git a/src/helper.c b/src/helper.c deleted file mode 100644 index 4542f40..0000000 --- a/src/helper.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include -#include "helper.h" -#include "vendor/optparse.h" -#include "utils.h" -#include "config.h" -#include "error.h" -#include "shared.h" - -cclt_options parse_arguments(char **argv, C_CSParameters *options) { - struct optparse opts; - //Initialize application options - cclt_options parameters = {NULL, "", "", false, false, 0, 0, 0, false, all}; - - //Parse command line args - struct optparse_long longopts[] = { - {"quality", 'q', OPTPARSE_REQUIRED}, - {"exif", 'e', OPTPARSE_NONE}, - {"output", 'o', OPTPARSE_REQUIRED}, - {"scale", 's', OPTPARSE_REQUIRED}, - {"recursive", 'R', OPTPARSE_NONE}, - {"keep-structure", 'S', OPTPARSE_NONE}, - {"overwrite", 'O', OPTPARSE_REQUIRED}, - {"dry-run", 'd', OPTPARSE_NONE}, - {"quiet", 'Q', OPTPARSE_NONE}, - {"version", 'v', OPTPARSE_NONE}, - {"help", 'h', OPTPARSE_NONE}, - {0} - }; - optparse_init(&opts, argv); - int option; - int quality = 0; - while ((option = optparse_long(&opts, longopts, NULL)) != -1) { - switch (option) { - case 'q': - quality = (int) strtol(opts.optarg, (char **) NULL, 10); - if (quality < 0 || quality > 100) { - display_error(CS_ERROR, 1); - } - if (quality == 0) { - options->optimize = true; - } else { - options->jpeg_quality = quality; - options->png_quality = quality; - options->webp_quality = quality; - options->gif_quality = quality; - } - break; - case 'e': - options->keep_metadata = true; - break; - case 'o': - if (opts.optarg[0] == '~') { - snprintf(parameters.output_folder, strlen(opts.optarg) + 1, "%s", opts.optarg); - } else { -#ifdef _WIN32 - _fullpath(parameters.output_folder, opts.optarg, MAX_PATH_SIZE); -#else - char *computedPath = realpath(opts.optarg, parameters.output_folder); - if (computedPath == NULL) { - //Folder does not exists and may just fail on some systems, like Docker Alpine - if (errno == 2) { - if (mkpath(opts.optarg) == 0) { - computedPath = realpath(opts.optarg, parameters.output_folder); - if (computedPath == NULL) { - //Just throw an error here - display_error(CS_ERROR, 16); - } - } else { - display_error(CS_ERROR, 17); - } - } else { - display_error(CS_ERROR, 16); - } - } -#endif - } - int pathlen = (int) strlen(parameters.output_folder); - if (parameters.output_folder[pathlen - 1] != '/' && - parameters.output_folder[pathlen - 1] != '\\') { - // append the extra slash/backslash -#ifdef _WIN32 - snprintf(parameters.output_folder + pathlen, 2, "\\"); -#else - snprintf(parameters.output_folder + pathlen, 2, "/"); -#endif - } - break; - case 'R': - parameters.recursive = true; - break; - case 'S': - parameters.keep_structure = true; - break; - case 'O': - parameters.overwrite = parse_overwrite_policy(opts.optarg); - break; - case 'd': - parameters.dry_run = true; - break; - case 'v': - print_to_console(stdout, 1, "%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); - exit(EXIT_SUCCESS); - case 'Q': - verbose = 0; - break; - case 'h': - print_help(); - break; - case '?': - default: - print_to_console(stderr, verbose, "%s: %s\n", argv[0], opts.errmsg); - display_error(CS_ERROR, 2); - } - } - //Remaining arguments - char *arg; - bool files_flag = false, folders_flag = false; - char resolved_path[MAX_PATH_SIZE]; - - print_to_console(stdout, verbose, "%s\n", "Collecting files..."); - - while ((arg = optparse_arg(&opts))) { - if (folders_flag) { - display_error(CS_WARNING, 8); - break; - } - - //Check if it's a directory and add its content - if (arg[0] == '~' && is_directory(arg)) { - if (arg[strlen(arg) - 1] == '/' || arg[strlen(arg) - 1] == '\\') { - snprintf(resolved_path, strlen(arg), "%s", arg); - } else { -#ifdef _WIN32 - snprintf(resolved_path, strlen(arg) + 1, "%s\\", arg); -#else - snprintf(resolved_path, strlen(arg) + 1, "%s/", arg); -#endif - } - } else { -#ifdef _WIN32 - _fullpath(resolved_path, arg, MAX_PATH_SIZE); -#else - realpath(arg, resolved_path); -#endif - } - - if (is_directory(resolved_path)) { - if (!files_flag) { - folders_flag = true; - size_t len = strlen(resolved_path); - if (resolved_path[len - 1] != '/' && resolved_path[strlen(resolved_path) - 1] != '\\') { -#ifdef _WIN32 - resolved_path[len] = '\\'; -#else - resolved_path[len] = '/'; -#endif - resolved_path[len + 1] = '\0'; - } - - snprintf(parameters.input_folder, strlen(resolved_path) + 1, "%s", resolved_path); - scan_folder(resolved_path, ¶meters, parameters.recursive); - if (parameters.files_count == 0) { - display_error(CS_WARNING, 3); - } - } else { - display_error(CS_WARNING, 9); - } - } else { - files_flag = true; - parameters.input_files = realloc(parameters.input_files, (parameters.files_count + 1) * sizeof(char *)); - parameters.input_files[parameters.files_count] = malloc((strlen(arg) + 1) * sizeof(char)); - snprintf(parameters.input_files[parameters.files_count], strlen(arg) + 1, "%s", arg); - parameters.files_count++; - } - } - - //Check if the output folder is a subfolder of the input to avoid infinite loops - //but just if the -R option is set - //However, if the folders are the same, we can let it go as it will overwrite the files - if (folders_flag) { - if (strstr(parameters.output_folder, parameters.input_folder) != NULL - && strcmp(parameters.output_folder, parameters.input_folder) != 0 - && parameters.recursive) { - display_error(CS_ERROR, 12); - } - } - - //-R and -S set warnings - if (parameters.recursive && !folders_flag) { - display_error(CS_WARNING, 10); - parameters.recursive = false; - } - if (!parameters.recursive && parameters.keep_structure) { - display_error(CS_WARNING, 11); - parameters.keep_structure = false; - } - //If there are files and folders, we cannot keep the structure - if (parameters.keep_structure && (!folders_flag && parameters.files_count > 1)) { - display_error(CS_WARNING, 4); - parameters.keep_structure = false; - } - return parameters; -} - -int start_compression(cclt_options *options, struct C_CSParameters parameters) { - int compressed_files = 0; - off_t input_file_size; - off_t output_file_size; - //Create the output folder if it does not exist - if (mkpath(options->output_folder) == -1) { - display_error(CS_ERROR, 5); - } - - for (int i = 0; i < options->files_count; i++) { - char *filename = get_filename(options->input_files[i]); - char *output_full_path = NULL; - char *original_output_full_path = NULL; - bool overwriting = false; - off_t file_size; - //If we don't need to keep the structure, we put all the files in one folder by just the filename - if (!options->keep_structure) { - output_full_path = malloc((strlen(filename) + strlen(options->output_folder) + 1) * sizeof(char)); - snprintf(output_full_path, (strlen(filename) + strlen(options->output_folder) + 1), "%s%s", - options->output_folder, filename); - } else { - /* - * Otherwise, we need to compute the whole directory structure - * We are sure we have a folder only as input, so that's the root - * Just compute the subfolders without the filename, make them and append the filename - * A piece of cake <3 - */ - - size_t index = strspn(options->input_folder, options->input_files[i]); - size_t size = strlen(options->input_files[i]) - index - strlen(filename); - char output_full_folder[strlen(options->output_folder) + size + 1]; - - snprintf(output_full_folder, strlen(options->output_folder) + size + 1, "%s%s", - options->output_folder, &options->input_files[i][index]); - output_full_path = malloc((strlen(output_full_folder) + strlen(filename) + 1) * sizeof(char)); - snprintf(output_full_path, strlen(output_full_folder) + strlen(filename) + 1, "%s%s", - output_full_folder, - filename); - - mkpath(output_full_folder); - } - - //Calculating the total input file size, ignoring if we are going to skip them later - file_size = get_file_size(options->input_files[i]); - if (file_size == 0) { - //We could not open the file - continue; - } - input_file_size = file_size; - options->input_total_size += input_file_size; - - //If the file already exist, create a temporary file - bool f_exists = file_exists(output_full_path); - if (f_exists) { - if (options->overwrite == none) { - print_to_console(stdout, verbose, "[SKIPPED] %s\n", output_full_path); - options->output_total_size += get_file_size(output_full_path); - goto free_and_go_on_with_next_file; - } else if (options->overwrite == prompt) { - print_to_console(stdout, verbose, "Overwrite %s? [y/n]\n", output_full_path); - int prompt = getchar(); - if (prompt == '\n') { - prompt = getchar(); - } - if (prompt != 'y' && prompt != 'Y') { - print_to_console(stdout, verbose, "[SKIPPED] %s\n", output_full_path); - options->output_total_size += get_file_size(output_full_path); - goto free_and_go_on_with_next_file; - } - } - - original_output_full_path = strdup(output_full_path); - output_full_path = realloc(output_full_path, (strlen(output_full_path) + 4) * sizeof(char)); - snprintf(output_full_path, (strlen(original_output_full_path) + 4), "%s.cs", original_output_full_path); - overwriting = true; - } - - print_to_console(stdout, verbose, "(%d/%d) %s -> %s\nCompressing...", - i + 1, - options->files_count, - filename, - f_exists ? original_output_full_path : output_full_path); - fflush(stdout); - //Prevent compression if running in dry mode - if (!options->dry_run) { - C_CSResult compression_result = c_compress(options->input_files[i], output_full_path, parameters); - if (compression_result.success) { - compressed_files++; - output_file_size = get_file_size(output_full_path); - - char *human_input_size = get_human_size(input_file_size); - char *human_output_size = get_human_size(output_file_size); - - if (options->overwrite == bigger && get_file_size(original_output_full_path) <= output_file_size) { - print_to_console(stdout, verbose, "Resulting file is bigger. Skipping.\n"); - remove(output_full_path); - options->output_total_size += get_file_size(original_output_full_path); - goto free_and_go_on_with_next_file; - } - options->output_total_size += output_file_size; - print_to_console(stdout, verbose, "\r%s -> %s [%.2f%%]\n", - human_input_size, - human_output_size, - ((float) output_file_size - (float) input_file_size) * 100 / (float) input_file_size); - } else { - print_to_console(stderr, verbose, "\nCompression failed: %s\n", compression_result.error_message); - options->input_total_size -= get_file_size(options->input_files[i]); - } - } - - //Rename if we were overwriting - if (overwriting && !options->dry_run) { -#ifdef _WIN32 - remove(original_output_full_path); -#endif - rename(output_full_path, original_output_full_path); - } - - free_and_go_on_with_next_file: - free(original_output_full_path); - free(output_full_path); - } - - return compressed_files; -} - - diff --git a/src/helper.h b/src/helper.h deleted file mode 100644 index c931e3a..0000000 --- a/src/helper.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef CAESIUM_CLT_HELPER_H -#define CAESIUM_CLT_HELPER_H - -#include - -#ifdef _WIN32 -#define MAX_PATH_SIZE _MAX_PATH -#else -#include -#include - -#define MAX_PATH_SIZE PATH_MAX -#endif - -typedef enum overwrite_policy { - none, - prompt, - bigger, - all -} overwrite_policy; - -typedef struct C_CSParameters { - bool keep_metadata; - unsigned int jpeg_quality; - unsigned int png_quality; - bool png_force_zopfli; - unsigned int gif_quality; - unsigned int webp_quality; - bool optimize; - int width; - int height; -} C_CSParameters; - -typedef struct C_CSResult { - bool success; - const char *error_message; -} C_CSResult; - -extern C_CSResult c_compress(const char *i, const char *o, struct C_CSParameters params); - -typedef struct cclt_options -{ - char **input_files; - char input_folder[MAX_PATH_SIZE]; - char output_folder[MAX_PATH_SIZE]; - bool recursive; - bool keep_structure; - int files_count; - off_t input_total_size; - off_t output_total_size; - bool dry_run; - overwrite_policy overwrite; -} cclt_options; - - - -cclt_options parse_arguments(char *argv[], C_CSParameters *options); - -int start_compression(cclt_options *options, struct C_CSParameters parameters); - -#endif //CAESIUM_CLT_HELPER_H diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..d55fd37 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,16 @@ +pub enum ErrorLevel { + Log, + Warning, + Error, +} + +pub fn log(message: &str, code: i32, level: ErrorLevel, verbose: u8) { + if verbose == 0 { + return; + } + match level { + ErrorLevel::Error => panic!("[ERROR] {} (Code: {})", message, code), + ErrorLevel::Warning => eprintln!("[WARNING] {} (Code: {})", message, code), + _ => println!("{}", message) + }; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c deleted file mode 100755 index 97228d7..0000000 --- a/src/main.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include "utils.h" -#include "shared.h" -#include - -int main(int argc, char *argv[]) -{ - //Exit if less than 2 arguments - if (argc < 2) { - print_help(); - exit(EXIT_FAILURE); - } - - long compression_time = 0; - cclt_options options; - - //Initialize the default parameters - C_CSParameters compress_options = { - false, - 80, - 80, - false, - 20, - 60, - false, - 0, - 0 - }; - //Set them according to command line parameters - options = parse_arguments(argv, &compress_options); - - //Start a timer before calling the compression - clock_t start = clock(), diff; - - start_compression(&options, compress_options); - - //Cleanup the memory allocated objects - free(options.input_files); - - //Get the difference - diff = clock() - start; - compression_time = diff * 1000 / CLOCKS_PER_SEC; - - - //Output the compression results - print_to_console(stdout, verbose, "-------------------------------\n"); - compression_time / 1000 % 60 >= 1 ? - print_to_console(stdout, verbose, "Compression completed in %lum%lus\n", - compression_time / 1000 / 60, compression_time / 1000 % 60) : - print_to_console(stdout, verbose, "Compression completed in %lum%lus%lums\n", - compression_time / 1000 / 60, compression_time / 1000 % 60, compression_time % 1000); - print_to_console(stdout, verbose, "%s -> %s [%.2f%% | %s]\n", - get_human_size(options.input_total_size), get_human_size(options.output_total_size), - ((float) options.output_total_size - options.input_total_size) * 100 / options.input_total_size, - get_human_size((options.output_total_size - options.input_total_size))); - - - exit(EXIT_SUCCESS); -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ec7dc5e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,264 @@ +use std::fs; +use std::path::Path; +use std::sync::{Arc, Mutex}; + +use human_bytes::human_bytes; +use indicatif::ProgressBar; +use indicatif::ProgressDrawTarget; +use indicatif::ProgressStyle; +use rand::{Rng, thread_rng}; +use rand::distributions::Alphanumeric; +use rayon::prelude::*; + +use crate::logger::ErrorLevel::{Error, Log, Warning}; +use crate::logger::log; +use crate::options::OverwritePolicy; + +mod scanfiles; +mod options; +mod logger; + +struct CompressionResult { + pub path: String, + pub output_path: String, + pub original_size: u64, + pub compressed_size: u64, + pub error: String, + pub result: bool, +} + +fn main() { + let opt = options::get_opts(); + let mut verbose = opt.verbose; + let args = opt.files; + let dry_run = opt.dry_run; + let output_dir = opt.output; + if opt.quiet { + verbose = 0; + } + let cpus = if opt.threads > 0 { + std::cmp::min(num_cpus::get(), opt.threads as usize) + } else { + num_cpus::get() + }; + rayon::ThreadPoolBuilder::new().num_threads(cpus).build_global().unwrap_or_default(); + + match fs::create_dir_all(output_dir.clone()) { + Ok(_) => {} + Err(_) => log("Cannot create output path. Check your permissions.", 201, Error, verbose) + } + + let (base_path, files) = scanfiles::scanfiles(args, opt.recursive); + + let mut compression_parameters = caesium::initialize_parameters(); + if opt.quality == 0 { + compression_parameters.optimize = true; + } else { + compression_parameters.jpeg.quality = opt.quality; + compression_parameters.png.quality = opt.quality; + compression_parameters.gif.quality = opt.quality; + compression_parameters.webp.quality = opt.quality; + } + compression_parameters.keep_metadata = opt.exif; + + if opt.width > 0 { + compression_parameters.width = opt.width; + } + + if opt.height > 0 { + compression_parameters.height = opt.height; + } + + let overwrite_policy = opt.overwrite; + let keep_structure = opt.keep_structure; + + let progress_bar = setup_progress_bar(files.len() as u64, verbose); + progress_bar.set_message("Compressing..."); + + let results = Arc::new(Mutex::new(Vec::new())); + files.par_iter().for_each(|input_file| { + let input_size = match fs::metadata(input_file) { + Ok(s) => s.len(), + Err(e) => { + let error_message = format!("Cannot get file size for {}, Error: {}", input_file.display(), e); + log(error_message.as_str(), 202, Warning, verbose); + 0 + } + }; + + let mut compression_result = CompressionResult { + path: input_file.display().to_string(), + output_path: "".to_string(), + original_size: input_size, + compressed_size: 0, + error: "Unknown".to_string(), + result: false, + }; + + let filename = if keep_structure { + input_file.strip_prefix(base_path.clone()).unwrap_or_else(|_| Path::new("")).as_os_str() + } else { + input_file.file_name().unwrap_or_default() + }; + + if filename.is_empty() { + compression_result.error = "Cannot retrieve filename for {}. Skipping.".to_string(); + results.lock().unwrap().push(compression_result); + return; + } + let filename_str = match filename.to_str() { + None => { + compression_result.error = "Cannot convert filename for {}. Skipping.".to_string(); + results.lock().unwrap().push(compression_result); + return; + } + Some(fs) => fs + }; + + + let random_suffix: String = (&mut thread_rng()).sample_iter(Alphanumeric) + .take(8) + .map(char::from) + .collect(); + let random_suffixed_name = format!("{}.{}", filename_str, random_suffix); + let final_output_full_path = output_dir.clone().join(filename); + let output_full_path = output_dir.clone().join(random_suffixed_name); + let output_full_dir = output_full_path.parent().unwrap_or_else(|| Path::new("/")); + compression_result.output_path = final_output_full_path.display().to_string(); + if !output_full_dir.exists() { + match fs::create_dir_all(output_full_dir) { + Ok(_) => {} + Err(e) => { + compression_result.error = format!("Cannot create output directory. Error: {}.", e); + results.lock().unwrap().push(compression_result); + return; + } + }; + } + if !matches!(overwrite_policy, OverwritePolicy::All) && final_output_full_path.exists() { + if let OverwritePolicy::None = overwrite_policy { return; } + } + let input_full_path = input_file.to_str().unwrap(); + let output_full_path_str = match output_full_path.to_str() { + None => { + compression_result.error = "Cannot convert output_full_path. Skipping.".to_string(); + return; + } + Some(ofp) => ofp + }; + if !dry_run { + match caesium::compress(input_full_path.to_string(), output_full_path_str.to_string(), &compression_parameters) { + Ok(_) => { + compression_result.result = true; + let output_metadata = fs::metadata(output_full_path.clone()); + let output_size = if let Ok(..) = output_metadata { + output_metadata.unwrap().len() + } else { + 0 + }; + let mut final_output_size = output_size; + if matches!(overwrite_policy, OverwritePolicy::Bigger) && final_output_full_path.exists() { + let existing_file_metadata = fs::metadata(final_output_full_path.clone()); + let existing_file_size = if let Ok(..) = existing_file_metadata { + existing_file_metadata.unwrap().len() + } else { + 0 + }; + if output_size >= existing_file_size { + match fs::remove_file(output_full_path) { + Ok(_) => {} + Err(e) => { + compression_result.error = format!("Cannot remove existing file. Error: {}.", e); + compression_result.result = false; + } + }; + final_output_size = existing_file_size; + } else { + match fs::rename(output_full_path, final_output_full_path) { + Ok(_) => {} + Err(e) => { + compression_result.error = format!("Cannot rename existing file. Error: {}.", e); + compression_result.result = false; + } + }; + } + } else { + match fs::rename(output_full_path, final_output_full_path) { + Ok(_) => {} + Err(e) => { + compression_result.error = format!("Cannot rename existing file. Error: {}.", e); + compression_result.result = false; + } + }; + } + compression_result.compressed_size = final_output_size; + results.lock().unwrap().push(compression_result); + } + Err(e) => { + compression_result.error = e.to_string(); + results.lock().unwrap().push(compression_result); + } + } + } else { + results.lock().unwrap().push(compression_result) + } + progress_bar.inc(1); + }); + + + progress_bar.finish_with_message("Compression completed!"); + + let mut total_original_size = 0.0; + let mut total_compressed_size = 0.0; + let mut total_errors: u32 = 0; + let mut total_compressed_files = 0; + + results.lock().unwrap().iter().for_each(|result| { + if result.result { + total_compressed_size += result.compressed_size as f64; + if verbose > 1 { + let message = format!("{} -> {}\n{} -> {} [{:.2}%]", + result.path, + result.output_path, + human_bytes(result.original_size as f64), + human_bytes(result.compressed_size as f64), + (result.compressed_size as f64 - result.original_size as f64) * 100.0 / result.original_size as f64 + ); + log(message.as_str(), 0, Log, verbose); + } + total_compressed_files += 1; + } else { + total_compressed_size += result.original_size as f64; + total_errors += 1; + + log(format!("File {} was not compressed. Reason: {}", result.path, result.error).as_str(), 210, Warning, verbose); + } + total_original_size += result.original_size as f64; + }); + + let recap_message = format!("\nCompressed {} files ({} errors)\n{} -> {} [{:.2}% | -{}]", + total_compressed_files, + total_errors, + human_bytes(total_original_size), + human_bytes(total_compressed_size), + (total_compressed_size - total_original_size) * 100.0 / total_original_size, + human_bytes(total_original_size - total_compressed_size) //TODO can be positive + ); + + log(recap_message.as_str(), 0, Log, verbose); +} + +fn setup_progress_bar(len: u64, verbose: u8) -> ProgressBar { + let progress_bar = ProgressBar::new(len); + progress_bar.set_draw_target(ProgressDrawTarget::stdout()); + progress_bar.set_style(ProgressStyle::default_bar() + .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len}\n{msg}") + .unwrap() + .progress_chars("#>-")); + + if verbose == 0 { + progress_bar.set_draw_target(ProgressDrawTarget::hidden()); + } + + progress_bar +} diff --git a/src/options.rs b/src/options.rs new file mode 100644 index 0000000..eec82c8 --- /dev/null +++ b/src/options.rs @@ -0,0 +1,93 @@ +use std::path::PathBuf; + +use structopt::clap::arg_enum; +use structopt::StructOpt; + +use crate::logger::ErrorLevel::Error; +use crate::logger::log; + +arg_enum! { + #[derive(Debug, Clone, Copy)] + pub enum OverwritePolicy { + All, + None, + Bigger + } +} + + +#[derive(StructOpt, Debug)] +#[structopt(name = "", about = "CaesiumCLT - Command Line Tools for image compression")] +pub struct Opt { + /// sets output file quality between [0-100], 0 for optimization + #[structopt(short = "q", long)] + pub quality: u32, + + /// keeps EXIF info during compression + #[structopt(short = "e", long)] + pub exif: bool, + + /// width of the output image, if height is not set will preserve aspect ratio + #[structopt(long, default_value = "0")] + pub width: u32, + + /// height of the output image, if width is not set will preserve aspect ratio + #[structopt(long, default_value = "0")] + pub height: u32, + + /// output folder + #[structopt(short = "o", long, parse(from_os_str))] + pub output: PathBuf, + + /// if input is a folder, scan subfolders too + #[structopt(short = "R", long)] + pub recursive: bool, + + /// keep the folder structure, can be used only with -R + #[structopt(short = "S", long)] + pub keep_structure: bool, + + /// overwrite policy + #[structopt(short = "O", long, default_value = "all")] + pub overwrite: OverwritePolicy, + + /// do not compress files but just show output paths + #[structopt(short = "d", long)] + pub dry_run: bool, + + /// suppress all output + #[structopt(short = "Q", long)] + pub quiet: bool, + + /// specify the number of parallel jobs (max is the number of processors available) + #[structopt(long, default_value = "0")] + pub threads: u32, + + /// select how much output you want to see, 0 is equal to -Q, --quiet + #[structopt(long, default_value = "1")] + pub verbose: u8, + + /// Files to process + #[structopt(name = "FILE", parse(from_os_str))] + pub files: Vec, +} + +pub fn get_opts() -> Opt { + let opt = Opt::from_args(); + validate_opts(&opt); + + opt +} + +fn validate_opts(opt: &Opt) { + let args = &opt.files; + let verbose = opt.verbose; + + if args.is_empty() { + log("Please provide at least one file or folder.", 101, Error, verbose); + } + + if !opt.output.is_dir() { + log("Please provide a folder as output path.", 102, Error, verbose); + } +} \ No newline at end of file diff --git a/src/scanfiles.rs b/src/scanfiles.rs new file mode 100644 index 0000000..4b2681f --- /dev/null +++ b/src/scanfiles.rs @@ -0,0 +1,110 @@ +use std::path::PathBuf; +use std::time::Duration; + +use indicatif::ProgressBar; +use indicatif::ProgressStyle; +use walkdir::WalkDir; + +pub fn is_filetype_supported(path: &PathBuf) -> bool { + let file_path = match path.to_str() { + None => return false, + Some(p) => p + }; + match infer::get_from_path(file_path) { + Ok(v) => match v { + None => false, + Some(ft) => matches!(ft.mime_type(), "image/jpeg" | "image/png" | "image/gif" | "image/webp"), + }, + Err(_) => false + } +} + + +fn is_valid(entry: &PathBuf) -> bool { + entry.exists() && entry.is_file() && is_filetype_supported(entry) +} + +pub fn scanfiles(args: Vec, recursive: bool) -> (PathBuf, Vec) { + let mut files: Vec = vec![]; + let mut base_path = PathBuf::new(); + + let progress_bar = init_progress_bar(); + + for input in args.into_iter() { + if input.exists() && input.is_dir() { + let mut walk_dir = WalkDir::new(input); + if !recursive { + walk_dir = walk_dir.max_depth(1); + } + for entry in walk_dir.into_iter().filter_map(|e| e.ok()) { + let path = entry.into_path(); + if is_valid(&path) { + if let Ok(ap) = path.canonicalize() { + base_path = compute_base_folder(&base_path, &ap); + files.push(ap); + } + } + } + } else if is_valid(&input) { + if let Ok(ap) = input.canonicalize() { + base_path = compute_base_folder(&base_path, &ap); + files.push(ap); + } + } + + progress_bar.tick(); + } + + progress_bar.finish_and_clear(); + + (base_path, files) +} + +fn compute_base_folder(base_folder: &PathBuf, new_path: &PathBuf) -> PathBuf { + if base_folder.parent().is_none() { + return if new_path.is_dir() { + new_path.clone() + } else { + new_path.parent().unwrap_or(&*PathBuf::from("/")).to_path_buf() + }; + } + let mut folder = PathBuf::new(); + let mut new_path_folder = new_path.clone(); + if new_path.is_file() { + new_path_folder = new_path.parent().unwrap_or(&*PathBuf::from("/")).to_path_buf(); + } + for (i, component) in base_folder.iter().enumerate() { + if let Some(new_path_component) = new_path_folder.iter().nth(i) { + if new_path_component == component { + folder.push(component); + } else { + break; + } + } else { + break; + } + } + + if folder.parent().is_none() { + return PathBuf::from("/"); + } + + folder +} + + +fn init_progress_bar() -> ProgressBar { + let progress_bar = ProgressBar::new_spinner(); + let style = match ProgressStyle::default_spinner() + .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]) + .template("{spinner:.cyan} {msg}") { + Ok(s) => s, + Err(_) => ProgressStyle::default_spinner() + }; + + progress_bar.set_message("Collecting files..."); + progress_bar.enable_steady_tick(Duration::from_millis(80)); + progress_bar.set_style(style); + + progress_bar +} \ No newline at end of file diff --git a/src/shared.c b/src/shared.c deleted file mode 100644 index c950335..0000000 --- a/src/shared.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - -int verbose = 1; diff --git a/src/shared.h b/src/shared.h deleted file mode 100644 index 145e22a..0000000 --- a/src/shared.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef CAESIUMCLT_SHARED_H -#define CAESIUMCLT_SHARED_H - -extern int verbose; - -#endif //CAESIUMCLT_SHARED_H diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 83d478b..0000000 --- a/src/utils.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#endif - -#include -#include - -#include "utils.h" -#include "vendor/tinydir.h" -#include "error.h" - -void print_help() { - print_to_console(stdout, 1, - "CaesiumCLT - Caesium Command Line Tools\n\n" - "Usage: caesiumclt [OPTIONS] INPUT...\n" - "Command line image compressor.\n\n" - - "Options:\n" - "\t-q, --quality\t\tset output file quality between [0-100], 0 for optimization\n" - "\t-e, --exif\t\tkeeps EXIF info during compression\n" - "\t-o, --output\t\toutput folder\n" - "\t-R, --recursive\t\tif input is a folder, scan subfolders too\n" - "\t-S, --keep-structure\tkeep the folder structure, use with -R\n" - "\t-O, --overwrite\t\tOverwrite policy: all, none, prompt, bigger. Default is bigger.\n" - "\t-d, --dry-run\t\tdo not really compress files but just show output paths\n" - "\t-Q, --quiet\t\tsuppress all output\n" - "\t-h, --help\t\tdisplay this help and exit\n" - "\t-v, --version\t\toutput version information and exit\n\n"); - exit(EXIT_SUCCESS); -} - -bool is_directory(const char *path) { -#ifdef _WIN32 - tinydir_dir dir; - - return tinydir_open(&dir, path) != -1; - -#else - tinydir_file file; - - if (tinydir_file_open(&file, path) == -1) { - display_error(CS_ERROR, 6); - } - - return (bool) file.is_dir; -#endif -} - -void scan_folder(const char *directory, cclt_options *options, bool recursive) { - tinydir_dir dir; - tinydir_open(&dir, directory); - - while (dir.has_next) { - tinydir_file file; - tinydir_readfile(&dir, &file); - - if (file.is_dir) { - if (strcmp(file.name, ".") != 0 && strcmp(file.name, "..") != 0 && recursive) { - scan_folder(file.path, options, true); - } - } else { - options->input_files = realloc(options->input_files, (options->files_count + 1) * sizeof(char *)); - options->input_files[options->files_count] = malloc((strlen(file.path) + 1) * sizeof(char)); - snprintf(options->input_files[options->files_count], - strlen(file.path) + 1, "%s", file.path); -#ifdef _WIN32 - options->input_files[options->files_count] = str_replace(options->input_files[options->files_count], "/", "\\"); -#endif - options->files_count++; - } - tinydir_next(&dir); - } - - tinydir_close(&dir); -} - -#ifdef _WIN32 -int mkpath(const char *pathname) -{ - const char *p; - char *temp; - bool ret = 0; - const char SEP = '\\'; - - temp = calloc(1, strlen(pathname) + 1); - /* Skip Windows drive letter. */ - - p = strchr(pathname, ':'); - if (p != NULL) { - p++; - } - else { - - p = pathname; - } - - while ((p = strchr(p, SEP)) != NULL) - { - /* Skip empty elements. Could be a Windows UNC path or - just multiple separators which is okay. */ - if (p != pathname && *(p - 1) == SEP) - { - p++; - continue; - } - /* Put the path up to this point into a temporary to - pass to the make directory function. */ - memcpy(temp, pathname, p - pathname); - temp[p - pathname] = '\0'; - p++; - if (CreateDirectory(temp, NULL) == FALSE) - { - if (GetLastError() != ERROR_ALREADY_EXISTS) - { - ret = -1; - break; - } - } - } - free(temp); - return ret; -} -#endif - -#ifndef _WIN32 - -int mkpath(const char *pathname) { - char parent[PATH_MAX], *p; - /* make a parent directory path */ - strncpy(parent, pathname, sizeof(parent)); - parent[sizeof(parent) - 1] = '\0'; - for (p = parent + strlen(parent); *p != '/' && p != parent; p--); - *p = '\0'; - /* try to make parent directory */ - if (p != parent && mkpath(parent) != 0) { - return -1; - } - /* make this one if parent has been made */ - if (mkdir(pathname, 0755) == 0) { - return 0; - } - /* if it already exists that is fine */ - if (errno == EEXIST) { - return 0; - } - return -1; -} - - -#endif - -char *get_filename(char *full_path) { - char *token, *tofree; - - //Get just the filename - tofree = strdup(full_path); -#ifdef _WIN32 - while ((token = strsep(&tofree, "\\")) != NULL) - { -#else - while ((token = strsep(&tofree, "/")) != NULL) { -#endif - if (tofree == NULL) { - break; - } - } - - free(tofree); - - return token; -} - -off_t get_file_size(const char *path) { - FILE *f = fopen(path, "rb"); - if (f == NULL) { - perror("Cannot open output file"); - display_error(CS_WARNING, 7); - return 0; - } - fseek(f, 0, SEEK_END); - off_t len = ftell(f); - fclose(f); - - return len; -} - -char *get_human_size(off_t size) { - if (size == 0) { - return "0.00 B"; - } - - //We should not get more than TB images - char *unit[5] = {"B", "KB", "MB", "GB", "TB"}; - //Index of the array containing the correct unit - double order = floor(log2(labs(size)) / 10); - //Alloc enough size for the final string - char *final = (char *) malloc(((int) (floor(log10(labs(size))) + 5)) * sizeof(char)); - - //If the order exceeds 4, something is fishy - if (order > 4) { - order = 4; - } - - //Copy the formatted string into the buffer - sprintf(final, "%.2f %s", size / (pow(1024, order)), unit[(int) order]); - //And return it - return final; -} - -bool file_exists(const char *file_path) { - struct stat buffer; - return (stat(file_path, &buffer) == 0); -} - -overwrite_policy parse_overwrite_policy(const char *overwrite_string) { - if (strcmp(overwrite_string, "none") == 0) { - return none; - } else if (strcmp(overwrite_string, "prompt") == 0) { - return prompt; - } else if (strcmp(overwrite_string, "bigger") == 0) { - return bigger; - } else if (strcmp(overwrite_string, "all") == 0) { - return all; - } - display_error(CS_WARNING, 15); - return bigger; -} - -void print_to_console(FILE *buffer, int verbose, const char *format, ...) { - if (!verbose) { - return; - } - - va_list args; - - va_start(args, format); - vfprintf(buffer, format, args); - va_end(args); -} - -#ifdef _WIN32 -char *str_replace(char *orig, char *rep, char *with) -{ - char *result; // the return string - char *ins; // the next insert point - char *tmp; // varies - int len_rep; // length of rep (the string to remove) - int len_with; // length of with (the string to replace rep with) - int len_front; // distance between rep and end of last rep - int count; // number of replacements - - if (!orig || !rep) - return NULL; - len_rep = strlen(rep); - if (len_rep == 0) - return NULL; - if (!with) - with = ""; - len_with = strlen(with); - - ins = orig; - for (count = 0; tmp = strstr(ins, rep); ++count) - { - ins = tmp + len_rep; - } - - tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); - - if (!result) - return NULL; - - while (count--) - { - ins = strstr(orig, rep); - len_front = ins - orig; - tmp = strncpy(tmp, orig, len_front) + len_front; - tmp = strcpy(tmp, with) + len_with; - orig += len_front + len_rep; - } - strcpy(tmp, orig); - return result; -} - -char *strsep(char **stringp, const char *delim) -{ - char *begin, *end; - - begin = *stringp; - if (begin == NULL) - return NULL; - - /* A frequent case is when the delimiter string contains only one - character. Here we don't need to call the expensive `strpbrk' - function and instead work using `strchr'. */ - if (delim[0] == '\0' || delim[1] == '\0') - { - char ch = delim[0]; - - if (ch == '\0') - end = NULL; - else - { - if (*begin == ch) - end = begin; - else if (*begin == '\0') - end = NULL; - else - end = strchr(begin + 1, ch); - } - } - else - /* Find the end of the token. */ - end = strpbrk(begin, delim); - - if (end) - { - /* Terminate the token and set *STRINGP past NUL character. */ - *end++ = '\0'; - *stringp = end; - } - else - /* No more delimiters; this is the last token. */ - *stringp = NULL; - - return begin; -} - -#endif \ No newline at end of file diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 2afafe9..0000000 --- a/src/utils.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2019 Matteo Paonessa - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef CAESIUM_CLT_UTILS_H -#define CAESIUM_CLT_UTILS_H - -#include "helper.h" - -void print_help(); - -bool is_directory(const char *path); - -void scan_folder(const char *directory, cclt_options *options, bool recursive); - -char *get_filename(char * full_path); - -off_t get_file_size(const char *path); - -char* get_human_size(off_t size); - -int mkpath(const char *pathname); - -bool file_exists(const char* file_path); - -overwrite_policy parse_overwrite_policy(const char* overwrite_string); - -void print_to_console(FILE* buffer, int verbose, const char* format, ...); - -#ifdef _WIN32 -char *str_replace(char *orig, char *rep, char *with); -char *strsep(char **stringp, const char *delim); -#endif - - -#endif //CAESIUM_CLT_UTILS_H diff --git a/src/vendor/optparse.c b/src/vendor/optparse.c deleted file mode 100644 index 78ecb8e..0000000 --- a/src/vendor/optparse.c +++ /dev/null @@ -1,264 +0,0 @@ -#include "optparse.h" - -#define MSG_INVALID "invalid option" -#define MSG_MISSING "option requires an argument" -#define MSG_TOOMANY "option takes no arguments" - -static int -opterror(struct optparse *options, const char *message, const char *data) -{ - unsigned p = 0; - while (*message) - options->errmsg[p++] = *message++; - const char *sep = " -- '"; - while (*sep) - options->errmsg[p++] = *sep++; - while (p < sizeof(options->errmsg) - 2 && *data) - options->errmsg[p++] = *data++; - options->errmsg[p++] = '\''; - options->errmsg[p++] = '\0'; - return '?'; -} - -void optparse_init(struct optparse *options, char **argv) -{ - options->argv = argv; - options->permute = 1; - options->optind = 1; - options->subopt = 0; - options->optarg = 0; - options->errmsg[0] = '\0'; -} - -static inline int -is_dashdash(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0'; -} - -static inline int -is_shortopt(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0'; -} - -static inline int -is_longopt(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0'; -} - -static void -permute(struct optparse *options, int index) -{ - char *nonoption = options->argv[index]; - for (int i = index; i < options->optind - 1; i++) - options->argv[i] = options->argv[i + 1]; - options->argv[options->optind - 1] = nonoption; -} - -static int -argtype(const char *optstring, char c) -{ - if (c == ':') - return -1; - for (; *optstring && c != *optstring; optstring++); - if (!*optstring) - return -1; - int count = OPTPARSE_NONE; - if (optstring[1] == ':') - count += optstring[2] == ':' ? 2 : 1; - return count; -} - -int optparse(struct optparse *options, const char *optstring) -{ - options->errmsg[0] = '\0'; - options->optopt = 0; - options->optarg = 0; - char *option = options->argv[options->optind]; - if (option == 0) { - return -1; - } else if (is_dashdash(option)) { - options->optind++; /* consume "--" */ - return -1; - } else if (!is_shortopt(option)) { - if (options->permute) { - int index = options->optind; - options->optind++; - int r = optparse(options, optstring); - permute(options, index); - options->optind--; - return r; - } else { - return -1; - } - } - option += options->subopt + 1; - options->optopt = option[0]; - int type = argtype(optstring, option[0]); - char *next = options->argv[options->optind + 1]; - switch (type) { - case OPTPARSE_NONE: - if (option[1]) { - options->subopt++; - } else { - options->subopt = 0; - options->optind++; - } - return option[0]; - case OPTPARSE_REQUIRED: - options->subopt = 0; - options->optind++; - if (option[1]) { - options->optarg = option + 1; - } else if (next != 0) { - options->optarg = next; - options->optind++; - } else { - options->optarg = 0; - char str[2] = {option[0]}; - return opterror(options, MSG_MISSING, str); - } - return option[0]; - case OPTPARSE_OPTIONAL: - options->subopt = 0; - options->optind++; - if (option[1]) - options->optarg = option + 1; - else - options->optarg = 0; - return option[0]; - default: - case -1: { - options->optind++; - char str[2] = {option[0]}; - return opterror(options, MSG_INVALID, str); - } - } -} - -char *optparse_arg(struct optparse *options) -{ - options->subopt = 0; - char *option = options->argv[options->optind]; - if (option != 0) - options->optind++; - return option; -} - -static inline int -longopts_end(const struct optparse_long *longopts, int i) -{ - return !longopts[i].longname && !longopts[i].shortname; -} - -static void -optstring_from_long(const struct optparse_long *longopts, char *optstring) -{ - char *p = optstring; - for (int i = 0; !longopts_end(longopts, i); i++) { - if (longopts[i].shortname) { - *p++ = longopts[i].shortname; - for (int a = 0; a < (int)longopts[i].argtype; a++) - *p++ = ':'; - } - } - *p = '\0'; -} - -/* Unlike strcmp(), handles options containing "=". */ -static int -longopts_match(const char *longname, const char *option) -{ - if (longname == 0) - return 0; - const char *a = option, *n = longname; - for (; *a && *n && *a != '='; a++, n++) - if (*a != *n) - return 0; - return *n == '\0' && (*a == '\0' || *a == '='); -} - -/* Return the part after "=", or NULL. */ -static char * -longopts_arg(char *option) -{ - for (; *option && *option != '='; option++); - if (*option == '=') - return option + 1; - else - return 0; -} - -static int -long_fallback(struct optparse *options, - const struct optparse_long *longopts, - int *longindex) -{ - char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ - optstring_from_long(longopts, optstring); - int result = optparse(options, optstring); - if (longindex != 0) { - *longindex = -1; - if (result != -1) - for (int i = 0; !longopts_end(longopts, i); i++) - if (longopts[i].shortname == options->optopt) - *longindex = i; - } - return result; -} - -int -optparse_long(struct optparse *options, - const struct optparse_long *longopts, - int *longindex) -{ - char *option = options->argv[options->optind]; - if (option == 0) { - return -1; - } else if (is_dashdash(option)) { - options->optind++; /* consume "--" */ - return -1; - } else if (is_shortopt(option)) { - return long_fallback(options, longopts, longindex); - } else if (!is_longopt(option)) { - if (options->permute) { - int index = options->optind; - options->optind++; - int r = optparse_long(options, longopts, longindex); - permute(options, index); - options->optind--; - return r; - } else { - return -1; - } - } - - /* Parse as long option. */ - options->errmsg[0] = '\0'; - options->optopt = 0; - options->optarg = 0; - option += 2; /* skip "--" */ - options->optind++; - for (int i = 0; !longopts_end(longopts, i); i++) { - const char *name = longopts[i].longname; - if (longopts_match(name, option)) { - if (longindex) - *longindex = i; - options->optopt = longopts[i].shortname; - char *arg = longopts_arg(option); - if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { - return opterror(options, MSG_TOOMANY, name); - } if (arg != 0) { - options->optarg = arg; - } else if (longopts[i].argtype == OPTPARSE_REQUIRED) { - options->optarg = options->argv[options->optind++]; - if (options->optarg == 0) - return opterror(options, MSG_MISSING, name); - } - return options->optopt; - } - } - return opterror(options, MSG_INVALID, option); -} \ No newline at end of file diff --git a/src/vendor/optparse.h b/src/vendor/optparse.h deleted file mode 100644 index c43a241..0000000 --- a/src/vendor/optparse.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef OPTPARSE_H -#define OPTPARSE_H - -/** - * Optparse -- portable, reentrant, embeddable, getopt-like option parser - * - * The POSIX getopt() option parser has three fatal flaws. These flaws - * are solved by Optparse. - * - * 1) Parser state is stored entirely in global variables, some of - * which are static and inaccessible. This means only one thread can - * use getopt(). It also means it's not possible to recursively parse - * nested sub-arguments while in the middle of argument parsing. - * Optparse fixes this by storing all state on a local struct. - * - * 2) The POSIX standard provides no way to properly reset the parser. - * This means for portable code that getopt() is only good for one - * run, over one argv with one optstring. It also means subcommand - * options cannot be processed with getopt(). Most implementations - * provide a method to reset the parser, but it's not portable. - * Optparse provides an optparse_arg() function for stepping over - * subcommands and continuing parsing of options with another - * optstring. The Optparse struct itself can be passed around to - * subcommand handlers for additional subcommand option parsing. A - * full reset can be achieved by with an additional optparse_init(). - * - * 3) Error messages are printed to stderr. This can be disabled with - * opterr, but the messages themselves are still inaccessible. - * Optparse solves this by writing an error message in its errmsg - * field. The downside to Optparse is that this error message will - * always be in English rather than the current locale. - * - * Optparse should be familiar with anyone accustomed to getopt(), and - * it could be a nearly drop-in replacement. The optstring is the same - * and the fields have the same names as the getopt() global variables - * (optarg, optind, optopt). - * - * Optparse also supports GNU-style long options with optparse_long(). - * The interface is slightly different and simpler than getopt_long(). - * - * By default, argv is permuted as it is parsed, moving non-option - * arguments to the end. This can be disabled by setting the `permute` - * field to 0 after initialization. - */ - -struct optparse { - char **argv; - int permute; - int optind; - int optopt; - char *optarg; - char errmsg[64]; - int subopt; -}; - -enum optparse_argtype { OPTPARSE_NONE, OPTPARSE_REQUIRED, OPTPARSE_OPTIONAL }; - -struct optparse_long { - const char *longname; - int shortname; - enum optparse_argtype argtype; -}; - -/** - * Initializes the parser state. - */ -void optparse_init(struct optparse *options, char **argv); - -/** - * Read the next option in the argv array. - * @param optstring a getopt()-formatted option string. - * @return the next option character, -1 for done, or '?' for error - * - * Just like getopt(), a character followed by no colons means no - * argument. One colon means the option has a required argument. Two - * colons means the option takes an optional argument. - */ -int optparse(struct optparse *options, const char *optstring); - -/** - * Handles GNU-style long options in addition to getopt() options. - * This works a lot like GNU's getopt_long(). The last option in - * longopts must be all zeros, marking the end of the array. The - * longindex argument may be NULL. - */ -int -optparse_long(struct optparse *options, - const struct optparse_long *longopts, - int *longindex); - -/** - * Used for stepping over non-option arguments. - * @return the next non-option argument, or -1 for no more arguments - * - * Argument parsing can continue with optparse() after using this - * function. That would be used to parse the options for the - * subcommand returned by optparse_arg(). This function allows you to - * ignore the value of optind. - */ -char *optparse_arg(struct optparse *options); - -#endif \ No newline at end of file diff --git a/src/vendor/tinydir.h b/src/vendor/tinydir.h deleted file mode 100644 index 3bd0bd6..0000000 --- a/src/vendor/tinydir.h +++ /dev/null @@ -1,804 +0,0 @@ -/* -Copyright (c) 2013-2016, tinydir authors: -- Cong Xu -- Lautis Sun -- Baudouin Feildel -- Andargor -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. - -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 OWNER 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. -*/ -#ifndef TINYDIR_H -#define TINYDIR_H - -#ifdef __cplusplus -extern "C" { -#endif - -#if ((defined _UNICODE) && !(defined UNICODE)) -#define UNICODE -#endif - -#if ((defined UNICODE) && !(defined _UNICODE)) -#define _UNICODE -#endif - -#include -#include -#include -#ifdef _MSC_VER -# define WIN32_LEAN_AND_MEAN -# include -# include -# pragma warning(push) -# pragma warning (disable : 4996) -#else -# include -# include -# include -# include -#endif -#ifdef __MINGW32__ -# include -#endif - - -/* types */ - -/* Windows UNICODE wide character support */ -#if defined _MSC_VER || defined __MINGW32__ -#define _tinydir_char_t TCHAR -#define TINYDIR_STRING(s) _TEXT(s) -#define _tinydir_strlen _tcslen -#define _tinydir_strcpy _tcscpy -#define _tinydir_strcat _tcscat -#define _tinydir_strcmp _tcscmp -#define _tinydir_strrchr _tcsrchr -#define _tinydir_strncmp _tcsncmp -#else -#define _tinydir_char_t char -#define TINYDIR_STRING(s) s -#define _tinydir_strlen strlen -#define _tinydir_strcpy strcpy -#define _tinydir_strcat strcat -#define _tinydir_strcmp strcmp -#define _tinydir_strrchr strrchr -#define _tinydir_strncmp strncmp -#endif - -#if (defined _MSC_VER || defined __MINGW32__) -#include -#define _TINYDIR_PATH_MAX MAX_PATH -#elif defined __linux__ -#include -#define _TINYDIR_PATH_MAX PATH_MAX -#else -#define _TINYDIR_PATH_MAX 4096 -#endif - -#ifdef _MSC_VER -/* extra chars for the "\\*" mask */ -# define _TINYDIR_PATH_EXTRA 2 -#else -# define _TINYDIR_PATH_EXTRA 0 -#endif - -#define _TINYDIR_FILENAME_MAX 256 - -#if (defined _MSC_VER || defined __MINGW32__) -#define _TINYDIR_DRIVE_MAX 3 -#endif - -#ifdef _MSC_VER -# define _TINYDIR_FUNC static __inline -#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L -# define _TINYDIR_FUNC static __inline__ -#else -# define _TINYDIR_FUNC static inline -#endif - -/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ -#ifdef TINYDIR_USE_READDIR_R - -/* readdir_r is a POSIX-only function, and may not be available under various - * environments/settings, e.g. MinGW. Use readdir fallback */ -#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ - _POSIX_SOURCE -# define _TINYDIR_HAS_READDIR_R -#endif -#if _POSIX_C_SOURCE >= 200112L -# define _TINYDIR_HAS_FPATHCONF -# include -#endif -#if _BSD_SOURCE || _SVID_SOURCE || \ - (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) -# define _TINYDIR_HAS_DIRFD -# include -#endif -#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ - defined _PC_NAME_MAX -# define _TINYDIR_USE_FPATHCONF -#endif -#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ - !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) -# define _TINYDIR_USE_READDIR -#endif - -/* Use readdir by default */ -#else -# define _TINYDIR_USE_READDIR -#endif - -/* MINGW32 has two versions of dirent, ASCII and UNICODE*/ -#ifndef _MSC_VER -#if (defined __MINGW32__) && (defined _UNICODE) -#define _TINYDIR_DIR _WDIR -#define _tinydir_dirent _wdirent -#define _tinydir_opendir _wopendir -#define _tinydir_readdir _wreaddir -#define _tinydir_closedir _wclosedir -#else -#define _TINYDIR_DIR DIR -#define _tinydir_dirent dirent -#define _tinydir_opendir opendir -#define _tinydir_readdir readdir -#define _tinydir_closedir closedir -#endif -#endif - -/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ -#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) -#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) -#else -#error "Either define both alloc and free or none of them!" -#endif - -#if !defined(_TINYDIR_MALLOC) -#define _TINYDIR_MALLOC(_size) malloc(_size) -#define _TINYDIR_FREE(_ptr) free(_ptr) -#endif /* !defined(_TINYDIR_MALLOC) */ - -typedef struct tinydir_file -{ - _tinydir_char_t path[_TINYDIR_PATH_MAX]; - _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; - _tinydir_char_t *extension; - int is_dir; - int is_reg; - -#ifndef _MSC_VER -#ifdef __MINGW32__ - struct _stat _s; -#else - struct stat _s; -#endif -#endif -} tinydir_file; - -typedef struct tinydir_dir -{ - _tinydir_char_t path[_TINYDIR_PATH_MAX]; - int has_next; - size_t n_files; - - tinydir_file *_files; -#ifdef _MSC_VER - HANDLE _h; - WIN32_FIND_DATA _f; -#else - _TINYDIR_DIR *_d; - struct _tinydir_dirent *_e; -#ifndef _TINYDIR_USE_READDIR - struct _tinydir_dirent *_ep; -#endif -#endif -} tinydir_dir; - - -/* declarations */ - -_TINYDIR_FUNC -int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); -_TINYDIR_FUNC -int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); -_TINYDIR_FUNC -void tinydir_close(tinydir_dir *dir); - -_TINYDIR_FUNC -int tinydir_next(tinydir_dir *dir); -_TINYDIR_FUNC -int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); -_TINYDIR_FUNC -int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); -_TINYDIR_FUNC -int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); - -_TINYDIR_FUNC -int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); -_TINYDIR_FUNC -void _tinydir_get_ext(tinydir_file *file); -_TINYDIR_FUNC -int _tinydir_file_cmp(const void *a, const void *b); -#ifndef _MSC_VER -#ifndef _TINYDIR_USE_READDIR -_TINYDIR_FUNC -size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); -#endif -#endif - - -/* definitions*/ - -_TINYDIR_FUNC -int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) -{ -#ifndef _MSC_VER -#ifndef _TINYDIR_USE_READDIR - int error; - int size; /* using int size */ -#endif -#else - _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; -#endif - _tinydir_char_t *pathp; - - if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) - { - errno = EINVAL; - return -1; - } - if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) - { - errno = ENAMETOOLONG; - return -1; - } - - /* initialise dir */ - dir->_files = NULL; -#ifdef _MSC_VER - dir->_h = INVALID_HANDLE_VALUE; -#else - dir->_d = NULL; -#ifndef _TINYDIR_USE_READDIR - dir->_ep = NULL; -#endif -#endif - tinydir_close(dir); - - _tinydir_strcpy(dir->path, path); - /* Remove trailing slashes */ - pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; - while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) - { - *pathp = TINYDIR_STRING('\0'); - pathp++; - } -#ifdef _MSC_VER - _tinydir_strcpy(path_buf, dir->path); - _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); - dir->_h = FindFirstFile(path_buf, &dir->_f); - if (dir->_h == INVALID_HANDLE_VALUE) - { - errno = ENOENT; -#else - dir->_d = _tinydir_opendir(path); - if (dir->_d == NULL) - { -#endif - goto bail; - } - - /* read first file */ - dir->has_next = 1; -#ifndef _MSC_VER -#ifdef _TINYDIR_USE_READDIR - dir->_e = _tinydir_readdir(dir->_d); -#else - /* allocate dirent buffer for readdir_r */ - size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ - if (size == -1) return -1; - dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); - if (dir->_ep == NULL) return -1; - - error = readdir_r(dir->_d, dir->_ep, &dir->_e); - if (error != 0) return -1; -#endif - if (dir->_e == NULL) - { - dir->has_next = 0; - } -#endif - - return 0; - - bail: - tinydir_close(dir); - return -1; -} - -_TINYDIR_FUNC -int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) -{ - /* Count the number of files first, to pre-allocate the files array */ - size_t n_files = 0; - if (tinydir_open(dir, path) == -1) - { - return -1; - } - while (dir->has_next) - { - n_files++; - if (tinydir_next(dir) == -1) - { - goto bail; - } - } - tinydir_close(dir); - - if (tinydir_open(dir, path) == -1) - { - return -1; - } - - dir->n_files = 0; - dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); - if (dir->_files == NULL) - { - goto bail; - } - while (dir->has_next) - { - tinydir_file *p_file; - dir->n_files++; - - p_file = &dir->_files[dir->n_files - 1]; - if (tinydir_readfile(dir, p_file) == -1) - { - goto bail; - } - - if (tinydir_next(dir) == -1) - { - goto bail; - } - - /* Just in case the number of files has changed between the first and - second reads, terminate without writing into unallocated memory */ - if (dir->n_files == n_files) - { - break; - } - } - - qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); - - return 0; - - bail: - tinydir_close(dir); - return -1; -} - -_TINYDIR_FUNC -void tinydir_close(tinydir_dir *dir) -{ - if (dir == NULL) - { - return; - } - - memset(dir->path, 0, sizeof(dir->path)); - dir->has_next = 0; - dir->n_files = 0; - _TINYDIR_FREE(dir->_files); - dir->_files = NULL; -#ifdef _MSC_VER - if (dir->_h != INVALID_HANDLE_VALUE) - { - FindClose(dir->_h); - } - dir->_h = INVALID_HANDLE_VALUE; -#else - if (dir->_d) - { - _tinydir_closedir(dir->_d); - } - dir->_d = NULL; - dir->_e = NULL; -#ifndef _TINYDIR_USE_READDIR - _TINYDIR_FREE(dir->_ep); - dir->_ep = NULL; -#endif -#endif -} - -_TINYDIR_FUNC -int tinydir_next(tinydir_dir *dir) -{ - if (dir == NULL) - { - errno = EINVAL; - return -1; - } - if (!dir->has_next) - { - errno = ENOENT; - return -1; - } - -#ifdef _MSC_VER - if (FindNextFile(dir->_h, &dir->_f) == 0) -#else -#ifdef _TINYDIR_USE_READDIR - dir->_e = _tinydir_readdir(dir->_d); -#else - if (dir->_ep == NULL) - { - return -1; - } - if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) - { - return -1; - } -#endif - if (dir->_e == NULL) -#endif - { - dir->has_next = 0; -#ifdef _MSC_VER - if (GetLastError() != ERROR_SUCCESS && - GetLastError() != ERROR_NO_MORE_FILES) - { - tinydir_close(dir); - errno = EIO; - return -1; - } -#endif - } - - return 0; -} - -_TINYDIR_FUNC -int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) -{ - if (dir == NULL || file == NULL) - { - errno = EINVAL; - return -1; - } -#ifdef _MSC_VER - if (dir->_h == INVALID_HANDLE_VALUE) -#else - if (dir->_e == NULL) -#endif - { - errno = ENOENT; - return -1; - } - if (_tinydir_strlen(dir->path) + - _tinydir_strlen( -#ifdef _MSC_VER - dir->_f.cFileName -#else - dir->_e->d_name -#endif - ) + 1 + _TINYDIR_PATH_EXTRA >= - _TINYDIR_PATH_MAX) - { - /* the path for the file will be too long */ - errno = ENAMETOOLONG; - return -1; - } - if (_tinydir_strlen( -#ifdef _MSC_VER - dir->_f.cFileName -#else - dir->_e->d_name -#endif - ) >= _TINYDIR_FILENAME_MAX) - { - errno = ENAMETOOLONG; - return -1; - } - - _tinydir_strcpy(file->path, dir->path); - _tinydir_strcat(file->path, TINYDIR_STRING("/")); - _tinydir_strcpy(file->name, - #ifdef _MSC_VER - dir->_f.cFileName -#else - dir->_e->d_name -#endif - ); - _tinydir_strcat(file->path, file->name); -#ifndef _MSC_VER -#ifdef __MINGW32__ - if (_tstat( -#else - if (stat( -#endif - file->path, &file->_s) == -1) - { - return -1; - } -#endif - _tinydir_get_ext(file); - - file->is_dir = -#ifdef _MSC_VER - !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); -#else - S_ISDIR(file->_s.st_mode); -#endif - file->is_reg = -#ifdef _MSC_VER - !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || - ( - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && -#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && -#endif -#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && -#endif - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && - !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); -#else - S_ISREG(file->_s.st_mode); -#endif - - return 0; -} - -_TINYDIR_FUNC -int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) -{ - if (dir == NULL || file == NULL) - { - errno = EINVAL; - return -1; - } - if (i >= dir->n_files) - { - errno = ENOENT; - return -1; - } - - memcpy(file, &dir->_files[i], sizeof(tinydir_file)); - _tinydir_get_ext(file); - - return 0; -} - -_TINYDIR_FUNC -int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) -{ - _tinydir_char_t path[_TINYDIR_PATH_MAX]; - if (dir == NULL) - { - errno = EINVAL; - return -1; - } - if (i >= dir->n_files || !dir->_files[i].is_dir) - { - errno = ENOENT; - return -1; - } - - _tinydir_strcpy(path, dir->_files[i].path); - tinydir_close(dir); - if (tinydir_open_sorted(dir, path) == -1) - { - return -1; - } - - return 0; -} - -/* Open a single file given its path */ -_TINYDIR_FUNC -int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) -{ - tinydir_dir dir; - int result = 0; - int found = 0; - _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; - _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX]; - _tinydir_char_t *dir_name; - _tinydir_char_t *base_name; -#if (defined _MSC_VER || defined __MINGW32__) - _tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX]; - _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; -#endif - - if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) - { - errno = EINVAL; - return -1; - } - if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) - { - errno = ENAMETOOLONG; - return -1; - } - - /* Get the parent path */ -#if (defined _MSC_VER || defined __MINGW32__) - #if ((defined _MSC_VER) && (_MSC_VER >= 1400)) - _tsplitpath_s( - path, - drive_buf, _TINYDIR_DRIVE_MAX, - dir_name_buf, _TINYDIR_FILENAME_MAX, - file_name_buf, _TINYDIR_FILENAME_MAX, - ext_buf, _TINYDIR_FILENAME_MAX); -#else - _tsplitpath( - path, - drive_buf, - dir_name_buf, - file_name_buf, - ext_buf); -#endif - -/* _splitpath_s not work fine with only filename and widechar support */ -#ifdef _UNICODE - if (drive_buf[0] == L'\xFEFE') - drive_buf[0] = '\0'; - if (dir_name_buf[0] == L'\xFEFE') - dir_name_buf[0] = '\0'; -#endif - - if (errno) - { - errno = EINVAL; - return -1; - } - /* Emulate the behavior of dirname by returning "." for dir name if it's - empty */ - if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') - { - _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); - } - /* Concatenate the drive letter and dir name to form full dir name */ - _tinydir_strcat(drive_buf, dir_name_buf); - dir_name = drive_buf; - /* Concatenate the file name and extension to form base name */ - _tinydir_strcat(file_name_buf, ext_buf); - base_name = file_name_buf; -#else - _tinydir_strcpy(dir_name_buf, path); - dir_name = dirname(dir_name_buf); - _tinydir_strcpy(file_name_buf, path); - base_name =basename(file_name_buf); -#endif - - /* Open the parent directory */ - if (tinydir_open(&dir, dir_name) == -1) - { - return -1; - } - - /* Read through the parent directory and look for the file */ - while (dir.has_next) - { - if (tinydir_readfile(&dir, file) == -1) - { - result = -1; - goto bail; - } - if (_tinydir_strcmp(file->name, base_name) == 0) - { - /* File found */ - found = 1; - break; - } - tinydir_next(&dir); - } - if (!found) - { - result = -1; - errno = ENOENT; - } - - bail: - tinydir_close(&dir); - return result; -} - -_TINYDIR_FUNC -void _tinydir_get_ext(tinydir_file *file) -{ - _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); - if (period == NULL) - { - file->extension = &(file->name[_tinydir_strlen(file->name)]); - } - else - { - file->extension = period + 1; - } -} - -_TINYDIR_FUNC -int _tinydir_file_cmp(const void *a, const void *b) -{ - const tinydir_file *fa = (const tinydir_file *)a; - const tinydir_file *fb = (const tinydir_file *)b; - if (fa->is_dir != fb->is_dir) - { - return -(fa->is_dir - fb->is_dir); - } - return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); -} - -#ifndef _MSC_VER -#ifndef _TINYDIR_USE_READDIR -/* -The following authored by Ben Hutchings -from https://womble.decadent.org.uk/readdir_r-advisory.html -*/ -/* Calculate the required buffer size (in bytes) for directory * -* entries read from the given directory handle. Return -1 if this * -* this cannot be done. * -* * -* This code does not trust values of NAME_MAX that are less than * -* 255, since some systems (including at least HP-UX) incorrectly * -* define it to be a smaller value. */ -_TINYDIR_FUNC -size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) -{ - long name_max; - size_t name_end; - /* parameter may be unused */ - (void)dirp; - -#if defined _TINYDIR_USE_FPATHCONF - name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); - if (name_max == -1) -#if defined(NAME_MAX) - name_max = (NAME_MAX > 255) ? NAME_MAX : 255; -#else - return (size_t)(-1); -#endif -#elif defined(NAME_MAX) - name_max = (NAME_MAX > 255) ? NAME_MAX : 255; -#else -#error "buffer size for readdir_r cannot be determined" -#endif - name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; - return (name_end > sizeof(struct _tinydir_dirent) ? - name_end : sizeof(struct _tinydir_dirent)); -} -#endif -#endif - -#ifdef __cplusplus -} -#endif - -# if defined (_MSC_VER) -# pragma warning(pop) -# endif - -#endif \ No newline at end of file