From b15611f651f3190c5db6baef609b03f898343510 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sun, 19 Jan 2020 23:32:54 -0500 Subject: [PATCH 1/5] Add CMake support Adds CMake tooling for libmspack to the project. This attempts to replicate every feature available in the autotools tooling and includes support for building on Windows. Adds a mspack-version.h header file, generated from mspack-version.h.in. mspack-version.h is to be installed alongside mspack.h so that application developers can use the version numbers and strings in the event that the API is modified and the application must maintain backwards compatibility. Adds error handling to autogen.sh. Reformats libmspack's README as Markdown. Adds mspack_error_msg() and MSPACK_ERROR() macro to mspack.h API. The new API is a cross between the cab_error() function previously found in cabextract and error_msg() found in test/error.h. This removes the need the examples to depend on test/error.h, a header not exported by any build target, and removes the code duplication with cabextract. Fixes __func__ macro for Windows. --- cabextract/autogen.sh | 6 +- cabextract/getopt.c | 2 +- cabextract/getopt1.c | 2 +- cabextract/md5.c | 4 +- cabextract/src/cabextract.c | 11 +- cabextract/src/cabinfo.c | 2 +- libmspack/.gitignore | 116 ++- libmspack/CMakeLists.txt | 271 +++++ libmspack/CMakeOptions.txt | 15 + libmspack/Makefile.am | 14 +- libmspack/{README => README.md} | 145 ++- libmspack/autogen.sh | 20 +- libmspack/cmake/COPYING-CMAKE-SCRIPTS | 22 + libmspack/cmake/CheckFileOffsetBits.c | 14 + libmspack/cmake/CheckFileOffsetBits.cmake | 43 + libmspack/cmake/ExtractValidFlags.cmake | 18 + libmspack/cmake/TestInline.c | 5 + libmspack/cmake/TestInline.cmake | 22 + libmspack/cmake/Version.cmake | 11 + libmspack/cmake/compiletest_mkdir.c | 13 + libmspack/config.h.in.cmake | 152 +++ libmspack/configure.ac | 2 +- libmspack/doc/CMakeLists.txt | 19 + libmspack/examples/CMakeLists.txt | 32 + libmspack/examples/cabd_memory.c | 4 +- libmspack/examples/cabrip.c | 24 +- libmspack/examples/chmextract.c | 14 +- libmspack/examples/error.h | 57 + libmspack/examples/msexpand.c | 9 +- libmspack/examples/multifh.c | 4 +- libmspack/examples/oabextract.c | 10 +- libmspack/mspack/.gitignore | 5 - libmspack/mspack/CMakeLists.txt | 123 +++ libmspack/mspack/cabc.c | 4 +- libmspack/mspack/cabd.c | 10 +- libmspack/mspack/chm.h | 2 +- libmspack/mspack/chmc.c | 4 +- libmspack/mspack/chmd.c | 4 +- libmspack/mspack/hlp.h | 2 +- libmspack/mspack/hlpc.c | 4 +- libmspack/mspack/hlpd.c | 4 +- libmspack/mspack/kwaj.h | 2 +- libmspack/mspack/kwajc.c | 4 +- libmspack/mspack/kwajd.c | 10 +- libmspack/mspack/lit.h | 6 +- libmspack/mspack/litc.c | 4 +- libmspack/mspack/litd.c | 4 +- libmspack/mspack/lzssd.c | 4 +- libmspack/mspack/lzxc.c | 4 +- libmspack/mspack/lzxd.c | 8 +- libmspack/mspack/mspack-version.h.in | 32 + libmspack/mspack/mspack.h | 3 + libmspack/mspack/mszipc.c | 4 +- libmspack/mspack/mszipd.c | 8 +- libmspack/mspack/oab.h | 2 +- libmspack/mspack/oabc.c | 4 +- libmspack/mspack/oabd.c | 8 +- libmspack/mspack/qtmc.c | 4 +- libmspack/mspack/qtmd.c | 6 +- libmspack/mspack/system.c | 4 +- libmspack/mspack/system.h | 9 +- libmspack/mspack/szdd.h | 2 +- libmspack/mspack/szddc.c | 4 +- libmspack/mspack/szddd.c | 4 +- libmspack/mspack/version.rc.in | 48 + libmspack/test/.gitignore | 6 - libmspack/test/CMakeLists.txt | 93 ++ libmspack/test/cabd_md5.c | 24 +- libmspack/test/cabd_test.c | 4 +- libmspack/test/chmd_find.c | 13 +- libmspack/test/chmd_md5.c | 12 +- libmspack/test/chmd_order.c | 20 +- libmspack/test/chmd_test.c | 6 +- libmspack/test/chminfo.c | 6 +- libmspack/test/error.h | 39 +- libmspack/test/kwajd_test.c | 4 +- libmspack/test/md5.c | 4 +- libmspack/test/md5_fh.h | 2 +- libmspack/test/win32/dirent.h | 1160 +++++++++++++++++++++ 79 files changed, 2614 insertions(+), 212 deletions(-) create mode 100644 libmspack/CMakeLists.txt create mode 100644 libmspack/CMakeOptions.txt rename libmspack/{README => README.md} (52%) create mode 100644 libmspack/cmake/COPYING-CMAKE-SCRIPTS create mode 100644 libmspack/cmake/CheckFileOffsetBits.c create mode 100644 libmspack/cmake/CheckFileOffsetBits.cmake create mode 100644 libmspack/cmake/ExtractValidFlags.cmake create mode 100644 libmspack/cmake/TestInline.c create mode 100644 libmspack/cmake/TestInline.cmake create mode 100644 libmspack/cmake/Version.cmake create mode 100644 libmspack/cmake/compiletest_mkdir.c create mode 100644 libmspack/config.h.in.cmake create mode 100644 libmspack/doc/CMakeLists.txt create mode 100644 libmspack/examples/CMakeLists.txt create mode 100644 libmspack/examples/error.h delete mode 100644 libmspack/mspack/.gitignore create mode 100644 libmspack/mspack/CMakeLists.txt create mode 100644 libmspack/mspack/mspack-version.h.in create mode 100644 libmspack/mspack/version.rc.in create mode 100644 libmspack/test/CMakeLists.txt create mode 100644 libmspack/test/win32/dirent.h diff --git a/cabextract/autogen.sh b/cabextract/autogen.sh index 19bda6c..1cf9bd1 100755 --- a/cabextract/autogen.sh +++ b/cabextract/autogen.sh @@ -2,4 +2,8 @@ # Runs the autoreconf tool, creating the configure script autoreconf -i -W all -echo you can now run ./configure +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/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/src/cabextract.c b/cabextract/src/cabextract.c index af5ed20..0bc870a 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 @@ -76,8 +76,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[] = { @@ -184,7 +188,6 @@ 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); 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/libmspack/.gitignore b/libmspack/.gitignore index 84c02e4..393f115 100644 --- a/libmspack/.gitignore +++ b/libmspack/.gitignore @@ -1,22 +1,100 @@ +# 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..2a2806d --- /dev/null +++ b/libmspack/CMakeLists.txt @@ -0,0 +1,271 @@ +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(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}) +# 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 ${PACKAGE_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_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) +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.md similarity index 52% rename from libmspack/README rename to libmspack/README.md index cf5aee9..b2f30df 100644 --- a/libmspack/README +++ b/libmspack/README.md @@ -1,4 +1,4 @@ -libmspack 0.10.1alpha +# libmspack 0.10.1alpha The purpose of libmspack is to provide compressors and decompressors, archivers and dearchivers for Microsoft compression formats: CAB, CHM, WIM, @@ -20,18 +20,22 @@ translation functionality. All file I/O is abstracted, although a default implementation using the standard C library is provided. -DOCUMENTATION +## 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 +## 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. @@ -52,8 +56,92 @@ 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 +## LEGAL ISSUES To the best of my knowledge, libmspack does not infringe on any compression or decompression patents. However, this is not legal @@ -75,39 +163,41 @@ provided you meet ALL of the following conditions: and either include the full libmspack distribution with your code, or provide access to it as per clause 4 of the LGPL. -EXAMPLE CODE +## 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 +| 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 +#include "mspack.h" int main() { struct mscab_decompressor *cabd; @@ -129,3 +219,4 @@ int main() { } 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..99aeb7a --- /dev/null +++ b/libmspack/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/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..08dd347 --- /dev/null +++ b/libmspack/mspack/CMakeLists.txt @@ -0,0 +1,123 @@ +# +# 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}) + install(TARGETS mspack DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install( + FILES + mspack.h + ${CMAKE_CURRENT_BINARY_DIR}/mspack-version.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + add_library( libmspack::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( libmspack::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..7f21eb3 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: * 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..7585275 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 _WIN32 +#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..ab82dcd 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) 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*/ From 58911edf044368b107fb059d9529ec30478de2c8 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sun, 29 Mar 2020 18:18:03 -0400 Subject: [PATCH 2/5] Fix cabd tests on Windows On Windows, the tests should write to "NUL" instead of "/dev/null". Resolved an issue where a large unsigned int cast to an off_t resulted in a negative value, because off_t is 32-bits instead of 64. --- libmspack/mspack/cabd.c | 4 ++-- libmspack/test/cabd_md5.c | 2 +- libmspack/test/cabd_test.c | 15 +++++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libmspack/mspack/cabd.c b/libmspack/mspack/cabd.c index 7f21eb3..4ee1e96 100644 --- a/libmspack/mspack/cabd.c +++ b/libmspack/mspack/cabd.c @@ -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/test/cabd_md5.c b/libmspack/test/cabd_md5.c index 7585275..b8f31b6 100644 --- a/libmspack/test/cabd_md5.c +++ b/libmspack/test/cabd_md5.c @@ -7,7 +7,7 @@ #include #include "mspack.h" #include -#ifdef _WIN32 +#ifdef _MSC_VER #include "dirent.h" #else #include diff --git a/libmspack/test/cabd_test.c b/libmspack/test/cabd_test.c index ab82dcd..2b94ed2 100644 --- a/libmspack/test/cabd_test.c +++ b/libmspack/test/cabd_test.c @@ -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); From 6572fa737bbdd91a3b8ac53f34d6e71726361d17 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sun, 7 Jun 2020 21:33:47 -0400 Subject: [PATCH 3/5] 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() From 67db401cabfdc80cd7f29e860f7c783f5a4be95f Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sat, 1 May 2021 18:31:12 -0700 Subject: [PATCH 4/5] CMake: Fix iconv detection The current implementations check for iconv.h and set HAVE_ICONV_H but don't set HAVE_ICONV, which is the macro we actually depend on. In the autotools build system, HAVE_ICONV is set by the `AM_ICONV` macro, if you have gettext-devel installed (note, this will cause an autoreconf warning if you don't have gettext-devel installed). Anyways... to fix this in CMake, I've added FindICONV.cmake module which is a slightly modified variant of the official CMake iconv detection module. This one has some additional features including setting the ICONV_CONST macro as needed. This commit fixes the cabextract "encoding" test, which was the final outstanding issue with CMake support, excluding the fact that there are no cabextract feature tests for Windows. I think this is acceptable for now, since it's not like there were any before switching to CMake. --- cabextract/.gitignore | 2 + cabextract/CMakeLists.txt | 17 +++- cabextract/cmake/FindICONV.cmake | 168 +++++++++++++++++++++++++++++++ cabextract/config.h.in.cmake | 8 +- libmspack/.gitignore | 2 + libmspack/CMakeLists.txt | 1 - libmspack/config.h.in.cmake | 3 - 7 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 cabextract/cmake/FindICONV.cmake diff --git a/cabextract/.gitignore b/cabextract/.gitignore index 3b8a8ef..d912544 100644 --- a/cabextract/.gitignore +++ b/cabextract/.gitignore @@ -1,3 +1,5 @@ +/build + # CabExtract specific COPYING INSTALL diff --git a/cabextract/CMakeLists.txt b/cabextract/CMakeLists.txt index 9429acf..51a3bea 100644 --- a/cabextract/CMakeLists.txt +++ b/cabextract/CMakeLists.txt @@ -53,7 +53,6 @@ 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) @@ -191,6 +190,16 @@ set(libdir ${CMAKE_INSTALL_FULL_LIBDIR}) set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) set(VERSION ${PROJECT_VERSION}) +# +# External library dependencies +# +if(NOT WIN32) + find_package(ICONV) + if(ICONV_FOUND) + set(HAVE_ICONV 1) + endif() +endif() + # Generate config.h add_definitions(-DHAVE_CONFIG_H) configure_file(config.h.in.cmake config.h) @@ -219,6 +228,9 @@ 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) + if(HAVE_ICONV) + target_link_libraries( cabextract PRIVATE ICONV::Iconv ) + endif() endif() target_include_directories(cabextract PRIVATE ${PROJECT_SOURCE_DIR}) target_link_libraries(cabextract MSPack::mspack) @@ -252,4 +264,7 @@ message(STATUS "Summary of build options: WARNCFLAGS: ${WARNCFLAGS} Features: External mspack: ${ENABLE_EXTERNAL_MSPACK} + Text conversion: + iconv ${ICONV_INCLUDE_DIRS} + ${ICONV_LIBRARIES} ") 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/config.h.in.cmake b/cabextract/config.h.in.cmake index 99aeb7a..9fecfff 100644 --- a/cabextract/config.h.in.cmake +++ b/cabextract/config.h.in.cmake @@ -35,9 +35,6 @@ /* 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 @@ -79,7 +76,10 @@ /* Define to empty if `const' does not conform to ANSI C. */ -#cmakedefine ICONV_CONST "@ICONV_CONST@" +#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. */ diff --git a/libmspack/.gitignore b/libmspack/.gitignore index 393f115..59ca3a9 100644 --- a/libmspack/.gitignore +++ b/libmspack/.gitignore @@ -1,3 +1,5 @@ +/build + # Logs *.log diff --git a/libmspack/CMakeLists.txt b/libmspack/CMakeLists.txt index 170d27d..0306a8d 100644 --- a/libmspack/CMakeLists.txt +++ b/libmspack/CMakeLists.txt @@ -80,7 +80,6 @@ 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) diff --git a/libmspack/config.h.in.cmake b/libmspack/config.h.in.cmake index 99aeb7a..ac25bba 100644 --- a/libmspack/config.h.in.cmake +++ b/libmspack/config.h.in.cmake @@ -35,9 +35,6 @@ /* 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 From 500c59788ddc2abc36ac2d0a9053a6b70ea4bf65 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sat, 1 May 2021 18:58:41 -0700 Subject: [PATCH 5/5] CMake: Add GitHub Actions Add CMake build for cabextract and libmspack on GitHub Actions. Will test on Ubuntu, macOS, Windows. Added build badges to the readme for each project. --- .github/workflows/cmake-cabextract.yml | 141 +++++++++++++++++++++++++ .github/workflows/cmake-libmspack.yml | 141 +++++++++++++++++++++++++ cabextract/CMakeLists.txt | 15 ++- cabextract/README.md | 2 + libmspack/README.md | 2 + 5 files changed, 293 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/cmake-cabextract.yml create mode 100644 .github/workflows/cmake-libmspack.yml 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/CMakeLists.txt b/cabextract/CMakeLists.txt index 51a3bea..c8ab402 100644 --- a/cabextract/CMakeLists.txt +++ b/cabextract/CMakeLists.txt @@ -193,11 +193,9 @@ set(VERSION ${PROJECT_VERSION}) # # External library dependencies # -if(NOT WIN32) - find_package(ICONV) - if(ICONV_FOUND) - set(HAVE_ICONV 1) - endif() +find_package(ICONV) +if(ICONV_FOUND) + set(HAVE_ICONV 1) endif() # Generate config.h @@ -228,10 +226,11 @@ 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) - if(HAVE_ICONV) - target_link_libraries( cabextract PRIVATE ICONV::Iconv ) - endif() 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}) diff --git a/cabextract/README.md b/cabextract/README.md index 5daec49..1c162c3 100644 --- a/cabextract/README.md +++ b/cabextract/README.md @@ -1,5 +1,7 @@ # cabextract 1.9.1 + + A program to extract Microsoft Cabinet files. (C) 2000-2019 Stuart Caie diff --git a/libmspack/README.md b/libmspack/README.md index 8c9ce75..9a37495 100644 --- a/libmspack/README.md +++ b/libmspack/README.md @@ -1,5 +1,7 @@ # 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,