From 6572fa737bbdd91a3b8ac53f34d6e71726361d17 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sun, 7 Jun 2020 21:33:47 -0400 Subject: [PATCH] Add CMake support (cabextract) Adds CMake tooling for cabextract to the project. Converts cabextract README to Markdown (.md) so it will look pretty on Github, and adds build instructions to cabextract README.md. --- cabextract/.gitignore | 121 +- cabextract/CMakeLists.txt | 255 +++++ cabextract/CMakeOptions.txt | 7 + cabextract/README | 46 - cabextract/README.md | 184 ++++ cabextract/autogen.sh | 13 +- cabextract/cmake/COPYING-CMAKE-SCRIPTS | 22 + cabextract/cmake/CheckFileOffsetBits.c | 14 + cabextract/cmake/CheckFileOffsetBits.cmake | 43 + cabextract/cmake/ExtractValidFlags.cmake | 18 + cabextract/cmake/FindMSPack.cmake | 86 ++ cabextract/cmake/TestInline.c | 5 + cabextract/cmake/TestInline.cmake | 22 + cabextract/cmake/compiletest_mkdir.c | 13 + cabextract/config.h.in.cmake | 152 +++ cabextract/mspack/CMakeLists.txt | 36 + cabextract/src/cabextract.c | 21 +- cabextract/test/CMakeLists.txt | 28 + cabextract/win32/dirent.h | 1160 ++++++++++++++++++++ libmspack/CMakeLists.txt | 11 +- libmspack/README.md | 8 +- libmspack/mspack/CMakeLists.txt | 7 +- 22 files changed, 2188 insertions(+), 84 deletions(-) create mode 100644 cabextract/CMakeLists.txt create mode 100644 cabextract/CMakeOptions.txt delete mode 100644 cabextract/README create mode 100644 cabextract/README.md create mode 100644 cabextract/cmake/COPYING-CMAKE-SCRIPTS create mode 100644 cabextract/cmake/CheckFileOffsetBits.c create mode 100644 cabextract/cmake/CheckFileOffsetBits.cmake create mode 100644 cabextract/cmake/ExtractValidFlags.cmake create mode 100644 cabextract/cmake/FindMSPack.cmake create mode 100644 cabextract/cmake/TestInline.c create mode 100644 cabextract/cmake/TestInline.cmake create mode 100644 cabextract/cmake/compiletest_mkdir.c create mode 100644 cabextract/config.h.in.cmake create mode 100644 cabextract/mspack/CMakeLists.txt create mode 100644 cabextract/test/CMakeLists.txt create mode 100644 cabextract/win32/dirent.h diff --git a/cabextract/.gitignore b/cabextract/.gitignore index ed26884..3b8a8ef 100644 --- a/cabextract/.gitignore +++ b/cabextract/.gitignore @@ -1,26 +1,109 @@ -*.a -*.o +# 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..9429acf --- /dev/null +++ b/cabextract/CMakeLists.txt @@ -0,0 +1,255 @@ +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(iconv.h HAVE_ICONV_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}) + +# 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() +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} +") 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..5daec49 --- /dev/null +++ b/cabextract/README.md @@ -0,0 +1,184 @@ +# 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 1cf9bd1..caa2ef7 100755 --- a/cabextract/autogen.sh +++ b/cabextract/autogen.sh @@ -1,7 +1,18 @@ #!/bin/sh # Runs the autoreconf tool, creating the configure script -autoreconf -i -W all +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 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/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..99aeb7a --- /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_ICONV_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/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 0bc870a..bb9ceca 100644 --- a/cabextract/src/cabextract.c +++ b/cabextract/src/cabextract.c @@ -31,7 +31,11 @@ #include #include #include +#ifndef _WIN32 #include +#else +#include +#endif #include #include #include @@ -111,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; }; @@ -154,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"; @@ -188,6 +192,7 @@ static void forget_files(struct file_mem **fml); static void add_filter(char *arg); static void free_filters(); static int ensure_filepath(char *path); +static char *cab_error(struct mscab_decompressor *cd); static struct mspack_file *cabx_open(struct mspack_system *this, const char *filename, int mode); @@ -451,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; @@ -472,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; } @@ -782,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] = '/'; @@ -909,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/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/CMakeLists.txt b/libmspack/CMakeLists.txt index 2a2806d..170d27d 100644 --- a/libmspack/CMakeLists.txt +++ b/libmspack/CMakeLists.txt @@ -216,7 +216,7 @@ 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 ${PACKAGE_VERSION}) +set(VERSION ${PROJECT_VERSION}) # Generate config.h add_definitions(-DHAVE_CONFIG_H) @@ -241,7 +241,6 @@ if(ENABLE_EXAMPLES) endif() enable_testing() -add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) add_subdirectory(test) if(ENABLE_DOCS) @@ -254,10 +253,10 @@ endif() 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} + 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} diff --git a/libmspack/README.md b/libmspack/README.md index b2f30df..8c9ce75 100644 --- a/libmspack/README.md +++ b/libmspack/README.md @@ -40,8 +40,8 @@ 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 +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: @@ -167,7 +167,7 @@ provided you meet ALL of the following conditions: libmspack is bundled with programs which demonstrate the library's features. -| Program | Description +| 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 @@ -177,7 +177,7 @@ libmspack is bundled with programs which demonstrate the library's features. | 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 diff --git a/libmspack/mspack/CMakeLists.txt b/libmspack/mspack/CMakeLists.txt index 08dd347..de4ee6e 100644 --- a/libmspack/mspack/CMakeLists.txt +++ b/libmspack/mspack/CMakeLists.txt @@ -86,6 +86,9 @@ if(ENABLE_SHARED_LIB) 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 @@ -93,7 +96,7 @@ if(ENABLE_SHARED_LIB) ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - add_library( libmspack::mspack ALIAS mspack ) + add_library( MSPack::mspack ALIAS mspack ) endif() if(ENABLE_STATIC_LIB) @@ -119,5 +122,5 @@ if(ENABLE_STATIC_LIB) ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - add_library( libmspack::mspack_static ALIAS mspack_static ) + add_library( MSPack::mspack_static ALIAS mspack_static ) endif()