diff --git a/.gitignore b/.gitignore index de19e81..8272f2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ *.o -/pigz -/unpigz -/pigzj -/pigzt -/pigzn +/build/ +/bin/ +/src/pigz.* +/src/unpigz.* +/src/pigzj.* +/src/pigzt.* +/src/pigzn.* +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..77b02e9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.1.0) + +if(COMMAND CMAKE_POLICY) + CMAKE_POLICY(SET CMP0003 NEW) +endif() + +project(pigz) + +include(${CMAKE_SOURCE_DIR}/SuperBuild/SuperBuild.cmake) diff --git a/README b/README deleted file mode 100644 index da89431..0000000 --- a/README +++ /dev/null @@ -1,47 +0,0 @@ -pigz 2.4 (26 Dec 2017) by Mark Adler - -pigz, which stands for Parallel Implementation of GZip, is a fully functional -replacement for gzip that exploits multiple processors and multiple cores to -the hilt when compressing data. - -pigz was written by Mark Adler and does not include third-party code. I am -making my contributions to and distributions of this project solely in my -personal capacity, and am not conveying any rights to any intellectual property -of any third parties. - -This version of pigz is written to be portable across Unix-style operating -systems that provide the zlib and pthread libraries. - -Type "make" in this directory to build the "pigz" executable. You can then -install the executable wherever you like in your path (e.g. /usr/local/bin/). -Type "pigz" to see the command help and all of the command options. - -The latest version of pigz can be found at http://zlib.net/pigz/ . You need -zlib version 1.2.3 or later to compile pigz. zlib version 1.2.6 or later is -recommended, which reduces the overhead between blocks. You can find the -latest version of zlib at http://zlib.net/ . You can look in pigz.c for the -change history. - -Questions, comments, bug reports, fixes, etc. can be emailed to Mark at his -address in the license below. - -The license from pigz.c is copied here: - - This software is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Mark Adler - madler@alumni.caltech.edu diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed31855 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +## About + + +[pigz](https://zlib.net/pigz/), which stands for parallel implementation of gzip, is a fully functional replacement for gzip that exploits multiple processors and multiple cores to the hilt when compressing data. pigz was written by [Mark Adler](https://en.wikipedia.org/wiki/Mark_Adler), and uses the [zlib](https://zlib.net) and [pthread](https://en.wikipedia.org/wiki/POSIX_Threads) libraries. To compile and use pigz, please read the README file in the source code distribution. You can read the pigz manual page [here](https://zlib.net/pigz/pigz.pdf). + +Most users can install pigz with a simple command (Debian: `sudo yum install pigz`, RedHat: `apt-get install pigz`, MacOS: `brew install pigz`). + +This repository provides the source code, allowing users to build their own executable. One reason to build your own copy is to improve performance (see CMake notes below). An additional reason is to ensure pigz is build with a current version of glibc. Specifically, pigz can generate an `internal threads error` on [Linux servers with old versions of glibc](https://github.com/madler/pigz/issues/68). If you see these errors you should upgrade your glibc library and recompile pigz. + +## Compiling with Make + +This is the simplest way to compile pigz. Type "make" in the source directory ("pigz/src") to build the "pigz" executable. You can then install the executable wherever you like in your path (e.g. /usr/local/bin/). Type "pigz" to see the command help and all of the command options. + +## Compiling with CMake + +Compiling with CMake is more complicated than using Make. However, it does allow you to use different variants of the zlib compression library that can [improve](https://github.com/neurolabusc/pigz-bench) performance. Compiling with CMake requires the computer to have CMake and git installed. By default, CMake will use the [CloudFlare zlib](https://github.com/cloudflare/zlib) which provides outstanding compression performance: + +``` +git clone https://github.com/madler/pigz +cd pigz +mkdir build && cd build +cmake .. +make +``` + +Alternatively, you can build for your system zlib, which will likely provide the poorest performance (but is the most popular so least likely to have any issues): + +``` +git clone https://github.com/madler/pigz +cd pigz +mkdir build && cd build +cmake -DZLIB_IMPLEMENTATION=System .. +make +``` + +Finally, you can build for [zlib-ng](https://github.com/zlib-ng/zlib-ng). At the moment this provides compression performance between CloudFlare and System, but provides exceptionally fast decompression: + +``` +git clone https://github.com/madler/pigz +cd pigz +mkdir build && cd build +cmake -DZLIB_IMPLEMENTATION=ng .. +make +``` + +Note that the process is a little different if you are using the Windows operating system. Windows users can compile using the [Microsoft C Compiler](https://visualstudio.microsoft.com/downloads/) or [MinGW]( http://mingw-w64.org/doku.php). Be aware there are several variations of the MinGW compiler, and the CMake script expects a version that supports the [-municode linker flag]( https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/). This flag is required to handle non-Latin letters in filenames. Here is an example of compiling on Windows targeting the Cloudflare zlib: + +``` +git clone https://github.com/madler/pigz +cd pigz +mkdir build +cd build +cmake -DZLIB_IMPLEMENTATION=Cloudflare .. +cmake --build . --config Release +``` + +## Details + +pigz 2.4 (26 Dec 2017) by Mark Adler + +pigz, which stands for Parallel Implementation of GZip, is a fully functional replacement for gzip that exploits multiple processors and multiple cores to the hilt when compressing data. + +pigz was written by Mark Adler and does not include third-party code. I am making my contributions to and distributions of this project solely in my personal capacity, and am not conveying any rights to any intellectual property of any third parties. + +This version of pigz is written to be portable across Unix-style operating systems that provide the zlib and pthread libraries. + +Type "make" in the source directory ("pigz/src") to build the "pigz" executable. You can then install the executable wherever you like in your path (e.g. /usr/local/bin/). Type "pigz" to see the command help and all of the command options. + +The latest version of pigz can be found at http://zlib.net/pigz/ . You need zlib version 1.2.3 or later to compile pigz. zlib version 1.2.6 or later is recommended, which reduces the overhead between blocks. You can find the latest version of zlib at http://zlib.net/. You can look in pigz.c for the change history. + +Questions, comments, bug reports, fixes, etc. can be emailed to Mark at his address in the license below. + +The license from pigz.c is copied here: + + This software is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Mark Adler + madler@alumni.caltech.edu diff --git a/SuperBuild/External-CLOUDFLARE-ZLIB.cmake b/SuperBuild/External-CLOUDFLARE-ZLIB.cmake new file mode 100644 index 0000000..4742228 --- /dev/null +++ b/SuperBuild/External-CLOUDFLARE-ZLIB.cmake @@ -0,0 +1,14 @@ +set(CLOUDFLARE_BRANCH gcc.amd64) # Cloudflare zlib branch + +ExternalProject_Add(zlib + GIT_REPOSITORY "${git_protocol}://github.com/cloudflare/zlib.git" + GIT_TAG "${CLOUDFLARE_BRANCH}" + SOURCE_DIR cloudflare-zlib + BINARY_DIR cloudflare-zlib-build + CMAKE_ARGS + -Wno-dev + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${DEP_INSTALL_DIR} +) + +set(ZLIB_ROOT ${DEP_INSTALL_DIR}) diff --git a/SuperBuild/External-PTHREADS4W.cmake b/SuperBuild/External-PTHREADS4W.cmake new file mode 100644 index 0000000..ec0f0f6 --- /dev/null +++ b/SuperBuild/External-PTHREADS4W.cmake @@ -0,0 +1,19 @@ +set(PTHREADS4W_TAG cmakebuild) + +set(CLEANUP_STYLE VC) +set(PTW32_VER 3) + +ExternalProject_Add(pthreads4w + GIT_REPOSITORY "${git_protocol}://github.com/ningfei/pthreads4w.git" + GIT_TAG "${PTHREADS4W_TAG}" + SOURCE_DIR pthreads4w + BINARY_DIR pthreads4w-build + CMAKE_ARGS + -Wno-dev + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${DEP_INSTALL_DIR} + -DCLEANUP_STYLE:STRING=${CLEANUP_STYLE} +) + +set(PTHREADS4W_INCLUDE_DIRS ${DEP_INSTALL_DIR}/include) +set(PTHREADS4W_LIBRARIES ${DEP_INSTALL_DIR}/lib/libpthread${CLEANUP_STYLE}${PTW32_VER}.lib) diff --git a/SuperBuild/External-ZLIBng.cmake b/SuperBuild/External-ZLIBng.cmake new file mode 100644 index 0000000..a6cef3e --- /dev/null +++ b/SuperBuild/External-ZLIBng.cmake @@ -0,0 +1,18 @@ +set(NG_BRANCH develop) # zlib-ng branch + +ExternalProject_Add(zlib + GIT_REPOSITORY "${git_protocol}://github.com/zlib-ng/zlib-ng.git" + GIT_TAG "${NG_BRANCH}" + SOURCE_DIR ng-zlib + BINARY_DIR ng-zlib-build + BUILD_BYPRODUCTS ${ZLIB_STATIC_LIBRARIES} + CMAKE_ARGS + -Wno-dev + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${DEP_INSTALL_DIR} + -DZLIB_COMPAT:STRING=ON + -DBUILD_SHARED_LIBS:STRING=OFF +) + +set(CMAKE_FIND_LIBRARY_SUFFIXES .a) +set(ZLIB_ROOT ${DEP_INSTALL_DIR}) diff --git a/SuperBuild/SuperBuild.cmake b/SuperBuild/SuperBuild.cmake new file mode 100644 index 0000000..8ed513a --- /dev/null +++ b/SuperBuild/SuperBuild.cmake @@ -0,0 +1,106 @@ +# Check if git exists +find_package(Git) +if(NOT GIT_FOUND) + message(FATAL_ERROR "Cannot find Git. Git is required for Superbuild") +endif() + +# Use git protocol or not +option(USE_GIT_PROTOCOL "If behind a firewall turn this off to use http instead." OFF) +if(USE_GIT_PROTOCOL) + set(git_protocol "git") +else() + set(git_protocol "https") +endif() + +# Basic CMake build settings +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") +endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +option(USE_STATIC_RUNTIME "Use static runtime" ON) + +if(USE_STATIC_RUNTIME) + if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + find_file(STATIC_LIBCXX "libstdc++.a" ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) + mark_as_advanced(STATIC_LIBCXX) + if(NOT STATIC_LIBCXX) + unset(STATIC_LIBCXX CACHE) + # Only on some Centos/Redhat systems + message(FATAL_ERROR + "\"USE_STATIC_RUNTIME\" set to ON but \"libstdcxx.a\" not found! \ + \"yum install libstdc++-static\" to resolve the error.") + endif() + endif() +endif() + +include(ExternalProject) + +set(DEPENDENCIES) + +option(INSTALL_DEPENDENCIES "Optionally install built dependent libraries for future use." OFF) + +if(INSTALL_DEPENDENCIES) + set(DEP_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}) +else() + set(DEP_INSTALL_DIR ${CMAKE_BINARY_DIR}) +endif() + +if(MSVC) + message("-- Use pthreads4w for MSVC") + message("-- Will build pthreads4w from github") + include(${CMAKE_SOURCE_DIR}/SuperBuild/External-PTHREADS4W.cmake) + list(APPEND DEPENDENCIES pthreads4w) +endif() + +set(ZLIB_IMPLEMENTATION "Cloudflare" CACHE STRING "Choose zlib implementation.") +set_property(CACHE ZLIB_IMPLEMENTATION PROPERTY STRINGS "Cloudflare;System;ng;Custom") +if(${ZLIB_IMPLEMENTATION} STREQUAL "Cloudflare") + message("-- Build with Cloudflare zlib: ON") + include(${CMAKE_SOURCE_DIR}/SuperBuild/External-CLOUDFLARE-ZLIB.cmake) + list(APPEND DEPENDENCIES zlib) + set(BUILD_CLOUDFLARE-ZLIB TRUE) + message("-- Will build Cloudflare zlib from github") +elseif(${ZLIB_IMPLEMENTATION} STREQUAL "ng") + message("-- Build with zlib-ng: ON") + include(${CMAKE_SOURCE_DIR}/SuperBuild/External-ZLIBng.cmake) + list(APPEND DEPENDENCIES zlib) + set(BUILD_NG-ZLIB TRUE) + message("-- Will build zlib-ng from github") +elseif(${ZLIB_IMPLEMENTATION} STREQUAL "Custom") + set(ZLIB_ROOT ${ZLIB_ROOT} CACHE PATH "Specify custom zlib root directory.") + if(NOT ZLIB_ROOT) + message(FATAL_ERROR "ZLIB_ROOT needs to be set to locate custom zlib!!!") + endif() +endif() + +set(CMAKE_ARGS + -Wno-dev + --no-warn-unused-cli + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR} + -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} + -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} + -DCMAKE_VERBOSE_MAKEFILE:BOOL=${CMAKE_VERBOSE_MAKEFILE} + -DUSE_STATIC_RUNTIME:BOOL=${USE_STATIC_RUNTIME} + -DZLIB_IMPLEMENTATION:STRING=${ZLIB_IMPLEMENTATION} + -DZLIB_ROOT:PATH=${ZLIB_ROOT}) + +if(MSVC) + list(APPEND CMAKE_ARGS + -DPTHREADS4W_INCLUDE_DIRS:PATH=${PTHREADS4W_INCLUDE_DIRS} + -DPTHREADS4W_LIBRARIES:STRING=${PTHREADS4W_LIBRARIES}) +endif() + +ExternalProject_Add(pigz + DEPENDS ${DEPENDENCIES} + DOWNLOAD_COMMAND "" + SOURCE_DIR ${CMAKE_SOURCE_DIR}/src + BINARY_DIR pigz-build + CMAKE_ARGS ${CMAKE_ARGS} +) + +install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION bin + USE_SOURCE_PERMISSIONS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..bc99848 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required(VERSION 3.1.0) + +project(pigz) + +# Option Choose whether to use static runtime +include(ucm.cmake) +option(USE_STATIC_RUNTIME "Use static runtime" ON) +if(USE_STATIC_RUNTIME) + ucm_set_runtime(STATIC) +else() + ucm_set_runtime(DYNAMIC) +endif() + +# Basic CMake build settings +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel") +endif() + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + # using Clang + add_definitions(-fno-caret-diagnostics) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip") +elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + # using GCC + if(NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.5.0)) + add_definitions(-Wno-unused-result) # available since GCC 4.5.0 + endif() + if(NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.8.0)) + add_definitions(-fno-diagnostics-show-caret) # available since GCC 4.8.0 + endif() +elseif(MSVC) + # using Visual Studio C++ + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4018") # '<': signed/unsigned mismatch + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068") # unknown pragma + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4101") # unreferenced local variable + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") # 'initializing': conversion from 'double' to 'int', possible loss of data + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267") # 'initializing': conversion from 'size_t' to 'int', possible loss of data + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4305") # 'argument': truncation from 'double' to 'float' + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4308") # negative integral constant converted to unsigned type + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4334") # '<<': result of 32-bit shift implicitly converted to 64 bits + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 'uint32_t' : forcing value to bool 'true' or 'false' + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819") # The file contains a character that cannot be represented in the current code page + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") # 'access': The POSIX name for this item is deprecated + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") # set "Stack Reserve Size" to 8MB (default value is 1MB) + include_directories(${CMAKE_SOURCE_DIR}/win/dirent) +endif() + +# Compiler dependent flags +include (CheckCXXCompilerFlag) +if(UNIX) + check_cxx_compiler_flag(-msse2 HAS_SSE2) + if(HAS_SSE2) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2 -mfpmath=sse") + endif() +endif() + +set(PROGRAMS ${PROJECT_NAME}) + +set(PIGZ_SRCS + pigz.c + yarn.c + try.c + zopfli/src/zopfli/deflate.c + zopfli/src/zopfli/blocksplitter.c + zopfli/src/zopfli/tree.c + zopfli/src/zopfli/lz77.c + zopfli/src/zopfli/cache.c + zopfli/src/zopfli/hash.c + zopfli/src/zopfli/util.c + zopfli/src/zopfli/squeeze.c + zopfli/src/zopfli/katajainen.c) + +add_executable(${PROJECT_NAME} ${PIGZ_SRCS}) + +if(MSVC) + target_include_directories(${PROJECT_NAME} PRIVATE ${PTHREADS4W_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} ${PTHREADS4W_LIBRARIES}) +else() + target_link_libraries(${PROJECT_NAME} m) + if(MINGW) + target_link_libraries(${PROJECT_NAME} "-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive") + else() + find_package(Threads REQUIRED) + target_link_libraries(${PROJECT_NAME} Threads::Threads) + endif() +endif() +set(THREADS_PREFER_PTHREAD_FLAG ON) + +if(MINGW) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS -municode) +endif() + +set(ZLIB_IMPLEMENTATION "System" CACHE STRING "Choose zlib implementation.") +set_property(CACHE ZLIB_IMPLEMENTATION PROPERTY STRINGS "System;Custom") +if(NOT ${ZLIB_IMPLEMENTATION} STREQUAL "System") + set(ZLIB_ROOT ${ZLIB_ROOT} CACHE PATH "Specify custom zlib root directory.") + if(NOT ZLIB_ROOT) + message(FATAL_ERROR "ZLIB_ROOT needs to be set to locate custom zlib!") + endif() +endif() +find_package(ZLIB REQUIRED) +target_include_directories(${PROJECT_NAME} PRIVATE ${ZLIB_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${ZLIB_LIBRARIES}) + +install(TARGETS ${PROGRAMS} DESTINATION bin) diff --git a/Makefile b/src/Makefile old mode 100644 new mode 100755 similarity index 100% rename from Makefile rename to src/Makefile diff --git a/pigz.1 b/src/pigz.1 similarity index 100% rename from pigz.1 rename to src/pigz.1 diff --git a/pigz.c b/src/pigz.c similarity index 96% rename from pigz.c rename to src/pigz.c index bbbfd2c..71d34ea 100644 --- a/pigz.c +++ b/src/pigz.c @@ -321,6 +321,17 @@ buffers to about the same number. */ +#if defined(_MSC_VER) +//#include +//#include +#pragma execution_character_set("utf-8") +//#include +#include +//#include +typedef unsigned short uint_least16_t; +typedef SSIZE_T ssize_t; +#endif + // Use large file functions if available. #define _FILE_OFFSET_BITS 64 @@ -341,13 +352,32 @@ #include // ssize_t #include // chmod(), stat(), fstat(), lstat(), struct stat, // S_IFDIR, S_IFLNK, S_IFMT, S_IFREG -#include // utimes(), gettimeofday(), struct timeval -#include // unlink(), _exit(), read(), write(), close(), - // lseek(), isatty(), chown(), fsync() -#include // open(), O_CREAT, O_EXCL, O_RDONLY, O_TRUNC, - // O_WRONLY, fcntl(), F_FULLFSYNC +#ifdef __MINGW32__ + #include + #include + #include + #ifndef _UNICODE + #define _UNICODE + #endif + #ifndef UNICODE + #define UNICODE + #endif + #include +#endif + +#ifndef _MSC_VER + #include // utimes(), gettimeofday(), struct timeval + #include // unlink(), _exit(), read(), write(), close(), + // lseek(), isatty(), chown(), fsync() + +#else + #include + #include +#endif #include // opendir(), readdir(), closedir(), DIR, // struct dirent +#include // open(), O_CREAT, O_EXCL, O_RDONLY, O_TRUNC, + // O_WRONLY, fcntl(), F_FULLFSYNC #include // UINT_MAX, INT_MAX #if __STDC_VERSION__-0 >= 199901L || __GNUC__-0 >= 3 # include // intmax_t, uintmax_t @@ -380,13 +410,98 @@ # define S_IFLNK 0 #endif +#if defined(_MSC_VER) || defined(__MINGW64__) + +//https://www.nu42.com/2017/02/unicode-windows-command-line.html +//https://raw.githubusercontent.com/gypified/libmp3lame/master/frontend/main.c +static wchar_t *mbsToUnicode(const char *mbstr, int code_page) { + int n = MultiByteToWideChar(code_page, 0, mbstr, -1, NULL, 0); + wchar_t* wstr = malloc( n*sizeof(wstr[0]) ); + if ( wstr !=0 ) { + n = MultiByteToWideChar(code_page, 0, mbstr, -1, wstr, n); + if ( n==0 ) { + free( wstr ); + wstr = 0; + } + } + return wstr; +} + +static char *unicodeToMbs(const wchar_t *wstr, int code_page) { + int n = 1+WideCharToMultiByte(code_page, 0, wstr, -1, 0, 0, 0, 0); + char* mbstr = malloc( n*sizeof(mbstr[0]) ); + if ( mbstr !=0 ) { + n = WideCharToMultiByte(code_page, 0, wstr, -1, mbstr, n, 0, 0); + if( n == 0 ){ + free( mbstr ); + mbstr = 0; + } + } + return mbstr; +} + +wchar_t *utf8ToUnicode(const char *mbstr) { + return mbsToUnicode(mbstr, CP_UTF8); +} + +char *unicodeToUtf8(const wchar_t *wstr) { + return unicodeToMbs(wstr, CP_UTF8); +} + +# define chown(p,o,g) 0 +# define utimes(p,t) 0 +# define _exit(s) exit(s) + +int lstat(const char *path, struct stat *buf){ + //https://mail.gnome.org/archives/gtk-devel-list/2011-September/msg00177.html + wchar_t *wstr = utf8ToUnicode(path); + int rc = _wstat(wstr, buf); + #ifdef PIGZ_DEBUG + printf("lstat(%s)=%d\n", path, rc ); + #endif + free(wstr); + return rc; +} + +int openUTF8(const char *path, int flags, int mode) { + wchar_t *wstr = utf8ToUnicode(path); + int rc = _wopen(wstr, flags, mode); + #ifdef PIGZ_DEBUG + printf("openUTF8(%s, %d, %d)=%d\n", path, flags, mode, rc ); + #endif + free(wstr); + return rc; +} + +int unlinkUTF8(const char *path) { + wchar_t *wstr = utf8ToUnicode(path); + int rc = _wunlink(wstr); + #ifdef PIGZ_DEBUG + printf("unlinkUTF8(%s)=%d\n", path, rc ); + #endif + free(wstr); + return rc; +} +#else +# define openUTF8(p,f,m) open(p,f,m) +# define unlinkUTF8(p) unlink(p) +#endif + #ifdef __MINGW32__ # define chown(p,o,g) 0 # define utimes(p,t) 0 -# define lstat(p,s) stat(p,s) +#if defined(__MINGW64__) + //lstat defined for unicode +#else + # define lstat(p,s) stat(p,s) +#endif # define _exit(s) exit(s) #endif + + + + #include "zlib.h" // deflateInit2(), deflateReset(), deflate(), // deflateEnd(), deflateSetDictionary(), crc32(), // adler32(), inflateBackInit(), inflateBack(), @@ -404,6 +519,8 @@ #endif #ifndef NOZOPFLI + + # include "zopfli/src/zopfli/deflate.h" // ZopfliDeflatePart(), // ZopfliInitOptions(), // ZopfliOptions @@ -661,7 +778,9 @@ local void zlib_free(voidpf opaque, voidpf address) { #define REALLOC(p, s) realloc_track(&mem_track, p, s) #define FREE(p) free_track(&mem_track, p) -#define OPAQUE (&mem_track) +#define OPAQUEz (&mem_track) + + #define ZALLOC zlib_alloc #define ZFREE zlib_free @@ -669,7 +788,7 @@ local void zlib_free(voidpf opaque, voidpf address) { #define REALLOC realloc #define FREE free -#define OPAQUE Z_NULL +#define OPAQUEz Z_NULL #define ZALLOC Z_NULL #define ZFREE Z_NULL @@ -843,7 +962,7 @@ local void cut_short(int sig) { Trace(("termination by user")); } if (g.outd != -1 && g.outd != 1) { - unlink(g.outf); + unlinkUTF8(g.outf); RELEASE(g.outf); g.outd = -1; } @@ -1633,7 +1752,7 @@ local void compress_thread(void *dummy) { // initialize the deflate stream for this thread strm.zfree = ZFREE; strm.zalloc = ZALLOC; - strm.opaque = OPAQUE; + strm.opaque = OPAQUEz; ret = deflateInit2(&strm, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); if (ret == Z_MEM_ERROR) throw(ENOMEM, "not enough memory"); @@ -2221,7 +2340,7 @@ local void single_compress(int reset) { strm = alloc(NULL, sizeof(z_stream)); strm->zfree = ZFREE; strm->zalloc = ZALLOC; - strm->opaque = OPAQUE; + strm->opaque = OPAQUEz; ret = deflateInit2(strm, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); if (ret == Z_MEM_ERROR) throw(ENOMEM, "not enough memory"); @@ -3257,7 +3376,7 @@ local void infchk(void) { g.out_check = CHECK(0L, Z_NULL, 0); strm.zalloc = ZALLOC; strm.zfree = ZFREE; - strm.opaque = OPAQUE; + strm.opaque = OPAQUEz; ret = inflateBackInit(&strm, 15, out_buf); if (ret == Z_MEM_ERROR) throw(ENOMEM, "not enough memory"); @@ -3626,6 +3745,73 @@ local void touch(char *path, time_t t) { (void)utimes(path, times); } + + +//#ifdef +#if defined(_MSC_VER) || defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +/* http://ab-initio.mit.edu/octave-Faddeeva/gnulib/lib/fsync.c + Emulate fsync on platforms that lack it, primarily Windows and + cross-compilers like MinGW. + + This is derived from sqlite3 sources. + http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/os_win.c + http://www.sqlite.org/copyright.html + + Written by Richard W.M. Jones + + Copyright (C) 2008-2012 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ +int fsync (int fd) { + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD err; + + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + if (!FlushFileBuffers (h)) + { + /* Translate some Windows errors into rough approximations of Unix + * errors. MSDN is useless as usual - in this case it doesn't + * document the full range of errors. + */ + err = GetLastError (); + switch (err) + { + case ERROR_ACCESS_DENIED: + /* For a read-only handle, fsync should succeed, even though we have + no way to sync the access-time changes. */ + return 0; + + /* eg. Trying to fsync a tty. */ + case ERROR_INVALID_HANDLE: + errno = EINVAL; + break; + + default: + errno = EIO; + } + return -1; + } + + return 0; +} +#endif /* !Windows */ + // Request that all data buffered by the operating system for g.outd be written // to the permanent storage device. If fsync(fd) is used (POSIX), then all of // the data is sent to the device, but will likely be buffered in volatile @@ -3640,10 +3826,16 @@ local void touch(char *path, time_t t) { local void out_push(void) { if (g.outd == -1) return; -#ifdef F_FULLSYNC - int ret = fcntl(g.outd, F_FULLSYNC); +#ifdef _MSC_VER + //int ret = fcntl(g.outd, F_FULLFSYNC); + int ret = fsync(g.outd); #else + + #ifdef F_FULLSYNC + int ret = fcntl(g.outd, F_FULLSYNC); + #else int ret = fsync(g.outd); + #endif #endif if (ret == -1) throw(errno, "sync error on %s (%s)", g.outf, strerror(errno)); @@ -3773,7 +3965,7 @@ local void process(char *path) { } // open input file - g.ind = open(g.inf, O_RDONLY, 0); + g.ind = openUTF8(g.inf, O_RDONLY, 0); if (g.ind < 0) throw(errno, "read error on %s (%s)", g.inf, strerror(errno)); @@ -3867,7 +4059,7 @@ local void process(char *path) { memcpy(g.outf, g.inf, pre); memcpy(g.outf + pre, to, len); strcpy(g.outf + pre + len, sufx); - g.outd = open(g.outf, O_CREAT | O_TRUNC | O_WRONLY | + g.outd = openUTF8(g.outf, O_CREAT | O_TRUNC | O_WRONLY | (g.force ? 0 : O_EXCL), 0600); // if exists and not -f, give user a chance to overwrite @@ -3883,7 +4075,7 @@ local void process(char *path) { reply = ch == 'y' || ch == 'Y' ? 1 : 0; } while (ch != EOF && ch != '\n' && ch != '\r'); if (reply == 1) - g.outd = open(g.outf, O_CREAT | O_TRUNC | O_WRONLY, + g.outd = openUTF8(g.outf, O_CREAT | O_TRUNC | O_WRONLY, 0600); } @@ -3922,7 +4114,7 @@ local void process(char *path) { if (g.outd != -1 && g.outd != 1) { close(g.outd); g.outd = -1; - unlink(g.outf); + unlinkUTF8(g.outf); RELEASE(g.outf); } } @@ -3949,7 +4141,7 @@ local void process(char *path) { if (g.ind != 0) { copymeta(g.inf, g.outf); if (!g.keep) - unlink(g.inf); + unlinkUTF8(g.inf); } if (g.decode && (g.headis & 2) != 0 && g.stamp) touch(g.outf, g.stamp); @@ -4299,7 +4491,11 @@ local void cut_yarn(int err) { #endif // Process command line arguments. +#if defined(_MSC_VER) || defined(__MINGW64__) +int c_main(int argc, char *argv[]) { +#else int main(int argc, char **argv) { +#endif int n; // general index int nop; // index before which "-" means stdin int done; // number of named files processed @@ -4439,3 +4635,22 @@ int main(int argc, char **argv) { log_dump(); return g.ret; } + +#if defined(_MSC_VER) || defined(__MINGW64__) +// beware, only some versions of MinGW support wmain and -municode linker flag +// use this version: http://mingw-w64.org/doku.php +// for details: https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/ +int wmain(int argc, wchar_t* argv[]) { //convert each argument to UTF8 + char **utf8_argv; + int ret; + utf8_argv = calloc(argc, sizeof(char*)); + for (int i = 0; i < argc; ++i) + utf8_argv[i] = unicodeToUtf8(argv[i]); + SetConsoleOutputCP(CP_UTF8); + ret = c_main(argc, utf8_argv); + for (int i = 0; i < argc; ++i) + free( utf8_argv[i] ); + free( utf8_argv ); + return ret; +} +#endif diff --git a/pigz.pdf b/src/pigz.pdf similarity index 100% rename from pigz.pdf rename to src/pigz.pdf diff --git a/pigz.spec b/src/pigz.spec similarity index 100% rename from pigz.spec rename to src/pigz.spec diff --git a/try.c b/src/try.c similarity index 100% rename from try.c rename to src/try.c diff --git a/try.h b/src/try.h similarity index 100% rename from try.h rename to src/try.h diff --git a/src/ucm.cmake b/src/ucm.cmake new file mode 100644 index 0000000..e6e7fc8 --- /dev/null +++ b/src/ucm.cmake @@ -0,0 +1,636 @@ +# +# ucm.cmake - useful cmake macros +# +# Copyright (c) 2016 Viktor Kirilov +# +# Distributed under the MIT Software License +# See accompanying file LICENSE.txt or copy at +# https://opensource.org/licenses/MIT +# +# The documentation can be found at the library's page: +# https://github.com/onqtam/ucm + +cmake_minimum_required(VERSION 2.8.12) + +include(CMakeParseArguments) + +# optionally include cotire - the git submodule might not be inited (or the user might have already included it) +if(NOT COMMAND cotire) + include(${CMAKE_CURRENT_LIST_DIR}/../cotire/CMake/cotire.cmake OPTIONAL) +endif() + +if(COMMAND cotire AND "1.7.9" VERSION_LESS "${COTIRE_CMAKE_MODULE_VERSION}") + set(ucm_with_cotire 1) +else() + set(ucm_with_cotire 0) +endif() + +# option(UCM_UNITY_BUILD "Enable unity build for targets registered with the ucm_add_target() macro" OFF) +# option(UCM_NO_COTIRE_FOLDER "Do not use a cotire folder in the solution explorer for all unity and cotire related targets" ON) + +# ucm_add_flags +# Adds compiler flags to CMAKE__FLAGS or to a specific config +macro(ucm_add_flags) + cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "" "CONFIG" ${ARGN}) + + if(NOT ARG_CONFIG) + set(ARG_CONFIG " ") + endif() + + foreach(CONFIG ${ARG_CONFIG}) + # determine to which flags to add + if(NOT ${CONFIG} STREQUAL " ") + string(TOUPPER ${CONFIG} CONFIG) + set(CXX_FLAGS CMAKE_CXX_FLAGS_${CONFIG}) + set(C_FLAGS CMAKE_C_FLAGS_${CONFIG}) + else() + set(CXX_FLAGS CMAKE_CXX_FLAGS) + set(C_FLAGS CMAKE_C_FLAGS) + endif() + + # clear the old flags + if(${ARG_CLEAR_OLD}) + if("${ARG_CXX}" OR NOT "${ARG_C}") + set(${CXX_FLAGS} "") + endif() + if("${ARG_C}" OR NOT "${ARG_CXX}") + set(${C_FLAGS} "") + endif() + endif() + + # add all the passed flags + foreach(flag ${ARG_UNPARSED_ARGUMENTS}) + if("${ARG_CXX}" OR NOT "${ARG_C}") + set(${CXX_FLAGS} "${${CXX_FLAGS}} ${flag}") + endif() + if("${ARG_C}" OR NOT "${ARG_CXX}") + set(${C_FLAGS} "${${C_FLAGS}} ${flag}") + endif() + endforeach() + endforeach() + +endmacro() + +# ucm_set_flags +# Sets the CMAKE__FLAGS compiler flags or for a specific config +macro(ucm_set_flags) + ucm_add_flags(CLEAR_OLD ${ARGN}) +endmacro() + +# ucm_add_linker_flags +# Adds linker flags to CMAKE__LINKER_FLAGS or to a specific config +macro(ucm_add_linker_flags) + cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "" "CONFIG" ${ARGN}) + + if(NOT ARG_CONFIG) + set(ARG_CONFIG " ") + endif() + + foreach(CONFIG ${ARG_CONFIG}) + string(TOUPPER "${CONFIG}" CONFIG) + + if(NOT ${ARG_EXE} AND NOT ${ARG_MODULE} AND NOT ${ARG_SHARED} AND NOT ${ARG_STATIC}) + set(ARG_EXE 1) + set(ARG_MODULE 1) + set(ARG_SHARED 1) + set(ARG_STATIC 1) + endif() + + set(flags_configs "") + if(${ARG_EXE}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS) + endif() + endif() + if(${ARG_MODULE}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS) + endif() + endif() + if(${ARG_SHARED}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(${ARG_STATIC}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS) + endif() + endif() + + # clear the old flags + if(${ARG_CLEAR_OLD}) + foreach(flags ${flags_configs}) + set(${flags} "") + endforeach() + endif() + + # add all the passed flags + foreach(flag ${ARG_UNPARSED_ARGUMENTS}) + foreach(flags ${flags_configs}) + set(${flags} "${${flags}} ${flag}") + endforeach() + endforeach() + endforeach() +endmacro() + +# ucm_set_linker_flags +# Sets the CMAKE__LINKER_FLAGS linker flags or for a specific config +macro(ucm_set_linker_flags) + ucm_add_linker_flags(CLEAR_OLD ${ARGN}) +endmacro() + +# ucm_gather_flags +# Gathers all lists of flags for printing or manipulation +macro(ucm_gather_flags with_linker result) + set(${result} "") + # add the main flags without a config + list(APPEND ${result} CMAKE_C_FLAGS) + list(APPEND ${result} CMAKE_CXX_FLAGS) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS) + endif() + + if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "") + # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set + string(TOUPPER ${CMAKE_BUILD_TYPE} config) + list(APPEND ${result} CMAKE_C_FLAGS_${config}) + list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) + endif() + else() + # handle multi config generators (like msvc, xcode) + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + list(APPEND ${result} CMAKE_C_FLAGS_${config}) + list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) + endif() + endforeach() + endif() +endmacro() + +# ucm_set_runtime +# Sets the runtime (static/dynamic) for msvc/gcc +macro(ucm_set_runtime) + cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN}) + + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "") + message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!") + endif() + + ucm_gather_flags(0 flags_configs) + + # add/replace the flags + # note that if the user has messed with the flags directly this function might fail + # - for example if with MSVC and the user has removed the flags - here we just switch/replace them + if("${ARG_STATIC}") + foreach(flags ${flags_configs}) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.4.7) # option "-static-libstdc++" available since GCC 4.5 + if(NOT ${flags} MATCHES "-static-libstdc\\+\\+") + set(${flags} "${${flags}} -static-libstdc++") + endif() + endif() + if(NOT ${flags} MATCHES "-static-libgcc") + set(${flags} "${${flags}} -static-libgcc") + endif() + elseif(MSVC) + if(${flags} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}") + endif() + endif() + endforeach() + elseif("${ARG_DYNAMIC}") + foreach(flags ${flags_configs}) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(${flags} MATCHES "-static-libstdc\\+\\+") + string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}") + endif() + if(${flags} MATCHES "-static-libgcc") + string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}") + endif() + elseif(MSVC) + if(${flags} MATCHES "/MT") + string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}") + endif() + endif() + endforeach() + endif() +endmacro() + +# ucm_print_flags +# Prints all compiler flags for all configurations +macro(ucm_print_flags) + ucm_gather_flags(1 flags_configs) + message("") + foreach(flags ${flags_configs}) + message("${flags}: ${${flags}}") + endforeach() + message("") +endmacro() + +# ucm_count_sources +# Counts the number of source files +macro(ucm_count_sources) + cmake_parse_arguments(ARG "" "RESULT" "" ${ARGN}) + if(${ARG_RESULT} STREQUAL "") + message(FATAL_ERROR "Need to pass RESULT and a variable name to ucm_count_sources()") + endif() + + set(result 0) + foreach(SOURCE_FILE ${ARG_UNPARSED_ARGUMENTS}) + if("${SOURCE_FILE}" MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx|i|ii\)$) + math(EXPR result "${result} + 1") + endif() + endforeach() + set(${ARG_RESULT} ${result}) +endmacro() + +# ucm_include_file_in_sources +# Includes the file to the source with compiler flags +macro(ucm_include_file_in_sources) + cmake_parse_arguments(ARG "" "HEADER" "" ${ARGN}) + if(${ARG_HEADER} STREQUAL "") + message(FATAL_ERROR "Need to pass HEADER and a header file to ucm_include_file_in_sources()") + endif() + + foreach(src ${ARG_UNPARSED_ARGUMENTS}) + if(${src} MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx\)$) + # get old flags + get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS) + if(old_compile_flags STREQUAL "NOTFOUND") + set(old_compile_flags "") + endif() + + # update flags + if(MSVC) + set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS + "${old_compile_flags} /FI\"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") + else() + set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS + "${old_compile_flags} -include \"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") + endif() + endif() + endforeach() +endmacro() + +# ucm_dir_list +# Returns a list of subdirectories for a given directory +macro(ucm_dir_list thedir result) + file(GLOB sub-dir "${thedir}/*") + set(list_of_dirs "") + foreach(dir ${sub-dir}) + if(IS_DIRECTORY ${dir}) + get_filename_component(DIRNAME ${dir} NAME) + LIST(APPEND list_of_dirs ${DIRNAME}) + endif() + endforeach() + set(${result} ${list_of_dirs}) +endmacro() + +# ucm_trim_front_words +# Trims X times the front word from a string separated with "/" and removes +# the front "/" characters after that (used for filters for visual studio) +macro(ucm_trim_front_words source out num_filter_trims) + set(result "${source}") + set(counter 0) + while(${counter} LESS ${num_filter_trims}) + MATH(EXPR counter "${counter} + 1") + # removes everything at the front up to a "/" character + string(REGEX REPLACE "^([^/]+)" "" result "${result}") + # removes all consecutive "/" characters from the front + string(REGEX REPLACE "^(/+)" "" result "${result}") + endwhile() + set(${out} ${result}) +endmacro() + +# ucm_remove_files +# Removes source files from a list of sources (path is the relative path for it to be found) +macro(ucm_remove_files) + cmake_parse_arguments(ARG "" "FROM" "" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative files to ucm_remove_files()") + endif() + if(${ARG_FROM} STREQUAL "") + message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_files()") + endif() + + foreach(cur_file ${ARG_UNPARSED_ARGUMENTS}) + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + endforeach() +endmacro() + +# ucm_remove_directories +# Removes all source files from the given directories from the sources list +macro(ucm_remove_directories) + cmake_parse_arguments(ARG "" "FROM" "MATCHES" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative directories to ucm_remove_directories()") + endif() + if(${ARG_FROM} STREQUAL "") + message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_directories()") + endif() + + foreach(cur_dir ${ARG_UNPARSED_ARGUMENTS}) + foreach(cur_file ${${ARG_FROM}}) + string(REGEX MATCH ${cur_dir} res ${cur_file}) + if(NOT "${res}" STREQUAL "") + if("${ARG_MATCHES}" STREQUAL "") + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + else() + foreach(curr_ptrn ${ARG_MATCHES}) + string(REGEX MATCH ${curr_ptrn} res ${cur_file}) + if(NOT "${res}" STREQUAL "") + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + break() + endif() + endforeach() + endif() + endif() + endforeach() + endforeach() +endmacro() + +# ucm_add_files_impl +macro(ucm_add_files_impl result trim files) + foreach(cur_file ${files}) + SET(${result} ${${result}} ${cur_file}) + get_filename_component(FILEPATH ${cur_file} PATH) + ucm_trim_front_words("${FILEPATH}" FILEPATH "${trim}") + # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) + STRING(REPLACE "/" "\\" FILTERS "${FILEPATH}") + SOURCE_GROUP("${FILTERS}" FILES ${cur_file}) + endforeach() +endmacro() + +# ucm_add_files +# Adds files to a list of sources +macro(ucm_add_files) + cmake_parse_arguments(ARG "" "TO;FILTER_POP" "" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative files to ucm_add_files()") + endif() + if(${ARG_TO} STREQUAL "") + message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_files()") + endif() + + if("${ARG_FILTER_POP}" STREQUAL "") + set(ARG_FILTER_POP 0) + endif() + + ucm_add_files_impl(${ARG_TO} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}") +endmacro() + +# ucm_add_dir_impl +macro(ucm_add_dir_impl result rec trim dirs_in additional_ext) + set(dirs "${dirs_in}") + + # handle the "" and "." cases + if("${dirs}" STREQUAL "" OR "${dirs}" STREQUAL ".") + set(dirs "./") + endif() + + foreach(cur_dir ${dirs}) + # to circumvent some linux/cmake/path issues - barely made it work... + if(cur_dir STREQUAL "./") + set(cur_dir "") + else() + set(cur_dir "${cur_dir}/") + endif() + + # since unix is case sensitive - add these valid extensions too + # we don't use "UNIX" but instead "CMAKE_HOST_UNIX" because we might be cross + # compiling (for example emscripten) under windows and UNIX may be set to 1 + # Also OSX is case insensitive like windows... + set(additional_file_extensions "") + if(CMAKE_HOST_UNIX AND NOT APPLE) + set(additional_file_extensions + "${cur_dir}*.CPP" + "${cur_dir}*.C" + "${cur_dir}*.H" + "${cur_dir}*.HPP" + ) + endif() + + foreach(ext ${additional_ext}) + list(APPEND additional_file_extensions "${cur_dir}*.${ext}") + endforeach() + + # find all sources and set them as result + FILE(GLOB found_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + # https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Overall-Options.html#index-file-name-suffix-71 + # sources + "${cur_dir}*.cpp" + "${cur_dir}*.cxx" + "${cur_dir}*.c++" + "${cur_dir}*.cc" + "${cur_dir}*.cp" + "${cur_dir}*.c" + "${cur_dir}*.i" + "${cur_dir}*.ii" + # headers + "${cur_dir}*.h" + "${cur_dir}*.h++" + "${cur_dir}*.hpp" + "${cur_dir}*.hxx" + "${cur_dir}*.hh" + "${cur_dir}*.inl" + "${cur_dir}*.inc" + "${cur_dir}*.ipp" + "${cur_dir}*.ixx" + "${cur_dir}*.txx" + "${cur_dir}*.tpp" + "${cur_dir}*.tcc" + "${cur_dir}*.tpl" + ${additional_file_extensions}) + SET(${result} ${${result}} ${found_sources}) + + # set the proper filters + ucm_trim_front_words("${cur_dir}" cur_dir "${trim}") + # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) + STRING(REPLACE "/" "\\" FILTERS "${cur_dir}") + SOURCE_GROUP("${FILTERS}" FILES ${found_sources}) + endforeach() + + if(${rec}) + foreach(cur_dir ${dirs}) + ucm_dir_list("${cur_dir}" subdirs) + foreach(subdir ${subdirs}) + ucm_add_dir_impl(${result} ${rec} ${trim} "${cur_dir}/${subdir}" "${additional_ext}") + endforeach() + endforeach() + endif() +endmacro() + +# ucm_add_dirs +# Adds all files from directories traversing them recursively to a list of sources +# and generates filters according to their location (accepts relative paths only). +# Also this macro trims X times the front word from the filter string for visual studio filters. +macro(ucm_add_dirs) + cmake_parse_arguments(ARG "RECURSIVE" "TO;FILTER_POP" "ADDITIONAL_EXT" ${ARGN}) + + if(${ARG_TO} STREQUAL "") + message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_dirs()") + endif() + + if("${ARG_FILTER_POP}" STREQUAL "") + set(ARG_FILTER_POP 0) + endif() + + ucm_add_dir_impl(${ARG_TO} ${ARG_RECURSIVE} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}" "${ARG_ADDITIONAL_EXT}") +endmacro() + +# ucm_add_target +# Adds a target eligible for cotiring - unity build and/or precompiled header +macro(ucm_add_target) + cmake_parse_arguments(ARG "UNITY" "NAME;TYPE;PCH_FILE;CPP_PER_UNITY" "UNITY_EXCLUDED;SOURCES" ${ARGN}) + + if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Unrecognized options passed to ucm_add_target()") + endif() + if("${ARG_NAME}" STREQUAL "") + message(FATAL_ERROR "Need to pass NAME and a name for the target to ucm_add_target()") + endif() + set(valid_types EXECUTABLE STATIC SHARED MODULE) + list(FIND valid_types "${ARG_TYPE}" is_type_valid) + if(${is_type_valid} STREQUAL "-1") + message(FATAL_ERROR "Need to pass TYPE and the type for the target [EXECUTABLE/STATIC/SHARED/MODULE] to ucm_add_target()") + endif() + if("${ARG_SOURCES}" STREQUAL "") + message(FATAL_ERROR "Need to pass SOURCES and a list of source files to ucm_add_target()") + endif() + + # init with the global unity flag + set(do_unity ${UCM_UNITY_BUILD}) + + # check the UNITY argument + if(NOT ARG_UNITY) + set(do_unity FALSE) + endif() + + # if target is excluded through the exclusion list + list(FIND UCM_UNITY_BUILD_EXCLUDE_TARGETS ${ARG_NAME} is_target_excluded) + if(NOT ${is_target_excluded} STREQUAL "-1") + set(do_unity FALSE) + endif() + + # unity build only for targets with > 1 source file (otherwise there will be an additional unnecessary target) + if(do_unity) # optimization + ucm_count_sources(${ARG_SOURCES} RESULT num_sources) + if(${num_sources} LESS 2) + set(do_unity FALSE) + endif() + endif() + + set(wanted_cotire ${do_unity}) + + # if cotire cannot be used + if(do_unity AND NOT ucm_with_cotire) + set(do_unity FALSE) + endif() + + # inform the developer that the current target might benefit from a unity build + if(NOT ARG_UNITY AND ${UCM_UNITY_BUILD}) + ucm_count_sources(${ARG_SOURCES} RESULT num_sources) + if(${num_sources} GREATER 1) + message(AUTHOR_WARNING "Target '${ARG_NAME}' may benefit from a unity build.\nIt has ${num_sources} sources - enable with UNITY flag") + endif() + endif() + + # prepare for the unity build + set(orig_target ${ARG_NAME}) + if(do_unity) + # the original target will be added with a different name than the requested + set(orig_target ${ARG_NAME}_ORIGINAL) + + # exclude requested files from unity build of the current target + foreach(excluded_file "${ARG_UNITY_EXCLUDED}") + set_source_files_properties(${excluded_file} PROPERTIES COTIRE_EXCLUDED TRUE) + endforeach() + endif() + + # add the original target + if(${ARG_TYPE} STREQUAL "EXECUTABLE") + add_executable(${orig_target} ${ARG_SOURCES}) + else() + add_library(${orig_target} ${ARG_TYPE} ${ARG_SOURCES}) + endif() + + if(do_unity) + # set the number of unity cpp files to be used for the unity target + if(NOT "${ARG_CPP_PER_UNITY}" STREQUAL "") + set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${ARG_CPP_PER_UNITY}") + else() + set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "100") + endif() + + if(NOT "${ARG_PCH_FILE}" STREQUAL "") + set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") + else() + set_target_properties(${orig_target} PROPERTIES COTIRE_ENABLE_PRECOMPILED_HEADER FALSE) + endif() + # add a unity target for the original one with the name intended for the original + set_target_properties(${orig_target} PROPERTIES COTIRE_UNITY_TARGET_NAME ${ARG_NAME}) + + # this is the library call that does the magic + cotire(${orig_target}) + set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") + + # disable the original target and enable the unity one + get_target_property(unity_target_name ${orig_target} COTIRE_UNITY_TARGET_NAME) + set_target_properties(${orig_target} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) + set_target_properties(${unity_target_name} PROPERTIES EXCLUDE_FROM_ALL 0 EXCLUDE_FROM_DEFAULT_BUILD 0) + + # also set the name of the target output as the original one + set_target_properties(${unity_target_name} PROPERTIES OUTPUT_NAME ${ARG_NAME}) + if(UCM_NO_COTIRE_FOLDER) + # reset the folder property so all unity targets dont end up in a single folder in the solution explorer of VS + set_target_properties(${unity_target_name} PROPERTIES FOLDER "") + endif() + set_target_properties(all_unity PROPERTIES FOLDER "CMakePredefinedTargets") + elseif(NOT "${ARG_PCH_FILE}" STREQUAL "") + set(wanted_cotire TRUE) + if(ucm_with_cotire) + set_target_properties(${orig_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) + set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") + cotire(${orig_target}) + set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") + endif() + endif() + + # print a message if the target was requested to be cotired but it couldn't + if(wanted_cotire AND NOT ucm_with_cotire) + if(NOT COMMAND cotire) + message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire isn't loaded") + else() + message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire is older than the required version") + endif() + endif() +endmacro() diff --git a/src/win/README.txt b/src/win/README.txt new file mode 100644 index 0000000..9c5a94d --- /dev/null +++ b/src/win/README.txt @@ -0,0 +1,4 @@ +To compile for Windows using the Microsoft C++ (MSVC) compiler + 1. Launch the "x64 Native Tools Command Prompt" + 2. Use 'cd' to change directory to ..\pigz\src\win + 3. Run the command 'msvc.bat' \ No newline at end of file diff --git a/src/win/dirent/dirent.h b/src/win/dirent/dirent.h new file mode 100644 index 0000000..c885b39 --- /dev/null +++ b/src/win/dirent/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/src/win/dirent/readme.txt b/src/win/dirent/readme.txt new file mode 100644 index 0000000..6cdf995 --- /dev/null +++ b/src/win/dirent/readme.txt @@ -0,0 +1,3 @@ + +dirent +https://github.com/tronkko/dirent \ No newline at end of file diff --git a/src/win/msvc.bat b/src/win/msvc.bat new file mode 100755 index 0000000..ae5b48d --- /dev/null +++ b/src/win/msvc.bat @@ -0,0 +1,19 @@ +REM zlib +git clone https://github.com/rordenlab/zlib.git +cd zlib +nmake -f win32\Makefile.msc +cd .. +REM pthreads +git clone https://github.com/indigo-astronomy/pthreads4w +cd pthreads4w +nmake clean VC-static +cd .. +rm *.obj + + +cl -c ../pigz.c /utf-8 /Idirent /Ipthreads4w /Izlib -DNOZOPFLI +cl -c ../yarn.c /utf-8 /Idirent /Ipthreads4w /Izlib +cl -c ../try.c /utf-8 /Idirent /Ipthreads4w /Izlib +cl /Fepigz pigz.obj yarn.obj try.obj /utf-8 /Idirent /Ipthreads4w /Izlib /link shell32.lib zlib/zlib.lib pthreads4w/libpthreadVC3.lib /NODEFAULTLIB:MSVCRT +rm *.obj +REM pigz -f -k Описание.pdf 测试.obj fx.pdf diff --git a/yarn.c b/src/yarn.c similarity index 100% rename from yarn.c rename to src/yarn.c diff --git a/yarn.h b/src/yarn.h similarity index 100% rename from yarn.h rename to src/yarn.h diff --git a/zopfli/CONTRIBUTORS b/src/zopfli/CONTRIBUTORS similarity index 100% rename from zopfli/CONTRIBUTORS rename to src/zopfli/CONTRIBUTORS diff --git a/zopfli/COPYING b/src/zopfli/COPYING similarity index 100% rename from zopfli/COPYING rename to src/zopfli/COPYING diff --git a/zopfli/README b/src/zopfli/README similarity index 100% rename from zopfli/README rename to src/zopfli/README diff --git a/zopfli/src/zopfli/blocksplitter.c b/src/zopfli/src/zopfli/blocksplitter.c similarity index 100% rename from zopfli/src/zopfli/blocksplitter.c rename to src/zopfli/src/zopfli/blocksplitter.c diff --git a/zopfli/src/zopfli/blocksplitter.h b/src/zopfli/src/zopfli/blocksplitter.h similarity index 100% rename from zopfli/src/zopfli/blocksplitter.h rename to src/zopfli/src/zopfli/blocksplitter.h diff --git a/zopfli/src/zopfli/cache.c b/src/zopfli/src/zopfli/cache.c similarity index 100% rename from zopfli/src/zopfli/cache.c rename to src/zopfli/src/zopfli/cache.c diff --git a/zopfli/src/zopfli/cache.h b/src/zopfli/src/zopfli/cache.h similarity index 100% rename from zopfli/src/zopfli/cache.h rename to src/zopfli/src/zopfli/cache.h diff --git a/zopfli/src/zopfli/deflate.c b/src/zopfli/src/zopfli/deflate.c similarity index 99% rename from zopfli/src/zopfli/deflate.c rename to src/zopfli/src/zopfli/deflate.c index 4b0724b..5f809f2 100644 --- a/zopfli/src/zopfli/deflate.c +++ b/src/zopfli/src/zopfli/deflate.c @@ -396,7 +396,7 @@ void OptimizeHuffmanForRle(int length, size_t* counts) { } /* 2) Let's mark all population counts that already can be encoded with an rle code.*/ - good_for_rle = (int*)malloc(length * sizeof(int)); + good_for_rle = (int*)malloc((unsigned)length * sizeof(int)); for (i = 0; i < length; ++i) good_for_rle[i] = 0; /* Let's not spoil any of the existing good rle codes. diff --git a/zopfli/src/zopfli/deflate.h b/src/zopfli/src/zopfli/deflate.h similarity index 100% rename from zopfli/src/zopfli/deflate.h rename to src/zopfli/src/zopfli/deflate.h diff --git a/zopfli/src/zopfli/hash.c b/src/zopfli/src/zopfli/hash.c similarity index 100% rename from zopfli/src/zopfli/hash.c rename to src/zopfli/src/zopfli/hash.c diff --git a/zopfli/src/zopfli/hash.h b/src/zopfli/src/zopfli/hash.h similarity index 100% rename from zopfli/src/zopfli/hash.h rename to src/zopfli/src/zopfli/hash.h diff --git a/zopfli/src/zopfli/katajainen.c b/src/zopfli/src/zopfli/katajainen.c similarity index 100% rename from zopfli/src/zopfli/katajainen.c rename to src/zopfli/src/zopfli/katajainen.c diff --git a/zopfli/src/zopfli/katajainen.h b/src/zopfli/src/zopfli/katajainen.h similarity index 100% rename from zopfli/src/zopfli/katajainen.h rename to src/zopfli/src/zopfli/katajainen.h diff --git a/zopfli/src/zopfli/lz77.c b/src/zopfli/src/zopfli/lz77.c similarity index 100% rename from zopfli/src/zopfli/lz77.c rename to src/zopfli/src/zopfli/lz77.c diff --git a/zopfli/src/zopfli/lz77.h b/src/zopfli/src/zopfli/lz77.h similarity index 100% rename from zopfli/src/zopfli/lz77.h rename to src/zopfli/src/zopfli/lz77.h diff --git a/zopfli/src/zopfli/squeeze.c b/src/zopfli/src/zopfli/squeeze.c similarity index 100% rename from zopfli/src/zopfli/squeeze.c rename to src/zopfli/src/zopfli/squeeze.c diff --git a/zopfli/src/zopfli/squeeze.h b/src/zopfli/src/zopfli/squeeze.h similarity index 100% rename from zopfli/src/zopfli/squeeze.h rename to src/zopfli/src/zopfli/squeeze.h diff --git a/zopfli/src/zopfli/tree.c b/src/zopfli/src/zopfli/tree.c similarity index 100% rename from zopfli/src/zopfli/tree.c rename to src/zopfli/src/zopfli/tree.c diff --git a/zopfli/src/zopfli/tree.h b/src/zopfli/src/zopfli/tree.h similarity index 100% rename from zopfli/src/zopfli/tree.h rename to src/zopfli/src/zopfli/tree.h diff --git a/zopfli/src/zopfli/util.c b/src/zopfli/src/zopfli/util.c similarity index 100% rename from zopfli/src/zopfli/util.c rename to src/zopfli/src/zopfli/util.c diff --git a/zopfli/src/zopfli/util.h b/src/zopfli/src/zopfli/util.h similarity index 100% rename from zopfli/src/zopfli/util.h rename to src/zopfli/src/zopfli/util.h diff --git a/zopfli/src/zopfli/zopfli.h b/src/zopfli/src/zopfli/zopfli.h similarity index 100% rename from zopfli/src/zopfli/zopfli.h rename to src/zopfli/src/zopfli/zopfli.h