diff --git a/.github/workflows/cmake-cabextract.yml b/.github/workflows/cmake-cabextract.yml new file mode 100644 index 0000000..6bab96b --- /dev/null +++ b/.github/workflows/cmake-cabextract.yml @@ -0,0 +1,141 @@ +name: CMake Build Cabextract + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + VCPKG_GIT_REF: 8a9a97315aefb3f8bc5d81bf66ca0025938b9c91 + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + # Restore from cache the previously built ports. If cache-miss, download, build vcpkg ports. + - name: Restore vcpkg ports from cache or install vcpkg + # Download and build vcpkg, without installing any port. If content is cached already, it is a no-op. + uses: lukka/run-vcpkg@v5 + id: runvcpkg + with: + vcpkgArguments: "libiconv" + vcpkgGitCommitId: "${{ env.VCPKG_GIT_REF }}" + vcpkgTriplet: "x64-windows" + + - name: Print the VCPKG_ROOT & VCPKG_TRIPLET (for debugging) + shell: bash + run: echo "'${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_ROOT_OUT }}' '${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_TRIPLET_OUT }}' " + + - name: dir the VCPKG_ROOT + run: dir ${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_ROOT_OUT }} + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/cabextract-build + + - name: Run CMake+Ninja with triplet (cmd) + uses: lukka/run-cmake@main + id: runcmake_cmd + with: + cmakeGenerator: "Ninja" # Visual Studio 15 2017 + cmakeListsOrSettingsJson: "CMakeListsTxtBasic" + cmakeListsTxtPath: "${{runner.workspace}}/libmspack/cabextract/CMakeLists.txt" + useVcpkgToolchainFile: true + cmakeAppendedArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -- -v' + cmakeBuildType: "${{ env.BUILD_TYPE }}" + vcpkgTriplet: ${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_TRIPLET_OUT }} + buildDirectory: "${{runner.workspace}}/cabextract-build" + + - name: Test + working-directory: ${{runner.workspace}}/cabextract-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V + + build-macos: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/cabextract-build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + working-directory: ${{runner.workspace}}/cabextract-build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: + cmake ${{runner.workspace}}/libmspack/libmspack -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + shell: bash + working-directory: ${{runner.workspace}}/cabextract-build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config ${{ env.BUILD_TYPE }} + + - name: Test + shell: bash + working-directory: ${{runner.workspace}}/cabextract-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V + + build-ubuntu: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/cabextract-build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + working-directory: ${{runner.workspace}}/cabextract-build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: + cmake ${{runner.workspace}}/libmspack/libmspack -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + shell: bash + working-directory: ${{runner.workspace}}/cabextract-build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config ${{ env.BUILD_TYPE }} + + - name: Test + shell: bash + working-directory: ${{runner.workspace}}/cabextract-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V diff --git a/.github/workflows/cmake-libmspack.yml b/.github/workflows/cmake-libmspack.yml new file mode 100644 index 0000000..6e7898c --- /dev/null +++ b/.github/workflows/cmake-libmspack.yml @@ -0,0 +1,141 @@ +name: CMake Build Libmspack + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + VCPKG_GIT_REF: 8a9a97315aefb3f8bc5d81bf66ca0025938b9c91 + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + # Restore from cache the previously built ports. If cache-miss, download, build vcpkg ports. + - name: Restore vcpkg ports from cache or install vcpkg + # Download and build vcpkg, without installing any port. If content is cached already, it is a no-op. + uses: lukka/run-vcpkg@v5 + id: runvcpkg + with: + vcpkgArguments: "libiconv" + vcpkgGitCommitId: "${{ env.VCPKG_GIT_REF }}" + vcpkgTriplet: "x64-windows" + + - name: Print the VCPKG_ROOT & VCPKG_TRIPLET (for debugging) + shell: bash + run: echo "'${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_ROOT_OUT }}' '${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_TRIPLET_OUT }}' " + + - name: dir the VCPKG_ROOT + run: dir ${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_ROOT_OUT }} + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/libmspack-build + + - name: Run CMake+Ninja with triplet (cmd) + uses: lukka/run-cmake@main + id: runcmake_cmd + with: + cmakeGenerator: "Ninja" # Visual Studio 15 2017 + cmakeListsOrSettingsJson: "CMakeListsTxtBasic" + cmakeListsTxtPath: "${{runner.workspace}}/libmspack/libmspack/CMakeLists.txt" + useVcpkgToolchainFile: true + cmakeAppendedArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -- -v' + cmakeBuildType: "${{ env.BUILD_TYPE }}" + vcpkgTriplet: ${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_TRIPLET_OUT }} + buildDirectory: "${{runner.workspace}}/libmspack-build" + + - name: Test + working-directory: ${{runner.workspace}}/libmspack-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V + + build-macos: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/libmspack-build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + working-directory: ${{runner.workspace}}/libmspack-build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: + cmake ${{runner.workspace}}/libmspack/libmspack -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + shell: bash + working-directory: ${{runner.workspace}}/libmspack-build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config ${{ env.BUILD_TYPE }} + + - name: Test + shell: bash + working-directory: ${{runner.workspace}}/libmspack-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V + + build-ubuntu: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - uses: lukka/get-cmake@latest + + - name: Create Build Directory + shell: bash + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/libmspack-build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + working-directory: ${{runner.workspace}}/libmspack-build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: + cmake ${{runner.workspace}}/libmspack/libmspack -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} + + - name: Build + shell: bash + working-directory: ${{runner.workspace}}/libmspack-build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config ${{ env.BUILD_TYPE }} + + - name: Test + shell: bash + working-directory: ${{runner.workspace}}/libmspack-build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{ env.BUILD_TYPE }} -V diff --git a/cabextract/.gitignore b/cabextract/.gitignore index ed26884..d912544 100644 --- a/cabextract/.gitignore +++ b/cabextract/.gitignore @@ -1,26 +1,111 @@ -*.a -*.o +/build + +# CabExtract specific COPYING INSTALL -/Makefile -Makefile.in -aclocal.m4 -ar-lib -autom4te.cache cabextract cabextract-* cabextract-*.tar.gz cabextract.spec -compile -config.guess -config.h -config.h.in -config.log -config.status -config.sub -configure -install-sh -missing -stamp-h1 -test-driver test-suite.log + +# Logs +*.log + +# 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 + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + +# http://www.gnu.org/software/automake +.deps +.dirstamp +.libs +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap + +# http://www.gnu.org/software/autoconf +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ +/ltmain.sh + +# http://www.gnu.org/software/texinfo +/texinfo.tex + +# http://www.gnu.org/software/m4/ +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 diff --git a/cabextract/CMakeLists.txt b/cabextract/CMakeLists.txt new file mode 100644 index 0000000..c8ab402 --- /dev/null +++ b/cabextract/CMakeLists.txt @@ -0,0 +1,269 @@ +cmake_minimum_required(VERSION 3.12) + +project(cabextract + VERSION 1.9.1 + DESCRIPTION "A program to extract Microsoft Cabinet files." + LANGUAGES C) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) + +include(CMakeOptions.txt) + +# Do not disable assertions based on CMAKE_BUILD_TYPE. +foreach(_build_type Release MinSizeRel RelWithDebInfo) + foreach(_lang C) + string(TOUPPER CMAKE_${_lang}_FLAGS_${_build_type} _var) + string(REGEX REPLACE "(^|)[/-]D *NDEBUG($|)" " " ${_var} "${${_var}}") + endforeach() +endforeach() + +# Support the latest c++ standard available. +include(ExtractValidFlags) +# Determine if _FILE_OFFSET_BITS 64 needs to be set to handle large files. +include(CheckFileOffsetBits) +# Define inline macro as needed. +include(TestInline) + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE) + + # Include "None" as option to disable any additional (optimization) flags, + # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by + # default). These strings are presented in cmake-gui. + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + None Debug Release MinSizeRel RelWithDebInfo) +endif() + +include(GNUInstallDirs) + +# Always use '-fPIC'/'-fPIE' option. +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Checks for header files. +include(CheckIncludeFile) +check_include_file(dlfcn.h HAVE_DLFCN_H) +check_include_file(inttypes.h HAVE_INTTYPES_H) +check_include_file(stdint.h HAVE_STDINT_H) +check_include_file(stddef.h HAVE_STDDEF_H) +check_include_file(limits.h HAVE_LIMITS_H) +check_include_file(ctype.h HAVE_CTYPE_H) +check_include_file(wctype.h HAVE_WCTYPE_H) +check_include_file(errno.h HAVE_ERRNO_H) +check_include_file(dirent.h HAVE_DIRENT_H) +check_include_file(sys/types.h HAVE_SYS_TYPES_H) +check_include_file(sys/stat.h HAVE_SYS_STAT_H) +check_include_file(fnmatch.h HAVE_FNMATCH_H) +check_include_file(locale.h HAVE_LOCALE_H) +check_include_file(stdarg.h HAVE_STDARG_H) +check_include_file(stdlib.h HAVE_STDLIB_H) +check_include_file(string.h HAVE_STRING_H) +check_include_file(strings.h HAVE_STRINGS_H) +check_include_file(sys/time.h HAVE_SYS_TIME_H) +check_include_file(float.h HAVE_FLOAT_H) +check_include_file(unistd.h HAVE_UNISTD_H) + +include(CheckIncludeFiles) +check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" StandardHeadersExist) +if(StandardHeadersExist) + set(STDC_HEADERS 1 CACHE INTERNAL "System has ANSI C header files") +else() + message(STATUS "ANSI C header files - not found") + set(STDC_HEADERS 0 CACHE INTERNAL "System has ANSI C header files") +endif() + + +# Checks for library functions. +include(CheckFunctionExists) +check_function_exists(fseeko HAVE_FSEEKO) +check_function_exists(mkdir HAVE_MKDIR) +check_function_exists(_mkdir HAVE__MKDIR) +check_function_exists(towlower HAVE_TOWLOWER) + +# Check size of types. +include(CheckTypeSize) +check_type_size("off_t" SIZEOF_OFF_T) +if(NOT SIZEOF_OFF_T) + # Set it to "long int" to match the behavior of AC_TYPE_OFF_T (autotools). + set(OFF_T_DEF "typedef int off_t;") +endif() + +check_type_size("size_t" SIZEOF_SIZE_T) +if(NOT SIZEOF_SIZE_T) + # Set it to "unsigned int" to match the behavior of AC_TYPE_SIZE_T (autotools). + set(SIZE_T_DEF "typedef int size_t;") +endif() + +check_type_size("ssize_t" SIZEOF_SSIZE_T) +if(NOT SIZEOF_SSIZE_T) + # Set it to "int" to match the behavior of AC_TYPE_SSIZE_T (autotools). + set(SSIZE_T_DEF "typedef int ssize_t;") +endif() + +check_type_size("mode_t" SIZEOF_MODE_T) +if(NOT SIZEOF_MODE_T) + # Set it to "int" to match the behavior of AC_TYPE_MODE_T (autotools). + set(MODE_T_DEF "typedef int mode_t;") +endif() + +# Compile tests +try_compile(MKDIR_TAKES_ONE_ARG ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/compiletest_mkdir.c) + +# Check if big-endian +include(TestBigEndian) +TEST_BIG_ENDIAN(WORDS_BIGENDIAN) + +set(WARNCFLAGS) +if(CMAKE_C_COMPILER_ID MATCHES "MSVC") + if(ENABLE_WERROR) + set(WARNCFLAGS /WX) + endif() +else() + if(ENABLE_WERROR) + extract_valid_c_flags(WARNCFLAGS -Werror) + endif() + + # For C compiler + extract_valid_c_flags(WARNCFLAGS + -Wall + -Wextra + -Wmissing-prototypes + -Wstrict-prototypes + -Wmissing-declarations + -Wpointer-arith + -Wdeclaration-after-statement + -Wformat-security + -Wwrite-strings + -Wshadow + -Winline + -Wnested-externs + -Wfloat-equal + -Wundef + -Wendif-labels + -Wempty-body + -Wcast-align + -Wclobbered + -Wvla + -Wpragmas + -Wunreachable-code + -Waddress + -Wattributes + -Wdiv-by-zero + -Wshorten-64-to-32 + -Wconversion + -Wextended-offsetof + -Wformat-nonliteral + -Wlanguage-extension-token + -Wmissing-field-initializers + -Wmissing-noreturn + -Wmissing-variable-declarations + # -Wpadded # Not used because we cannot change public structs + -Wsign-conversion + # -Wswitch-enum # Not used because this basically disallows default case + -Wunreachable-code-break + -Wunused-macros + -Wunused-parameter + -Wredundant-decls + -Wheader-guard + #-Wno-format-nonliteral # This is required because we pass format string as "const char*. + -Wno-unused-parameter + -Wno-unused-result + ) +endif() + +if(ENABLE_DEBUG) + set(DEBUGBUILD 1) +endif() + +# autotools-compatible names +# Sphinx expects relative paths in the .rst files. Use the fact that the files +# below are all one directory level deep. +file(RELATIVE_PATH top_srcdir ${CMAKE_CURRENT_BINARY_DIR}/dir ${CMAKE_CURRENT_SOURCE_DIR}) +file(RELATIVE_PATH top_builddir ${CMAKE_CURRENT_BINARY_DIR}/dir ${CMAKE_CURRENT_BINARY_DIR}) +set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) +set(abs_top_builddir ${CMAKE_CURRENT_BINARY_DIR}) + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix ${CMAKE_INSTALL_PREFIX}) +set(bindir ${CMAKE_INSTALL_FULL_BINDIR}) +set(sbindir ${CMAKE_INSTALL_FULL_SBINDIR}) +set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) +set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) +set(VERSION ${PROJECT_VERSION}) + +# +# External library dependencies +# +find_package(ICONV) +if(ICONV_FOUND) + set(HAVE_ICONV 1) +endif() + +# Generate config.h +add_definitions(-DHAVE_CONFIG_H) +configure_file(config.h.in.cmake config.h) +include_directories(${PROJECT_BINARY_DIR}) + +# +# The build targets. +# +if(NOT ENABLE_EXTERNAL_MSPACK) + include_directories(../libmspack/mspack) + add_subdirectory(../libmspack ${PROJECT_BINARY_DIR}/libmspack) +else() + find_package(MSPack) +endif() + +add_executable(cabextract) +target_sources(cabextract + PRIVATE + src/cabextract.c + getopt.c getopt.h + getopt1.c + md5.h md5.c) +if(NOT WIN32) + target_include_directories(cabextract PRIVATE ${PROJECT_SOURCE_DIR}) +else() + target_include_directories(cabextract PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/win32) + target_sources(cabextract PRIVATE win32/dirent.h) + target_link_libraries(cabextract Shlwapi.lib) +endif() +if(HAVE_ICONV) + target_link_libraries( cabextract ICONV::Iconv ) +endif() + +target_include_directories(cabextract PRIVATE ${PROJECT_SOURCE_DIR}) +target_link_libraries(cabextract MSPack::mspack) +install(TARGETS cabextract DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if(NOT WIN32) + add_executable(cabinfo) + target_sources(cabinfo + PRIVATE + src/cabinfo.c) + target_include_directories(cabinfo PRIVATE ${PROJECT_SOURCE_DIR}) + target_link_libraries(cabinfo MSPack::mspack) + + enable_testing() + add_subdirectory(test) +endif() + +# +# The Summary Info. +# +string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type) +message(STATUS "Summary of build options: + + Package version: ${VERSION} + Install prefix: ${CMAKE_INSTALL_PREFIX} + Target system: ${CMAKE_SYSTEM_NAME} + Compiler: + Build type: ${CMAKE_BUILD_TYPE} + C compiler: ${CMAKE_C_COMPILER} + CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} + WARNCFLAGS: ${WARNCFLAGS} + Features: + External mspack: ${ENABLE_EXTERNAL_MSPACK} + Text conversion: + iconv ${ICONV_INCLUDE_DIRS} + ${ICONV_LIBRARIES} +") diff --git a/cabextract/CMakeOptions.txt b/cabextract/CMakeOptions.txt new file mode 100644 index 0000000..43e701b --- /dev/null +++ b/cabextract/CMakeOptions.txt @@ -0,0 +1,7 @@ +# Features that can be enabled for cmake (see CMakeLists.txt) + +option(ENABLE_WERROR "Turn on compile time warnings as errors") + +option(ENABLE_DEBUG "Turn on debug output") + +option(ENABLE_EXTERNAL_MSPACK "Use a system-installed mspack instead of the bundled mspack") diff --git a/cabextract/README b/cabextract/README deleted file mode 100644 index 43306b6..0000000 --- a/cabextract/README +++ /dev/null @@ -1,46 +0,0 @@ -cabextract 1.9.1 - a program to extract Microsoft Cabinet files. -(C) 2000-2019 Stuart Caie -This is free software with ABSOLUTELY NO WARRANTY. - -Cabinet (.CAB) files are a form of archive, which Microsoft use to -distribute their software, and things like Windows Font Packs. The -cabextract program unpacks these files. - -For more information, see https://www.cabextract.org.uk/ -or run the command 'cabextract --help'. - -Microsoft cabinet files should not be confused with InstallShield cabinet -files. InstallShield files are generally called "_sys.cab", "data1.hdr" -"data1.cab", "data2.cab" and so on, and are found in the same directory as -"setup.exe". They begin with the magic characters "ISc(" rather than -"MSCF". cabextract will print the message "This is probably an -InstallShield file." when it finds such a file. The file "doc/magic" in -the cabextract source archive includes additional file-identification -rules for the UNIX file(1) command, which distinguishes between Microsoft -and InstallShield cabinet files. - -Example usage: - -Extracting files from a cabinet file: -$ cabextract wibble.cab - -Extracting files from an executable which contains a cabinet file: -$ cabextract wibble.exe -[cabextract will automatically search executables for embedded cabinets] - -Extracting files from a set of cabinet files; wib01.cab, wib02.cab, ...: -$ cabextract wib01.cab -[cabextract will automatically get the names of the other files] - -Extracting files to a directory of your choice (in this case, 'boogie'): -$ cabextract -d boogie wibble.cab -[cabextract will create the directory if it does not already exist] - -Extracting files that match a filename pattern: -$ cabextract -F *.avi -F *.mpg movies.cab - -Listing files from a cabinet file: -$ cabextract -l wibble.cab - -Testing the integrity of a cabinet file, without extracting it: -$ cabextract -t wibble.cab diff --git a/cabextract/README.md b/cabextract/README.md new file mode 100644 index 0000000..1c162c3 --- /dev/null +++ b/cabextract/README.md @@ -0,0 +1,186 @@ +# cabextract 1.9.1 + + + +A program to extract Microsoft Cabinet files. + +(C) 2000-2019 Stuart Caie +This is free software with ABSOLUTELY NO WARRANTY. + +Cabinet (.CAB) files are a form of archive, which Microsoft use to +distribute their software, and things like Windows Font Packs. The +cabextract program unpacks these files. + +For more information, see https://www.cabextract.org.uk/ +or run the command 'cabextract --help'. + +Microsoft cabinet files should not be confused with InstallShield cabinet +files. InstallShield files are generally called "_sys.cab", "data1.hdr" +"data1.cab", "data2.cab" and so on, and are found in the same directory as +"setup.exe". They begin with the magic characters "ISc(" rather than +"MSCF". cabextract will print the message "This is probably an +InstallShield file." when it finds such a file. The file "doc/magic" in +the cabextract source archive includes additional file-identification +rules for the UNIX file(1) command, which distinguishes between Microsoft +and InstallShield cabinet files. + +## Example usage + +Extracting files from a cabinet file: + +```sh +$ cabextract wibble.cab +``` + +Extracting files from an executable which contains a cabinet file: + +```sh +$ cabextract wibble.exe +[cabextract will automatically search executables for embedded cabinets] +``` + +Extracting files from a set of cabinet files; wib01.cab, wib02.cab, ...: + +```sh +$ cabextract wib01.cab +[cabextract will automatically get the names of the other files] +``` + +Extracting files to a directory of your choice (in this case, 'boogie'): + +```sh +$ cabextract -d boogie wibble.cab +[cabextract will create the directory if it does not already exist] +``` + +Extracting files that match a filename pattern: + +```sh +$ cabextract -F *.avi -F *.mpg movies.cab +``` + +Listing files from a cabinet file: + +```sh +$ cabextract -l wibble.cab +``` + +Testing the integrity of a cabinet file, without extracting it: + +```sh +$ cabextract -t wibble.cab +``` + +## BUILDING / INSTALLING + +### Autotools + +```sh +./configure +make +make install +``` + +This will install the cabextract program. +Some other libraries and executables are built, but not installed. + +If building from the Git repository, running `rebuild.sh` will create all the +auto-generated files, then run `./configure && make`. Running `cleanup.sh` will +perform a thorough clean, deleting all auto-generated files. + +In addition to gcc, you also need the following for building from repository: + +- at least autoconf 2.57 +- at least automake 1.11 +- libtool + +### CMake + +cabextract can be compiled with the [CMake] build system. +The following instructions recommend compiling in a `build` subdirectory. + +#### Basic Release build + +```sh +mkdir build && cd build +cmake .. +cmake --build . --config Release +``` + +#### Basic Debug build + +```sh +mkdir build && cd build +cmake .. -DCMAKE_BUILD_TYPE="Debug" +cmake --build . --config Debug +``` + +#### Build and install to a specific install location (prefix) + +```sh +mkdir build && cd build +cmake -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/install .. +cmake --build . --target install +``` + +Windows (Powershell) +```ps1 +mkdir build && cd build +cmake .. -DCMAKE_INSTALL_PREFIX:PATH=$(Get-Location)/install +cmake --build . --target install +``` + +#### Build and run tests + +- `-V`: Verbose +- `-C`: Required for Windows builds + +```sh +mkdir build && cd build +cmake .. +cmake --build . --config Debug +ctest -C Debug -V +``` + +Or try `ctest -C Debug -VV --output-on-failure` for extra verbose output. + +#### Build, test, and install in Release mode + +```sh +mkdir build && cd build +cmake .. -DENABLE_EXAMPLES=ON -DENABLE_STATIC_LIB=ON -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/install -DENABLE_DOCS=ON +cmake --build . --config Release +ctest -C Release -V +cmake --build . --config Release --target install doxygen +``` + +Windows (Powershell): +```ps1 +mkdir build ; if($?) {cd build} +cmake .. -DENABLE_EXAMPLES=ON -DENABLE_STATIC_LIB=ON -DCMAKE_INSTALL_PREFIX:PATH=$(Get-Location)/install +cmake --build . --config Release +ctest -C Release -V +cmake --build . --config Release --target install +``` + +#### Windows=specific build instructions + +A build using the bundled mspack files won't work on Windows for a couple of reasons. Instead, you can build cabextract on Windows by first building libmspack and then build cabextract using the ENABLE_EXTERNAL_MSPACK option. + +In the example below, we use build and link with the static libmspack library so we don't have to copy mspack.dll into the cabextract.exe directory. + +In the libmspack directory: +```ps1 +mkdir build ; if($?) {cd build} +cmake -DCMAKE_INSTALL_PREFIX:PATH=install .. -DENABLE_STATIC_LIB=ON +cmake --build . --config Release --target install +``` + +Then in the cabextract directory something like this*: +```ps1 +mkdir build ; if($?) {cd build} +cmake .. -DENABLE_EXTERNAL_MSPACK=ON -DMSPack_INCLUDE_DIR="C:\Users\...\libmspack\build\install\include" -DMSPack_LIBRARY="C:\Users\...\libmspack\install\lib\mspack_static.lib" +cmake --build . --config Debug +.\Debug\cabextract.exe --help + +*Important*: set the `MSPack_INCLUDE_DIR` and `MSPack_LIBRARY` variables to your mspack `include` directory and `mspack_static.lib` file diff --git a/cabextract/autogen.sh b/cabextract/autogen.sh index 19bda6c..caa2ef7 100755 --- a/cabextract/autogen.sh +++ b/cabextract/autogen.sh @@ -1,5 +1,20 @@ #!/bin/sh # Runs the autoreconf tool, creating the configure script -autoreconf -i -W all -echo you can now run ./configure +BASEDIR="$( cd "$(dirname "$0")" ; pwd -P )" +echo "Generating autotools files in: $BASEDIR ..." +cd $BASEDIR + +# If this is a source checkout then call autoreconf with error as well +if test -d .git; then + WARNINGS="all,error" +else + WARNINGS="all" +fi + +autoreconf -i -f +rc=$?; if [[ $rc != 0 ]]; then + echo "Error: Failed to generate autojunk!"; exit $rc +else + echo "You can now run ./configure" +fi diff --git a/cabextract/cmake/COPYING-CMAKE-SCRIPTS b/cabextract/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cabextract/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +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 copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. diff --git a/cabextract/cmake/CheckFileOffsetBits.c b/cabextract/cmake/CheckFileOffsetBits.c new file mode 100644 index 0000000..d948fec --- /dev/null +++ b/cabextract/cmake/CheckFileOffsetBits.c @@ -0,0 +1,14 @@ +#include + +#define KB ((off_t)1024) +#define MB ((off_t)1024 * KB) +#define GB ((off_t)1024 * MB) +#define TB ((off_t)1024 * GB) +int t2[(((64 * GB -1) % 671088649) == 268434537) + && (((TB - (64 * GB -1) + 255) % 1792151290) == 305159546)? 1: -1]; + +int main() +{ + ; + return 0; +} diff --git a/cabextract/cmake/CheckFileOffsetBits.cmake b/cabextract/cmake/CheckFileOffsetBits.cmake new file mode 100644 index 0000000..8a74b9e --- /dev/null +++ b/cabextract/cmake/CheckFileOffsetBits.cmake @@ -0,0 +1,43 @@ +# - Check if _FILE_OFFSET_BITS macro needed for large files +# CHECK_FILE_OFFSET_BITS () +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# Copyright (c) 2009, Michihiro NAKAJIMA +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +#INCLUDE(CheckCSourceCompiles) + +GET_FILENAME_COMPONENT(_selfdir_CheckFileOffsetBits + "${CMAKE_CURRENT_LIST_FILE}" PATH) + +MACRO (CHECK_FILE_OFFSET_BITS) + IF(NOT DEFINED _FILE_OFFSET_BITS) + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files") + TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - needed") + ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed") + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + ENDIF(NOT DEFINED _FILE_OFFSET_BITS) + +ENDMACRO (CHECK_FILE_OFFSET_BITS) diff --git a/cabextract/cmake/ExtractValidFlags.cmake b/cabextract/cmake/ExtractValidFlags.cmake new file mode 100644 index 0000000..2081a0e --- /dev/null +++ b/cabextract/cmake/ExtractValidFlags.cmake @@ -0,0 +1,18 @@ +# Convenience function that checks the availability of certain +# C or C++ compiler flags and returns valid ones as a string. + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + +function(extract_valid_c_flags varname) + set(valid_flags) + foreach(flag IN LISTS ARGN) + string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag}) + set(flag_var "C_FLAG_${flag_var}") + check_c_compiler_flag("${flag}" "${flag_var}") + if(${flag_var}) + set(valid_flags "${valid_flags} ${flag}") + endif() + endforeach() + set(${varname} "${valid_flags}" PARENT_SCOPE) +endfunction() diff --git a/cabextract/cmake/FindICONV.cmake b/cabextract/cmake/FindICONV.cmake new file mode 100644 index 0000000..4ffa0a8 --- /dev/null +++ b/cabextract/cmake/FindICONV.cmake @@ -0,0 +1,168 @@ +# From https://github.com/Kitware/CMake/blob/master/Modules/FindIconv.cmake +# +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Mods by Micah Snyder to support systems with both libc's iconv + libconv + +#[=======================================================================[.rst: +FindICONV +--------- + +.. versionadded:: 3.11 + +This module finds the ``iconv()`` POSIX.1 functions on the system. +These functions might be provided in the regular C library or externally +in the form of an additional library. + +The following variables are provided to indicate iconv support: + +.. variable:: ICONV_FOUND + + Variable indicating if the iconv support was found. + +.. variable:: ICONV_CONST + + Variable to use when declaring "in" char* variable for the iconv() function + so that it is const if the iconv implementation expects it to be const. + +.. variable:: ICONV_INCLUDE_DIRS + + The directories containing the iconv headers. + +.. variable:: ICONV_LIBRARIES + + The iconv libraries to be linked. + +.. variable:: ICONV_IS_BUILT_IN + + A variable indicating whether iconv support is stemming from the + C library or not. Even if the C library provides `iconv()`, the presence of + an external `libiconv` implementation might lead to this being false. + +Additionally, the following :prop_tgt:`IMPORTED` target is being provided: + +.. variable:: ICONV::Iconv + + Imported target for using iconv. + +The following cache variables may also be set: + +.. variable:: ICONV_INCLUDE_DIR + + The directory containing the iconv headers. + +.. variable:: ICONV_LIBRARY + + The iconv library (if not implicitly given in the C library). + +.. note:: + On POSIX platforms, iconv might be part of the C library and the cache + variables ``ICONV_INCLUDE_DIR`` and ``ICONV_LIBRARY`` might be empty. + +#]=======================================================================] + +include(CMakePushCheckState) +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +# iconv can only be provided in libc on a POSIX system. +# If any cache variable is already set, we'll skip this test. +if(NOT DEFINED ICONV_IS_BUILT_IN) + # Check for iconv.h first. + # If it's not the built-in one, then ICONV_INCLUDE_DIR will + find_path(ICONV_INCLUDE_DIR + NAMES "iconv.h" + DOC "iconv include directory") + set(ICONV_LIBRARY_NAMES "iconv" "libiconv") + + if(UNIX AND ICONV_INCLUDE_DIR AND NOT DEFINED ICONV_LIBRARY) + cmake_push_check_state(RESET) + # We always suppress the message here: Otherwise on supported systems + # not having iconv in their C library (e.g. those using libiconv) + # would always display a confusing "Looking for iconv - not found" message + set(CMAKE_FIND_QUIETLY TRUE) + # The following code will not work, but it's sufficient to see if it compiles. + # Note: libiconv will define the iconv functions as macros, so CheckSymbolExists + # will not yield correct results. + set(ICONV_IMPLICIT_TEST_CODE + " + #include + #include + int main() { + char *a, *b; + size_t i, j; + iconv_t ic; + ic = iconv_open(\"to\", \"from\"); + iconv(ic, &a, &i, &b, &j); + iconv_close(ic); + } + " + ) + + # Make sure we're using the iconv.h we found above. This way we don't + # accidentally compile against libiconv's header later but link with only + # libc on systems that have both (eg FreeBSD with libiconv pkg installed). + set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) + + if(CMAKE_C_COMPILER_LOADED) + check_c_source_compiles("${ICONV_IMPLICIT_TEST_CODE}" ICONV_IS_BUILT_IN) + else() + check_cxx_source_compiles("${ICONV_IMPLICIT_TEST_CODE}" ICONV_IS_BUILT_IN) + endif() + cmake_pop_check_state() + else() + set(ICONV_IS_BUILT_IN FALSE) + endif() +endif() + +if(ICONV_IS_BUILT_IN) + set(ICONV_INCLUDE_DIR "" CACHE FILEPATH "iconv include directory") + set(ICONV_LIBRARY_NAMES "c") +endif() + +find_library(ICONV_LIBRARY + NAMES ${ICONV_LIBRARY_NAMES} + NAMES_PER_DIR + DOC "iconv library (potentially the C library)") + +mark_as_advanced(ICONV_INCLUDE_DIR) +mark_as_advanced(ICONV_LIBRARY) + +include(FindPackageHandleStandardArgs) +if(NOT ICONV_IS_BUILT_IN) + find_package_handle_standard_args(ICONV REQUIRED_VARS ICONV_LIBRARY ICONV_INCLUDE_DIR) +else() + find_package_handle_standard_args(ICONV REQUIRED_VARS ICONV_LIBRARY) +endif() + +if(ICONV_FOUND) + # Check if the second argument for iconv() needs to be const + set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + check_c_source_compiles(" + #include + int main() { + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } +" ICONV_SECOND_ARGUMENT_IS_CONST ) + set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") + + if(ICONV_SECOND_ARGUMENT_IS_CONST) + set(ICONV_CONST "const") + endif(ICONV_SECOND_ARGUMENT_IS_CONST) + + set(ICONV_INCLUDE_DIRS "${ICONV_INCLUDE_DIR}") + set(ICONV_LIBRARIES "${ICONV_LIBRARY}") + if(NOT TARGET ICONV::Iconv) + add_library(ICONV::Iconv INTERFACE IMPORTED) + endif() + set_property(TARGET ICONV::Iconv PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${ICONV_INCLUDE_DIRS}") + set_property(TARGET ICONV::Iconv PROPERTY INTERFACE_LINK_LIBRARIES "${ICONV_LIBRARIES}") +endif() diff --git a/cabextract/cmake/FindMSPack.cmake b/cabextract/cmake/FindMSPack.cmake new file mode 100644 index 0000000..a470e32 --- /dev/null +++ b/cabextract/cmake/FindMSPack.cmake @@ -0,0 +1,86 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindMSPack +------- + +Finds the MSPack library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``MSPack::mspack`` + The MSPack library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``MSPack_FOUND`` + True if the system has the MSPack library. +``MSPack_VERSION`` + The version of the MSPack library which was found. +``MSPack_INCLUDE_DIRS`` + Include directories needed to use MSPack. +``MSPack_LIBRARIES`` + Libraries needed to link to MSPack. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``MSPack_INCLUDE_DIR`` + The directory containing ``foo.h``. +``MSPack_LIBRARY`` + The path to the MSPack library. + +#]=======================================================================] + +find_package(PkgConfig) +pkg_check_modules(PC_mspack QUIET mspack) + +find_path(MSPack_INCLUDE_DIR + NAMES mspack.h + PATHS ${PC_MSPack_INCLUDE_DIRS} + PATH_SUFFIXES mspack +) +find_library(MSPack_LIBRARY + NAMES mspack + PATHS ${PC_MSPack_LIBRARY_DIRS} +) + +set(MSPack_VERSION ${PC_MSPack_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MSPack + FOUND_VAR MSPack_FOUND + REQUIRED_VARS + MSPack_LIBRARY + MSPack_INCLUDE_DIR + VERSION_VAR MSPack_VERSION +) + +if(MSPack_FOUND) + set(MSPack_LIBRARIES ${MSPack_LIBRARY}) + set(MSPack_INCLUDE_DIRS ${MSPack_INCLUDE_DIR}) + set(MSPack_DEFINITIONS ${PC_MSPack_CFLAGS_OTHER}) +endif() + +if(MSPack_FOUND AND NOT TARGET MSPack::mspack) + add_library(MSPack::mspack UNKNOWN IMPORTED) + set_target_properties(MSPack::mspack PROPERTIES + IMPORTED_LOCATION "${MSPack_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PC_MSPack_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${MSPack_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + MSPack_INCLUDE_DIR + MSPack_LIBRARY +) diff --git a/cabextract/cmake/TestInline.c b/cabextract/cmake/TestInline.c new file mode 100644 index 0000000..db0d056 --- /dev/null +++ b/cabextract/cmake/TestInline.c @@ -0,0 +1,5 @@ +/* Test source lifted from /usr/share/autoconf/autoconf/c.m4 */ +typedef int foo_t; +static inline foo_t static_foo(){return 0;} +foo_t foo(){return 0;} +int main(int argc, char *argv[]){return 0;} diff --git a/cabextract/cmake/TestInline.cmake b/cabextract/cmake/TestInline.cmake new file mode 100644 index 0000000..a3264b2 --- /dev/null +++ b/cabextract/cmake/TestInline.cmake @@ -0,0 +1,22 @@ +# From https://gitlab.kitware.com/cmake/community/-/wikis/contrib/macros/TestInline +# Modified to use configure_file() approach, and to address script path issues. +# See: https://stackoverflow.com/questions/3781222/add-definitions-vs-configure-file +# Inspired from /usr/share/autoconf/autoconf/c.m4 + +GET_FILENAME_COMPONENT(_selfdir_TestInline + "${CMAKE_CURRENT_LIST_FILE}" PATH) + +FOREACH(KEYWORD "inline" "__inline__" "__inline") + IF(NOT DEFINED C_INLINE) + TRY_COMPILE(C_HAS_${KEYWORD} "${CMAKE_CURRENT_BINARY_DIR}" + "${_selfdir_TestInline}/TestInline.c" + COMPILE_DEFINITIONS "-Dinline=${KEYWORD}") + IF(C_HAS_${KEYWORD}) + SET(C_INLINE TRUE) + SET(INLINE_KEYWORD "${KEYWORD}" CACHE INTERNAL "inline macro defined as ${KEYWORD}") + ENDIF(C_HAS_${KEYWORD}) + ENDIF(NOT DEFINED C_INLINE) +ENDFOREACH(KEYWORD) +IF(NOT DEFINED C_INLINE) + SET(INLINE_KEYWORD "" CACHE INTERNAL "inline macro definition not required") +ENDIF(NOT DEFINED C_INLINE) diff --git a/cabextract/cmake/compiletest_mkdir.c b/cabextract/cmake/compiletest_mkdir.c new file mode 100644 index 0000000..d9dab27 --- /dev/null +++ b/cabextract/cmake/compiletest_mkdir.c @@ -0,0 +1,13 @@ +/* + * Compile-test to check if mkdir() only takes one argument. + */ + +#include +#if HAVE_UNISTD_H +# include +#endif + +int main(void) { + mkdir("."); + return 0; +} diff --git a/cabextract/config.h.in.cmake b/cabextract/config.h.in.cmake new file mode 100644 index 0000000..9fecfff --- /dev/null +++ b/cabextract/config.h.in.cmake @@ -0,0 +1,152 @@ +/* + * CMake file to generate config.h + */ + + /* Turn debugging mode on? */ +#cmakedefine DEBUG @ENABLE_DEBUG@ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CTYPE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FNMATCH_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS 1 + +/* Define to 1 if you have the `fseeko' function. */ +#cmakedefine HAVE_FSEEKO 1 + +/* Define to 1 if you have the `mkdir' function. */ +#cmakedefine HAVE_MKDIR 1 + +/* Define to 1 if you have the `_mkdir' function. */ +#cmakedefine HAVE__MKDIR 1 + +/* Define to 1 if you have the `tolower' function. */ +#cmakedefine HAVE_TOWLOWER 1 + + +/* Define to empty if `const' does not conform to ANSI C. */ +#define ICONV_CONST @ICONV_CONST@ + +/* Define to 1 if you have the iconv library. */ +#cmakedefine HAVE_ICONV 1 + + +/* The size of `off_t', as computed by sizeof. */ +#cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ + +/* The size of `size_t', as computed by sizeof. */ +#cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@ + +/* The size of `ssize_t', as computed by sizeof. */ +#cmakedefine SIZEOF_SSIZE_T @SIZEOF_SSIZE_T@ + +/* The size of `mode_t', as computed by sizeof. */ +#cmakedefine SIZEOF_MODE_T @SIZEOF_MODE_T@ + + +/* Define if mkdir takes only one argument. */ +#cmakedefine MKDIR_TAKES_ONE_ARG 1 + + +/* Define to `long int' if does not define. */ +@OFF_T_DEF@ + +/* Define to `unsigned int' if does not define. */ +@SIZE_T_DEF@ + +/* Define to `int' if does not define. */ +@SSIZE_T_DEF@ + +/* Define to `int' if does not define. */ +@MODE_T_DEF@ + + +/* Define to 1 if you can safely include both and . */ +#ifdef __GNUC__ +# define TIME_WITH_SYS_TIME 1 +#else +# define TIME_WITH_SYS_TIME 0 +#endif + +#ifdef __AMIGA__ +# define LATIN1_FILENAMES 1 +#endif + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#cmakedefine WORDS_BIGENDIAN 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#cmakedefine _LARGEFILE_SOURCE 1 + +/* Define for large files, on AIX-style hosts. */ +#cmakedefine _LARGE_FILES 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline @INLINE_KEYWORD@ +#endif + +/* Version number of package */ +#cmakedefine VERSION "@VERSION@" + +#ifdef _MSC_VER +//not #if defined(_WIN32) || defined(_WIN64) because mingw has strncasecmp +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif diff --git a/cabextract/getopt.c b/cabextract/getopt.c index 7a24e2d..f52050a 100644 --- a/cabextract/getopt.c +++ b/cabextract/getopt.c @@ -28,7 +28,7 @@ #endif #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #if !defined __STDC__ || !__STDC__ diff --git a/cabextract/getopt1.c b/cabextract/getopt1.c index 0e03343..47613ce 100644 --- a/cabextract/getopt1.c +++ b/cabextract/getopt1.c @@ -19,7 +19,7 @@ Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include "getopt.h" diff --git a/cabextract/md5.c b/cabextract/md5.c index a3d2277..a689756 100644 --- a/cabextract/md5.c +++ b/cabextract/md5.c @@ -21,14 +21,14 @@ /* Written by Ulrich Drepper , 1995. */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include #include -#include +#include "md5.h" #ifdef _LIBC # include diff --git a/cabextract/mspack/CMakeLists.txt b/cabextract/mspack/CMakeLists.txt new file mode 100644 index 0000000..3249846 --- /dev/null +++ b/cabextract/mspack/CMakeLists.txt @@ -0,0 +1,36 @@ +# +# mspack targets +# + +# The bundled mscabd OBJECT-library +add_library(mscabd_ObjLib OBJECT) +target_sources(mscabd_ObjLib + PRIVATE + system.h system.c + cab.h cabd.c + lzx.h lzxd.c + mszip.h mszipd.c + qtm.h qtmd.c + readbits.h readhuff.h + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h +) +target_include_directories(mscabd_ObjLib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# The bundled mscabd static library. +# We'll call it mspack, though it only provides cabd features. +# It's enough for cabextract. +add_library(mspack_static STATIC) +target_sources(mspack_static + PRIVATE + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h +) +target_link_libraries(mspack_static PRIVATE mscabd_ObjLib) +set_target_properties(mspack_static PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + ARCHIVE_OUTPUT_NAME mspack_static) +target_compile_definitions(mspack_static PUBLIC MSPACK_STATICLIB) + +# Alias this as MSPack::mspack for cabextract +add_library( MSPack::mspack ALIAS mspack_static ) diff --git a/cabextract/src/cabextract.c b/cabextract/src/cabextract.c index af5ed20..bb9ceca 100644 --- a/cabextract/src/cabextract.c +++ b/cabextract/src/cabextract.c @@ -23,7 +23,7 @@ #define _GNU_SOURCE 1 #if HAVE_CONFIG_H -# include +# include "config.h" #endif #include @@ -31,7 +31,11 @@ #include #include #include +#ifndef _WIN32 #include +#else +#include +#endif #include #include #include @@ -76,8 +80,12 @@ #include "getopt.h" -#include -#include +#include "mspack.h" +#include "md5.h" + +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif /* structures and global variables */ struct option optlist[] = { @@ -107,7 +115,7 @@ const char *OPTSTRING = "d:fF:hlLpqstv"; struct file_mem { struct file_mem *next; dev_t st_dev; - ino_t st_ino; + ino_t st_ino; char *from; }; @@ -150,7 +158,7 @@ const char *STDOUT_FNAME = "stdout"; /** A special filename. Extracting to this filename will send the output * through an MD5 checksum calculator, instead of a file on disk. The * magic happens in cabx_open() when the TEST_FNAME pointer is given as a - * filename, so treat this like a constant rather than a string. + * filename, so treat this like a constant rather than a string. */ const char *TEST_FNAME = "test"; @@ -448,8 +456,8 @@ static int process_cabinet(char *basename) { } /* the full UNIX output filename includes the output - * directory. However, for filtering purposes, we don't want to - * include that. So, we work out where the filename part of the + * directory. However, for filtering purposes, we don't want to + * include that. So, we work out where the filename part of the * output name begins. This is the same for every extracted file. */ fname_offset = args.dir ? (strlen(args.dir) + 1) : 0; @@ -469,7 +477,11 @@ static int process_cabinet(char *basename) { int matched = 0; struct filter *f; for (f = args.filters; f; f = f->next) { +#ifndef _WIN32 if (!fnmatch(f->filter, &name[fname_offset], FNM_CASEFOLD)) { +#else + if (TRUE == PathMatchSpecA(&name[fname_offset], f->filter)) { +#endif matched = 1; break; } @@ -779,7 +791,7 @@ static char *create_output_name(const char *fname, const char *dir, return NULL; } - /* copy directory prefix if needed */ + /* copy directory prefix if needed */ if (dir) { strcpy(name, dir); name[dirlen - 1] = '/'; @@ -906,7 +918,7 @@ static char *create_output_name(const char *fname, const char *dir, /** * Sets the last-modified time and file permissions on a file. * - * @param file the internal CAB file whose date, time and attributes will + * @param file the internal CAB file whose date, time and attributes will * be used. * @param filename the name of the UNIX file whose last-modified time and * file permissions will be set. diff --git a/cabextract/src/cabinfo.c b/cabextract/src/cabinfo.c index 28eaa99..e96151f 100644 --- a/cabextract/src/cabinfo.c +++ b/cabextract/src/cabinfo.c @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include diff --git a/cabextract/test/CMakeLists.txt b/cabextract/test/CMakeLists.txt new file mode 100644 index 0000000..849a5e9 --- /dev/null +++ b/cabextract/test/CMakeLists.txt @@ -0,0 +1,28 @@ +if(NOT WIN32) + + set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/testcase.in + ${CMAKE_CURRENT_BINARY_DIR}/test/testcase + ) + # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bugs + # DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/cabs + # DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + + find_program (BASH_PROGRAM bash) + + if (BASH_PROGRAM) + add_test (bugs ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/bugs.test) + add_test (case-ascii ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/case-ascii.test) + add_test (dirwalk-vulns ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/dirwalk-vulns.test) + add_test (encoding ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/encoding.test) + add_test (large-files ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/large-files.test) + add_test (mixed ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/mixed.test) + add_test (search ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/search.test) + add_test (simple ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/simple.test) + add_test (split ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/split.test) + add_test (utf8-stresstest ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/utf8-stresstest.test) + endif (BASH_PROGRAM) + +endif() diff --git a/cabextract/win32/dirent.h b/cabextract/win32/dirent.h new file mode 100644 index 0000000..f7a46da --- /dev/null +++ b/cabextract/win32/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/libmspack/.gitignore b/libmspack/.gitignore index 84c02e4..59ca3a9 100644 --- a/libmspack/.gitignore +++ b/libmspack/.gitignore @@ -1,22 +1,102 @@ +/build + +# Logs +*.log + +# 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 + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + +# http://www.gnu.org/software/automake +.deps +.dirstamp .libs -INSTALL -/Makefile Makefile.in -aclocal.m4 -ar-lib +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap + +# http://www.gnu.org/software/autoconf autom4te.cache -compile -config.* -configure -depcomp -install-sh -lib*.la -libmspack-*.tar.gz -libmspack.pc -libtool -ltmain.sh -m4 -missing -stamp-h1 -test-driver -test-suite.log +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ +/ltmain.sh + +# http://www.gnu.org/software/texinfo +/texinfo.tex + +# http://www.gnu.org/software/m4/ +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 diff --git a/libmspack/CMakeLists.txt b/libmspack/CMakeLists.txt new file mode 100644 index 0000000..0306a8d --- /dev/null +++ b/libmspack/CMakeLists.txt @@ -0,0 +1,269 @@ +cmake_minimum_required(VERSION 3.12) + +project(libmspack + VERSION 0.10.1 + DESCRIPTION "libmspack is a library that provides creation & extraction of various Microsoft compression and archive formats." + LANGUAGES C) + +# See versioning rule: +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# +# KEEP THESE IN SYNC WITH configure.ac +set(LT_CURRENT 1) +set(LT_REVISION 1) +set(LT_AGE 0) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) +include(Version) + +math(EXPR LT_SOVERSION "${LT_CURRENT} - ${LT_AGE}") +set(LT_VERSION "${LT_SOVERSION}.${LT_AGE}.${LT_REVISION}") +set(PACKAGE_VERSION ${PROJECT_VERSION}) +HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH}) + +set(ENABLE_EXAMPLES_DEFAULT OFF) +set(ENABLE_DOCS_DEFAULT OFF) +include(CMakeOptions.txt) + +if(ENABLE_LIB_ONLY AND (ENABLE_APP OR ENABLE_EXAMPLES)) + # Remember when disabled options are disabled for later diagnostics. + set(ENABLE_LIB_ONLY_DISABLED_OTHERS 1) +else() + set(ENABLE_LIB_ONLY_DISABLED_OTHERS 0) +endif() +if(ENABLE_LIB_ONLY) + set(ENABLE_APP OFF) + set(ENABLE_EXAMPLES OFF) +endif() + +# Do not disable assertions based on CMAKE_BUILD_TYPE. +foreach(_build_type Release MinSizeRel RelWithDebInfo) + foreach(_lang C) + string(TOUPPER CMAKE_${_lang}_FLAGS_${_build_type} _var) + string(REGEX REPLACE "(^|)[/-]D *NDEBUG($|)" " " ${_var} "${${_var}}") + endforeach() +endforeach() + +# Support the latest c++ standard available. +include(ExtractValidFlags) +# Determine if _FILE_OFFSET_BITS 64 needs to be set to handle large files. +include(CheckFileOffsetBits) +# Define inline macro as needed. +include(TestInline) + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE) + + # Include "None" as option to disable any additional (optimization) flags, + # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by + # default). These strings are presented in cmake-gui. + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + None Debug Release MinSizeRel RelWithDebInfo) +endif() + +include(GNUInstallDirs) + +# Always use '-fPIC'/'-fPIE' option. +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Checks for header files. +include(CheckIncludeFile) +check_include_file(dlfcn.h HAVE_DLFCN_H) +check_include_file(inttypes.h HAVE_INTTYPES_H) +check_include_file(stdint.h HAVE_STDINT_H) +check_include_file(stddef.h HAVE_STDDEF_H) +check_include_file(limits.h HAVE_LIMITS_H) +check_include_file(ctype.h HAVE_CTYPE_H) +check_include_file(wctype.h HAVE_WCTYPE_H) +check_include_file(errno.h HAVE_ERRNO_H) +check_include_file(dirent.h HAVE_DIRENT_H) +check_include_file(sys/types.h HAVE_SYS_TYPES_H) +check_include_file(sys/stat.h HAVE_SYS_STAT_H) +check_include_file(fnmatch.h HAVE_FNMATCH_H) +check_include_file(locale.h HAVE_LOCALE_H) +check_include_file(stdarg.h HAVE_STDARG_H) +check_include_file(stdlib.h HAVE_STDLIB_H) +check_include_file(string.h HAVE_STRING_H) +check_include_file(strings.h HAVE_STRINGS_H) +check_include_file(sys/time.h HAVE_SYS_TIME_H) +check_include_file(float.h HAVE_FLOAT_H) +check_include_file(unistd.h HAVE_UNISTD_H) + +include(CheckIncludeFiles) +check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" StandardHeadersExist) +if(StandardHeadersExist) + set(STDC_HEADERS 1 CACHE INTERNAL "System has ANSI C header files") +else() + message(STATUS "ANSI C header files - not found") + set(STDC_HEADERS 0 CACHE INTERNAL "System has ANSI C header files") +endif() + + +# Checks for library functions. +include(CheckFunctionExists) +check_function_exists(fseeko HAVE_FSEEKO) +check_function_exists(mkdir HAVE_MKDIR) +check_function_exists(_mkdir HAVE__MKDIR) +check_function_exists(towlower HAVE_TOWLOWER) + +# Check size of types. +include(CheckTypeSize) +check_type_size("off_t" SIZEOF_OFF_T) +if(NOT SIZEOF_OFF_T) + # Set it to "long int" to match the behavior of AC_TYPE_OFF_T (autotools). + set(OFF_T_DEF "typedef int off_t;") +endif() + +check_type_size("size_t" SIZEOF_SIZE_T) +if(NOT SIZEOF_SIZE_T) + # Set it to "unsigned int" to match the behavior of AC_TYPE_SIZE_T (autotools). + set(SIZE_T_DEF "typedef int size_t;") +endif() + +check_type_size("ssize_t" SIZEOF_SSIZE_T) +if(NOT SIZEOF_SSIZE_T) + # Set it to "int" to match the behavior of AC_TYPE_SSIZE_T (autotools). + set(SSIZE_T_DEF "typedef int ssize_t;") +endif() + +check_type_size("mode_t" SIZEOF_MODE_T) +if(NOT SIZEOF_MODE_T) + # Set it to "int" to match the behavior of AC_TYPE_MODE_T (autotools). + set(MODE_T_DEF "typedef int mode_t;") +endif() + +# Compile tests +try_compile(MKDIR_TAKES_ONE_ARG ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/compiletest_mkdir.c) + +# Check if big-endian +include(TestBigEndian) +TEST_BIG_ENDIAN(WORDS_BIGENDIAN) + +set(WARNCFLAGS) +if(CMAKE_C_COMPILER_ID MATCHES "MSVC") + if(ENABLE_WERROR) + set(WARNCFLAGS /WX) + endif() +else() + if(ENABLE_WERROR) + extract_valid_c_flags(WARNCFLAGS -Werror) + endif() + + # For C compiler + extract_valid_c_flags(WARNCFLAGS + -Wall + -Wextra + -Wmissing-prototypes + -Wstrict-prototypes + -Wmissing-declarations + -Wpointer-arith + -Wdeclaration-after-statement + -Wformat-security + -Wwrite-strings + -Wshadow + -Winline + -Wnested-externs + -Wfloat-equal + -Wundef + -Wendif-labels + -Wempty-body + -Wcast-align + -Wclobbered + -Wvla + -Wpragmas + -Wunreachable-code + -Waddress + -Wattributes + -Wdiv-by-zero + -Wshorten-64-to-32 + -Wconversion + -Wextended-offsetof + -Wformat-nonliteral + -Wlanguage-extension-token + -Wmissing-field-initializers + -Wmissing-noreturn + -Wmissing-variable-declarations + # -Wpadded # Not used because we cannot change public structs + -Wsign-conversion + # -Wswitch-enum # Not used because this basically disallows default case + -Wunreachable-code-break + -Wunused-macros + -Wunused-parameter + -Wredundant-decls + -Wheader-guard + #-Wno-format-nonliteral # This is required because we pass format string as "const char*. + -Wno-unused-parameter + -Wno-unused-result + ) +endif() + +if(ENABLE_DEBUG) + set(DEBUGBUILD 1) +endif() + +# autotools-compatible names +# Sphinx expects relative paths in the .rst files. Use the fact that the files +# below are all one directory level deep. +file(RELATIVE_PATH top_srcdir ${CMAKE_CURRENT_BINARY_DIR}/dir ${CMAKE_CURRENT_SOURCE_DIR}) +file(RELATIVE_PATH top_builddir ${CMAKE_CURRENT_BINARY_DIR}/dir ${CMAKE_CURRENT_BINARY_DIR}) +set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) +set(abs_top_builddir ${CMAKE_CURRENT_BINARY_DIR}) +# libmspack.pc (pkg-config file) +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix ${CMAKE_INSTALL_PREFIX}) +set(bindir ${CMAKE_INSTALL_FULL_BINDIR}) +set(sbindir ${CMAKE_INSTALL_FULL_SBINDIR}) +set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) +set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) +set(VERSION ${PROJECT_VERSION}) + +# Generate config.h +add_definitions(-DHAVE_CONFIG_H) +configure_file(config.h.in.cmake config.h) +include_directories(${PROJECT_BINARY_DIR}) + +# Generate, install pkg-config file. +configure_file( + libmspack.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/libmspack.pc + @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmspack.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +# +# The build targets. +# +add_subdirectory(mspack) + +if(ENABLE_EXAMPLES) + add_subdirectory(examples) +endif() + +enable_testing() +add_subdirectory(test) + +if(ENABLE_DOCS) + add_subdirectory(doc) +endif() + +# +# The Summary Info. +# +string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type) +message(STATUS "Summary of build options: + + Package version: ${VERSION} + Library version: ${LT_CURRENT}:${LT_REVISION}:${LT_AGE} + Install prefix: ${CMAKE_INSTALL_PREFIX} + Target system: ${CMAKE_SYSTEM_NAME} + Compiler: + Build type: ${CMAKE_BUILD_TYPE} + C compiler: ${CMAKE_C_COMPILER} + CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} + WARNCFLAGS: ${WARNCFLAGS} + Docs: + Build docs: ${ENABLE_DOCS} + Features: + Static library: ${ENABLE_STATIC_LIB} + Examples: ${ENABLE_EXAMPLES} +") diff --git a/libmspack/CMakeOptions.txt b/libmspack/CMakeOptions.txt new file mode 100644 index 0000000..e12b802 --- /dev/null +++ b/libmspack/CMakeOptions.txt @@ -0,0 +1,15 @@ +# Features that can be enabled for cmake (see CMakeLists.txt) + +option(ENABLE_WERROR "Turn on compile time warnings as errors") + +option(ENABLE_DEBUG "Turn on debug output") + +option(ENABLE_DOCS "Generate documentation" + ${ENABLE_DOCS_DEFAULT}) + +option(ENABLE_EXAMPLES "Build examples" + ${ENABLE_EXAMPLES_DEFAULT}) + +option(ENABLE_STATIC_LIB "Build libmspack in static mode also") + +option(ENABLE_SHARED_LIB "Build libmspack as a shared library" ON) diff --git a/libmspack/Makefile.am b/libmspack/Makefile.am index 3264707..92c53bd 100644 --- a/libmspack/Makefile.am +++ b/libmspack/Makefile.am @@ -63,22 +63,22 @@ examples_cabd_memory_SOURCES = examples/cabd_memory.c libmscabd.la examples_cabd_memory_LDADD = libmscabd.la examples_cabrip_SOURCES = examples/cabrip.c libmspack.la examples_cabrip_LDADD = libmspack.la -examples_chmextract_SOURCES = examples/chmextract.c test/error.h libmspack.la +examples_chmextract_SOURCES = examples/chmextract.c libmspack.la examples_chmextract_LDADD = libmspack.la -examples_msexpand_SOURCES = examples/msexpand.c test/error.h libmspack.la +examples_msexpand_SOURCES = examples/msexpand.c libmspack.la examples_msexpand_LDADD = libmspack.la examples_multifh_SOURCES = examples/multifh.c libmscabd.la examples_multifh_LDADD = libmscabd.la -examples_oabextract_SOURCES = examples/oabextract.c test/error.h libmspack.la +examples_oabextract_SOURCES = examples/oabextract.c libmspack.la examples_oabextract_LDADD = libmspack.la -test_cabd_md5_SOURCES = test/cabd_md5.c test/md5.c test/md5.h test/md5_fh.h test/error.h libmscabd.la +test_cabd_md5_SOURCES = test/cabd_md5.c test/md5.c test/md5.h test/md5_fh.h libmscabd.la test_cabd_md5_LDADD = libmscabd.la -test_chmd_find_SOURCES = test/chmd_find.c test/error.h libmschmd.la +test_chmd_find_SOURCES = test/chmd_find.c libmschmd.la test_chmd_find_LDADD = libmschmd.la -test_chmd_md5_SOURCES = test/chmd_md5.c test/md5.c test/md5.h test/md5_fh.h test/error.h libmschmd.la +test_chmd_md5_SOURCES = test/chmd_md5.c test/md5.c test/md5.h test/md5_fh.h libmschmd.la test_chmd_md5_LDADD = libmschmd.la -test_chmd_order_SOURCES = test/chmd_order.c test/md5.c test/md5.h test/md5_fh.h test/error.h libmschmd.la +test_chmd_order_SOURCES = test/chmd_order.c test/md5.c test/md5.h test/md5_fh.h libmschmd.la test_chmd_order_LDADD = libmschmd.la test_chminfo_SOURCES = test/chminfo.c libmschmd.la test_chminfo_LDADD = libmschmd.la diff --git a/libmspack/README b/libmspack/README deleted file mode 100644 index cf5aee9..0000000 --- a/libmspack/README +++ /dev/null @@ -1,131 +0,0 @@ -libmspack 0.10.1alpha - -The purpose of libmspack is to provide compressors and decompressors, -archivers and dearchivers for Microsoft compression formats: CAB, CHM, WIM, -LIT, HLP, KWAJ and SZDD. It is also designed to be easily embeddable, -stable, robust and resource-efficient. - -The library is not intended as a generalised "any archiver" interface. -Users of the library must explicitly choose the format they intend to work -with. - -All special features of the above formats will be covered as fully as -possible -- for example, CAB's multi-part cabinet sets, or CHM's fast -lookup indices. All compression methods used by the formats will be -implemented as completely as possible. - -However, other than what is required for access to these formats and their -features, no other functionality is intended. There is no file metadata -translation functionality. All file I/O is abstracted, although a default -implementation using the standard C library is provided. - - -DOCUMENTATION - -The API documentation is stored in the doc/ directory. It is generated -automatically from mspack.h with doxygen. It is also available online at -https://www.cabextract.org.uk/libmspack/doc/ - - -BUILDING / INSTALLING - -./configure -make -make install - -This will install the main libmspack library and mspack.h header file. -Some other libraries and executables are built, but not installed. - -If building from the Git repository, running rebuild.sh will create all the -auto-generated files, then run ./configure && make. Running cleanup.sh will -perform a thorough clean, deleting all auto-generated files. - -In addition to gcc, you also need the following for building from repository: - -- at least autoconf 2.57 -- at least automake 1.11 -- libtool - -This is an alpha release. Unless you are in a position to package the -libmspack library for the environment you intend to run your application, -it is recommended that you do not rely on users of your software having -the binary library installed and instead you should include the libmspack -source files directly in your application's build environment. - - -LEGAL ISSUES - -To the best of my knowledge, libmspack does not infringe on any -compression or decompression patents. However, this is not legal -advice, and it is recommended that you perform your own patent search. - -libmspack is licensed under the LGPL - see COPYING.LIB in this directory. - -The LGPL requires you to build libmspack as a stand alone library then link -your code to it using a linker. I personally grant you some extra rights: -you can incorporate libmspack's source code wholly or partially in your own -code, without having to build and link libmspack as an independent library, -provided you meet ALL of the following conditions: - -1. ANY modifications to the existing libmspack source code are published and - distributed under the LGPL license. -2. You MUST NOT use libmspack function calls, structures or definitions unless - they are defined in the public library interface "mspack.h". -3. When distributing your code, you MUST make clear your code uses libmspack, - and either include the full libmspack distribution with your code, or - provide access to it as per clause 4 of the LGPL. - -EXAMPLE CODE - -libmspack is bundled with programs which demonstrate the library's features. - -examples/cabd_memory.c - an mspack_system that can read and write to memory -examples/multifh.c - an mspack_system that can simultaneously work on - in-memory images, raw file descriptors, open file - handles and regular disk files - -examples/cabrip.c - extracts any CAB files embedded in another file -examples/chmextract.c - extracts all files in a CHM file to disk -examples/msexpand.c - expands an SZDD or KWAJ file -examples/oabextract.c - extracts an Exchange Offline Address Book (.LZX) file - -test/cabd_c10 - tests the CAB decompressor on the C10 collection -test/cabd_compare - compares libmspack with Microsoft's EXTRACT/EXPAND.EXE -test/cabd_md5 - shows MD5 checksums of all files in a CAB file/set -test/chmd_compare - compares libmspack with Microsoft's HH.EXE -test/chmd_find.c - checks all files in a CHM file can be fast-found -test/chmd_md5.c - shows MD5 checksums of all files within a CHM file -test/chmd_order.c - extracts files in a CHM file in four different ways -test/chminfo.c - prints verbose information about CHM file structures -test/msdecompile_md5 - runs Microsoft's HH.EXE -DECOMPILE via WINE -test/msexpand_md5 - runs Microsoft's EXPAND.EXE via WINE -test/msextract_md5 - runs Microsoft's EXTRACT.EXE via WINE - -Here is a simple example of usage, which will create a CAB decompressor, -then use that to open an existing Microsoft CAB file called "example.cab", -and list the names of all the files contained in that cab. - -#include -#include -#include - -int main() { - struct mscab_decompressor *cabd; - struct mscabd_cabinet *cab; - struct mscabd_file *file; - int test; - - MSPACK_SYS_SELFTEST(test); - if (test != MSPACK_ERR_OK) exit(0); - - if ((cabd = mspack_create_cab_decompressor(NULL))) { - if ((cab = cabd->open(cabd, "example.cab"))) { - for (file = cab->files; file; file = file->next) { - printf("%s\n", file->filename); - } - cabd->close(cabd, cab); - } - mspack_destroy_cab_decompressor(cabd); - } - return 0; -} diff --git a/libmspack/README.md b/libmspack/README.md new file mode 100644 index 0000000..9a37495 --- /dev/null +++ b/libmspack/README.md @@ -0,0 +1,224 @@ +# libmspack 0.10.1alpha + + + +The purpose of libmspack is to provide compressors and decompressors, +archivers and dearchivers for Microsoft compression formats: CAB, CHM, WIM, +LIT, HLP, KWAJ and SZDD. It is also designed to be easily embeddable, +stable, robust and resource-efficient. + +The library is not intended as a generalised "any archiver" interface. +Users of the library must explicitly choose the format they intend to work +with. + +All special features of the above formats will be covered as fully as +possible -- for example, CAB's multi-part cabinet sets, or CHM's fast +lookup indices. All compression methods used by the formats will be +implemented as completely as possible. + +However, other than what is required for access to these formats and their +features, no other functionality is intended. There is no file metadata +translation functionality. All file I/O is abstracted, although a default +implementation using the standard C library is provided. + + +## DOCUMENTATION + +The API documentation is stored in the doc/ directory. It is generated +automatically from mspack.h with doxygen. It is also available online at +https://www.cabextract.org.uk/libmspack/doc/ + + +## BUILDING / INSTALLING + +### Autotools + +```sh +./configure +make +make install +``` + +This will install the main libmspack library and mspack.h header file. +Some other libraries and executables are built, but not installed. + +If building from the Git repository, running `rebuild.sh` will create all the +auto-generated files, then run `./configure && make`. Running `cleanup.sh` will +perform a thorough clean, deleting all auto-generated files. + +In addition to gcc, you also need the following for building from repository: + +- at least autoconf 2.57 +- at least automake 1.11 +- libtool + +This is an alpha release. Unless you are in a position to package the +libmspack library for the environment you intend to run your application, +it is recommended that you do not rely on users of your software having +the binary library installed and instead you should include the libmspack +source files directly in your application's build environment. + +### CMake + +libmspack can be compiled with the [CMake] build system. +The following instructions recommend compiling in a `build` subdirectory. + +#### Basic Release build + +```sh +mkdir build && cd build +cmake .. +cmake --build . --config Release +``` + +#### Basic Debug build + +```sh +mkdir build && cd build +cmake .. -DCMAKE_BUILD_TYPE="Debug" +cmake --build . --config Debug +``` + +#### Build and install to a specific install location (prefix) + +```sh +mkdir build && cd build +cmake -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/install .. +cmake --build . --target install +``` + +Windows (Powershell) +```ps1 +mkdir build && cd build +cmake .. -DCMAKE_INSTALL_PREFIX:PATH=$(Get-Location)/install +cmake --build . --target install +``` + +#### Build with example applications + +```sh +mkdir build && cd build +cmake .. -DENABLE_EXAMPLES=ON +cmake --build . +``` + +#### Build and generate html documentation + +```sh +mkdir build && cd build +cmake .. -DENABLE_DOCS=ON +cmake --build . --target doxygen +``` + +#### Build and run tests + +- `-V`: Verbose +- `-C`: Required for Windows builds + +```sh +mkdir build && cd build +cmake .. +cmake --build . --config Debug +ctest -C Debug -V +``` + +Or try `ctest -C Debug -VV --output-on-failure` for extra verbose output. + +#### Build, test, and install in Release mode + +```sh +mkdir build && cd build +cmake .. -DENABLE_EXAMPLES=ON -DENABLE_STATIC_LIB=ON -DCMAKE_INSTALL_PREFIX:PATH=`pwd`/install -DENABLE_DOCS=ON +cmake --build . --config Release +ctest -C Release -V +cmake --build . --config Release --target install doxygen +``` + +Windows (Powershell): +```ps1 +mkdir build ; if($?) {cd build} +cmake .. -DENABLE_EXAMPLES=ON -DENABLE_STATIC_LIB=ON -DCMAKE_INSTALL_PREFIX:PATH=$(Get-Location)/install +cmake --build . --config Release +ctest -C Release -V +cmake --build . --config Release --target install +``` + +## LEGAL ISSUES + +To the best of my knowledge, libmspack does not infringe on any +compression or decompression patents. However, this is not legal +advice, and it is recommended that you perform your own patent search. + +libmspack is licensed under the LGPL - see COPYING.LIB in this directory. + +The LGPL requires you to build libmspack as a stand alone library then link +your code to it using a linker. I personally grant you some extra rights: +you can incorporate libmspack's source code wholly or partially in your own +code, without having to build and link libmspack as an independent library, +provided you meet ALL of the following conditions: + +1. ANY modifications to the existing libmspack source code are published and + distributed under the LGPL license. +2. You MUST NOT use libmspack function calls, structures or definitions unless + they are defined in the public library interface "mspack.h". +3. When distributing your code, you MUST make clear your code uses libmspack, + and either include the full libmspack distribution with your code, or + provide access to it as per clause 4 of the LGPL. + +## EXAMPLE CODE + +libmspack is bundled with programs which demonstrate the library's features. + +| Program | Description +:------------------------|:------------------------------------------------------ +| examples/cabd_memory.c | an mspack_system that can read and write to memory +| examples/multifh.c | an mspack_system that can simultaneously work on +| | in-memory images, raw file descriptors, open file +| | handles and regular disk files +| examples/cabrip.c | extracts any CAB files embedded in another file +| examples/chmextract.c | extracts all files in a CHM file to disk +| examples/msexpand.c | expands an SZDD or KWAJ file +| examples/oabextract.c | extracts an Exchange Offline Address Book (.LZX) file +| | +| test/cabd_c10 | tests the CAB decompressor on the C10 collection +| test/cabd_compare | compares libmspack with Microsoft's EXTRACT/EXPAND.EXE +| test/cabd_md5 | shows MD5 checksums of all files in a CAB file/set +| test/chmd_compare | compares libmspack with Microsoft's HH.EXE +| test/chmd_find.c | checks all files in a CHM file can be fast-found +| test/chmd_md5.c | shows MD5 checksums of all files within a CHM file +| test/chmd_order.c | extracts files in a CHM file in four different ways +| test/chminfo.c | prints verbose information about CHM file structures +| test/msdecompile_md5 | runs Microsoft's HH.EXE -DECOMPILE via WINE +| test/msexpand_md5 | runs Microsoft's EXPAND.EXE via WINE +| test/msextract_md5 | runs Microsoft's EXTRACT.EXE via WINE + +Here is a simple example of usage, which will create a CAB decompressor, +then use that to open an existing Microsoft CAB file called "example.cab", +and list the names of all the files contained in that cab. + +```c +#include +#include +#include "mspack.h" + +int main() { + struct mscab_decompressor *cabd; + struct mscabd_cabinet *cab; + struct mscabd_file *file; + int test; + + MSPACK_SYS_SELFTEST(test); + if (test != MSPACK_ERR_OK) exit(0); + + if ((cabd = mspack_create_cab_decompressor(NULL))) { + if ((cab = cabd->open(cabd, "example.cab"))) { + for (file = cab->files; file; file = file->next) { + printf("%s\n", file->filename); + } + cabd->close(cabd, cab); + } + mspack_destroy_cab_decompressor(cabd); + } + return 0; +} +``` diff --git a/libmspack/autogen.sh b/libmspack/autogen.sh index c2f7128..81ce07e 100755 --- a/libmspack/autogen.sh +++ b/libmspack/autogen.sh @@ -2,5 +2,21 @@ # Runs the autoreconf tool, creating the configure script [ -d m4 ] || mkdir m4 -autoreconf -i -W all -echo you can now run ./configure + +BASEDIR="$( cd "$(dirname "$0")" ; pwd -P )" +echo "Generating autotools files in: $BASEDIR ..." +cd $BASEDIR + +# If this is a source checkout then call autoreconf with error as well +if test -d .git; then + WARNINGS="all,error" +else + WARNINGS="all" +fi + +autoreconf -i -f +rc=$?; if [[ $rc != 0 ]]; then + echo "Error: Failed to generate autojunk!"; exit $rc +else + echo "You can now run ./configure" +fi diff --git a/libmspack/cmake/COPYING-CMAKE-SCRIPTS b/libmspack/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/libmspack/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +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 copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. diff --git a/libmspack/cmake/CheckFileOffsetBits.c b/libmspack/cmake/CheckFileOffsetBits.c new file mode 100644 index 0000000..d948fec --- /dev/null +++ b/libmspack/cmake/CheckFileOffsetBits.c @@ -0,0 +1,14 @@ +#include + +#define KB ((off_t)1024) +#define MB ((off_t)1024 * KB) +#define GB ((off_t)1024 * MB) +#define TB ((off_t)1024 * GB) +int t2[(((64 * GB -1) % 671088649) == 268434537) + && (((TB - (64 * GB -1) + 255) % 1792151290) == 305159546)? 1: -1]; + +int main() +{ + ; + return 0; +} diff --git a/libmspack/cmake/CheckFileOffsetBits.cmake b/libmspack/cmake/CheckFileOffsetBits.cmake new file mode 100644 index 0000000..8a74b9e --- /dev/null +++ b/libmspack/cmake/CheckFileOffsetBits.cmake @@ -0,0 +1,43 @@ +# - Check if _FILE_OFFSET_BITS macro needed for large files +# CHECK_FILE_OFFSET_BITS () +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# Copyright (c) 2009, Michihiro NAKAJIMA +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +#INCLUDE(CheckCSourceCompiles) + +GET_FILENAME_COMPONENT(_selfdir_CheckFileOffsetBits + "${CMAKE_CURRENT_LIST_FILE}" PATH) + +MACRO (CHECK_FILE_OFFSET_BITS) + IF(NOT DEFINED _FILE_OFFSET_BITS) + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files") + TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - needed") + ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed") + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + ENDIF(NOT DEFINED _FILE_OFFSET_BITS) + +ENDMACRO (CHECK_FILE_OFFSET_BITS) diff --git a/libmspack/cmake/ExtractValidFlags.cmake b/libmspack/cmake/ExtractValidFlags.cmake new file mode 100644 index 0000000..2081a0e --- /dev/null +++ b/libmspack/cmake/ExtractValidFlags.cmake @@ -0,0 +1,18 @@ +# Convenience function that checks the availability of certain +# C or C++ compiler flags and returns valid ones as a string. + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + +function(extract_valid_c_flags varname) + set(valid_flags) + foreach(flag IN LISTS ARGN) + string(REGEX REPLACE "[^a-zA-Z0-9_]+" "_" flag_var ${flag}) + set(flag_var "C_FLAG_${flag_var}") + check_c_compiler_flag("${flag}" "${flag_var}") + if(${flag_var}) + set(valid_flags "${valid_flags} ${flag}") + endif() + endforeach() + set(${varname} "${valid_flags}" PARENT_SCOPE) +endfunction() diff --git a/libmspack/cmake/TestInline.c b/libmspack/cmake/TestInline.c new file mode 100644 index 0000000..db0d056 --- /dev/null +++ b/libmspack/cmake/TestInline.c @@ -0,0 +1,5 @@ +/* Test source lifted from /usr/share/autoconf/autoconf/c.m4 */ +typedef int foo_t; +static inline foo_t static_foo(){return 0;} +foo_t foo(){return 0;} +int main(int argc, char *argv[]){return 0;} diff --git a/libmspack/cmake/TestInline.cmake b/libmspack/cmake/TestInline.cmake new file mode 100644 index 0000000..a3264b2 --- /dev/null +++ b/libmspack/cmake/TestInline.cmake @@ -0,0 +1,22 @@ +# From https://gitlab.kitware.com/cmake/community/-/wikis/contrib/macros/TestInline +# Modified to use configure_file() approach, and to address script path issues. +# See: https://stackoverflow.com/questions/3781222/add-definitions-vs-configure-file +# Inspired from /usr/share/autoconf/autoconf/c.m4 + +GET_FILENAME_COMPONENT(_selfdir_TestInline + "${CMAKE_CURRENT_LIST_FILE}" PATH) + +FOREACH(KEYWORD "inline" "__inline__" "__inline") + IF(NOT DEFINED C_INLINE) + TRY_COMPILE(C_HAS_${KEYWORD} "${CMAKE_CURRENT_BINARY_DIR}" + "${_selfdir_TestInline}/TestInline.c" + COMPILE_DEFINITIONS "-Dinline=${KEYWORD}") + IF(C_HAS_${KEYWORD}) + SET(C_INLINE TRUE) + SET(INLINE_KEYWORD "${KEYWORD}" CACHE INTERNAL "inline macro defined as ${KEYWORD}") + ENDIF(C_HAS_${KEYWORD}) + ENDIF(NOT DEFINED C_INLINE) +ENDFOREACH(KEYWORD) +IF(NOT DEFINED C_INLINE) + SET(INLINE_KEYWORD "" CACHE INTERNAL "inline macro definition not required") +ENDIF(NOT DEFINED C_INLINE) diff --git a/libmspack/cmake/Version.cmake b/libmspack/cmake/Version.cmake new file mode 100644 index 0000000..8ac4849 --- /dev/null +++ b/libmspack/cmake/Version.cmake @@ -0,0 +1,11 @@ +# Converts a version such as 1.2.255 to 0x0102ff +function(HexVersion version_hex_var major minor patch) + math(EXPR version_dec "${major} * 256 * 256 + ${minor} * 256 + ${patch}") + set(version_hex "0x") + foreach(i RANGE 5 0 -1) + math(EXPR num "(${version_dec} >> (4 * ${i})) & 15") + string(SUBSTRING "0123456789abcdef" ${num} 1 num_hex) + set(version_hex "${version_hex}${num_hex}") + endforeach() + set(${version_hex_var} "${version_hex}" PARENT_SCOPE) +endfunction() diff --git a/libmspack/cmake/compiletest_mkdir.c b/libmspack/cmake/compiletest_mkdir.c new file mode 100644 index 0000000..d9dab27 --- /dev/null +++ b/libmspack/cmake/compiletest_mkdir.c @@ -0,0 +1,13 @@ +/* + * Compile-test to check if mkdir() only takes one argument. + */ + +#include +#if HAVE_UNISTD_H +# include +#endif + +int main(void) { + mkdir("."); + return 0; +} diff --git a/libmspack/config.h.in.cmake b/libmspack/config.h.in.cmake new file mode 100644 index 0000000..ac25bba --- /dev/null +++ b/libmspack/config.h.in.cmake @@ -0,0 +1,149 @@ +/* + * CMake file to generate config.h + */ + + /* Turn debugging mode on? */ +#cmakedefine DEBUG @ENABLE_DEBUG@ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CTYPE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FNMATCH_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine STDC_HEADERS 1 + +/* Define to 1 if you have the `fseeko' function. */ +#cmakedefine HAVE_FSEEKO 1 + +/* Define to 1 if you have the `mkdir' function. */ +#cmakedefine HAVE_MKDIR 1 + +/* Define to 1 if you have the `_mkdir' function. */ +#cmakedefine HAVE__MKDIR 1 + +/* Define to 1 if you have the `tolower' function. */ +#cmakedefine HAVE_TOWLOWER 1 + + +/* Define to empty if `const' does not conform to ANSI C. */ +#cmakedefine ICONV_CONST "@ICONV_CONST@" + + +/* The size of `off_t', as computed by sizeof. */ +#cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ + +/* The size of `size_t', as computed by sizeof. */ +#cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@ + +/* The size of `ssize_t', as computed by sizeof. */ +#cmakedefine SIZEOF_SSIZE_T @SIZEOF_SSIZE_T@ + +/* The size of `mode_t', as computed by sizeof. */ +#cmakedefine SIZEOF_MODE_T @SIZEOF_MODE_T@ + + +/* Define if mkdir takes only one argument. */ +#cmakedefine MKDIR_TAKES_ONE_ARG 1 + + +/* Define to `long int' if does not define. */ +@OFF_T_DEF@ + +/* Define to `unsigned int' if does not define. */ +@SIZE_T_DEF@ + +/* Define to `int' if does not define. */ +@SSIZE_T_DEF@ + +/* Define to `int' if does not define. */ +@MODE_T_DEF@ + + +/* Define to 1 if you can safely include both and . */ +#ifdef __GNUC__ +# define TIME_WITH_SYS_TIME 1 +#else +# define TIME_WITH_SYS_TIME 0 +#endif + +#ifdef __AMIGA__ +# define LATIN1_FILENAMES 1 +#endif + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#cmakedefine WORDS_BIGENDIAN 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#cmakedefine _LARGEFILE_SOURCE 1 + +/* Define for large files, on AIX-style hosts. */ +#cmakedefine _LARGE_FILES 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline @INLINE_KEYWORD@ +#endif + +/* Version number of package */ +#cmakedefine VERSION "@VERSION@" + +#ifdef _MSC_VER +//not #if defined(_WIN32) || defined(_WIN64) because mingw has strncasecmp +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif diff --git a/libmspack/configure.ac b/libmspack/configure.ac index f3da180..970416c 100644 --- a/libmspack/configure.ac +++ b/libmspack/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) AC_INIT([libmspack],[0.10.1alpha],[kyzer@cabextract.org.uk]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.11]) +AM_INIT_AUTOMAKE([1.11 foreign]) AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR([mspack/mspack.h]) AC_CONFIG_HEADER([config.h]) diff --git a/libmspack/doc/CMakeLists.txt b/libmspack/doc/CMakeLists.txt new file mode 100644 index 0000000..a5257ba --- /dev/null +++ b/libmspack/doc/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package(Doxygen REQUIRED) + +set(DOXYGEN_GENERATE_HTML YES) +set(DOXYGEN_GENERATE_MAN NO) +set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +set(EXTRACT_ALL YES) +set(HIDE_UNDOC_MEMBERS YES) +set(JAVADOC_AUTOBRIEF YES) +set(OPTIMIZE_OUTPUT_FOR_C YES) +set(FULL_PATH_NAMES NO) +set(GENERATE_LATEX NO) + +doxygen_add_docs( + doxygen + ../mspack/mspack.h + COMMENT "Generate html documentation" +) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR}) diff --git a/libmspack/examples/CMakeLists.txt b/libmspack/examples/CMakeLists.txt new file mode 100644 index 0000000..805a6b1 --- /dev/null +++ b/libmspack/examples/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Example executables +# +add_executable(cabd_memory) +target_sources(cabd_memory + PRIVATE cabd_memory.c) +target_link_libraries(cabd_memory mscabd_ObjLib) + +add_executable(cabrip) +target_sources(cabrip + PRIVATE cabrip.c) +target_link_libraries(cabrip mspack_ObjLib) + +add_executable(chmextract) +target_sources(chmextract + PRIVATE chmextract.c) +target_link_libraries(chmextract mspack_ObjLib) + +add_executable(msexpand) +target_sources(msexpand + PRIVATE msexpand.c) +target_link_libraries(msexpand mspack_ObjLib) + +add_executable(multifh) +target_sources(multifh + PRIVATE multifh.c) +target_link_libraries(multifh mscabd_ObjLib) + +add_executable(oabextract) +target_sources(oabextract + PRIVATE oabextract.c) +target_link_libraries(oabextract mspack_ObjLib) diff --git a/libmspack/examples/cabd_memory.c b/libmspack/examples/cabd_memory.c index 84be146..46e539f 100644 --- a/libmspack/examples/cabd_memory.c +++ b/libmspack/examples/cabd_memory.c @@ -1,13 +1,13 @@ /* An implementation of the mspack_system interface using only memory */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" /* use a pointer to a mem_buf structure as "filenames" */ struct mem_buf { diff --git a/libmspack/examples/cabrip.c b/libmspack/examples/cabrip.c index 9dc142a..931822b 100644 --- a/libmspack/examples/cabrip.c +++ b/libmspack/examples/cabrip.c @@ -1,17 +1,35 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include -#include "mspack/macros.h" +#include "mspack.h" +//#include "mspack/macros.h" #if HAVE_FSEEKO # define fseek fseeko #endif +/* define LD and LU as printf-format for signed and unsigned long offsets */ +#if HAVE_INTTYPES_H +# include +#else +# define PRId64 "lld" +# define PRIu64 "llu" +# define PRId32 "ld" +# define PRIu32 "lu" +#endif + +#if SIZEOF_OFF_T >= 8 +# define LD PRId64 +# define LU PRIu64 +#else +# define LD PRId32 +# define LU PRIu32 +#endif + #define BUF_SIZE (1024*4096) char buf[BUF_SIZE]; diff --git a/libmspack/examples/chmextract.c b/libmspack/examples/chmextract.c index a75b4fe..2049e84 100644 --- a/libmspack/examples/chmextract.c +++ b/libmspack/examples/chmextract.c @@ -1,14 +1,14 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #include -#include +#include "error.h" #if HAVE_MKDIR # if MKDIR_TAKES_ONE_ARG @@ -22,6 +22,10 @@ # endif #endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + mode_t user_umask; /** @@ -103,7 +107,7 @@ int main(int argc, char *argv[]) { ensure_filepath(outname); if (chmd->extract(chmd, f[i], outname)) { printf("%s: extract error on \"%s\": %s\n", - *argv, f[i]->filename, ERROR(chmd)); + *argv, f[i]->filename, MSPACK_ERROR(chmd)); } free(outname); } @@ -112,7 +116,7 @@ int main(int argc, char *argv[]) { chmd->close(chmd, chm); } else { - printf("%s: can't open -- %s\n", *argv, ERROR(chmd)); + printf("%s: can't open -- %s\n", *argv, MSPACK_ERROR(chmd)); } } mspack_destroy_chm_decompressor(chmd); diff --git a/libmspack/examples/error.h b/libmspack/examples/error.h new file mode 100644 index 0000000..29c0fc5 --- /dev/null +++ b/libmspack/examples/error.h @@ -0,0 +1,57 @@ +#include +#include "mspack.h" + +/** + * Returns a string with an error message appropriate for the last error + * of an mspack compressor or decompressor. + * + * For use with mspack compressor and decompressor struct pointers. + * + * Example usage: + * + * @code + * if (chmd->extract(chmd, f[i], outname)) { + * printf("%s: extract error on \"%s\": %s\n", + * *argv, f[i]->filename, MSPACK_ERROR(chmd)); + * } + * @endcode + * + * @param base An mspack compressor or decompressor struct pointer. + * @return a constant string with an appropriate error message. + */ +#define MSPACK_ERROR(base) mspack_error_msg(base->last_error(base)) + +/** + * A function to convert the MSPACK error codes into strings. + * + * Example usage: + * + * @code + * if (err != MSPACK_ERR_OK) { + * fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], mspack_error_msg(err)); + * } + * @endcode + * + * @param int An MSPACK_ERR code. + * @return a constant string with an appropriate error message. + */ +static inline const char *mspack_error_msg(int error) { + static char buf[32]; + switch (error) { + case MSPACK_ERR_OK: return "no error"; + case MSPACK_ERR_ARGS: return "bad arguments to library function"; + case MSPACK_ERR_OPEN: return "error opening file"; + case MSPACK_ERR_READ: return "read error"; + case MSPACK_ERR_WRITE: return "write error"; + case MSPACK_ERR_SEEK: return "seek error"; + case MSPACK_ERR_NOMEMORY: return "out of memory"; + case MSPACK_ERR_SIGNATURE: return "bad signature"; + case MSPACK_ERR_DATAFORMAT: return "error in data format"; + case MSPACK_ERR_CHECKSUM: return "checksum error"; + case MSPACK_ERR_CRUNCH: return "compression error"; + case MSPACK_ERR_DECRUNCH: return "decompression error"; + } + + snprintf(buf, sizeof(buf), "unknown error %d", error); + return buf; +} diff --git a/libmspack/examples/msexpand.c b/libmspack/examples/msexpand.c index b9fc704..13d118b 100644 --- a/libmspack/examples/msexpand.c +++ b/libmspack/examples/msexpand.c @@ -1,14 +1,15 @@ /* acts like Microsoft's EXPAND.EXE */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include -#include +#include "mspack.h" + +#include "error.h" int main(int argc, char *argv[]) { struct msszdd_decompressor *szddd; @@ -34,7 +35,7 @@ int main(int argc, char *argv[]) { err = kwajd->decompress(kwajd, argv[1], argv[2]); } if (err != MSPACK_ERR_OK) { - fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], error_msg(err)); + fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], mspack_error_msg(err)); } } else { diff --git a/libmspack/examples/multifh.c b/libmspack/examples/multifh.c index 91cbe50..4bda21a 100644 --- a/libmspack/examples/multifh.c +++ b/libmspack/examples/multifh.c @@ -7,14 +7,14 @@ */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include #include -#include +#include "mspack.h" /* definitions */ diff --git a/libmspack/examples/oabextract.c b/libmspack/examples/oabextract.c index 53f675c..b9f51b9 100644 --- a/libmspack/examples/oabextract.c +++ b/libmspack/examples/oabextract.c @@ -1,13 +1,13 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" -#include +#include "error.h" int main(int argc, char *argv[]) { struct msoab_decompressor *oabd; @@ -22,11 +22,11 @@ int main(int argc, char *argv[]) { if ((oabd = mspack_create_oab_decompressor(NULL))) { if (argc == 3) { err = oabd->decompress(oabd, argv[1], argv[2]); - if (err) fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], error_msg(err)); + if (err) fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], mspack_error_msg(err)); } else if (argc == 4) { err = oabd->decompress_incremental(oabd, argv[2], argv[1], argv[3]); - if (err) fprintf(stderr, "%s + %s -> %s: %s\n", argv[1], argv[2], argv[3], error_msg(err)); + if (err) fprintf(stderr, "%s + %s -> %s: %s\n", argv[1], argv[2], argv[3], mspack_error_msg(err)); } else { fprintf(stderr, "Usage: %s \n", *argv); diff --git a/libmspack/mspack/.gitignore b/libmspack/mspack/.gitignore deleted file mode 100644 index baccf0c..0000000 --- a/libmspack/mspack/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.lo -*.o -.deps -.dirstamp -.libs diff --git a/libmspack/mspack/CMakeLists.txt b/libmspack/mspack/CMakeLists.txt new file mode 100644 index 0000000..de4ee6e --- /dev/null +++ b/libmspack/mspack/CMakeLists.txt @@ -0,0 +1,126 @@ +# +# mspack targets +# +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/mspack-version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h +) + +# The mspack OBJECT-library +add_library(mspack_ObjLib OBJECT) +target_sources(mspack_ObjLib + PRIVATE + system.h system.c + cab.h cabc.c cabd.c + chm.h chmc.c chmd.c + hlp.h hlpc.c hlpd.c + lit.h litc.c litd.c + kwaj.h kwajc.c kwajd.c + szdd.h szddc.c szddd.c + oab.h oabc.c oabd.c + lzx.h lzxc.c lzxd.c + mszip.h mszipc.c mszipd.c + qtm.h qtmd.c + readbits.h readhuff.h + lzss.h lzssd.c + des.h sha.h + crc32.c crc32.h + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h +) +target_include_directories(mspack_ObjLib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# The mscabd OBJECT-library +add_library(mscabd_ObjLib OBJECT) +target_sources(mscabd_ObjLib + PRIVATE + system.h system.c + cab.h cabd.c + lzx.h lzxd.c + mszip.h mszipd.c + qtm.h qtmd.c + readbits.h readhuff.h + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h +) +target_include_directories(mscabd_ObjLib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# The mschmd OBJECT-library +add_library(mschmd_ObjLib OBJECT) +target_sources(mschmd_ObjLib + PRIVATE + system.h system.c + chm.h chmd.c + lzx.h lzxd.c + readbits.h readhuff.h + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h +) +target_include_directories(mschmd_ObjLib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# Windows resource file +set(MSPACK_RES "") +if(WIN32) + configure_file( + version.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set(MSPACK_RES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +if(ENABLE_SHARED_LIB) + # The libmspack shared library. + add_library(mspack SHARED ${MSPACK_RES}) + target_sources(mspack + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.def + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h + ) + target_link_libraries(mspack PRIVATE mspack_ObjLib) + set_target_properties(mspack PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}) + if(WIN32) + set_target_properties(mspack PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + endif() + install(TARGETS mspack DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install( + FILES + mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + add_library( MSPack::mspack ALIAS mspack ) +endif() + +if(ENABLE_STATIC_LIB) + # The libmspack static library. + add_library(mspack_static STATIC) + target_sources(mspack_static + PRIVATE + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h + ) + target_link_libraries(mspack_static PRIVATE mspack_ObjLib) + set_target_properties(mspack_static PROPERTIES + COMPILE_FLAGS "${WARNCFLAGS}" + VERSION ${LT_VERSION} + SOVERSION ${LT_SOVERSION} + ARCHIVE_OUTPUT_NAME mspack_static) + target_compile_definitions(mspack_static PUBLIC MSPACK_STATICLIB) + install(TARGETS mspack_static DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install( + FILES + mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + add_library( MSPack::mspack_static ALIAS mspack_static ) +endif() diff --git a/libmspack/mspack/cabc.c b/libmspack/mspack/cabc.c index 242e034..08f3a81 100644 --- a/libmspack/mspack/cabc.c +++ b/libmspack/mspack/cabc.c @@ -9,8 +9,8 @@ /* CAB compression implementation */ -#include -#include +#include "system.h" +#include "cab.h" struct mscab_compressor * mspack_create_cab_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/cabd.c b/libmspack/mspack/cabd.c index 96eac41..4ee1e96 100644 --- a/libmspack/mspack/cabd.c +++ b/libmspack/mspack/cabd.c @@ -21,11 +21,11 @@ /* CAB decompression implementation */ -#include -#include -#include -#include -#include +#include "system.h" +#include "cab.h" +#include "mszip.h" +#include "lzx.h" +#include "qtm.h" /* Notes on compliance with cabinet specification: * @@ -1014,7 +1014,7 @@ static int cabd_extract(struct mscab_decompressor *base, struct mscabd_folder_p *fol; struct mspack_system *sys; struct mspack_file *fh; - off_t filelen; + unsigned int filelen; if (!self) return MSPACK_ERR_ARGS; if (!file) return self->error = MSPACK_ERR_ARGS; @@ -1051,7 +1051,7 @@ static int cabd_extract(struct mscab_decompressor *base, * In salvage mode, don't assume block sizes, just try decoding */ if (!self->salvage) { - off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX; + unsigned int maxlen = fol->base.num_blocks * CAB_BLOCKMAX; if ((file->offset + filelen) > maxlen) { sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " "cabinet set is incomplete", file->filename); diff --git a/libmspack/mspack/chm.h b/libmspack/mspack/chm.h index a85d2e1..4b19f15 100644 --- a/libmspack/mspack/chm.h +++ b/libmspack/mspack/chm.h @@ -10,7 +10,7 @@ #ifndef MSPACK_CHM_H #define MSPACK_CHM_H 1 -#include +#include "lzx.h" /* generic CHM definitions */ diff --git a/libmspack/mspack/chmc.c b/libmspack/mspack/chmc.c index 72f6c5b..bc3c69b 100644 --- a/libmspack/mspack/chmc.c +++ b/libmspack/mspack/chmc.c @@ -9,8 +9,8 @@ /* CHM compression implementation */ -#include -#include +#include "system.h" +#include "chm.h" struct mschm_compressor * mspack_create_chm_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/chmd.c b/libmspack/mspack/chmd.c index f6ba3dd..26daff9 100644 --- a/libmspack/mspack/chmd.c +++ b/libmspack/mspack/chmd.c @@ -9,8 +9,8 @@ /* CHM decompression implementation */ -#include -#include +#include "system.h" +#include "chm.h" /* prototypes */ static struct mschmd_header * chmd_open( diff --git a/libmspack/mspack/hlp.h b/libmspack/mspack/hlp.h index b7486fa..a6e3abc 100644 --- a/libmspack/mspack/hlp.h +++ b/libmspack/mspack/hlp.h @@ -10,7 +10,7 @@ #ifndef MSPACK_HLP_H #define MSPACK_HLP_H 1 -#include +#include "lzss.h" /* generic HLP definitions */ diff --git a/libmspack/mspack/hlpc.c b/libmspack/mspack/hlpc.c index 60eabfe..f3be51d 100644 --- a/libmspack/mspack/hlpc.c +++ b/libmspack/mspack/hlpc.c @@ -9,8 +9,8 @@ /* HLP compression implementation */ -#include -#include +#include "system.h" +#include "hlp.h" struct mshlp_compressor * mspack_create_hlp_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/hlpd.c b/libmspack/mspack/hlpd.c index 43354f0..b84557c 100644 --- a/libmspack/mspack/hlpd.c +++ b/libmspack/mspack/hlpd.c @@ -9,8 +9,8 @@ /* HLP decompression implementation */ -#include -#include +#include "system.h" +#include "hlp.h" struct mshlp_decompressor * mspack_create_hlp_decompressor(struct mspack_system *sys) diff --git a/libmspack/mspack/kwaj.h b/libmspack/mspack/kwaj.h index 09673c0..75425d9 100644 --- a/libmspack/mspack/kwaj.h +++ b/libmspack/mspack/kwaj.h @@ -10,7 +10,7 @@ #ifndef MSPACK_KWAJ_H #define MSPACK_KWAJ_H 1 -#include +#include "lzss.h" /* generic KWAJ definitions */ #define kwajh_Signature1 (0x00) diff --git a/libmspack/mspack/kwajc.c b/libmspack/mspack/kwajc.c index b88ed76..babfa21 100644 --- a/libmspack/mspack/kwajc.c +++ b/libmspack/mspack/kwajc.c @@ -9,8 +9,8 @@ /* KWAJ compression implementation */ -#include -#include +#include "system.h" +#include "kwaj.h" struct mskwaj_compressor * mspack_create_kwaj_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/kwajd.c b/libmspack/mspack/kwajd.c index 490778c..3965bfa 100644 --- a/libmspack/mspack/kwajd.c +++ b/libmspack/mspack/kwajd.c @@ -12,9 +12,9 @@ /* KWAJ decompression implementation */ -#include -#include -#include +#include "system.h" +#include "kwaj.h" +#include "mszip.h" /* prototypes */ static struct mskwajd_header *kwajd_open( @@ -373,7 +373,7 @@ static int kwajd_error(struct mskwaj_decompressor *base) } \ INJECT_BITS(*i_ptr++, 8); \ } while (0) -#include +#include "readbits.h" /* import huffman-reading macros and code */ #define TABLEBITS(tbl) KWAJ_TABLEBITS @@ -381,7 +381,7 @@ static int kwajd_error(struct mskwaj_decompressor *base) #define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] #define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] #define HUFF_ERROR return MSPACK_ERR_DATAFORMAT -#include +#include "readhuff.h" /* In the KWAJ LZH format, there is no special 'eof' marker, it just * ends. Depending on how many bits are left in the final byte when diff --git a/libmspack/mspack/lit.h b/libmspack/mspack/lit.h index 79ba44d..2ccc7dd 100644 --- a/libmspack/mspack/lit.h +++ b/libmspack/mspack/lit.h @@ -10,9 +10,9 @@ #ifndef MSPACK_LIT_H #define MSPACK_LIT_H 1 -#include -#include -#include +#include "lzx.h" +#include "des.h" +#include "sha.h" /* generic LIT definitions */ diff --git a/libmspack/mspack/litc.c b/libmspack/mspack/litc.c index a8a709a..3e17d00 100644 --- a/libmspack/mspack/litc.c +++ b/libmspack/mspack/litc.c @@ -9,8 +9,8 @@ /* LIT compression implementation */ -#include -#include +#include "system.h" +#include "lit.h" struct mslit_compressor * mspack_create_lit_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/litd.c b/libmspack/mspack/litd.c index 6e0dc9a..22df020 100644 --- a/libmspack/mspack/litd.c +++ b/libmspack/mspack/litd.c @@ -9,8 +9,8 @@ /* LIT decompression implementation */ -#include -#include +#include "system.h" +#include "lit.h" struct mslit_decompressor * mspack_create_lit_decompressor(struct mspack_system *sys) diff --git a/libmspack/mspack/lzssd.c b/libmspack/mspack/lzssd.c index bf2b82a..910baeb 100644 --- a/libmspack/mspack/lzssd.c +++ b/libmspack/mspack/lzssd.c @@ -11,8 +11,8 @@ * For further details, see the file COPYING.LIB distributed with libmspack */ -#include -#include +#include "system.h" +#include "lzss.h" #define ENSURE_BYTES do { \ if (i_ptr >= i_end) { \ diff --git a/libmspack/mspack/lzxc.c b/libmspack/mspack/lzxc.c index 1207a0d..3ad474b 100644 --- a/libmspack/mspack/lzxc.c +++ b/libmspack/mspack/lzxc.c @@ -12,7 +12,7 @@ /* LZX compression implementation */ -#include -#include +#include "system.h" +#include "lzx.h" /* todo */ diff --git a/libmspack/mspack/lzxd.c b/libmspack/mspack/lzxd.c index b13b1e8..de0d014 100644 --- a/libmspack/mspack/lzxd.c +++ b/libmspack/mspack/lzxd.c @@ -12,8 +12,8 @@ /* LZX decompression implementation */ -#include -#include +#include "system.h" +#include "lzx.h" /* Microsoft's LZX document (in cab-sdk.exe) and their implementation * of the com.ms.util.cab Java package do not concur. @@ -89,7 +89,7 @@ READ_IF_NEEDED; b1 = *i_ptr++; \ INJECT_BITS((b1 << 8) | b0, 16); \ } while (0) -#include +#include "readbits.h" /* import huffman-reading macros and code */ #define TABLEBITS(tbl) LZX_##tbl##_TABLEBITS @@ -97,7 +97,7 @@ #define HUFF_TABLE(tbl,idx) lzx->tbl##_table[idx] #define HUFF_LEN(tbl,idx) lzx->tbl##_len[idx] #define HUFF_ERROR return lzx->error = MSPACK_ERR_DECRUNCH -#include +#include "readhuff.h" /* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ #define BUILD_TABLE(tbl) \ diff --git a/libmspack/mspack/mspack-version.h.in b/libmspack/mspack/mspack-version.h.in new file mode 100644 index 0000000..cbf0c54 --- /dev/null +++ b/libmspack/mspack/mspack-version.h.in @@ -0,0 +1,32 @@ +#ifndef MSPACK_VER_H +#define MSPACK_VER_H + +/** + * @macro + * Version string of the mspack project release. + */ +#define MSPACK_VERSION "@PROJECT_VERSION@" + +/** + * @macro + * Numerical representation of the version string of the mspack project release. + * This is a 24 bit number with 8 bytes for major, 8 bites for minor, and 8 bits for patch. + * Ex: 1.2.3 becomes 0x010203 + */ +#define LIBMSPACK_VERSION "@LIBMSPACK_VERSION_NUM@" + +/** + * @macro + * Version string of the mspack library release. + */ +#define LIBMSPACK_VERSION "@LIBMSPACK_VERSION@" + +/** + * @macro + * Numerical representation of the version string of the mspack project release. + * This is a 24 bit number with 8 bytes for major, 8 bites for minor, and 8 bits for patch. + * Ex: 1.2.3 becomes 0x010203 + */ +#define LIBMSPACK_VERSION "@LIBMSPACK_VERSION_NUM@" + +#endif // MSPACK_VER_H \ No newline at end of file diff --git a/libmspack/mspack/mspack.h b/libmspack/mspack/mspack.h index 3e99624..e5940cd 100644 --- a/libmspack/mspack/mspack.h +++ b/libmspack/mspack/mspack.h @@ -164,6 +164,9 @@ extern "C" { #include #include +#include +#include +#include /** * System self-test function, to ensure both library and calling program diff --git a/libmspack/mspack/mszipc.c b/libmspack/mspack/mszipc.c index 2f1ecb2..30f84bf 100644 --- a/libmspack/mspack/mszipc.c +++ b/libmspack/mspack/mszipc.c @@ -12,7 +12,7 @@ /* MS-ZIP compression implementation */ -#include -#include +#include "system.h" +#include "mszip.h" /* todo */ diff --git a/libmspack/mspack/mszipd.c b/libmspack/mspack/mszipd.c index ca0c15b..743827f 100644 --- a/libmspack/mspack/mszipd.c +++ b/libmspack/mspack/mszipd.c @@ -12,8 +12,8 @@ /* MS-ZIP decompression implementation. */ -#include -#include +#include "system.h" +#include "mszip.h" /* import bit-reading macros and code */ #define BITS_TYPE struct mszipd_stream @@ -24,7 +24,7 @@ READ_IF_NEEDED; \ INJECT_BITS(*i_ptr++, 8); \ } while (0) -#include +#include "readbits.h" /* import huffman macros and code */ #define TABLEBITS(tbl) MSZIP_##tbl##_TABLEBITS @@ -32,7 +32,7 @@ #define HUFF_TABLE(tbl,idx) zip->tbl##_table[idx] #define HUFF_LEN(tbl,idx) zip->tbl##_len[idx] #define HUFF_ERROR return INF_ERR_HUFFSYM -#include +#include "readhuff.h" #define FLUSH_IF_NEEDED do { \ if (zip->window_posn == MSZIP_FRAME_SIZE) { \ diff --git a/libmspack/mspack/oab.h b/libmspack/mspack/oab.h index 7bd4993..d37fbb9 100644 --- a/libmspack/mspack/oab.h +++ b/libmspack/mspack/oab.h @@ -10,7 +10,7 @@ #ifndef MSPACK_OAB_H #define MSPACK_OAB_H 1 -#include +#include "system.h" /* generic OAB definitions */ diff --git a/libmspack/mspack/oabc.c b/libmspack/mspack/oabc.c index 327ce61..6bb1832 100644 --- a/libmspack/mspack/oabc.c +++ b/libmspack/mspack/oabc.c @@ -9,8 +9,8 @@ /* OAB compression implementation */ -#include -#include +#include "system.h" +#include "oab.h" struct msoab_compressor * mspack_create_oab_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/oabd.c b/libmspack/mspack/oabd.c index 6cd6541..ed3c2b7 100644 --- a/libmspack/mspack/oabd.c +++ b/libmspack/mspack/oabd.c @@ -22,10 +22,10 @@ /* OAB decompression implementation */ -#include -#include -#include -#include +#include "system.h" +#include "oab.h" +#include "lzx.h" +#include "crc32.h" /* prototypes */ static int oabd_decompress(struct msoab_decompressor *self, const char *input, diff --git a/libmspack/mspack/qtmc.c b/libmspack/mspack/qtmc.c index f6e3718..15554d9 100644 --- a/libmspack/mspack/qtmc.c +++ b/libmspack/mspack/qtmc.c @@ -12,7 +12,7 @@ /* Quantum compression implementation */ -#include -#include +#include "system.h" +#include "qtm.h" /* todo */ diff --git a/libmspack/mspack/qtmd.c b/libmspack/mspack/qtmd.c index df84d20..4a4275d 100644 --- a/libmspack/mspack/qtmd.c +++ b/libmspack/mspack/qtmd.c @@ -20,8 +20,8 @@ * http://www.speakeasy.org/~russotto/quantumcomp.html */ -#include -#include +#include "system.h" +#include "qtm.h" /* import bit-reading macros and code */ #define BITS_TYPE struct qtmd_stream @@ -33,7 +33,7 @@ READ_IF_NEEDED; b1 = *i_ptr++; \ INJECT_BITS((b0 << 8) | b1, 16); \ } while (0) -#include +#include "readbits.h" /* Quantum static data tables: * diff --git a/libmspack/mspack/system.c b/libmspack/mspack/system.c index d085551..8efccfe 100644 --- a/libmspack/mspack/system.c +++ b/libmspack/mspack/system.c @@ -8,10 +8,10 @@ */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif -#include +#include "system.h" int mspack_version(int entity) { switch (entity) { diff --git a/libmspack/mspack/system.h b/libmspack/mspack/system.h index 646a846..792440b 100644 --- a/libmspack/mspack/system.h +++ b/libmspack/mspack/system.h @@ -16,10 +16,11 @@ extern "C" { /* ensure config.h is read before mspack.h */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif -#include -#include + +#include "mspack.h" +#include "macros.h" /* assume exists */ #ifndef MSPACK_NO_DEFAULT_SYSTEM @@ -62,4 +63,4 @@ extern int mspack_valid_system(struct mspack_system *sys); } #endif -#endif +#endif \ No newline at end of file diff --git a/libmspack/mspack/szdd.h b/libmspack/mspack/szdd.h index e07c6b7..b9936b4 100644 --- a/libmspack/mspack/szdd.h +++ b/libmspack/mspack/szdd.h @@ -10,7 +10,7 @@ #ifndef MSPACK_SZDD_H #define MSPACK_SZDD_H 1 -#include +#include "lzss.h" /* input buffer size during decompression - not worth parameterising IMHO */ #define SZDD_INPUT_SIZE (2048) diff --git a/libmspack/mspack/szddc.c b/libmspack/mspack/szddc.c index cdd39a6..bf7e6f9 100644 --- a/libmspack/mspack/szddc.c +++ b/libmspack/mspack/szddc.c @@ -9,8 +9,8 @@ /* SZDD compression implementation */ -#include -#include +#include "system.h" +#include "szdd.h" struct msszdd_compressor * mspack_create_szdd_compressor(struct mspack_system *sys) diff --git a/libmspack/mspack/szddd.c b/libmspack/mspack/szddd.c index 100fa34..115d8c2 100644 --- a/libmspack/mspack/szddd.c +++ b/libmspack/mspack/szddd.c @@ -13,8 +13,8 @@ /* SZDD decompression implementation */ -#include -#include +#include "system.h" +#include "szdd.h" /* prototypes */ static struct msszddd_header *szddd_open( diff --git a/libmspack/mspack/version.rc.in b/libmspack/mspack/version.rc.in new file mode 100644 index 0000000..91dbf1e --- /dev/null +++ b/libmspack/mspack/version.rc.in @@ -0,0 +1,48 @@ +#include + +VS_VERSION_INFO VERSIONINFO + +FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 +PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 +FILEFLAGSMASK 0x3fL +FILEOS VOS__WINDOWS32 +#if defined(LIBMSPACK) + FILETYPE VFT_DLL +#else + FILETYPE VFT_APP +#endif +FILESUBTYPE 0x0L +#ifdef _DEBUG + #define VER_STR "@PROJECT_VERSION@.0 (MSVC debug)" + #define DBG "d" + FILEFLAGS 0x1L +#else + #define VER_STR "@PROJECT_VERSION@.0 (MSVC release)" + #define DBG "" + FILEFLAGS 0x0L +#endif +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + // VALUE "Comments", "" + VALUE "CompanyName", "libmspack, https://cabextract.org.uk/" + VALUE "FileDescription", "Compressors and decompressors for Microsoft formats" + VALUE "FileVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" +#if defined(LIBMSPACK) + VALUE "InternalName", "libmspack" + VALUE "OriginalFilename", "libmspack.dll" +#endif + VALUE "LegalCopyright", "Copyright (C) 2000-2019 Stuart Caie . All rights reserved." + VALUE "LegalTrademarks", "" + VALUE "ProductName", "libmspack" + VALUE "ProductVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" + // VALUE "SpecialBuild", "" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 0x4b0 + END +END diff --git a/libmspack/test/.gitignore b/libmspack/test/.gitignore index 94b6fe5..b4dc304 100644 --- a/libmspack/test/.gitignore +++ b/libmspack/test/.gitignore @@ -1,9 +1,3 @@ -*.log -*.o -*.trs -.deps -.dirstamp -.libs cabd_md5 cabd_test chmd_find diff --git a/libmspack/test/CMakeLists.txt b/libmspack/test/CMakeLists.txt new file mode 100644 index 0000000..39b6fcf --- /dev/null +++ b/libmspack/test/CMakeLists.txt @@ -0,0 +1,93 @@ +# +# The md5 OBJECT-library, required for some tests +# +add_library(md5_ObjLib OBJECT) +target_sources(md5_ObjLib + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/md5.c + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/md5.h + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/md5_fh.h) +target_include_directories(md5_ObjLib + PRIVATE + ${PROJECT_SOURCE_DIR} + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) + +# +# Test executables, run with `ctest` +# +add_executable(cabd_test) +target_sources(cabd_test + PRIVATE cabd_test.c) +target_link_libraries(cabd_test + PRIVATE mscabd_ObjLib md5_ObjLib) +target_include_directories(cabd_test PRIVATE ${PROJECT_SOURCE_DIR}) +set_target_properties(cabd_test PROPERTIES + COMPILE_DEFINITIONS TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test_files/cabd) +add_test(NAME "cabd_test" COMMAND cabd_test) + +add_executable(chmd_test) +target_sources(chmd_test + PRIVATE chmd_test.c) +target_link_libraries(chmd_test + PRIVATE mschmd_ObjLib md5_ObjLib) +target_include_directories(chmd_test PRIVATE ${PROJECT_SOURCE_DIR}) +set_target_properties(chmd_test PROPERTIES + COMPILE_DEFINITIONS TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test_files/chmd) +add_test(NAME "chmd_test" COMMAND chmd_test) + +add_executable(kwajd_test) +target_sources(kwajd_test + PRIVATE kwajd_test.c) +target_link_libraries(kwajd_test + PRIVATE mspack_ObjLib) +target_include_directories(kwajd_test PRIVATE ${PROJECT_SOURCE_DIR}) +set_target_properties(kwajd_test PROPERTIES + COMPILE_DEFINITIONS TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test_files/kwajd) +add_test(NAME "kwajd_test" COMMAND kwajd_test) + +# +# Other test executables +# +add_executable(cabd_md5) +target_sources(cabd_md5 + PRIVATE cabd_md5.c) +target_link_libraries(cabd_md5 + PRIVATE mscabd_ObjLib md5_ObjLib) +if(WIN32) + target_include_directories(cabd_md5 + PRIVATE + ${PROJECT_SOURCE_DIR} + win32) + target_sources(cabd_md5 PRIVATE win32/dirent.h) + set_target_properties(cabd_md5 PROPERTIES + COMPILE_DEFINITIONS HAVE_DIRENT_H) +endif() + +add_executable(chmd_find) +target_sources(chmd_find + PRIVATE chmd_find.c) +target_link_libraries(chmd_find + PRIVATE mschmd_ObjLib) +target_include_directories(chmd_find PRIVATE ${PROJECT_SOURCE_DIR}) + +add_executable(chmd_md5) +target_sources(chmd_md5 + PRIVATE chmd_md5.c) +target_link_libraries(chmd_md5 + PRIVATE mschmd_ObjLib md5_ObjLib) +target_include_directories(chmd_md5 PRIVATE ${PROJECT_SOURCE_DIR}) + +add_executable(chmd_order) +target_sources(chmd_order + PRIVATE chmd_order.c) +target_link_libraries(chmd_order + PRIVATE mschmd_ObjLib md5_ObjLib) +target_include_directories(chmd_order PRIVATE ${PROJECT_SOURCE_DIR}) + +add_executable(chminfo) +target_sources(chminfo + PRIVATE chminfo.c) +target_link_libraries(chminfo + PRIVATE mschmd_ObjLib) +target_include_directories(chminfo PRIVATE ${PROJECT_SOURCE_DIR}) diff --git a/libmspack/test/cabd_md5.c b/libmspack/test/cabd_md5.c index c02d9c9..b8f31b6 100644 --- a/libmspack/test/cabd_md5.c +++ b/libmspack/test/cabd_md5.c @@ -1,16 +1,20 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #include +#ifdef _MSC_VER +#include "dirent.h" +#else #include +#endif -#include -#include +#include "error.h" +#include "md5_fh.h" /** * Matches a cabinet's filename case-insensitively in the filesystem and @@ -98,7 +102,7 @@ int main(int argc, char *argv[]) { printf("*** %s\n", cabname); if (!(cab = cabd->open(cabd, cabname))) { - fprintf(stderr, "cab open error: %s\n", ERROR(cabd)); + fprintf(stderr, "cab open error: %s\n", MSPACK_ERROR(cabd)); continue; } @@ -111,12 +115,12 @@ int main(int argc, char *argv[]) { } if (!(c2 = cabd->open(cabd, newname))) { fprintf(stderr, "%s: error opening \"%s\" for prepend: %s\n", - cabname, newname, ERROR(cabd)); + cabname, newname, MSPACK_ERROR(cabd)); break; } if (cabd->prepend(cabd, c, c2) != MSPACK_ERR_OK) { fprintf(stderr, "%s: error prepending \"%s\": %s\n", - cabname, newname, ERROR(cabd)); + cabname, newname, MSPACK_ERROR(cabd)); break; } } @@ -130,12 +134,12 @@ int main(int argc, char *argv[]) { } if (!(c2 = cabd->open(cabd, newname))) { fprintf(stderr, "%s: error opening \"%s\" for append: %s\n", - cabname, newname, ERROR(cabd)); + cabname, newname, MSPACK_ERROR(cabd)); break; } if (cabd->append(cabd, c, c2) != MSPACK_ERR_OK) { fprintf(stderr, "%s: error appending \"%s\": %s\n", - cabname, newname, ERROR(cabd)); + cabname, newname, MSPACK_ERROR(cabd)); break; } } @@ -147,7 +151,7 @@ int main(int argc, char *argv[]) { } else { fprintf(stderr, "%s: error extracting \"%s\": %s\n", - cabname, file->filename, ERROR(cabd)); + cabname, file->filename, MSPACK_ERROR(cabd)); } } diff --git a/libmspack/test/cabd_test.c b/libmspack/test/cabd_test.c index b6dca39..2b94ed2 100644 --- a/libmspack/test/cabd_test.c +++ b/libmspack/test/cabd_test.c @@ -1,13 +1,13 @@ /* cabinet decompression regression test suite */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include #include -#include +#include "mspack.h" #define __tf3(x) #x #define __tf2(x) __tf3(x) @@ -20,6 +20,13 @@ unsigned int test_count = 0; if (!(x)) {printf("%s:%d FAILED %s\n",__func__,__LINE__,#x);exit(1);} \ } while (0) + +#ifdef _MSC_VER +# define DEV_NULL "NUL" +#else +# define DEV_NULL "/dev/null" +#endif + /* open where cab file doesn't exist */ void cabd_open_test_01() { struct mscab_decompressor *cabd; @@ -342,7 +349,7 @@ void cabd_extract_test_01() { TEST(cab = cabd->open(cabd, files[i])); TEST(cab->files != NULL); for (file = cab->files; file; file = file->next) { - int err = cabd->extract(cabd, file, "/dev/null"); + int err = cabd->extract(cabd, file, DEV_NULL); TEST(err == MSPACK_ERR_DATAFORMAT || err == MSPACK_ERR_DECRUNCH); } cabd->close(cabd, cab); @@ -363,11 +370,11 @@ void cabd_extract_test_02() { */ TEST(cabd = mspack_create_cab_decompressor(NULL)); TEST(cab = cabd->open(cabd, TESTFILE("cve-2014-9732-folders-segfault.cab"))); - err = cabd->extract(cabd, cab->files, "/dev/null"); + err = cabd->extract(cabd, cab->files, DEV_NULL); TEST(err == MSPACK_ERR_OK); - err = cabd->extract(cabd, cab->files->next, "/dev/null"); + err = cabd->extract(cabd, cab->files->next, DEV_NULL); TEST(err == MSPACK_ERR_DATAFORMAT || err == MSPACK_ERR_DECRUNCH); - err = cabd->extract(cabd, cab->files, "/dev/null"); + err = cabd->extract(cabd, cab->files, DEV_NULL); TEST(err == MSPACK_ERR_OK); cabd->close(cabd, cab); mspack_destroy_cab_decompressor(cabd); diff --git a/libmspack/test/chmd_find.c b/libmspack/test/chmd_find.c index cb95ed8..0b00438 100644 --- a/libmspack/test/chmd_find.c +++ b/libmspack/test/chmd_find.c @@ -1,15 +1,16 @@ /* chmd_find: tests fast-find functionality */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #include "mspack/macros.h" -#include + +#include "error.h" void find(struct mschm_decompressor *chmd, struct mschmd_header *chm, char *archive, char *filename, struct mschmd_file *compare) @@ -17,7 +18,7 @@ void find(struct mschm_decompressor *chmd, struct mschmd_header *chm, struct mschmd_file result; if (chmd->fast_find(chmd, chm, filename, &result, sizeof(result))) { fprintf(stderr, "%s: find error on \"%s\": %s\n", - archive, filename, ERROR(chmd)); + archive, filename, MSPACK_ERROR(chmd)); } else if (!result.section) { if (compare) { @@ -80,13 +81,13 @@ int main(int argc, char *argv[]) { } } else { - printf("%s: can't open -- %s\n", argv[1], ERROR(chmd)); + printf("%s: can't open -- %s\n", argv[1], MSPACK_ERROR(chmd)); } } chmd->close(chmd, chm); } else { - printf("%s: can't open -- %s\n", argv[1], ERROR(chmd)); + printf("%s: can't open -- %s\n", argv[1], MSPACK_ERROR(chmd)); } mspack_destroy_chm_decompressor(chmd); } diff --git a/libmspack/test/chmd_md5.c b/libmspack/test/chmd_md5.c index a72c664..18070a9 100644 --- a/libmspack/test/chmd_md5.c +++ b/libmspack/test/chmd_md5.c @@ -1,14 +1,14 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" -#include -#include +#include "error.h" +#include "md5_fh.h" static int sortfunc(const void *a, const void *b) { off_t diff = @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { for (i = 0; i < numf; i++) { if (chmd->extract(chmd, f[i], NULL)) { fprintf(stderr, "%s: extract error on \"%s\": %s\n", - *argv, f[i]->filename, ERROR(chmd)); + *argv, f[i]->filename, MSPACK_ERROR(chmd)); } else { printf("%s %s\n", md5_string, f[i]->filename); @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) { chmd->close(chmd, chm); } else { - fprintf(stderr, "%s: can't open -- %s\n", *argv, ERROR(chmd)); + fprintf(stderr, "%s: can't open -- %s\n", *argv, MSPACK_ERROR(chmd)); } } mspack_destroy_chm_decompressor(chmd); diff --git a/libmspack/test/chmd_order.c b/libmspack/test/chmd_order.c index 2696563..a52feb6 100644 --- a/libmspack/test/chmd_order.c +++ b/libmspack/test/chmd_order.c @@ -6,16 +6,16 @@ * - extracting files from two chms at the same time with one decompressor */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" -#include -#include +#include "error.h" +#include "md5_fh.h" struct my_file { struct mschmd_file *file; @@ -57,7 +57,7 @@ int main(int argc, char *argv[]) { f[i].file = file; if (chmd->extract(chmd, file, NULL)) { fprintf(stderr, "%s: O extract error on \"%s\": %s\n", - *argv, file->filename, ERROR(chmd)); + *argv, file->filename, MSPACK_ERROR(chmd)); continue; } memcpy(&f[i].ordered[0], md5_string, 32); @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) { printf("SX %s\n", f[i].file->filename); if (chmd->extract(chmd, f[i].file, NULL)) { fprintf(stderr, "%s: S extract error on \"%s\": %s\n", - *argv, f[i].file->filename, ERROR(chmd)); + *argv, f[i].file->filename, MSPACK_ERROR(chmd)); continue; } memcpy(&f[i].sorted[0], md5_string, 32); @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) { &f[i].result, sizeof(struct mschmd_file))) { fprintf(stderr, "%s: find error on \"%s\": %s\n", - *argv, f[i].file->filename, ERROR(chmd)); + *argv, f[i].file->filename, MSPACK_ERROR(chmd)); continue; } if (!f[i].result.section) { @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { } if (chmd->extract(chmd, &f[i].result, NULL)) { fprintf(stderr, "%s: F extract error on \"%s\": %s\n", - *argv, f[i].file->filename, ERROR(chmd)); + *argv, f[i].file->filename, MSPACK_ERROR(chmd)); continue; } memcpy(&f[i].fast_find[0], md5_string, 32); @@ -108,7 +108,7 @@ int main(int argc, char *argv[]) { chmd->extract(chmd, f[i].file, NULL); if (chmd->extract(chmd, &f[i].result, NULL)) { fprintf(stderr, "%s: M extract error on \"%s\": %s\n", - *argv, f[i].file->filename, ERROR(chmd)); + *argv, f[i].file->filename, MSPACK_ERROR(chmd)); continue; } memcpy(&f[i].mixed[0], md5_string, 32); @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { chmd->close(chmd, chm); } else { - printf("%s: can't open -- %s\n", *argv, ERROR(chmd)); + printf("%s: can't open -- %s\n", *argv, MSPACK_ERROR(chmd)); } } mspack_destroy_chm_decompressor(chmd); diff --git a/libmspack/test/chmd_test.c b/libmspack/test/chmd_test.c index 5e471f2..54a3644 100644 --- a/libmspack/test/chmd_test.c +++ b/libmspack/test/chmd_test.c @@ -1,15 +1,17 @@ /* CHM regression test suite */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include +#ifdef HAVE_UNISTD_H #include +#endif -#include +#include "mspack.h" #define __tf3(x) #x #define __tf2(x) __tf3(x) diff --git a/libmspack/test/chminfo.c b/libmspack/test/chminfo.c index c866dfc..ae9ef34 100644 --- a/libmspack/test/chminfo.c +++ b/libmspack/test/chminfo.c @@ -1,13 +1,15 @@ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include +#ifdef HAVE_UNISTD_H #include +#endif #include -#include +#include "mspack.h" #include "mspack/macros.h" #define FILENAME ".chminfo-temp" diff --git a/libmspack/test/error.h b/libmspack/test/error.h index b1d627b..29c0fc5 100644 --- a/libmspack/test/error.h +++ b/libmspack/test/error.h @@ -1,6 +1,41 @@ -#define ERROR(base) error_msg(base->last_error(base)) +#include +#include "mspack.h" -const char *error_msg(int error) { +/** + * Returns a string with an error message appropriate for the last error + * of an mspack compressor or decompressor. + * + * For use with mspack compressor and decompressor struct pointers. + * + * Example usage: + * + * @code + * if (chmd->extract(chmd, f[i], outname)) { + * printf("%s: extract error on \"%s\": %s\n", + * *argv, f[i]->filename, MSPACK_ERROR(chmd)); + * } + * @endcode + * + * @param base An mspack compressor or decompressor struct pointer. + * @return a constant string with an appropriate error message. + */ +#define MSPACK_ERROR(base) mspack_error_msg(base->last_error(base)) + +/** + * A function to convert the MSPACK error codes into strings. + * + * Example usage: + * + * @code + * if (err != MSPACK_ERR_OK) { + * fprintf(stderr, "%s -> %s: %s\n", argv[1], argv[2], mspack_error_msg(err)); + * } + * @endcode + * + * @param int An MSPACK_ERR code. + * @return a constant string with an appropriate error message. + */ +static inline const char *mspack_error_msg(int error) { static char buf[32]; switch (error) { case MSPACK_ERR_OK: return "no error"; diff --git a/libmspack/test/kwajd_test.c b/libmspack/test/kwajd_test.c index e3c3370..0e0c199 100644 --- a/libmspack/test/kwajd_test.c +++ b/libmspack/test/kwajd_test.c @@ -1,13 +1,13 @@ /* KWAJ regression test suite */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #define __tf3(x) #x #define __tf2(x) __tf3(x) diff --git a/libmspack/test/md5.c b/libmspack/test/md5.c index a3d2277..a689756 100644 --- a/libmspack/test/md5.c +++ b/libmspack/test/md5.c @@ -21,14 +21,14 @@ /* Written by Ulrich Drepper , 1995. */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include #include -#include +#include "md5.h" #ifdef _LIBC # include diff --git a/libmspack/test/md5_fh.h b/libmspack/test/md5_fh.h index 9bc2900..8a2dd09 100644 --- a/libmspack/test/md5_fh.h +++ b/libmspack/test/md5_fh.h @@ -5,7 +5,7 @@ * probably the most obvious. The code is not multithreadable. */ -#include +#include "md5.h" #include #include diff --git a/libmspack/test/win32/dirent.h b/libmspack/test/win32/dirent.h new file mode 100644 index 0000000..f7a46da --- /dev/null +++ b/libmspack/test/win32/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/