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..482ba4b 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); @@ -409,7 +412,7 @@ static int process_cabinet(char *basename) { /* search the file for cabinets */ if (!(basecab = cabd->search(cabd, basename))) { if (cabd->last_error(cabd)) { - fprintf(stderr, "%s: %s\n", basename, cab_error(cabd)); + fprintf(stderr, "%s: %s\n", basename, MSPACK_ERROR(cabd)); } else { fprintf(stderr, "%s: no valid cabinets found\n", basename); @@ -489,7 +492,7 @@ static int process_cabinet(char *basename) { else if (args.test) { if (cabd->extract(cabd, file, TEST_FNAME)) { /* file failed to extract */ - printf(" %s failed (%s)\n", name, cab_error(cabd)); + printf(" %s failed (%s)\n", name, MSPACK_ERROR(cabd)); errors++; } else { @@ -516,7 +519,7 @@ static int process_cabinet(char *basename) { /* extracting to stdout */ if (cabd->extract(cabd, file, STDOUT_FNAME)) { fprintf(stderr, "%s(%s): %s\n", STDOUT_FNAME, name, - cab_error(cabd)); + MSPACK_ERROR(cabd)); errors++; } } @@ -530,7 +533,7 @@ static int process_cabinet(char *basename) { } else { if (cabd->extract(cabd, file, name)) { - fprintf(stderr, "%s: %s\n", name, cab_error(cabd)); + fprintf(stderr, "%s: %s\n", name, MSPACK_ERROR(cabd)); errors++; } else { @@ -580,7 +583,7 @@ static void load_spanning_cabinets(struct mscabd_cabinet *basecab, } if (!(cab2 = cabd->open(cabd,name)) || cabd->prepend(cabd, cab, cab2)) { fprintf(stderr, "%s: can't prepend %s: %s\n", basename, - cab->prevname, cab_error(cabd)); + cab->prevname, MSPACK_ERROR(cabd)); if (cab2) cabd->close(cabd, cab2); break; } @@ -600,7 +603,7 @@ static void load_spanning_cabinets(struct mscabd_cabinet *basecab, } if (!(cab2 = cabd->open(cabd,name)) || cabd->append(cabd, cab, cab2)) { fprintf(stderr, "%s: can't append %s: %s\n", basename, - cab->nextname, cab_error(cabd)); + cab->nextname, MSPACK_ERROR(cabd)); if (cab2) cabd->close(cabd, cab2); break; } @@ -1136,37 +1139,6 @@ static int ensure_filepath(char *path) { return 1; } -/** - * Returns a string with an error message appropriate for the last error - * of the CAB decompressor. - * - * @param cd the CAB decompressor. - * @return a constant string with an appropriate error message. - */ -static char *cab_error(struct mscab_decompressor *cd) { - switch (cd->last_error(cd)) { - case MSPACK_ERR_OPEN: - return errno ? strerror(errno) : "file open error"; - case MSPACK_ERR_READ: - return errno ? strerror(errno) : "file read error"; - case MSPACK_ERR_WRITE: - return errno ? strerror(errno) : "file write error"; - case MSPACK_ERR_SEEK: - return errno ? strerror(errno) : "file seek error"; - case MSPACK_ERR_NOMEMORY: - return "out of memory"; - case MSPACK_ERR_SIGNATURE: - return "bad CAB signature"; - case MSPACK_ERR_DATAFORMAT: - return "error in CAB data format"; - case MSPACK_ERR_CHECKSUM: - return "checksum error"; - case MSPACK_ERR_DECRUNCH: - return "decompression error"; - } - return "unknown error"; -} - struct mspack_file_p { FILE *fh; const char *name; diff --git a/cabextract/src/cabinfo.c b/cabextract/src/cabinfo.c index 7967f1b..3d01697 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 @@ -25,9 +25,9 @@ #include /* include from libmspack for LD and EndGetI?? macros */ -#include +#include "mspack/system.h" /* include from libmspack for cab structure offsets */ -#include +#include "mspack/cab.h" #if HAVE_FSEEKO # define FSEEK fseeko 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 ec88b28..e4acb8f 100644 --- a/libmspack/Makefile.am +++ b/libmspack/Makefile.am @@ -60,22 +60,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..1288f17 100755 --- a/libmspack/autogen.sh +++ b/libmspack/autogen.sh @@ -3,4 +3,8 @@ [ -d m4 ] || mkdir m4 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/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..bcfb102 --- /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 1aed04b..7612222 100644 --- a/libmspack/examples/cabrip.c +++ b/libmspack/examples/cabrip.c @@ -1,12 +1,12 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include -#include +#include "mspack.h" +#include "system.h" #if HAVE_FSEEKO # define fseek fseeko diff --git a/libmspack/examples/chmextract.c b/libmspack/examples/chmextract.c index a75b4fe..275a5aa 100644 --- a/libmspack/examples/chmextract.c +++ b/libmspack/examples/chmextract.c @@ -1,15 +1,13 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #include -#include - #if HAVE_MKDIR # if MKDIR_TAKES_ONE_ARG # define mkdir(a, b) mkdir(a) @@ -22,6 +20,10 @@ # endif #endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + mode_t user_umask; /** @@ -103,7 +105,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 +114,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/msexpand.c b/libmspack/examples/msexpand.c index b9fc704..e50d890 100644 --- a/libmspack/examples/msexpand.c +++ b/libmspack/examples/msexpand.c @@ -1,14 +1,13 @@ /* acts like Microsoft's EXPAND.EXE */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include -#include +#include "mspack.h" int main(int argc, char *argv[]) { struct msszdd_decompressor *szddd; @@ -34,7 +33,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..4b68ee4 100644 --- a/libmspack/examples/oabextract.c +++ b/libmspack/examples/oabextract.c @@ -1,13 +1,11 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include - -#include +#include "mspack.h" int main(int argc, char *argv[]) { struct msoab_decompressor *oabd; @@ -22,11 +20,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..7055e26 --- /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}) +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}) +endif() + +# Create ALIAS targets. +add_library( libmspack::mspack ALIAS mspack ) +add_library( libmspack::mspack_static ALIAS mspack_static ) 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 32dfb8c..56549b8 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 bc537b0..1895dcc 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 5f1a980..f29b393 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..be6d9be 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 @@ -506,6 +509,61 @@ struct mspack_file { /** Error code: error during decompression */ #define MSPACK_ERR_DECRUNCH (11) +/** + * 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; +} + /* --- functions available in library -------------------------------------- */ /** Creates a new CAB compressor. 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 47f5272..1471363 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" #if !LARGEFILE_SUPPORT const char *largefile_msg = "library not compiled to support large files."; diff --git a/libmspack/mspack/system.h b/libmspack/mspack/system.h index 7e4be46..56fed6d 100644 --- a/libmspack/mspack/system.h +++ b/libmspack/mspack/system.h @@ -16,10 +16,10 @@ extern "C" { /* ensure config.h is read before mspack.h */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif -#include +#include "mspack.h" /* assume exists */ #include @@ -38,7 +38,11 @@ extern "C" { # if __GNUC__ >= 2 # define __func__ __FUNCTION__ # else -# define __func__ "" +# ifdef WIN32 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif # endif #endif 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..6a68615 --- /dev/null +++ b/libmspack/test/CMakeLists.txt @@ -0,0 +1,79 @@ +# +# 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 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) +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) +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) +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 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) + +add_executable(chmd_md5) +target_sources(chmd_md5 + PRIVATE chmd_md5.c) +target_link_libraries(chmd_md5 + PRIVATE mschmd_ObjLib md5_ObjLib) + +add_executable(chmd_order) +target_sources(chmd_order + PRIVATE chmd_order.c) +target_link_libraries(chmd_order + PRIVATE mschmd_ObjLib md5_ObjLib) + +add_executable(chminfo) +target_sources(chminfo + PRIVATE chminfo.c) +target_link_libraries(chminfo + PRIVATE mschmd_ObjLib) diff --git a/libmspack/test/cabd_md5.c b/libmspack/test/cabd_md5.c index c02d9c9..b14b77c 100644 --- a/libmspack/test/cabd_md5.c +++ b/libmspack/test/cabd_md5.c @@ -1,16 +1,19 @@ #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 /** * Matches a cabinet's filename case-insensitively in the filesystem and @@ -98,7 +101,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 +114,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 +133,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 +150,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 3ab362b..3c4e561 100644 --- a/libmspack/test/cabd_test.c +++ b/libmspack/test/cabd_test.c @@ -1,14 +1,14 @@ /* cabinet decompression regression test suite */ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include #include -#include -#include +#include "mspack.h" +#include "system.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 60ed938..b1e656f 100644 --- a/libmspack/test/chmd_find.c +++ b/libmspack/test/chmd_find.c @@ -1,16 +1,15 @@ /* chmd_find: tests fast-find functionality */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" -#include -#include +#include "system.h" void find(struct mschm_decompressor *chmd, struct mschmd_header *chm, char *archive, char *filename, struct mschmd_file *compare) @@ -18,7 +17,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) { @@ -81,13 +80,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..df23169 100644 --- a/libmspack/test/chmd_md5.c +++ b/libmspack/test/chmd_md5.c @@ -1,14 +1,13 @@ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include +#include "mspack.h" #include -#include static int sortfunc(const void *a, const void *b) { off_t diff = @@ -43,7 +42,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 +54,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..4d5c570 100644 --- a/libmspack/test/chmd_order.c +++ b/libmspack/test/chmd_order.c @@ -6,16 +6,15 @@ * - 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 struct my_file { struct mschmd_file *file; @@ -57,7 +56,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 +70,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 +85,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 +95,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 +107,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 +134,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 8391ba8..23d7368 100644 --- a/libmspack/test/chmd_test.c +++ b/libmspack/test/chmd_test.c @@ -1,16 +1,18 @@ /* CHM regression test suite */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include +#ifdef HAVE_UNISTD_H #include +#endif -#include -#include +#include "mspack.h" +#include "system.h" #define __tf3(x) #x #define __tf2(x) __tf3(x) diff --git a/libmspack/test/chminfo.c b/libmspack/test/chminfo.c index ac71b0d..ac0c18b 100644 --- a/libmspack/test/chminfo.c +++ b/libmspack/test/chminfo.c @@ -1,14 +1,16 @@ #ifdef HAVE_CONFIG_H -# include +# include "config.h" #endif #include #include +#ifdef HAVE_UNISTD_H #include +#endif #include -#include -#include +#include "mspack.h" +#include "system.h" #define FILENAME ".chminfo-temp" diff --git a/libmspack/test/error.h b/libmspack/test/error.h deleted file mode 100644 index b1d627b..0000000 --- a/libmspack/test/error.h +++ /dev/null @@ -1,22 +0,0 @@ -#define ERROR(base) error_msg(base->last_error(base)) - -const char *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/test/kwajd_test.c b/libmspack/test/kwajd_test.c index 4a8152c..81ced23 100644 --- a/libmspack/test/kwajd_test.c +++ b/libmspack/test/kwajd_test.c @@ -1,14 +1,14 @@ /* KWAJ regression test suite */ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #include #include #include -#include -#include +#include "mspack.h" +#include "system.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*/