diff options
Diffstat (limited to 'Source/kwsys')
81 files changed, 34883 insertions, 0 deletions
diff --git a/Source/kwsys/.gitattributes b/Source/kwsys/.gitattributes new file mode 100644 index 0000000..7065eb5 --- /dev/null +++ b/Source/kwsys/.gitattributes @@ -0,0 +1,13 @@ +.git* export-ignore + +*.c kwsys-c-style +*.c.in kwsys-c-style +*.cxx kwsys-c-style +*.h kwsys-c-style +*.h.in kwsys-c-style +*.hxx kwsys-c-style +*.hxx.in kwsys-c-style + +*.cmake whitespace=tab-in-indent +*.rst whitespace=tab-in-indent conflict-marker-size=79 +*.txt whitespace=tab-in-indent diff --git a/Source/kwsys/Base64.c b/Source/kwsys/Base64.c new file mode 100644 index 0000000..bf876f2 --- /dev/null +++ b/Source/kwsys/Base64.c @@ -0,0 +1,225 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Base64.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Base64.h.in" +#endif + +static const unsigned char kwsysBase64EncodeTable[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static const unsigned char kwsysBase64DecodeTable[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /*------------------------------------*/ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static unsigned char kwsysBase64EncodeChar(int c) +{ + return kwsysBase64EncodeTable[(unsigned char)c]; +} + +static unsigned char kwsysBase64DecodeChar(unsigned char c) +{ + return kwsysBase64DecodeTable[c]; +} + +/* Encode 3 bytes into a 4 byte string. */ +void kwsysBase64_Encode3(const unsigned char* src, unsigned char* dest) +{ + dest[0] = kwsysBase64EncodeChar((src[0] >> 2) & 0x3F); + dest[1] = + kwsysBase64EncodeChar(((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0F)); + dest[2] = + kwsysBase64EncodeChar(((src[1] << 2) & 0x3C) | ((src[2] >> 6) & 0x03)); + dest[3] = kwsysBase64EncodeChar(src[2] & 0x3F); +} + +/* Encode 2 bytes into a 4 byte string. */ +void kwsysBase64_Encode2(const unsigned char* src, unsigned char* dest) +{ + dest[0] = kwsysBase64EncodeChar((src[0] >> 2) & 0x3F); + dest[1] = + kwsysBase64EncodeChar(((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0F)); + dest[2] = kwsysBase64EncodeChar(((src[1] << 2) & 0x3C)); + dest[3] = '='; +} + +/* Encode 1 bytes into a 4 byte string. */ +void kwsysBase64_Encode1(const unsigned char* src, unsigned char* dest) +{ + dest[0] = kwsysBase64EncodeChar((src[0] >> 2) & 0x3F); + dest[1] = kwsysBase64EncodeChar(((src[0] << 4) & 0x30)); + dest[2] = '='; + dest[3] = '='; +} + +/* Encode 'length' bytes from the input buffer and store the + encoded stream into the output buffer. Return the length of the encoded + buffer (output). Note that the output buffer must be allocated by the caller + (length * 1.5 should be a safe estimate). If 'mark_end' is true than an + extra set of 4 bytes is added to the end of the stream if the input is a + multiple of 3 bytes. These bytes are invalid chars and therefore they will + stop the decoder thus enabling the caller to decode a stream without + actually knowing how much data to expect (if the input is not a multiple of + 3 bytes then the extra padding needed to complete the encode 4 bytes will + stop the decoding anyway). */ +size_t kwsysBase64_Encode(const unsigned char* input, size_t length, + unsigned char* output, int mark_end) +{ + const unsigned char* ptr = input; + const unsigned char* end = input + length; + unsigned char* optr = output; + + /* Encode complete triplet */ + + while ((end - ptr) >= 3) { + kwsysBase64_Encode3(ptr, optr); + ptr += 3; + optr += 4; + } + + /* Encodes a 2-byte ending into 3 bytes and 1 pad byte and writes. */ + + if (end - ptr == 2) { + kwsysBase64_Encode2(ptr, optr); + optr += 4; + } + + /* Encodes a 1-byte ending into 2 bytes and 2 pad bytes */ + + else if (end - ptr == 1) { + kwsysBase64_Encode1(ptr, optr); + optr += 4; + } + + /* Do we need to mark the end */ + + else if (mark_end) { + optr[0] = optr[1] = optr[2] = optr[3] = '='; + optr += 4; + } + + return (size_t)(optr - output); +} + +/* Decode 4 bytes into a 3 byte string. */ +int kwsysBase64_Decode3(const unsigned char* src, unsigned char* dest) +{ + unsigned char d0, d1, d2, d3; + + d0 = kwsysBase64DecodeChar(src[0]); + d1 = kwsysBase64DecodeChar(src[1]); + d2 = kwsysBase64DecodeChar(src[2]); + d3 = kwsysBase64DecodeChar(src[3]); + + /* Make sure all characters were valid */ + + if (d0 == 0xFF || d1 == 0xFF || d2 == 0xFF || d3 == 0xFF) { + return 0; + } + + /* Decode the 3 bytes */ + + dest[0] = (unsigned char)(((d0 << 2) & 0xFC) | ((d1 >> 4) & 0x03)); + dest[1] = (unsigned char)(((d1 << 4) & 0xF0) | ((d2 >> 2) & 0x0F)); + dest[2] = (unsigned char)(((d2 << 6) & 0xC0) | ((d3 >> 0) & 0x3F)); + + /* Return the number of bytes actually decoded */ + + if (src[2] == '=') { + return 1; + } + if (src[3] == '=') { + return 2; + } + return 3; +} + +/* Decode bytes from the input buffer and store the decoded stream + into the output buffer until 'length' bytes have been decoded. Return the + real length of the decoded stream (which should be equal to 'length'). Note + that the output buffer must be allocated by the caller. If + 'max_input_length' is not null, then it specifies the number of encoded + bytes that should be at most read from the input buffer. In that case the + 'length' parameter is ignored. This enables the caller to decode a stream + without actually knowing how much decoded data to expect (of course, the + buffer must be large enough). */ +size_t kwsysBase64_Decode(const unsigned char* input, size_t length, + unsigned char* output, size_t max_input_length) +{ + const unsigned char* ptr = input; + unsigned char* optr = output; + + /* Decode complete triplet */ + + if (max_input_length) { + const unsigned char* end = input + max_input_length; + while (ptr < end) { + int len = kwsysBase64_Decode3(ptr, optr); + optr += len; + if (len < 3) { + return (size_t)(optr - output); + } + ptr += 4; + } + } else { + unsigned char* oend = output + length; + while ((oend - optr) >= 3) { + int len = kwsysBase64_Decode3(ptr, optr); + optr += len; + if (len < 3) { + return (size_t)(optr - output); + } + ptr += 4; + } + + /* Decode the last triplet */ + + if (oend - optr == 2) { + unsigned char temp[3]; + int len = kwsysBase64_Decode3(ptr, temp); + if (len >= 2) { + optr[0] = temp[0]; + optr[1] = temp[1]; + optr += 2; + } else if (len > 0) { + optr[0] = temp[0]; + optr += 1; + } + } else if (oend - optr == 1) { + unsigned char temp[3]; + int len = kwsysBase64_Decode3(ptr, temp); + if (len > 0) { + optr[0] = temp[0]; + optr += 1; + } + } + } + + return (size_t)(optr - output); +} diff --git a/Source/kwsys/Base64.h.in b/Source/kwsys/Base64.h.in new file mode 100644 index 0000000..729f972 --- /dev/null +++ b/Source/kwsys/Base64.h.in @@ -0,0 +1,110 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Base64_h +#define @KWSYS_NAMESPACE@_Base64_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +#include <stddef.h> /* size_t */ + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysBase64 kwsys_ns(Base64) +# define kwsysBase64_Decode kwsys_ns(Base64_Decode) +# define kwsysBase64_Decode3 kwsys_ns(Base64_Decode3) +# define kwsysBase64_Encode kwsys_ns(Base64_Encode) +# define kwsysBase64_Encode1 kwsys_ns(Base64_Encode1) +# define kwsysBase64_Encode2 kwsys_ns(Base64_Encode2) +# define kwsysBase64_Encode3 kwsys_ns(Base64_Encode3) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Encode 3 bytes into a 4 byte string. + */ +kwsysEXPORT void kwsysBase64_Encode3(const unsigned char* src, + unsigned char* dest); + +/** + * Encode 2 bytes into a 4 byte string. + */ +kwsysEXPORT void kwsysBase64_Encode2(const unsigned char* src, + unsigned char* dest); + +/** + * Encode 1 bytes into a 4 byte string. + */ +kwsysEXPORT void kwsysBase64_Encode1(const unsigned char* src, + unsigned char* dest); + +/** + * Encode 'length' bytes from the input buffer and store the encoded + * stream into the output buffer. Return the length of the encoded + * buffer (output). Note that the output buffer must be allocated by + * the caller (length * 1.5 should be a safe estimate). If 'mark_end' + * is true than an extra set of 4 bytes is added to the end of the + * stream if the input is a multiple of 3 bytes. These bytes are + * invalid chars and therefore they will stop the decoder thus + * enabling the caller to decode a stream without actually knowing how + * much data to expect (if the input is not a multiple of 3 bytes then + * the extra padding needed to complete the encode 4 bytes will stop + * the decoding anyway). + */ +kwsysEXPORT size_t kwsysBase64_Encode(const unsigned char* input, + size_t length, unsigned char* output, + int mark_end); + +/** + * Decode 4 bytes into a 3 byte string. Returns the number of bytes + * actually decoded. + */ +kwsysEXPORT int kwsysBase64_Decode3(const unsigned char* src, + unsigned char* dest); + +/** + * Decode bytes from the input buffer and store the decoded stream + * into the output buffer until 'length' bytes have been decoded. + * Return the real length of the decoded stream (which should be equal + * to 'length'). Note that the output buffer must be allocated by the + * caller. If 'max_input_length' is not null, then it specifies the + * number of encoded bytes that should be at most read from the input + * buffer. In that case the 'length' parameter is ignored. This + * enables the caller to decode a stream without actually knowing how + * much decoded data to expect (of course, the buffer must be large + * enough). + */ +kwsysEXPORT size_t kwsysBase64_Decode(const unsigned char* input, + size_t length, unsigned char* output, + size_t max_input_length); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysBase64 +# undef kwsysBase64_Decode +# undef kwsysBase64_Decode3 +# undef kwsysBase64_Encode +# undef kwsysBase64_Encode1 +# undef kwsysBase64_Encode2 +# undef kwsysBase64_Encode3 +# endif +#endif + +#endif diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt new file mode 100644 index 0000000..5de2776 --- /dev/null +++ b/Source/kwsys/CMakeLists.txt @@ -0,0 +1,1260 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing#kwsys for details. + +# The Kitware System Library is intended to be included in other +# projects. It is completely configurable in that the library's +# namespace can be configured and the components that are included can +# be selected invididually. + +# Typical usage is to import the kwsys directory tree into a +# subdirectory under a parent project and enable the classes that will +# be used. All classes are disabled by default. The CMake listfile +# above this one configures the library as follows: +# +# set(KWSYS_NAMESPACE foosys) +# set(KWSYS_USE_Directory 1) # Enable Directory class. +# add_subdirectory(kwsys) +# +# Optional settings are as follows: +# +# KWSYS_HEADER_ROOT = The directory into which to generate the kwsys headers. +# A directory called "${KWSYS_NAMESPACE}" will be +# created under this root directory to hold the files. +# KWSYS_SPLIT_OBJECTS_FROM_INTERFACE +# = Instead of creating a single ${KWSYS_NAMESPACE} library +# target, create three separate targets: +# ${KWSYS_NAMESPACE} +# - An INTERFACE library only containing usage +# requirements. +# ${KWSYS_NAMESPACE}_objects +# - An OBJECT library for the built kwsys objects. +# Note: This is omitted from the install rules +# ${KWSYS_NAMESPACE}_private +# - An INTERFACE library combining both that is +# appropriate for use with PRIVATE linking in +# target_link_libraries. Because of how interface +# properties propagate, this target is not suitable +# for use with PUBLIC or INTERFACE linking. +# KWSYS_ALIAS_TARGET = The name of an alias target to create to the actual target. +# +# Example: +# +# set(KWSYS_HEADER_ROOT ${PROJECT_BINARY_DIR}) +# include_directories(${PROJECT_BINARY_DIR}) +# +# KWSYS_CXX_STANDARD = A value for CMAKE_CXX_STANDARD within KWSys. +# Set to empty string to use no default value. +# KWSYS_CXX_COMPILE_FEATURES = target_compile_features arguments for KWSys. +# +# Optional settings to setup install rules are as follows: +# +# KWSYS_INSTALL_BIN_DIR = The installation target directories into +# KWSYS_INSTALL_LIB_DIR which the libraries and headers from +# KWSYS_INSTALL_INCLUDE_DIR kwsys should be installed by a "make install". +# The values should be specified relative to +# the installation prefix and NOT start with '/'. +# KWSYS_INSTALL_DOC_DIR = The installation target directory for documentation +# such as copyright information. +# +# KWSYS_INSTALL_COMPONENT_NAME_RUNTIME = Name of runtime and development +# KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT installation components. +# If not given the install rules +# will not be in any component. +# +# KWSYS_INSTALL_EXPORT_NAME = The EXPORT option value for install(TARGETS) calls. +# +# Example: +# +# set(KWSYS_INSTALL_BIN_DIR bin) +# set(KWSYS_INSTALL_LIB_DIR lib) +# set(KWSYS_INSTALL_INCLUDE_DIR include) +# set(KWSYS_INSTALL_COMPONENT_NAME_RUNTIME Runtime) +# set(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT Development) + +# Once configured, kwsys should be used as follows from C or C++ code: +# +# #include <foosys/Directory.hxx> +# ... +# foosys::Directory directory; +# + +# NOTE: This library is intended for internal use by Kitware-driven +# projects. In order to keep it simple no attempt will be made to +# maintain backward compatibility when changes are made to KWSys. +# When an incompatible change is made Kitware's projects that use +# KWSys will be fixed, but no notification will necessarily be sent to +# any outside mailing list and no documentation of the change will be +# written. + +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +foreach(p + CMP0056 # CMake 3.2, Honor link flags in try_compile() source-file signature. + CMP0063 # CMake 3.3, Honor visibility properties for all target types. + CMP0067 # CMake 3.8, Honor language standard in try_compile source-file signature. + CMP0069 # CMake 3.9, INTERPROCEDURAL_OPTIMIZATION is enforced when enabled. + ) + if(POLICY ${p}) + cmake_policy(SET ${p} NEW) + endif() +endforeach() + +#----------------------------------------------------------------------------- +# If a namespace is not specified, use "kwsys" and enable testing. +# This should be the case only when kwsys is not included inside +# another project and is being tested. +if(NOT KWSYS_NAMESPACE) + set(KWSYS_NAMESPACE "kwsys") + set(KWSYS_STANDALONE 1) +endif() + +#----------------------------------------------------------------------------- +# The project name is that of the specified namespace. +project(${KWSYS_NAMESPACE}) + +# Tell CMake how to follow dependencies of sources in this directory. +set_property(DIRECTORY + PROPERTY IMPLICIT_DEPENDS_INCLUDE_TRANSFORM + "KWSYS_HEADER(%)=<${KWSYS_NAMESPACE}/%>" + ) + +if(KWSYS_CXX_STANDARD) + set(CMAKE_CXX_STANDARD "${KWSYS_CXX_STANDARD}") +elseif(NOT DEFINED CMAKE_CXX_STANDARD AND NOT DEFINED KWSYS_CXX_STANDARD) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC" + AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" + ) + set(CMAKE_CXX_STANDARD 14) + else() + set(CMAKE_CXX_STANDARD 11) + endif() +endif() + +# Select library components. +if(KWSYS_STANDALONE OR CMake_SOURCE_DIR) + set(KWSYS_ENABLE_C 1) + # Enable all components. + set(KWSYS_USE_Base64 1) + set(KWSYS_USE_Directory 1) + set(KWSYS_USE_DynamicLoader 1) + set(KWSYS_USE_Encoding 1) + set(KWSYS_USE_Glob 1) + set(KWSYS_USE_MD5 1) + set(KWSYS_USE_Process 1) + set(KWSYS_USE_RegularExpression 1) + set(KWSYS_USE_System 1) + set(KWSYS_USE_SystemTools 1) + set(KWSYS_USE_CommandLineArguments 1) + set(KWSYS_USE_Terminal 1) + set(KWSYS_USE_IOStream 1) + set(KWSYS_USE_FStream 1) + set(KWSYS_USE_String 1) + set(KWSYS_USE_SystemInformation 1) + set(KWSYS_USE_ConsoleBuf 1) +endif() + +# Enforce component dependencies. +if(KWSYS_USE_SystemTools) + set(KWSYS_USE_Directory 1) + set(KWSYS_USE_FStream 1) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_Glob) + set(KWSYS_USE_Directory 1) + set(KWSYS_USE_SystemTools 1) + set(KWSYS_USE_RegularExpression 1) + set(KWSYS_USE_FStream 1) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_Process) + set(KWSYS_USE_System 1) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_SystemInformation) + set(KWSYS_USE_Process 1) +endif() +if(KWSYS_USE_System) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_Directory) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_DynamicLoader) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_FStream) + set(KWSYS_USE_Encoding 1) +endif() +if(KWSYS_USE_ConsoleBuf) + set(KWSYS_USE_Encoding 1) +endif() + +# Specify default 8 bit encoding for Windows +if(NOT KWSYS_ENCODING_DEFAULT_CODEPAGE) + set(KWSYS_ENCODING_DEFAULT_CODEPAGE CP_ACP) +endif() + +# Enable testing if building standalone. +if(KWSYS_STANDALONE) + include(Dart) + mark_as_advanced(BUILD_TESTING DART_ROOT TCL_TCLSH) + if(BUILD_TESTING) + enable_testing() + endif() +endif() + +# Choose default shared/static build if not specified. +if(NOT DEFINED KWSYS_BUILD_SHARED) + set(KWSYS_BUILD_SHARED ${BUILD_SHARED_LIBS}) +endif() + +# Include helper macros. +include(${CMAKE_CURRENT_SOURCE_DIR}/kwsysPlatformTests.cmake) +include(CheckTypeSize) + +# Do full dependency headers. +include_regular_expression("^.*$") + +# Use new KWSYS_INSTALL_*_DIR variable names to control installation. +# Take defaults from the old names. Note that there was no old name +# for the bin dir, so we take the old lib dir name so DLLs will be +# installed in a compatible way for old code. +if(NOT KWSYS_INSTALL_INCLUDE_DIR) + string(REGEX REPLACE "^/" "" KWSYS_INSTALL_INCLUDE_DIR + "${KWSYS_HEADER_INSTALL_DIR}") +endif() +if(NOT KWSYS_INSTALL_LIB_DIR) + string(REGEX REPLACE "^/" "" KWSYS_INSTALL_LIB_DIR + "${KWSYS_LIBRARY_INSTALL_DIR}") +endif() +if(NOT KWSYS_INSTALL_BIN_DIR) + string(REGEX REPLACE "^/" "" KWSYS_INSTALL_BIN_DIR + "${KWSYS_LIBRARY_INSTALL_DIR}") +endif() + +# Setup header install rules. +set(KWSYS_INSTALL_INCLUDE_OPTIONS) +if(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT) + set(KWSYS_INSTALL_INCLUDE_OPTIONS ${KWSYS_INSTALL_INCLUDE_OPTIONS} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT} + ) +endif() + +# Setup library install rules. +set(KWSYS_INSTALL_LIBRARY_RULE) +set(KWSYS_INSTALL_NAMELINK_RULE) +if(KWSYS_INSTALL_LIB_DIR) + if(KWSYS_INSTALL_EXPORT_NAME) + list(APPEND KWSYS_INSTALL_LIBRARY_RULE EXPORT ${KWSYS_INSTALL_EXPORT_NAME}) + endif() + # Install the shared library to the lib directory. + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + LIBRARY DESTINATION ${KWSYS_INSTALL_LIB_DIR} NAMELINK_SKIP + ) + # Assign the shared library to the runtime component. + if(KWSYS_INSTALL_COMPONENT_NAME_RUNTIME) + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_RUNTIME} + ) + endif() + if(KWSYS_BUILD_SHARED) + set(KWSYS_INSTALL_NAMELINK_RULE ${KWSYS_INSTALL_NAMELINK_RULE} + LIBRARY DESTINATION ${KWSYS_INSTALL_LIB_DIR} NAMELINK_ONLY + ) + # Assign the namelink to the development component. + if(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT) + set(KWSYS_INSTALL_NAMELINK_RULE ${KWSYS_INSTALL_NAMELINK_RULE} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT} + ) + endif() + endif() + + # Install the archive to the lib directory. + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + ARCHIVE DESTINATION ${KWSYS_INSTALL_LIB_DIR} + ) + # Assign the archive to the development component. + if(KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT) + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_DEVELOPMENT} + ) + endif() +endif() +if(KWSYS_INSTALL_BIN_DIR) + # Install the runtime library to the bin directory. + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + RUNTIME DESTINATION ${KWSYS_INSTALL_BIN_DIR} + ) + # Assign the runtime library to the runtime component. + if(KWSYS_INSTALL_COMPONENT_NAME_RUNTIME) + set(KWSYS_INSTALL_LIBRARY_RULE ${KWSYS_INSTALL_LIBRARY_RULE} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_RUNTIME} + ) + endif() +endif() + +# Do not support old KWSYS_*a_INSTALL_DIR variable names. +set(KWSYS_HEADER_INSTALL_DIR) +set(KWSYS_LIBRARY_INSTALL_DIR) + +# Generated source files will need this header. +string(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" + KWSYS_IN_SOURCE_BUILD) +if(NOT KWSYS_IN_SOURCE_BUILD) + configure_file(${PROJECT_SOURCE_DIR}/kwsysPrivate.h + ${PROJECT_BINARY_DIR}/kwsysPrivate.h COPYONLY IMMEDIATE) +endif() + +# Select plugin module file name convention. +if(NOT KWSYS_DynamicLoader_PREFIX) + set(KWSYS_DynamicLoader_PREFIX ${CMAKE_SHARED_MODULE_PREFIX}) +endif() +if(NOT KWSYS_DynamicLoader_SUFFIX) + set(KWSYS_DynamicLoader_SUFFIX ${CMAKE_SHARED_MODULE_SUFFIX}) +endif() + +#----------------------------------------------------------------------------- +# We require ANSI support from the C compiler. Add any needed flags. +if(CMAKE_ANSI_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_ANSI_CFLAGS}") +endif() + +#----------------------------------------------------------------------------- +# Adjust compiler flags for some platforms. +if(NOT CMAKE_COMPILER_IS_GNUCXX) + if(CMAKE_SYSTEM MATCHES "OSF1-V.*") + string(REGEX MATCH "-timplicit_local" + KWSYS_CXX_FLAGS_HAVE_IMPLICIT_LOCAL "${CMAKE_CXX_FLAGS}") + string(REGEX MATCH "-no_implicit_include" + KWSYS_CXX_FLAGS_HAVE_NO_IMPLICIT_INCLUDE "${CMAKE_CXX_FLAGS}") + if(NOT KWSYS_CXX_FLAGS_HAVE_IMPLICIT_LOCAL) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -timplicit_local") + endif() + if(NOT KWSYS_CXX_FLAGS_HAVE_NO_IMPLICIT_INCLUDE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -no_implicit_include") + endif() + endif() + if(CMAKE_SYSTEM MATCHES "HP-UX") + set(KWSYS_PLATFORM_CXX_TEST_EXTRA_FLAGS "+p") + if(CMAKE_CXX_COMPILER_ID MATCHES "HP") + # it is known that version 3.85 fails and 6.25 works without these flags + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4) + # use new C++ library and improved template support + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -AA +hpxstd98") + endif() + endif() + endif() +endif() +if(KWSYS_STANDALONE) + if(CMAKE_CXX_COMPILER_ID STREQUAL SunPro) + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.13) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++03") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -library=stlport4") + endif() + endif() +endif() + +#----------------------------------------------------------------------------- +# Configure the standard library header wrappers based on compiler's +# capabilities and parent project's request. Enforce 0/1 as only +# possible values for configuration into Configure.hxx. + +# Check existence and uniqueness of long long and __int64. +KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_LONG_LONG + "Checking whether C++ compiler has 'long long'" DIRECT) +KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS___INT64 + "Checking whether C++ compiler has '__int64'" DIRECT) +if(KWSYS_CXX_HAS___INT64) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_SAME_LONG_AND___INT64 + "Checking whether long and __int64 are the same type" DIRECT) + if(KWSYS_CXX_HAS_LONG_LONG) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_SAME_LONG_LONG_AND___INT64 + "Checking whether long long and __int64 are the same type" DIRECT) + endif() +endif() + +# Enable the "long long" type if it is available. It is standard in +# C99 and C++03 but not in earlier standards. +if(KWSYS_CXX_HAS_LONG_LONG) + set(KWSYS_USE_LONG_LONG 1) +else() + set(KWSYS_USE_LONG_LONG 0) +endif() + +# Enable the "__int64" type if it is available and unique. It is not +# standard. +set(KWSYS_USE___INT64 0) +if(KWSYS_CXX_HAS___INT64) + if(NOT KWSYS_CXX_SAME_LONG_AND___INT64) + if(NOT KWSYS_CXX_SAME_LONG_LONG_AND___INT64) + set(KWSYS_USE___INT64 1) + endif() + endif() +endif() + +if(KWSYS_USE_Encoding) + # Look for type size helper macros. + KWSYS_PLATFORM_CXX_TEST(KWSYS_STL_HAS_WSTRING + "Checking whether wstring is available" DIRECT) +endif() + +if(KWSYS_USE_IOStream) + # Determine whether iostreams support long long. + if(KWSYS_CXX_HAS_LONG_LONG) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_ISTREAM_LONG_LONG + "Checking if istream supports long long" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_OSTREAM_LONG_LONG + "Checking if ostream supports long long" DIRECT) + else() + set(KWSYS_IOS_HAS_ISTREAM_LONG_LONG 0) + set(KWSYS_IOS_HAS_OSTREAM_LONG_LONG 0) + endif() + if(KWSYS_CXX_HAS___INT64) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_ISTREAM___INT64 + "Checking if istream supports __int64" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_IOS_HAS_OSTREAM___INT64 + "Checking if ostream supports __int64" DIRECT) + else() + set(KWSYS_IOS_HAS_ISTREAM___INT64 0) + set(KWSYS_IOS_HAS_OSTREAM___INT64 0) + endif() +endif() + +if(KWSYS_NAMESPACE MATCHES "^kwsys$") + set(KWSYS_NAME_IS_KWSYS 1) +else() + set(KWSYS_NAME_IS_KWSYS 0) +endif() + +if(KWSYS_BUILD_SHARED) + set(KWSYS_BUILD_SHARED 1) + set(KWSYS_LIBRARY_TYPE SHARED) +else() + set(KWSYS_BUILD_SHARED 0) + set(KWSYS_LIBRARY_TYPE STATIC) +endif() + +if(NOT DEFINED KWSYS_BUILD_PIC) + set(KWSYS_BUILD_PIC 0) +endif() + +#----------------------------------------------------------------------------- +# Configure some implementation details. + +KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_PTRDIFF_T + "Checking whether C compiler has ptrdiff_t in stddef.h" DIRECT) +KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_SSIZE_T + "Checking whether C compiler has ssize_t in unistd.h" DIRECT) +if(KWSYS_USE_Process) + KWSYS_PLATFORM_C_TEST(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC + "Checking whether C compiler has clock_gettime" DIRECT) +endif() + +set_source_files_properties(ProcessUNIX.c System.c PROPERTIES + COMPILE_FLAGS "-DKWSYS_C_HAS_PTRDIFF_T=${KWSYS_C_HAS_PTRDIFF_T} -DKWSYS_C_HAS_SSIZE_T=${KWSYS_C_HAS_SSIZE_T} -DKWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC=${KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC}" + ) + +if(DEFINED KWSYS_PROCESS_USE_SELECT) + get_property(ProcessUNIX_FLAGS SOURCE ProcessUNIX.c PROPERTY COMPILE_FLAGS) + set_property(SOURCE ProcessUNIX.c PROPERTY COMPILE_FLAGS "${ProcessUNIX_FLAGS} -DKWSYSPE_USE_SELECT=${KWSYSPE_USE_SELECT}") +endif() + +if(KWSYS_USE_DynamicLoader) + get_property(KWSYS_SUPPORTS_SHARED_LIBS GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + if(KWSYS_SUPPORTS_SHARED_LIBS) + set(KWSYS_SUPPORTS_SHARED_LIBS 1) + else() + set(KWSYS_SUPPORTS_SHARED_LIBS 0) + endif() + set_property(SOURCE DynamicLoader.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_SUPPORTS_SHARED_LIBS=${KWSYS_SUPPORTS_SHARED_LIBS}) +endif() + +if(KWSYS_USE_SystemTools) + if (NOT DEFINED KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) + endif () + if (KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) + else () + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 0) + endif () + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV + "Checking whether CXX compiler has setenv" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UNSETENV + "Checking whether CXX compiler has unsetenv" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H + "Checking whether CXX compiler has environ in stdlib.h" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UTIMES + "Checking whether CXX compiler has utimes" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UTIMENSAT + "Checking whether CXX compiler has utimensat" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_STAT_HAS_ST_MTIM + "Checking whether CXX compiler struct stat has st_mtim member" DIRECT) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_STAT_HAS_ST_MTIMESPEC + "Checking whether CXX compiler struct stat has st_mtimespec member" DIRECT) + set_property(SOURCE SystemTools.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_CXX_HAS_SETENV=${KWSYS_CXX_HAS_SETENV} + KWSYS_CXX_HAS_UNSETENV=${KWSYS_CXX_HAS_UNSETENV} + KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H=${KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H} + KWSYS_CXX_HAS_UTIMES=${KWSYS_CXX_HAS_UTIMES} + KWSYS_CXX_HAS_UTIMENSAT=${KWSYS_CXX_HAS_UTIMENSAT} + KWSYS_CXX_STAT_HAS_ST_MTIM=${KWSYS_CXX_STAT_HAS_ST_MTIM} + KWSYS_CXX_STAT_HAS_ST_MTIMESPEC=${KWSYS_CXX_STAT_HAS_ST_MTIMESPEC} + ) + if(NOT WIN32) + if(KWSYS_STANDALONE) + option(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES "If true, Windows paths will be supported on Unix as well" ON) + endif() + if(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES) + set_property(SOURCE SystemTools.cxx testSystemTools.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES + ) + endif() + endif() + + # Disable getpwnam for static linux builds since it depends on shared glibc + get_property(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT SHARED_LIBS_SUPPORTED) + set_property(SOURCE SystemTools.cxx APPEND PROPERTY COMPILE_DEFINITIONS + HAVE_GETPWNAM=0 + ) + endif() +endif() + +if(KWSYS_USE_SystemInformation) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS SIZEOF_VOID_P=${CMAKE_SIZEOF_VOID_P}) + if(NOT CYGWIN) + include(CheckIncludeFiles) + CHECK_INCLUDE_FILES("sys/types.h;ifaddrs.h" KWSYS_SYS_HAS_IFADDRS_H) + if(KWSYS_SYS_HAS_IFADDRS_H) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYS_HAS_IFADDRS_H=1) + endif() + endif() + if(WIN32) + include(CheckSymbolExists) + set(CMAKE_REQUIRED_LIBRARIES psapi) + CHECK_SYMBOL_EXISTS(GetProcessMemoryInfo "windows.h;psapi.h" KWSYS_SYS_HAS_PSAPI) + unset(CMAKE_REQUIRED_LIBRARIES) + if(KWSYS_SYS_HAS_PSAPI) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYS_HAS_PSAPI=1) + if(MSVC70 OR MSVC71) + # Suppress LNK4089: all references to 'PSAPI.DLL' discarded by /OPT:REF + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /IGNORE:4089") + endif() + endif() + endif() + if(CMAKE_SYSTEM MATCHES "HP-UX") + CHECK_INCLUDE_FILES("sys/mpctl.h" KWSYS_SYS_HAS_MPCTL_H) + if(KWSYS_SYS_HAS_MPCTL_H) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYS_HAS_MPCTL_H=1) + endif() + endif() + if(CMAKE_SYSTEM MATCHES "BSD") + CHECK_INCLUDE_FILES("machine/cpu.h" KWSYS_SYS_HAS_MACHINE_CPU_H) + if(KWSYS_SYS_HAS_MACHINE_CPU_H) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYS_HAS_MACHINE_CPU_H=1) + endif() + endif() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_RLIMIT64 + "Checking whether CXX compiler has rlimit64" DIRECT) + set(KWSYS_PLATFORM_CXX_TEST_DEFINES) + if(KWSYS_CXX_HAS_RLIMIT64) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_RLIMIT64=1) + endif() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_ATOL + "Checking whether CXX compiler has atol" DIRECT) + if(KWSYS_CXX_HAS_ATOL) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_ATOL=1) + endif() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_ATOLL + "Checking whether CXX compiler has atoll" DIRECT) + if(KWSYS_CXX_HAS_ATOLL) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_ATOLL=1) + endif() + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS__ATOI64 + "Checking whether CXX compiler has _atoi64" DIRECT) + if(KWSYS_CXX_HAS__ATOI64) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS__ATOI64=1) + endif() + if(UNIX) + include(CheckIncludeFileCXX) + # check for simple stack trace + # usually it's in libc but on FreeBSD + # it's in libexecinfo + find_library(EXECINFO_LIB "execinfo") + mark_as_advanced(EXECINFO_LIB) + if (NOT EXECINFO_LIB) + set(EXECINFO_LIB "") + endif() + CHECK_INCLUDE_FILE_CXX("execinfo.h" KWSYS_CXX_HAS_EXECINFOH) + if (KWSYS_CXX_HAS_EXECINFOH) + # we have the backtrace header check if it + # can be used with this compiler + set(KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES ${EXECINFO_LIB}) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_BACKTRACE + "Checking whether backtrace works with this C++ compiler" DIRECT) + set(KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES) + if (KWSYS_CXX_HAS_BACKTRACE) + # backtrace is supported by this system and compiler. + # now check for the more advanced capabilities. + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE=1) + # check for symbol lookup using dladdr + CHECK_INCLUDE_FILE_CXX("dlfcn.h" KWSYS_CXX_HAS_DLFCNH) + if (KWSYS_CXX_HAS_DLFCNH) + # we have symbol lookup libraries and headers + # check if they can be used with this compiler + set(KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES ${CMAKE_DL_LIBS}) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_DLADDR + "Checking whether dladdr works with this C++ compiler" DIRECT) + set(KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES) + if (KWSYS_CXX_HAS_DLADDR) + # symbol lookup is supported by this system + # and compiler. + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP=1) + endif() + endif() + # c++ demangling support + # check for cxxabi headers + CHECK_INCLUDE_FILE_CXX("cxxabi.h" KWSYS_CXX_HAS_CXXABIH) + if (KWSYS_CXX_HAS_CXXABIH) + # check if cxxabi can be used with this + # system and compiler. + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_CXXABI + "Checking whether cxxabi works with this C++ compiler" DIRECT) + if (KWSYS_CXX_HAS_CXXABI) + # c++ demangle using cxxabi is supported with + # this system and compiler + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE=1) + endif() + endif() + # basic backtrace works better with release build + # don't bother with advanced features for release + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS_DEBUG KWSYS_SYSTEMINFORMATION_HAS_DEBUG_BUILD=1) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS_RELWITHDEBINFO KWSYS_SYSTEMINFORMATION_HAS_DEBUG_BUILD=1) + endif() + endif() + endif() + if(BORLAND) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_BORLAND_ASM + "Checking whether Borland CXX compiler supports assembler instructions" DIRECT) + if(KWSYS_CXX_HAS_BORLAND_ASM) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_BORLAND_ASM=1) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_BORLAND_ASM_CPUID + "Checking whether Borland CXX compiler supports CPUID assembler instruction" DIRECT) + if(KWSYS_CXX_HAS_BORLAND_ASM_CPUID) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_BORLAND_ASM_CPUID=1) + endif() + endif() + endif() + if(KWSYS_USE___INT64) + set_property(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_USE___INT64=1) + endif() + if(KWSYS_USE_LONG_LONG) + set_property(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_USE_LONG_LONG=1) + endif() + if(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) + set_property(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_IOS_HAS_OSTREAM_LONG_LONG=1) + endif() + if(KWSYS_IOS_HAS_OSTREAM___INT64) + set_property(SOURCE SystemInformation.cxx testSystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_IOS_HAS_OSTREAM___INT64=1) + endif() + if(KWSYS_BUILD_SHARED) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_BUILD_SHARED=1) + endif() + + if(UNIX AND NOT CYGWIN) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_GETLOADAVG + "Checking whether CXX compiler has getloadavg" DIRECT) + if(KWSYS_CXX_HAS_GETLOADAVG) + set_property(SOURCE SystemInformation.cxx APPEND PROPERTY + COMPILE_DEFINITIONS KWSYS_CXX_HAS_GETLOADAVG=1) + endif() + endif() +endif() + +if(KWSYS_USE_FStream) + KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H + "Checking whether <ext/stdio_filebuf.h> is available" DIRECT) +endif() + +#----------------------------------------------------------------------------- +# Choose a directory for the generated headers. +if(NOT KWSYS_HEADER_ROOT) + set(KWSYS_HEADER_ROOT "${PROJECT_BINARY_DIR}") +endif() +set(KWSYS_HEADER_DIR "${KWSYS_HEADER_ROOT}/${KWSYS_NAMESPACE}") +include_directories(${KWSYS_HEADER_ROOT}) + +#----------------------------------------------------------------------------- +if(KWSYS_INSTALL_DOC_DIR) + # Assign the license to the runtime component since it must be + # distributed with binary forms of this software. + if(KWSYS_INSTALL_COMPONENT_NAME_RUNTIME) + set(KWSYS_INSTALL_LICENSE_OPTIONS ${KWSYS_INSTALL_LICENSE_OPTIONS} + COMPONENT ${KWSYS_INSTALL_COMPONENT_NAME_RUNTIME} + ) + endif() + + # Install the license under the documentation directory. + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt + DESTINATION ${KWSYS_INSTALL_DOC_DIR}/${KWSYS_NAMESPACE} + ${KWSYS_INSTALL_LICENSE_OPTIONS}) +endif() + +#----------------------------------------------------------------------------- +# Build a list of classes and headers we need to implement the +# selected components. Initialize with required components. +set(KWSYS_CLASSES) +set(KWSYS_H_FILES Configure SharedForward) +set(KWSYS_HXX_FILES Configure String) + +if(NOT CMake_SOURCE_DIR) + set(KWSYS_HXX_FILES ${KWSYS_HXX_FILES} + hashtable hash_fun hash_map hash_set + ) +endif() + +# Add selected C++ classes. +set(cppclasses + Directory DynamicLoader Encoding Glob RegularExpression SystemTools + CommandLineArguments IOStream FStream SystemInformation ConsoleBuf + ) +foreach(cpp ${cppclasses}) + if(KWSYS_USE_${cpp}) + # Use the corresponding class. + set(KWSYS_CLASSES ${KWSYS_CLASSES} ${cpp}) + + # Load component-specific CMake code. + if(EXISTS ${PROJECT_SOURCE_DIR}/kwsys${cpp}.cmake) + include(${PROJECT_SOURCE_DIR}/kwsys${cpp}.cmake) + endif() + endif() +endforeach() + +# Add selected C components. +foreach(c + Process Base64 Encoding MD5 Terminal System String + ) + if(KWSYS_USE_${c}) + # Use the corresponding header file. + set(KWSYS_H_FILES ${KWSYS_H_FILES} ${c}) + + # Load component-specific CMake code. + if(EXISTS ${PROJECT_SOURCE_DIR}/kwsys${c}.cmake) + include(${PROJECT_SOURCE_DIR}/kwsys${c}.cmake) + endif() + endif() +endforeach() + +#----------------------------------------------------------------------------- +# Build a list of sources for the library based on components that are +# included. +set(KWSYS_C_SRCS) +set(KWSYS_CXX_SRCS) + +# Add the proper sources for this platform's Process implementation. +if(KWSYS_USE_Process) + if(NOT UNIX) + # Use the Windows implementation. + set(KWSYS_C_SRCS ${KWSYS_C_SRCS} ProcessWin32.c) + else() + # Use the UNIX implementation. + set(KWSYS_C_SRCS ${KWSYS_C_SRCS} ProcessUNIX.c) + endif() +endif() + +# Add selected C sources. +foreach(c Base64 Encoding MD5 Terminal System String) + if(KWSYS_USE_${c}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${c}C.c) + list(APPEND KWSYS_C_SRCS ${c}C.c) + else() + list(APPEND KWSYS_C_SRCS ${c}.c) + endif() + endif() +endforeach() + +# Configure headers of C++ classes and construct the list of sources. +foreach(c ${KWSYS_CLASSES}) + # Add this source to the list of source files for the library. + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${c}CXX.cxx) + list(APPEND KWSYS_CXX_SRCS ${c}CXX.cxx) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${c}.cxx) + list(APPEND KWSYS_CXX_SRCS ${c}.cxx) + endif() + + # Configure the header for this class. + configure_file(${PROJECT_SOURCE_DIR}/${c}.hxx.in ${KWSYS_HEADER_DIR}/${c}.hxx + @ONLY IMMEDIATE) + set(KWSYS_CXX_SRCS ${KWSYS_CXX_SRCS} ${KWSYS_HEADER_DIR}/${c}.hxx) + + # Create an install target for the header. + if(KWSYS_INSTALL_INCLUDE_DIR) + install(FILES ${KWSYS_HEADER_DIR}/${c}.hxx + DESTINATION ${KWSYS_INSTALL_INCLUDE_DIR}/${KWSYS_NAMESPACE} + ${KWSYS_INSTALL_INCLUDE_OPTIONS}) + endif() +endforeach() + +# Configure C headers. +foreach(h ${KWSYS_H_FILES}) + # Configure the header into the given directory. + configure_file(${PROJECT_SOURCE_DIR}/${h}.h.in ${KWSYS_HEADER_DIR}/${h}.h + @ONLY IMMEDIATE) + set(KWSYS_C_SRCS ${KWSYS_C_SRCS} ${KWSYS_HEADER_DIR}/${h}.h) + + # Create an install target for the header. + if(KWSYS_INSTALL_INCLUDE_DIR) + install(FILES ${KWSYS_HEADER_DIR}/${h}.h + DESTINATION ${KWSYS_INSTALL_INCLUDE_DIR}/${KWSYS_NAMESPACE} + ${KWSYS_INSTALL_INCLUDE_OPTIONS}) + endif() +endforeach() + +# Configure other C++ headers. +foreach(h ${KWSYS_HXX_FILES}) + # Configure the header into the given directory. + configure_file(${PROJECT_SOURCE_DIR}/${h}.hxx.in ${KWSYS_HEADER_DIR}/${h}.hxx + @ONLY IMMEDIATE) + set(KWSYS_CXX_SRCS ${KWSYS_CXX_SRCS} ${KWSYS_HEADER_DIR}/${h}.hxx) + + # Create an install target for the header. + if(KWSYS_INSTALL_INCLUDE_DIR) + install(FILES ${KWSYS_HEADER_DIR}/${h}.hxx + DESTINATION ${KWSYS_INSTALL_INCLUDE_DIR}/${KWSYS_NAMESPACE} + ${KWSYS_INSTALL_INCLUDE_OPTIONS}) + endif() +endforeach() + +#----------------------------------------------------------------------------- +# Add the library with the configured name and list of sources. +if(KWSYS_C_SRCS OR KWSYS_CXX_SRCS) + if(KWSYS_SPLIT_OBJECTS_FROM_INTERFACE) + set(KWSYS_TARGET_INTERFACE ${KWSYS_NAMESPACE}) + set(KWSYS_TARGET_OBJECT ${KWSYS_NAMESPACE}_objects) + set(KWSYS_TARGET_LINK ${KWSYS_NAMESPACE}_private) + set(KWSYS_TARGET_INSTALL ${KWSYS_TARGET_INTERFACE} ${KWSYS_TARGET_LINK}) + set(KWSYS_LINK_DEPENDENCY INTERFACE) + add_library(${KWSYS_TARGET_OBJECT} OBJECT + ${KWSYS_C_SRCS} ${KWSYS_CXX_SRCS}) + if(KWSYS_BUILD_SHARED OR KWSYS_BUILD_PIC) + set_property(TARGET ${KWSYS_TARGET_OBJECT} PROPERTY + POSITION_INDEPENDENT_CODE TRUE) + endif() + add_library(${KWSYS_TARGET_INTERFACE} INTERFACE) + add_library(${KWSYS_TARGET_LINK} INTERFACE) + target_link_libraries(${KWSYS_TARGET_LINK} INTERFACE + ${KWSYS_TARGET_INTERFACE}) + target_sources(${KWSYS_TARGET_LINK} INTERFACE + $<TARGET_OBJECTS:${KWSYS_TARGET_OBJECT}>) + target_compile_features(${KWSYS_TARGET_OBJECT} PRIVATE ${KWSYS_CXX_COMPILE_FEATURES}) + target_compile_features(${KWSYS_TARGET_INTERFACE} INTERFACE ${KWSYS_CXX_COMPILE_FEATURES}) + else() + set(KWSYS_TARGET_INTERFACE ${KWSYS_NAMESPACE}) + set(KWSYS_TARGET_OBJECT ${KWSYS_NAMESPACE}) + set(KWSYS_TARGET_LINK ${KWSYS_NAMESPACE}) + set(KWSYS_TARGET_INSTALL ${KWSYS_TARGET_LINK}) + set(KWSYS_LINK_DEPENDENCY PUBLIC) + add_library(${KWSYS_TARGET_INTERFACE} ${KWSYS_LIBRARY_TYPE} + ${KWSYS_C_SRCS} ${KWSYS_CXX_SRCS}) + target_compile_features(${KWSYS_TARGET_INTERFACE} PUBLIC ${KWSYS_CXX_COMPILE_FEATURES}) + endif() + if (KWSYS_ALIAS_TARGET) + add_library(${KWSYS_ALIAS_TARGET} ALIAS ${KWSYS_TARGET_INTERFACE}) + endif () + set_target_properties(${KWSYS_TARGET_OBJECT} PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + C_INCLUDE_WHAT_YOU_USE "" + CXX_INCLUDE_WHAT_YOU_USE "" + LABELS "${KWSYS_LABELS_LIB}") + if(KWSYS_USE_DynamicLoader) + if(UNIX) + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} + ${CMAKE_DL_LIBS}) + endif() + endif() + + if(KWSYS_USE_SystemInformation) + if(WIN32) + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} ws2_32) + # link in dbghelp.dll for symbol lookup if MSVC 1800 or later + # Note that the dbghelp runtime is part of MS Windows OS + if(MSVC_VERSION AND NOT MSVC_VERSION VERSION_LESS 1800) + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} dbghelp) + endif() + if(KWSYS_SYS_HAS_PSAPI) + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} + psapi) + endif() + elseif(UNIX) + if (EXECINFO_LIB AND KWSYS_CXX_HAS_BACKTRACE) + # backtrace on FreeBSD is not in libc + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} + ${EXECINFO_LIB}) + endif() + if (KWSYS_CXX_HAS_DLADDR) + # for symbol lookup using dladdr + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} + ${CMAKE_DL_LIBS}) + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + target_link_libraries(${KWSYS_TARGET_INTERFACE} ${KWSYS_LINK_DEPENDENCY} + socket) + endif() + endif() + endif() + + # Apply user-defined target properties to the library. + if(KWSYS_PROPERTIES_CXX) + set_target_properties(${KWSYS_TARGET_INTERFACE} PROPERTIES + ${KWSYS_PROPERTIES_CXX}) + endif() + + # Set up include usage requirement + if(COMMAND TARGET_INCLUDE_DIRECTORIES) + target_include_directories(${KWSYS_TARGET_INTERFACE} INTERFACE + $<BUILD_INTERFACE:${KWSYS_HEADER_ROOT}>) + if(KWSYS_INSTALL_INCLUDE_DIR) + target_include_directories(${KWSYS_TARGET_INTERFACE} INTERFACE + $<INSTALL_INTERFACE:${KWSYS_INSTALL_INCLUDE_DIR}>) + endif() + endif() + + # Create an install target for the library. + if(KWSYS_INSTALL_LIBRARY_RULE) + install(TARGETS ${KWSYS_TARGET_INSTALL} ${KWSYS_INSTALL_LIBRARY_RULE}) + endif() + if(KWSYS_INSTALL_NAMELINK_RULE) + install(TARGETS ${KWSYS_TARGET_INSTALL} ${KWSYS_INSTALL_NAMELINK_RULE}) + endif() +endif() + +# Add a C-only library if requested. +if(KWSYS_ENABLE_C AND KWSYS_C_SRCS) + if(KWSYS_SPLIT_OBJECTS_FROM_INTERFACE) + set(KWSYS_TARGET_C_INTERFACE ${KWSYS_NAMESPACE}_c) + set(KWSYS_TARGET_C_OBJECT ${KWSYS_NAMESPACE}_c_objects) + set(KWSYS_TARGET_C_LINK ${KWSYS_NAMESPACE}_c_private) + set(KWSYS_TARGET_C_INSTALL + ${KWSYS_TARGET_C_INTERFACE} ${KWSYS_TARGET_C_LINK}) + set(KWSYS_LINK_DEPENDENCY INTERFACE) + add_library(${KWSYS_TARGET_C_OBJECT} OBJECT ${KWSYS_C_SRCS}) + if(KWSYS_BUILD_SHARED OR KWSYS_BUILD_PIC) + set_property(TARGET ${KWSYS_TARGET_C_OBJECT} PROPERTY + POSITION_INDEPENDENT_CODE TRUE) + endif() + add_library(${KWSYS_TARGET_C_INTERFACE} INTERFACE) + add_library(${KWSYS_TARGET_C_LINK} INTERFACE) + target_link_libraries(${KWSYS_TARGET_C_LINK} INTERFACE + ${KWSYS_TARGET_C_INTERFACE}) + target_sources(${KWSYS_TARGET_C_LINK} INTERFACE + $<TARGET_OBJECTS:${KWSYS_TARGET_C_OBJECT}>) + else() + set(KWSYS_TARGET_C_INTERFACE ${KWSYS_NAMESPACE}_c) + set(KWSYS_TARGET_C_OBJECT ${KWSYS_NAMESPACE}_c) + set(KWSYS_TARGET_C_LINK ${KWSYS_NAMESPACE}_c) + set(KWSYS_TARGET_C_INSTALL ${KWSYS_TARGET_C_LINK}) + set(KWSYS_LINK_DEPENDENCY PUBLIC) + add_library(${KWSYS_TARGET_C_INTERFACE} ${KWSYS_LIBRARY_TYPE} + ${KWSYS_C_SRCS}) + endif() + set_target_properties(${KWSYS_TARGET_C_OBJECT} PROPERTIES + LABELS "${KWSYS_LABELS_LIB}") + + # Apply user-defined target properties to the library. + if(KWSYS_PROPERTIES_C) + set_target_properties(${KWSYS_TARGET_C_INTERFACE} PROPERTIES + ${KWSYS_PROPERTIES_C}) + endif() + + # Set up include usage requirement + if(COMMAND TARGET_INCLUDE_DIRECTORIES) + target_include_directories(${KWSYS_TARGET_C_INTERFACE} INTERFACE + $<BUILD_INTERFACE:${KWSYS_HEADER_ROOT}>) + if(KWSYS_INSTALL_INCLUDE_DIR) + target_include_directories(${KWSYS_TARGET_C_INTERFACE} INTERFACE + $<INSTALL_INTERFACE:${KWSYS_INSTALL_INCLUDE_DIR}>) + endif() + endif() + + # Create an install target for the library. + if(KWSYS_INSTALL_LIBRARY_RULE) + install(TARGETS ${KWSYS_TARGET_C_INSTALL}) + endif() +endif() + +# For building kwsys itself, we use a macro defined on the command +# line to configure the namespace in the C and C++ source files. +add_definitions("-DKWSYS_NAMESPACE=${KWSYS_NAMESPACE}") + +# Disable deprecation warnings for standard C functions. +if(MSVC OR (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "Intel" OR + (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))) + add_definitions( + -D_CRT_NONSTDC_NO_DEPRECATE + -D_CRT_SECURE_NO_DEPRECATE + -D_CRT_SECURE_NO_WARNINGS + -D_SCL_SECURE_NO_DEPRECATE + ) +endif() + +if(WIN32) + # Help enforce the use of wide Windows apis. + add_definitions(-DUNICODE -D_UNICODE) +endif() + +if(KWSYS_USE_String) + # Activate code in "String.c". See the comment in the source. + set_source_files_properties(String.c PROPERTIES + COMPILE_FLAGS "-DKWSYS_STRING_C") +endif() + +if(KWSYS_USE_Encoding) + # Set default 8 bit encoding in "EndcodingC.c". + set_property(SOURCE EncodingC.c EncodingCXX.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) +endif() + +#----------------------------------------------------------------------------- +# Setup testing if not being built as part of another project. +if(KWSYS_STANDALONE OR CMake_SOURCE_DIR) + if(BUILD_TESTING) + # Compute the location of executables. + set(EXEC_DIR "${CMAKE_CURRENT_BINARY_DIR}") + if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(EXEC_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + endif() + + # C tests + set(KWSYS_C_TESTS + testEncode.c + testTerminal.c + ) + if(KWSYS_STANDALONE) + set(KWSYS_C_TESTS ${KWSYS_C_TESTS} testFail.c) + endif() + create_test_sourcelist( + KWSYS_C_TEST_SRCS ${KWSYS_NAMESPACE}TestsC.c + ${KWSYS_C_TESTS} + ) + add_executable(${KWSYS_NAMESPACE}TestsC ${KWSYS_C_TEST_SRCS}) + set_property(TARGET ${KWSYS_NAMESPACE}TestsC PROPERTY LABELS ${KWSYS_LABELS_EXE}) + target_link_libraries(${KWSYS_NAMESPACE}TestsC ${KWSYS_TARGET_C_LINK}) + foreach(testfile ${KWSYS_C_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) + add_test(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsC ${test} ${KWSYS_TEST_ARGS_${test}}) + set_property(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST}) + endforeach() + + # C++ tests + if(NOT WATCOM AND NOT CMake_SOURCE_DIR) + set(KWSYS_CXX_TESTS + testHashSTL.cxx + ) + endif() + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} + testConfigure.cxx + testSystemTools.cxx + testCommandLineArguments.cxx + testCommandLineArguments1.cxx + testDirectory.cxx + ) + if(KWSYS_STL_HAS_WSTRING) + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} + testEncoding.cxx + ) + endif() + if(KWSYS_USE_FStream) + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} + testFStream.cxx + ) + endif() + if(KWSYS_USE_ConsoleBuf) + add_executable(testConsoleBufChild testConsoleBufChild.cxx) + set_property(TARGET testConsoleBufChild PROPERTY C_CLANG_TIDY "") + set_property(TARGET testConsoleBufChild PROPERTY CXX_CLANG_TIDY "") + set_property(TARGET testConsoleBufChild PROPERTY C_INCLUDE_WHAT_YOU_USE "") + set_property(TARGET testConsoleBufChild PROPERTY CXX_INCLUDE_WHAT_YOU_USE "") + set_property(TARGET testConsoleBufChild PROPERTY LABELS ${KWSYS_LABELS_EXE}) + target_link_libraries(testConsoleBufChild ${KWSYS_TARGET_LINK}) + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} + testConsoleBuf.cxx + ) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "19.0.23506") + set_property(SOURCE testConsoleBuf.cxx testConsoleBufChild.cxx PROPERTY COMPILE_FLAGS /utf-8) + endif() + set_property(SOURCE testConsoleBuf.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) + endif() + if(KWSYS_USE_SystemInformation) + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation.cxx) + endif() + if(KWSYS_USE_DynamicLoader) + set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testDynamicLoader.cxx) + # If kwsys contains the DynamicLoader, need extra library + add_library(${KWSYS_NAMESPACE}TestDynload MODULE testDynload.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynload PROPERTY LABELS ${KWSYS_LABELS_LIB}) + add_dependencies(${KWSYS_NAMESPACE}TestDynload ${KWSYS_TARGET_INTERFACE}) + + if (WIN32) + # Windows tests supported flags. + add_library(${KWSYS_NAMESPACE}TestDynloadImpl SHARED testDynloadImpl.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY LABELS ${KWSYS_LABELS_LIB}) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY DEFINE_SYMBOL BUILDING_TestDynloadImpl) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynloaddir") + add_dependencies(${KWSYS_NAMESPACE}TestDynloadImpl ${KWSYS_TARGET_INTERFACE}) + add_library(${KWSYS_NAMESPACE}TestDynloadUse MODULE testDynloadUse.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadUse PROPERTY LABELS ${KWSYS_LABELS_LIB}) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadUse PROPERTY LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynloaddir") + add_dependencies(${KWSYS_NAMESPACE}TestDynloadUse ${KWSYS_TARGET_INTERFACE}) + target_link_libraries(${KWSYS_NAMESPACE}TestDynloadUse PRIVATE ${KWSYS_NAMESPACE}TestDynloadImpl) + endif () + endif() + create_test_sourcelist( + KWSYS_CXX_TEST_SRCS ${KWSYS_NAMESPACE}TestsCxx.cxx + ${KWSYS_CXX_TESTS} + ) + add_executable(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_CXX_TEST_SRCS}) + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY C_CLANG_TIDY "") + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY CXX_CLANG_TIDY "") + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY C_INCLUDE_WHAT_YOU_USE "") + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY CXX_INCLUDE_WHAT_YOU_USE "") + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY LABELS ${KWSYS_LABELS_EXE}) + target_link_libraries(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_TARGET_LINK}) + + set(TEST_SYSTEMTOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(TEST_SYSTEMTOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + configure_file( + ${PROJECT_SOURCE_DIR}/testSystemTools.h.in + ${PROJECT_BINARY_DIR}/testSystemTools.h) + include_directories(${PROJECT_BINARY_DIR}) + + if(CTEST_TEST_KWSYS) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/ExtraTest.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/ExtraTest.cmake") + set_directory_properties(PROPERTIES TEST_INCLUDE_FILE "${CMAKE_CURRENT_BINARY_DIR}/ExtraTest.cmake") + endif() + + set(KWSYS_TEST_ARGS_testCommandLineArguments + --another-bool-variable + --long3=opt + --set-bool-arg1 + -SSS ken brad bill andy + --some-bool-variable=true + --some-double-variable12.5 + --some-int-variable 14 + "--some-string-variable=test string with space" + --some-multi-argument 5 1 8 3 7 1 3 9 7 1 + -N 12.5 -SS=andy -N 1.31 -N 22 + -SS=bill -BBtrue -SS=brad + -BBtrue + -BBfalse + -SS=ken + -A + -C=test + --long2 hello + ) + set(KWSYS_TEST_ARGS_testCommandLineArguments1 + --ignored + -n 24 + --second-ignored + "-m=test value" + third-ignored + -p + some junk at the end + ) + foreach(testfile ${KWSYS_CXX_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) + add_test(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsCxx ${test} ${KWSYS_TEST_ARGS_${test}}) + set_property(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST}) + endforeach() + + # Process tests. + add_executable(${KWSYS_NAMESPACE}TestProcess testProcess.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestProcess PROPERTY LABELS ${KWSYS_LABELS_EXE}) + target_link_libraries(${KWSYS_NAMESPACE}TestProcess ${KWSYS_TARGET_C_LINK}) + if(NOT CYGWIN) + set(KWSYS_TEST_PROCESS_7 7) + endif() + foreach(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10) + add_test(kwsys.testProcess-${n} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n}) + set_property(TEST kwsys.testProcess-${n} PROPERTY LABELS ${KWSYS_LABELS_TEST}) + set_tests_properties(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120) + endforeach() + + set(testProcess_COMPILE_FLAGS "") + # Some Apple compilers produce bad optimizations in this source. + if(APPLE AND CMAKE_C_COMPILER_ID MATCHES "^(GNU|LLVM)$") + set(testProcess_COMPILE_FLAGS "${testProcess_COMPILE_FLAGS} -O0") + elseif(CMAKE_C_COMPILER_ID STREQUAL "XL") + # Tell IBM XL not to warn about our test infinite loop + if(CMAKE_SYSTEM MATCHES "Linux.*ppc64le" + AND CMAKE_C_COMPILER_VERSION VERSION_LESS "16.1.0" + AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS "13.1.1") + # v13.1.[1-6] on Linux ppc64le is clang based and does not accept + # the -qsuppress option, so just suppress all warnings. + set(testProcess_COMPILE_FLAGS "${testProcess_COMPILE_FLAGS} -w") + else() + set(testProcess_COMPILE_FLAGS "${testProcess_COMPILE_FLAGS} -qsuppress=1500-010") + endif() + endif() + if(CMAKE_C_FLAGS MATCHES "-fsanitize=") + set(testProcess_COMPILE_FLAGS "${testProcess_COMPILE_FLAGS} -DCRASH_USING_ABORT") + endif() + set_property(SOURCE testProcess.c PROPERTY COMPILE_FLAGS "${testProcess_COMPILE_FLAGS}") + + # Test SharedForward + configure_file(${PROJECT_SOURCE_DIR}/testSharedForward.c.in + ${PROJECT_BINARY_DIR}/testSharedForward.c @ONLY IMMEDIATE) + add_executable(${KWSYS_NAMESPACE}TestSharedForward + ${PROJECT_BINARY_DIR}/testSharedForward.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestSharedForward PROPERTY LABELS ${KWSYS_LABELS_EXE}) + add_dependencies(${KWSYS_NAMESPACE}TestSharedForward ${KWSYS_TARGET_C_LINK}) + add_test(kwsys.testSharedForward ${EXEC_DIR}/${KWSYS_NAMESPACE}TestSharedForward 1) + set_property(TEST kwsys.testSharedForward PROPERTY LABELS ${KWSYS_LABELS_TEST}) + + # Configure some test properties. + if(KWSYS_STANDALONE) + # We expect test to fail + set_tests_properties(kwsys.testFail PROPERTIES WILL_FAIL ON) + get_test_property(kwsys.testFail WILL_FAIL wfv) + set_tests_properties(kwsys.testFail PROPERTIES MEASUREMENT "Some Key=Some Value") + message(STATUS "GET_TEST_PROPERTY returned: ${wfv}") + endif() + + # Set up ctest custom configuration file. + configure_file(${PROJECT_SOURCE_DIR}/CTestCustom.cmake.in + ${PROJECT_BINARY_DIR}/CTestCustom.cmake @ONLY) + + # Suppress known consistent failures on buggy systems. + if(KWSYS_TEST_BOGUS_FAILURES) + set_tests_properties(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL ON) + endif() + + endif() +endif() diff --git a/Source/kwsys/CONTRIBUTING.rst b/Source/kwsys/CONTRIBUTING.rst new file mode 100644 index 0000000..32e7b83 --- /dev/null +++ b/Source/kwsys/CONTRIBUTING.rst @@ -0,0 +1,49 @@ +Contributing to KWSys +********************* + +Patches +======= + +KWSys is kept in its own Git repository and shared by several projects +via copies in their source trees. Changes to KWSys should not be made +directly in a host project, except perhaps in maintenance branches. + +KWSys uses `Kitware's GitLab Instance`_ to manage development and code review. +To contribute patches: + +#. Fork the upstream `KWSys Repository`_ into a personal account. +#. Base all new work on the upstream ``master`` branch. +#. Run ``./SetupForDevelopment.sh`` in new local work trees. +#. Create commits making incremental, distinct, logically complete changes. +#. Push a topic branch to a personal repository fork on GitLab. +#. Create a GitLab Merge Request targeting the upstream ``master`` branch. + +Once changes are reviewed, tested, and integrated to KWSys upstream then +copies of KWSys within dependent projects can be updated to get the changes. + +.. _`Kitware's GitLab Instance`: https://gitlab.kitware.com +.. _`KWSys Repository`: https://gitlab.kitware.com/utils/kwsys + +Code Style +========== + +We use `clang-format`_ version **6.0** to define our style for C++ code in +the KWSys source tree. See the `.clang-format`_ configuration file for +our style settings. Use the `clang-format.bash`_ script to format source +code. It automatically runs ``clang-format`` on the set of source files +for which we enforce style. The script also has options to format only +a subset of files, such as those that are locally modified. + +.. _`clang-format`: http://clang.llvm.org/docs/ClangFormat.html +.. _`.clang-format`: .clang-format +.. _`clang-format.bash`: clang-format.bash + +License +======= + +We do not require any formal copyright assignment or contributor license +agreement. Any contributions intentionally sent upstream are presumed +to be offered under terms of the OSI-approved BSD 3-clause License. +See `Copyright.txt`_ for details. + +.. _`Copyright.txt`: Copyright.txt diff --git a/Source/kwsys/CTestConfig.cmake b/Source/kwsys/CTestConfig.cmake new file mode 100644 index 0000000..33ea84c --- /dev/null +++ b/Source/kwsys/CTestConfig.cmake @@ -0,0 +1,9 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing#kwsys for details. + +set(CTEST_PROJECT_NAME "KWSys") +set(CTEST_NIGHTLY_START_TIME "21:00:00 EDT") +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "open.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Source/kwsys/CTestCustom.cmake.in b/Source/kwsys/CTestCustom.cmake.in new file mode 100644 index 0000000..c07f0f3 --- /dev/null +++ b/Source/kwsys/CTestCustom.cmake.in @@ -0,0 +1,18 @@ +# kwsys.testProcess-10 involves sending SIGINT to a child process, which then +# exits abnormally via a call to _exit(). (On Windows, a call to ExitProcess). +# Naturally, this results in plenty of memory being "leaked" by this child +# process - the memory check results are not meaningful in this case. +# +# kwsys.testProcess-9 also tests sending SIGINT to a child process. However, +# normal operation of that test involves the child process timing out, and the +# host process kills (SIGKILL) it as a result. Since it was SIGKILL'ed, the +# resulting memory leaks are not logged by valgrind anyway. Therefore, we +# don't have to exclude it. + +list(APPEND CTEST_CUSTOM_MEMCHECK_IGNORE + kwsys.testProcess-10 + ) + +list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION + "LICENSE WARNING" + ) diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx new file mode 100644 index 0000000..a2ed874 --- /dev/null +++ b/Source/kwsys/CommandLineArguments.cxx @@ -0,0 +1,760 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(CommandLineArguments.hxx) + +#include KWSYS_HEADER(Configure.hxx) +#include KWSYS_HEADER(String.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "CommandLineArguments.hxx.in" +# include "Configure.hxx.in" +# include "String.hxx.in" +#endif + +#include <iostream> +#include <map> +#include <set> +#include <sstream> +#include <vector> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _MSC_VER +# pragma warning(disable : 4786) +#endif + +#if defined(__sgi) && !defined(__GNUC__) +# pragma set woff 1375 /* base class destructor not virtual */ +#endif + +#if 0 +# define CommandLineArguments_DEBUG(x) \ + std::cout << __LINE__ << " CLA: " << x << std::endl +#else +# define CommandLineArguments_DEBUG(x) +#endif + +namespace KWSYS_NAMESPACE { + +struct CommandLineArgumentsCallbackStructure +{ + const char* Argument; + int ArgumentType; + CommandLineArguments::CallbackType Callback; + void* CallData; + void* Variable; + int VariableType; + const char* Help; +}; + +class CommandLineArgumentsVectorOfStrings : public std::vector<kwsys::String> +{ +}; +class CommandLineArgumentsSetOfStrings : public std::set<kwsys::String> +{ +}; +class CommandLineArgumentsMapOfStrucs + : public std::map<kwsys::String, CommandLineArgumentsCallbackStructure> +{ +}; + +class CommandLineArgumentsInternal +{ +public: + CommandLineArgumentsInternal() = default; + + using VectorOfStrings = CommandLineArgumentsVectorOfStrings; + using CallbacksMap = CommandLineArgumentsMapOfStrucs; + using String = kwsys::String; + using SetOfStrings = CommandLineArgumentsSetOfStrings; + + VectorOfStrings Argv; + String Argv0; + CallbacksMap Callbacks; + + CommandLineArguments::ErrorCallbackType UnknownArgumentCallback{ nullptr }; + void* ClientData{ nullptr }; + + VectorOfStrings::size_type LastArgument{ 0 }; + + VectorOfStrings UnusedArguments; +}; + +CommandLineArguments::CommandLineArguments() +{ + this->Internals = new CommandLineArguments::Internal; + this->Help = ""; + this->LineLength = 80; + this->StoreUnusedArgumentsFlag = false; +} + +CommandLineArguments::~CommandLineArguments() +{ + delete this->Internals; +} + +void CommandLineArguments::Initialize(int argc, const char* const argv[]) +{ + int cc; + + this->Initialize(); + this->Internals->Argv0 = argv[0]; + for (cc = 1; cc < argc; cc++) { + this->ProcessArgument(argv[cc]); + } +} + +void CommandLineArguments::Initialize(int argc, char* argv[]) +{ + this->Initialize(argc, static_cast<const char* const*>(argv)); +} + +void CommandLineArguments::Initialize() +{ + this->Internals->Argv.clear(); + this->Internals->LastArgument = 0; +} + +void CommandLineArguments::ProcessArgument(const char* arg) +{ + this->Internals->Argv.push_back(arg); +} + +bool CommandLineArguments::GetMatchedArguments( + std::vector<std::string>* matches, const std::string& arg) +{ + matches->clear(); + CommandLineArguments::Internal::CallbacksMap::iterator it; + + // Does the argument match to any we know about? + for (it = this->Internals->Callbacks.begin(); + it != this->Internals->Callbacks.end(); it++) { + const CommandLineArguments::Internal::String& parg = it->first; + CommandLineArgumentsCallbackStructure* cs = &it->second; + if (cs->ArgumentType == CommandLineArguments::NO_ARGUMENT || + cs->ArgumentType == CommandLineArguments::SPACE_ARGUMENT) { + if (arg == parg) { + matches->push_back(parg); + } + } else if (arg.find(parg) == 0) { + matches->push_back(parg); + } + } + return !matches->empty(); +} + +int CommandLineArguments::Parse() +{ + std::vector<std::string>::size_type cc; + std::vector<std::string> matches; + if (this->StoreUnusedArgumentsFlag) { + this->Internals->UnusedArguments.clear(); + } + for (cc = 0; cc < this->Internals->Argv.size(); cc++) { + const std::string& arg = this->Internals->Argv[cc]; + CommandLineArguments_DEBUG("Process argument: " << arg); + this->Internals->LastArgument = cc; + if (this->GetMatchedArguments(&matches, arg)) { + // Ok, we found one or more arguments that match what user specified. + // Let's find the longest one. + CommandLineArguments::Internal::VectorOfStrings::size_type kk; + CommandLineArguments::Internal::VectorOfStrings::size_type maxidx = 0; + CommandLineArguments::Internal::String::size_type maxlen = 0; + for (kk = 0; kk < matches.size(); kk++) { + if (matches[kk].size() > maxlen) { + maxlen = matches[kk].size(); + maxidx = kk; + } + } + // So, the longest one is probably the right one. Now see if it has any + // additional value + CommandLineArgumentsCallbackStructure* cs = + &this->Internals->Callbacks[matches[maxidx]]; + const std::string& sarg = matches[maxidx]; + if (cs->Argument != sarg) { + abort(); + } + switch (cs->ArgumentType) { + case NO_ARGUMENT: + // No value + if (!this->PopulateVariable(cs, nullptr)) { + return 0; + } + break; + case SPACE_ARGUMENT: + if (cc == this->Internals->Argv.size() - 1) { + this->Internals->LastArgument--; + return 0; + } + CommandLineArguments_DEBUG("This is a space argument: " + << arg << " value: " + << this->Internals->Argv[cc + 1]); + // Value is the next argument + if (!this->PopulateVariable(cs, + this->Internals->Argv[cc + 1].c_str())) { + return 0; + } + cc++; + break; + case EQUAL_ARGUMENT: + if (arg.size() == sarg.size() || arg.at(sarg.size()) != '=') { + this->Internals->LastArgument--; + return 0; + } + // Value is everythng followed the '=' sign + if (!this->PopulateVariable(cs, arg.c_str() + sarg.size() + 1)) { + return 0; + } + break; + case CONCAT_ARGUMENT: + // Value is whatever follows the argument + if (!this->PopulateVariable(cs, arg.c_str() + sarg.size())) { + return 0; + } + break; + case MULTI_ARGUMENT: + // Suck in all the rest of the arguments + CommandLineArguments_DEBUG("This is a multi argument: " << arg); + for (cc++; cc < this->Internals->Argv.size(); ++cc) { + const std::string& marg = this->Internals->Argv[cc]; + CommandLineArguments_DEBUG( + " check multi argument value: " << marg); + if (this->GetMatchedArguments(&matches, marg)) { + CommandLineArguments_DEBUG("End of multi argument " + << arg << " with value: " << marg); + break; + } + CommandLineArguments_DEBUG( + " populate multi argument value: " << marg); + if (!this->PopulateVariable(cs, marg.c_str())) { + return 0; + } + } + if (cc != this->Internals->Argv.size()) { + CommandLineArguments_DEBUG("Again End of multi argument " << arg); + cc--; + continue; + } + break; + default: + std::cerr << "Got unknown argument type: \"" << cs->ArgumentType + << "\"" << std::endl; + this->Internals->LastArgument--; + return 0; + } + } else { + // Handle unknown arguments + if (this->Internals->UnknownArgumentCallback) { + if (!this->Internals->UnknownArgumentCallback( + arg.c_str(), this->Internals->ClientData)) { + this->Internals->LastArgument--; + return 0; + } + return 1; + } else if (this->StoreUnusedArgumentsFlag) { + CommandLineArguments_DEBUG("Store unused argument " << arg); + this->Internals->UnusedArguments.push_back(arg); + } else { + std::cerr << "Got unknown argument: \"" << arg << "\"" << std::endl; + this->Internals->LastArgument--; + return 0; + } + } + } + return 1; +} + +void CommandLineArguments::GetRemainingArguments(int* argc, char*** argv) +{ + CommandLineArguments::Internal::VectorOfStrings::size_type size = + this->Internals->Argv.size() - this->Internals->LastArgument + 1; + CommandLineArguments::Internal::VectorOfStrings::size_type cc; + + // Copy Argv0 as the first argument + char** args = new char*[size]; + args[0] = new char[this->Internals->Argv0.size() + 1]; + strcpy(args[0], this->Internals->Argv0.c_str()); + int cnt = 1; + + // Copy everything after the LastArgument, since that was not parsed. + for (cc = this->Internals->LastArgument + 1; + cc < this->Internals->Argv.size(); cc++) { + args[cnt] = new char[this->Internals->Argv[cc].size() + 1]; + strcpy(args[cnt], this->Internals->Argv[cc].c_str()); + cnt++; + } + *argc = cnt; + *argv = args; +} + +void CommandLineArguments::GetUnusedArguments(int* argc, char*** argv) +{ + CommandLineArguments::Internal::VectorOfStrings::size_type size = + this->Internals->UnusedArguments.size() + 1; + CommandLineArguments::Internal::VectorOfStrings::size_type cc; + + // Copy Argv0 as the first argument + char** args = new char*[size]; + args[0] = new char[this->Internals->Argv0.size() + 1]; + strcpy(args[0], this->Internals->Argv0.c_str()); + int cnt = 1; + + // Copy everything after the LastArgument, since that was not parsed. + for (cc = 0; cc < this->Internals->UnusedArguments.size(); cc++) { + kwsys::String& str = this->Internals->UnusedArguments[cc]; + args[cnt] = new char[str.size() + 1]; + strcpy(args[cnt], str.c_str()); + cnt++; + } + *argc = cnt; + *argv = args; +} + +void CommandLineArguments::DeleteRemainingArguments(int argc, char*** argv) +{ + int cc; + for (cc = 0; cc < argc; ++cc) { + delete[](*argv)[cc]; + } + delete[] * argv; +} + +void CommandLineArguments::AddCallback(const char* argument, + ArgumentTypeEnum type, + CallbackType callback, void* call_data, + const char* help) +{ + CommandLineArgumentsCallbackStructure s; + s.Argument = argument; + s.ArgumentType = type; + s.Callback = callback; + s.CallData = call_data; + s.VariableType = CommandLineArguments::NO_VARIABLE_TYPE; + s.Variable = nullptr; + s.Help = help; + + this->Internals->Callbacks[argument] = s; + this->GenerateHelp(); +} + +void CommandLineArguments::AddArgument(const char* argument, + ArgumentTypeEnum type, + VariableTypeEnum vtype, void* variable, + const char* help) +{ + CommandLineArgumentsCallbackStructure s; + s.Argument = argument; + s.ArgumentType = type; + s.Callback = nullptr; + s.CallData = nullptr; + s.VariableType = vtype; + s.Variable = variable; + s.Help = help; + + this->Internals->Callbacks[argument] = s; + this->GenerateHelp(); +} + +#define CommandLineArgumentsAddArgumentMacro(type, ctype) \ + void CommandLineArguments::AddArgument(const char* argument, \ + ArgumentTypeEnum type, \ + ctype* variable, const char* help) \ + { \ + this->AddArgument(argument, type, CommandLineArguments::type##_TYPE, \ + variable, help); \ + } + +/* clang-format off */ +CommandLineArgumentsAddArgumentMacro(BOOL, bool) +CommandLineArgumentsAddArgumentMacro(INT, int) +CommandLineArgumentsAddArgumentMacro(DOUBLE, double) +CommandLineArgumentsAddArgumentMacro(STRING, char*) +CommandLineArgumentsAddArgumentMacro(STL_STRING, std::string) + +CommandLineArgumentsAddArgumentMacro(VECTOR_BOOL, std::vector<bool>) +CommandLineArgumentsAddArgumentMacro(VECTOR_INT, std::vector<int>) +CommandLineArgumentsAddArgumentMacro(VECTOR_DOUBLE, std::vector<double>) +CommandLineArgumentsAddArgumentMacro(VECTOR_STRING, std::vector<char*>) +CommandLineArgumentsAddArgumentMacro(VECTOR_STL_STRING, + std::vector<std::string>) +#ifdef HELP_CLANG_FORMAT +; +#endif +/* clang-format on */ + +#define CommandLineArgumentsAddBooleanArgumentMacro(type, ctype) \ + void CommandLineArguments::AddBooleanArgument( \ + const char* argument, ctype* variable, const char* help) \ + { \ + this->AddArgument(argument, CommandLineArguments::NO_ARGUMENT, \ + CommandLineArguments::type##_TYPE, variable, help); \ + } + +/* clang-format off */ +CommandLineArgumentsAddBooleanArgumentMacro(BOOL, bool) +CommandLineArgumentsAddBooleanArgumentMacro(INT, int) +CommandLineArgumentsAddBooleanArgumentMacro(DOUBLE, double) +CommandLineArgumentsAddBooleanArgumentMacro(STRING, char*) +CommandLineArgumentsAddBooleanArgumentMacro(STL_STRING, std::string) +#ifdef HELP_CLANG_FORMAT +; +#endif +/* clang-format on */ + +void CommandLineArguments::SetClientData(void* client_data) +{ + this->Internals->ClientData = client_data; +} + +void CommandLineArguments::SetUnknownArgumentCallback( + CommandLineArguments::ErrorCallbackType callback) +{ + this->Internals->UnknownArgumentCallback = callback; +} + +const char* CommandLineArguments::GetHelp(const char* arg) +{ + auto it = this->Internals->Callbacks.find(arg); + if (it == this->Internals->Callbacks.end()) { + return nullptr; + } + + // Since several arguments may point to the same argument, find the one this + // one point to if this one is pointing to another argument. + CommandLineArgumentsCallbackStructure* cs = &(it->second); + for (;;) { + auto hit = this->Internals->Callbacks.find(cs->Help); + if (hit == this->Internals->Callbacks.end()) { + break; + } + cs = &(hit->second); + } + return cs->Help; +} + +void CommandLineArguments::SetLineLength(unsigned int ll) +{ + if (ll < 9 || ll > 1000) { + return; + } + this->LineLength = ll; + this->GenerateHelp(); +} + +const char* CommandLineArguments::GetArgv0() +{ + return this->Internals->Argv0.c_str(); +} + +unsigned int CommandLineArguments::GetLastArgument() +{ + return static_cast<unsigned int>(this->Internals->LastArgument + 1); +} + +void CommandLineArguments::GenerateHelp() +{ + std::ostringstream str; + + // Collapse all arguments into the map of vectors of all arguments that do + // the same thing. + CommandLineArguments::Internal::CallbacksMap::iterator it; + using MapArgs = std::map<CommandLineArguments::Internal::String, + CommandLineArguments::Internal::SetOfStrings>; + MapArgs mp; + MapArgs::iterator mpit, smpit; + for (it = this->Internals->Callbacks.begin(); + it != this->Internals->Callbacks.end(); it++) { + CommandLineArgumentsCallbackStructure* cs = &(it->second); + mpit = mp.find(cs->Help); + if (mpit != mp.end()) { + mpit->second.insert(it->first); + mp[it->first].insert(it->first); + } else { + mp[it->first].insert(it->first); + } + } + for (it = this->Internals->Callbacks.begin(); + it != this->Internals->Callbacks.end(); it++) { + CommandLineArgumentsCallbackStructure* cs = &(it->second); + mpit = mp.find(cs->Help); + if (mpit != mp.end()) { + mpit->second.insert(it->first); + smpit = mp.find(it->first); + CommandLineArguments::Internal::SetOfStrings::iterator sit; + for (sit = smpit->second.begin(); sit != smpit->second.end(); sit++) { + mpit->second.insert(*sit); + } + mp.erase(smpit); + } else { + mp[it->first].insert(it->first); + } + } + + // Find the length of the longest string + CommandLineArguments::Internal::String::size_type maxlen = 0; + for (mpit = mp.begin(); mpit != mp.end(); mpit++) { + CommandLineArguments::Internal::SetOfStrings::iterator sit; + for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) { + CommandLineArguments::Internal::String::size_type clen = sit->size(); + switch (this->Internals->Callbacks[*sit].ArgumentType) { + case CommandLineArguments::NO_ARGUMENT: + clen += 0; + break; + case CommandLineArguments::CONCAT_ARGUMENT: + clen += 3; + break; + case CommandLineArguments::SPACE_ARGUMENT: + clen += 4; + break; + case CommandLineArguments::EQUAL_ARGUMENT: + clen += 4; + break; + } + if (clen > maxlen) { + maxlen = clen; + } + } + } + + CommandLineArguments::Internal::String::size_type maxstrlen = maxlen; + maxlen += 4; // For the space before and after the option + + // Print help for each option + for (mpit = mp.begin(); mpit != mp.end(); mpit++) { + CommandLineArguments::Internal::SetOfStrings::iterator sit; + for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) { + str << std::endl; + std::string argument = *sit; + switch (this->Internals->Callbacks[*sit].ArgumentType) { + case CommandLineArguments::NO_ARGUMENT: + break; + case CommandLineArguments::CONCAT_ARGUMENT: + argument += "opt"; + break; + case CommandLineArguments::SPACE_ARGUMENT: + argument += " opt"; + break; + case CommandLineArguments::EQUAL_ARGUMENT: + argument += "=opt"; + break; + case CommandLineArguments::MULTI_ARGUMENT: + argument += " opt opt ..."; + break; + } + str << " " << argument.substr(0, maxstrlen) << " "; + } + const char* ptr = this->Internals->Callbacks[mpit->first].Help; + size_t len = strlen(ptr); + int cnt = 0; + while (len > 0) { + // If argument with help is longer than line length, split it on previous + // space (or tab) and continue on the next line + CommandLineArguments::Internal::String::size_type cc; + for (cc = 0; ptr[cc]; cc++) { + if (*ptr == ' ' || *ptr == '\t') { + ptr++; + len--; + } + } + if (cnt > 0) { + for (cc = 0; cc < maxlen; cc++) { + str << " "; + } + } + CommandLineArguments::Internal::String::size_type skip = len; + if (skip > this->LineLength - maxlen) { + skip = this->LineLength - maxlen; + for (cc = skip - 1; cc > 0; cc--) { + if (ptr[cc] == ' ' || ptr[cc] == '\t') { + break; + } + } + if (cc != 0) { + skip = cc; + } + } + str.write(ptr, static_cast<std::streamsize>(skip)); + str << std::endl; + ptr += skip; + len -= skip; + cnt++; + } + } + /* + // This can help debugging help string + str << endl; + unsigned int cc; + for ( cc = 0; cc < this->LineLength; cc ++ ) + { + str << cc % 10; + } + str << endl; + */ + this->Help = str.str(); +} + +void CommandLineArguments::PopulateVariable(bool* variable, + const std::string& value) +{ + if (value == "1" || value == "ON" || value == "on" || value == "On" || + value == "TRUE" || value == "true" || value == "True" || + value == "yes" || value == "Yes" || value == "YES") { + *variable = true; + } else { + *variable = false; + } +} + +void CommandLineArguments::PopulateVariable(int* variable, + const std::string& value) +{ + char* res = nullptr; + *variable = static_cast<int>(strtol(value.c_str(), &res, 10)); + // if ( res && *res ) + // { + // Can handle non-int + // } +} + +void CommandLineArguments::PopulateVariable(double* variable, + const std::string& value) +{ + char* res = nullptr; + *variable = strtod(value.c_str(), &res); + // if ( res && *res ) + // { + // Can handle non-double + // } +} + +void CommandLineArguments::PopulateVariable(char** variable, + const std::string& value) +{ + delete[] * variable; + *variable = new char[value.size() + 1]; + strcpy(*variable, value.c_str()); +} + +void CommandLineArguments::PopulateVariable(std::string* variable, + const std::string& value) +{ + *variable = value; +} + +void CommandLineArguments::PopulateVariable(std::vector<bool>* variable, + const std::string& value) +{ + bool val = false; + if (value == "1" || value == "ON" || value == "on" || value == "On" || + value == "TRUE" || value == "true" || value == "True" || + value == "yes" || value == "Yes" || value == "YES") { + val = true; + } + variable->push_back(val); +} + +void CommandLineArguments::PopulateVariable(std::vector<int>* variable, + const std::string& value) +{ + char* res = nullptr; + variable->push_back(static_cast<int>(strtol(value.c_str(), &res, 10))); + // if ( res && *res ) + // { + // Can handle non-int + // } +} + +void CommandLineArguments::PopulateVariable(std::vector<double>* variable, + const std::string& value) +{ + char* res = nullptr; + variable->push_back(strtod(value.c_str(), &res)); + // if ( res && *res ) + // { + // Can handle non-int + // } +} + +void CommandLineArguments::PopulateVariable(std::vector<char*>* variable, + const std::string& value) +{ + char* var = new char[value.size() + 1]; + strcpy(var, value.c_str()); + variable->push_back(var); +} + +void CommandLineArguments::PopulateVariable(std::vector<std::string>* variable, + const std::string& value) +{ + variable->push_back(value); +} + +bool CommandLineArguments::PopulateVariable( + CommandLineArgumentsCallbackStructure* cs, const char* value) +{ + // Call the callback + if (cs->Callback) { + if (!cs->Callback(cs->Argument, value, cs->CallData)) { + this->Internals->LastArgument--; + return false; + } + } + CommandLineArguments_DEBUG("Set argument: " << cs->Argument << " to " + << value); + if (cs->Variable) { + std::string var = "1"; + if (value) { + var = value; + } + switch (cs->VariableType) { + case CommandLineArguments::INT_TYPE: + this->PopulateVariable(static_cast<int*>(cs->Variable), var); + break; + case CommandLineArguments::DOUBLE_TYPE: + this->PopulateVariable(static_cast<double*>(cs->Variable), var); + break; + case CommandLineArguments::STRING_TYPE: + this->PopulateVariable(static_cast<char**>(cs->Variable), var); + break; + case CommandLineArguments::STL_STRING_TYPE: + this->PopulateVariable(static_cast<std::string*>(cs->Variable), var); + break; + case CommandLineArguments::BOOL_TYPE: + this->PopulateVariable(static_cast<bool*>(cs->Variable), var); + break; + case CommandLineArguments::VECTOR_BOOL_TYPE: + this->PopulateVariable(static_cast<std::vector<bool>*>(cs->Variable), + var); + break; + case CommandLineArguments::VECTOR_INT_TYPE: + this->PopulateVariable(static_cast<std::vector<int>*>(cs->Variable), + var); + break; + case CommandLineArguments::VECTOR_DOUBLE_TYPE: + this->PopulateVariable(static_cast<std::vector<double>*>(cs->Variable), + var); + break; + case CommandLineArguments::VECTOR_STRING_TYPE: + this->PopulateVariable(static_cast<std::vector<char*>*>(cs->Variable), + var); + break; + case CommandLineArguments::VECTOR_STL_STRING_TYPE: + this->PopulateVariable( + static_cast<std::vector<std::string>*>(cs->Variable), var); + break; + default: + std::cerr << "Got unknown variable type: \"" << cs->VariableType + << "\"" << std::endl; + this->Internals->LastArgument--; + return false; + } + } + return true; +} + +} // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/CommandLineArguments.hxx.in b/Source/kwsys/CommandLineArguments.hxx.in new file mode 100644 index 0000000..7db9015 --- /dev/null +++ b/Source/kwsys/CommandLineArguments.hxx.in @@ -0,0 +1,270 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_CommandLineArguments_hxx +#define @KWSYS_NAMESPACE@_CommandLineArguments_hxx + +#include <@KWSYS_NAMESPACE@/Configure.h> +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <string> +#include <vector> + +namespace @KWSYS_NAMESPACE@ { + +class CommandLineArgumentsInternal; +struct CommandLineArgumentsCallbackStructure; + +/** \class CommandLineArguments + * \brief Command line arguments processing code. + * + * Find specified arguments with optional options and execute specified methods + * or set given variables. + * + * The two interfaces it knows are callback based and variable based. For + * callback based, you have to register callback for particular argument using + * AddCallback method. When that argument is passed, the callback will be + * called with argument, value, and call data. For boolean (NO_ARGUMENT) + * arguments, the value is "1". If the callback returns 0 the argument parsing + * will stop with an error. + * + * For the variable interface you associate variable with each argument. When + * the argument is specified, the variable is set to the specified value casted + * to the appropriate type. For boolean (NO_ARGUMENT), the value is "1". + * + * Both interfaces can be used at the same time. + * + * Possible argument types are: + * NO_ARGUMENT - The argument takes no value : --A + * CONCAT_ARGUMENT - The argument takes value after no space : --Aval + * SPACE_ARGUMENT - The argument takes value after space : --A val + * EQUAL_ARGUMENT - The argument takes value after equal : --A=val + * MULTI_ARGUMENT - The argument takes values after space : --A val1 val2 + * val3 ... + * + * Example use: + * + * kwsys::CommandLineArguments arg; + * arg.Initialize(argc, argv); + * typedef kwsys::CommandLineArguments argT; + * arg.AddArgument("--something", argT::EQUAL_ARGUMENT, &some_variable, + * "This is help string for --something"); + * if ( !arg.Parse() ) + * { + * std::cerr << "Problem parsing arguments" << std::endl; + * res = 1; + * } + * + */ + +class @KWSYS_NAMESPACE@_EXPORT CommandLineArguments +{ +public: + CommandLineArguments(); + ~CommandLineArguments(); + + CommandLineArguments(const CommandLineArguments&) = delete; + CommandLineArguments& operator=(const CommandLineArguments&) = delete; + + /** + * Various argument types. + */ + enum ArgumentTypeEnum + { + NO_ARGUMENT, + CONCAT_ARGUMENT, + SPACE_ARGUMENT, + EQUAL_ARGUMENT, + MULTI_ARGUMENT + }; + + /** + * Various variable types. When using the variable interface, this specifies + * what type the variable is. + */ + enum VariableTypeEnum + { + NO_VARIABLE_TYPE = 0, // The variable is not specified + INT_TYPE, // The variable is integer (int) + BOOL_TYPE, // The variable is boolean (bool) + DOUBLE_TYPE, // The variable is float (double) + STRING_TYPE, // The variable is string (char*) + STL_STRING_TYPE, // The variable is string (char*) + VECTOR_INT_TYPE, // The variable is integer (int) + VECTOR_BOOL_TYPE, // The variable is boolean (bool) + VECTOR_DOUBLE_TYPE, // The variable is float (double) + VECTOR_STRING_TYPE, // The variable is string (char*) + VECTOR_STL_STRING_TYPE, // The variable is string (char*) + LAST_VARIABLE_TYPE + }; + + /** + * Prototypes for callbacks for callback interface. + */ + typedef int (*CallbackType)(const char* argument, const char* value, + void* call_data); + typedef int (*ErrorCallbackType)(const char* argument, void* client_data); + + /** + * Initialize internal data structures. This should be called before parsing. + */ + void Initialize(int argc, const char* const argv[]); + void Initialize(int argc, char* argv[]); + + /** + * Initialize internal data structure and pass arguments one by one. This is + * convenience method for use from scripting languages where argc and argv + * are not available. + */ + void Initialize(); + void ProcessArgument(const char* arg); + + /** + * This method will parse arguments and call appropriate methods. + */ + int Parse(); + + /** + * This method will add a callback for a specific argument. The arguments to + * it are argument, argument type, callback method, and call data. The + * argument help specifies the help string used with this option. The + * callback and call_data can be skipped. + */ + void AddCallback(const char* argument, ArgumentTypeEnum type, + CallbackType callback, void* call_data, const char* help); + + /** + * Add handler for argument which is going to set the variable to the + * specified value. If the argument is specified, the option is casted to the + * appropriate type. + */ + void AddArgument(const char* argument, ArgumentTypeEnum type, bool* variable, + const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, int* variable, + const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + double* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + char** variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::string* variable, const char* help); + + /** + * Add handler for argument which is going to set the variable to the + * specified value. If the argument is specified, the option is casted to the + * appropriate type. This will handle the multi argument values. + */ + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::vector<bool>* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::vector<int>* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::vector<double>* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::vector<char*>* variable, const char* help); + void AddArgument(const char* argument, ArgumentTypeEnum type, + std::vector<std::string>* variable, const char* help); + + /** + * Add handler for boolean argument. The argument does not take any option + * and if it is specified, the value of the variable is true/1, otherwise it + * is false/0. + */ + void AddBooleanArgument(const char* argument, bool* variable, + const char* help); + void AddBooleanArgument(const char* argument, int* variable, + const char* help); + void AddBooleanArgument(const char* argument, double* variable, + const char* help); + void AddBooleanArgument(const char* argument, char** variable, + const char* help); + void AddBooleanArgument(const char* argument, std::string* variable, + const char* help); + + /** + * Set the callbacks for error handling. + */ + void SetClientData(void* client_data); + void SetUnknownArgumentCallback(ErrorCallbackType callback); + + /** + * Get remaining arguments. It allocates space for argv, so you have to call + * delete[] on it. + */ + void GetRemainingArguments(int* argc, char*** argv); + void DeleteRemainingArguments(int argc, char*** argv); + + /** + * If StoreUnusedArguments is set to true, then all unknown arguments will be + * stored and the user can access the modified argc, argv without known + * arguments. + */ + void StoreUnusedArguments(bool val) { this->StoreUnusedArgumentsFlag = val; } + void GetUnusedArguments(int* argc, char*** argv); + + /** + * Return string containing help. If the argument is specified, only return + * help for that argument. + */ + const char* GetHelp() { return this->Help.c_str(); } + const char* GetHelp(const char* arg); + + /** + * Get / Set the help line length. This length is used when generating the + * help page. Default length is 80. + */ + void SetLineLength(unsigned int); + unsigned int GetLineLength(); + + /** + * Get the executable name (argv0). This is only available when using + * Initialize with argc/argv. + */ + const char* GetArgv0(); + + /** + * Get index of the last argument parsed. This is the last argument that was + * parsed ok in the original argc/argv list. + */ + unsigned int GetLastArgument(); + +protected: + void GenerateHelp(); + + //! This is internal method that registers variable with argument + void AddArgument(const char* argument, ArgumentTypeEnum type, + VariableTypeEnum vtype, void* variable, const char* help); + + bool GetMatchedArguments(std::vector<std::string>* matches, + const std::string& arg); + + //! Populate individual variables + bool PopulateVariable(CommandLineArgumentsCallbackStructure* cs, + const char* value); + + //! Populate individual variables of type ... + void PopulateVariable(bool* variable, const std::string& value); + void PopulateVariable(int* variable, const std::string& value); + void PopulateVariable(double* variable, const std::string& value); + void PopulateVariable(char** variable, const std::string& value); + void PopulateVariable(std::string* variable, const std::string& value); + void PopulateVariable(std::vector<bool>* variable, const std::string& value); + void PopulateVariable(std::vector<int>* variable, const std::string& value); + void PopulateVariable(std::vector<double>* variable, + const std::string& value); + void PopulateVariable(std::vector<char*>* variable, + const std::string& value); + void PopulateVariable(std::vector<std::string>* variable, + const std::string& value); + + typedef CommandLineArgumentsInternal Internal; + Internal* Internals; + std::string Help; + + unsigned int LineLength; + + bool StoreUnusedArgumentsFlag; +}; + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/Configure.h.in b/Source/kwsys/Configure.h.in new file mode 100644 index 0000000..5323c57 --- /dev/null +++ b/Source/kwsys/Configure.h.in @@ -0,0 +1,89 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Configure_h +#define @KWSYS_NAMESPACE@_Configure_h + +/* If we are building a kwsys .c or .cxx file, let it use the kwsys + namespace. When not building a kwsys source file these macros are + temporarily defined inside the headers that use them. */ +#if defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif + +/* Disable some warnings inside kwsys source files. */ +#if defined(KWSYS_NAMESPACE) +# if defined(__BORLANDC__) +# pragma warn - 8027 /* function not inlined. */ +# endif +# if defined(__INTEL_COMPILER) +# pragma warning(disable : 1572) /* floating-point equality test */ +# endif +# if defined(__sgi) && !defined(__GNUC__) +# pragma set woff 3970 /* pointer to int conversion */ +# pragma set woff 3968 /* 64 bit conversion */ +# endif +#endif + +/* Whether kwsys namespace is "kwsys". */ +#define @KWSYS_NAMESPACE@_NAME_IS_KWSYS @KWSYS_NAME_IS_KWSYS@ + +/* Setup the export macro. */ +#if @KWSYS_BUILD_SHARED@ +# if defined(_WIN32) || defined(__CYGWIN__) +# if defined(@KWSYS_NAMESPACE@_EXPORTS) +# define @KWSYS_NAMESPACE@_EXPORT __declspec(dllexport) +# else +# define @KWSYS_NAMESPACE@_EXPORT __declspec(dllimport) +# endif +# elif __GNUC__ >= 4 +# define @KWSYS_NAMESPACE@_EXPORT __attribute__((visibility("default"))) +# else +# define @KWSYS_NAMESPACE@_EXPORT +# endif +#else +# define @KWSYS_NAMESPACE@_EXPORT +#endif + +/* Enable warnings that are off by default but are useful. */ +#if !defined(@KWSYS_NAMESPACE@_NO_WARNING_ENABLE) +# if defined(_MSC_VER) +# pragma warning(default : 4263) /* no override, call convention differs \ + */ +# endif +#endif + +/* Disable warnings that are on by default but occur in valid code. */ +#if !defined(@KWSYS_NAMESPACE@_NO_WARNING_DISABLE) +# if defined(_MSC_VER) +# pragma warning(disable : 4097) /* typedef is synonym for class */ +# pragma warning(disable : 4127) /* conditional expression is constant */ +# pragma warning(disable : 4244) /* possible loss in conversion */ +# pragma warning(disable : 4251) /* missing DLL-interface */ +# pragma warning(disable : 4305) /* truncation from type1 to type2 */ +# pragma warning(disable : 4309) /* truncation of constant value */ +# pragma warning(disable : 4514) /* unreferenced inline function */ +# pragma warning(disable : 4706) /* assignment in conditional expression \ + */ +# pragma warning(disable : 4710) /* function not inlined */ +# pragma warning(disable : 4786) /* identifier truncated in debug info */ +# endif +# if defined(__BORLANDC__) && !defined(__cplusplus) +/* Code has no effect; raised by winnt.h in C (not C++) when ignoring an + unused parameter using "(param)" syntax (i.e. no cast to void). */ +# pragma warn - 8019 +# endif +#endif + +/* MSVC 6.0 in release mode will warn about code it produces with its + optimizer. Disable the warnings specifically for this + configuration. Real warnings will be revealed by a debug build or + by other compilers. */ +#if !defined(@KWSYS_NAMESPACE@_NO_WARNING_DISABLE_BOGUS) +# if defined(_MSC_VER) && (_MSC_VER < 1300) && defined(NDEBUG) +# pragma warning(disable : 4701) /* Variable may be used uninitialized. */ +# pragma warning(disable : 4702) /* Unreachable code. */ +# endif +#endif + +#endif diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in new file mode 100644 index 0000000..29a2dd1 --- /dev/null +++ b/Source/kwsys/Configure.hxx.in @@ -0,0 +1,65 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Configure_hxx +#define @KWSYS_NAMESPACE@_Configure_hxx + +/* Include C configuration. */ +#include <@KWSYS_NAMESPACE@/Configure.h> + +/* Whether wstring is available. */ +#define @KWSYS_NAMESPACE@_STL_HAS_WSTRING @KWSYS_STL_HAS_WSTRING@ +/* Whether <ext/stdio_filebuf.h> is available. */ +#define @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H \ + @KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@ +/* Whether the translation map is available or not. */ +#define @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP \ + @KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@ + +#if defined(__SUNPRO_CC) && __SUNPRO_CC > 0x5130 && defined(__has_attribute) +# define @KWSYS_NAMESPACE@__has_cpp_attribute(x) __has_attribute(x) +#elif defined(__has_cpp_attribute) +# define @KWSYS_NAMESPACE@__has_cpp_attribute(x) __has_cpp_attribute(x) +#else +# define @KWSYS_NAMESPACE@__has_cpp_attribute(x) 0 +#endif + +#if __cplusplus >= 201103L +# define @KWSYS_NAMESPACE@_NULLPTR nullptr +#else +# define @KWSYS_NAMESPACE@_NULLPTR 0 +#endif + +#ifndef @KWSYS_NAMESPACE@_FALLTHROUGH +# if __cplusplus >= 201703L && \ + @KWSYS_NAMESPACE@__has_cpp_attribute(fallthrough) +# define @KWSYS_NAMESPACE@_FALLTHROUGH [[fallthrough]] +# elif __cplusplus >= 201103L && \ + @KWSYS_NAMESPACE@__has_cpp_attribute(gnu::fallthrough) +# define @KWSYS_NAMESPACE@_FALLTHROUGH [[gnu::fallthrough]] +# elif __cplusplus >= 201103L && \ + @KWSYS_NAMESPACE@__has_cpp_attribute(clang::fallthrough) +# define @KWSYS_NAMESPACE@_FALLTHROUGH [[clang::fallthrough]] +# endif +#endif +#ifndef @KWSYS_NAMESPACE@_FALLTHROUGH +# define @KWSYS_NAMESPACE@_FALLTHROUGH static_cast<void>(0) +#endif + +#undef @KWSYS_NAMESPACE@__has_cpp_attribute + +/* If building a C++ file in kwsys itself, give the source file + access to the macros without a configured namespace. */ +#if defined(KWSYS_NAMESPACE) +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsys @KWSYS_NAMESPACE@ +# endif +# define KWSYS_NAME_IS_KWSYS @KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define KWSYS_STL_HAS_WSTRING @KWSYS_NAMESPACE@_STL_HAS_WSTRING +# define KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H \ + @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H +# define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH +# define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP \ + @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP +#endif + +#endif diff --git a/Source/kwsys/ConsoleBuf.hxx.in b/Source/kwsys/ConsoleBuf.hxx.in new file mode 100644 index 0000000..49dbdf7 --- /dev/null +++ b/Source/kwsys/ConsoleBuf.hxx.in @@ -0,0 +1,398 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_ConsoleBuf_hxx +#define @KWSYS_NAMESPACE@_ConsoleBuf_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <@KWSYS_NAMESPACE@/Encoding.hxx> + +#include <cstring> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <streambuf> +#include <string> + +#if defined(_WIN32) +# include <windows.h> +# if __cplusplus >= 201103L +# include <system_error> +# endif +#endif + +namespace @KWSYS_NAMESPACE@ { +#if defined(_WIN32) + +template <class CharT, class Traits = std::char_traits<CharT> > +class BasicConsoleBuf : public std::basic_streambuf<CharT, Traits> +{ +public: + typedef typename Traits::int_type int_type; + typedef typename Traits::char_type char_type; + + class Manager + { + public: + Manager(std::basic_ios<CharT, Traits>& ios, const bool err = false) + : m_consolebuf(0) + { + m_ios = &ios; + try { + m_consolebuf = new BasicConsoleBuf<CharT, Traits>(err); + m_streambuf = m_ios->rdbuf(m_consolebuf); + } catch (const std::runtime_error& ex) { + std::cerr << "Failed to create ConsoleBuf!" << std::endl + << ex.what() << std::endl; + }; + } + + BasicConsoleBuf<CharT, Traits>* GetConsoleBuf() { return m_consolebuf; } + + void SetUTF8Pipes() + { + if (m_consolebuf) { + m_consolebuf->input_pipe_codepage = CP_UTF8; + m_consolebuf->output_pipe_codepage = CP_UTF8; + m_consolebuf->activateCodepageChange(); + } + } + + ~Manager() + { + if (m_consolebuf) { + delete m_consolebuf; + m_ios->rdbuf(m_streambuf); + } + } + + private: + std::basic_ios<CharT, Traits>* m_ios; + std::basic_streambuf<CharT, Traits>* m_streambuf; + BasicConsoleBuf<CharT, Traits>* m_consolebuf; + }; + + BasicConsoleBuf(const bool err = false) + : flush_on_newline(true) + , input_pipe_codepage(0) + , output_pipe_codepage(0) + , input_file_codepage(CP_UTF8) + , output_file_codepage(CP_UTF8) + , m_consolesCodepage(0) + { + m_hInput = ::GetStdHandle(STD_INPUT_HANDLE); + checkHandle(true, "STD_INPUT_HANDLE"); + if (!setActiveInputCodepage()) { + throw std::runtime_error("setActiveInputCodepage failed!"); + } + m_hOutput = err ? ::GetStdHandle(STD_ERROR_HANDLE) + : ::GetStdHandle(STD_OUTPUT_HANDLE); + checkHandle(false, err ? "STD_ERROR_HANDLE" : "STD_OUTPUT_HANDLE"); + if (!setActiveOutputCodepage()) { + throw std::runtime_error("setActiveOutputCodepage failed!"); + } + _setg(); + _setp(); + } + + ~BasicConsoleBuf() throw() { sync(); } + + bool activateCodepageChange() + { + return setActiveInputCodepage() && setActiveOutputCodepage(); + } + +protected: + virtual int sync() + { + bool success = true; + if (m_hInput && m_isConsoleInput && + ::FlushConsoleInputBuffer(m_hInput) == 0) { + success = false; + } + if (m_hOutput && !m_obuffer.empty()) { + const std::wstring wbuffer = getBuffer(m_obuffer); + if (m_isConsoleOutput) { + DWORD charsWritten; + success = + ::WriteConsoleW(m_hOutput, wbuffer.c_str(), (DWORD)wbuffer.size(), + &charsWritten, nullptr) == 0 + ? false + : true; + } else { + DWORD bytesWritten; + std::string buffer; + success = encodeOutputBuffer(wbuffer, buffer); + if (success) { + success = + ::WriteFile(m_hOutput, buffer.c_str(), (DWORD)buffer.size(), + &bytesWritten, nullptr) == 0 + ? false + : true; + } + } + } + m_ibuffer.clear(); + m_obuffer.clear(); + _setg(); + _setp(); + return success ? 0 : -1; + } + + virtual int_type underflow() + { + if (this->gptr() >= this->egptr()) { + if (!m_hInput) { + _setg(true); + return Traits::eof(); + } + if (m_isConsoleInput) { + // ReadConsole doesn't tell if there's more input available + // don't support reading more characters than this + wchar_t wbuffer[8192]; + DWORD charsRead; + if (ReadConsoleW(m_hInput, wbuffer, + (sizeof(wbuffer) / sizeof(wbuffer[0])), &charsRead, + nullptr) == 0 || + charsRead == 0) { + _setg(true); + return Traits::eof(); + } + setBuffer(std::wstring(wbuffer, charsRead), m_ibuffer); + } else { + std::wstring wbuffer; + std::string strbuffer; + DWORD bytesRead; + LARGE_INTEGER size; + if (GetFileSizeEx(m_hInput, &size) == 0) { + _setg(true); + return Traits::eof(); + } + char* buffer = new char[size.LowPart]; + while (ReadFile(m_hInput, buffer, size.LowPart, &bytesRead, nullptr) == + 0) { + if (GetLastError() == ERROR_MORE_DATA) { + strbuffer += std::string(buffer, bytesRead); + continue; + } + _setg(true); + delete[] buffer; + return Traits::eof(); + } + if (bytesRead > 0) { + strbuffer += std::string(buffer, bytesRead); + } + delete[] buffer; + if (!decodeInputBuffer(strbuffer, wbuffer)) { + _setg(true); + return Traits::eof(); + } + setBuffer(wbuffer, m_ibuffer); + } + _setg(); + } + return Traits::to_int_type(*this->gptr()); + } + + virtual int_type overflow(int_type ch = Traits::eof()) + { + if (!Traits::eq_int_type(ch, Traits::eof())) { + char_type chr = Traits::to_char_type(ch); + m_obuffer += chr; + if ((flush_on_newline && Traits::eq(chr, '\n')) || + Traits::eq_int_type(ch, 0x00)) { + sync(); + } + return ch; + } + sync(); + return Traits::eof(); + } + +public: + bool flush_on_newline; + UINT input_pipe_codepage; + UINT output_pipe_codepage; + UINT input_file_codepage; + UINT output_file_codepage; + +private: + HANDLE m_hInput; + HANDLE m_hOutput; + std::basic_string<char_type> m_ibuffer; + std::basic_string<char_type> m_obuffer; + bool m_isConsoleInput; + bool m_isConsoleOutput; + UINT m_activeInputCodepage; + UINT m_activeOutputCodepage; + UINT m_consolesCodepage; + void checkHandle(bool input, std::string handleName) + { + if ((input && m_hInput == INVALID_HANDLE_VALUE) || + (!input && m_hOutput == INVALID_HANDLE_VALUE)) { + std::string errmsg = + "GetStdHandle(" + handleName + ") returned INVALID_HANDLE_VALUE"; +# if __cplusplus >= 201103L + throw std::system_error(::GetLastError(), std::system_category(), + errmsg); +# else + throw std::runtime_error(errmsg); +# endif + } + } + UINT getConsolesCodepage() + { + if (!m_consolesCodepage) { + m_consolesCodepage = GetConsoleCP(); + if (!m_consolesCodepage) { + m_consolesCodepage = GetACP(); + } + } + return m_consolesCodepage; + } + bool setActiveInputCodepage() + { + m_isConsoleInput = false; + switch (GetFileType(m_hInput)) { + case FILE_TYPE_DISK: + m_activeInputCodepage = input_file_codepage; + break; + case FILE_TYPE_CHAR: + // Check for actual console. + DWORD consoleMode; + m_isConsoleInput = + GetConsoleMode(m_hInput, &consoleMode) == 0 ? false : true; + if (m_isConsoleInput) { + break; + } + @KWSYS_NAMESPACE@_FALLTHROUGH; + case FILE_TYPE_PIPE: + m_activeInputCodepage = input_pipe_codepage; + break; + default: + return false; + } + if (!m_isConsoleInput && m_activeInputCodepage == 0) { + m_activeInputCodepage = getConsolesCodepage(); + } + return true; + } + bool setActiveOutputCodepage() + { + m_isConsoleOutput = false; + switch (GetFileType(m_hOutput)) { + case FILE_TYPE_DISK: + m_activeOutputCodepage = output_file_codepage; + break; + case FILE_TYPE_CHAR: + // Check for actual console. + DWORD consoleMode; + m_isConsoleOutput = + GetConsoleMode(m_hOutput, &consoleMode) == 0 ? false : true; + if (m_isConsoleOutput) { + break; + } + @KWSYS_NAMESPACE@_FALLTHROUGH; + case FILE_TYPE_PIPE: + m_activeOutputCodepage = output_pipe_codepage; + break; + default: + return false; + } + if (!m_isConsoleOutput && m_activeOutputCodepage == 0) { + m_activeOutputCodepage = getConsolesCodepage(); + } + return true; + } + void _setg(bool empty = false) + { + if (!empty) { + this->setg((char_type*)m_ibuffer.data(), (char_type*)m_ibuffer.data(), + (char_type*)m_ibuffer.data() + m_ibuffer.size()); + } else { + this->setg((char_type*)m_ibuffer.data(), + (char_type*)m_ibuffer.data() + m_ibuffer.size(), + (char_type*)m_ibuffer.data() + m_ibuffer.size()); + } + } + void _setp() + { + this->setp((char_type*)m_obuffer.data(), + (char_type*)m_obuffer.data() + m_obuffer.size()); + } + bool encodeOutputBuffer(const std::wstring wbuffer, std::string& buffer) + { + if (wbuffer.size() == 0) { + buffer = std::string(); + return true; + } + const int length = + WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), + (int)wbuffer.size(), nullptr, 0, nullptr, nullptr); + char* buf = new char[length]; + const bool success = + WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), + (int)wbuffer.size(), buf, length, nullptr, + nullptr) > 0 + ? true + : false; + buffer = std::string(buf, length); + delete[] buf; + return success; + } + bool decodeInputBuffer(const std::string buffer, std::wstring& wbuffer) + { + size_t length = buffer.length(); + if (length == 0) { + wbuffer = std::wstring(); + return true; + } + int actualCodepage = m_activeInputCodepage; + const char BOM_UTF8[] = { char(0xEF), char(0xBB), char(0xBF) }; + const char* data = buffer.data(); + const size_t BOMsize = sizeof(BOM_UTF8); + if (length >= BOMsize && std::memcmp(data, BOM_UTF8, BOMsize) == 0) { + // PowerShell uses UTF-8 with BOM for pipes + actualCodepage = CP_UTF8; + data += BOMsize; + length -= BOMsize; + } + const size_t wlength = static_cast<size_t>(MultiByteToWideChar( + actualCodepage, 0, data, static_cast<int>(length), nullptr, 0)); + wchar_t* wbuf = new wchar_t[wlength]; + const bool success = + MultiByteToWideChar(actualCodepage, 0, data, static_cast<int>(length), + wbuf, static_cast<int>(wlength)) > 0 + ? true + : false; + wbuffer = std::wstring(wbuf, wlength); + delete[] wbuf; + return success; + } + std::wstring getBuffer(const std::basic_string<char> buffer) + { + return Encoding::ToWide(buffer); + } + std::wstring getBuffer(const std::basic_string<wchar_t> buffer) + { + return buffer; + } + void setBuffer(const std::wstring wbuffer, std::basic_string<char>& target) + { + target = Encoding::ToNarrow(wbuffer); + } + void setBuffer(const std::wstring wbuffer, + std::basic_string<wchar_t>& target) + { + target = wbuffer; + } + +}; // BasicConsoleBuf class + +typedef BasicConsoleBuf<char> ConsoleBuf; +typedef BasicConsoleBuf<wchar_t> WConsoleBuf; + +#endif +} // KWSYS_NAMESPACE + +#endif diff --git a/Source/kwsys/Copyright.txt b/Source/kwsys/Copyright.txt new file mode 100644 index 0000000..33d7fb4 --- /dev/null +++ b/Source/kwsys/Copyright.txt @@ -0,0 +1,38 @@ +KWSys - Kitware System Library +Copyright 2000-2016 Kitware, Inc. and Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Kitware, Inc. nor the names of Contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 COPYRIGHT +HOLDER OR CONTRIBUTORS 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. + +------------------------------------------------------------------------------ + +The following individuals and institutions are among the Contributors: + +* Insight Software Consortium <insightsoftwareconsortium.org> + +See version control history for details of individual contributions. diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx new file mode 100644 index 0000000..1a772b4 --- /dev/null +++ b/Source/kwsys/Directory.cxx @@ -0,0 +1,236 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Directory.hxx) + +#include KWSYS_HEADER(Configure.hxx) + +#include KWSYS_HEADER(Encoding.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Configure.hxx.in" +# include "Directory.hxx.in" +# include "Encoding.hxx.in" +#endif + +#include <string> +#include <vector> + +namespace KWSYS_NAMESPACE { + +class DirectoryInternals +{ +public: + // Array of Files + std::vector<std::string> Files; + + // Path to Open'ed directory + std::string Path; +}; + +Directory::Directory() +{ + this->Internal = new DirectoryInternals; +} + +Directory::~Directory() +{ + delete this->Internal; +} + +unsigned long Directory::GetNumberOfFiles() const +{ + return static_cast<unsigned long>(this->Internal->Files.size()); +} + +const char* Directory::GetFile(unsigned long dindex) const +{ + if (dindex >= this->Internal->Files.size()) { + return nullptr; + } + return this->Internal->Files[dindex].c_str(); +} + +const char* Directory::GetPath() const +{ + return this->Internal->Path.c_str(); +} + +void Directory::Clear() +{ + this->Internal->Path.resize(0); + this->Internal->Files.clear(); +} + +} // namespace KWSYS_NAMESPACE + +// First Windows platforms + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> + +# include <ctype.h> +# include <fcntl.h> +# include <io.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# include <sys/stat.h> +# include <sys/types.h> + +// Wide function names can vary depending on compiler: +# ifdef __BORLANDC__ +# define _wfindfirst_func __wfindfirst +# define _wfindnext_func __wfindnext +# else +# define _wfindfirst_func _wfindfirst +# define _wfindnext_func _wfindnext +# endif + +namespace KWSYS_NAMESPACE { + +bool Directory::Load(const std::string& name) +{ + this->Clear(); +# if (defined(_MSC_VER) && _MSC_VER < 1300) || defined(__BORLANDC__) + // Older Visual C++ and Embarcadero compilers. + long srchHandle; +# else // Newer Visual C++ + intptr_t srchHandle; +# endif + char* buf; + size_t n = name.size(); + if (name.back() == '/' || name.back() == '\\') { + buf = new char[n + 1 + 1]; + sprintf(buf, "%s*", name.c_str()); + } else { + // Make sure the slashes in the wildcard suffix are consistent with the + // rest of the path + buf = new char[n + 2 + 1]; + if (name.find('\\') != std::string::npos) { + sprintf(buf, "%s\\*", name.c_str()); + } else { + sprintf(buf, "%s/*", name.c_str()); + } + } + struct _wfinddata_t data; // data of current file + + // Now put them into the file array + srchHandle = _wfindfirst_func( + (wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data); + delete[] buf; + + if (srchHandle == -1) { + return 0; + } + + // Loop through names + do { + this->Internal->Files.push_back(Encoding::ToNarrow(data.name)); + } while (_wfindnext_func(srchHandle, &data) != -1); + this->Internal->Path = name; + return _findclose(srchHandle) != -1; +} + +unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name) +{ +# if (defined(_MSC_VER) && _MSC_VER < 1300) || defined(__BORLANDC__) + // Older Visual C++ and Embarcadero compilers. + long srchHandle; +# else // Newer Visual C++ + intptr_t srchHandle; +# endif + char* buf; + size_t n = name.size(); + if (name.back() == '/') { + buf = new char[n + 1 + 1]; + sprintf(buf, "%s*", name.c_str()); + } else { + buf = new char[n + 2 + 1]; + sprintf(buf, "%s/*", name.c_str()); + } + struct _wfinddata_t data; // data of current file + + // Now put them into the file array + srchHandle = + _wfindfirst_func((wchar_t*)Encoding::ToWide(buf).c_str(), &data); + delete[] buf; + + if (srchHandle == -1) { + return 0; + } + + // Loop through names + unsigned long count = 0; + do { + count++; + } while (_wfindnext_func(srchHandle, &data) != -1); + _findclose(srchHandle); + return count; +} + +} // namespace KWSYS_NAMESPACE + +#else + +// Now the POSIX style directory access + +# include <sys/types.h> + +# include <dirent.h> + +// PGI with glibc has trouble with dirent and large file support: +// http://www.pgroup.com/userforum/viewtopic.php? +// p=1992&sid=f16167f51964f1a68fe5041b8eb213b6 +// Work around the problem by mapping dirent the same way as readdir. +# if defined(__PGI) && defined(__GLIBC__) +# define kwsys_dirent_readdir dirent +# define kwsys_dirent_readdir64 dirent64 +# define kwsys_dirent kwsys_dirent_lookup(readdir) +# define kwsys_dirent_lookup(x) kwsys_dirent_lookup_delay(x) +# define kwsys_dirent_lookup_delay(x) kwsys_dirent_##x +# else +# define kwsys_dirent dirent +# endif + +namespace KWSYS_NAMESPACE { + +bool Directory::Load(const std::string& name) +{ + this->Clear(); + + DIR* dir = opendir(name.c_str()); + + if (!dir) { + return false; + } + + for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) { + this->Internal->Files.emplace_back(d->d_name); + } + this->Internal->Path = name; + closedir(dir); + return true; +} + +unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name) +{ + DIR* dir = opendir(name.c_str()); + + if (!dir) { + return 0; + } + + unsigned long count = 0; + for (kwsys_dirent* d = readdir(dir); d; d = readdir(dir)) { + count++; + } + closedir(dir); + return count; +} + +} // namespace KWSYS_NAMESPACE + +#endif diff --git a/Source/kwsys/Directory.hxx.in b/Source/kwsys/Directory.hxx.in new file mode 100644 index 0000000..ad8c51b --- /dev/null +++ b/Source/kwsys/Directory.hxx.in @@ -0,0 +1,72 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Directory_hxx +#define @KWSYS_NAMESPACE@_Directory_hxx + +#include <@KWSYS_NAMESPACE@/Configure.h> + +#include <string> + +namespace @KWSYS_NAMESPACE@ { + +class DirectoryInternals; + +/** \class Directory + * \brief Portable directory/filename traversal. + * + * Directory provides a portable way of finding the names of the files + * in a system directory. + * + * Directory currently works with Windows and Unix operating systems. + */ +class @KWSYS_NAMESPACE@_EXPORT Directory +{ +public: + Directory(); + ~Directory(); + + /** + * Load the specified directory and load the names of the files + * in that directory. 0 is returned if the directory can not be + * opened, 1 if it is opened. + */ + bool Load(const std::string&); + + /** + * Return the number of files in the current directory. + */ + unsigned long GetNumberOfFiles() const; + + /** + * Return the number of files in the specified directory. + * A higher performance static method. + */ + static unsigned long GetNumberOfFilesInDirectory(const std::string&); + + /** + * Return the file at the given index, the indexing is 0 based + */ + const char* GetFile(unsigned long) const; + + /** + * Return the path to Open'ed directory + */ + const char* GetPath() const; + + /** + * Clear the internal structure. Used internally at beginning of Load(...) + * to clear the cache. + */ + void Clear(); + +private: + // Private implementation details. + DirectoryInternals* Internal; + + Directory(const Directory&); // Not implemented. + void operator=(const Directory&); // Not implemented. +}; // End Class: Directory + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx new file mode 100644 index 0000000..a4b8641 --- /dev/null +++ b/Source/kwsys/DynamicLoader.cxx @@ -0,0 +1,495 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#if defined(_WIN32) +# define NOMINMAX // hide min,max to not conflict with <limits> +#endif + +#include "kwsysPrivate.h" +#include KWSYS_HEADER(DynamicLoader.hxx) + +#include KWSYS_HEADER(Configure.hxx) +#include KWSYS_HEADER(Encoding.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Configure.hxx.in" +# include "DynamicLoader.hxx.in" +#endif + +// This file actually contains several different implementations: +// * NOOP for environments without dynamic libs +// * HP machines which uses shl_load +// * Mac OS X 10.2.x and earlier which uses NSLinkModule +// * Windows which uses LoadLibrary +// * BeOS / Haiku +// * FreeMiNT for Atari +// * Default implementation for *NIX systems (including Mac OS X 10.3 and +// later) which use dlopen +// +// Each part of the ifdef contains a complete implementation for +// the static methods of DynamicLoader. + +#define CHECK_OPEN_FLAGS(var, supported, ret) \ + do { \ + /* Check for unknown flags. */ \ + if ((var & AllOpenFlags) != var) { \ + return ret; \ + } \ + \ + /* Check for unsupported flags. */ \ + if ((var & (supported)) != var) { \ + return ret; \ + } \ + } while (0) + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname) +{ + return DynamicLoader::OpenLibrary(libname, 0); +} +} + +#if !KWSYS_SUPPORTS_SHARED_LIBS +// Implementation for environments without dynamic libs +# include <string.h> // for strerror() + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + return 0; +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + if (!lib) { + return 0; + } + + return 1; +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + return 0; +} + +const char* DynamicLoader::LastError() +{ + return "General error"; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(__hpux) +// Implementation for HPUX machines +# include <dl.h> +# include <errno.h> + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, 0, 0); + + return shl_load(libname.c_str(), BIND_DEFERRED | DYNAMIC_PATH, 0L); +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + if (!lib) { + return 0; + } + return !shl_unload(lib); +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + void* addr; + int status; + + /* TYPE_PROCEDURE Look for a function or procedure. (This used to be default) + * TYPE_DATA Look for a symbol in the data segment (for example, + * variables). + * TYPE_UNDEFINED Look for any symbol. + */ + status = shl_findsym(&lib, sym.c_str(), TYPE_UNDEFINED, &addr); + void* result = (status < 0) ? (void*)0 : addr; + + // Hack to cast pointer-to-data to pointer-to-function. + return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result); +} + +const char* DynamicLoader::LastError() +{ + // TODO: Need implementation with errno/strerror + /* If successful, shl_findsym returns an integer (int) value zero. If + * shl_findsym cannot find sym, it returns -1 and sets errno to zero. + * If any other errors occur, shl_findsym returns -1 and sets errno to one + * of these values (defined in <errno.h>): + * ENOEXEC + * A format error was detected in the specified library. + * ENOSYM + * A symbol on which sym depends could not be found. + * EINVAL + * The specified handle is invalid. + */ + + if (errno == ENOEXEC || errno == ENOSYM || errno == EINVAL) { + return strerror(errno); + } + // else + return 0; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1030) +// Implementation for Mac OS X 10.2.x and earlier +# include <mach-o/dyld.h> +# include <string.h> // for strlen + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, 0, 0); + + NSObjectFileImageReturnCode rc; + NSObjectFileImage image = 0; + + rc = NSCreateObjectFileImageFromFile(libname.c_str(), &image); + // rc == NSObjectFileImageInappropriateFile when trying to load a dylib file + if (rc != NSObjectFileImageSuccess) { + return 0; + } + NSModule handle = NSLinkModule(image, libname.c_str(), + NSLINKMODULE_OPTION_BINDNOW | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(image); + return handle; +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + // NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED + // With this option the memory for the module is not deallocated + // allowing pointers into the module to still be valid. + // You should use this option instead if your code experience some problems + // reported against Panther 10.3.9 (fixed in Tiger 10.4.2 and up) + bool success = NSUnLinkModule(lib, NSUNLINKMODULE_OPTION_NONE); + return success; +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + void* result = 0; + // Need to prepend symbols with '_' on Apple-gcc compilers + std::string rsym = '_' + sym; + + NSSymbol symbol = NSLookupSymbolInModule(lib, rsym.c_str()); + if (symbol) { + result = NSAddressOfSymbol(symbol); + } + + // Hack to cast pointer-to-data to pointer-to-function. + return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result); +} + +const char* DynamicLoader::LastError() +{ + return 0; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(_WIN32) && !defined(__CYGWIN__) +// Implementation for Windows win32 code but not cygwin +# include <windows.h> + +# include <stdio.h> + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, nullptr); + + DWORD llFlags = 0; + if (flags & SearchBesideLibrary) { + llFlags |= LOAD_WITH_ALTERED_SEARCH_PATH; + } + + return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(), + nullptr, llFlags); +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + return (int)FreeLibrary(lib); +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + // TODO: The calling convention affects the name of the symbol. We + // should have a tool to help get the symbol with the desired + // calling convention. Currently we assume cdecl. + // + // Borland: + // __cdecl = "_func" (default) + // __fastcall = "@_func" + // __stdcall = "func" + // + // Watcom: + // __cdecl = "_func" + // __fastcall = "@_func@X" + // __stdcall = "_func@X" + // __watcall = "func_" (default) + // + // MSVC: + // __cdecl = "func" (default) + // __fastcall = "@_func@X" + // __stdcall = "_func@X" + // + // Note that the "@X" part of the name above is the total size (in + // bytes) of the arguments on the stack. + void* result; +# if defined(__BORLANDC__) || defined(__WATCOMC__) + // Need to prepend symbols with '_' + std::string ssym = '_' + sym; + const char* rsym = ssym.c_str(); +# else + const char* rsym = sym.c_str(); +# endif + result = (void*)GetProcAddress(lib, rsym); +// Hack to cast pointer-to-data to pointer-to-function. +# ifdef __WATCOMC__ + return *(DynamicLoader::SymbolPointer*)(&result); +# else + return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result); +# endif +} + +# define DYNLOAD_ERROR_BUFFER_SIZE 1024 + +const char* DynamicLoader::LastError() +{ + wchar_t lpMsgBuf[DYNLOAD_ERROR_BUFFER_SIZE + 1]; + + DWORD error = GetLastError(); + DWORD length = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, nullptr); + + static char str[DYNLOAD_ERROR_BUFFER_SIZE + 1]; + + if (length < 1) { + /* FormatMessage failed. Use a default message. */ + _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE, + "DynamicLoader encountered error 0x%X. " + "FormatMessage failed with error 0x%X", + error, GetLastError()); + return str; + } + + if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, str, + DYNLOAD_ERROR_BUFFER_SIZE, nullptr, nullptr)) { + /* WideCharToMultiByte failed. Use a default message. */ + _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE, + "DynamicLoader encountered error 0x%X. " + "WideCharToMultiByte failed with error 0x%X", + error, GetLastError()); + } + + return str; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(__BEOS__) +// Implementation for BeOS / Haiku +# include <string.h> // for strerror() + +# include <be/kernel/image.h> +# include <be/support/Errors.h> + +namespace KWSYS_NAMESPACE { + +static image_id last_dynamic_err = B_OK; + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, 0, 0); + + // image_id's are integers, errors are negative. Add one just in case we + // get a valid image_id of zero (is that even possible?). + image_id rc = load_add_on(libname.c_str()); + if (rc < 0) { + last_dynamic_err = rc; + return 0; + } + + return rc + 1; +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + if (!lib) { + last_dynamic_err = B_BAD_VALUE; + return 0; + } else { + // The function dlclose() returns 0 on success, and non-zero on error. + status_t rc = unload_add_on(lib - 1); + if (rc != B_OK) { + last_dynamic_err = rc; + return 0; + } + } + + return 1; +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + // Hack to cast pointer-to-data to pointer-to-function. + union + { + void* pvoid; + DynamicLoader::SymbolPointer psym; + } result; + + result.psym = nullptr; + + if (!lib) { + last_dynamic_err = B_BAD_VALUE; + } else { + // !!! FIXME: BeOS can do function-only lookups...does this ever + // !!! FIXME: actually _want_ a data symbol lookup, or was this union + // !!! FIXME: a leftover of dlsym()? (s/ANY/TEXT for functions only). + status_t rc = + get_image_symbol(lib - 1, sym.c_str(), B_SYMBOL_TYPE_ANY, &result.pvoid); + if (rc != B_OK) { + last_dynamic_err = rc; + result.psym = nullptr; + } + } + return result.psym; +} + +const char* DynamicLoader::LastError() +{ + const char* retval = strerror(last_dynamic_err); + last_dynamic_err = B_OK; + return retval; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(__MINT__) +// Implementation for FreeMiNT on Atari +# define _GNU_SOURCE /* for program_invocation_name */ +# include <dld.h> +# include <errno.h> +# include <malloc.h> +# include <string.h> + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, 0, nullptr); + + char* name = (char*)calloc(1, libname.size() + 1); + dld_init(program_invocation_name); + strncpy(name, libname.c_str(), libname.size()); + dld_link(libname.c_str()); + return (void*)name; +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + dld_unlink_by_file((char*)lib, 0); + free(lib); + return 0; +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + // Hack to cast pointer-to-data to pointer-to-function. + union + { + void* pvoid; + DynamicLoader::SymbolPointer psym; + } result; + result.pvoid = dld_get_symbol(sym.c_str()); + return result.psym; +} + +const char* DynamicLoader::LastError() +{ + return dld_strerror(dld_errno); +} + +} // namespace KWSYS_NAMESPACE + +#else +// Default implementation for *NIX systems (including Mac OS X 10.3 and +// later) which use dlopen +# include <dlfcn.h> + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname, int flags) +{ + CHECK_OPEN_FLAGS(flags, 0, nullptr); + + return dlopen(libname.c_str(), RTLD_LAZY); +} + +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + if (lib) { + // The function dlclose() returns 0 on success, and non-zero on error. + return !dlclose(lib); + } + // else + return 0; +} + +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + // Hack to cast pointer-to-data to pointer-to-function. + union + { + void* pvoid; + DynamicLoader::SymbolPointer psym; + } result; + result.pvoid = dlsym(lib, sym.c_str()); + return result.psym; +} + +const char* DynamicLoader::LastError() +{ + return dlerror(); +} + +} // namespace KWSYS_NAMESPACE +#endif diff --git a/Source/kwsys/DynamicLoader.hxx.in b/Source/kwsys/DynamicLoader.hxx.in new file mode 100644 index 0000000..539c742 --- /dev/null +++ b/Source/kwsys/DynamicLoader.hxx.in @@ -0,0 +1,106 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_DynamicLoader_hxx +#define @KWSYS_NAMESPACE@_DynamicLoader_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <string> + +#if defined(__hpux) +# include <dl.h> +#elif defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +#elif defined(__APPLE__) +# include <AvailabilityMacros.h> +# if MAC_OS_X_VERSION_MAX_ALLOWED < 1030 +# include <mach-o/dyld.h> +# endif +#elif defined(__BEOS__) +# include <be/kernel/image.h> +#endif + +namespace @KWSYS_NAMESPACE@ { +/** \class DynamicLoader + * \brief Portable loading of dynamic libraries or dll's. + * + * DynamicLoader provides a portable interface to loading dynamic + * libraries or dll's into a process. + * + * Directory currently works with Windows, Apple, HP-UX and Unix (POSIX) + * operating systems + * + * \warning dlopen on *nix system works the following way: + * If filename contains a slash ("/"), then it is interpreted as a (relative + * or absolute) pathname. Otherwise, the dynamic linker searches for the + * library as follows : see ld.so(8) for further details): + * Whereas this distinction does not exist on Win32. Therefore ideally you + * should be doing full path to guarantee to have a consistent way of dealing + * with dynamic loading of shared library. + * + * \warning the Cygwin implementation do not use the Win32 HMODULE. Put extra + * condition so that we can include the correct declaration (POSIX) + */ + +class @KWSYS_NAMESPACE@_EXPORT DynamicLoader +{ +public: +// Ugly stuff for library handles +// They are different on several different OS's +#if defined(__hpux) + typedef shl_t LibraryHandle; +#elif defined(_WIN32) && !defined(__CYGWIN__) + typedef HMODULE LibraryHandle; +#elif defined(__APPLE__) +# if MAC_OS_X_VERSION_MAX_ALLOWED < 1030 + typedef NSModule LibraryHandle; +# else + typedef void* LibraryHandle; +# endif +#elif defined(__BEOS__) + typedef image_id LibraryHandle; +#else // POSIX + typedef void* LibraryHandle; +#endif + + // Return type from DynamicLoader::GetSymbolAddress. + typedef void (*SymbolPointer)(); + + enum OpenFlags + { + // Search for dependent libraries beside the library being loaded. + // + // This is currently only supported on Windows. + SearchBesideLibrary = 0x00000001, + + AllOpenFlags = SearchBesideLibrary + }; + + /** Load a dynamic library into the current process. + * The returned LibraryHandle can be used to access the symbols in the + * library. The optional second argument is a set of flags to use when + * opening the library. If unrecognized or unsupported flags are specified, + * the library is not opened. */ + static LibraryHandle OpenLibrary(const std::string&); + static LibraryHandle OpenLibrary(const std::string&, int); + + /** Attempt to detach a dynamic library from the + * process. A value of true is returned if it is successful. */ + static int CloseLibrary(LibraryHandle); + + /** Find the address of the symbol in the given library. */ + static SymbolPointer GetSymbolAddress(LibraryHandle, const std::string&); + + /** Return the default module prefix for the current platform. */ + static const char* LibPrefix() { return "@KWSYS_DynamicLoader_PREFIX@"; } + + /** Return the default module suffix for the current platform. */ + static const char* LibExtension() { return "@KWSYS_DynamicLoader_SUFFIX@"; } + + /** Return the last error produced from a calls made on this class. */ + static const char* LastError(); +}; // End Class: DynamicLoader + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/Encoding.h.in b/Source/kwsys/Encoding.h.in new file mode 100644 index 0000000..86a2669 --- /dev/null +++ b/Source/kwsys/Encoding.h.in @@ -0,0 +1,69 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Encoding_h +#define @KWSYS_NAMESPACE@_Encoding_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +#include <wchar.h> + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysEncoding kwsys_ns(Encoding) +# define kwsysEncoding_mbstowcs kwsys_ns(Encoding_mbstowcs) +# define kwsysEncoding_DupToWide kwsys_ns(Encoding_DupToWide) +# define kwsysEncoding_wcstombs kwsys_ns(Encoding_wcstombs) +# define kwsysEncoding_DupToNarrow kwsys_ns(Encoding_DupToNarrow) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Convert a narrow string to a wide string. + On Windows, UTF-8 is assumed, and on other platforms, + the current locale is assumed. + */ +kwsysEXPORT size_t kwsysEncoding_mbstowcs(wchar_t* dest, const char* src, + size_t n); + +/* Convert a narrow string to a wide string. + This can return NULL if the conversion fails. */ +kwsysEXPORT wchar_t* kwsysEncoding_DupToWide(const char* src); + +/* Convert a wide string to a narrow string. + On Windows, UTF-8 is assumed, and on other platforms, + the current locale is assumed. */ +kwsysEXPORT size_t kwsysEncoding_wcstombs(char* dest, const wchar_t* src, + size_t n); + +/* Convert a wide string to a narrow string. + This can return NULL if the conversion fails. */ +kwsysEXPORT char* kwsysEncoding_DupToNarrow(const wchar_t* str); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !defined(KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysEncoding +# undef kwsysEncoding_mbstowcs +# undef kwsysEncoding_DupToWide +# undef kwsysEncoding_wcstombs +# undef kwsysEncoding_DupToNarrow +# endif +#endif + +#endif diff --git a/Source/kwsys/Encoding.hxx.in b/Source/kwsys/Encoding.hxx.in new file mode 100644 index 0000000..75a2d4d --- /dev/null +++ b/Source/kwsys/Encoding.hxx.in @@ -0,0 +1,80 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Encoding_hxx +#define @KWSYS_NAMESPACE@_Encoding_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <string> +#include <vector> + +namespace @KWSYS_NAMESPACE@ { +class @KWSYS_NAMESPACE@_EXPORT Encoding +{ +public: + // Container class for argc/argv. + class @KWSYS_NAMESPACE@_EXPORT CommandLineArguments + { + public: + // On Windows, get the program command line arguments + // in this Encoding module's 8 bit encoding. + // On other platforms the given argc/argv is used, and + // to be consistent, should be the argc/argv from main(). + static CommandLineArguments Main(int argc, char const* const* argv); + + // Construct CommandLineArguments with the given + // argc/argv. It is assumed that the string is already + // in the encoding used by this module. + CommandLineArguments(int argc, char const* const* argv); + + // Construct CommandLineArguments with the given + // argc and wide argv. This is useful if wmain() is used. + CommandLineArguments(int argc, wchar_t const* const* argv); + ~CommandLineArguments(); + CommandLineArguments(const CommandLineArguments&); + CommandLineArguments& operator=(const CommandLineArguments&); + + int argc() const; + char const* const* argv() const; + + protected: + std::vector<char*> argv_; + }; + + /** + * Convert between char and wchar_t + */ + +#if @KWSYS_NAMESPACE@_STL_HAS_WSTRING + + // Convert a narrow string to a wide string. + // On Windows, UTF-8 is assumed, and on other platforms, + // the current locale is assumed. + static std::wstring ToWide(const std::string& str); + static std::wstring ToWide(const char* str); + + // Convert a wide string to a narrow string. + // On Windows, UTF-8 is assumed, and on other platforms, + // the current locale is assumed. + static std::string ToNarrow(const std::wstring& str); + static std::string ToNarrow(const wchar_t* str); + +# if defined(_WIN32) + /** + * Convert the path to an extended length path to avoid MAX_PATH length + * limitations on Windows. If the input is a local path the result will be + * prefixed with \\?\; if the input is instead a network path, the result + * will be prefixed with \\?\UNC\. All output will also be converted to + * absolute paths with Windows-style backslashes. + **/ + static std::wstring ToWindowsExtendedPath(std::string const&); + static std::wstring ToWindowsExtendedPath(const char* source); + static std::wstring ToWindowsExtendedPath(std::wstring const& wsource); +# endif + +#endif // @KWSYS_NAMESPACE@_STL_HAS_WSTRING + +}; // class Encoding +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/EncodingC.c b/Source/kwsys/EncodingC.c new file mode 100644 index 0000000..e12236a --- /dev/null +++ b/Source/kwsys/EncodingC.c @@ -0,0 +1,72 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Encoding.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Encoding.h.in" +#endif + +#include <stdlib.h> + +#ifdef _WIN32 +# include <windows.h> +#endif + +size_t kwsysEncoding_mbstowcs(wchar_t* dest, const char* str, size_t n) +{ + if (str == 0) { + return (size_t)-1; + } +#ifdef _WIN32 + return MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str, -1, dest, + (int)n) - + 1; +#else + return mbstowcs(dest, str, n); +#endif +} + +wchar_t* kwsysEncoding_DupToWide(const char* str) +{ + wchar_t* ret = NULL; + size_t length = kwsysEncoding_mbstowcs(NULL, str, 0) + 1; + if (length > 0) { + ret = (wchar_t*)malloc((length) * sizeof(wchar_t)); + if (ret) { + ret[0] = 0; + kwsysEncoding_mbstowcs(ret, str, length); + } + } + return ret; +} + +size_t kwsysEncoding_wcstombs(char* dest, const wchar_t* str, size_t n) +{ + if (str == 0) { + return (size_t)-1; + } +#ifdef _WIN32 + return WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str, -1, dest, + (int)n, NULL, NULL) - + 1; +#else + return wcstombs(dest, str, n); +#endif +} + +char* kwsysEncoding_DupToNarrow(const wchar_t* str) +{ + char* ret = NULL; + size_t length = kwsysEncoding_wcstombs(0, str, 0) + 1; + if (length > 0) { + ret = (char*)malloc(length); + if (ret) { + ret[0] = 0; + kwsysEncoding_wcstombs(ret, str, length); + } + } + return ret; +} diff --git a/Source/kwsys/EncodingCXX.cxx b/Source/kwsys/EncodingCXX.cxx new file mode 100644 index 0000000..5cad934 --- /dev/null +++ b/Source/kwsys/EncodingCXX.cxx @@ -0,0 +1,288 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef __osf__ +# define _OSF_SOURCE +# define _POSIX_C_SOURCE 199506L +# define _XOPEN_SOURCE_EXTENDED +#endif + +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Encoding.hxx) +#include KWSYS_HEADER(Encoding.h) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Encoding.h.in" +# include "Encoding.hxx.in" +#endif + +#include <stdlib.h> +#include <string.h> +#include <vector> + +#ifdef _MSC_VER +# pragma warning(disable : 4786) +#endif + +// Windows API. +#if defined(_WIN32) +# include <windows.h> + +# include <ctype.h> +# include <shellapi.h> +#endif + +namespace KWSYS_NAMESPACE { + +Encoding::CommandLineArguments Encoding::CommandLineArguments::Main( + int argc, char const* const* argv) +{ +#ifdef _WIN32 + (void)argc; + (void)argv; + + int ac; + LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &ac); + + std::vector<std::string> av1(ac); + std::vector<char const*> av2(ac); + for (int i = 0; i < ac; i++) { + av1[i] = ToNarrow(w_av[i]); + av2[i] = av1[i].c_str(); + } + LocalFree(w_av); + return CommandLineArguments(ac, &av2[0]); +#else + return CommandLineArguments(argc, argv); +#endif +} + +Encoding::CommandLineArguments::CommandLineArguments(int ac, + char const* const* av) +{ + this->argv_.resize(ac + 1); + for (int i = 0; i < ac; i++) { + this->argv_[i] = strdup(av[i]); + } + this->argv_[ac] = nullptr; +} + +Encoding::CommandLineArguments::CommandLineArguments(int ac, + wchar_t const* const* av) +{ + this->argv_.resize(ac + 1); + for (int i = 0; i < ac; i++) { + this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]); + } + this->argv_[ac] = nullptr; +} + +Encoding::CommandLineArguments::~CommandLineArguments() +{ + for (size_t i = 0; i < this->argv_.size(); i++) { + free(argv_[i]); + } +} + +Encoding::CommandLineArguments::CommandLineArguments( + const CommandLineArguments& other) +{ + this->argv_.resize(other.argv_.size()); + for (size_t i = 0; i < this->argv_.size(); i++) { + this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr; + } +} + +Encoding::CommandLineArguments& Encoding::CommandLineArguments::operator=( + const CommandLineArguments& other) +{ + if (this != &other) { + size_t i; + for (i = 0; i < this->argv_.size(); i++) { + free(this->argv_[i]); + } + + this->argv_.resize(other.argv_.size()); + for (i = 0; i < this->argv_.size(); i++) { + this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr; + } + } + + return *this; +} + +int Encoding::CommandLineArguments::argc() const +{ + return static_cast<int>(this->argv_.size() - 1); +} + +char const* const* Encoding::CommandLineArguments::argv() const +{ + return &this->argv_[0]; +} + +#if KWSYS_STL_HAS_WSTRING + +std::wstring Encoding::ToWide(const std::string& str) +{ + std::wstring wstr; +# if defined(_WIN32) + const int wlength = + MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), + int(str.size()), nullptr, 0); + if (wlength > 0) { + wchar_t* wdata = new wchar_t[wlength]; + int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), + int(str.size()), wdata, wlength); + if (r > 0) { + wstr = std::wstring(wdata, wlength); + } + delete[] wdata; + } +# else + size_t pos = 0; + size_t nullPos = 0; + do { + if (pos < str.size() && str.at(pos) != '\0') { + wstr += ToWide(str.c_str() + pos); + } + nullPos = str.find('\0', pos); + if (nullPos != std::string::npos) { + pos = nullPos + 1; + wstr += wchar_t('\0'); + } + } while (nullPos != std::string::npos); +# endif + return wstr; +} + +std::string Encoding::ToNarrow(const std::wstring& str) +{ + std::string nstr; +# if defined(_WIN32) + int length = + WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), + int(str.size()), nullptr, 0, nullptr, nullptr); + if (length > 0) { + char* data = new char[length]; + int r = + WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), + int(str.size()), data, length, nullptr, nullptr); + if (r > 0) { + nstr = std::string(data, length); + } + delete[] data; + } +# else + size_t pos = 0; + size_t nullPos = 0; + do { + if (pos < str.size() && str.at(pos) != '\0') { + nstr += ToNarrow(str.c_str() + pos); + } + nullPos = str.find(wchar_t('\0'), pos); + if (nullPos != std::string::npos) { + pos = nullPos + 1; + nstr += '\0'; + } + } while (nullPos != std::string::npos); +# endif + return nstr; +} + +std::wstring Encoding::ToWide(const char* cstr) +{ + std::wstring wstr; + size_t length = kwsysEncoding_mbstowcs(nullptr, cstr, 0) + 1; + if (length > 0) { + std::vector<wchar_t> wchars(length); + if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) { + wstr = &wchars[0]; + } + } + return wstr; +} + +std::string Encoding::ToNarrow(const wchar_t* wcstr) +{ + std::string str; + size_t length = kwsysEncoding_wcstombs(nullptr, wcstr, 0) + 1; + if (length > 0) { + std::vector<char> chars(length); + if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) { + str = &chars[0]; + } + } + return str; +} + +# if defined(_WIN32) +// Convert local paths to UNC style paths +std::wstring Encoding::ToWindowsExtendedPath(std::string const& source) +{ + return ToWindowsExtendedPath(ToWide(source)); +} + +// Convert local paths to UNC style paths +std::wstring Encoding::ToWindowsExtendedPath(const char* source) +{ + return ToWindowsExtendedPath(ToWide(source)); +} + +// Convert local paths to UNC style paths +std::wstring Encoding::ToWindowsExtendedPath(std::wstring const& wsource) +{ + // Resolve any relative paths + DWORD wfull_len; + + /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that + * won't return a large enough buffer size if the input is too small */ + wfull_len = GetFullPathNameW(wsource.c_str(), 0, nullptr, nullptr) + 3; + std::vector<wchar_t> wfull(wfull_len); + GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr); + + /* This should get the correct size without any extra padding from the + * previous size workaround. */ + wfull_len = static_cast<DWORD>(wcslen(&wfull[0])); + + if (wfull_len >= 2 && isalpha(wfull[0]) && + wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + std::wstring(&wfull[0]); + } else if (wfull_len >= 2 && wfull[0] == L'\\' && + wfull[1] == L'\\') { /* Starts with \\ */ + if (wfull_len >= 4 && wfull[2] == L'?' && + wfull[3] == L'\\') { /* Starts with \\?\ */ + if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && + wfull[6] == L'C' && + wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */ + return std::wstring(&wfull[0]); + } else if (wfull_len >= 6 && isalpha(wfull[4]) && + wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */ + return std::wstring(&wfull[0]); + } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]); + } + } else if (wfull_len >= 4 && wfull[2] == L'.' && + wfull[3] == L'\\') { /* Starts with \\.\ a device name */ + if (wfull_len >= 6 && isalpha(wfull[4]) && + wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */ + return L"\\\\?\\" + std::wstring(&wfull[4]); + } else if (wfull_len >= + 5) { /* \\.\Foo\bar\ Device name is left unchanged */ + return std::wstring(&wfull[0]); + } + } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */ + return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]); + } + } + + // If this case has been reached, then the path is invalid. Leave it + // unchanged + return wsource; +} +# endif + +#endif // KWSYS_STL_HAS_WSTRING + +} // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/ExtraTest.cmake.in b/Source/kwsys/ExtraTest.cmake.in new file mode 100644 index 0000000..4cec9e2 --- /dev/null +++ b/Source/kwsys/ExtraTest.cmake.in @@ -0,0 +1 @@ +message("*** This message is generated by message inside a file that is included in DartTestfile.txt ***") diff --git a/Source/kwsys/FStream.cxx b/Source/kwsys/FStream.cxx new file mode 100644 index 0000000..5e4133a --- /dev/null +++ b/Source/kwsys/FStream.cxx @@ -0,0 +1,55 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(FStream.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "FStream.hxx.in" +#endif + +namespace KWSYS_NAMESPACE { +namespace FStream { + +BOM ReadBOM(std::istream& in) +{ + if (!in.good()) { + return BOM_None; + } + unsigned long orig = in.tellg(); + unsigned char bom[4]; + in.read(reinterpret_cast<char*>(bom), 2); + if (!in.good()) { + in.clear(); + in.seekg(orig); + return BOM_None; + } + if (bom[0] == 0xEF && bom[1] == 0xBB) { + in.read(reinterpret_cast<char*>(bom + 2), 1); + if (in.good() && bom[2] == 0xBF) { + return BOM_UTF8; + } + } else if (bom[0] == 0xFE && bom[1] == 0xFF) { + return BOM_UTF16BE; + } else if (bom[0] == 0x00 && bom[1] == 0x00) { + in.read(reinterpret_cast<char*>(bom + 2), 2); + if (in.good() && bom[2] == 0xFE && bom[3] == 0xFF) { + return BOM_UTF32BE; + } + } else if (bom[0] == 0xFF && bom[1] == 0xFE) { + unsigned long p = in.tellg(); + in.read(reinterpret_cast<char*>(bom + 2), 2); + if (in.good() && bom[2] == 0x00 && bom[3] == 0x00) { + return BOM_UTF32LE; + } + in.seekg(p); + return BOM_UTF16LE; + } + in.clear(); + in.seekg(orig); + return BOM_None; +} + +} // FStream namespace +} // KWSYS_NAMESPACE diff --git a/Source/kwsys/FStream.hxx.in b/Source/kwsys/FStream.hxx.in new file mode 100644 index 0000000..b424488 --- /dev/null +++ b/Source/kwsys/FStream.hxx.in @@ -0,0 +1,282 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_FStream_hxx +#define @KWSYS_NAMESPACE@_FStream_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <@KWSYS_NAMESPACE@/Encoding.hxx> + +#include <fstream> +#if defined(_WIN32) +# if !defined(_MSC_VER) && @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H +# include <ext/stdio_filebuf.h> +# endif +#endif + +namespace @KWSYS_NAMESPACE@ { +#if defined(_WIN32) && \ + (defined(_MSC_VER) || @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H) +# if defined(_NOEXCEPT) +# define @KWSYS_NAMESPACE@_FStream_NOEXCEPT _NOEXCEPT +# else +# define @KWSYS_NAMESPACE@_FStream_NOEXCEPT +# endif + +# if defined(_MSC_VER) + +template <typename CharType, typename Traits> +class basic_filebuf : public std::basic_filebuf<CharType, Traits> +{ +# if _MSC_VER >= 1400 +public: + typedef std::basic_filebuf<CharType, Traits> my_base_type; + basic_filebuf* open(char const* s, std::ios_base::openmode mode) + { + const std::wstring wstr = Encoding::ToWindowsExtendedPath(s); + return static_cast<basic_filebuf*>(my_base_type::open(wstr.c_str(), mode)); + } +# endif +}; + +# else + +inline std::wstring getcmode(const std::ios_base::openmode mode) +{ + std::wstring cmode; + bool plus = false; + if (mode & std::ios_base::app) { + cmode += L"a"; + plus = mode & std::ios_base::in ? true : false; + } else if (mode & std::ios_base::trunc || + (mode & std::ios_base::out && (mode & std::ios_base::in) == 0)) { + cmode += L"w"; + plus = mode & std::ios_base::in ? true : false; + } else { + cmode += L"r"; + plus = mode & std::ios_base::out ? true : false; + } + if (plus) { + cmode += L"+"; + } + if (mode & std::ios_base::binary) { + cmode += L"b"; + } else { + cmode += L"t"; + } + return cmode; +}; + +# endif + +template <typename CharType, typename Traits = std::char_traits<CharType> > +class basic_efilebuf +{ +public: +# if defined(_MSC_VER) + typedef basic_filebuf<CharType, Traits> internal_buffer_type; +# else + typedef __gnu_cxx::stdio_filebuf<CharType, Traits> internal_buffer_type; +# endif + + basic_efilebuf() + : file_(0) + { + buf_ = 0; + } + + bool _open(char const* file_name, std::ios_base::openmode mode) + { + if (_is_open() || file_) { + return false; + } +# if defined(_MSC_VER) + const bool success = buf_->open(file_name, mode) != 0; +# else + const std::wstring wstr = Encoding::ToWindowsExtendedPath(file_name); + bool success = false; + std::wstring cmode = getcmode(mode); + file_ = _wfopen(wstr.c_str(), cmode.c_str()); + if (file_) { + if (buf_) { + delete buf_; + } + buf_ = new internal_buffer_type(file_, mode); + success = true; + } +# endif + return success; + } + + bool _is_open() + { + if (!buf_) { + return false; + } + return buf_->is_open(); + } + + bool _is_open() const + { + if (!buf_) { + return false; + } + return buf_->is_open(); + } + + bool _close() + { + bool success = false; + if (buf_) { + success = buf_->close() != 0; +# if !defined(_MSC_VER) + if (file_) { + success = fclose(file_) == 0 ? success : false; + file_ = 0; + } +# endif + } + return success; + } + + static void _set_state(bool success, std::basic_ios<CharType, Traits>* ios, + basic_efilebuf* efilebuf) + { +# if !defined(_MSC_VER) + ios->rdbuf(efilebuf->buf_); +# else + static_cast<void>(efilebuf); +# endif + if (!success) { + ios->setstate(std::ios_base::failbit); + } else { + ios->clear(); + } + } + + ~basic_efilebuf() + { + if (buf_) { + delete buf_; + } + } + +protected: + internal_buffer_type* buf_; + FILE* file_; +}; + +template <typename CharType, typename Traits = std::char_traits<CharType> > +class basic_ifstream + : public std::basic_istream<CharType, Traits> + , public basic_efilebuf<CharType, Traits> +{ +public: + typedef typename basic_efilebuf<CharType, Traits>::internal_buffer_type + internal_buffer_type; + typedef std::basic_istream<CharType, Traits> internal_stream_type; + + basic_ifstream() + : internal_stream_type(new internal_buffer_type()) + { + this->buf_ = + static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); + } + explicit basic_ifstream(char const* file_name, + std::ios_base::openmode mode = std::ios_base::in) + : internal_stream_type(new internal_buffer_type()) + { + this->buf_ = + static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); + open(file_name, mode); + } + + void open(char const* file_name, + std::ios_base::openmode mode = std::ios_base::in) + { + mode = mode | std::ios_base::in; + this->_set_state(this->_open(file_name, mode), this, this); + } + + bool is_open() { return this->_is_open(); } + + void close() { this->_set_state(this->_close(), this, this); } + + using basic_efilebuf<CharType, Traits>::_is_open; + + internal_buffer_type* rdbuf() const { return this->buf_; } + + ~basic_ifstream() @KWSYS_NAMESPACE@_FStream_NOEXCEPT { close(); } +}; + +template <typename CharType, typename Traits = std::char_traits<CharType> > +class basic_ofstream + : public std::basic_ostream<CharType, Traits> + , public basic_efilebuf<CharType, Traits> +{ + using basic_efilebuf<CharType, Traits>::_is_open; + +public: + typedef typename basic_efilebuf<CharType, Traits>::internal_buffer_type + internal_buffer_type; + typedef std::basic_ostream<CharType, Traits> internal_stream_type; + + basic_ofstream() + : internal_stream_type(new internal_buffer_type()) + { + this->buf_ = + static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); + } + explicit basic_ofstream(char const* file_name, + std::ios_base::openmode mode = std::ios_base::out) + : internal_stream_type(new internal_buffer_type()) + { + this->buf_ = + static_cast<internal_buffer_type*>(internal_stream_type::rdbuf()); + open(file_name, mode); + } + void open(char const* file_name, + std::ios_base::openmode mode = std::ios_base::out) + { + mode = mode | std::ios_base::out; + this->_set_state(this->_open(file_name, mode), this, this); + } + + void close() { this->_set_state(this->_close(), this, this); } + + bool is_open() { return this->_is_open(); } + + internal_buffer_type* rdbuf() const { return this->buf_; } + + ~basic_ofstream() @KWSYS_NAMESPACE@_FStream_NOEXCEPT { close(); } +}; + +typedef basic_ifstream<char> ifstream; +typedef basic_ofstream<char> ofstream; + +# undef @KWSYS_NAMESPACE@_FStream_NOEXCEPT +#else +using std::ofstream; +using std::ifstream; +#endif + +namespace FStream { +enum BOM +{ + BOM_None, + BOM_UTF8, + BOM_UTF16BE, + BOM_UTF16LE, + BOM_UTF32BE, + BOM_UTF32LE +}; + +// Read a BOM, if one exists. +// If a BOM exists, the stream is advanced to after the BOM. +// This function requires a seekable stream (but not a relative +// seekable stream). +@KWSYS_NAMESPACE@_EXPORT BOM ReadBOM(std::istream& in); +} +} + +#endif diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx new file mode 100644 index 0000000..658b816 --- /dev/null +++ b/Source/kwsys/Glob.cxx @@ -0,0 +1,447 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Glob.hxx) + +#include KWSYS_HEADER(Configure.hxx) + +#include KWSYS_HEADER(RegularExpression.hxx) +#include KWSYS_HEADER(SystemTools.hxx) +#include KWSYS_HEADER(Directory.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Configure.hxx.in" +# include "Directory.hxx.in" +# include "Glob.hxx.in" +# include "RegularExpression.hxx.in" +# include "SystemTools.hxx.in" +#endif + +#include <algorithm> +#include <string> +#include <vector> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +namespace KWSYS_NAMESPACE { +#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) +// On Windows and Apple, no difference between lower and upper case +# define KWSYS_GLOB_CASE_INDEPENDENT +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +// Handle network paths +# define KWSYS_GLOB_SUPPORT_NETWORK_PATHS +#endif + +class GlobInternals +{ +public: + std::vector<std::string> Files; + std::vector<kwsys::RegularExpression> Expressions; +}; + +Glob::Glob() +{ + this->Internals = new GlobInternals; + this->Recurse = false; + this->Relative = ""; + + this->RecurseThroughSymlinks = true; + // RecurseThroughSymlinks is true by default for backwards compatibility, + // not because it's a good idea... + this->FollowedSymlinkCount = 0; + + // Keep separate variables for directory listing for back compatibility + this->ListDirs = true; + this->RecurseListDirs = false; +} + +Glob::~Glob() +{ + delete this->Internals; +} + +std::vector<std::string>& Glob::GetFiles() +{ + return this->Internals->Files; +} + +std::string Glob::PatternToRegex(const std::string& pattern, + bool require_whole_string, bool preserve_case) +{ + // Incrementally build the regular expression from the pattern. + std::string regex = require_whole_string ? "^" : ""; + std::string::const_iterator pattern_first = pattern.begin(); + std::string::const_iterator pattern_last = pattern.end(); + for (std::string::const_iterator i = pattern_first; i != pattern_last; ++i) { + int c = *i; + if (c == '*') { + // A '*' (not between brackets) matches any string. + // We modify this to not match slashes since the original glob + // pattern documentation was meant for matching file name + // components separated by slashes. + regex += "[^/]*"; + } else if (c == '?') { + // A '?' (not between brackets) matches any single character. + // We modify this to not match slashes since the original glob + // pattern documentation was meant for matching file name + // components separated by slashes. + regex += "[^/]"; + } else if (c == '[') { + // Parse out the bracket expression. It begins just after the + // opening character. + std::string::const_iterator bracket_first = i + 1; + std::string::const_iterator bracket_last = bracket_first; + + // The first character may be complementation '!' or '^'. + if (bracket_last != pattern_last && + (*bracket_last == '!' || *bracket_last == '^')) { + ++bracket_last; + } + + // If the next character is a ']' it is included in the brackets + // because the bracket string may not be empty. + if (bracket_last != pattern_last && *bracket_last == ']') { + ++bracket_last; + } + + // Search for the closing ']'. + while (bracket_last != pattern_last && *bracket_last != ']') { + ++bracket_last; + } + + // Check whether we have a complete bracket string. + if (bracket_last == pattern_last) { + // The bracket string did not end, so it was opened simply by + // a '[' that is supposed to be matched literally. + regex += "\\["; + } else { + // Convert the bracket string to its regex equivalent. + std::string::const_iterator k = bracket_first; + + // Open the regex block. + regex += "["; + + // A regex range complement uses '^' instead of '!'. + if (k != bracket_last && *k == '!') { + regex += "^"; + ++k; + } + + // Convert the remaining characters. + for (; k != bracket_last; ++k) { + // Backslashes must be escaped. + if (*k == '\\') { + regex += "\\"; + } + + // Store this character. + regex += *k; + } + + // Close the regex block. + regex += "]"; + + // Jump to the end of the bracket string. + i = bracket_last; + } + } else { + // A single character matches itself. + int ch = c; + if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9'))) { + // Escape the non-alphanumeric character. + regex += "\\"; + } +#if defined(KWSYS_GLOB_CASE_INDEPENDENT) + else { + // On case-insensitive systems file names are converted to lower + // case before matching. + if (!preserve_case) { + ch = tolower(ch); + } + } +#endif + (void)preserve_case; + // Store the character. + regex.append(1, static_cast<char>(ch)); + } + } + + if (require_whole_string) { + regex += "$"; + } + return regex; +} + +bool Glob::RecurseDirectory(std::string::size_type start, + const std::string& dir, GlobMessages* messages) +{ + kwsys::Directory d; + if (!d.Load(dir)) { + return true; + } + unsigned long cc; + std::string realname; + std::string fname; + for (cc = 0; cc < d.GetNumberOfFiles(); cc++) { + fname = d.GetFile(cc); + if (fname == "." || fname == "..") { + continue; + } + + if (start == 0) { + realname = dir + fname; + } else { + realname = dir + "/" + fname; + } + +#if defined(KWSYS_GLOB_CASE_INDEPENDENT) + // On Windows and Apple, no difference between lower and upper case + fname = kwsys::SystemTools::LowerCase(fname); +#endif + + bool isDir = kwsys::SystemTools::FileIsDirectory(realname); + bool isSymLink = kwsys::SystemTools::FileIsSymlink(realname); + + if (isDir && (!isSymLink || this->RecurseThroughSymlinks)) { + if (isSymLink) { + ++this->FollowedSymlinkCount; + std::string realPathErrorMessage; + std::string canonicalPath( + SystemTools::GetRealPath(dir, &realPathErrorMessage)); + + if (!realPathErrorMessage.empty()) { + if (messages) { + messages->push_back( + Message(Glob::error, + "Canonical path generation from path '" + dir + + "' failed! Reason: '" + realPathErrorMessage + "'")); + } + return false; + } + + if (std::find(this->VisitedSymlinks.begin(), + this->VisitedSymlinks.end(), + canonicalPath) == this->VisitedSymlinks.end()) { + if (this->RecurseListDirs) { + // symlinks are treated as directories + this->AddFile(this->Internals->Files, realname); + } + + this->VisitedSymlinks.push_back(canonicalPath); + if (!this->RecurseDirectory(start + 1, realname, messages)) { + this->VisitedSymlinks.pop_back(); + + return false; + } + this->VisitedSymlinks.pop_back(); + } + // else we have already visited this symlink - prevent cyclic recursion + else if (messages) { + std::string message; + for (std::vector<std::string>::const_iterator pathIt = + std::find(this->VisitedSymlinks.begin(), + this->VisitedSymlinks.end(), canonicalPath); + pathIt != this->VisitedSymlinks.end(); ++pathIt) { + message += *pathIt + "\n"; + } + message += canonicalPath + "/" + fname; + messages->push_back(Message(Glob::cyclicRecursion, message)); + } + } else { + if (this->RecurseListDirs) { + this->AddFile(this->Internals->Files, realname); + } + if (!this->RecurseDirectory(start + 1, realname, messages)) { + return false; + } + } + } else { + if (!this->Internals->Expressions.empty() && + this->Internals->Expressions.back().find(fname)) { + this->AddFile(this->Internals->Files, realname); + } + } + } + + return true; +} + +void Glob::ProcessDirectory(std::string::size_type start, + const std::string& dir, GlobMessages* messages) +{ + // std::cout << "ProcessDirectory: " << dir << std::endl; + bool last = (start == this->Internals->Expressions.size() - 1); + if (last && this->Recurse) { + this->RecurseDirectory(start, dir, messages); + return; + } + + if (start >= this->Internals->Expressions.size()) { + return; + } + + kwsys::Directory d; + if (!d.Load(dir)) { + return; + } + unsigned long cc; + std::string realname; + std::string fname; + for (cc = 0; cc < d.GetNumberOfFiles(); cc++) { + fname = d.GetFile(cc); + if (fname == "." || fname == "..") { + continue; + } + + if (start == 0) { + realname = dir + fname; + } else { + realname = dir + "/" + fname; + } + +#if defined(KWSYS_GLOB_CASE_INDEPENDENT) + // On case-insensitive file systems convert to lower case for matching. + fname = kwsys::SystemTools::LowerCase(fname); +#endif + + // std::cout << "Look at file: " << fname << std::endl; + // std::cout << "Match: " + // << this->Internals->TextExpressions[start].c_str() << std::endl; + // std::cout << "Real name: " << realname << std::endl; + + if ((!last && !kwsys::SystemTools::FileIsDirectory(realname)) || + (!this->ListDirs && last && + kwsys::SystemTools::FileIsDirectory(realname))) { + continue; + } + + if (this->Internals->Expressions[start].find(fname)) { + if (last) { + this->AddFile(this->Internals->Files, realname); + } else { + this->ProcessDirectory(start + 1, realname, messages); + } + } + } +} + +bool Glob::FindFiles(const std::string& inexpr, GlobMessages* messages) +{ + std::string cexpr; + std::string::size_type cc; + std::string expr = inexpr; + + this->Internals->Expressions.clear(); + this->Internals->Files.clear(); + + if (!kwsys::SystemTools::FileIsFullPath(expr)) { + expr = kwsys::SystemTools::GetCurrentWorkingDirectory(); + expr += "/" + inexpr; + } + std::string fexpr = expr; + + std::string::size_type skip = 0; + std::string::size_type last_slash = 0; + for (cc = 0; cc < expr.size(); cc++) { + if (cc > 0 && expr[cc] == '/' && expr[cc - 1] != '\\') { + last_slash = cc; + } + if (cc > 0 && (expr[cc] == '[' || expr[cc] == '?' || expr[cc] == '*') && + expr[cc - 1] != '\\') { + break; + } + } + if (last_slash > 0) { + // std::cout << "I can skip: " << fexpr.substr(0, last_slash) + // << std::endl; + skip = last_slash; + } + if (skip == 0) { +#if defined(KWSYS_GLOB_SUPPORT_NETWORK_PATHS) + // Handle network paths + if (expr[0] == '/' && expr[1] == '/') { + int cnt = 0; + for (cc = 2; cc < expr.size(); cc++) { + if (expr[cc] == '/') { + cnt++; + if (cnt == 2) { + break; + } + } + } + skip = int(cc + 1); + } else +#endif + // Handle drive letters on Windows + if (expr[1] == ':' && expr[0] != '/') { + skip = 2; + } + } + + if (skip > 0) { + expr = expr.substr(skip); + } + + cexpr = ""; + for (cc = 0; cc < expr.size(); cc++) { + int ch = expr[cc]; + if (ch == '/') { + if (!cexpr.empty()) { + this->AddExpression(cexpr); + } + cexpr = ""; + } else { + cexpr.append(1, static_cast<char>(ch)); + } + } + if (!cexpr.empty()) { + this->AddExpression(cexpr); + } + + // Handle network paths + if (skip > 0) { + this->ProcessDirectory(0, fexpr.substr(0, skip) + "/", messages); + } else { + this->ProcessDirectory(0, "/", messages); + } + return true; +} + +void Glob::AddExpression(const std::string& expr) +{ + this->Internals->Expressions.emplace_back(this->PatternToRegex(expr)); +} + +void Glob::SetRelative(const char* dir) +{ + if (!dir) { + this->Relative = ""; + return; + } + this->Relative = dir; +} + +const char* Glob::GetRelative() +{ + if (this->Relative.empty()) { + return nullptr; + } + return this->Relative.c_str(); +} + +void Glob::AddFile(std::vector<std::string>& files, const std::string& file) +{ + if (!this->Relative.empty()) { + files.push_back(kwsys::SystemTools::RelativePath(this->Relative, file)); + } else { + files.push_back(file); + } +} + +} // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/Glob.hxx.in b/Source/kwsys/Glob.hxx.in new file mode 100644 index 0000000..170766f --- /dev/null +++ b/Source/kwsys/Glob.hxx.in @@ -0,0 +1,134 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Glob_hxx +#define @KWSYS_NAMESPACE@_Glob_hxx + +#include <@KWSYS_NAMESPACE@/Configure.h> +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <string> +#include <vector> + +namespace @KWSYS_NAMESPACE@ { + +class GlobInternals; + +/** \class Glob + * \brief Portable globbing searches. + * + * Globbing expressions are much simpler than regular + * expressions. This class will search for files using + * globbing expressions. + * + * Finds all files that match a given globbing expression. + */ +class @KWSYS_NAMESPACE@_EXPORT Glob +{ +public: + enum MessageType + { + error, + cyclicRecursion + }; + + struct Message + { + MessageType type; + std::string content; + + Message(MessageType t, const std::string& c) + : type(t) + , content(c) + { + } + ~Message() = default; + Message(const Message& msg) = default; + Message& operator=(Message const& msg) = default; + }; + + typedef std::vector<Message> GlobMessages; + typedef std::vector<Message>::iterator GlobMessagesIterator; + +public: + Glob(); + ~Glob(); + + //! Find all files that match the pattern. + bool FindFiles(const std::string& inexpr, GlobMessages* messages = nullptr); + + //! Return the list of files that matched. + std::vector<std::string>& GetFiles(); + + //! Set recurse to true to match subdirectories. + void RecurseOn() { this->SetRecurse(true); } + void RecurseOff() { this->SetRecurse(false); } + void SetRecurse(bool i) { this->Recurse = i; } + bool GetRecurse() { return this->Recurse; } + + //! Set recurse through symlinks to true if recursion should traverse the + // linked-to directories + void RecurseThroughSymlinksOn() { this->SetRecurseThroughSymlinks(true); } + void RecurseThroughSymlinksOff() { this->SetRecurseThroughSymlinks(false); } + void SetRecurseThroughSymlinks(bool i) { this->RecurseThroughSymlinks = i; } + bool GetRecurseThroughSymlinks() { return this->RecurseThroughSymlinks; } + + //! Get the number of symlinks followed through recursion + unsigned int GetFollowedSymlinkCount() { return this->FollowedSymlinkCount; } + + //! Set relative to true to only show relative path to files. + void SetRelative(const char* dir); + const char* GetRelative(); + + /** Convert the given globbing pattern to a regular expression. + There is no way to quote meta-characters. The + require_whole_string argument specifies whether the regex is + automatically surrounded by "^" and "$" to match the whole + string. This is on by default because patterns always match + whole strings, but may be disabled to support concatenating + expressions more easily (regex1|regex2|etc). */ + static std::string PatternToRegex(const std::string& pattern, + bool require_whole_string = true, + bool preserve_case = false); + + /** Getters and setters for enabling and disabling directory + listing in recursive and non recursive globbing mode. + If listing is enabled in recursive mode it also lists + directory symbolic links even if follow symlinks is enabled. */ + void SetListDirs(bool list) { this->ListDirs = list; } + bool GetListDirs() const { return this->ListDirs; } + void SetRecurseListDirs(bool list) { this->RecurseListDirs = list; } + bool GetRecurseListDirs() const { return this->RecurseListDirs; } + +protected: + //! Process directory + void ProcessDirectory(std::string::size_type start, const std::string& dir, + GlobMessages* messages); + + //! Process last directory, but only when recurse flags is on. That is + // effectively like saying: /path/to/file/**/file + bool RecurseDirectory(std::string::size_type start, const std::string& dir, + GlobMessages* messages); + + //! Add regular expression + void AddExpression(const std::string& expr); + + //! Add a file to the list + void AddFile(std::vector<std::string>& files, const std::string& file); + + GlobInternals* Internals; + bool Recurse; + std::string Relative; + bool RecurseThroughSymlinks; + unsigned int FollowedSymlinkCount; + std::vector<std::string> VisitedSymlinks; + bool ListDirs; + bool RecurseListDirs; + +private: + Glob(const Glob&); // Not implemented. + void operator=(const Glob&); // Not implemented. +}; + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/IOStream.cxx b/Source/kwsys/IOStream.cxx new file mode 100644 index 0000000..e21f87d --- /dev/null +++ b/Source/kwsys/IOStream.cxx @@ -0,0 +1,255 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Configure.hxx) + +// Include the streams library. +#include <iostream> +#include KWSYS_HEADER(IOStream.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Configure.hxx.in" +# include "IOStream.hxx.in" +#endif + +// Implement the rest of this file only if it is needed. +#if KWSYS_IOS_NEED_OPERATORS_LL + +# include <stdio.h> // sscanf, sprintf +# include <string.h> // memchr + +# if defined(_MAX_INT_DIG) +# define KWSYS_IOS_INT64_MAX_DIG _MAX_INT_DIG +# else +# define KWSYS_IOS_INT64_MAX_DIG 32 +# endif + +namespace KWSYS_NAMESPACE { + +// Scan an input stream for an integer value. +static int IOStreamScanStream(std::istream& is, char* buffer) +{ + // Prepare to write to buffer. + char* out = buffer; + char* end = buffer + KWSYS_IOS_INT64_MAX_DIG - 1; + + // Look for leading sign. + if (is.peek() == '+') { + *out++ = '+'; + is.ignore(); + } else if (is.peek() == '-') { + *out++ = '-'; + is.ignore(); + } + + // Determine the base. If not specified in the stream, try to + // detect it from the input. A leading 0x means hex, and a leading + // 0 alone means octal. + int base = 0; + int flags = is.flags() & std::ios_base::basefield; + if (flags == std::ios_base::oct) { + base = 8; + } else if (flags == std::ios_base::dec) { + base = 10; + } else if (flags == std::ios_base::hex) { + base = 16; + } + bool foundDigit = false; + bool foundNonZero = false; + if (is.peek() == '0') { + foundDigit = true; + is.ignore(); + if ((is.peek() == 'x' || is.peek() == 'X') && (base == 0 || base == 16)) { + base = 16; + foundDigit = false; + is.ignore(); + } else if (base == 0) { + base = 8; + } + } + + // Determine the range of digits allowed for this number. + const char* digits = "0123456789abcdefABCDEF"; + int maxDigitIndex = 10; + if (base == 8) { + maxDigitIndex = 8; + } else if (base == 16) { + maxDigitIndex = 10 + 6 + 6; + } + + // Scan until an invalid digit is found. + for (; is.peek() != EOF; is.ignore()) { + if (memchr(digits, *out = (char)is.peek(), maxDigitIndex) != 0) { + if ((foundNonZero || *out != '0') && out < end) { + ++out; + foundNonZero = true; + } + foundDigit = true; + } else { + break; + } + } + + // Correct the buffer contents for degenerate cases. + if (foundDigit && !foundNonZero) { + *out++ = '0'; + } else if (!foundDigit) { + out = buffer; + } + + // Terminate the string in the buffer. + *out = '\0'; + + return base; +} + +// Read an integer value from an input stream. +template <class T> +std::istream& IOStreamScanTemplate(std::istream& is, T& value, char type) +{ + int state = std::ios_base::goodbit; + + // Skip leading whitespace. + std::istream::sentry okay(is); + + if (okay) { + try { + // Copy the string to a buffer and construct the format string. + char buffer[KWSYS_IOS_INT64_MAX_DIG]; +# if defined(_MSC_VER) + char format[] = "%I64_"; + const int typeIndex = 4; +# else + char format[] = "%ll_"; + const int typeIndex = 3; +# endif + switch (IOStreamScanStream(is, buffer)) { + case 8: + format[typeIndex] = 'o'; + break; + case 0: // Default to decimal if not told otherwise. + case 10: + format[typeIndex] = type; + break; + case 16: + format[typeIndex] = 'x'; + break; + }; + + // Use sscanf to parse the number from the buffer. + T result; + int success = (sscanf(buffer, format, &result) == 1) ? 1 : 0; + + // Set flags for resulting state. + if (is.peek() == EOF) { + state |= std::ios_base::eofbit; + } + if (!success) { + state |= std::ios_base::failbit; + } else { + value = result; + } + } catch (...) { + state |= std::ios_base::badbit; + } + } + + is.setstate(std::ios_base::iostate(state)); + return is; +} + +// Print an integer value to an output stream. +template <class T> +std::ostream& IOStreamPrintTemplate(std::ostream& os, T value, char type) +{ + std::ostream::sentry okay(os); + if (okay) { + try { + // Construct the format string. + char format[8]; + char* f = format; + *f++ = '%'; + if (os.flags() & std::ios_base::showpos) { + *f++ = '+'; + } + if (os.flags() & std::ios_base::showbase) { + *f++ = '#'; + } +# if defined(_MSC_VER) + *f++ = 'I'; + *f++ = '6'; + *f++ = '4'; +# else + *f++ = 'l'; + *f++ = 'l'; +# endif + long bflags = os.flags() & std::ios_base::basefield; + if (bflags == std::ios_base::oct) { + *f++ = 'o'; + } else if (bflags != std::ios_base::hex) { + *f++ = type; + } else if (os.flags() & std::ios_base::uppercase) { + *f++ = 'X'; + } else { + *f++ = 'x'; + } + *f = '\0'; + + // Use sprintf to print to a buffer and then write the + // buffer to the stream. + char buffer[2 * KWSYS_IOS_INT64_MAX_DIG]; + sprintf(buffer, format, value); + os << buffer; + } catch (...) { + os.clear(os.rdstate() | std::ios_base::badbit); + } + } + return os; +} + +# if !KWSYS_IOS_HAS_ISTREAM_LONG_LONG +// Implement input stream operator for IOStreamSLL. +std::istream& IOStreamScan(std::istream& is, IOStreamSLL& value) +{ + return IOStreamScanTemplate(is, value, 'd'); +} + +// Implement input stream operator for IOStreamULL. +std::istream& IOStreamScan(std::istream& is, IOStreamULL& value) +{ + return IOStreamScanTemplate(is, value, 'u'); +} +# endif + +# if !KWSYS_IOS_HAS_OSTREAM_LONG_LONG +// Implement output stream operator for IOStreamSLL. +std::ostream& IOStreamPrint(std::ostream& os, IOStreamSLL value) +{ + return IOStreamPrintTemplate(os, value, 'd'); +} + +// Implement output stream operator for IOStreamULL. +std::ostream& IOStreamPrint(std::ostream& os, IOStreamULL value) +{ + return IOStreamPrintTemplate(os, value, 'u'); +} +# endif + +} // namespace KWSYS_NAMESPACE + +#else + +namespace KWSYS_NAMESPACE { + +// Create one public symbol in this object file to avoid warnings from +// archivers. +void IOStreamSymbolToAvoidWarning(); +void IOStreamSymbolToAvoidWarning() +{ +} + +} // namespace KWSYS_NAMESPACE + +#endif // KWSYS_IOS_NEED_OPERATORS_LL diff --git a/Source/kwsys/IOStream.hxx.in b/Source/kwsys/IOStream.hxx.in new file mode 100644 index 0000000..db8a23e --- /dev/null +++ b/Source/kwsys/IOStream.hxx.in @@ -0,0 +1,126 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_IOStream_hxx +#define @KWSYS_NAMESPACE@_IOStream_hxx + +#include <iosfwd> + +/* Define these macros temporarily to keep the code readable. */ +#if !defined(KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif + +/* Whether istream supports long long. */ +#define @KWSYS_NAMESPACE@_IOS_HAS_ISTREAM_LONG_LONG \ + @KWSYS_IOS_HAS_ISTREAM_LONG_LONG@ + +/* Whether ostream supports long long. */ +#define @KWSYS_NAMESPACE@_IOS_HAS_OSTREAM_LONG_LONG \ + @KWSYS_IOS_HAS_OSTREAM_LONG_LONG@ + +/* Determine whether we need to define the streaming operators for + long long or __int64. */ +#if @KWSYS_USE_LONG_LONG@ +# if !@KWSYS_NAMESPACE@_IOS_HAS_ISTREAM_LONG_LONG || \ + !@KWSYS_NAMESPACE@_IOS_HAS_OSTREAM_LONG_LONG +# define @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL 1 +namespace @KWSYS_NAMESPACE@ { +typedef long long IOStreamSLL; +typedef unsigned long long IOStreamULL; +} +# endif +#elif defined(_MSC_VER) && _MSC_VER < 1300 +# define @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL 1 +namespace @KWSYS_NAMESPACE@ { +typedef __int64 IOStreamSLL; +typedef unsigned __int64 IOStreamULL; +} +#endif +#if !defined(@KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL) +# define @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL 0 +#endif + +#if @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL +# if !@KWSYS_NAMESPACE@_IOS_HAS_ISTREAM_LONG_LONG + +/* Input stream operator implementation functions. */ +namespace @KWSYS_NAMESPACE@ { +kwsysEXPORT std::istream& IOStreamScan(std::istream&, IOStreamSLL&); +kwsysEXPORT std::istream& IOStreamScan(std::istream&, IOStreamULL&); +} + +/* Provide input stream operator for long long. */ +# if !defined(@KWSYS_NAMESPACE@_IOS_NO_ISTREAM_LONG_LONG) && \ + !defined(KWSYS_IOS_ISTREAM_LONG_LONG_DEFINED) +# define KWSYS_IOS_ISTREAM_LONG_LONG_DEFINED +# define @KWSYS_NAMESPACE@_IOS_ISTREAM_LONG_LONG_DEFINED +inline std::istream& operator>>(std::istream& is, + @KWSYS_NAMESPACE@::IOStreamSLL& value) +{ + return @KWSYS_NAMESPACE@::IOStreamScan(is, value); +} +# endif + +/* Provide input stream operator for unsigned long long. */ +# if !defined(@KWSYS_NAMESPACE@_IOS_NO_ISTREAM_UNSIGNED_LONG_LONG) && \ + !defined(KWSYS_IOS_ISTREAM_UNSIGNED_LONG_LONG_DEFINED) +# define KWSYS_IOS_ISTREAM_UNSIGNED_LONG_LONG_DEFINED +# define @KWSYS_NAMESPACE@_IOS_ISTREAM_UNSIGNED_LONG_LONG_DEFINED +inline std::istream& operator>>(std::istream& is, + @KWSYS_NAMESPACE@::IOStreamULL& value) +{ + return @KWSYS_NAMESPACE@::IOStreamScan(is, value); +} +# endif +# endif /* !@KWSYS_NAMESPACE@_IOS_HAS_ISTREAM_LONG_LONG */ + +# if !@KWSYS_NAMESPACE@_IOS_HAS_OSTREAM_LONG_LONG + +/* Output stream operator implementation functions. */ +namespace @KWSYS_NAMESPACE@ { +kwsysEXPORT std::ostream& IOStreamPrint(std::ostream&, IOStreamSLL); +kwsysEXPORT std::ostream& IOStreamPrint(std::ostream&, IOStreamULL); +} + +/* Provide output stream operator for long long. */ +# if !defined(@KWSYS_NAMESPACE@_IOS_NO_OSTREAM_LONG_LONG) && \ + !defined(KWSYS_IOS_OSTREAM_LONG_LONG_DEFINED) +# define KWSYS_IOS_OSTREAM_LONG_LONG_DEFINED +# define @KWSYS_NAMESPACE@_IOS_OSTREAM_LONG_LONG_DEFINED +inline std::ostream& operator<<(std::ostream& os, + @KWSYS_NAMESPACE@::IOStreamSLL value) +{ + return @KWSYS_NAMESPACE@::IOStreamPrint(os, value); +} +# endif + +/* Provide output stream operator for unsigned long long. */ +# if !defined(@KWSYS_NAMESPACE@_IOS_NO_OSTREAM_UNSIGNED_LONG_LONG) && \ + !defined(KWSYS_IOS_OSTREAM_UNSIGNED_LONG_LONG_DEFINED) +# define KWSYS_IOS_OSTREAM_UNSIGNED_LONG_LONG_DEFINED +# define @KWSYS_NAMESPACE@_IOS_OSTREAM_UNSIGNED_LONG_LONG_DEFINED +inline std::ostream& operator<<(std::ostream& os, + @KWSYS_NAMESPACE@::IOStreamULL value) +{ + return @KWSYS_NAMESPACE@::IOStreamPrint(os, value); +} +# endif +# endif /* !@KWSYS_NAMESPACE@_IOS_HAS_OSTREAM_LONG_LONG */ +#endif /* @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL */ + +/* Undefine temporary macros. */ +#if !defined(KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysEXPORT +#endif + +/* If building a C++ file in kwsys itself, give the source file + access to the macros without a configured namespace. */ +#if defined(KWSYS_NAMESPACE) +# define KWSYS_IOS_HAS_ISTREAM_LONG_LONG \ + @KWSYS_NAMESPACE@_IOS_HAS_ISTREAM_LONG_LONG +# define KWSYS_IOS_HAS_OSTREAM_LONG_LONG \ + @KWSYS_NAMESPACE@_IOS_HAS_OSTREAM_LONG_LONG +# define KWSYS_IOS_NEED_OPERATORS_LL @KWSYS_NAMESPACE@_IOS_NEED_OPERATORS_LL +#endif + +#endif diff --git a/Source/kwsys/MD5.c b/Source/kwsys/MD5.c new file mode 100644 index 0000000..97cf9ba --- /dev/null +++ b/Source/kwsys/MD5.c @@ -0,0 +1,494 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(MD5.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "MD5.h.in" +#endif + +#include <stddef.h> /* size_t */ +#include <stdlib.h> /* malloc, free */ +#include <string.h> /* memcpy, strlen */ + +/* This MD5 implementation has been taken from a third party. Slight + modifications to the arrangement of the code have been made to put + it in a single source file instead of a separate header and + implementation file. */ + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-align" +#endif + +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors 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. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s +{ + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + +static void md5_process(md5_state_t* pms, const md5_byte_t* data /*[64]*/) +{ + md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], + d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t* X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t*)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t*)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t*)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t* xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = + (md5_word_t)(xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24)); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +/* Round 1. */ +/* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti) \ + t = a + F(b, c, d) + X[k] + (Ti); \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + +/* Round 2. */ +/* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti) \ + t = a + G(b, c, d) + X[k] + (Ti); \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + +/* Round 3. */ +/* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti) \ + t = a + H(b, c, d) + X[k] + (Ti); \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + +/* Round 4. */ +/* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti) \ + t = a + I(b, c, d) + X[k] + (Ti); \ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +/* Initialize the algorithm. */ +static void md5_init(md5_state_t* pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +/* Append a string to the message. */ +static void md5_append(md5_state_t* pms, const md5_byte_t* data, size_t nbytes) +{ + const md5_byte_t* p = data; + size_t left = nbytes; + size_t offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += (md5_word_t)(nbytes >> 29); + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +/* Finish the message and return the digest. */ +static void md5_finish(md5_state_t* pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic pop +#endif + +/* Wrap up the MD5 state in our opaque structure. */ +struct kwsysMD5_s +{ + md5_state_t md5_state; +}; + +kwsysMD5* kwsysMD5_New(void) +{ + /* Allocate a process control structure. */ + kwsysMD5* md5 = (kwsysMD5*)malloc(sizeof(kwsysMD5)); + if (!md5) { + return 0; + } + return md5; +} + +void kwsysMD5_Delete(kwsysMD5* md5) +{ + /* Make sure we have an instance. */ + if (!md5) { + return; + } + + /* Free memory. */ + free(md5); +} + +void kwsysMD5_Initialize(kwsysMD5* md5) +{ + md5_init(&md5->md5_state); +} + +void kwsysMD5_Append(kwsysMD5* md5, unsigned char const* data, int length) +{ + size_t dlen; + if (length < 0) { + dlen = strlen((char const*)data); + } else { + dlen = (size_t)length; + } + md5_append(&md5->md5_state, (md5_byte_t const*)data, dlen); +} + +void kwsysMD5_Finalize(kwsysMD5* md5, unsigned char digest[16]) +{ + md5_finish(&md5->md5_state, (md5_byte_t*)digest); +} + +void kwsysMD5_FinalizeHex(kwsysMD5* md5, char buffer[32]) +{ + unsigned char digest[16]; + kwsysMD5_Finalize(md5, digest); + kwsysMD5_DigestToHex(digest, buffer); +} + +void kwsysMD5_DigestToHex(unsigned char const digest[16], char buffer[32]) +{ + /* Map from 4-bit index to hexadecimal representation. */ + static char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /* Map each 4-bit block separately. */ + char* out = buffer; + int i; + for (i = 0; i < 16; ++i) { + *out++ = hex[digest[i] >> 4]; + *out++ = hex[digest[i] & 0xF]; + } +} diff --git a/Source/kwsys/MD5.h.in b/Source/kwsys/MD5.h.in new file mode 100644 index 0000000..7646f12 --- /dev/null +++ b/Source/kwsys/MD5.h.in @@ -0,0 +1,97 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_MD5_h +#define @KWSYS_NAMESPACE@_MD5_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysMD5 kwsys_ns(MD5) +# define kwsysMD5_s kwsys_ns(MD5_s) +# define kwsysMD5_New kwsys_ns(MD5_New) +# define kwsysMD5_Delete kwsys_ns(MD5_Delete) +# define kwsysMD5_Initialize kwsys_ns(MD5_Initialize) +# define kwsysMD5_Append kwsys_ns(MD5_Append) +# define kwsysMD5_Finalize kwsys_ns(MD5_Finalize) +# define kwsysMD5_FinalizeHex kwsys_ns(MD5_FinalizeHex) +# define kwsysMD5_DigestToHex kwsys_ns(MD5_DigestToHex) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * MD5 state data structure. + */ +typedef struct kwsysMD5_s kwsysMD5; + +/** + * Create a new MD5 instance. The returned instance is not initialized. + */ +kwsysEXPORT kwsysMD5* kwsysMD5_New(void); + +/** + * Delete an old MD5 instance. + */ +kwsysEXPORT void kwsysMD5_Delete(kwsysMD5* md5); + +/** + * Initialize a new MD5 digest. + */ +kwsysEXPORT void kwsysMD5_Initialize(kwsysMD5* md5); + +/** + * Append data to an MD5 digest. If the given length is negative, + * data will be read up to but not including a terminating null. + */ +kwsysEXPORT void kwsysMD5_Append(kwsysMD5* md5, unsigned char const* data, + int length); + +/** + * Finalize a MD5 digest and get the 16-byte hash value. + */ +kwsysEXPORT void kwsysMD5_Finalize(kwsysMD5* md5, unsigned char digest[16]); + +/** + * Finalize a MD5 digest and get the 32-bit hexadecimal representation. + */ +kwsysEXPORT void kwsysMD5_FinalizeHex(kwsysMD5* md5, char buffer[32]); + +/** + * Convert a MD5 digest 16-byte value to a 32-byte hexadecimal representation. + */ +kwsysEXPORT void kwsysMD5_DigestToHex(unsigned char const digest[16], + char buffer[32]); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysMD5 +# undef kwsysMD5_s +# undef kwsysMD5_New +# undef kwsysMD5_Delete +# undef kwsysMD5_Initialize +# undef kwsysMD5_Append +# undef kwsysMD5_Finalize +# undef kwsysMD5_FinalizeHex +# undef kwsysMD5_DigestToHex +# endif +#endif + +#endif diff --git a/Source/kwsys/Process.h.in b/Source/kwsys/Process.h.in new file mode 100644 index 0000000..9f2162b --- /dev/null +++ b/Source/kwsys/Process.h.in @@ -0,0 +1,544 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Process_h +#define @KWSYS_NAMESPACE@_Process_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysProcess kwsys_ns(Process) +# define kwsysProcess_s kwsys_ns(Process_s) +# define kwsysProcess_New kwsys_ns(Process_New) +# define kwsysProcess_Delete kwsys_ns(Process_Delete) +# define kwsysProcess_SetCommand kwsys_ns(Process_SetCommand) +# define kwsysProcess_AddCommand kwsys_ns(Process_AddCommand) +# define kwsysProcess_SetTimeout kwsys_ns(Process_SetTimeout) +# define kwsysProcess_SetWorkingDirectory \ + kwsys_ns(Process_SetWorkingDirectory) +# define kwsysProcess_SetPipeFile kwsys_ns(Process_SetPipeFile) +# define kwsysProcess_SetPipeNative kwsys_ns(Process_SetPipeNative) +# define kwsysProcess_SetPipeShared kwsys_ns(Process_SetPipeShared) +# define kwsysProcess_Option_Detach kwsys_ns(Process_Option_Detach) +# define kwsysProcess_Option_HideWindow kwsys_ns(Process_Option_HideWindow) +# define kwsysProcess_Option_MergeOutput kwsys_ns(Process_Option_MergeOutput) +# define kwsysProcess_Option_Verbatim kwsys_ns(Process_Option_Verbatim) +# define kwsysProcess_Option_CreateProcessGroup \ + kwsys_ns(Process_Option_CreateProcessGroup) +# define kwsysProcess_GetOption kwsys_ns(Process_GetOption) +# define kwsysProcess_SetOption kwsys_ns(Process_SetOption) +# define kwsysProcess_Option_e kwsys_ns(Process_Option_e) +# define kwsysProcess_State_Starting kwsys_ns(Process_State_Starting) +# define kwsysProcess_State_Error kwsys_ns(Process_State_Error) +# define kwsysProcess_State_Exception kwsys_ns(Process_State_Exception) +# define kwsysProcess_State_Executing kwsys_ns(Process_State_Executing) +# define kwsysProcess_State_Exited kwsys_ns(Process_State_Exited) +# define kwsysProcess_State_Expired kwsys_ns(Process_State_Expired) +# define kwsysProcess_State_Killed kwsys_ns(Process_State_Killed) +# define kwsysProcess_State_Disowned kwsys_ns(Process_State_Disowned) +# define kwsysProcess_State_e kwsys_ns(Process_State_e) +# define kwsysProcess_Exception_None kwsys_ns(Process_Exception_None) +# define kwsysProcess_Exception_Fault kwsys_ns(Process_Exception_Fault) +# define kwsysProcess_Exception_Illegal kwsys_ns(Process_Exception_Illegal) +# define kwsysProcess_Exception_Interrupt \ + kwsys_ns(Process_Exception_Interrupt) +# define kwsysProcess_Exception_Numerical \ + kwsys_ns(Process_Exception_Numerical) +# define kwsysProcess_Exception_Other kwsys_ns(Process_Exception_Other) +# define kwsysProcess_Exception_e kwsys_ns(Process_Exception_e) +# define kwsysProcess_GetState kwsys_ns(Process_GetState) +# define kwsysProcess_GetExitException kwsys_ns(Process_GetExitException) +# define kwsysProcess_GetExitCode kwsys_ns(Process_GetExitCode) +# define kwsysProcess_GetExitValue kwsys_ns(Process_GetExitValue) +# define kwsysProcess_GetErrorString kwsys_ns(Process_GetErrorString) +# define kwsysProcess_GetExceptionString kwsys_ns(Process_GetExceptionString) +# define kwsysProcess_GetStateByIndex kwsys_ns(Process_GetStateByIndex) +# define kwsysProcess_GetExitExceptionByIndex \ + kwsys_ns(Process_GetExitExceptionByIndex) +# define kwsysProcess_GetExitCodeByIndex kwsys_ns(Process_GetExitCodeByIndex) +# define kwsysProcess_GetExitValueByIndex \ + kwsys_ns(Process_GetExitValueByIndex) +# define kwsysProcess_GetExceptionStringByIndex \ + kwsys_ns(Process_GetExceptionStringByIndex) +# define kwsysProcess_GetExitCodeByIndex kwsys_ns(Process_GetExitCodeByIndex) +# define kwsysProcess_Execute kwsys_ns(Process_Execute) +# define kwsysProcess_Disown kwsys_ns(Process_Disown) +# define kwsysProcess_WaitForData kwsys_ns(Process_WaitForData) +# define kwsysProcess_Pipes_e kwsys_ns(Process_Pipes_e) +# define kwsysProcess_Pipe_None kwsys_ns(Process_Pipe_None) +# define kwsysProcess_Pipe_STDIN kwsys_ns(Process_Pipe_STDIN) +# define kwsysProcess_Pipe_STDOUT kwsys_ns(Process_Pipe_STDOUT) +# define kwsysProcess_Pipe_STDERR kwsys_ns(Process_Pipe_STDERR) +# define kwsysProcess_Pipe_Timeout kwsys_ns(Process_Pipe_Timeout) +# define kwsysProcess_Pipe_Handle kwsys_ns(Process_Pipe_Handle) +# define kwsysProcess_WaitForExit kwsys_ns(Process_WaitForExit) +# define kwsysProcess_Interrupt kwsys_ns(Process_Interrupt) +# define kwsysProcess_Kill kwsys_ns(Process_Kill) +# define kwsysProcess_KillPID kwsys_ns(Process_KillPID) +# define kwsysProcess_ResetStartTime kwsys_ns(Process_ResetStartTime) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Process control data structure. + */ +typedef struct kwsysProcess_s kwsysProcess; + +/* Platform-specific pipe handle type. */ +#if defined(_WIN32) && !defined(__CYGWIN__) +typedef void* kwsysProcess_Pipe_Handle; +#else +typedef int kwsysProcess_Pipe_Handle; +#endif + +/** + * Create a new Process instance. + */ +kwsysEXPORT kwsysProcess* kwsysProcess_New(void); + +/** + * Delete an existing Process instance. If the instance is currently + * executing a process, this blocks until the process terminates. + */ +kwsysEXPORT void kwsysProcess_Delete(kwsysProcess* cp); + +/** + * Set the command line to be executed. Argument is an array of + * pointers to the command and each argument. The array must end with + * a NULL pointer. Any previous command lines are removed. Returns + * 1 for success and 0 otherwise. + */ +kwsysEXPORT int kwsysProcess_SetCommand(kwsysProcess* cp, + char const* const* command); + +/** + * Add a command line to be executed. Argument is an array of + * pointers to the command and each argument. The array must end with + * a NULL pointer. If this is not the first command added, its + * standard input will be connected to the standard output of the + * previous command. Returns 1 for success and 0 otherwise. + */ +kwsysEXPORT int kwsysProcess_AddCommand(kwsysProcess* cp, + char const* const* command); + +/** + * Set the timeout in seconds for the child process. The timeout + * period begins when the child is executed. If the child has not + * terminated when the timeout expires, it will be killed. A + * non-positive (<= 0) value will disable the timeout. + */ +kwsysEXPORT void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout); + +/** + * Set the working directory for the child process. The working + * directory can be absolute or relative to the current directory. + * Returns 1 for success and 0 for failure. + */ +kwsysEXPORT int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, + const char* dir); + +/** + * Set the name of a file to be attached to the given pipe. Returns 1 + * for success and 0 for failure. + */ +kwsysEXPORT int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, + const char* file); + +/** + * Set whether the given pipe in the child is shared with the parent + * process. The default is no for Pipe_STDOUT and Pipe_STDERR and yes + * for Pipe_STDIN. + */ +kwsysEXPORT void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, + int shared); + +/** + * Specify a platform-specific native pipe for use as one of the child + * interface pipes. The native pipe is specified by an array of two + * descriptors or handles. The first entry in the array (index 0) + * should be the read end of the pipe. The second entry in the array + * (index 1) should be the write end of the pipe. If a null pointer + * is given the option will be disabled. + * + * For Pipe_STDIN the native pipe is connected to the first child in + * the pipeline as its stdin. After the children are created the + * write end of the pipe will be closed in the child process and the + * read end will be closed in the parent process. + * + * For Pipe_STDOUT and Pipe_STDERR the pipe is connected to the last + * child as its stdout or stderr. After the children are created the + * write end of the pipe will be closed in the parent process and the + * read end will be closed in the child process. + */ +kwsysEXPORT void kwsysProcess_SetPipeNative( + kwsysProcess* cp, int pipe, const kwsysProcess_Pipe_Handle p[2]); + +/** + * Get/Set a possibly platform-specific option. Possible options are: + * + * kwsysProcess_Option_Detach = Whether to detach the process. + * 0 = No (default) + * 1 = Yes + * + * kwsysProcess_Option_HideWindow = Whether to hide window on Windows. + * 0 = No (default) + * 1 = Yes + * + * kwsysProcess_Option_MergeOutput = Whether to merge stdout/stderr. + * No content will be returned as stderr. + * Any actual stderr will be on stdout. + * 0 = No (default) + * 1 = Yes + * + * kwsysProcess_Option_Verbatim = Whether SetCommand and AddCommand + * should treat the first argument + * as a verbatim command line + * and ignore the rest of the arguments. + * 0 = No (default) + * 1 = Yes + * + * kwsysProcess_Option_CreateProcessGroup = Whether to place the process in a + * new process group. This is + * useful if you want to send Ctrl+C + * to the process. On UNIX, also + * places the process in a new + * session. + * 0 = No (default) + * 1 = Yes + */ +kwsysEXPORT int kwsysProcess_GetOption(kwsysProcess* cp, int optionId); +kwsysEXPORT void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, + int value); +enum kwsysProcess_Option_e +{ + kwsysProcess_Option_HideWindow, + kwsysProcess_Option_Detach, + kwsysProcess_Option_MergeOutput, + kwsysProcess_Option_Verbatim, + kwsysProcess_Option_CreateProcessGroup +}; + +/** + * Get the current state of the Process instance. Possible states are: + * + * kwsysProcess_State_Starting = Execute has not yet been called. + * kwsysProcess_State_Error = Error administrating the child process. + * kwsysProcess_State_Exception = Child process exited abnormally. + * kwsysProcess_State_Executing = Child process is currently running. + * kwsysProcess_State_Exited = Child process exited normally. + * kwsysProcess_State_Expired = Child process's timeout expired. + * kwsysProcess_State_Killed = Child process terminated by Kill method. + * kwsysProcess_State_Disowned = Child is no longer managed by this object. + */ +kwsysEXPORT int kwsysProcess_GetState(kwsysProcess* cp); +enum kwsysProcess_State_e +{ + kwsysProcess_State_Starting, + kwsysProcess_State_Error, + kwsysProcess_State_Exception, + kwsysProcess_State_Executing, + kwsysProcess_State_Exited, + kwsysProcess_State_Expired, + kwsysProcess_State_Killed, + kwsysProcess_State_Disowned +}; + +/** + * When GetState returns "Exception", this method returns a + * platform-independent description of the exceptional behavior that + * caused the child to terminate abnormally. Possible exceptions are: + * + * kwsysProcess_Exception_None = No exceptional behavior occurred. + * kwsysProcess_Exception_Fault = Child crashed with a memory fault. + * kwsysProcess_Exception_Illegal = Child crashed with an illegal + * instruction. + * kwsysProcess_Exception_Interrupt = Child was interrupted by user + * (Cntl-C/Break). + * kwsysProcess_Exception_Numerical = Child crashed with a numerical + * exception. + * kwsysProcess_Exception_Other = Child terminated for another reason. + */ +kwsysEXPORT int kwsysProcess_GetExitException(kwsysProcess* cp); +enum kwsysProcess_Exception_e +{ + kwsysProcess_Exception_None, + kwsysProcess_Exception_Fault, + kwsysProcess_Exception_Illegal, + kwsysProcess_Exception_Interrupt, + kwsysProcess_Exception_Numerical, + kwsysProcess_Exception_Other +}; + +/** + * When GetState returns "Exited" or "Exception", this method returns + * the platform-specific raw exit code of the process. UNIX platforms + * should use WIFEXITED/WEXITSTATUS and WIFSIGNALED/WTERMSIG to access + * this value. Windows users should compare the value to the various + * EXCEPTION_* values. + * + * If GetState returns "Exited", use GetExitValue to get the + * platform-independent child return value. + */ +kwsysEXPORT int kwsysProcess_GetExitCode(kwsysProcess* cp); + +/** + * When GetState returns "Exited", this method returns the child's + * platform-independent exit code (such as the value returned by the + * child's main). + */ +kwsysEXPORT int kwsysProcess_GetExitValue(kwsysProcess* cp); + +/** + * When GetState returns "Error", this method returns a string + * describing the problem. Otherwise, it returns NULL. + */ +kwsysEXPORT const char* kwsysProcess_GetErrorString(kwsysProcess* cp); + +/** + * When GetState returns "Exception", this method returns a string + * describing the problem. Otherwise, it returns NULL. + */ +kwsysEXPORT const char* kwsysProcess_GetExceptionString(kwsysProcess* cp); + +/** + * Get the current state of the Process instance. Possible states are: + * + * kwsysProcess_StateByIndex_Starting = Execute has not yet been called. + * kwsysProcess_StateByIndex_Exception = Child process exited abnormally. + * kwsysProcess_StateByIndex_Exited = Child process exited normally. + * kwsysProcess_StateByIndex_Error = Error getting the child return code. + */ +kwsysEXPORT int kwsysProcess_GetStateByIndex(kwsysProcess* cp, int idx); +enum kwsysProcess_StateByIndex_e +{ + kwsysProcess_StateByIndex_Starting = kwsysProcess_State_Starting, + kwsysProcess_StateByIndex_Exception = kwsysProcess_State_Exception, + kwsysProcess_StateByIndex_Exited = kwsysProcess_State_Exited, + kwsysProcess_StateByIndex_Error = kwsysProcess_State_Error +}; + +/** + * When GetState returns "Exception", this method returns a + * platform-independent description of the exceptional behavior that + * caused the child to terminate abnormally. Possible exceptions are: + * + * kwsysProcess_Exception_None = No exceptional behavior occurred. + * kwsysProcess_Exception_Fault = Child crashed with a memory fault. + * kwsysProcess_Exception_Illegal = Child crashed with an illegal + * instruction. + * kwsysProcess_Exception_Interrupt = Child was interrupted by user + * (Cntl-C/Break). + * kwsysProcess_Exception_Numerical = Child crashed with a numerical + * exception. + * kwsysProcess_Exception_Other = Child terminated for another reason. + */ +kwsysEXPORT int kwsysProcess_GetExitExceptionByIndex(kwsysProcess* cp, + int idx); + +/** + * When GetState returns "Exited" or "Exception", this method returns + * the platform-specific raw exit code of the process. UNIX platforms + * should use WIFEXITED/WEXITSTATUS and WIFSIGNALED/WTERMSIG to access + * this value. Windows users should compare the value to the various + * EXCEPTION_* values. + * + * If GetState returns "Exited", use GetExitValue to get the + * platform-independent child return value. + */ +kwsysEXPORT int kwsysProcess_GetExitCodeByIndex(kwsysProcess* cp, int idx); + +/** + * When GetState returns "Exited", this method returns the child's + * platform-independent exit code (such as the value returned by the + * child's main). + */ +kwsysEXPORT int kwsysProcess_GetExitValueByIndex(kwsysProcess* cp, int idx); + +/** + * When GetState returns "Exception", this method returns a string + * describing the problem. Otherwise, it returns NULL. + */ +kwsysEXPORT const char* kwsysProcess_GetExceptionStringByIndex( + kwsysProcess* cp, int idx); + +/** + * Start executing the child process. + */ +kwsysEXPORT void kwsysProcess_Execute(kwsysProcess* cp); + +/** + * Stop management of a detached child process. This closes any pipes + * being read. If the child was not created with the + * kwsysProcess_Option_Detach option, this method does nothing. This + * is because disowning a non-detached process will cause the child + * exit signal to be left unhandled until this process exits. + */ +kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp); + +/** + * Block until data are available on a pipe, a timeout expires, or the + * child process terminates. Arguments are as follows: + * + * data = If data are read, the pointer to which this points is + * set to point to the data. + * length = If data are read, the integer to which this points is + * set to the length of the data read. + * timeout = Specifies the maximum time this call may block. Upon + * return after reading data, the time elapsed is subtracted + * from the timeout value. If this timeout expires, the + * value is set to 0. A NULL pointer passed for this argument + * indicates no timeout for the call. A negative or zero + * value passed for this argument may be used for polling + * and will always return immediately. + * + * Return value will be one of: + * + * Pipe_None = No more data will be available from the child process, + * ( == 0) or no process has been executed. WaitForExit should + * be called to wait for the process to terminate. + * Pipe_STDOUT = Data have been read from the child's stdout pipe. + * Pipe_STDERR = Data have been read from the child's stderr pipe. + * Pipe_Timeout = No data available within timeout specified for the + * call. Time elapsed has been subtracted from timeout + * argument. + */ +kwsysEXPORT int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, + int* length, double* timeout); +enum kwsysProcess_Pipes_e +{ + kwsysProcess_Pipe_None, + kwsysProcess_Pipe_STDIN, + kwsysProcess_Pipe_STDOUT, + kwsysProcess_Pipe_STDERR, + kwsysProcess_Pipe_Timeout = 255 +}; + +/** + * Block until the child process terminates or the given timeout + * expires. If no process is running, returns immediately. The + * argument is: + * + * timeout = Specifies the maximum time this call may block. Upon + * returning due to child termination, the elapsed time + * is subtracted from the given value. A NULL pointer + * passed for this argument indicates no timeout for the + * call. + * + * Return value will be one of: + * + * 0 = Child did not terminate within timeout specified for + * the call. Time elapsed has been subtracted from timeout + * argument. + * 1 = Child has terminated or was not running. + */ +kwsysEXPORT int kwsysProcess_WaitForExit(kwsysProcess* cp, double* timeout); + +/** + * Interrupt the process group for the child process that is currently + * running by sending it the appropriate operating-system specific signal. + * The caller should call WaitForExit after this returns to wait for the + * child to terminate. + * + * WARNING: If you didn't specify kwsysProcess_Option_CreateProcessGroup, + * you will interrupt your own process group. + */ +kwsysEXPORT void kwsysProcess_Interrupt(kwsysProcess* cp); + +/** + * Forcefully terminate the child process that is currently running. + * The caller should call WaitForExit after this returns to wait for + * the child to terminate. + */ +kwsysEXPORT void kwsysProcess_Kill(kwsysProcess* cp); + +/** + * Same as kwsysProcess_Kill using process ID to locate process to + * terminate. + * @see kwsysProcess_Kill(kwsysProcess* cp) + */ +kwsysEXPORT void kwsysProcess_KillPID(unsigned long); + +/** + * Reset the start time of the child process to the current time. + */ +kwsysEXPORT void kwsysProcess_ResetStartTime(kwsysProcess* cp); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysProcess +# undef kwsysProcess_s +# undef kwsysProcess_New +# undef kwsysProcess_Delete +# undef kwsysProcess_SetCommand +# undef kwsysProcess_AddCommand +# undef kwsysProcess_SetTimeout +# undef kwsysProcess_SetWorkingDirectory +# undef kwsysProcess_SetPipeFile +# undef kwsysProcess_SetPipeNative +# undef kwsysProcess_SetPipeShared +# undef kwsysProcess_Option_Detach +# undef kwsysProcess_Option_HideWindow +# undef kwsysProcess_Option_MergeOutput +# undef kwsysProcess_Option_Verbatim +# undef kwsysProcess_Option_CreateProcessGroup +# undef kwsysProcess_GetOption +# undef kwsysProcess_SetOption +# undef kwsysProcess_Option_e +# undef kwsysProcess_State_Starting +# undef kwsysProcess_State_Error +# undef kwsysProcess_State_Exception +# undef kwsysProcess_State_Executing +# undef kwsysProcess_State_Exited +# undef kwsysProcess_State_Expired +# undef kwsysProcess_State_Killed +# undef kwsysProcess_State_Disowned +# undef kwsysProcess_GetState +# undef kwsysProcess_State_e +# undef kwsysProcess_Exception_None +# undef kwsysProcess_Exception_Fault +# undef kwsysProcess_Exception_Illegal +# undef kwsysProcess_Exception_Interrupt +# undef kwsysProcess_Exception_Numerical +# undef kwsysProcess_Exception_Other +# undef kwsysProcess_GetExitException +# undef kwsysProcess_Exception_e +# undef kwsysProcess_GetExitCode +# undef kwsysProcess_GetExitValue +# undef kwsysProcess_GetErrorString +# undef kwsysProcess_GetExceptionString +# undef kwsysProcess_Execute +# undef kwsysProcess_Disown +# undef kwsysProcess_WaitForData +# undef kwsysProcess_Pipes_e +# undef kwsysProcess_Pipe_None +# undef kwsysProcess_Pipe_STDIN +# undef kwsysProcess_Pipe_STDOUT +# undef kwsysProcess_Pipe_STDERR +# undef kwsysProcess_Pipe_Timeout +# undef kwsysProcess_Pipe_Handle +# undef kwsysProcess_WaitForExit +# undef kwsysProcess_Interrupt +# undef kwsysProcess_Kill +# undef kwsysProcess_ResetStartTime +# endif +#endif + +#endif diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c new file mode 100644 index 0000000..5fde0b0 --- /dev/null +++ b/Source/kwsys/ProcessUNIX.c @@ -0,0 +1,2922 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Process.h) +#include KWSYS_HEADER(System.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Process.h.in" +# include "System.h.in" +#endif + +/* + +Implementation for UNIX + +On UNIX, a child process is forked to exec the program. Three output +pipes are read by the parent process using a select call to block +until data are ready. Two of the pipes are stdout and stderr for the +child. The third is a special pipe populated by a signal handler to +indicate that a child has terminated. This is used in conjunction +with the timeout on the select call to implement a timeout for program +even when it closes stdout and stderr and at the same time avoiding +races. + +*/ + +/* + +TODO: + +We cannot create the pipeline of processes in suspended states. How +do we cleanup processes already started when one fails to load? Right +now we are just killing them, which is probably not the right thing to +do. + +*/ + +#if defined(__CYGWIN__) +/* Increase the file descriptor limit for select() before including + related system headers. (Default: 64) */ +# define FD_SETSIZE 16384 +#endif + +#include <assert.h> /* assert */ +#include <ctype.h> /* isspace */ +#include <dirent.h> /* DIR, dirent */ +#include <errno.h> /* errno */ +#include <fcntl.h> /* fcntl */ +#include <signal.h> /* sigaction */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdio.h> /* snprintf */ +#include <stdlib.h> /* malloc, free */ +#include <string.h> /* strdup, strerror, memset */ +#include <sys/stat.h> /* open mode */ +#include <sys/time.h> /* struct timeval */ +#include <sys/types.h> /* pid_t, fd_set */ +#include <sys/wait.h> /* waitpid */ +#include <time.h> /* gettimeofday */ +#include <unistd.h> /* pipe, close, fork, execvp, select, _exit */ + +#if defined(__VMS) +# define KWSYSPE_VMS_NONBLOCK , O_NONBLOCK +#else +# define KWSYSPE_VMS_NONBLOCK +#endif + +#if defined(KWSYS_C_HAS_PTRDIFF_T) && KWSYS_C_HAS_PTRDIFF_T +typedef ptrdiff_t kwsysProcess_ptrdiff_t; +#else +typedef int kwsysProcess_ptrdiff_t; +#endif + +#if defined(KWSYS_C_HAS_SSIZE_T) && KWSYS_C_HAS_SSIZE_T +typedef ssize_t kwsysProcess_ssize_t; +#else +typedef int kwsysProcess_ssize_t; +#endif + +#if defined(__BEOS__) && !defined(__ZETA__) +/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */ +# include <be/kernel/OS.h> +static inline void kwsysProcess_usleep(unsigned int msec) +{ + snooze(msec); +} +#else +# define kwsysProcess_usleep usleep +#endif + +/* + * BeOS's select() works like WinSock: it's for networking only, and + * doesn't work with Unix file handles...socket and file handles are + * different namespaces (the same descriptor means different things in + * each context!) + * + * So on Unix-like systems where select() is flakey, we'll set the + * pipes' file handles to be non-blocking and just poll them directly + * without select(). + */ +#if !defined(__BEOS__) && !defined(__VMS) && !defined(__MINT__) && \ + !defined(KWSYSPE_USE_SELECT) +# define KWSYSPE_USE_SELECT 1 +#endif + +/* Some platforms do not have siginfo on their signal handlers. */ +#if defined(SA_SIGINFO) && !defined(__BEOS__) +# define KWSYSPE_USE_SIGINFO 1 +#endif + +/* The number of pipes for the child's output. The standard stdout + and stderr pipes are the first two. One more pipe is used to + detect when the child process has terminated. The third pipe is + not given to the child process, so it cannot close it until it + terminates. */ +#define KWSYSPE_PIPE_COUNT 3 +#define KWSYSPE_PIPE_STDOUT 0 +#define KWSYSPE_PIPE_STDERR 1 +#define KWSYSPE_PIPE_SIGNAL 2 + +/* The maximum amount to read from a pipe at a time. */ +#define KWSYSPE_PIPE_BUFFER_SIZE 1024 + +/* Keep track of times using a signed representation. Switch to the + native (possibly unsigned) representation only when calling native + functions. */ +typedef struct timeval kwsysProcessTimeNative; +typedef struct kwsysProcessTime_s kwsysProcessTime; +struct kwsysProcessTime_s +{ + long tv_sec; + long tv_usec; +}; + +typedef struct kwsysProcessCreateInformation_s +{ + int StdIn; + int StdOut; + int StdErr; + int ErrorPipe[2]; +} kwsysProcessCreateInformation; + +static void kwsysProcessVolatileFree(volatile void* p); +static int kwsysProcessInitialize(kwsysProcess* cp); +static void kwsysProcessCleanup(kwsysProcess* cp, int error); +static void kwsysProcessCleanupDescriptor(int* pfd); +static void kwsysProcessClosePipes(kwsysProcess* cp); +static int kwsysProcessSetNonBlocking(int fd); +static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, + kwsysProcessCreateInformation* si); +static void kwsysProcessDestroy(kwsysProcess* cp); +static int kwsysProcessSetupOutputPipeFile(int* p, const char* name); +static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]); +static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, + const double* userTimeout, + kwsysProcessTime* timeoutTime); +static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, + const double* userTimeout, + kwsysProcessTimeNative* timeoutLength, + int zeroIsExpired); +static kwsysProcessTime kwsysProcessTimeGetCurrent(void); +static double kwsysProcessTimeToDouble(kwsysProcessTime t); +static kwsysProcessTime kwsysProcessTimeFromDouble(double d); +static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2); +static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, + kwsysProcessTime in2); +static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, + kwsysProcessTime in2); +static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int sig, + int idx); +static void kwsysProcessChildErrorExit(int errorPipe); +static void kwsysProcessRestoreDefaultSignalHandlers(void); +static pid_t kwsysProcessFork(kwsysProcess* cp, + kwsysProcessCreateInformation* si); +static void kwsysProcessKill(pid_t process_id); +#if defined(__VMS) +static int kwsysProcessSetVMSFeature(const char* name, int value); +#endif +static int kwsysProcessesAdd(kwsysProcess* cp); +static void kwsysProcessesRemove(kwsysProcess* cp); +#if KWSYSPE_USE_SIGINFO +static void kwsysProcessesSignalHandler(int signum, siginfo_t* info, + void* ucontext); +#else +static void kwsysProcessesSignalHandler(int signum); +#endif + +/* A structure containing results data for each process. */ +typedef struct kwsysProcessResults_s kwsysProcessResults; +struct kwsysProcessResults_s +{ + /* The status of the child process. */ + int State; + + /* The exceptional behavior that terminated the process, if any. */ + int ExitException; + + /* The process exit code. */ + int ExitCode; + + /* The process return code, if any. */ + int ExitValue; + + /* Description for the ExitException. */ + char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE + 1]; +}; + +/* Structure containing data used to implement the child's execution. */ +struct kwsysProcess_s +{ + /* The command lines to execute. */ + char*** Commands; + volatile int NumberOfCommands; + + /* Descriptors for the read ends of the child's output pipes and + the signal pipe. */ + int PipeReadEnds[KWSYSPE_PIPE_COUNT]; + + /* Descriptors for the child's ends of the pipes. + Used temporarily during process creation. */ + int PipeChildStd[3]; + + /* Write descriptor for child termination signal pipe. */ + int SignalPipe; + + /* Buffer for pipe data. */ + char PipeBuffer[KWSYSPE_PIPE_BUFFER_SIZE]; + + /* Process IDs returned by the calls to fork. Everything is volatile + because the signal handler accesses them. You must be very careful + when reaping PIDs or modifying this array to avoid race conditions. */ + volatile pid_t* volatile ForkPIDs; + + /* Flag for whether the children were terminated by a failed select. */ + int SelectError; + + /* The timeout length. */ + double Timeout; + + /* The working directory for the process. */ + char* WorkingDirectory; + + /* Whether to create the child as a detached process. */ + int OptionDetach; + + /* Whether the child was created as a detached process. */ + int Detached; + + /* Whether to treat command lines as verbatim. */ + int Verbatim; + + /* Whether to merge stdout/stderr of the child. */ + int MergeOutput; + + /* Whether to create the process in a new process group. */ + volatile sig_atomic_t CreateProcessGroup; + + /* Time at which the child started. Negative for no timeout. */ + kwsysProcessTime StartTime; + + /* Time at which the child will timeout. Negative for no timeout. */ + kwsysProcessTime TimeoutTime; + + /* Flag for whether the timeout expired. */ + int TimeoutExpired; + + /* The number of pipes left open during execution. */ + int PipesLeft; + +#if KWSYSPE_USE_SELECT + /* File descriptor set for call to select. */ + fd_set PipeSet; +#endif + + /* The number of children still executing. */ + int CommandsLeft; + + /* The status of the process structure. Must be atomic because + the signal handler checks this to avoid a race. */ + volatile sig_atomic_t State; + + /* Whether the process was killed. */ + volatile sig_atomic_t Killed; + + /* Buffer for error message in case of failure. */ + char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE + 1]; + + /* process results. */ + kwsysProcessResults* ProcessResults; + + /* The exit codes of each child process in the pipeline. */ + int* CommandExitCodes; + + /* Name of files to which stdin and stdout pipes are attached. */ + char* PipeFileSTDIN; + char* PipeFileSTDOUT; + char* PipeFileSTDERR; + + /* Whether each pipe is shared with the parent process. */ + int PipeSharedSTDIN; + int PipeSharedSTDOUT; + int PipeSharedSTDERR; + + /* Native pipes provided by the user. */ + int PipeNativeSTDIN[2]; + int PipeNativeSTDOUT[2]; + int PipeNativeSTDERR[2]; + + /* The real working directory of this process. */ + int RealWorkingDirectoryLength; + char* RealWorkingDirectory; +}; + +kwsysProcess* kwsysProcess_New(void) +{ + /* Allocate a process control structure. */ + kwsysProcess* cp = (kwsysProcess*)malloc(sizeof(kwsysProcess)); + if (!cp) { + return 0; + } + memset(cp, 0, sizeof(kwsysProcess)); + + /* Share stdin with the parent process by default. */ + cp->PipeSharedSTDIN = 1; + + /* No native pipes by default. */ + cp->PipeNativeSTDIN[0] = -1; + cp->PipeNativeSTDIN[1] = -1; + cp->PipeNativeSTDOUT[0] = -1; + cp->PipeNativeSTDOUT[1] = -1; + cp->PipeNativeSTDERR[0] = -1; + cp->PipeNativeSTDERR[1] = -1; + + /* Set initial status. */ + cp->State = kwsysProcess_State_Starting; + + return cp; +} + +void kwsysProcess_Delete(kwsysProcess* cp) +{ + /* Make sure we have an instance. */ + if (!cp) { + return; + } + + /* If the process is executing, wait for it to finish. */ + if (cp->State == kwsysProcess_State_Executing) { + if (cp->Detached) { + kwsysProcess_Disown(cp); + } else { + kwsysProcess_WaitForExit(cp, 0); + } + } + + /* Free memory. */ + kwsysProcess_SetCommand(cp, 0); + kwsysProcess_SetWorkingDirectory(cp, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0); + free(cp->CommandExitCodes); + free(cp->ProcessResults); + free(cp); +} + +int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command) +{ + int i; + if (!cp) { + return 0; + } + for (i = 0; i < cp->NumberOfCommands; ++i) { + char** c = cp->Commands[i]; + while (*c) { + free(*c++); + } + free(cp->Commands[i]); + } + cp->NumberOfCommands = 0; + if (cp->Commands) { + free(cp->Commands); + cp->Commands = 0; + } + if (command) { + return kwsysProcess_AddCommand(cp, command); + } + return 1; +} + +int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) +{ + int newNumberOfCommands; + char*** newCommands; + + /* Make sure we have a command to add. */ + if (!cp || !command || !*command) { + return 0; + } + + /* Allocate a new array for command pointers. */ + newNumberOfCommands = cp->NumberOfCommands + 1; + if (!(newCommands = + (char***)malloc(sizeof(char**) * (size_t)(newNumberOfCommands)))) { + /* Out of memory. */ + return 0; + } + + /* Copy any existing commands into the new array. */ + { + int i; + for (i = 0; i < cp->NumberOfCommands; ++i) { + newCommands[i] = cp->Commands[i]; + } + } + + /* Add the new command. */ + if (cp->Verbatim) { + /* In order to run the given command line verbatim we need to + parse it. */ + newCommands[cp->NumberOfCommands] = + kwsysSystem_Parse_CommandForUnix(*command, 0); + if (!newCommands[cp->NumberOfCommands] || + !newCommands[cp->NumberOfCommands][0]) { + /* Out of memory or no command parsed. */ + free(newCommands); + return 0; + } + } else { + /* Copy each argument string individually. */ + char const* const* c = command; + kwsysProcess_ptrdiff_t n = 0; + kwsysProcess_ptrdiff_t i = 0; + while (*c++) + ; + n = c - command - 1; + newCommands[cp->NumberOfCommands] = + (char**)malloc((size_t)(n + 1) * sizeof(char*)); + if (!newCommands[cp->NumberOfCommands]) { + /* Out of memory. */ + free(newCommands); + return 0; + } + for (i = 0; i < n; ++i) { + assert(command[i]); /* Quiet Clang scan-build. */ + newCommands[cp->NumberOfCommands][i] = strdup(command[i]); + if (!newCommands[cp->NumberOfCommands][i]) { + break; + } + } + if (i < n) { + /* Out of memory. */ + for (; i > 0; --i) { + free(newCommands[cp->NumberOfCommands][i - 1]); + } + free(newCommands); + return 0; + } + newCommands[cp->NumberOfCommands][n] = 0; + } + + /* Successfully allocated new command array. Free the old array. */ + free(cp->Commands); + cp->Commands = newCommands; + cp->NumberOfCommands = newNumberOfCommands; + + return 1; +} + +void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout) +{ + if (!cp) { + return; + } + cp->Timeout = timeout; + if (cp->Timeout < 0) { + cp->Timeout = 0; + } + // Force recomputation of TimeoutTime. + cp->TimeoutTime.tv_sec = -1; +} + +int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) +{ + if (!cp) { + return 0; + } + if (cp->WorkingDirectory == dir) { + return 1; + } + if (cp->WorkingDirectory && dir && strcmp(cp->WorkingDirectory, dir) == 0) { + return 1; + } + if (cp->WorkingDirectory) { + free(cp->WorkingDirectory); + cp->WorkingDirectory = 0; + } + if (dir) { + cp->WorkingDirectory = strdup(dir); + if (!cp->WorkingDirectory) { + return 0; + } + } + return 1; +} + +int kwsysProcess_SetPipeFile(kwsysProcess* cp, int prPipe, const char* file) +{ + char** pfile; + if (!cp) { + return 0; + } + switch (prPipe) { + case kwsysProcess_Pipe_STDIN: + pfile = &cp->PipeFileSTDIN; + break; + case kwsysProcess_Pipe_STDOUT: + pfile = &cp->PipeFileSTDOUT; + break; + case kwsysProcess_Pipe_STDERR: + pfile = &cp->PipeFileSTDERR; + break; + default: + return 0; + } + if (*pfile) { + free(*pfile); + *pfile = 0; + } + if (file) { + *pfile = strdup(file); + if (!*pfile) { + return 0; + } + } + + /* If we are redirecting the pipe, do not share it or use a native + pipe. */ + if (*pfile) { + kwsysProcess_SetPipeNative(cp, prPipe, 0); + kwsysProcess_SetPipeShared(cp, prPipe, 0); + } + return 1; +} + +void kwsysProcess_SetPipeShared(kwsysProcess* cp, int prPipe, int shared) +{ + if (!cp) { + return; + } + + switch (prPipe) { + case kwsysProcess_Pipe_STDIN: + cp->PipeSharedSTDIN = shared ? 1 : 0; + break; + case kwsysProcess_Pipe_STDOUT: + cp->PipeSharedSTDOUT = shared ? 1 : 0; + break; + case kwsysProcess_Pipe_STDERR: + cp->PipeSharedSTDERR = shared ? 1 : 0; + break; + default: + return; + } + + /* If we are sharing the pipe, do not redirect it to a file or use a + native pipe. */ + if (shared) { + kwsysProcess_SetPipeFile(cp, prPipe, 0); + kwsysProcess_SetPipeNative(cp, prPipe, 0); + } +} + +void kwsysProcess_SetPipeNative(kwsysProcess* cp, int prPipe, const int p[2]) +{ + int* pPipeNative = 0; + + if (!cp) { + return; + } + + switch (prPipe) { + case kwsysProcess_Pipe_STDIN: + pPipeNative = cp->PipeNativeSTDIN; + break; + case kwsysProcess_Pipe_STDOUT: + pPipeNative = cp->PipeNativeSTDOUT; + break; + case kwsysProcess_Pipe_STDERR: + pPipeNative = cp->PipeNativeSTDERR; + break; + default: + return; + } + + /* Copy the native pipe descriptors provided. */ + if (p) { + pPipeNative[0] = p[0]; + pPipeNative[1] = p[1]; + } else { + pPipeNative[0] = -1; + pPipeNative[1] = -1; + } + + /* If we are using a native pipe, do not share it or redirect it to + a file. */ + if (p) { + kwsysProcess_SetPipeFile(cp, prPipe, 0); + kwsysProcess_SetPipeShared(cp, prPipe, 0); + } +} + +int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) +{ + if (!cp) { + return 0; + } + + switch (optionId) { + case kwsysProcess_Option_Detach: + return cp->OptionDetach; + case kwsysProcess_Option_MergeOutput: + return cp->MergeOutput; + case kwsysProcess_Option_Verbatim: + return cp->Verbatim; + case kwsysProcess_Option_CreateProcessGroup: + return cp->CreateProcessGroup; + default: + return 0; + } +} + +void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) +{ + if (!cp) { + return; + } + + switch (optionId) { + case kwsysProcess_Option_Detach: + cp->OptionDetach = value; + break; + case kwsysProcess_Option_MergeOutput: + cp->MergeOutput = value; + break; + case kwsysProcess_Option_Verbatim: + cp->Verbatim = value; + break; + case kwsysProcess_Option_CreateProcessGroup: + cp->CreateProcessGroup = value; + break; + default: + break; + } +} + +int kwsysProcess_GetState(kwsysProcess* cp) +{ + return cp ? cp->State : kwsysProcess_State_Error; +} + +int kwsysProcess_GetExitException(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitException + : kwsysProcess_Exception_Other; +} + +int kwsysProcess_GetExitCode(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitCode + : 0; +} + +int kwsysProcess_GetExitValue(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitValue + : -1; +} + +const char* kwsysProcess_GetErrorString(kwsysProcess* cp) +{ + if (!cp) { + return "Process management structure could not be allocated"; + } else if (cp->State == kwsysProcess_State_Error) { + return cp->ErrorMessage; + } + return "Success"; +} + +const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) +{ + if (!(cp && cp->ProcessResults && (cp->NumberOfCommands > 0))) { + return "GetExceptionString called with NULL process management structure"; + } else if (cp->State == kwsysProcess_State_Exception) { + return cp->ProcessResults[cp->NumberOfCommands - 1].ExitExceptionString; + } + return "No exception"; +} + +/* the index should be in array bound. */ +#define KWSYSPE_IDX_CHK(RET) \ + if (!cp || idx >= cp->NumberOfCommands || idx < 0) { \ + return RET; \ + } + +int kwsysProcess_GetStateByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(kwsysProcess_State_Error) + return cp->ProcessResults[idx].State; +} + +int kwsysProcess_GetExitExceptionByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(kwsysProcess_Exception_Other) + return cp->ProcessResults[idx].ExitException; +} + +int kwsysProcess_GetExitValueByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(-1) + return cp->ProcessResults[idx].ExitValue; +} + +int kwsysProcess_GetExitCodeByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(-1) + return cp->CommandExitCodes[idx]; +} + +const char* kwsysProcess_GetExceptionStringByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK("GetExceptionString called with NULL process management " + "structure or index out of bound") + if (cp->ProcessResults[idx].State == kwsysProcess_StateByIndex_Exception) { + return cp->ProcessResults[idx].ExitExceptionString; + } + return "No exception"; +} + +#undef KWSYSPE_IDX_CHK + +void kwsysProcess_Execute(kwsysProcess* cp) +{ + int i; + + /* Do not execute a second copy simultaneously. */ + if (!cp || cp->State == kwsysProcess_State_Executing) { + return; + } + + /* Make sure we have something to run. */ + if (cp->NumberOfCommands < 1) { + strcpy(cp->ErrorMessage, "No command"); + cp->State = kwsysProcess_State_Error; + return; + } + + /* Initialize the control structure for a new process. */ + if (!kwsysProcessInitialize(cp)) { + strcpy(cp->ErrorMessage, "Out of memory"); + cp->State = kwsysProcess_State_Error; + return; + } + +#if defined(__VMS) + /* Make sure pipes behave like streams on VMS. */ + if (!kwsysProcessSetVMSFeature("DECC$STREAM_PIPE", 1)) { + kwsysProcessCleanup(cp, 1); + return; + } +#endif + + /* Save the real working directory of this process and change to + the working directory for the child processes. This is needed + to make pipe file paths evaluate correctly. */ + if (cp->WorkingDirectory) { + int r; + if (!getcwd(cp->RealWorkingDirectory, + (size_t)(cp->RealWorkingDirectoryLength))) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Some platforms specify that the chdir call may be + interrupted. Repeat the call until it finishes. */ + while (((r = chdir(cp->WorkingDirectory)) < 0) && (errno == EINTR)) + ; + if (r < 0) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + /* If not running a detached child, add this object to the global + set of process objects that wish to be notified when a child + exits. */ + if (!cp->OptionDetach) { + if (!kwsysProcessesAdd(cp)) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + /* Setup the stdin pipe for the first process. */ + if (cp->PipeFileSTDIN) { + /* Open a file for the child's stdin to read. */ + cp->PipeChildStd[0] = open(cp->PipeFileSTDIN, O_RDONLY); + if (cp->PipeChildStd[0] < 0) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Set close-on-exec flag on the pipe's end. */ + if (fcntl(cp->PipeChildStd[0], F_SETFD, FD_CLOEXEC) < 0) { + kwsysProcessCleanup(cp, 1); + return; + } + } else if (cp->PipeSharedSTDIN) { + cp->PipeChildStd[0] = 0; + } else if (cp->PipeNativeSTDIN[0] >= 0) { + cp->PipeChildStd[0] = cp->PipeNativeSTDIN[0]; + + /* Set close-on-exec flag on the pipe's ends. The read end will + be dup2-ed into the stdin descriptor after the fork but before + the exec. */ + if ((fcntl(cp->PipeNativeSTDIN[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(cp->PipeNativeSTDIN[1], F_SETFD, FD_CLOEXEC) < 0)) { + kwsysProcessCleanup(cp, 1); + return; + } + } else { + cp->PipeChildStd[0] = -1; + } + + /* Create the output pipe for the last process. + We always create this so the pipe can be passed to select even if + it will report closed immediately. */ + { + /* Create the pipe. */ + int p[2]; + if (pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Store the pipe. */ + cp->PipeReadEnds[KWSYSPE_PIPE_STDOUT] = p[0]; + cp->PipeChildStd[1] = p[1]; + + /* Set close-on-exec flag on the pipe's ends. */ + if ((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Set to non-blocking in case select lies, or for the polling + implementation. */ + if (!kwsysProcessSetNonBlocking(p[0])) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + if (cp->PipeFileSTDOUT) { + /* Use a file for stdout. */ + if (!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], + cp->PipeFileSTDOUT)) { + kwsysProcessCleanup(cp, 1); + return; + } + } else if (cp->PipeSharedSTDOUT) { + /* Use the parent stdout. */ + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[1]); + cp->PipeChildStd[1] = 1; + } else if (cp->PipeNativeSTDOUT[1] >= 0) { + /* Use the given descriptor for stdout. */ + if (!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[1], + cp->PipeNativeSTDOUT)) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + /* Create stderr pipe to be shared by all processes in the pipeline. + We always create this so the pipe can be passed to select even if + it will report closed immediately. */ + { + /* Create the pipe. */ + int p[2]; + if (pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Store the pipe. */ + cp->PipeReadEnds[KWSYSPE_PIPE_STDERR] = p[0]; + cp->PipeChildStd[2] = p[1]; + + /* Set close-on-exec flag on the pipe's ends. */ + if ((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { + kwsysProcessCleanup(cp, 1); + return; + } + + /* Set to non-blocking in case select lies, or for the polling + implementation. */ + if (!kwsysProcessSetNonBlocking(p[0])) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + if (cp->PipeFileSTDERR) { + /* Use a file for stderr. */ + if (!kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], + cp->PipeFileSTDERR)) { + kwsysProcessCleanup(cp, 1); + return; + } + } else if (cp->PipeSharedSTDERR) { + /* Use the parent stderr. */ + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[2]); + cp->PipeChildStd[2] = 2; + } else if (cp->PipeNativeSTDERR[1] >= 0) { + /* Use the given handle for stderr. */ + if (!kwsysProcessSetupOutputPipeNative(&cp->PipeChildStd[2], + cp->PipeNativeSTDERR)) { + kwsysProcessCleanup(cp, 1); + return; + } + } + + /* The timeout period starts now. */ + cp->StartTime = kwsysProcessTimeGetCurrent(); + cp->TimeoutTime.tv_sec = -1; + cp->TimeoutTime.tv_usec = -1; + + /* Create the pipeline of processes. */ + { + kwsysProcessCreateInformation si = { -1, -1, -1, { -1, -1 } }; + int nextStdIn = cp->PipeChildStd[0]; + for (i = 0; i < cp->NumberOfCommands; ++i) { + /* Setup the process's pipes. */ + si.StdIn = nextStdIn; + if (i == cp->NumberOfCommands - 1) { + nextStdIn = -1; + si.StdOut = cp->PipeChildStd[1]; + } else { + /* Create a pipe to sit between the children. */ + int p[2] = { -1, -1 }; + if (pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { + if (nextStdIn != cp->PipeChildStd[0]) { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); + return; + } + + /* Set close-on-exec flag on the pipe's ends. */ + if ((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { + close(p[0]); + close(p[1]); + if (nextStdIn != cp->PipeChildStd[0]) { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); + return; + } + nextStdIn = p[0]; + si.StdOut = p[1]; + } + si.StdErr = cp->MergeOutput ? cp->PipeChildStd[1] : cp->PipeChildStd[2]; + + { + int res = kwsysProcessCreate(cp, i, &si); + + /* Close our copies of pipes used between children. */ + if (si.StdIn != cp->PipeChildStd[0]) { + kwsysProcessCleanupDescriptor(&si.StdIn); + } + if (si.StdOut != cp->PipeChildStd[1]) { + kwsysProcessCleanupDescriptor(&si.StdOut); + } + if (si.StdErr != cp->PipeChildStd[2] && !cp->MergeOutput) { + kwsysProcessCleanupDescriptor(&si.StdErr); + } + + if (!res) { + kwsysProcessCleanupDescriptor(&si.ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&si.ErrorPipe[1]); + if (nextStdIn != cp->PipeChildStd[0]) { + kwsysProcessCleanupDescriptor(&nextStdIn); + } + kwsysProcessCleanup(cp, 1); + return; + } + } + } + } + + /* The parent process does not need the child's pipe ends. */ + for (i = 0; i < 3; ++i) { + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); + } + + /* Restore the working directory. */ + if (cp->RealWorkingDirectory) { + /* Some platforms specify that the chdir call may be + interrupted. Repeat the call until it finishes. */ + while ((chdir(cp->RealWorkingDirectory) < 0) && (errno == EINTR)) + ; + free(cp->RealWorkingDirectory); + cp->RealWorkingDirectory = 0; + } + + /* All the pipes are now open. */ + cp->PipesLeft = KWSYSPE_PIPE_COUNT; + + /* The process has now started. */ + cp->State = kwsysProcess_State_Executing; + cp->Detached = cp->OptionDetach; +} + +kwsysEXPORT void kwsysProcess_Disown(kwsysProcess* cp) +{ + /* Make sure a detached child process is running. */ + if (!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing || + cp->TimeoutExpired || cp->Killed) { + return; + } + + /* Close all the pipes safely. */ + kwsysProcessClosePipes(cp); + + /* We will not wait for exit, so cleanup now. */ + kwsysProcessCleanup(cp, 0); + + /* The process has been disowned. */ + cp->State = kwsysProcess_State_Disowned; +} + +typedef struct kwsysProcessWaitData_s +{ + int Expired; + int PipeId; + int User; + double* UserTimeout; + kwsysProcessTime TimeoutTime; +} kwsysProcessWaitData; +static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length, + kwsysProcessWaitData* wd); + +int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, + double* userTimeout) +{ + kwsysProcessTime userStartTime = { 0, 0 }; + kwsysProcessWaitData wd = { 0, kwsysProcess_Pipe_None, 0, 0, { 0, 0 } }; + wd.UserTimeout = userTimeout; + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing || cp->Killed || + cp->TimeoutExpired) { + return kwsysProcess_Pipe_None; + } + + /* Record the time at which user timeout period starts. */ + if (userTimeout) { + userStartTime = kwsysProcessTimeGetCurrent(); + } + + /* Calculate the time at which a timeout will expire, and whether it + is the user or process timeout. */ + wd.User = kwsysProcessGetTimeoutTime(cp, userTimeout, &wd.TimeoutTime); + + /* Data can only be available when pipes are open. If the process + is not running, cp->PipesLeft will be 0. */ + while (cp->PipesLeft > 0 && + !kwsysProcessWaitForPipe(cp, data, length, &wd)) { + } + + /* Update the user timeout. */ + if (userTimeout) { + kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent(); + kwsysProcessTime difference = + kwsysProcessTimeSubtract(userEndTime, userStartTime); + double d = kwsysProcessTimeToDouble(difference); + *userTimeout -= d; + if (*userTimeout < 0) { + *userTimeout = 0; + } + } + + /* Check what happened. */ + if (wd.PipeId) { + /* Data are ready on a pipe. */ + return wd.PipeId; + } else if (wd.Expired) { + /* A timeout has expired. */ + if (wd.User) { + /* The user timeout has expired. It has no time left. */ + return kwsysProcess_Pipe_Timeout; + } else { + /* The process timeout has expired. Kill the children now. */ + kwsysProcess_Kill(cp); + cp->Killed = 0; + cp->TimeoutExpired = 1; + return kwsysProcess_Pipe_None; + } + } else { + /* No pipes are left open. */ + return kwsysProcess_Pipe_None; + } +} + +static int kwsysProcessWaitForPipe(kwsysProcess* cp, char** data, int* length, + kwsysProcessWaitData* wd) +{ + int i; + kwsysProcessTimeNative timeoutLength; + +#if KWSYSPE_USE_SELECT + int numReady = 0; + int max = -1; + kwsysProcessTimeNative* timeout = 0; + + /* Check for any open pipes with data reported ready by the last + call to select. According to "man select_tut" we must deal + with all descriptors reported by a call to select before + passing them to another select call. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + if (cp->PipeReadEnds[i] >= 0 && + FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet)) { + kwsysProcess_ssize_t n; + + /* We are handling this pipe now. Remove it from the set. */ + FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet); + + /* The pipe is ready to read without blocking. Keep trying to + read until the operation is not interrupted. */ + while (((n = read(cp->PipeReadEnds[i], cp->PipeBuffer, + KWSYSPE_PIPE_BUFFER_SIZE)) < 0) && + (errno == EINTR)) + ; + if (n > 0) { + /* We have data on this pipe. */ + if (i == KWSYSPE_PIPE_SIGNAL) { + /* A child process has terminated. */ + kwsysProcessDestroy(cp); + } else if (data && length) { + /* Report this data. */ + *data = cp->PipeBuffer; + *length = (int)(n); + switch (i) { + case KWSYSPE_PIPE_STDOUT: + wd->PipeId = kwsysProcess_Pipe_STDOUT; + break; + case KWSYSPE_PIPE_STDERR: + wd->PipeId = kwsysProcess_Pipe_STDERR; + break; + } + return 1; + } + } else if (n < 0 && errno == EAGAIN) { + /* No data are really ready. The select call lied. See the + "man select" page on Linux for cases when this occurs. */ + } else { + /* We are done reading from this pipe. */ + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + --cp->PipesLeft; + } + } + } + + /* If we have data, break early. */ + if (wd->PipeId) { + return 1; + } + + /* Make sure the set is empty (it should always be empty here + anyway). */ + FD_ZERO(&cp->PipeSet); + + /* Setup a timeout if required. */ + if (wd->TimeoutTime.tv_sec < 0) { + timeout = 0; + } else { + timeout = &timeoutLength; + } + if (kwsysProcessGetTimeoutLeft( + &wd->TimeoutTime, wd->User ? wd->UserTimeout : 0, &timeoutLength, 0)) { + /* Timeout has already expired. */ + wd->Expired = 1; + return 1; + } + + /* Add the pipe reading ends that are still open. */ + max = -1; + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + if (cp->PipeReadEnds[i] >= 0) { + FD_SET(cp->PipeReadEnds[i], &cp->PipeSet); + if (cp->PipeReadEnds[i] > max) { + max = cp->PipeReadEnds[i]; + } + } + } + + /* Make sure we have a non-empty set. */ + if (max < 0) { + /* All pipes have closed. Child has terminated. */ + return 1; + } + + /* Run select to block until data are available. Repeat call + until it is not interrupted. */ + while (((numReady = select(max + 1, &cp->PipeSet, 0, 0, timeout)) < 0) && + (errno == EINTR)) + ; + + /* Check result of select. */ + if (numReady == 0) { + /* Select's timeout expired. */ + wd->Expired = 1; + return 1; + } else if (numReady < 0) { + /* Select returned an error. Leave the error description in the + pipe buffer. */ + strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + + /* Kill the children now. */ + kwsysProcess_Kill(cp); + cp->Killed = 0; + cp->SelectError = 1; + } + + return 0; +#else + /* Poll pipes for data since we do not have select. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + if (cp->PipeReadEnds[i] >= 0) { + const int fd = cp->PipeReadEnds[i]; + int n = read(fd, cp->PipeBuffer, KWSYSPE_PIPE_BUFFER_SIZE); + if (n > 0) { + /* We have data on this pipe. */ + if (i == KWSYSPE_PIPE_SIGNAL) { + /* A child process has terminated. */ + kwsysProcessDestroy(cp); + } else if (data && length) { + /* Report this data. */ + *data = cp->PipeBuffer; + *length = n; + switch (i) { + case KWSYSPE_PIPE_STDOUT: + wd->PipeId = kwsysProcess_Pipe_STDOUT; + break; + case KWSYSPE_PIPE_STDERR: + wd->PipeId = kwsysProcess_Pipe_STDERR; + break; + }; + } + return 1; + } else if (n == 0) /* EOF */ + { +/* We are done reading from this pipe. */ +# if defined(__VMS) + if (!cp->CommandsLeft) +# endif + { + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + --cp->PipesLeft; + } + } else if (n < 0) /* error */ + { +# if defined(__VMS) + if (!cp->CommandsLeft) { + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + --cp->PipesLeft; + } else +# endif + if ((errno != EINTR) && (errno != EAGAIN)) { + strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + /* Kill the children now. */ + kwsysProcess_Kill(cp); + cp->Killed = 0; + cp->SelectError = 1; + return 1; + } + } + } + } + + /* If we have data, break early. */ + if (wd->PipeId) { + return 1; + } + + if (kwsysProcessGetTimeoutLeft( + &wd->TimeoutTime, wd->User ? wd->UserTimeout : 0, &timeoutLength, 1)) { + /* Timeout has already expired. */ + wd->Expired = 1; + return 1; + } + + /* Sleep a little, try again. */ + { + unsigned int msec = + ((timeoutLength.tv_sec * 1000) + (timeoutLength.tv_usec / 1000)); + if (msec > 100000) { + msec = 100000; /* do not sleep more than 100 milliseconds at a time */ + } + kwsysProcess_usleep(msec); + } + return 0; +#endif +} + +int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) +{ + int prPipe = 0; + + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing) { + return 1; + } + + /* Wait for all the pipes to close. Ignore all data. */ + while ((prPipe = kwsysProcess_WaitForData(cp, 0, 0, userTimeout)) > 0) { + if (prPipe == kwsysProcess_Pipe_Timeout) { + return 0; + } + } + + /* Check if there was an error in one of the waitpid calls. */ + if (cp->State == kwsysProcess_State_Error) { + /* The error message is already in its buffer. Tell + kwsysProcessCleanup to not create it. */ + kwsysProcessCleanup(cp, 0); + return 1; + } + + /* Check whether the child reported an error invoking the process. */ + if (cp->SelectError) { + /* The error message is already in its buffer. Tell + kwsysProcessCleanup to not create it. */ + kwsysProcessCleanup(cp, 0); + cp->State = kwsysProcess_State_Error; + return 1; + } + /* Determine the outcome. */ + if (cp->Killed) { + /* We killed the child. */ + cp->State = kwsysProcess_State_Killed; + } else if (cp->TimeoutExpired) { + /* The timeout expired. */ + cp->State = kwsysProcess_State_Expired; + } else { + /* The children exited. Report the outcome of the child processes. */ + for (prPipe = 0; prPipe < cp->NumberOfCommands; ++prPipe) { + cp->ProcessResults[prPipe].ExitCode = cp->CommandExitCodes[prPipe]; + if (WIFEXITED(cp->ProcessResults[prPipe].ExitCode)) { + /* The child exited normally. */ + cp->ProcessResults[prPipe].State = kwsysProcess_StateByIndex_Exited; + cp->ProcessResults[prPipe].ExitException = kwsysProcess_Exception_None; + cp->ProcessResults[prPipe].ExitValue = + (int)WEXITSTATUS(cp->ProcessResults[prPipe].ExitCode); + } else if (WIFSIGNALED(cp->ProcessResults[prPipe].ExitCode)) { + /* The child received an unhandled signal. */ + cp->ProcessResults[prPipe].State = kwsysProcess_State_Exception; + kwsysProcessSetExitExceptionByIndex( + cp, (int)WTERMSIG(cp->ProcessResults[prPipe].ExitCode), prPipe); + } else { + /* Error getting the child return code. */ + strcpy(cp->ProcessResults[prPipe].ExitExceptionString, + "Error getting child return code."); + cp->ProcessResults[prPipe].State = kwsysProcess_StateByIndex_Error; + } + } + /* support legacy state status value */ + cp->State = cp->ProcessResults[cp->NumberOfCommands - 1].State; + } + /* Normal cleanup. */ + kwsysProcessCleanup(cp, 0); + return 1; +} + +void kwsysProcess_Interrupt(kwsysProcess* cp) +{ + int i; + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || + cp->Killed) { + return; + } + + /* Interrupt the children. */ + if (cp->CreateProcessGroup) { + if (cp->ForkPIDs) { + for (i = 0; i < cp->NumberOfCommands; ++i) { + /* Make sure the PID is still valid. */ + if (cp->ForkPIDs[i]) { + /* The user created a process group for this process. The group ID + is the process ID for the original process in the group. */ + kill(-cp->ForkPIDs[i], SIGINT); + } + } + } + } else { + /* No process group was created. Kill our own process group. + NOTE: While one could argue that we could call kill(cp->ForkPIDs[i], + SIGINT) as a way to still interrupt the process even though it's not in + a special group, this is not an option on Windows. Therefore, we kill + the current process group for consistency with Windows. */ + kill(0, SIGINT); + } +} + +void kwsysProcess_Kill(kwsysProcess* cp) +{ + int i; + + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing) { + return; + } + + /* First close the child exit report pipe write end to avoid causing a + SIGPIPE when the child terminates and our signal handler tries to + report it after we have already closed the read end. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); + +#if !defined(__APPLE__) + /* Close all the pipe read ends. Do this before killing the + children because Cygwin has problems killing processes that are + blocking to wait for writing to their output pipes. */ + kwsysProcessClosePipes(cp); +#endif + + /* Kill the children. */ + cp->Killed = 1; + for (i = 0; i < cp->NumberOfCommands; ++i) { + int status; + if (cp->ForkPIDs[i]) { + /* Kill the child. */ + kwsysProcessKill(cp->ForkPIDs[i]); + + /* Reap the child. Keep trying until the call is not + interrupted. */ + while ((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && (errno == EINTR)) + ; + } + } + +#if defined(__APPLE__) + /* Close all the pipe read ends. Do this after killing the + children because OS X has problems closing pipe read ends whose + pipes are full and still have an open write end. */ + kwsysProcessClosePipes(cp); +#endif + + cp->CommandsLeft = 0; +} + +/* Call the free() function with a pointer to volatile without causing + compiler warnings. */ +static void kwsysProcessVolatileFree(volatile void* p) +{ +/* clang has made it impossible to free memory that points to volatile + without first using special pragmas to disable a warning... */ +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-qual" +#endif + free((void*)p); /* The cast will silence most compilers, but not clang. */ +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic pop +#endif +} + +/* Initialize a process control structure for kwsysProcess_Execute. */ +static int kwsysProcessInitialize(kwsysProcess* cp) +{ + int i; + volatile pid_t* oldForkPIDs; + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + cp->PipeReadEnds[i] = -1; + } + for (i = 0; i < 3; ++i) { + cp->PipeChildStd[i] = -1; + } + cp->SignalPipe = -1; + cp->SelectError = 0; + cp->StartTime.tv_sec = -1; + cp->StartTime.tv_usec = -1; + cp->TimeoutTime.tv_sec = -1; + cp->TimeoutTime.tv_usec = -1; + cp->TimeoutExpired = 0; + cp->PipesLeft = 0; + cp->CommandsLeft = 0; +#if KWSYSPE_USE_SELECT + FD_ZERO(&cp->PipeSet); +#endif + cp->State = kwsysProcess_State_Starting; + cp->Killed = 0; + cp->ErrorMessage[0] = 0; + + oldForkPIDs = cp->ForkPIDs; + cp->ForkPIDs = (volatile pid_t*)malloc(sizeof(volatile pid_t) * + (size_t)(cp->NumberOfCommands)); + kwsysProcessVolatileFree(oldForkPIDs); + if (!cp->ForkPIDs) { + return 0; + } + for (i = 0; i < cp->NumberOfCommands; ++i) { + cp->ForkPIDs[i] = 0; /* can't use memset due to volatile */ + } + + free(cp->CommandExitCodes); + cp->CommandExitCodes = + (int*)malloc(sizeof(int) * (size_t)(cp->NumberOfCommands)); + if (!cp->CommandExitCodes) { + return 0; + } + memset(cp->CommandExitCodes, 0, + sizeof(int) * (size_t)(cp->NumberOfCommands)); + + /* Allocate process result information for each process. */ + free(cp->ProcessResults); + cp->ProcessResults = (kwsysProcessResults*)malloc( + sizeof(kwsysProcessResults) * (size_t)(cp->NumberOfCommands)); + if (!cp->ProcessResults) { + return 0; + } + memset(cp->ProcessResults, 0, + sizeof(kwsysProcessResults) * (size_t)(cp->NumberOfCommands)); + for (i = 0; i < cp->NumberOfCommands; i++) { + cp->ProcessResults[i].ExitException = kwsysProcess_Exception_None; + cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Starting; + cp->ProcessResults[i].ExitCode = 1; + cp->ProcessResults[i].ExitValue = 1; + strcpy(cp->ProcessResults[i].ExitExceptionString, "No exception"); + } + + /* Allocate memory to save the real working directory. */ + if (cp->WorkingDirectory) { +#if defined(MAXPATHLEN) + cp->RealWorkingDirectoryLength = MAXPATHLEN; +#elif defined(PATH_MAX) + cp->RealWorkingDirectoryLength = PATH_MAX; +#else + cp->RealWorkingDirectoryLength = 4096; +#endif + cp->RealWorkingDirectory = + (char*)malloc((size_t)(cp->RealWorkingDirectoryLength)); + if (!cp->RealWorkingDirectory) { + return 0; + } + } + + return 1; +} + +/* Free all resources used by the given kwsysProcess instance that were + allocated by kwsysProcess_Execute. */ +static void kwsysProcessCleanup(kwsysProcess* cp, int error) +{ + int i; + + if (error) { + /* We are cleaning up due to an error. Report the error message + if one has not been provided already. */ + if (cp->ErrorMessage[0] == 0) { + strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + } + + /* Set the error state. */ + cp->State = kwsysProcess_State_Error; + + /* Kill any children already started. */ + if (cp->ForkPIDs) { + int status; + for (i = 0; i < cp->NumberOfCommands; ++i) { + if (cp->ForkPIDs[i]) { + /* Kill the child. */ + kwsysProcessKill(cp->ForkPIDs[i]); + + /* Reap the child. Keep trying until the call is not + interrupted. */ + while ((waitpid(cp->ForkPIDs[i], &status, 0) < 0) && + (errno == EINTR)) + ; + } + } + } + + /* Restore the working directory. */ + if (cp->RealWorkingDirectory) { + while ((chdir(cp->RealWorkingDirectory) < 0) && (errno == EINTR)) + ; + } + } + + /* If not creating a detached child, remove this object from the + global set of process objects that wish to be notified when a + child exits. */ + if (!cp->OptionDetach) { + kwsysProcessesRemove(cp); + } + + /* Free memory. */ + if (cp->ForkPIDs) { + kwsysProcessVolatileFree(cp->ForkPIDs); + cp->ForkPIDs = 0; + } + if (cp->RealWorkingDirectory) { + free(cp->RealWorkingDirectory); + cp->RealWorkingDirectory = 0; + } + + /* Close pipe handles. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + } + for (i = 0; i < 3; ++i) { + kwsysProcessCleanupDescriptor(&cp->PipeChildStd[i]); + } +} + +/* Close the given file descriptor if it is open. Reset its value to -1. */ +static void kwsysProcessCleanupDescriptor(int* pfd) +{ + if (pfd && *pfd > 2) { + /* Keep trying to close until it is not interrupted by a + * signal. */ + while ((close(*pfd) < 0) && (errno == EINTR)) + ; + *pfd = -1; + } +} + +static void kwsysProcessClosePipes(kwsysProcess* cp) +{ + int i; + + /* Close any pipes that are still open. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + if (cp->PipeReadEnds[i] >= 0) { +#if KWSYSPE_USE_SELECT + /* If the pipe was reported by the last call to select, we must + read from it. This is needed to satisfy the suggestions from + "man select_tut" and is not needed for the polling + implementation. Ignore the data. */ + if (FD_ISSET(cp->PipeReadEnds[i], &cp->PipeSet)) { + /* We are handling this pipe now. Remove it from the set. */ + FD_CLR(cp->PipeReadEnds[i], &cp->PipeSet); + + /* The pipe is ready to read without blocking. Keep trying to + read until the operation is not interrupted. */ + while ((read(cp->PipeReadEnds[i], cp->PipeBuffer, + KWSYSPE_PIPE_BUFFER_SIZE) < 0) && + (errno == EINTR)) + ; + } +#endif + + /* We are done reading from this pipe. */ + kwsysProcessCleanupDescriptor(&cp->PipeReadEnds[i]); + --cp->PipesLeft; + } + } +} + +static int kwsysProcessSetNonBlocking(int fd) +{ + int flags = fcntl(fd, F_GETFL); + if (flags >= 0) { + flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } + return flags >= 0; +} + +#if defined(__VMS) +int decc$set_child_standard_streams(int fd1, int fd2, int fd3); +#endif + +static int kwsysProcessCreate(kwsysProcess* cp, int prIndex, + kwsysProcessCreateInformation* si) +{ + sigset_t mask, old_mask; + int pgidPipe[2]; + char tmp; + ssize_t readRes; + + /* Create the error reporting pipe. */ + if (pipe(si->ErrorPipe) < 0) { + return 0; + } + + /* Create a pipe for detecting that the child process has created a process + group and session. */ + if (pipe(pgidPipe) < 0) { + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); + return 0; + } + + /* Set close-on-exec flag on the pipe's write end. */ + if (fcntl(si->ErrorPipe[1], F_SETFD, FD_CLOEXEC) < 0 || + fcntl(pgidPipe[1], F_SETFD, FD_CLOEXEC) < 0) { + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); + kwsysProcessCleanupDescriptor(&pgidPipe[0]); + kwsysProcessCleanupDescriptor(&pgidPipe[1]); + return 0; + } + + /* Block SIGINT / SIGTERM while we start. The purpose is so that our signal + handler doesn't get called from the child process after the fork and + before the exec, and subsequently start kill()'ing PIDs from ForkPIDs. */ + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + if (sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0) { + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); + kwsysProcessCleanupDescriptor(&pgidPipe[0]); + kwsysProcessCleanupDescriptor(&pgidPipe[1]); + return 0; + } + +/* Fork off a child process. */ +#if defined(__VMS) + /* VMS needs vfork and execvp to be in the same function because + they use setjmp/longjmp to run the child startup code in the + parent! TODO: OptionDetach. Also + TODO: CreateProcessGroup. */ + cp->ForkPIDs[prIndex] = vfork(); +#else + cp->ForkPIDs[prIndex] = kwsysProcessFork(cp, si); +#endif + if (cp->ForkPIDs[prIndex] < 0) { + sigprocmask(SIG_SETMASK, &old_mask, 0); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); + kwsysProcessCleanupDescriptor(&pgidPipe[0]); + kwsysProcessCleanupDescriptor(&pgidPipe[1]); + return 0; + } + + if (cp->ForkPIDs[prIndex] == 0) { +#if defined(__VMS) + /* Specify standard pipes for child process. */ + decc$set_child_standard_streams(si->StdIn, si->StdOut, si->StdErr); +#else + /* Close the read end of the error reporting / process group + setup pipe. */ + close(si->ErrorPipe[0]); + close(pgidPipe[0]); + + /* Setup the stdin, stdout, and stderr pipes. */ + if (si->StdIn > 0) { + dup2(si->StdIn, 0); + } else if (si->StdIn < 0) { + close(0); + } + if (si->StdOut != 1) { + dup2(si->StdOut, 1); + } + if (si->StdErr != 2) { + dup2(si->StdErr, 2); + } + + /* Clear the close-on-exec flag for stdin, stdout, and stderr. + All other pipe handles will be closed when exec succeeds. */ + fcntl(0, F_SETFD, 0); + fcntl(1, F_SETFD, 0); + fcntl(2, F_SETFD, 0); + + /* Restore all default signal handlers. */ + kwsysProcessRestoreDefaultSignalHandlers(); + + /* Now that we have restored default signal handling and created the + process group, restore mask. */ + sigprocmask(SIG_SETMASK, &old_mask, 0); + + /* Create new process group. We use setsid instead of setpgid to avoid + the child getting hung up on signals like SIGTTOU. (In the real world, + this has been observed where "git svn" ends up calling the "resize" + program which opens /dev/tty. */ + if (cp->CreateProcessGroup && setsid() < 0) { + kwsysProcessChildErrorExit(si->ErrorPipe[1]); + } +#endif + + /* Execute the real process. If successful, this does not return. */ + execvp(cp->Commands[prIndex][0], cp->Commands[prIndex]); + /* TODO: What does VMS do if the child fails to start? */ + /* TODO: On VMS, how do we put the process in a new group? */ + + /* Failure. Report error to parent and terminate. */ + kwsysProcessChildErrorExit(si->ErrorPipe[1]); + } + +#if defined(__VMS) + /* Restore the standard pipes of this process. */ + decc$set_child_standard_streams(0, 1, 2); +#endif + + /* We are done with the error reporting pipe and process group setup pipe + write end. */ + kwsysProcessCleanupDescriptor(&si->ErrorPipe[1]); + kwsysProcessCleanupDescriptor(&pgidPipe[1]); + + /* Make sure the child is in the process group before we proceed. This + avoids race conditions with calls to the kill function that we make for + signalling process groups. */ + while ((readRes = read(pgidPipe[0], &tmp, 1)) > 0) + ; + if (readRes < 0) { + sigprocmask(SIG_SETMASK, &old_mask, 0); + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + kwsysProcessCleanupDescriptor(&pgidPipe[0]); + return 0; + } + kwsysProcessCleanupDescriptor(&pgidPipe[0]); + + /* Unmask signals. */ + if (sigprocmask(SIG_SETMASK, &old_mask, 0) < 0) { + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + return 0; + } + + /* A child has been created. */ + ++cp->CommandsLeft; + + /* Block until the child's exec call succeeds and closes the error + pipe or writes data to the pipe to report an error. */ + { + kwsysProcess_ssize_t total = 0; + kwsysProcess_ssize_t n = 1; + /* Read the entire error message up to the length of our buffer. */ + while (total < KWSYSPE_PIPE_BUFFER_SIZE && n > 0) { + /* Keep trying to read until the operation is not interrupted. */ + while (((n = read(si->ErrorPipe[0], cp->ErrorMessage + total, + (size_t)(KWSYSPE_PIPE_BUFFER_SIZE - total))) < 0) && + (errno == EINTR)) + ; + if (n > 0) { + total += n; + } + } + + /* We are done with the error reporting pipe read end. */ + kwsysProcessCleanupDescriptor(&si->ErrorPipe[0]); + + if (total > 0) { + /* The child failed to execute the process. */ + return 0; + } + } + + return 1; +} + +static void kwsysProcessDestroy(kwsysProcess* cp) +{ + /* A child process has terminated. Reap it if it is one handled by + this object. */ + int i; + /* Temporarily disable signals that access ForkPIDs. We don't want them to + read a reaped PID, and writes to ForkPIDs are not atomic. */ + sigset_t mask, old_mask; + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + if (sigprocmask(SIG_BLOCK, &mask, &old_mask) < 0) { + return; + } + + for (i = 0; i < cp->NumberOfCommands; ++i) { + if (cp->ForkPIDs[i]) { + int result; + while (((result = waitpid(cp->ForkPIDs[i], &cp->CommandExitCodes[i], + WNOHANG)) < 0) && + (errno == EINTR)) + ; + if (result > 0) { + /* This child has termianted. */ + cp->ForkPIDs[i] = 0; + if (--cp->CommandsLeft == 0) { + /* All children have terminated. Close the signal pipe + write end so that no more notifications are sent to this + object. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); + + /* TODO: Once the children have terminated, switch + WaitForData to use a non-blocking read to get the + rest of the data from the pipe. This is needed when + grandchildren keep the output pipes open. */ + } + } else if (result < 0 && cp->State != kwsysProcess_State_Error) { + /* Unexpected error. Report the first time this happens. */ + strncpy(cp->ErrorMessage, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + cp->State = kwsysProcess_State_Error; + } + } + } + + /* Re-enable signals. */ + sigprocmask(SIG_SETMASK, &old_mask, 0); +} + +static int kwsysProcessSetupOutputPipeFile(int* p, const char* name) +{ + int fout; + if (!name) { + return 1; + } + + /* Close the existing descriptor. */ + kwsysProcessCleanupDescriptor(p); + + /* Open a file for the pipe to write. */ + if ((fout = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { + return 0; + } + + /* Set close-on-exec flag on the pipe's end. */ + if (fcntl(fout, F_SETFD, FD_CLOEXEC) < 0) { + close(fout); + return 0; + } + + /* Assign the replacement descriptor. */ + *p = fout; + return 1; +} + +static int kwsysProcessSetupOutputPipeNative(int* p, int des[2]) +{ + /* Close the existing descriptor. */ + kwsysProcessCleanupDescriptor(p); + + /* Set close-on-exec flag on the pipe's ends. The proper end will + be dup2-ed into the standard descriptor number after fork but + before exec. */ + if ((fcntl(des[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(des[1], F_SETFD, FD_CLOEXEC) < 0)) { + return 0; + } + + /* Assign the replacement descriptor. */ + *p = des[1]; + return 1; +} + +/* Get the time at which either the process or user timeout will + expire. Returns 1 if the user timeout is first, and 0 otherwise. */ +static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, + const double* userTimeout, + kwsysProcessTime* timeoutTime) +{ + /* The first time this is called, we need to calculate the time at + which the child will timeout. */ + if (cp->Timeout > 0 && cp->TimeoutTime.tv_sec < 0) { + kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout); + cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length); + } + + /* Start with process timeout. */ + *timeoutTime = cp->TimeoutTime; + + /* Check if the user timeout is earlier. */ + if (userTimeout) { + kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); + kwsysProcessTime userTimeoutLength = + kwsysProcessTimeFromDouble(*userTimeout); + kwsysProcessTime userTimeoutTime = + kwsysProcessTimeAdd(currentTime, userTimeoutLength); + if (timeoutTime->tv_sec < 0 || + kwsysProcessTimeLess(userTimeoutTime, *timeoutTime)) { + *timeoutTime = userTimeoutTime; + return 1; + } + } + return 0; +} + +/* Get the length of time before the given timeout time arrives. + Returns 1 if the time has already arrived, and 0 otherwise. */ +static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, + const double* userTimeout, + kwsysProcessTimeNative* timeoutLength, + int zeroIsExpired) +{ + if (timeoutTime->tv_sec < 0) { + /* No timeout time has been requested. */ + return 0; + } else { + /* Calculate the remaining time. */ + kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); + kwsysProcessTime timeLeft = + kwsysProcessTimeSubtract(*timeoutTime, currentTime); + if (timeLeft.tv_sec < 0 && userTimeout && *userTimeout <= 0) { + /* Caller has explicitly requested a zero timeout. */ + timeLeft.tv_sec = 0; + timeLeft.tv_usec = 0; + } + + if (timeLeft.tv_sec < 0 || + (timeLeft.tv_sec == 0 && timeLeft.tv_usec == 0 && zeroIsExpired)) { + /* Timeout has already expired. */ + return 1; + } else { + /* There is some time left. */ + timeoutLength->tv_sec = timeLeft.tv_sec; + timeoutLength->tv_usec = timeLeft.tv_usec; + return 0; + } + } +} + +static kwsysProcessTime kwsysProcessTimeGetCurrent(void) +{ + kwsysProcessTime current; + kwsysProcessTimeNative current_native; +#if KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC + struct timespec current_timespec; + clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); + + current_native.tv_sec = current_timespec.tv_sec; + current_native.tv_usec = current_timespec.tv_nsec / 1000; +#else + gettimeofday(¤t_native, 0); +#endif + current.tv_sec = (long)current_native.tv_sec; + current.tv_usec = (long)current_native.tv_usec; + return current; +} + +static double kwsysProcessTimeToDouble(kwsysProcessTime t) +{ + return (double)t.tv_sec + (double)(t.tv_usec) * 0.000001; +} + +static kwsysProcessTime kwsysProcessTimeFromDouble(double d) +{ + kwsysProcessTime t; + t.tv_sec = (long)d; + t.tv_usec = (long)((d - (double)(t.tv_sec)) * 1000000); + return t; +} + +static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2) +{ + return ((in1.tv_sec < in2.tv_sec) || + ((in1.tv_sec == in2.tv_sec) && (in1.tv_usec < in2.tv_usec))); +} + +static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, + kwsysProcessTime in2) +{ + kwsysProcessTime out; + out.tv_sec = in1.tv_sec + in2.tv_sec; + out.tv_usec = in1.tv_usec + in2.tv_usec; + if (out.tv_usec >= 1000000) { + out.tv_usec -= 1000000; + out.tv_sec += 1; + } + return out; +} + +static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, + kwsysProcessTime in2) +{ + kwsysProcessTime out; + out.tv_sec = in1.tv_sec - in2.tv_sec; + out.tv_usec = in1.tv_usec - in2.tv_usec; + if (out.tv_usec < 0) { + out.tv_usec += 1000000; + out.tv_sec -= 1; + } + return out; +} + +#define KWSYSPE_CASE(type, str) \ + cp->ProcessResults[idx].ExitException = kwsysProcess_Exception_##type; \ + strcpy(cp->ProcessResults[idx].ExitExceptionString, str) +static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int sig, + int idx) +{ + switch (sig) { +#ifdef SIGSEGV + case SIGSEGV: + KWSYSPE_CASE(Fault, "Segmentation fault"); + break; +#endif +#ifdef SIGBUS +# if !defined(SIGSEGV) || SIGBUS != SIGSEGV + case SIGBUS: + KWSYSPE_CASE(Fault, "Bus error"); + break; +# endif +#endif +#ifdef SIGFPE + case SIGFPE: + KWSYSPE_CASE(Numerical, "Floating-point exception"); + break; +#endif +#ifdef SIGILL + case SIGILL: + KWSYSPE_CASE(Illegal, "Illegal instruction"); + break; +#endif +#ifdef SIGINT + case SIGINT: + KWSYSPE_CASE(Interrupt, "User interrupt"); + break; +#endif +#ifdef SIGABRT + case SIGABRT: + KWSYSPE_CASE(Other, "Child aborted"); + break; +#endif +#ifdef SIGKILL + case SIGKILL: + KWSYSPE_CASE(Other, "Child killed"); + break; +#endif +#ifdef SIGTERM + case SIGTERM: + KWSYSPE_CASE(Other, "Child terminated"); + break; +#endif +#ifdef SIGHUP + case SIGHUP: + KWSYSPE_CASE(Other, "SIGHUP"); + break; +#endif +#ifdef SIGQUIT + case SIGQUIT: + KWSYSPE_CASE(Other, "SIGQUIT"); + break; +#endif +#ifdef SIGTRAP + case SIGTRAP: + KWSYSPE_CASE(Other, "SIGTRAP"); + break; +#endif +#ifdef SIGIOT +# if !defined(SIGABRT) || SIGIOT != SIGABRT + case SIGIOT: + KWSYSPE_CASE(Other, "SIGIOT"); + break; +# endif +#endif +#ifdef SIGUSR1 + case SIGUSR1: + KWSYSPE_CASE(Other, "SIGUSR1"); + break; +#endif +#ifdef SIGUSR2 + case SIGUSR2: + KWSYSPE_CASE(Other, "SIGUSR2"); + break; +#endif +#ifdef SIGPIPE + case SIGPIPE: + KWSYSPE_CASE(Other, "SIGPIPE"); + break; +#endif +#ifdef SIGALRM + case SIGALRM: + KWSYSPE_CASE(Other, "SIGALRM"); + break; +#endif +#ifdef SIGSTKFLT + case SIGSTKFLT: + KWSYSPE_CASE(Other, "SIGSTKFLT"); + break; +#endif +#ifdef SIGCHLD + case SIGCHLD: + KWSYSPE_CASE(Other, "SIGCHLD"); + break; +#elif defined(SIGCLD) + case SIGCLD: + KWSYSPE_CASE(Other, "SIGCLD"); + break; +#endif +#ifdef SIGCONT + case SIGCONT: + KWSYSPE_CASE(Other, "SIGCONT"); + break; +#endif +#ifdef SIGSTOP + case SIGSTOP: + KWSYSPE_CASE(Other, "SIGSTOP"); + break; +#endif +#ifdef SIGTSTP + case SIGTSTP: + KWSYSPE_CASE(Other, "SIGTSTP"); + break; +#endif +#ifdef SIGTTIN + case SIGTTIN: + KWSYSPE_CASE(Other, "SIGTTIN"); + break; +#endif +#ifdef SIGTTOU + case SIGTTOU: + KWSYSPE_CASE(Other, "SIGTTOU"); + break; +#endif +#ifdef SIGURG + case SIGURG: + KWSYSPE_CASE(Other, "SIGURG"); + break; +#endif +#ifdef SIGXCPU + case SIGXCPU: + KWSYSPE_CASE(Other, "SIGXCPU"); + break; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: + KWSYSPE_CASE(Other, "SIGXFSZ"); + break; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: + KWSYSPE_CASE(Other, "SIGVTALRM"); + break; +#endif +#ifdef SIGPROF + case SIGPROF: + KWSYSPE_CASE(Other, "SIGPROF"); + break; +#endif +#ifdef SIGWINCH + case SIGWINCH: + KWSYSPE_CASE(Other, "SIGWINCH"); + break; +#endif +#ifdef SIGPOLL + case SIGPOLL: + KWSYSPE_CASE(Other, "SIGPOLL"); + break; +#endif +#ifdef SIGIO +# if !defined(SIGPOLL) || SIGIO != SIGPOLL + case SIGIO: + KWSYSPE_CASE(Other, "SIGIO"); + break; +# endif +#endif +#ifdef SIGPWR + case SIGPWR: + KWSYSPE_CASE(Other, "SIGPWR"); + break; +#endif +#ifdef SIGSYS + case SIGSYS: + KWSYSPE_CASE(Other, "SIGSYS"); + break; +#endif +#ifdef SIGUNUSED +# if !defined(SIGSYS) || SIGUNUSED != SIGSYS + case SIGUNUSED: + KWSYSPE_CASE(Other, "SIGUNUSED"); + break; +# endif +#endif + default: + cp->ProcessResults[idx].ExitException = kwsysProcess_Exception_Other; + sprintf(cp->ProcessResults[idx].ExitExceptionString, "Signal %d", sig); + break; + } +} +#undef KWSYSPE_CASE + +/* When the child process encounters an error before its program is + invoked, this is called to report the error to the parent and + exit. */ +static void kwsysProcessChildErrorExit(int errorPipe) +{ + /* Construct the error message. */ + char buffer[KWSYSPE_PIPE_BUFFER_SIZE]; + kwsysProcess_ssize_t result; + strncpy(buffer, strerror(errno), KWSYSPE_PIPE_BUFFER_SIZE); + buffer[KWSYSPE_PIPE_BUFFER_SIZE - 1] = '\0'; + + /* Report the error to the parent through the special pipe. */ + result = write(errorPipe, buffer, strlen(buffer)); + (void)result; + + /* Terminate without cleanup. */ + _exit(1); +} + +/* Restores all signal handlers to their default values. */ +static void kwsysProcessRestoreDefaultSignalHandlers(void) +{ + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_DFL; +#ifdef SIGHUP + sigaction(SIGHUP, &act, 0); +#endif +#ifdef SIGINT + sigaction(SIGINT, &act, 0); +#endif +#ifdef SIGQUIT + sigaction(SIGQUIT, &act, 0); +#endif +#ifdef SIGILL + sigaction(SIGILL, &act, 0); +#endif +#ifdef SIGTRAP + sigaction(SIGTRAP, &act, 0); +#endif +#ifdef SIGABRT + sigaction(SIGABRT, &act, 0); +#endif +#ifdef SIGIOT + sigaction(SIGIOT, &act, 0); +#endif +#ifdef SIGBUS + sigaction(SIGBUS, &act, 0); +#endif +#ifdef SIGFPE + sigaction(SIGFPE, &act, 0); +#endif +#ifdef SIGUSR1 + sigaction(SIGUSR1, &act, 0); +#endif +#ifdef SIGSEGV + sigaction(SIGSEGV, &act, 0); +#endif +#ifdef SIGUSR2 + sigaction(SIGUSR2, &act, 0); +#endif +#ifdef SIGPIPE + sigaction(SIGPIPE, &act, 0); +#endif +#ifdef SIGALRM + sigaction(SIGALRM, &act, 0); +#endif +#ifdef SIGTERM + sigaction(SIGTERM, &act, 0); +#endif +#ifdef SIGSTKFLT + sigaction(SIGSTKFLT, &act, 0); +#endif +#ifdef SIGCLD + sigaction(SIGCLD, &act, 0); +#endif +#ifdef SIGCHLD + sigaction(SIGCHLD, &act, 0); +#endif +#ifdef SIGCONT + sigaction(SIGCONT, &act, 0); +#endif +#ifdef SIGTSTP + sigaction(SIGTSTP, &act, 0); +#endif +#ifdef SIGTTIN + sigaction(SIGTTIN, &act, 0); +#endif +#ifdef SIGTTOU + sigaction(SIGTTOU, &act, 0); +#endif +#ifdef SIGURG + sigaction(SIGURG, &act, 0); +#endif +#ifdef SIGXCPU + sigaction(SIGXCPU, &act, 0); +#endif +#ifdef SIGXFSZ + sigaction(SIGXFSZ, &act, 0); +#endif +#ifdef SIGVTALRM + sigaction(SIGVTALRM, &act, 0); +#endif +#ifdef SIGPROF + sigaction(SIGPROF, &act, 0); +#endif +#ifdef SIGWINCH + sigaction(SIGWINCH, &act, 0); +#endif +#ifdef SIGPOLL + sigaction(SIGPOLL, &act, 0); +#endif +#ifdef SIGIO + sigaction(SIGIO, &act, 0); +#endif +#ifdef SIGPWR + sigaction(SIGPWR, &act, 0); +#endif +#ifdef SIGSYS + sigaction(SIGSYS, &act, 0); +#endif +#ifdef SIGUNUSED + sigaction(SIGUNUSED, &act, 0); +#endif +} + +static void kwsysProcessExit(void) +{ + _exit(0); +} + +#if !defined(__VMS) +static pid_t kwsysProcessFork(kwsysProcess* cp, + kwsysProcessCreateInformation* si) +{ + /* Create a detached process if requested. */ + if (cp->OptionDetach) { + /* Create an intermediate process. */ + pid_t middle_pid = fork(); + if (middle_pid < 0) { + /* Fork failed. Return as if we were not detaching. */ + return middle_pid; + } else if (middle_pid == 0) { + /* This is the intermediate process. Create the real child. */ + pid_t child_pid = fork(); + if (child_pid == 0) { + /* This is the real child process. There is nothing to do here. */ + return 0; + } else { + /* Use the error pipe to report the pid to the real parent. */ + while ((write(si->ErrorPipe[1], &child_pid, sizeof(child_pid)) < 0) && + (errno == EINTR)) + ; + + /* Exit without cleanup. The parent holds all resources. */ + kwsysProcessExit(); + return 0; /* Never reached, but avoids SunCC warning. */ + } + } else { + /* This is the original parent process. The intermediate + process will use the error pipe to report the pid of the + detached child. */ + pid_t child_pid; + int status; + while ((read(si->ErrorPipe[0], &child_pid, sizeof(child_pid)) < 0) && + (errno == EINTR)) + ; + + /* Wait for the intermediate process to exit and clean it up. */ + while ((waitpid(middle_pid, &status, 0) < 0) && (errno == EINTR)) + ; + return child_pid; + } + } else { + /* Not creating a detached process. Use normal fork. */ + return fork(); + } +} +#endif + +/* We try to obtain process information by invoking the ps command. + Here we define the command to call on each platform and the + corresponding parsing format string. The parsing format should + have two integers to store: the pid and then the ppid. */ +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__GLIBC__) || defined(__GNU__) +# define KWSYSPE_PS_COMMAND "ps axo pid,ppid" +# define KWSYSPE_PS_FORMAT "%d %d\n" +#elif defined(__sun) && (defined(__SVR4) || defined(__svr4__)) /* Solaris */ +# define KWSYSPE_PS_COMMAND "ps -e -o pid,ppid" +# define KWSYSPE_PS_FORMAT "%d %d\n" +#elif defined(__hpux) || defined(__sun__) || defined(__sgi) || \ + defined(_AIX) || defined(__sparc) +# define KWSYSPE_PS_COMMAND "ps -ef" +# define KWSYSPE_PS_FORMAT "%*s %d %d %*[^\n]\n" +#elif defined(__QNX__) +# define KWSYSPE_PS_COMMAND "ps -Af" +# define KWSYSPE_PS_FORMAT "%*d %d %d %*[^\n]\n" +#elif defined(__CYGWIN__) +# define KWSYSPE_PS_COMMAND "ps aux" +# define KWSYSPE_PS_FORMAT "%d %d %*[^\n]\n" +#endif + +void kwsysProcess_KillPID(unsigned long process_id) +{ + kwsysProcessKill((pid_t)process_id); +} + +static void kwsysProcessKill(pid_t process_id) +{ +#if defined(__linux__) || defined(__CYGWIN__) + DIR* procdir; +#endif + + /* Suspend the process to be sure it will not create more children. */ + kill(process_id, SIGSTOP); + +#if defined(__CYGWIN__) + /* Some Cygwin versions seem to need help here. Give up our time slice + so that the child can process SIGSTOP before we send SIGKILL. */ + usleep(1); +#endif + +/* Kill all children if we can find them. */ +#if defined(__linux__) || defined(__CYGWIN__) + /* First try using the /proc filesystem. */ + if ((procdir = opendir("/proc")) != NULL) { +# if defined(MAXPATHLEN) + char fname[MAXPATHLEN]; +# elif defined(PATH_MAX) + char fname[PATH_MAX]; +# else + char fname[4096]; +# endif + char buffer[KWSYSPE_PIPE_BUFFER_SIZE + 1]; + struct dirent* d; + + /* Each process has a directory in /proc whose name is the pid. + Within this directory is a file called stat that has the + following format: + + pid (command line) status ppid ... + + We want to get the ppid for all processes. Those that have + process_id as their parent should be recursively killed. */ + for (d = readdir(procdir); d; d = readdir(procdir)) { + int pid; + if (sscanf(d->d_name, "%d", &pid) == 1 && pid != 0) { + struct stat finfo; + sprintf(fname, "/proc/%d/stat", pid); + if (stat(fname, &finfo) == 0) { + FILE* f = fopen(fname, "r"); + if (f) { + size_t nread = fread(buffer, 1, KWSYSPE_PIPE_BUFFER_SIZE, f); + fclose(f); + buffer[nread] = '\0'; + if (nread > 0) { + const char* rparen = strrchr(buffer, ')'); + int ppid; + if (rparen && (sscanf(rparen + 1, "%*s %d", &ppid) == 1)) { + if (ppid == process_id) { + /* Recursively kill this child and its children. */ + kwsysProcessKill(pid); + } + } + } + } + } + } + } + closedir(procdir); + } else +#endif + { +#if defined(KWSYSPE_PS_COMMAND) + /* Try running "ps" to get the process information. */ + FILE* ps = popen(KWSYSPE_PS_COMMAND, "r"); + + /* Make sure the process started and provided a valid header. */ + if (ps && fscanf(ps, "%*[^\n]\n") != EOF) { + /* Look for processes whose parent is the process being killed. */ + int pid, ppid; + while (fscanf(ps, KWSYSPE_PS_FORMAT, &pid, &ppid) == 2) { + if (ppid == process_id) { + /* Recursively kill this child and its children. */ + kwsysProcessKill(pid); + } + } + } + + /* We are done with the ps process. */ + if (ps) { + pclose(ps); + } +#endif + } + + /* Kill the process. */ + kill(process_id, SIGKILL); + +#if defined(__APPLE__) + /* On OS X 10.3 the above SIGSTOP occasionally prevents the SIGKILL + from working. Just in case, we resume the child and kill it + again. There is a small race condition in this obscure case. If + the child manages to fork again between these two signals, we + will not catch its children. */ + kill(process_id, SIGCONT); + kill(process_id, SIGKILL); +#endif +} + +#if defined(__VMS) +int decc$feature_get_index(const char* name); +int decc$feature_set_value(int index, int mode, int value); +static int kwsysProcessSetVMSFeature(const char* name, int value) +{ + int i; + errno = 0; + i = decc$feature_get_index(name); + return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0); +} +#endif + +/* Global set of executing processes for use by the signal handler. + This global instance will be zero-initialized by the compiler. */ +typedef struct kwsysProcessInstances_s +{ + int Count; + int Size; + kwsysProcess** Processes; +} kwsysProcessInstances; +static kwsysProcessInstances kwsysProcesses; + +/* The old SIGCHLD / SIGINT / SIGTERM handlers. */ +static struct sigaction kwsysProcessesOldSigChldAction; +static struct sigaction kwsysProcessesOldSigIntAction; +static struct sigaction kwsysProcessesOldSigTermAction; + +static void kwsysProcessesUpdate(kwsysProcessInstances* newProcesses) +{ + /* Block signals while we update the set of pipes to check. + TODO: sigprocmask is undefined for threaded apps. See + pthread_sigmask. */ + sigset_t newset; + sigset_t oldset; + sigemptyset(&newset); + sigaddset(&newset, SIGCHLD); + sigaddset(&newset, SIGINT); + sigaddset(&newset, SIGTERM); + sigprocmask(SIG_BLOCK, &newset, &oldset); + + /* Store the new set in that seen by the signal handler. */ + kwsysProcesses = *newProcesses; + + /* Restore the signal mask to the previous setting. */ + sigprocmask(SIG_SETMASK, &oldset, 0); +} + +static int kwsysProcessesAdd(kwsysProcess* cp) +{ + /* Create a pipe through which the signal handler can notify the + given process object that a child has exited. */ + { + /* Create the pipe. */ + int p[2]; + if (pipe(p KWSYSPE_VMS_NONBLOCK) < 0) { + return 0; + } + + /* Store the pipes now to be sure they are cleaned up later. */ + cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL] = p[0]; + cp->SignalPipe = p[1]; + + /* Switch the pipe to non-blocking mode so that reading a byte can + be an atomic test-and-set. */ + if (!kwsysProcessSetNonBlocking(p[0]) || + !kwsysProcessSetNonBlocking(p[1])) { + return 0; + } + + /* The children do not need this pipe. Set close-on-exec flag on + the pipe's ends. */ + if ((fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0) || + (fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)) { + return 0; + } + } + + /* Attempt to add the given signal pipe to the signal handler set. */ + { + + /* Make sure there is enough space for the new signal pipe. */ + kwsysProcessInstances oldProcesses = kwsysProcesses; + kwsysProcessInstances newProcesses = oldProcesses; + if (oldProcesses.Count == oldProcesses.Size) { + /* Start with enough space for a small number of process instances + and double the size each time more is needed. */ + newProcesses.Size = oldProcesses.Size ? oldProcesses.Size * 2 : 4; + + /* Try allocating the new block of memory. */ + if ((newProcesses.Processes = ((kwsysProcess**)malloc( + (size_t)(newProcesses.Size) * sizeof(kwsysProcess*))))) { + /* Copy the old pipe set to the new memory. */ + if (oldProcesses.Count > 0) { + memcpy(newProcesses.Processes, oldProcesses.Processes, + ((size_t)(oldProcesses.Count) * sizeof(kwsysProcess*))); + } + } else { + /* Failed to allocate memory for the new signal pipe set. */ + return 0; + } + } + + /* Append the new signal pipe to the set. */ + newProcesses.Processes[newProcesses.Count++] = cp; + + /* Store the new set in that seen by the signal handler. */ + kwsysProcessesUpdate(&newProcesses); + + /* Free the original pipes if new ones were allocated. */ + if (newProcesses.Processes != oldProcesses.Processes) { + free(oldProcesses.Processes); + } + + /* If this is the first process, enable the signal handler. */ + if (newProcesses.Count == 1) { + /* Install our handler for SIGCHLD. Repeat call until it is not + interrupted. */ + struct sigaction newSigAction; + memset(&newSigAction, 0, sizeof(struct sigaction)); +#if KWSYSPE_USE_SIGINFO + newSigAction.sa_sigaction = kwsysProcessesSignalHandler; + newSigAction.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; +# ifdef SA_RESTART + newSigAction.sa_flags |= SA_RESTART; +# endif +#else + newSigAction.sa_handler = kwsysProcessesSignalHandler; + newSigAction.sa_flags = SA_NOCLDSTOP; +#endif + sigemptyset(&newSigAction.sa_mask); + while ((sigaction(SIGCHLD, &newSigAction, + &kwsysProcessesOldSigChldAction) < 0) && + (errno == EINTR)) + ; + + /* Install our handler for SIGINT / SIGTERM. Repeat call until + it is not interrupted. */ + sigemptyset(&newSigAction.sa_mask); + sigaddset(&newSigAction.sa_mask, SIGTERM); + while ((sigaction(SIGINT, &newSigAction, + &kwsysProcessesOldSigIntAction) < 0) && + (errno == EINTR)) + ; + + sigemptyset(&newSigAction.sa_mask); + sigaddset(&newSigAction.sa_mask, SIGINT); + while ((sigaction(SIGTERM, &newSigAction, + &kwsysProcessesOldSigIntAction) < 0) && + (errno == EINTR)) + ; + } + } + + return 1; +} + +static void kwsysProcessesRemove(kwsysProcess* cp) +{ + /* Attempt to remove the given signal pipe from the signal handler set. */ + { + /* Find the given process in the set. */ + kwsysProcessInstances newProcesses = kwsysProcesses; + int i; + for (i = 0; i < newProcesses.Count; ++i) { + if (newProcesses.Processes[i] == cp) { + break; + } + } + if (i < newProcesses.Count) { + /* Remove the process from the set. */ + --newProcesses.Count; + for (; i < newProcesses.Count; ++i) { + newProcesses.Processes[i] = newProcesses.Processes[i + 1]; + } + + /* If this was the last process, disable the signal handler. */ + if (newProcesses.Count == 0) { + /* Restore the signal handlers. Repeat call until it is not + interrupted. */ + while ((sigaction(SIGCHLD, &kwsysProcessesOldSigChldAction, 0) < 0) && + (errno == EINTR)) + ; + while ((sigaction(SIGINT, &kwsysProcessesOldSigIntAction, 0) < 0) && + (errno == EINTR)) + ; + while ((sigaction(SIGTERM, &kwsysProcessesOldSigTermAction, 0) < 0) && + (errno == EINTR)) + ; + + /* Free the table of process pointers since it is now empty. + This is safe because the signal handler has been removed. */ + newProcesses.Size = 0; + free(newProcesses.Processes); + newProcesses.Processes = 0; + } + + /* Store the new set in that seen by the signal handler. */ + kwsysProcessesUpdate(&newProcesses); + } + } + + /* Close the pipe through which the signal handler may have notified + the given process object that a child has exited. */ + kwsysProcessCleanupDescriptor(&cp->SignalPipe); +} + +static void kwsysProcessesSignalHandler(int signum +#if KWSYSPE_USE_SIGINFO + , + siginfo_t* info, void* ucontext +#endif +) +{ + int i, j, procStatus, old_errno = errno; +#if KWSYSPE_USE_SIGINFO + (void)info; + (void)ucontext; +#endif + + /* Signal all process objects that a child has terminated. */ + switch (signum) { + case SIGCHLD: + for (i = 0; i < kwsysProcesses.Count; ++i) { + /* Set the pipe in a signalled state. */ + char buf = 1; + kwsysProcess* cp = kwsysProcesses.Processes[i]; + kwsysProcess_ssize_t pipeStatus = + read(cp->PipeReadEnds[KWSYSPE_PIPE_SIGNAL], &buf, 1); + (void)pipeStatus; + pipeStatus = write(cp->SignalPipe, &buf, 1); + (void)pipeStatus; + } + break; + case SIGINT: + case SIGTERM: + /* Signal child processes that are running in new process groups. */ + for (i = 0; i < kwsysProcesses.Count; ++i) { + kwsysProcess* cp = kwsysProcesses.Processes[i]; + /* Check Killed to avoid data race condition when killing. + Check State to avoid data race condition in kwsysProcessCleanup + when there is an error (it leaves a reaped PID). */ + if (cp->CreateProcessGroup && !cp->Killed && + cp->State != kwsysProcess_State_Error && cp->ForkPIDs) { + for (j = 0; j < cp->NumberOfCommands; ++j) { + /* Make sure the PID is still valid. */ + if (cp->ForkPIDs[j]) { + /* The user created a process group for this process. The group + ID + is the process ID for the original process in the group. */ + kill(-cp->ForkPIDs[j], SIGINT); + } + } + } + } + + /* Wait for all processes to terminate. */ + while (wait(&procStatus) >= 0 || errno != ECHILD) { + } + + /* Terminate the process, which is now in an inconsistent state + because we reaped all the PIDs that it may have been reaping + or may have reaped in the future. Reraise the signal so that + the proper exit code is returned. */ + { + /* Install default signal handler. */ + struct sigaction defSigAction; + sigset_t unblockSet; + memset(&defSigAction, 0, sizeof(defSigAction)); + defSigAction.sa_handler = SIG_DFL; + sigemptyset(&defSigAction.sa_mask); + while ((sigaction(signum, &defSigAction, 0) < 0) && (errno == EINTR)) + ; + /* Unmask the signal. */ + sigemptyset(&unblockSet); + sigaddset(&unblockSet, signum); + sigprocmask(SIG_UNBLOCK, &unblockSet, 0); + /* Raise the signal again. */ + raise(signum); + /* We shouldn't get here... but if we do... */ + _exit(1); + } + /* break omitted to silence unreachable code clang compiler warning. */ + } + +#if !KWSYSPE_USE_SIGINFO + /* Re-Install our handler. Repeat call until it is not interrupted. */ + { + struct sigaction newSigAction; + struct sigaction& oldSigAction; + memset(&newSigAction, 0, sizeof(struct sigaction)); + newSigChldAction.sa_handler = kwsysProcessesSignalHandler; + newSigChldAction.sa_flags = SA_NOCLDSTOP; + sigemptyset(&newSigAction.sa_mask); + switch (signum) { + case SIGCHLD: + oldSigAction = &kwsysProcessesOldSigChldAction; + break; + case SIGINT: + sigaddset(&newSigAction.sa_mask, SIGTERM); + oldSigAction = &kwsysProcessesOldSigIntAction; + break; + case SIGTERM: + sigaddset(&newSigAction.sa_mask, SIGINT); + oldSigAction = &kwsysProcessesOldSigTermAction; + break; + default: + return 0; + } + while ((sigaction(signum, &newSigAction, oldSigAction) < 0) && + (errno == EINTR)) + ; + } +#endif + + errno = old_errno; +} + +void kwsysProcess_ResetStartTime(kwsysProcess* cp) +{ + if (!cp) { + return; + } + /* Reset start time. */ + cp->StartTime = kwsysProcessTimeGetCurrent(); +} diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c new file mode 100644 index 0000000..56bbd20 --- /dev/null +++ b/Source/kwsys/ProcessWin32.c @@ -0,0 +1,2785 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Process.h) +#include KWSYS_HEADER(Encoding.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Encoding.h.in" +# include "Process.h.in" +#endif + +/* + +Implementation for Windows + +On windows, a thread is created to wait for data on each pipe. The +threads are synchronized with the main thread to simulate the use of +a UNIX-style select system call. + +*/ + +#ifdef _MSC_VER +# pragma warning(push, 1) +#endif +#include <windows.h> /* Windows API */ +#if defined(_MSC_VER) && _MSC_VER >= 1800 +# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx +#endif +#include <io.h> /* _unlink */ +#include <stdio.h> /* sprintf */ +#include <string.h> /* strlen, strdup */ +#ifdef __WATCOMC__ +# define _unlink unlink +#endif + +#ifndef _MAX_FNAME +# define _MAX_FNAME 4096 +#endif +#ifndef _MAX_PATH +# define _MAX_PATH 4096 +#endif + +#ifdef _MSC_VER +# pragma warning(pop) +# pragma warning(disable : 4514) +# pragma warning(disable : 4706) +#endif + +#if defined(__BORLANDC__) +# pragma warn - 8004 /* assigned a value that is never used */ +# pragma warn - 8060 /* Assignment inside if() condition. */ +#endif + +/* There are pipes for the process pipeline's stdout and stderr. */ +#define KWSYSPE_PIPE_COUNT 2 +#define KWSYSPE_PIPE_STDOUT 0 +#define KWSYSPE_PIPE_STDERR 1 + +/* The maximum amount to read from a pipe at a time. */ +#define KWSYSPE_PIPE_BUFFER_SIZE 1024 + +/* Debug output macro. */ +#if 0 +# define KWSYSPE_DEBUG(x) \ + ((void*)cp == (void*)0x00226DE0 \ + ? (fprintf(stderr, "%d/%p/%d ", (int)GetCurrentProcessId(), cp, \ + __LINE__), \ + fprintf x, fflush(stderr), 1) \ + : (1)) +#else +# define KWSYSPE_DEBUG(x) (void)1 +#endif + +typedef LARGE_INTEGER kwsysProcessTime; + +typedef struct kwsysProcessCreateInformation_s +{ + /* Windows child startup control data. */ + STARTUPINFOW StartupInfo; + + /* Original handles before making inherited duplicates. */ + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} kwsysProcessCreateInformation; + +typedef struct kwsysProcessPipeData_s kwsysProcessPipeData; +static DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd); +static void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, + kwsysProcessPipeData* td); +static DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd); +static void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, + kwsysProcessPipeData* td); +static int kwsysProcessInitialize(kwsysProcess* cp); +static DWORD kwsysProcessCreate(kwsysProcess* cp, int index, + kwsysProcessCreateInformation* si); +static void kwsysProcessDestroy(kwsysProcess* cp, int event); +static DWORD kwsysProcessSetupOutputPipeFile(PHANDLE handle, const char* name); +static void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle); +static void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle); +static void kwsysProcessCleanupHandle(PHANDLE h); +static void kwsysProcessCleanup(kwsysProcess* cp, DWORD error); +static void kwsysProcessCleanErrorMessage(kwsysProcess* cp); +static int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, + kwsysProcessTime* timeoutTime); +static int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, + double* userTimeout, + kwsysProcessTime* timeoutLength); +static kwsysProcessTime kwsysProcessTimeGetCurrent(void); +static DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t); +static double kwsysProcessTimeToDouble(kwsysProcessTime t); +static kwsysProcessTime kwsysProcessTimeFromDouble(double d); +static int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2); +static kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, + kwsysProcessTime in2); +static kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, + kwsysProcessTime in2); +static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code, + int idx); +static void kwsysProcessKillTree(int pid); +static void kwsysProcessDisablePipeThreads(kwsysProcess* cp); +static int kwsysProcessesInitialize(void); +static int kwsysTryEnterCreateProcessSection(void); +static void kwsysLeaveCreateProcessSection(void); +static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessId, + int newProcessGroup); +static void kwsysProcessesRemove(HANDLE hProcess); +static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType); + +/* A structure containing synchronization data for each thread. */ +typedef struct kwsysProcessPipeSync_s kwsysProcessPipeSync; +struct kwsysProcessPipeSync_s +{ + /* Handle to the thread. */ + HANDLE Thread; + + /* Semaphore indicating to the thread that a process has started. */ + HANDLE Ready; + + /* Semaphore indicating to the thread that it should begin work. */ + HANDLE Go; + + /* Semaphore indicating thread has reset for another process. */ + HANDLE Reset; +}; + +/* A structure containing data for each pipe's threads. */ +struct kwsysProcessPipeData_s +{ + /* ------------- Data managed per instance of kwsysProcess ------------- */ + + /* Synchronization data for reading thread. */ + kwsysProcessPipeSync Reader; + + /* Synchronization data for waking thread. */ + kwsysProcessPipeSync Waker; + + /* Index of this pipe. */ + int Index; + + /* The kwsysProcess instance owning this pipe. */ + kwsysProcess* Process; + + /* ------------- Data managed per call to Execute ------------- */ + + /* Buffer for data read in this pipe's thread. */ + char DataBuffer[KWSYSPE_PIPE_BUFFER_SIZE]; + + /* The length of the data stored in the buffer. */ + DWORD DataLength; + + /* Whether the pipe has been closed. */ + int Closed; + + /* Handle for the read end of this pipe. */ + HANDLE Read; + + /* Handle for the write end of this pipe. */ + HANDLE Write; +}; + +/* A structure containing results data for each process. */ +typedef struct kwsysProcessResults_s kwsysProcessResults; +struct kwsysProcessResults_s +{ + /* The status of the process. */ + int State; + + /* The exceptional behavior that terminated the process, if any. */ + int ExitException; + + /* The process exit code. */ + DWORD ExitCode; + + /* The process return code, if any. */ + int ExitValue; + + /* Description for the ExitException. */ + char ExitExceptionString[KWSYSPE_PIPE_BUFFER_SIZE + 1]; +}; + +/* Structure containing data used to implement the child's execution. */ +struct kwsysProcess_s +{ + /* ------------- Data managed per instance of kwsysProcess ------------- */ + + /* The status of the process structure. */ + int State; + + /* The command lines to execute. */ + wchar_t** Commands; + int NumberOfCommands; + + /* The exit code of each command. */ + DWORD* CommandExitCodes; + + /* The working directory for the child process. */ + wchar_t* WorkingDirectory; + + /* Whether to create the child as a detached process. */ + int OptionDetach; + + /* Whether the child was created as a detached process. */ + int Detached; + + /* Whether to hide the child process's window. */ + int HideWindow; + + /* Whether to treat command lines as verbatim. */ + int Verbatim; + + /* Whether to merge stdout/stderr of the child. */ + int MergeOutput; + + /* Whether to create the process in a new process group. */ + int CreateProcessGroup; + + /* Mutex to protect the shared index used by threads to report data. */ + HANDLE SharedIndexMutex; + + /* Semaphore used by threads to signal data ready. */ + HANDLE Full; + + /* Whether we are currently deleting this kwsysProcess instance. */ + int Deleting; + + /* Data specific to each pipe and its thread. */ + kwsysProcessPipeData Pipe[KWSYSPE_PIPE_COUNT]; + + /* Name of files to which stdin and stdout pipes are attached. */ + char* PipeFileSTDIN; + char* PipeFileSTDOUT; + char* PipeFileSTDERR; + + /* Whether each pipe is shared with the parent process. */ + int PipeSharedSTDIN; + int PipeSharedSTDOUT; + int PipeSharedSTDERR; + + /* Native pipes provided by the user. */ + HANDLE PipeNativeSTDIN[2]; + HANDLE PipeNativeSTDOUT[2]; + HANDLE PipeNativeSTDERR[2]; + + /* ------------- Data managed per call to Execute ------------- */ + + /* Index of last pipe to report data, if any. */ + int CurrentIndex; + + /* Index shared by threads to report data. */ + int SharedIndex; + + /* The timeout length. */ + double Timeout; + + /* Time at which the child started. */ + kwsysProcessTime StartTime; + + /* Time at which the child will timeout. Negative for no timeout. */ + kwsysProcessTime TimeoutTime; + + /* Flag for whether the process was killed. */ + int Killed; + + /* Flag for whether the timeout expired. */ + int TimeoutExpired; + + /* Flag for whether the process has terminated. */ + int Terminated; + + /* The number of pipes still open during execution and while waiting + for pipes to close after process termination. */ + int PipesLeft; + + /* Buffer for error messages. */ + char ErrorMessage[KWSYSPE_PIPE_BUFFER_SIZE + 1]; + + /* process results. */ + kwsysProcessResults* ProcessResults; + + /* Windows process information data. */ + PROCESS_INFORMATION* ProcessInformation; + + /* Data and process termination events for which to wait. */ + PHANDLE ProcessEvents; + int ProcessEventsLength; + + /* Real working directory of our own process. */ + DWORD RealWorkingDirectoryLength; + wchar_t* RealWorkingDirectory; + + /* Own handles for the child's ends of the pipes in the parent process. + Used temporarily during process creation. */ + HANDLE PipeChildStd[3]; +}; + +kwsysProcess* kwsysProcess_New(void) +{ + int i; + + /* Process control structure. */ + kwsysProcess* cp; + + /* Windows version number data. */ + OSVERSIONINFO osv; + + /* Initialize list of processes before we get any farther. It's especially + important that the console Ctrl handler be added BEFORE starting the + first process. This prevents the risk of an orphaned process being + started by the main thread while the default Ctrl handler is in + progress. */ + if (!kwsysProcessesInitialize()) { + return 0; + } + + /* Allocate a process control structure. */ + cp = (kwsysProcess*)malloc(sizeof(kwsysProcess)); + if (!cp) { + /* Could not allocate memory for the control structure. */ + return 0; + } + ZeroMemory(cp, sizeof(*cp)); + + /* Share stdin with the parent process by default. */ + cp->PipeSharedSTDIN = 1; + + /* Set initial status. */ + cp->State = kwsysProcess_State_Starting; + + /* Choose a method of running the child based on version of + windows. */ + ZeroMemory(&osv, sizeof(osv)); + osv.dwOSVersionInfoSize = sizeof(osv); +#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +#endif + GetVersionEx(&osv); +#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +#endif + if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + /* Win9x no longer supported. */ + kwsysProcess_Delete(cp); + return 0; + } + + /* Initially no thread owns the mutex. Initialize semaphore to 1. */ + if (!(cp->SharedIndexMutex = CreateSemaphore(0, 1, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* Initially no data are available. Initialize semaphore to 0. */ + if (!(cp->Full = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* Create the thread to read each pipe. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + DWORD dummy = 0; + + /* Assign the thread its index. */ + cp->Pipe[i].Index = i; + + /* Give the thread a pointer back to the kwsysProcess instance. */ + cp->Pipe[i].Process = cp; + + /* No process is yet running. Initialize semaphore to 0. */ + if (!(cp->Pipe[i].Reader.Ready = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* The pipe is not yet reset. Initialize semaphore to 0. */ + if (!(cp->Pipe[i].Reader.Reset = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* The thread's buffer is initially empty. Initialize semaphore to 1. */ + if (!(cp->Pipe[i].Reader.Go = CreateSemaphore(0, 1, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* Create the reading thread. It will block immediately. The + thread will not make deeply nested calls, so we need only a + small stack. */ + if (!(cp->Pipe[i].Reader.Thread = CreateThread( + 0, 1024, kwsysProcessPipeThreadRead, &cp->Pipe[i], 0, &dummy))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* No process is yet running. Initialize semaphore to 0. */ + if (!(cp->Pipe[i].Waker.Ready = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* The pipe is not yet reset. Initialize semaphore to 0. */ + if (!(cp->Pipe[i].Waker.Reset = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* The waker should not wake immediately. Initialize semaphore to 0. */ + if (!(cp->Pipe[i].Waker.Go = CreateSemaphore(0, 0, 1, 0))) { + kwsysProcess_Delete(cp); + return 0; + } + + /* Create the waking thread. It will block immediately. The + thread will not make deeply nested calls, so we need only a + small stack. */ + if (!(cp->Pipe[i].Waker.Thread = CreateThread( + 0, 1024, kwsysProcessPipeThreadWake, &cp->Pipe[i], 0, &dummy))) { + kwsysProcess_Delete(cp); + return 0; + } + } + for (i = 0; i < 3; ++i) { + cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; + } + + return cp; +} + +void kwsysProcess_Delete(kwsysProcess* cp) +{ + int i; + + /* Make sure we have an instance. */ + if (!cp) { + return; + } + + /* If the process is executing, wait for it to finish. */ + if (cp->State == kwsysProcess_State_Executing) { + if (cp->Detached) { + kwsysProcess_Disown(cp); + } else { + kwsysProcess_WaitForExit(cp, 0); + } + } + + /* We are deleting the kwsysProcess instance. */ + cp->Deleting = 1; + + /* Terminate each of the threads. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + /* Terminate this reading thread. */ + if (cp->Pipe[i].Reader.Thread) { + /* Signal the thread we are ready for it. It will terminate + immediately since Deleting is set. */ + ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0); + + /* Wait for the thread to exit. */ + WaitForSingleObject(cp->Pipe[i].Reader.Thread, INFINITE); + + /* Close the handle to the thread. */ + kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Thread); + } + + /* Terminate this waking thread. */ + if (cp->Pipe[i].Waker.Thread) { + /* Signal the thread we are ready for it. It will terminate + immediately since Deleting is set. */ + ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0); + + /* Wait for the thread to exit. */ + WaitForSingleObject(cp->Pipe[i].Waker.Thread, INFINITE); + + /* Close the handle to the thread. */ + kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Thread); + } + + /* Cleanup the pipe's semaphores. */ + kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Ready); + kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Go); + kwsysProcessCleanupHandle(&cp->Pipe[i].Reader.Reset); + kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Ready); + kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Go); + kwsysProcessCleanupHandle(&cp->Pipe[i].Waker.Reset); + } + + /* Close the shared semaphores. */ + kwsysProcessCleanupHandle(&cp->SharedIndexMutex); + kwsysProcessCleanupHandle(&cp->Full); + + /* Free memory. */ + kwsysProcess_SetCommand(cp, 0); + kwsysProcess_SetWorkingDirectory(cp, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDIN, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDOUT, 0); + kwsysProcess_SetPipeFile(cp, kwsysProcess_Pipe_STDERR, 0); + free(cp->CommandExitCodes); + free(cp->ProcessResults); + free(cp); +} + +int kwsysProcess_SetCommand(kwsysProcess* cp, char const* const* command) +{ + int i; + if (!cp) { + return 0; + } + for (i = 0; i < cp->NumberOfCommands; ++i) { + free(cp->Commands[i]); + } + cp->NumberOfCommands = 0; + if (cp->Commands) { + free(cp->Commands); + cp->Commands = 0; + } + if (command) { + return kwsysProcess_AddCommand(cp, command); + } + return 1; +} + +int kwsysProcess_AddCommand(kwsysProcess* cp, char const* const* command) +{ + int newNumberOfCommands; + wchar_t** newCommands; + + /* Make sure we have a command to add. */ + if (!cp || !command || !*command) { + return 0; + } + + /* Allocate a new array for command pointers. */ + newNumberOfCommands = cp->NumberOfCommands + 1; + if (!(newCommands = + (wchar_t**)malloc(sizeof(wchar_t*) * newNumberOfCommands))) { + /* Out of memory. */ + return 0; + } + + /* Copy any existing commands into the new array. */ + { + int i; + for (i = 0; i < cp->NumberOfCommands; ++i) { + newCommands[i] = cp->Commands[i]; + } + } + + if (cp->Verbatim) { + /* Copy the verbatim command line into the buffer. */ + newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(*command); + } else { + /* Encode the arguments so CommandLineToArgvW can decode + them from the command line string in the child. */ + char buffer[32768]; /* CreateProcess max command-line length. */ + char* end = buffer + sizeof(buffer); + char* out = buffer; + char const* const* a; + for (a = command; *a; ++a) { + int quote = !**a; /* Quote the empty string. */ + int slashes = 0; + char const* c; + if (a != command && out != end) { + *out++ = ' '; + } + for (c = *a; !quote && *c; ++c) { + quote = (*c == ' ' || *c == '\t'); + } + if (quote && out != end) { + *out++ = '"'; + } + for (c = *a; *c; ++c) { + if (*c == '\\') { + ++slashes; + } else { + if (*c == '"') { + // Add n+1 backslashes to total 2n+1 before internal '"'. + while (slashes-- >= 0 && out != end) { + *out++ = '\\'; + } + } + slashes = 0; + } + if (out != end) { + *out++ = *c; + } + } + if (quote) { + // Add n backslashes to total 2n before ending '"'. + while (slashes-- > 0 && out != end) { + *out++ = '\\'; + } + if (out != end) { + *out++ = '"'; + } + } + } + if (out != end) { + *out = '\0'; + newCommands[cp->NumberOfCommands] = kwsysEncoding_DupToWide(buffer); + } else { + newCommands[cp->NumberOfCommands] = 0; + } + } + if (!newCommands[cp->NumberOfCommands]) { + /* Out of memory or command line too long. */ + free(newCommands); + return 0; + } + + /* Save the new array of commands. */ + free(cp->Commands); + cp->Commands = newCommands; + cp->NumberOfCommands = newNumberOfCommands; + return 1; +} + +void kwsysProcess_SetTimeout(kwsysProcess* cp, double timeout) +{ + if (!cp) { + return; + } + cp->Timeout = timeout; + if (cp->Timeout < 0) { + cp->Timeout = 0; + } + // Force recomputation of TimeoutTime. + cp->TimeoutTime.QuadPart = -1; +} + +int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) +{ + if (!cp) { + return 0; + } + if (cp->WorkingDirectory) { + free(cp->WorkingDirectory); + cp->WorkingDirectory = 0; + } + if (dir && dir[0]) { + wchar_t* wdir = kwsysEncoding_DupToWide(dir); + /* We must convert the working directory to a full path. */ + DWORD length = GetFullPathNameW(wdir, 0, 0, 0); + if (length > 0) { + wchar_t* work_dir = malloc(length * sizeof(wchar_t)); + if (!work_dir) { + free(wdir); + return 0; + } + if (!GetFullPathNameW(wdir, length, work_dir, 0)) { + free(work_dir); + free(wdir); + return 0; + } + cp->WorkingDirectory = work_dir; + } + free(wdir); + } + return 1; +} + +int kwsysProcess_SetPipeFile(kwsysProcess* cp, int pipe, const char* file) +{ + char** pfile; + if (!cp) { + return 0; + } + switch (pipe) { + case kwsysProcess_Pipe_STDIN: + pfile = &cp->PipeFileSTDIN; + break; + case kwsysProcess_Pipe_STDOUT: + pfile = &cp->PipeFileSTDOUT; + break; + case kwsysProcess_Pipe_STDERR: + pfile = &cp->PipeFileSTDERR; + break; + default: + return 0; + } + if (*pfile) { + free(*pfile); + *pfile = 0; + } + if (file) { + *pfile = strdup(file); + if (!*pfile) { + return 0; + } + } + + /* If we are redirecting the pipe, do not share it or use a native + pipe. */ + if (*pfile) { + kwsysProcess_SetPipeNative(cp, pipe, 0); + kwsysProcess_SetPipeShared(cp, pipe, 0); + } + + return 1; +} + +void kwsysProcess_SetPipeShared(kwsysProcess* cp, int pipe, int shared) +{ + if (!cp) { + return; + } + + switch (pipe) { + case kwsysProcess_Pipe_STDIN: + cp->PipeSharedSTDIN = shared ? 1 : 0; + break; + case kwsysProcess_Pipe_STDOUT: + cp->PipeSharedSTDOUT = shared ? 1 : 0; + break; + case kwsysProcess_Pipe_STDERR: + cp->PipeSharedSTDERR = shared ? 1 : 0; + break; + default: + return; + } + + /* If we are sharing the pipe, do not redirect it to a file or use a + native pipe. */ + if (shared) { + kwsysProcess_SetPipeFile(cp, pipe, 0); + kwsysProcess_SetPipeNative(cp, pipe, 0); + } +} + +void kwsysProcess_SetPipeNative(kwsysProcess* cp, int pipe, const HANDLE p[2]) +{ + HANDLE* pPipeNative = 0; + + if (!cp) { + return; + } + + switch (pipe) { + case kwsysProcess_Pipe_STDIN: + pPipeNative = cp->PipeNativeSTDIN; + break; + case kwsysProcess_Pipe_STDOUT: + pPipeNative = cp->PipeNativeSTDOUT; + break; + case kwsysProcess_Pipe_STDERR: + pPipeNative = cp->PipeNativeSTDERR; + break; + default: + return; + } + + /* Copy the native pipe handles provided. */ + if (p) { + pPipeNative[0] = p[0]; + pPipeNative[1] = p[1]; + } else { + pPipeNative[0] = 0; + pPipeNative[1] = 0; + } + + /* If we are using a native pipe, do not share it or redirect it to + a file. */ + if (p) { + kwsysProcess_SetPipeFile(cp, pipe, 0); + kwsysProcess_SetPipeShared(cp, pipe, 0); + } +} + +int kwsysProcess_GetOption(kwsysProcess* cp, int optionId) +{ + if (!cp) { + return 0; + } + + switch (optionId) { + case kwsysProcess_Option_Detach: + return cp->OptionDetach; + case kwsysProcess_Option_HideWindow: + return cp->HideWindow; + case kwsysProcess_Option_MergeOutput: + return cp->MergeOutput; + case kwsysProcess_Option_Verbatim: + return cp->Verbatim; + case kwsysProcess_Option_CreateProcessGroup: + return cp->CreateProcessGroup; + default: + return 0; + } +} + +void kwsysProcess_SetOption(kwsysProcess* cp, int optionId, int value) +{ + if (!cp) { + return; + } + + switch (optionId) { + case kwsysProcess_Option_Detach: + cp->OptionDetach = value; + break; + case kwsysProcess_Option_HideWindow: + cp->HideWindow = value; + break; + case kwsysProcess_Option_MergeOutput: + cp->MergeOutput = value; + break; + case kwsysProcess_Option_Verbatim: + cp->Verbatim = value; + break; + case kwsysProcess_Option_CreateProcessGroup: + cp->CreateProcessGroup = value; + break; + default: + break; + } +} + +int kwsysProcess_GetState(kwsysProcess* cp) +{ + return cp ? cp->State : kwsysProcess_State_Error; +} + +int kwsysProcess_GetExitException(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitException + : kwsysProcess_Exception_Other; +} + +int kwsysProcess_GetExitValue(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitValue + : -1; +} + +int kwsysProcess_GetExitCode(kwsysProcess* cp) +{ + return (cp && cp->ProcessResults && (cp->NumberOfCommands > 0)) + ? cp->ProcessResults[cp->NumberOfCommands - 1].ExitCode + : 0; +} + +const char* kwsysProcess_GetErrorString(kwsysProcess* cp) +{ + if (!cp) { + return "Process management structure could not be allocated"; + } else if (cp->State == kwsysProcess_State_Error) { + return cp->ErrorMessage; + } + return "Success"; +} + +const char* kwsysProcess_GetExceptionString(kwsysProcess* cp) +{ + if (!(cp && cp->ProcessResults && (cp->NumberOfCommands > 0))) { + return "GetExceptionString called with NULL process management structure"; + } else if (cp->State == kwsysProcess_State_Exception) { + return cp->ProcessResults[cp->NumberOfCommands - 1].ExitExceptionString; + } + return "No exception"; +} + +/* the index should be in array bound. */ +#define KWSYSPE_IDX_CHK(RET) \ + if (!cp || idx >= cp->NumberOfCommands || idx < 0) { \ + KWSYSPE_DEBUG((stderr, "array index out of bound\n")); \ + return RET; \ + } + +int kwsysProcess_GetStateByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(kwsysProcess_State_Error) + return cp->ProcessResults[idx].State; +} + +int kwsysProcess_GetExitExceptionByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(kwsysProcess_Exception_Other) + return cp->ProcessResults[idx].ExitException; +} + +int kwsysProcess_GetExitValueByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(-1) + return cp->ProcessResults[idx].ExitValue; +} + +int kwsysProcess_GetExitCodeByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK(-1) + return cp->CommandExitCodes[idx]; +} + +const char* kwsysProcess_GetExceptionStringByIndex(kwsysProcess* cp, int idx) +{ + KWSYSPE_IDX_CHK("GetExceptionString called with NULL process management " + "structure or index out of bound") + if (cp->ProcessResults[idx].State == kwsysProcess_StateByIndex_Exception) { + return cp->ProcessResults[idx].ExitExceptionString; + } + return "No exception"; +} + +#undef KWSYSPE_IDX_CHK + +void kwsysProcess_Execute(kwsysProcess* cp) +{ + int i; + + /* Do not execute a second time. */ + if (!cp || cp->State == kwsysProcess_State_Executing) { + return; + } + + /* Make sure we have something to run. */ + if (cp->NumberOfCommands < 1) { + strcpy(cp->ErrorMessage, "No command"); + cp->State = kwsysProcess_State_Error; + return; + } + + /* Initialize the control structure for a new process. */ + if (!kwsysProcessInitialize(cp)) { + strcpy(cp->ErrorMessage, "Out of memory"); + cp->State = kwsysProcess_State_Error; + return; + } + + /* Save the real working directory of this process and change to + the working directory for the child processes. This is needed + to make pipe file paths evaluate correctly. */ + if (cp->WorkingDirectory) { + if (!GetCurrentDirectoryW(cp->RealWorkingDirectoryLength, + cp->RealWorkingDirectory)) { + kwsysProcessCleanup(cp, GetLastError()); + return; + } + SetCurrentDirectoryW(cp->WorkingDirectory); + } + + /* Setup the stdin pipe for the first process. */ + if (cp->PipeFileSTDIN) { + /* Create a handle to read a file for stdin. */ + wchar_t* wstdin = kwsysEncoding_DupToWide(cp->PipeFileSTDIN); + DWORD error; + cp->PipeChildStd[0] = + CreateFileW(wstdin, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + OPEN_EXISTING, 0, 0); + error = GetLastError(); /* Check now in case free changes this. */ + free(wstdin); + if (cp->PipeChildStd[0] == INVALID_HANDLE_VALUE) { + kwsysProcessCleanup(cp, error); + return; + } + } else if (cp->PipeSharedSTDIN) { + /* Share this process's stdin with the child. */ + kwsysProcessSetupSharedPipe(STD_INPUT_HANDLE, &cp->PipeChildStd[0]); + } else if (cp->PipeNativeSTDIN[0]) { + /* Use the provided native pipe. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDIN[0], &cp->PipeChildStd[0]); + } else { + /* Explicitly give the child no stdin. */ + cp->PipeChildStd[0] = INVALID_HANDLE_VALUE; + } + + /* Create the output pipe for the last process. + We always create this so the pipe thread can run even if we + do not end up giving the write end to the child below. */ + if (!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDOUT].Read, + &cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, 0, 0)) { + kwsysProcessCleanup(cp, GetLastError()); + return; + } + + if (cp->PipeFileSTDOUT) { + /* Use a file for stdout. */ + DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[1], + cp->PipeFileSTDOUT); + if (error) { + kwsysProcessCleanup(cp, error); + return; + } + } else if (cp->PipeSharedSTDOUT) { + /* Use the parent stdout. */ + kwsysProcessSetupSharedPipe(STD_OUTPUT_HANDLE, &cp->PipeChildStd[1]); + } else if (cp->PipeNativeSTDOUT[1]) { + /* Use the given handle for stdout. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDOUT[1], &cp->PipeChildStd[1]); + } else { + /* Use our pipe for stdout. Duplicate the handle since our waker + thread will use the original. Do not make it inherited yet. */ + if (!DuplicateHandle(GetCurrentProcess(), + cp->Pipe[KWSYSPE_PIPE_STDOUT].Write, + GetCurrentProcess(), &cp->PipeChildStd[1], 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + kwsysProcessCleanup(cp, GetLastError()); + return; + } + } + + /* Create stderr pipe to be shared by all processes in the pipeline. + We always create this so the pipe thread can run even if we do not + end up giving the write end to the child below. */ + if (!CreatePipe(&cp->Pipe[KWSYSPE_PIPE_STDERR].Read, + &cp->Pipe[KWSYSPE_PIPE_STDERR].Write, 0, 0)) { + kwsysProcessCleanup(cp, GetLastError()); + return; + } + + if (cp->PipeFileSTDERR) { + /* Use a file for stderr. */ + DWORD error = kwsysProcessSetupOutputPipeFile(&cp->PipeChildStd[2], + cp->PipeFileSTDERR); + if (error) { + kwsysProcessCleanup(cp, error); + return; + } + } else if (cp->PipeSharedSTDERR) { + /* Use the parent stderr. */ + kwsysProcessSetupSharedPipe(STD_ERROR_HANDLE, &cp->PipeChildStd[2]); + } else if (cp->PipeNativeSTDERR[1]) { + /* Use the given handle for stderr. */ + kwsysProcessSetupPipeNative(cp->PipeNativeSTDERR[1], &cp->PipeChildStd[2]); + } else { + /* Use our pipe for stderr. Duplicate the handle since our waker + thread will use the original. Do not make it inherited yet. */ + if (!DuplicateHandle(GetCurrentProcess(), + cp->Pipe[KWSYSPE_PIPE_STDERR].Write, + GetCurrentProcess(), &cp->PipeChildStd[2], 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + kwsysProcessCleanup(cp, GetLastError()); + return; + } + } + + /* Create the pipeline of processes. */ + { + /* Child startup control data. */ + kwsysProcessCreateInformation si; + HANDLE nextStdInput = cp->PipeChildStd[0]; + + /* Initialize startup info data. */ + ZeroMemory(&si, sizeof(si)); + si.StartupInfo.cb = sizeof(si.StartupInfo); + + /* Decide whether a child window should be shown. */ + si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = + (unsigned short)(cp->HideWindow ? SW_HIDE : SW_SHOWDEFAULT); + + /* Connect the child's output pipes to the threads. */ + si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + + for (i = 0; i < cp->NumberOfCommands; ++i) { + /* Setup the process's pipes. */ + si.hStdInput = nextStdInput; + if (i == cp->NumberOfCommands - 1) { + /* The last child gets the overall stdout. */ + nextStdInput = INVALID_HANDLE_VALUE; + si.hStdOutput = cp->PipeChildStd[1]; + } else { + /* Create a pipe to sit between the children. */ + HANDLE p[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; + if (!CreatePipe(&p[0], &p[1], 0, 0)) { + DWORD error = GetLastError(); + if (nextStdInput != cp->PipeChildStd[0]) { + kwsysProcessCleanupHandle(&nextStdInput); + } + kwsysProcessCleanup(cp, error); + return; + } + nextStdInput = p[0]; + si.hStdOutput = p[1]; + } + si.hStdError = + cp->MergeOutput ? cp->PipeChildStd[1] : cp->PipeChildStd[2]; + + { + DWORD error = kwsysProcessCreate(cp, i, &si); + + /* Close our copies of pipes used between children. */ + if (si.hStdInput != cp->PipeChildStd[0]) { + kwsysProcessCleanupHandle(&si.hStdInput); + } + if (si.hStdOutput != cp->PipeChildStd[1]) { + kwsysProcessCleanupHandle(&si.hStdOutput); + } + if (si.hStdError != cp->PipeChildStd[2] && !cp->MergeOutput) { + kwsysProcessCleanupHandle(&si.hStdError); + } + if (!error) { + cp->ProcessEvents[i + 1] = cp->ProcessInformation[i].hProcess; + } else { + if (nextStdInput != cp->PipeChildStd[0]) { + kwsysProcessCleanupHandle(&nextStdInput); + } + kwsysProcessCleanup(cp, error); + return; + } + } + } + } + + /* The parent process does not need the child's pipe ends. */ + for (i = 0; i < 3; ++i) { + kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); + } + + /* Restore the working directory. */ + if (cp->RealWorkingDirectory) { + SetCurrentDirectoryW(cp->RealWorkingDirectory); + free(cp->RealWorkingDirectory); + cp->RealWorkingDirectory = 0; + } + + /* The timeout period starts now. */ + cp->StartTime = kwsysProcessTimeGetCurrent(); + cp->TimeoutTime = kwsysProcessTimeFromDouble(-1); + + /* All processes in the pipeline have been started in suspended + mode. Resume them all now. */ + for (i = 0; i < cp->NumberOfCommands; ++i) { + ResumeThread(cp->ProcessInformation[i].hThread); + } + + /* ---- It is no longer safe to call kwsysProcessCleanup. ----- */ + /* Tell the pipe threads that a process has started. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + ReleaseSemaphore(cp->Pipe[i].Reader.Ready, 1, 0); + ReleaseSemaphore(cp->Pipe[i].Waker.Ready, 1, 0); + } + + /* We don't care about the children's main threads. */ + for (i = 0; i < cp->NumberOfCommands; ++i) { + kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); + } + + /* No pipe has reported data. */ + cp->CurrentIndex = KWSYSPE_PIPE_COUNT; + cp->PipesLeft = KWSYSPE_PIPE_COUNT; + + /* The process has now started. */ + cp->State = kwsysProcess_State_Executing; + cp->Detached = cp->OptionDetach; +} + +void kwsysProcess_Disown(kwsysProcess* cp) +{ + int i; + + /* Make sure we are executing a detached process. */ + if (!cp || !cp->Detached || cp->State != kwsysProcess_State_Executing || + cp->TimeoutExpired || cp->Killed || cp->Terminated) { + return; + } + + /* Disable the reading threads. */ + kwsysProcessDisablePipeThreads(cp); + + /* Wait for all pipe threads to reset. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE); + WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE); + } + + /* We will not wait for exit, so cleanup now. */ + kwsysProcessCleanup(cp, 0); + + /* The process has been disowned. */ + cp->State = kwsysProcess_State_Disowned; +} + +int kwsysProcess_WaitForData(kwsysProcess* cp, char** data, int* length, + double* userTimeout) +{ + kwsysProcessTime userStartTime; + kwsysProcessTime timeoutLength; + kwsysProcessTime timeoutTime; + DWORD timeout; + int user; + int done = 0; + int expired = 0; + int pipeId = kwsysProcess_Pipe_None; + DWORD w; + + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing || cp->Killed || + cp->TimeoutExpired) { + return kwsysProcess_Pipe_None; + } + + /* Record the time at which user timeout period starts. */ + userStartTime = kwsysProcessTimeGetCurrent(); + + /* Calculate the time at which a timeout will expire, and whether it + is the user or process timeout. */ + user = kwsysProcessGetTimeoutTime(cp, userTimeout, &timeoutTime); + + /* Loop until we have a reason to return. */ + while (!done && cp->PipesLeft > 0) { + /* If we previously got data from a thread, let it know we are + done with the data. */ + if (cp->CurrentIndex < KWSYSPE_PIPE_COUNT) { + KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); + ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); + cp->CurrentIndex = KWSYSPE_PIPE_COUNT; + } + + /* Setup a timeout if required. */ + if (kwsysProcessGetTimeoutLeft(&timeoutTime, user ? userTimeout : 0, + &timeoutLength)) { + /* Timeout has already expired. */ + expired = 1; + break; + } + if (timeoutTime.QuadPart < 0) { + timeout = INFINITE; + } else { + timeout = kwsysProcessTimeToDWORD(timeoutLength); + } + + /* Wait for a pipe's thread to signal or a process to terminate. */ + w = WaitForMultipleObjects(cp->ProcessEventsLength, cp->ProcessEvents, 0, + timeout); + if (w == WAIT_TIMEOUT) { + /* Timeout has expired. */ + expired = 1; + done = 1; + } else if (w == WAIT_OBJECT_0) { + /* Save the index of the reporting thread and release the mutex. + The thread will block until we signal its Empty mutex. */ + cp->CurrentIndex = cp->SharedIndex; + ReleaseSemaphore(cp->SharedIndexMutex, 1, 0); + + /* Data are available or a pipe closed. */ + if (cp->Pipe[cp->CurrentIndex].Closed) { + /* The pipe closed at the write end. Close the read end and + inform the wakeup thread it is done with this process. */ + kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read); + ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Waker.Go, 1, 0); + KWSYSPE_DEBUG((stderr, "wakeup %d\n", cp->CurrentIndex)); + --cp->PipesLeft; + } else if (data && length) { + /* Report this data. */ + *data = cp->Pipe[cp->CurrentIndex].DataBuffer; + *length = cp->Pipe[cp->CurrentIndex].DataLength; + switch (cp->CurrentIndex) { + case KWSYSPE_PIPE_STDOUT: + pipeId = kwsysProcess_Pipe_STDOUT; + break; + case KWSYSPE_PIPE_STDERR: + pipeId = kwsysProcess_Pipe_STDERR; + break; + } + done = 1; + } + } else { + /* A process has terminated. */ + kwsysProcessDestroy(cp, w - WAIT_OBJECT_0); + } + } + + /* Update the user timeout. */ + if (userTimeout) { + kwsysProcessTime userEndTime = kwsysProcessTimeGetCurrent(); + kwsysProcessTime difference = + kwsysProcessTimeSubtract(userEndTime, userStartTime); + double d = kwsysProcessTimeToDouble(difference); + *userTimeout -= d; + if (*userTimeout < 0) { + *userTimeout = 0; + } + } + + /* Check what happened. */ + if (pipeId) { + /* Data are ready on a pipe. */ + return pipeId; + } else if (expired) { + /* A timeout has expired. */ + if (user) { + /* The user timeout has expired. It has no time left. */ + return kwsysProcess_Pipe_Timeout; + } else { + /* The process timeout has expired. Kill the child now. */ + KWSYSPE_DEBUG((stderr, "killing child because timeout expired\n")); + kwsysProcess_Kill(cp); + cp->TimeoutExpired = 1; + cp->Killed = 0; + return kwsysProcess_Pipe_None; + } + } else { + /* The children have terminated and no more data are available. */ + return kwsysProcess_Pipe_None; + } +} + +int kwsysProcess_WaitForExit(kwsysProcess* cp, double* userTimeout) +{ + int i; + int pipe; + + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing) { + return 1; + } + + /* Wait for the process to terminate. Ignore all data. */ + while ((pipe = kwsysProcess_WaitForData(cp, 0, 0, userTimeout)) > 0) { + if (pipe == kwsysProcess_Pipe_Timeout) { + /* The user timeout has expired. */ + return 0; + } + } + + KWSYSPE_DEBUG((stderr, "no more data\n")); + + /* When the last pipe closes in WaitForData, the loop terminates + without releasing the pipe's thread. Release it now. */ + if (cp->CurrentIndex < KWSYSPE_PIPE_COUNT) { + KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); + ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); + cp->CurrentIndex = KWSYSPE_PIPE_COUNT; + } + + /* Wait for all pipe threads to reset. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + KWSYSPE_DEBUG((stderr, "waiting reader reset %d\n", i)); + WaitForSingleObject(cp->Pipe[i].Reader.Reset, INFINITE); + KWSYSPE_DEBUG((stderr, "waiting waker reset %d\n", i)); + WaitForSingleObject(cp->Pipe[i].Waker.Reset, INFINITE); + } + + /* ---- It is now safe again to call kwsysProcessCleanup. ----- */ + /* Close all the pipes. */ + kwsysProcessCleanup(cp, 0); + + /* Determine the outcome. */ + if (cp->Killed) { + /* We killed the child. */ + cp->State = kwsysProcess_State_Killed; + } else if (cp->TimeoutExpired) { + /* The timeout expired. */ + cp->State = kwsysProcess_State_Expired; + } else { + /* The children exited. Report the outcome of the child processes. */ + for (i = 0; i < cp->NumberOfCommands; ++i) { + cp->ProcessResults[i].ExitCode = cp->CommandExitCodes[i]; + if ((cp->ProcessResults[i].ExitCode & 0xF0000000) == 0xC0000000) { + /* Child terminated due to exceptional behavior. */ + cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Exception; + cp->ProcessResults[i].ExitValue = 1; + kwsysProcessSetExitExceptionByIndex(cp, cp->ProcessResults[i].ExitCode, + i); + } else { + /* Child exited without exception. */ + cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Exited; + cp->ProcessResults[i].ExitException = kwsysProcess_Exception_None; + cp->ProcessResults[i].ExitValue = cp->ProcessResults[i].ExitCode; + } + } + /* support legacy state status value */ + cp->State = cp->ProcessResults[cp->NumberOfCommands - 1].State; + } + + return 1; +} + +void kwsysProcess_Interrupt(kwsysProcess* cp) +{ + int i; + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || + cp->Killed) { + KWSYSPE_DEBUG((stderr, "interrupt: child not executing\n")); + return; + } + + /* Skip actually interrupting the child if it has already terminated. */ + if (cp->Terminated) { + KWSYSPE_DEBUG((stderr, "interrupt: child already terminated\n")); + return; + } + + /* Interrupt the children. */ + if (cp->CreateProcessGroup) { + if (cp->ProcessInformation) { + for (i = 0; i < cp->NumberOfCommands; ++i) { + /* Make sure the process handle isn't closed (e.g. from disowning). */ + if (cp->ProcessInformation[i].hProcess) { + /* The user created a process group for this process. The group ID + is the process ID for the original process in the group. Note + that we have to use Ctrl+Break: Ctrl+C is not allowed for process + groups. */ + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, + cp->ProcessInformation[i].dwProcessId); + } + } + } + } else { + /* No process group was created. Kill our own process group... */ + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0); + } +} + +void kwsysProcess_Kill(kwsysProcess* cp) +{ + int i; + /* Make sure we are executing a process. */ + if (!cp || cp->State != kwsysProcess_State_Executing || cp->TimeoutExpired || + cp->Killed) { + KWSYSPE_DEBUG((stderr, "kill: child not executing\n")); + return; + } + + /* Disable the reading threads. */ + KWSYSPE_DEBUG((stderr, "kill: disabling pipe threads\n")); + kwsysProcessDisablePipeThreads(cp); + + /* Skip actually killing the child if it has already terminated. */ + if (cp->Terminated) { + KWSYSPE_DEBUG((stderr, "kill: child already terminated\n")); + return; + } + + /* Kill the children. */ + cp->Killed = 1; + for (i = 0; i < cp->NumberOfCommands; ++i) { + kwsysProcessKillTree(cp->ProcessInformation[i].dwProcessId); + /* Remove from global list of processes and close handles. */ + kwsysProcessesRemove(cp->ProcessInformation[i].hProcess); + kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); + kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess); + } + + /* We are killing the children and ignoring all data. Do not wait + for them to exit. */ +} + +void kwsysProcess_KillPID(unsigned long process_id) +{ + kwsysProcessKillTree((DWORD)process_id); +} + +/* + Function executed for each pipe's thread. Argument is a pointer to + the kwsysProcessPipeData instance for this thread. +*/ +DWORD WINAPI kwsysProcessPipeThreadRead(LPVOID ptd) +{ + kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd; + kwsysProcess* cp = td->Process; + + /* Wait for a process to be ready. */ + while ((WaitForSingleObject(td->Reader.Ready, INFINITE), !cp->Deleting)) { + /* Read output from the process for this thread's pipe. */ + kwsysProcessPipeThreadReadPipe(cp, td); + + /* Signal the main thread we have reset for a new process. */ + ReleaseSemaphore(td->Reader.Reset, 1, 0); + } + return 0; +} + +/* + Function called in each pipe's thread to handle data for one + execution of a subprocess. +*/ +void kwsysProcessPipeThreadReadPipe(kwsysProcess* cp, kwsysProcessPipeData* td) +{ + /* Wait for space in the thread's buffer. */ + while ((KWSYSPE_DEBUG((stderr, "wait for read %d\n", td->Index)), + WaitForSingleObject(td->Reader.Go, INFINITE), !td->Closed)) { + KWSYSPE_DEBUG((stderr, "reading %d\n", td->Index)); + + /* Read data from the pipe. This may block until data are available. */ + if (!ReadFile(td->Read, td->DataBuffer, KWSYSPE_PIPE_BUFFER_SIZE, + &td->DataLength, 0)) { + if (GetLastError() != ERROR_BROKEN_PIPE) { + /* UNEXPECTED failure to read the pipe. */ + } + + /* The pipe closed. There are no more data to read. */ + td->Closed = 1; + KWSYSPE_DEBUG((stderr, "read closed %d\n", td->Index)); + } + + KWSYSPE_DEBUG((stderr, "read %d\n", td->Index)); + + /* Wait for our turn to be handled by the main thread. */ + WaitForSingleObject(cp->SharedIndexMutex, INFINITE); + + KWSYSPE_DEBUG((stderr, "reporting read %d\n", td->Index)); + + /* Tell the main thread we have something to report. */ + cp->SharedIndex = td->Index; + ReleaseSemaphore(cp->Full, 1, 0); + } + + /* We were signalled to exit with our buffer empty. Reset the + mutex for a new process. */ + KWSYSPE_DEBUG((stderr, "self releasing reader %d\n", td->Index)); + ReleaseSemaphore(td->Reader.Go, 1, 0); +} + +/* + Function executed for each pipe's thread. Argument is a pointer to + the kwsysProcessPipeData instance for this thread. +*/ +DWORD WINAPI kwsysProcessPipeThreadWake(LPVOID ptd) +{ + kwsysProcessPipeData* td = (kwsysProcessPipeData*)ptd; + kwsysProcess* cp = td->Process; + + /* Wait for a process to be ready. */ + while ((WaitForSingleObject(td->Waker.Ready, INFINITE), !cp->Deleting)) { + /* Wait for a possible wakeup. */ + kwsysProcessPipeThreadWakePipe(cp, td); + + /* Signal the main thread we have reset for a new process. */ + ReleaseSemaphore(td->Waker.Reset, 1, 0); + } + return 0; +} + +/* + Function called in each pipe's thread to handle reading thread + wakeup for one execution of a subprocess. +*/ +void kwsysProcessPipeThreadWakePipe(kwsysProcess* cp, kwsysProcessPipeData* td) +{ + (void)cp; + + /* Wait for a possible wake command. */ + KWSYSPE_DEBUG((stderr, "wait for wake %d\n", td->Index)); + WaitForSingleObject(td->Waker.Go, INFINITE); + KWSYSPE_DEBUG((stderr, "waking %d\n", td->Index)); + + /* If the pipe is not closed, we need to wake up the reading thread. */ + if (!td->Closed) { + DWORD dummy; + KWSYSPE_DEBUG((stderr, "waker %d writing byte\n", td->Index)); + WriteFile(td->Write, "", 1, &dummy, 0); + KWSYSPE_DEBUG((stderr, "waker %d wrote byte\n", td->Index)); + } +} + +/* Initialize a process control structure for kwsysProcess_Execute. */ +int kwsysProcessInitialize(kwsysProcess* cp) +{ + int i; + /* Reset internal status flags. */ + cp->TimeoutExpired = 0; + cp->Terminated = 0; + cp->Killed = 0; + + free(cp->ProcessResults); + /* Allocate process result information for each process. */ + cp->ProcessResults = (kwsysProcessResults*)malloc( + sizeof(kwsysProcessResults) * (cp->NumberOfCommands)); + if (!cp->ProcessResults) { + return 0; + } + ZeroMemory(cp->ProcessResults, + sizeof(kwsysProcessResults) * cp->NumberOfCommands); + for (i = 0; i < cp->NumberOfCommands; i++) { + cp->ProcessResults[i].ExitException = kwsysProcess_Exception_None; + cp->ProcessResults[i].State = kwsysProcess_StateByIndex_Starting; + cp->ProcessResults[i].ExitCode = 1; + cp->ProcessResults[i].ExitValue = 1; + strcpy(cp->ProcessResults[i].ExitExceptionString, "No exception"); + } + + /* Allocate process information for each process. */ + free(cp->ProcessInformation); + cp->ProcessInformation = (PROCESS_INFORMATION*)malloc( + sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands); + if (!cp->ProcessInformation) { + return 0; + } + ZeroMemory(cp->ProcessInformation, + sizeof(PROCESS_INFORMATION) * cp->NumberOfCommands); + free(cp->CommandExitCodes); + cp->CommandExitCodes = (DWORD*)malloc(sizeof(DWORD) * cp->NumberOfCommands); + if (!cp->CommandExitCodes) { + return 0; + } + ZeroMemory(cp->CommandExitCodes, sizeof(DWORD) * cp->NumberOfCommands); + + /* Allocate event wait array. The first event is cp->Full, the rest + are the process termination events. */ + cp->ProcessEvents = + (PHANDLE)malloc(sizeof(HANDLE) * (cp->NumberOfCommands + 1)); + if (!cp->ProcessEvents) { + return 0; + } + ZeroMemory(cp->ProcessEvents, sizeof(HANDLE) * (cp->NumberOfCommands + 1)); + cp->ProcessEvents[0] = cp->Full; + cp->ProcessEventsLength = cp->NumberOfCommands + 1; + + /* Allocate space to save the real working directory of this process. */ + if (cp->WorkingDirectory) { + cp->RealWorkingDirectoryLength = GetCurrentDirectoryW(0, 0); + if (cp->RealWorkingDirectoryLength > 0) { + cp->RealWorkingDirectory = + malloc(cp->RealWorkingDirectoryLength * sizeof(wchar_t)); + if (!cp->RealWorkingDirectory) { + return 0; + } + } + } + { + for (i = 0; i < 3; ++i) { + cp->PipeChildStd[i] = INVALID_HANDLE_VALUE; + } + } + + return 1; +} + +static DWORD kwsysProcessCreateChildHandle(PHANDLE out, HANDLE in, int isStdIn) +{ + DWORD flags; + + /* Check whether the handle is valid for this process. */ + if (in != INVALID_HANDLE_VALUE && GetHandleInformation(in, &flags)) { + /* Use the handle as-is if it is already inherited. */ + if (flags & HANDLE_FLAG_INHERIT) { + *out = in; + return ERROR_SUCCESS; + } + + /* Create an inherited copy of this handle. */ + if (DuplicateHandle(GetCurrentProcess(), in, GetCurrentProcess(), out, 0, + TRUE, DUPLICATE_SAME_ACCESS)) { + return ERROR_SUCCESS; + } else { + return GetLastError(); + } + } else { + /* The given handle is not valid for this process. Some child + processes may break if they do not have a valid standard handle, + so open NUL to give to the child. */ + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(sa)); + sa.nLength = (DWORD)sizeof(sa); + sa.bInheritHandle = 1; + *out = CreateFileW( + L"NUL", + (isStdIn ? GENERIC_READ : (GENERIC_WRITE | FILE_READ_ATTRIBUTES)), + FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0); + return (*out != INVALID_HANDLE_VALUE) ? ERROR_SUCCESS : GetLastError(); + } +} + +DWORD kwsysProcessCreate(kwsysProcess* cp, int index, + kwsysProcessCreateInformation* si) +{ + DWORD creationFlags; + DWORD error = ERROR_SUCCESS; + + /* Check if we are currently exiting. */ + if (!kwsysTryEnterCreateProcessSection()) { + /* The Ctrl handler is currently working on exiting our process. Rather + than return an error code, which could cause incorrect conclusions to be + reached by the caller, we simply hang. (For example, a CMake try_run + configure step might cause the project to configure wrong.) */ + Sleep(INFINITE); + } + + /* Create the child in a suspended state so we can wait until all + children have been created before running any one. */ + creationFlags = CREATE_SUSPENDED; + if (cp->CreateProcessGroup) { + creationFlags |= CREATE_NEW_PROCESS_GROUP; + } + + /* Create inherited copies of the handles. */ + (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdInput, + si->hStdInput, 1)) || + (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdOutput, + si->hStdOutput, 0)) || + (error = kwsysProcessCreateChildHandle(&si->StartupInfo.hStdError, + si->hStdError, 0)) || + /* Create the process. */ + (!CreateProcessW(0, cp->Commands[index], 0, 0, TRUE, creationFlags, 0, 0, + &si->StartupInfo, &cp->ProcessInformation[index]) && + (error = GetLastError())); + + /* Close the inherited copies of the handles. */ + if (si->StartupInfo.hStdInput != si->hStdInput) { + kwsysProcessCleanupHandle(&si->StartupInfo.hStdInput); + } + if (si->StartupInfo.hStdOutput != si->hStdOutput) { + kwsysProcessCleanupHandle(&si->StartupInfo.hStdOutput); + } + if (si->StartupInfo.hStdError != si->hStdError) { + kwsysProcessCleanupHandle(&si->StartupInfo.hStdError); + } + + /* Add the process to the global list of processes. */ + if (!error && + !kwsysProcessesAdd(cp->ProcessInformation[index].hProcess, + cp->ProcessInformation[index].dwProcessId, + cp->CreateProcessGroup)) { + /* This failed for some reason. Kill the suspended process. */ + TerminateProcess(cp->ProcessInformation[index].hProcess, 1); + /* And clean up... */ + kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess); + kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hThread); + strcpy(cp->ErrorMessage, "kwsysProcessesAdd function failed"); + error = ERROR_NOT_ENOUGH_MEMORY; /* Most likely reason. */ + } + + /* If the console Ctrl handler is waiting for us, this will release it... */ + kwsysLeaveCreateProcessSection(); + return error; +} + +void kwsysProcessDestroy(kwsysProcess* cp, int event) +{ + int i; + int index; + + /* Find the process index for the termination event. */ + for (index = 0; index < cp->NumberOfCommands; ++index) { + if (cp->ProcessInformation[index].hProcess == cp->ProcessEvents[event]) { + break; + } + } + + /* Check the exit code of the process. */ + GetExitCodeProcess(cp->ProcessInformation[index].hProcess, + &cp->CommandExitCodes[index]); + + /* Remove from global list of processes. */ + kwsysProcessesRemove(cp->ProcessInformation[index].hProcess); + + /* Close the process handle for the terminated process. */ + kwsysProcessCleanupHandle(&cp->ProcessInformation[index].hProcess); + + /* Remove the process from the available events. */ + cp->ProcessEventsLength -= 1; + for (i = event; i < cp->ProcessEventsLength; ++i) { + cp->ProcessEvents[i] = cp->ProcessEvents[i + 1]; + } + + /* Check if all processes have terminated. */ + if (cp->ProcessEventsLength == 1) { + cp->Terminated = 1; + + /* Close our copies of the pipe write handles so the pipe threads + can detect end-of-data. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + /* TODO: If the child created its own child (our grandchild) + which inherited a copy of the pipe write-end then the pipe + may not close and we will still need the waker write pipe. + However we still want to be able to detect end-of-data in the + normal case. The reader thread will have to switch to using + PeekNamedPipe to read the last bit of data from the pipe + without blocking. This is equivalent to using a non-blocking + read on posix. */ + KWSYSPE_DEBUG((stderr, "closing wakeup write %d\n", i)); + kwsysProcessCleanupHandle(&cp->Pipe[i].Write); + } + } +} + +DWORD kwsysProcessSetupOutputPipeFile(PHANDLE phandle, const char* name) +{ + HANDLE fout; + wchar_t* wname; + DWORD error; + if (!name) { + return ERROR_INVALID_PARAMETER; + } + + /* Close the existing handle. */ + kwsysProcessCleanupHandle(phandle); + + /* Create a handle to write a file for the pipe. */ + wname = kwsysEncoding_DupToWide(name); + fout = + CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); + error = GetLastError(); + free(wname); + if (fout == INVALID_HANDLE_VALUE) { + return error; + } + + /* Assign the replacement handle. */ + *phandle = fout; + return ERROR_SUCCESS; +} + +void kwsysProcessSetupSharedPipe(DWORD nStdHandle, PHANDLE handle) +{ + /* Close the existing handle. */ + kwsysProcessCleanupHandle(handle); + /* Store the new standard handle. */ + *handle = GetStdHandle(nStdHandle); +} + +void kwsysProcessSetupPipeNative(HANDLE native, PHANDLE handle) +{ + /* Close the existing handle. */ + kwsysProcessCleanupHandle(handle); + /* Store the new given handle. */ + *handle = native; +} + +/* Close the given handle if it is open. Reset its value to 0. */ +void kwsysProcessCleanupHandle(PHANDLE h) +{ + if (h && *h && *h != INVALID_HANDLE_VALUE && + *h != GetStdHandle(STD_INPUT_HANDLE) && + *h != GetStdHandle(STD_OUTPUT_HANDLE) && + *h != GetStdHandle(STD_ERROR_HANDLE)) { + CloseHandle(*h); + *h = INVALID_HANDLE_VALUE; + } +} + +/* Close all handles created by kwsysProcess_Execute. */ +void kwsysProcessCleanup(kwsysProcess* cp, DWORD error) +{ + int i; + /* If this is an error case, report the error. */ + if (error) { + /* Construct an error message if one has not been provided already. */ + if (cp->ErrorMessage[0] == 0) { + /* Format the error message. */ + wchar_t err_msg[KWSYSPE_PIPE_BUFFER_SIZE]; + DWORD length = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_msg, + KWSYSPE_PIPE_BUFFER_SIZE, 0); + if (length < 1) { + /* FormatMessage failed. Use a default message. */ + _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, + "Process execution failed with error 0x%X. " + "FormatMessage failed with error 0x%X", + error, GetLastError()); + } + if (!WideCharToMultiByte(CP_UTF8, 0, err_msg, -1, cp->ErrorMessage, + KWSYSPE_PIPE_BUFFER_SIZE, NULL, NULL)) { + /* WideCharToMultiByte failed. Use a default message. */ + _snprintf(cp->ErrorMessage, KWSYSPE_PIPE_BUFFER_SIZE, + "Process execution failed with error 0x%X. " + "WideCharToMultiByte failed with error 0x%X", + error, GetLastError()); + } + } + + /* Remove trailing period and newline, if any. */ + kwsysProcessCleanErrorMessage(cp); + + /* Set the error state. */ + cp->State = kwsysProcess_State_Error; + + /* Cleanup any processes already started in a suspended state. */ + if (cp->ProcessInformation) { + for (i = 0; i < cp->NumberOfCommands; ++i) { + if (cp->ProcessInformation[i].hProcess) { + TerminateProcess(cp->ProcessInformation[i].hProcess, 255); + WaitForSingleObject(cp->ProcessInformation[i].hProcess, INFINITE); + } + } + for (i = 0; i < cp->NumberOfCommands; ++i) { + /* Remove from global list of processes and close handles. */ + kwsysProcessesRemove(cp->ProcessInformation[i].hProcess); + kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hThread); + kwsysProcessCleanupHandle(&cp->ProcessInformation[i].hProcess); + } + } + + /* Restore the working directory. */ + if (cp->RealWorkingDirectory) { + SetCurrentDirectoryW(cp->RealWorkingDirectory); + } + } + + /* Free memory. */ + if (cp->ProcessInformation) { + free(cp->ProcessInformation); + cp->ProcessInformation = 0; + } + if (cp->ProcessEvents) { + free(cp->ProcessEvents); + cp->ProcessEvents = 0; + } + if (cp->RealWorkingDirectory) { + free(cp->RealWorkingDirectory); + cp->RealWorkingDirectory = 0; + } + + /* Close each pipe. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + kwsysProcessCleanupHandle(&cp->Pipe[i].Write); + kwsysProcessCleanupHandle(&cp->Pipe[i].Read); + cp->Pipe[i].Closed = 0; + } + for (i = 0; i < 3; ++i) { + kwsysProcessCleanupHandle(&cp->PipeChildStd[i]); + } +} + +void kwsysProcessCleanErrorMessage(kwsysProcess* cp) +{ + /* Remove trailing period and newline, if any. */ + size_t length = strlen(cp->ErrorMessage); + if (cp->ErrorMessage[length - 1] == '\n') { + cp->ErrorMessage[length - 1] = 0; + --length; + if (length > 0 && cp->ErrorMessage[length - 1] == '\r') { + cp->ErrorMessage[length - 1] = 0; + --length; + } + } + if (length > 0 && cp->ErrorMessage[length - 1] == '.') { + cp->ErrorMessage[length - 1] = 0; + } +} + +/* Get the time at which either the process or user timeout will + expire. Returns 1 if the user timeout is first, and 0 otherwise. */ +int kwsysProcessGetTimeoutTime(kwsysProcess* cp, double* userTimeout, + kwsysProcessTime* timeoutTime) +{ + /* The first time this is called, we need to calculate the time at + which the child will timeout. */ + if (cp->Timeout && cp->TimeoutTime.QuadPart < 0) { + kwsysProcessTime length = kwsysProcessTimeFromDouble(cp->Timeout); + cp->TimeoutTime = kwsysProcessTimeAdd(cp->StartTime, length); + } + + /* Start with process timeout. */ + *timeoutTime = cp->TimeoutTime; + + /* Check if the user timeout is earlier. */ + if (userTimeout) { + kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); + kwsysProcessTime userTimeoutLength = + kwsysProcessTimeFromDouble(*userTimeout); + kwsysProcessTime userTimeoutTime = + kwsysProcessTimeAdd(currentTime, userTimeoutLength); + if (timeoutTime->QuadPart < 0 || + kwsysProcessTimeLess(userTimeoutTime, *timeoutTime)) { + *timeoutTime = userTimeoutTime; + return 1; + } + } + return 0; +} + +/* Get the length of time before the given timeout time arrives. + Returns 1 if the time has already arrived, and 0 otherwise. */ +int kwsysProcessGetTimeoutLeft(kwsysProcessTime* timeoutTime, + double* userTimeout, + kwsysProcessTime* timeoutLength) +{ + if (timeoutTime->QuadPart < 0) { + /* No timeout time has been requested. */ + return 0; + } else { + /* Calculate the remaining time. */ + kwsysProcessTime currentTime = kwsysProcessTimeGetCurrent(); + *timeoutLength = kwsysProcessTimeSubtract(*timeoutTime, currentTime); + + if (timeoutLength->QuadPart < 0 && userTimeout && *userTimeout <= 0) { + /* Caller has explicitly requested a zero timeout. */ + timeoutLength->QuadPart = 0; + } + + if (timeoutLength->QuadPart < 0) { + /* Timeout has already expired. */ + return 1; + } else { + /* There is some time left. */ + return 0; + } + } +} + +kwsysProcessTime kwsysProcessTimeGetCurrent() +{ + kwsysProcessTime current; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + current.LowPart = ft.dwLowDateTime; + current.HighPart = ft.dwHighDateTime; + return current; +} + +DWORD kwsysProcessTimeToDWORD(kwsysProcessTime t) +{ + return (DWORD)(t.QuadPart * 0.0001); +} + +double kwsysProcessTimeToDouble(kwsysProcessTime t) +{ + return t.QuadPart * 0.0000001; +} + +kwsysProcessTime kwsysProcessTimeFromDouble(double d) +{ + kwsysProcessTime t; + t.QuadPart = (LONGLONG)(d * 10000000); + return t; +} + +int kwsysProcessTimeLess(kwsysProcessTime in1, kwsysProcessTime in2) +{ + return in1.QuadPart < in2.QuadPart; +} + +kwsysProcessTime kwsysProcessTimeAdd(kwsysProcessTime in1, + kwsysProcessTime in2) +{ + kwsysProcessTime out; + out.QuadPart = in1.QuadPart + in2.QuadPart; + return out; +} + +kwsysProcessTime kwsysProcessTimeSubtract(kwsysProcessTime in1, + kwsysProcessTime in2) +{ + kwsysProcessTime out; + out.QuadPart = in1.QuadPart - in2.QuadPart; + return out; +} + +#define KWSYSPE_CASE(type, str) \ + cp->ProcessResults[idx].ExitException = kwsysProcess_Exception_##type; \ + strcpy(cp->ProcessResults[idx].ExitExceptionString, str) +static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code, + int idx) +{ + switch (code) { + case STATUS_CONTROL_C_EXIT: + KWSYSPE_CASE(Interrupt, "User interrupt"); + break; + + case STATUS_FLOAT_DENORMAL_OPERAND: + KWSYSPE_CASE(Numerical, "Floating-point exception (denormal operand)"); + break; + case STATUS_FLOAT_DIVIDE_BY_ZERO: + KWSYSPE_CASE(Numerical, "Divide-by-zero"); + break; + case STATUS_FLOAT_INEXACT_RESULT: + KWSYSPE_CASE(Numerical, "Floating-point exception (inexact result)"); + break; + case STATUS_FLOAT_INVALID_OPERATION: + KWSYSPE_CASE(Numerical, "Invalid floating-point operation"); + break; + case STATUS_FLOAT_OVERFLOW: + KWSYSPE_CASE(Numerical, "Floating-point overflow"); + break; + case STATUS_FLOAT_STACK_CHECK: + KWSYSPE_CASE(Numerical, "Floating-point stack check failed"); + break; + case STATUS_FLOAT_UNDERFLOW: + KWSYSPE_CASE(Numerical, "Floating-point underflow"); + break; +#ifdef STATUS_FLOAT_MULTIPLE_FAULTS + case STATUS_FLOAT_MULTIPLE_FAULTS: + KWSYSPE_CASE(Numerical, "Floating-point exception (multiple faults)"); + break; +#endif +#ifdef STATUS_FLOAT_MULTIPLE_TRAPS + case STATUS_FLOAT_MULTIPLE_TRAPS: + KWSYSPE_CASE(Numerical, "Floating-point exception (multiple traps)"); + break; +#endif + case STATUS_INTEGER_DIVIDE_BY_ZERO: + KWSYSPE_CASE(Numerical, "Integer divide-by-zero"); + break; + case STATUS_INTEGER_OVERFLOW: + KWSYSPE_CASE(Numerical, "Integer overflow"); + break; + + case STATUS_DATATYPE_MISALIGNMENT: + KWSYSPE_CASE(Fault, "Datatype misalignment"); + break; + case STATUS_ACCESS_VIOLATION: + KWSYSPE_CASE(Fault, "Access violation"); + break; + case STATUS_IN_PAGE_ERROR: + KWSYSPE_CASE(Fault, "In-page error"); + break; + case STATUS_INVALID_HANDLE: + KWSYSPE_CASE(Fault, "Invalid hanlde"); + break; + case STATUS_NONCONTINUABLE_EXCEPTION: + KWSYSPE_CASE(Fault, "Noncontinuable exception"); + break; + case STATUS_INVALID_DISPOSITION: + KWSYSPE_CASE(Fault, "Invalid disposition"); + break; + case STATUS_ARRAY_BOUNDS_EXCEEDED: + KWSYSPE_CASE(Fault, "Array bounds exceeded"); + break; + case STATUS_STACK_OVERFLOW: + KWSYSPE_CASE(Fault, "Stack overflow"); + break; + + case STATUS_ILLEGAL_INSTRUCTION: + KWSYSPE_CASE(Illegal, "Illegal instruction"); + break; + case STATUS_PRIVILEGED_INSTRUCTION: + KWSYSPE_CASE(Illegal, "Privileged instruction"); + break; + + case STATUS_NO_MEMORY: + default: + cp->ProcessResults[idx].ExitException = kwsysProcess_Exception_Other; + _snprintf(cp->ProcessResults[idx].ExitExceptionString, + KWSYSPE_PIPE_BUFFER_SIZE, "Exit code 0x%x\n", code); + break; + } +} +#undef KWSYSPE_CASE + +typedef struct kwsysProcess_List_s kwsysProcess_List; +static kwsysProcess_List* kwsysProcess_List_New(void); +static void kwsysProcess_List_Delete(kwsysProcess_List* self); +static int kwsysProcess_List_Update(kwsysProcess_List* self); +static int kwsysProcess_List_NextProcess(kwsysProcess_List* self); +static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self); +static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self); + +/* Windows NT 4 API definitions. */ +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +typedef LONG NTSTATUS; +typedef LONG KPRIORITY; +typedef struct _UNICODE_STRING UNICODE_STRING; +struct _UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +}; + +/* The process information structure. Declare only enough to get + process identifiers. The rest may be ignored because we use the + NextEntryDelta to move through an array of instances. */ +typedef struct _SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION; +typedef SYSTEM_PROCESS_INFORMATION* PSYSTEM_PROCESS_INFORMATION; +struct _SYSTEM_PROCESS_INFORMATION +{ + ULONG NextEntryDelta; + ULONG ThreadCount; + ULONG Reserved1[6]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ProcessName; + KPRIORITY BasePriority; + ULONG ProcessId; + ULONG InheritedFromProcessId; +}; + +/* Toolhelp32 API definitions. */ +#define TH32CS_SNAPPROCESS 0x00000002 +#if defined(_WIN64) +typedef unsigned __int64 ProcessULONG_PTR; +#else +typedef unsigned long ProcessULONG_PTR; +#endif +typedef struct tagPROCESSENTRY32 PROCESSENTRY32; +typedef PROCESSENTRY32* LPPROCESSENTRY32; +struct tagPROCESSENTRY32 +{ + DWORD dwSize; + DWORD cntUsage; + DWORD th32ProcessID; + ProcessULONG_PTR th32DefaultHeapID; + DWORD th32ModuleID; + DWORD cntThreads; + DWORD th32ParentProcessID; + LONG pcPriClassBase; + DWORD dwFlags; + char szExeFile[MAX_PATH]; +}; + +/* Windows API function types. */ +typedef HANDLE(WINAPI* CreateToolhelp32SnapshotType)(DWORD, DWORD); +typedef BOOL(WINAPI* Process32FirstType)(HANDLE, LPPROCESSENTRY32); +typedef BOOL(WINAPI* Process32NextType)(HANDLE, LPPROCESSENTRY32); +typedef NTSTATUS(WINAPI* ZwQuerySystemInformationType)(ULONG, PVOID, ULONG, + PULONG); + +static int kwsysProcess_List__New_NT4(kwsysProcess_List* self); +static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self); +static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self); +static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self); +static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self); +static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self); +static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self); +static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self); +static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self); +static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self); +static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self); +static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self); + +struct kwsysProcess_List_s +{ + /* Implementation switches at runtime based on version of Windows. */ + int NT4; + + /* Implementation functions and data for NT 4. */ + ZwQuerySystemInformationType P_ZwQuerySystemInformation; + char* Buffer; + int BufferSize; + PSYSTEM_PROCESS_INFORMATION CurrentInfo; + + /* Implementation functions and data for other Windows versions. */ + CreateToolhelp32SnapshotType P_CreateToolhelp32Snapshot; + Process32FirstType P_Process32First; + Process32NextType P_Process32Next; + HANDLE Snapshot; + PROCESSENTRY32 CurrentEntry; +}; + +static kwsysProcess_List* kwsysProcess_List_New(void) +{ + OSVERSIONINFO osv; + kwsysProcess_List* self; + + /* Allocate and initialize the list object. */ + if (!(self = (kwsysProcess_List*)malloc(sizeof(kwsysProcess_List)))) { + return 0; + } + memset(self, 0, sizeof(*self)); + + /* Select an implementation. */ + ZeroMemory(&osv, sizeof(osv)); + osv.dwOSVersionInfoSize = sizeof(osv); +#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +#endif + GetVersionEx(&osv); +#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +#endif + self->NT4 = + (osv.dwPlatformId == VER_PLATFORM_WIN32_NT && osv.dwMajorVersion < 5) ? 1 + : 0; + + /* Initialize the selected implementation. */ + if (!(self->NT4 ? kwsysProcess_List__New_NT4(self) + : kwsysProcess_List__New_Snapshot(self))) { + kwsysProcess_List_Delete(self); + return 0; + } + + /* Update to the current set of processes. */ + if (!kwsysProcess_List_Update(self)) { + kwsysProcess_List_Delete(self); + return 0; + } + return self; +} + +static void kwsysProcess_List_Delete(kwsysProcess_List* self) +{ + if (self) { + if (self->NT4) { + kwsysProcess_List__Delete_NT4(self); + } else { + kwsysProcess_List__Delete_Snapshot(self); + } + free(self); + } +} + +static int kwsysProcess_List_Update(kwsysProcess_List* self) +{ + return self ? (self->NT4 ? kwsysProcess_List__Update_NT4(self) + : kwsysProcess_List__Update_Snapshot(self)) + : 0; +} + +static int kwsysProcess_List_GetCurrentProcessId(kwsysProcess_List* self) +{ + return self ? (self->NT4 ? kwsysProcess_List__GetProcessId_NT4(self) + : kwsysProcess_List__GetProcessId_Snapshot(self)) + : -1; +} + +static int kwsysProcess_List_GetCurrentParentId(kwsysProcess_List* self) +{ + return self ? (self->NT4 ? kwsysProcess_List__GetParentId_NT4(self) + : kwsysProcess_List__GetParentId_Snapshot(self)) + : -1; +} + +static int kwsysProcess_List_NextProcess(kwsysProcess_List* self) +{ + return (self ? (self->NT4 ? kwsysProcess_List__Next_NT4(self) + : kwsysProcess_List__Next_Snapshot(self)) + : 0); +} + +static int kwsysProcess_List__New_NT4(kwsysProcess_List* self) +{ + /* Get a handle to the NT runtime module that should already be + loaded in this program. This does not actually increment the + reference count to the module so we do not need to close the + handle. */ + HMODULE hNT = GetModuleHandleW(L"ntdll.dll"); + if (hNT) { + /* Get pointers to the needed API functions. */ + self->P_ZwQuerySystemInformation = + ((ZwQuerySystemInformationType)GetProcAddress( + hNT, "ZwQuerySystemInformation")); + } + if (!self->P_ZwQuerySystemInformation) { + return 0; + } + + /* Allocate an initial process information buffer. */ + self->BufferSize = 32768; + self->Buffer = (char*)malloc(self->BufferSize); + return self->Buffer ? 1 : 0; +} + +static void kwsysProcess_List__Delete_NT4(kwsysProcess_List* self) +{ + /* Free the process information buffer. */ + free(self->Buffer); +} + +static int kwsysProcess_List__Update_NT4(kwsysProcess_List* self) +{ + self->CurrentInfo = 0; + for (;;) { + /* Query number 5 is for system process list. */ + NTSTATUS status = + self->P_ZwQuerySystemInformation(5, self->Buffer, self->BufferSize, 0); + if (status == STATUS_INFO_LENGTH_MISMATCH) { + /* The query requires a bigger buffer. */ + int newBufferSize = self->BufferSize * 2; + char* newBuffer = (char*)malloc(newBufferSize); + if (newBuffer) { + free(self->Buffer); + self->Buffer = newBuffer; + self->BufferSize = newBufferSize; + } else { + return 0; + } + } else if (status >= 0) { + /* The query succeeded. Initialize traversal of the process list. */ + self->CurrentInfo = (PSYSTEM_PROCESS_INFORMATION)self->Buffer; + return 1; + } else { + /* The query failed. */ + return 0; + } + } +} + +static int kwsysProcess_List__Next_NT4(kwsysProcess_List* self) +{ + if (self->CurrentInfo) { + if (self->CurrentInfo->NextEntryDelta > 0) { + self->CurrentInfo = ((PSYSTEM_PROCESS_INFORMATION)( + (char*)self->CurrentInfo + self->CurrentInfo->NextEntryDelta)); + return 1; + } + self->CurrentInfo = 0; + } + return 0; +} + +static int kwsysProcess_List__GetProcessId_NT4(kwsysProcess_List* self) +{ + return self->CurrentInfo ? self->CurrentInfo->ProcessId : -1; +} + +static int kwsysProcess_List__GetParentId_NT4(kwsysProcess_List* self) +{ + return self->CurrentInfo ? self->CurrentInfo->InheritedFromProcessId : -1; +} + +static int kwsysProcess_List__New_Snapshot(kwsysProcess_List* self) +{ + /* Get a handle to the Windows runtime module that should already be + loaded in this program. This does not actually increment the + reference count to the module so we do not need to close the + handle. */ + HMODULE hKernel = GetModuleHandleW(L"kernel32.dll"); + if (hKernel) { + self->P_CreateToolhelp32Snapshot = + ((CreateToolhelp32SnapshotType)GetProcAddress( + hKernel, "CreateToolhelp32Snapshot")); + self->P_Process32First = + ((Process32FirstType)GetProcAddress(hKernel, "Process32First")); + self->P_Process32Next = + ((Process32NextType)GetProcAddress(hKernel, "Process32Next")); + } + return (self->P_CreateToolhelp32Snapshot && self->P_Process32First && + self->P_Process32Next) + ? 1 + : 0; +} + +static void kwsysProcess_List__Delete_Snapshot(kwsysProcess_List* self) +{ + if (self->Snapshot) { + CloseHandle(self->Snapshot); + } +} + +static int kwsysProcess_List__Update_Snapshot(kwsysProcess_List* self) +{ + if (self->Snapshot) { + CloseHandle(self->Snapshot); + } + if (!(self->Snapshot = + self->P_CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) { + return 0; + } + ZeroMemory(&self->CurrentEntry, sizeof(self->CurrentEntry)); + self->CurrentEntry.dwSize = sizeof(self->CurrentEntry); + if (!self->P_Process32First(self->Snapshot, &self->CurrentEntry)) { + CloseHandle(self->Snapshot); + self->Snapshot = 0; + return 0; + } + return 1; +} + +static int kwsysProcess_List__Next_Snapshot(kwsysProcess_List* self) +{ + if (self->Snapshot) { + if (self->P_Process32Next(self->Snapshot, &self->CurrentEntry)) { + return 1; + } + CloseHandle(self->Snapshot); + self->Snapshot = 0; + } + return 0; +} + +static int kwsysProcess_List__GetProcessId_Snapshot(kwsysProcess_List* self) +{ + return self->Snapshot ? self->CurrentEntry.th32ProcessID : -1; +} + +static int kwsysProcess_List__GetParentId_Snapshot(kwsysProcess_List* self) +{ + return self->Snapshot ? self->CurrentEntry.th32ParentProcessID : -1; +} + +static void kwsysProcessKill(DWORD pid) +{ + HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, pid); + if (h) { + TerminateProcess(h, 255); + WaitForSingleObject(h, INFINITE); + CloseHandle(h); + } +} + +static void kwsysProcessKillTree(int pid) +{ + kwsysProcess_List* plist = kwsysProcess_List_New(); + kwsysProcessKill(pid); + if (plist) { + do { + if (kwsysProcess_List_GetCurrentParentId(plist) == pid) { + int ppid = kwsysProcess_List_GetCurrentProcessId(plist); + kwsysProcessKillTree(ppid); + } + } while (kwsysProcess_List_NextProcess(plist)); + kwsysProcess_List_Delete(plist); + } +} + +static void kwsysProcessDisablePipeThreads(kwsysProcess* cp) +{ + int i; + + /* If data were just reported data, release the pipe's thread. */ + if (cp->CurrentIndex < KWSYSPE_PIPE_COUNT) { + KWSYSPE_DEBUG((stderr, "releasing reader %d\n", cp->CurrentIndex)); + ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); + cp->CurrentIndex = KWSYSPE_PIPE_COUNT; + } + + /* Wakeup all reading threads that are not on closed pipes. */ + for (i = 0; i < KWSYSPE_PIPE_COUNT; ++i) { + /* The wakeup threads will write one byte to the pipe write ends. + If there are no data in the pipe then this is enough to wakeup + the reading threads. If there are already data in the pipe + this may block. We cannot use PeekNamedPipe to check whether + there are data because an outside process might still be + writing data if we are disowning it. Also, PeekNamedPipe will + block if checking a pipe on which the reading thread is + currently calling ReadPipe. Therefore we need a separate + thread to call WriteFile. If it blocks, that is okay because + it will unblock when we close the read end and break the pipe + below. */ + if (cp->Pipe[i].Read) { + KWSYSPE_DEBUG((stderr, "releasing waker %d\n", i)); + ReleaseSemaphore(cp->Pipe[i].Waker.Go, 1, 0); + } + } + + /* Tell pipe threads to reset until we run another process. */ + while (cp->PipesLeft > 0) { + /* The waking threads will cause all reading threads to report. + Wait for the next one and save its index. */ + KWSYSPE_DEBUG((stderr, "waiting for reader\n")); + WaitForSingleObject(cp->Full, INFINITE); + cp->CurrentIndex = cp->SharedIndex; + ReleaseSemaphore(cp->SharedIndexMutex, 1, 0); + KWSYSPE_DEBUG((stderr, "got reader %d\n", cp->CurrentIndex)); + + /* We are done reading this pipe. Close its read handle. */ + cp->Pipe[cp->CurrentIndex].Closed = 1; + kwsysProcessCleanupHandle(&cp->Pipe[cp->CurrentIndex].Read); + --cp->PipesLeft; + + /* Tell the reading thread we are done with the data. It will + reset immediately because the pipe is closed. */ + ReleaseSemaphore(cp->Pipe[cp->CurrentIndex].Reader.Go, 1, 0); + } +} + +/* Global set of executing processes for use by the Ctrl handler. + This global instance will be zero-initialized by the compiler. + + Note that the console Ctrl handler runs on a background thread and so + everything it does must be thread safe. Here, we track the hProcess + HANDLEs directly instead of kwsysProcess instances, so that we don't have + to make kwsysProcess thread safe. */ +typedef struct kwsysProcessInstance_s +{ + HANDLE hProcess; + DWORD dwProcessId; + int NewProcessGroup; /* Whether the process was created in a new group. */ +} kwsysProcessInstance; + +typedef struct kwsysProcessInstances_s +{ + /* Whether we have initialized key fields below, like critical sections. */ + int Initialized; + + /* Ctrl handler runs on a different thread, so we must sync access. */ + CRITICAL_SECTION Lock; + + int Exiting; + size_t Count; + size_t Size; + kwsysProcessInstance* Processes; +} kwsysProcessInstances; +static kwsysProcessInstances kwsysProcesses; + +/* Initialize critial section and set up console Ctrl handler. You MUST call + this before using any other kwsysProcesses* functions below. */ +static int kwsysProcessesInitialize(void) +{ + /* Initialize everything if not done already. */ + if (!kwsysProcesses.Initialized) { + InitializeCriticalSection(&kwsysProcesses.Lock); + + /* Set up console ctrl handler. */ + if (!SetConsoleCtrlHandler(kwsysCtrlHandler, TRUE)) { + return 0; + } + + kwsysProcesses.Initialized = 1; + } + return 1; +} + +/* The Ctrl handler waits on the global list of processes. To prevent an + orphaned process, do not create a new process if the Ctrl handler is + already running. Do so by using this function to check if it is ok to + create a process. */ +static int kwsysTryEnterCreateProcessSection(void) +{ + /* Enter main critical section; this means creating a process and the Ctrl + handler are mutually exclusive. */ + EnterCriticalSection(&kwsysProcesses.Lock); + /* Indicate to the caller if they can create a process. */ + if (kwsysProcesses.Exiting) { + LeaveCriticalSection(&kwsysProcesses.Lock); + return 0; + } else { + return 1; + } +} + +/* Matching function on successful kwsysTryEnterCreateProcessSection return. + Make sure you called kwsysProcessesAdd if applicable before calling this.*/ +static void kwsysLeaveCreateProcessSection(void) +{ + LeaveCriticalSection(&kwsysProcesses.Lock); +} + +/* Add new process to global process list. The Ctrl handler will wait for + the process to exit before it returns. Do not close the process handle + until after calling kwsysProcessesRemove. The newProcessGroup parameter + must be set if the process was created with CREATE_NEW_PROCESS_GROUP. */ +static int kwsysProcessesAdd(HANDLE hProcess, DWORD dwProcessid, + int newProcessGroup) +{ + if (!kwsysProcessesInitialize() || !hProcess || + hProcess == INVALID_HANDLE_VALUE) { + return 0; + } + + /* Enter the critical section. */ + EnterCriticalSection(&kwsysProcesses.Lock); + + /* Make sure there is enough space for the new process handle. */ + if (kwsysProcesses.Count == kwsysProcesses.Size) { + size_t newSize; + kwsysProcessInstance* newArray; + /* Start with enough space for a small number of process handles + and double the size each time more is needed. */ + newSize = kwsysProcesses.Size ? kwsysProcesses.Size * 2 : 4; + + /* Try allocating the new block of memory. */ + if ((newArray = (kwsysProcessInstance*)malloc( + newSize * sizeof(kwsysProcessInstance)))) { + /* Copy the old process handles to the new memory. */ + if (kwsysProcesses.Count > 0) { + memcpy(newArray, kwsysProcesses.Processes, + kwsysProcesses.Count * sizeof(kwsysProcessInstance)); + } + } else { + /* Failed to allocate memory for the new process handle set. */ + LeaveCriticalSection(&kwsysProcesses.Lock); + return 0; + } + + /* Free original array. */ + free(kwsysProcesses.Processes); + + /* Update original structure with new allocation. */ + kwsysProcesses.Size = newSize; + kwsysProcesses.Processes = newArray; + } + + /* Append the new process information to the set. */ + kwsysProcesses.Processes[kwsysProcesses.Count].hProcess = hProcess; + kwsysProcesses.Processes[kwsysProcesses.Count].dwProcessId = dwProcessid; + kwsysProcesses.Processes[kwsysProcesses.Count++].NewProcessGroup = + newProcessGroup; + + /* Leave critical section and return success. */ + LeaveCriticalSection(&kwsysProcesses.Lock); + + return 1; +} + +/* Removes process to global process list. */ +static void kwsysProcessesRemove(HANDLE hProcess) +{ + size_t i; + + if (!hProcess || hProcess == INVALID_HANDLE_VALUE) { + return; + } + + EnterCriticalSection(&kwsysProcesses.Lock); + + /* Find the given process in the set. */ + for (i = 0; i < kwsysProcesses.Count; ++i) { + if (kwsysProcesses.Processes[i].hProcess == hProcess) { + break; + } + } + if (i < kwsysProcesses.Count) { + /* Found it! Remove the process from the set. */ + --kwsysProcesses.Count; + for (; i < kwsysProcesses.Count; ++i) { + kwsysProcesses.Processes[i] = kwsysProcesses.Processes[i + 1]; + } + + /* If this was the last process, free the array. */ + if (kwsysProcesses.Count == 0) { + kwsysProcesses.Size = 0; + free(kwsysProcesses.Processes); + kwsysProcesses.Processes = 0; + } + } + + LeaveCriticalSection(&kwsysProcesses.Lock); +} + +static BOOL WINAPI kwsysCtrlHandler(DWORD dwCtrlType) +{ + size_t i; + (void)dwCtrlType; + /* Enter critical section. */ + EnterCriticalSection(&kwsysProcesses.Lock); + + /* Set flag indicating that we are exiting. */ + kwsysProcesses.Exiting = 1; + + /* If some of our processes were created in a new process group, we must + manually interrupt them. They won't otherwise receive a Ctrl+C/Break. */ + for (i = 0; i < kwsysProcesses.Count; ++i) { + if (kwsysProcesses.Processes[i].NewProcessGroup) { + DWORD groupId = kwsysProcesses.Processes[i].dwProcessId; + if (groupId) { + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, groupId); + } + } + } + + /* Wait for each child process to exit. This is the key step that prevents + us from leaving several orphaned children processes running in the + background when the user presses Ctrl+C. */ + for (i = 0; i < kwsysProcesses.Count; ++i) { + WaitForSingleObject(kwsysProcesses.Processes[i].hProcess, INFINITE); + } + + /* Leave critical section. */ + LeaveCriticalSection(&kwsysProcesses.Lock); + + /* Continue on to default Ctrl handler (which calls ExitProcess). */ + return FALSE; +} + +void kwsysProcess_ResetStartTime(kwsysProcess* cp) +{ + if (!cp) { + return; + } + /* Reset start time. */ + cp->StartTime = kwsysProcessTimeGetCurrent(); +} diff --git a/Source/kwsys/README.rst b/Source/kwsys/README.rst new file mode 100644 index 0000000..fc6b590 --- /dev/null +++ b/Source/kwsys/README.rst @@ -0,0 +1,37 @@ +KWSys +***** + +Introduction +============ + +KWSys is the Kitware System Library. It provides platform-independent +APIs to many common system features that are implemented differently on +every platform. This library is intended to be shared among many +projects at the source level, so it has a configurable namespace. +Each project should configure KWSys to use a namespace unique to itself. +See comments in `CMakeLists.txt`_ for details. + +.. _`CMakeLists.txt`: CMakeLists.txt + +License +======= + +KWSys is distributed under the OSI-approved BSD 3-clause License. +See `Copyright.txt`_ for details. + +.. _`Copyright.txt`: Copyright.txt + +Reporting Bugs +============== + +KWSys has no independent issue tracker. After encountering an issue +(bug) please submit a patch using the instructions for `Contributing`_. +Otherwise please report the issue to the tracker for the project that +hosts the copy of KWSys in which the problem was found. + +Contributing +============ + +See `CONTRIBUTING.rst`_ for instructions to contribute. + +.. _`CONTRIBUTING.rst`: CONTRIBUTING.rst diff --git a/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx new file mode 100644 index 0000000..5e6f8da --- /dev/null +++ b/Source/kwsys/RegularExpression.cxx @@ -0,0 +1,1218 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +// +// Copyright (C) 1991 Texas Instruments Incorporated. +// +// Permission is granted to any individual or institution to use, copy, modify +// and distribute this software, provided that this complete copyright and +// permission notice is maintained, intact, in all copies and supporting +// documentation. +// +// Texas Instruments Incorporated provides this software "as is" without +// express or implied warranty. +// +// +// Created: MNF 06/13/89 Initial Design and Implementation +// Updated: LGO 08/09/89 Inherit from Generic +// Updated: MBN 09/07/89 Added conditional exception handling +// Updated: MBN 12/15/89 Sprinkled "const" qualifiers all over the place! +// Updated: DLS 03/22/91 New lite version +// + +#include "kwsysPrivate.h" +#include KWSYS_HEADER(RegularExpression.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "RegularExpression.hxx.in" +#endif + +#include <stdio.h> +#include <string.h> + +namespace KWSYS_NAMESPACE { + +// RegularExpression -- Copies the given regular expression. +RegularExpression::RegularExpression(const RegularExpression& rxp) +{ + if (!rxp.program) { + this->program = nullptr; + return; + } + int ind; + this->progsize = rxp.progsize; // Copy regular expression size + this->program = new char[this->progsize]; // Allocate storage + for (ind = this->progsize; ind-- != 0;) // Copy regular expression + this->program[ind] = rxp.program[ind]; + // Copy pointers into last successful "find" operation + this->regmatch = rxp.regmatch; + this->regmust = rxp.regmust; // Copy field + if (rxp.regmust != nullptr) { + char* dum = rxp.program; + ind = 0; + while (dum != rxp.regmust) { + ++dum; + ++ind; + } + this->regmust = this->program + ind; + } + this->regstart = rxp.regstart; // Copy starting index + this->reganch = rxp.reganch; // Copy remaining private data + this->regmlen = rxp.regmlen; // Copy remaining private data +} + +// operator= -- Copies the given regular expression. +RegularExpression& RegularExpression::operator=(const RegularExpression& rxp) +{ + if (this == &rxp) { + return *this; + } + if (!rxp.program) { + this->program = nullptr; + return *this; + } + int ind; + this->progsize = rxp.progsize; // Copy regular expression size + delete[] this->program; + this->program = new char[this->progsize]; // Allocate storage + for (ind = this->progsize; ind-- != 0;) // Copy regular expression + this->program[ind] = rxp.program[ind]; + // Copy pointers into last successful "find" operation + this->regmatch = rxp.regmatch; + this->regmust = rxp.regmust; // Copy field + if (rxp.regmust != nullptr) { + char* dum = rxp.program; + ind = 0; + while (dum != rxp.regmust) { + ++dum; + ++ind; + } + this->regmust = this->program + ind; + } + this->regstart = rxp.regstart; // Copy starting index + this->reganch = rxp.reganch; // Copy remaining private data + this->regmlen = rxp.regmlen; // Copy remaining private data + + return *this; +} + +// operator== -- Returns true if two regular expressions have the same +// compiled program for pattern matching. +bool RegularExpression::operator==(const RegularExpression& rxp) const +{ + if (this != &rxp) { // Same address? + int ind = this->progsize; // Get regular expression size + if (ind != rxp.progsize) // If different size regexp + return false; // Return failure + while (ind-- != 0) // Else while still characters + if (this->program[ind] != rxp.program[ind]) // If regexp are different + return false; // Return failure + } + return true; // Else same, return success +} + +// deep_equal -- Returns true if have the same compiled regular expressions +// and the same start and end pointers. + +bool RegularExpression::deep_equal(const RegularExpression& rxp) const +{ + int ind = this->progsize; // Get regular expression size + if (ind != rxp.progsize) // If different size regexp + return false; // Return failure + while (ind-- != 0) // Else while still characters + if (this->program[ind] != rxp.program[ind]) // If regexp are different + return false; // Return failure + // Else if same start/end ptrs, return true + return (this->regmatch.start() == rxp.regmatch.start() && + this->regmatch.end() == rxp.regmatch.end()); +} + +// The remaining code in this file is derived from the regular expression code +// whose copyright statement appears below. It has been changed to work +// with the class concepts of C++ and COOL. + +/* + * compile and find + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or + * nullptr regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that compile() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in find() needs it and compile() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +// definition number opnd? meaning +#define END 0 // no End of program. +#define BOL 1 // no Match "" at beginning of line. +#define EOL 2 // no Match "" at end of line. +#define ANY 3 // no Match any one character. +#define ANYOF 4 // str Match any character in this string. +#define ANYBUT \ + 5 // str Match any character not in this + // string. +#define BRANCH \ + 6 // node Match this alternative, or the + // next... +#define BACK 7 // no Match "", "next" ptr points backward. +#define EXACTLY 8 // str Match this string. +#define NOTHING 9 // no Match empty string. +#define STAR \ + 10 // node Match this (simple) thing 0 or more + // times. +#define PLUS \ + 11 // node Match this (simple) thing 1 or more + // times. +#define OPEN \ + 20 // no Mark this point in input as start of + // #n. +// OPEN+1 is number 1, etc. +#define CLOSE 30 // no Analogous to OPEN. + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ + +#define OP(p) (*(p)) +#define NEXT(p) (((*((p) + 1) & 0377) << 8) + (*((p) + 2) & 0377)) +#define OPERAND(p) ((p) + 3) + +const unsigned char MAGIC = 0234; +/* + * Utility definitions. + */ + +#define UCHARAT(p) (reinterpret_cast<const unsigned char*>(p))[0] + +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 // Known never to match null string. +#define SIMPLE 02 // Simple enough to be STAR/PLUS operand. +#define SPSTART 04 // Starts with * or +. +#define WORST 0 // Worst case. + +///////////////////////////////////////////////////////////////////////// +// +// COMPILE AND ASSOCIATED FUNCTIONS +// +///////////////////////////////////////////////////////////////////////// + +/* + * Read only utility variables. + */ +static char regdummy; +static char* const regdummyptr = ®dummy; + +/* + * Utility class for RegularExpression::compile(). + */ +class RegExpCompile +{ +public: + const char* regparse; // Input-scan pointer. + int regnpar; // () count. + char* regcode; // Code-emit pointer; regdummyptr = don't. + long regsize; // Code size. + + char* reg(int, int*); + char* regbranch(int*); + char* regpiece(int*); + char* regatom(int*); + char* regnode(char); + void regc(char); + void reginsert(char, char*); + static void regtail(char*, const char*); + static void regoptail(char*, const char*); +}; + +static const char* regnext(const char*); +static char* regnext(char*); + +#ifdef STRCSPN +static int strcspn(); +#endif + +/* + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ + +// compile -- compile a regular expression into internal code +// for later pattern matching. + +bool RegularExpression::compile(const char* exp) +{ + const char* scan; + const char* longest; + int flags; + + if (exp == nullptr) { + // RAISE Error, SYM(RegularExpression), SYM(No_Expr), + printf("RegularExpression::compile(): No expression supplied.\n"); + return false; + } + + // First pass: determine size, legality. + RegExpCompile comp; + comp.regparse = exp; + comp.regnpar = 1; + comp.regsize = 0L; + comp.regcode = regdummyptr; + comp.regc(static_cast<char>(MAGIC)); + if (!comp.reg(0, &flags)) { + printf("RegularExpression::compile(): Error in compile.\n"); + return false; + } + this->regmatch.clear(); + + // Small enough for pointer-storage convention? + if (comp.regsize >= 32767L) { // Probably could be 65535L. + // RAISE Error, SYM(RegularExpression), SYM(Expr_Too_Big), + printf("RegularExpression::compile(): Expression too big.\n"); + return false; + } + + // Allocate space. + //#ifndef _WIN32 + if (this->program != nullptr) + delete[] this->program; + //#endif + this->program = new char[comp.regsize]; + this->progsize = static_cast<int>(comp.regsize); + + if (this->program == nullptr) { + // RAISE Error, SYM(RegularExpression), SYM(Out_Of_Memory), + printf("RegularExpression::compile(): Out of memory.\n"); + return false; + } + + // Second pass: emit code. + comp.regparse = exp; + comp.regnpar = 1; + comp.regcode = this->program; + comp.regc(static_cast<char>(MAGIC)); + comp.reg(0, &flags); + + // Dig out information for optimizations. + this->regstart = '\0'; // Worst-case defaults. + this->reganch = 0; + this->regmust = nullptr; + this->regmlen = 0; + scan = this->program + 1; // First BRANCH. + if (OP(regnext(scan)) == END) { // Only one top-level choice. + scan = OPERAND(scan); + + // Starting-point info. + if (OP(scan) == EXACTLY) + this->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + this->reganch++; + + // + // If there's something expensive in the r.e., find the longest + // literal string that must appear and make it the regmust. Resolve + // ties in favor of later strings, since the regstart check works + // with the beginning of the r.e. and avoiding duplication + // strengthens checking. Not a strong reason, but sufficient in the + // absence of others. + // + if (flags & SPSTART) { + longest = nullptr; + size_t len = 0; + for (; scan != nullptr; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + this->regmust = longest; + this->regmlen = len; + } + } + return true; +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +char* RegExpCompile::reg(int paren, int* flagp) +{ + char* ret; + char* br; + char* ender; + int parno = 0; + int flags; + + *flagp = HASWIDTH; // Tentatively. + + // Make an OPEN node, if parenthesized. + if (paren) { + if (regnpar >= RegularExpressionMatch::NSUBEXP) { + // RAISE Error, SYM(RegularExpression), SYM(Too_Many_Parens), + printf("RegularExpression::compile(): Too many parentheses.\n"); + return nullptr; + } + parno = regnpar; + regnpar++; + ret = regnode(static_cast<char>(OPEN + parno)); + } else + ret = nullptr; + + // Pick up the branches, linking them together. + br = regbranch(&flags); + if (br == nullptr) + return (nullptr); + if (ret != nullptr) + regtail(ret, br); // OPEN -> first. + else + ret = br; + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == nullptr) + return (nullptr); + regtail(ret, br); // BRANCH -> BRANCH. + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + } + + // Make a closing node, and hook it on the end. + ender = regnode(static_cast<char>((paren) ? CLOSE + parno : END)); + regtail(ret, ender); + + // Hook the tails of the branches to the closing node. + for (br = ret; br != nullptr; br = regnext(br)) + regoptail(br, ender); + + // Check for proper termination. + if (paren && *regparse++ != ')') { + // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens), + printf("RegularExpression::compile(): Unmatched parentheses.\n"); + return nullptr; + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens), + printf("RegularExpression::compile(): Unmatched parentheses.\n"); + return nullptr; + } else { + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf("RegularExpression::compile(): Internal error.\n"); + return nullptr; + } + // NOTREACHED + } + return (ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +char* RegExpCompile::regbranch(int* flagp) +{ + char* ret; + char* chain; + char* latest; + int flags; + + *flagp = WORST; // Tentatively. + + ret = regnode(BRANCH); + chain = nullptr; + while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { + latest = regpiece(&flags); + if (latest == nullptr) + return (nullptr); + *flagp |= flags & HASWIDTH; + if (chain == nullptr) // First piece. + *flagp |= flags & SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == nullptr) // Loop ran zero times. + regnode(NOTHING); + + return (ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +char* RegExpCompile::regpiece(int* flagp) +{ + char* ret; + char op; + char* next; + int flags; + + ret = regatom(&flags); + if (ret == nullptr) + return (nullptr); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return (ret); + } + + if (!(flags & HASWIDTH) && op != '?') { + // RAISE Error, SYM(RegularExpression), SYM(Empty_Operand), + printf("RegularExpression::compile() : *+ operand could be empty.\n"); + return nullptr; + } + *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH); + + if (op == '*' && (flags & SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + // Emit x* as (x&|), where & means "self". + reginsert(BRANCH, ret); // Either x + regoptail(ret, regnode(BACK)); // and loop + regoptail(ret, ret); // back + regtail(ret, regnode(BRANCH)); // or + regtail(ret, regnode(NOTHING)); // null. + } else if (op == '+' && (flags & SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + // Emit x+ as x(&|), where & means "self". + next = regnode(BRANCH); // Either + regtail(ret, next); + regtail(regnode(BACK), ret); // loop back + regtail(next, regnode(BRANCH)); // or + regtail(ret, regnode(NOTHING)); // null. + } else if (op == '?') { + // Emit x? as (x|) + reginsert(BRANCH, ret); // Either x + regtail(ret, regnode(BRANCH)); // or + next = regnode(NOTHING); // null. + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) { + // RAISE Error, SYM(RegularExpression), SYM(Nested_Operand), + printf("RegularExpression::compile(): Nested *?+.\n"); + return nullptr; + } + return (ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +char* RegExpCompile::regatom(int* flagp) +{ + char* ret; + int flags; + + *flagp = WORST; // Tentatively. + + switch (*regparse++) { + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH | SIMPLE; + break; + case '[': { + int rxpclass; + int rxpclassend; + + if (*regparse == '^') { // Complement of range. + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + rxpclass = UCHARAT(regparse - 2) + 1; + rxpclassend = UCHARAT(regparse); + if (rxpclass > rxpclassend + 1) { + // RAISE Error, SYM(RegularExpression), SYM(Invalid_Range), + printf("RegularExpression::compile(): Invalid range in [].\n"); + return nullptr; + } + for (; rxpclass <= rxpclassend; rxpclass++) + regc(static_cast<char>(rxpclass)); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') { + // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Bracket), + printf("RegularExpression::compile(): Unmatched [].\n"); + return nullptr; + } + regparse++; + *flagp |= HASWIDTH | SIMPLE; + } break; + case '(': + ret = reg(1, &flags); + if (ret == nullptr) + return (nullptr); + *flagp |= flags & (HASWIDTH | SPSTART); + break; + case '\0': + case '|': + case ')': + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf("RegularExpression::compile(): Internal error.\n"); // Never here + return nullptr; + case '?': + case '+': + case '*': + // RAISE Error, SYM(RegularExpression), SYM(No_Operand), + printf("RegularExpression::compile(): ?+* follows nothing.\n"); + return nullptr; + case '\\': + if (*regparse == '\0') { + // RAISE Error, SYM(RegularExpression), SYM(Trailing_Backslash), + printf("RegularExpression::compile(): Trailing backslash.\n"); + return nullptr; + } + ret = regnode(EXACTLY); + regc(*regparse++); + regc('\0'); + *flagp |= HASWIDTH | SIMPLE; + break; + default: { + int len; + char ender; + + regparse--; + len = int(strcspn(regparse, META)); + if (len <= 0) { + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf("RegularExpression::compile(): Internal error.\n"); + return nullptr; + } + ender = *(regparse + len); + if (len > 1 && ISMULT(ender)) + len--; // Back off clear of ?+* operand. + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } break; + } + return (ret); +} + +/* + - regnode - emit a node + Location. + */ +char* RegExpCompile::regnode(char op) +{ + char* ret; + char* ptr; + + ret = regcode; + if (ret == regdummyptr) { + regsize += 3; + return (ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; // Null "next" pointer. + *ptr++ = '\0'; + regcode = ptr; + + return (ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +void RegExpCompile::regc(char b) +{ + if (regcode != regdummyptr) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +void RegExpCompile::reginsert(char op, char* opnd) +{ + char* src; + char* dst; + char* place; + + if (regcode == regdummyptr) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; // Op node, where operand used to be. + *place++ = op; + *place++ = '\0'; + *place = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +void RegExpCompile::regtail(char* p, const char* val) +{ + char* scan; + char* temp; + int offset; + + if (p == regdummyptr) + return; + + // Find last node. + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == nullptr) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = int(scan - val); + else + offset = int(val - scan); + *(scan + 1) = static_cast<char>((offset >> 8) & 0377); + *(scan + 2) = static_cast<char>(offset & 0377); +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +void RegExpCompile::regoptail(char* p, const char* val) +{ + // "Operandless" and "op != BRANCH" are synonymous in practice. + if (p == nullptr || p == regdummyptr || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +//////////////////////////////////////////////////////////////////////// +// +// find and friends +// +//////////////////////////////////////////////////////////////////////// + +/* + * Utility class for RegularExpression::find(). + */ +class RegExpFind +{ +public: + const char* reginput; // String-input pointer. + const char* regbol; // Beginning of input, for ^ check. + const char** regstartp; // Pointer to startp array. + const char** regendp; // Ditto for endp. + + int regtry(const char*, const char**, const char**, const char*); + int regmatch(const char*); + int regrepeat(const char*); +}; + +// find -- Matches the regular expression to the given string. +// Returns true if found, and sets start and end indexes accordingly. +bool RegularExpression::find(char const* string, + RegularExpressionMatch& rmatch) const +{ + const char* s; + + rmatch.clear(); + rmatch.searchstring = string; + + if (!this->program) { + return false; + } + + // Check validity of program. + if (UCHARAT(this->program) != MAGIC) { + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf( + "RegularExpression::find(): Compiled regular expression corrupted.\n"); + return false; + } + + // If there is a "must appear" string, look for it. + if (this->regmust != nullptr) { + s = string; + while ((s = strchr(s, this->regmust[0])) != nullptr) { + if (strncmp(s, this->regmust, this->regmlen) == 0) + break; // Found it. + s++; + } + if (s == nullptr) // Not present. + return false; + } + + RegExpFind regFind; + + // Mark beginning of line for ^ . + regFind.regbol = string; + + // Simplest case: anchored match need be tried only once. + if (this->reganch) + return ( + regFind.regtry(string, rmatch.startp, rmatch.endp, this->program) != 0); + + // Messy cases: unanchored match. + s = string; + if (this->regstart != '\0') + // We know what char it must start with. + while ((s = strchr(s, this->regstart)) != nullptr) { + if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program)) + return true; + s++; + } + else + // We don't -- general case. + do { + if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program)) + return true; + } while (*s++ != '\0'); + + // Failure. + return false; +} + +/* + - regtry - try match at specific point + 0 failure, 1 success + */ +int RegExpFind::regtry(const char* string, const char** start, + const char** end, const char* prog) +{ + int i; + const char** sp1; + const char** ep; + + reginput = string; + regstartp = start; + regendp = end; + + sp1 = start; + ep = end; + for (i = RegularExpressionMatch::NSUBEXP; i > 0; i--) { + *sp1++ = nullptr; + *ep++ = nullptr; + } + if (regmatch(prog + 1)) { + start[0] = string; + end[0] = reginput; + return (1); + } else + return (0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + * 0 failure, 1 success + */ +int RegExpFind::regmatch(const char* prog) +{ + const char* scan; // Current node. + const char* next; // Next node. + + scan = prog; + + while (scan != nullptr) { + + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return (0); + break; + case EOL: + if (*reginput != '\0') + return (0); + break; + case ANY: + if (*reginput == '\0') + return (0); + reginput++; + break; + case EXACTLY: { + size_t len; + const char* opnd; + + opnd = OPERAND(scan); + // Inline the first character, for speed. + if (*opnd != *reginput) + return (0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return (0); + reginput += len; + } break; + case ANYOF: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == nullptr) + return (0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != nullptr) + return (0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9: { + int no; + const char* save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + + // + // Don't set startp if some later invocation of the + // same parentheses already has. + // + if (regstartp[no] == nullptr) + regstartp[no] = save; + return (1); + } else + return (0); + } + // break; + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9: { + int no; + const char* save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + + // + // Don't set endp if some later invocation of the + // same parentheses already has. + // + if (regendp[no] == nullptr) + regendp[no] = save; + return (1); + } else + return (0); + } + // break; + case BRANCH: { + + const char* save; + + if (OP(next) != BRANCH) // No choice. + next = OPERAND(scan); // Avoid recursion. + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return (1); + reginput = save; + scan = regnext(scan); + } while (scan != nullptr && OP(scan) == BRANCH); + return (0); + // NOTREACHED + } + } break; + case STAR: + case PLUS: { + char nextch; + int no; + const char* save; + int min_no; + + // + // Lookahead to avoid useless match attempts when we know + // what character comes next. + // + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min_no = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min_no) { + // If it could work, try it. + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return (1); + // Couldn't or didn't -- back up. + no--; + reginput = save + no; + } + return (0); + } + // break; + case END: + return (1); // Success! + + default: + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf( + "RegularExpression::find(): Internal error -- memory corrupted.\n"); + return 0; + } + scan = next; + } + + // + // We get here only if there's trouble -- normally "case END" is the + // terminating point. + // + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf("RegularExpression::find(): Internal error -- corrupted pointers.\n"); + return (0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +int RegExpFind::regrepeat(const char* p) +{ + int count = 0; + const char* scan; + const char* opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = int(strlen(scan)); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != nullptr) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == nullptr) { + count++; + scan++; + } + break; + default: // Oh dear. Called inappropriately. + // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), + printf("cm RegularExpression::find(): Internal error.\n"); + return 0; + } + reginput = scan; + return (count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static const char* regnext(const char* p) +{ + int offset; + + if (p == regdummyptr) + return (nullptr); + + offset = NEXT(p); + if (offset == 0) + return (nullptr); + + if (OP(p) == BACK) + return (p - offset); + else + return (p + offset); +} + +static char* regnext(char* p) +{ + int offset; + + if (p == regdummyptr) + return (nullptr); + + offset = NEXT(p); + if (offset == 0) + return (nullptr); + + if (OP(p) == BACK) + return (p - offset); + else + return (p + offset); +} + +} // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/RegularExpression.hxx.in b/Source/kwsys/RegularExpression.hxx.in new file mode 100644 index 0000000..d11db88 --- /dev/null +++ b/Source/kwsys/RegularExpression.hxx.in @@ -0,0 +1,573 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +// Original Copyright notice: +// Copyright (C) 1991 Texas Instruments Incorporated. +// +// Permission is granted to any individual or institution to use, copy, modify, +// and distribute this software, provided that this complete copyright and +// permission notice is maintained, intact, in all copies and supporting +// documentation. +// +// Texas Instruments Incorporated provides this software "as is" without +// express or implied warranty. +// +// Created: MNF 06/13/89 Initial Design and Implementation +// Updated: LGO 08/09/89 Inherit from Generic +// Updated: MBN 09/07/89 Added conditional exception handling +// Updated: MBN 12/15/89 Sprinkled "const" qualifiers all over the place! +// Updated: DLS 03/22/91 New lite version +// + +#ifndef @KWSYS_NAMESPACE@_RegularExpression_hxx +#define @KWSYS_NAMESPACE@_RegularExpression_hxx + +#include <@KWSYS_NAMESPACE@/Configure.h> +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <string> + +/* Disable useless Borland warnings. KWSys tries not to force things + on its includers, but there is no choice here. */ +#if defined(__BORLANDC__) +# pragma warn - 8027 /* function not inlined. */ +#endif + +namespace @KWSYS_NAMESPACE@ { + +// Forward declaration +class RegularExpression; + +/** \class RegularExpressionMatch + * \brief Stores the pattern matches of a RegularExpression + */ +class @KWSYS_NAMESPACE@_EXPORT RegularExpressionMatch +{ +public: + RegularExpressionMatch(); + + bool isValid() const; + void clear(); + + std::string::size_type start() const; + std::string::size_type end() const; + std::string::size_type start(int n) const; + std::string::size_type end(int n) const; + std::string match(int n) const; + + enum + { + NSUBEXP = 10 + }; + +private: + friend class RegularExpression; + const char* startp[NSUBEXP]; + const char* endp[NSUBEXP]; + const char* searchstring; +}; + +#ifdef _MSC_VER +# pragma warning(push) +# if _MSC_VER < 1900 +# pragma warning(disable : 4351) /* new behavior */ +# endif +#endif + +/** + * \brief Creates an invalid match object + */ +inline RegularExpressionMatch::RegularExpressionMatch() + : startp{} + , endp{} + , searchstring{} +{ +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +/** + * \brief Returns true if the match pointers are valid + */ +inline bool RegularExpressionMatch::isValid() const +{ + return (this->startp[0] != nullptr); +} + +/** + * \brief Resets to the (invalid) construction state. + */ +inline void RegularExpressionMatch::clear() +{ + startp[0] = nullptr; + endp[0] = nullptr; + searchstring = nullptr; +} + +/** + * \brief Returns the start index of the full match. + */ +inline std::string::size_type RegularExpressionMatch::start() const +{ + return static_cast<std::string::size_type>(this->startp[0] - searchstring); +} + +/** + * \brief Returns the end index of the full match. + */ +inline std::string::size_type RegularExpressionMatch::end() const +{ + return static_cast<std::string::size_type>(this->endp[0] - searchstring); +} + +/** + * \brief Returns the start index of nth submatch. + * start(0) is the start of the full match. + */ +inline std::string::size_type RegularExpressionMatch::start(int n) const +{ + return static_cast<std::string::size_type>(this->startp[n] - + this->searchstring); +} + +/** + * \brief Returns the end index of nth submatch. + * end(0) is the end of the full match. + */ +inline std::string::size_type RegularExpressionMatch::end(int n) const +{ + return static_cast<std::string::size_type>(this->endp[n] - + this->searchstring); +} + +/** + * \brief Returns the nth submatch as a string. + */ +inline std::string RegularExpressionMatch::match(int n) const +{ + if (this->startp[n] == nullptr) { + return std::string(); + } else { + return std::string( + this->startp[n], + static_cast<std::string::size_type>(this->endp[n] - this->startp[n])); + } +} + +/** \class RegularExpression + * \brief Implements pattern matching with regular expressions. + * + * This is the header file for the regular expression class. An object of + * this class contains a regular expression, in a special "compiled" format. + * This compiled format consists of several slots all kept as the objects + * private data. The RegularExpression class provides a convenient way to + * represent regular expressions. It makes it easy to search for the same + * regular expression in many different strings without having to compile a + * string to regular expression format more than necessary. + * + * This class implements pattern matching via regular expressions. + * A regular expression allows a programmer to specify complex + * patterns that can be searched for and matched against the + * character string of a string object. In its simplest form, a + * regular expression is a sequence of characters used to + * search for exact character matches. However, many times the + * exact sequence to be found is not known, or only a match at + * the beginning or end of a string is desired. The RegularExpression regu- + * lar expression class implements regular expression pattern + * matching as is found and implemented in many UNIX commands + * and utilities. + * + * Example: The perl code + * + * $filename =~ m"([a-z]+)\.cc"; + * print $1; + * + * Is written as follows in C++ + * + * RegularExpression re("([a-z]+)\\.cc"); + * re.find(filename); + * cerr << re.match(1); + * + * + * The regular expression class provides a convenient mechanism + * for specifying and manipulating regular expressions. The + * regular expression object allows specification of such pat- + * terns by using the following regular expression metacharac- + * ters: + * + * ^ Matches at beginning of a line + * + * $ Matches at end of a line + * + * . Matches any single character + * + * [ ] Matches any character(s) inside the brackets + * + * [^ ] Matches any character(s) not inside the brackets + * + * - Matches any character in range on either side of a dash + * + * * Matches preceding pattern zero or more times + * + * + Matches preceding pattern one or more times + * + * ? Matches preceding pattern zero or once only + * + * () Saves a matched expression and uses it in a later match + * + * Note that more than one of these metacharacters can be used + * in a single regular expression in order to create complex + * search patterns. For example, the pattern [^ab1-9] says to + * match any character sequence that does not begin with the + * characters "ab" followed by numbers in the series one + * through nine. + * + * There are three constructors for RegularExpression. One just creates an + * empty RegularExpression object. Another creates a RegularExpression + * object and initializes it with a regular expression that is given in the + * form of a char*. The third takes a reference to a RegularExpression + * object as an argument and creates an object initialized with the + * information from the given RegularExpression object. + * + * The find member function finds the first occurrence of the regular + * expression of that object in the string given to find as an argument. Find + * returns a boolean, and if true, mutates the private data appropriately. + * Find sets pointers to the beginning and end of the thing last found, they + * are pointers into the actual string that was searched. The start and end + * member functions return indices into the searched string that correspond + * to the beginning and end pointers respectively. The compile member + * function takes a char* and puts the compiled version of the char* argument + * into the object's private data fields. The == and != operators only check + * the to see if the compiled regular expression is the same, and the + * deep_equal functions also checks to see if the start and end pointers are + * the same. The is_valid function returns false if program is set to + * nullptr, (i.e. there is no valid compiled expression). The set_invalid + * function sets the program to nullptr (Warning: this deletes the compiled + * expression). The following examples may help clarify regular expression + * usage: + * + * * The regular expression "^hello" matches a "hello" only at the + * beginning of a line. It would match "hello there" but not "hi, + * hello there". + * + * * The regular expression "long$" matches a "long" only at the end + * of a line. It would match "so long\0", but not "long ago". + * + * * The regular expression "t..t..g" will match anything that has a + * "t" then any two characters, another "t", any two characters and + * then a "g". It will match "testing", or "test again" but would + * not match "toasting" + * + * * The regular expression "[1-9ab]" matches any number one through + * nine, and the characters "a" and "b". It would match "hello 1" + * or "begin", but would not match "no-match". + * + * * The regular expression "[^1-9ab]" matches any character that is + * not a number one through nine, or an "a" or "b". It would NOT + * match "hello 1" or "begin", but would match "no-match". + * + * * The regular expression "br* " matches something that begins with + * a "b", is followed by zero or more "r"s, and ends in a space. It + * would match "brrrrr ", and "b ", but would not match "brrh ". + * + * * The regular expression "br+ " matches something that begins with + * a "b", is followed by one or more "r"s, and ends in a space. It + * would match "brrrrr ", and "br ", but would not match "b " or + * "brrh ". + * + * * The regular expression "br? " matches something that begins with + * a "b", is followed by zero or one "r"s, and ends in a space. It + * would match "br ", and "b ", but would not match "brrrr " or + * "brrh ". + * + * * The regular expression "(..p)b" matches something ending with pb + * and beginning with whatever the two characters before the first p + * encountered in the line were. It would find "repb" in "rep drepa + * qrepb". The regular expression "(..p)a" would find "repa qrepb" + * in "rep drepa qrepb" + * + * * The regular expression "d(..p)" matches something ending with p, + * beginning with d, and having two characters in between that are + * the same as the two characters before the first p encountered in + * the line. It would match "drepa qrepb" in "rep drepa qrepb". + * + * All methods of RegularExpression can be called simultaneously from + * different threads but only if each invocation uses an own instance of + * RegularExpression. + */ +class @KWSYS_NAMESPACE@_EXPORT RegularExpression +{ +public: + /** + * Instantiate RegularExpression with program=nullptr. + */ + inline RegularExpression(); + + /** + * Instantiate RegularExpression with compiled char*. + */ + inline RegularExpression(char const*); + + /** + * Instantiate RegularExpression as a copy of another regular expression. + */ + RegularExpression(RegularExpression const&); + + /** + * Instantiate RegularExpression with compiled string. + */ + inline RegularExpression(std::string const&); + + /** + * Destructor. + */ + inline ~RegularExpression(); + + /** + * Compile a regular expression into internal code + * for later pattern matching. + */ + bool compile(char const*); + + /** + * Compile a regular expression into internal code + * for later pattern matching. + */ + inline bool compile(std::string const&); + + /** + * Matches the regular expression to the given string. + * Returns true if found, and sets start and end indexes + * in the RegularExpressionMatch instance accordingly. + * + * This method is thread safe when called with different + * RegularExpressionMatch instances. + */ + bool find(char const*, RegularExpressionMatch&) const; + + /** + * Matches the regular expression to the given string. + * Returns true if found, and sets start and end indexes accordingly. + */ + inline bool find(char const*); + + /** + * Matches the regular expression to the given std string. + * Returns true if found, and sets start and end indexes accordingly. + */ + inline bool find(std::string const&); + + /** + * Match indices + */ + inline RegularExpressionMatch const& regMatch() const; + inline std::string::size_type start() const; + inline std::string::size_type end() const; + inline std::string::size_type start(int n) const; + inline std::string::size_type end(int n) const; + + /** + * Match strings + */ + inline std::string match(int n) const; + + /** + * Copy the given regular expression. + */ + RegularExpression& operator=(const RegularExpression& rxp); + + /** + * Returns true if two regular expressions have the same + * compiled program for pattern matching. + */ + bool operator==(RegularExpression const&) const; + + /** + * Returns true if two regular expressions have different + * compiled program for pattern matching. + */ + inline bool operator!=(RegularExpression const&) const; + + /** + * Returns true if have the same compiled regular expressions + * and the same start and end pointers. + */ + bool deep_equal(RegularExpression const&) const; + + /** + * True if the compiled regexp is valid. + */ + inline bool is_valid() const; + + /** + * Marks the regular expression as invalid. + */ + inline void set_invalid(); + +private: + RegularExpressionMatch regmatch; + char regstart; // Internal use only + char reganch; // Internal use only + const char* regmust; // Internal use only + std::string::size_type regmlen; // Internal use only + char* program; + int progsize; +}; + +/** + * Create an empty regular expression. + */ +inline RegularExpression::RegularExpression() + : regstart{} + , reganch{} + , regmust{} + , program{ nullptr } + , progsize{} +{ +} + +/** + * Creates a regular expression from string s, and + * compiles s. + */ +inline RegularExpression::RegularExpression(const char* s) + : regstart{} + , reganch{} + , regmust{} + , program{ nullptr } + , progsize{} +{ + if (s) { + this->compile(s); + } +} + +/** + * Creates a regular expression from string s, and + * compiles s. + */ +inline RegularExpression::RegularExpression(const std::string& s) + : regstart{} + , reganch{} + , regmust{} + , program{ nullptr } + , progsize{} +{ + this->compile(s); +} + +/** + * Destroys and frees space allocated for the regular expression. + */ +inline RegularExpression::~RegularExpression() +{ + //#ifndef _WIN32 + delete[] this->program; + //#endif +} + +/** + * Compile a regular expression into internal code + * for later pattern matching. + */ +inline bool RegularExpression::compile(std::string const& s) +{ + return this->compile(s.c_str()); +} + +/** + * Matches the regular expression to the given std string. + * Returns true if found, and sets start and end indexes accordingly. + */ +inline bool RegularExpression::find(const char* s) +{ + return this->find(s, this->regmatch); +} + +/** + * Matches the regular expression to the given std string. + * Returns true if found, and sets start and end indexes accordingly. + */ +inline bool RegularExpression::find(std::string const& s) +{ + return this->find(s.c_str()); +} + +/** + * Returns the internal match object + */ +inline RegularExpressionMatch const& RegularExpression::regMatch() const +{ + return this->regmatch; +} + +/** + * Returns the start index of the full match. + */ +inline std::string::size_type RegularExpression::start() const +{ + return regmatch.start(); +} + +/** + * Returns the end index of the full match. + */ +inline std::string::size_type RegularExpression::end() const +{ + return regmatch.end(); +} + +/** + * Return start index of nth submatch. start(0) is the start of the full match. + */ +inline std::string::size_type RegularExpression::start(int n) const +{ + return regmatch.start(n); +} + +/** + * Return end index of nth submatch. end(0) is the end of the full match. + */ +inline std::string::size_type RegularExpression::end(int n) const +{ + return regmatch.end(n); +} + +/** + * Return nth submatch as a string. + */ +inline std::string RegularExpression::match(int n) const +{ + return regmatch.match(n); +} + +/** + * Returns true if two regular expressions have different + * compiled program for pattern matching. + */ +inline bool RegularExpression::operator!=(const RegularExpression& r) const +{ + return (!(*this == r)); +} + +/** + * Returns true if a valid regular expression is compiled + * and ready for pattern matching. + */ +inline bool RegularExpression::is_valid() const +{ + return (this->program != nullptr); +} + +inline void RegularExpression::set_invalid() +{ + //#ifndef _WIN32 + delete[] this->program; + //#endif + this->program = nullptr; +} + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/SharedForward.h.in b/Source/kwsys/SharedForward.h.in new file mode 100644 index 0000000..5716cd4 --- /dev/null +++ b/Source/kwsys/SharedForward.h.in @@ -0,0 +1,879 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_SharedForward_h +# define @KWSYS_NAMESPACE@_SharedForward_h + +/* + This header is used to create a forwarding executable sets up the + shared library search path and replaces itself with a real + executable. This is useful when creating installations on UNIX with + shared libraries that will run from any install directory. Typical + usage: + + #if defined(CMAKE_INTDIR) + # define CONFIG_DIR_PRE CMAKE_INTDIR "/" + # define CONFIG_DIR_POST "/" CMAKE_INTDIR + #else + # define CONFIG_DIR_PRE "" + # define CONFIG_DIR_POST "" + #endif + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "/path/to/foo-build/bin" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL "../lib/foo-1.2" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD CONFIG_DIR_PRE "foo-real" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL + "../lib/foo-1.2/foo-real" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print" + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd" + #if defined(CMAKE_INTDIR) + # define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR + #endif + #include <@KWSYS_NAMESPACE@/SharedForward.h> + int main(int argc, char** argv) + { + return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv); + } + + Specify search and executable paths relative to the forwarding + executable location or as full paths. Include no trailing slash. + In the case of a multi-configuration build, when CMAKE_INTDIR is + defined, the DIR_BUILD setting should point at the directory above + the executable (the one containing the per-configuration + subdirectory specified by CMAKE_INTDIR). Then PATH_BUILD entries + and EXE_BUILD should be specified relative to this location and use + CMAKE_INTDIR as necessary. In the above example imagine appending + the PATH_BUILD or EXE_BUILD setting to the DIR_BUILD setting. The + result should form a valid path with per-configuration subdirectory. + + Additional paths may be specified in the PATH_BUILD and PATH_INSTALL + variables by using comma-separated strings. For example: + + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD \ + "." CONFIG_DIR_POST, "/path/to/bar-build" CONFIG_DIR_POST + #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL \ + "../lib/foo-1.2", "../lib/bar-4.5" + + See the comments below for specific explanations of each macro. +*/ + +/* Disable -Wcast-qual warnings since they are too hard to fix in a + cross-platform way. */ +# if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wcast-qual") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-qual" +# endif +# endif + +# if defined(__BORLANDC__) && !defined(__cplusplus) +/* Code has no effect; raised by winnt.h in C (not C++) when ignoring an + unused parameter using "(param)" syntax (i.e. no cast to void). */ +# pragma warn - 8019 +# endif + +/* Full path to the directory in which this executable is built. Do + not include a trailing slash. */ +# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD) +# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD" +# endif +# if !defined(KWSYS_SHARED_FORWARD_DIR_BUILD) +# define KWSYS_SHARED_FORWARD_DIR_BUILD \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD +# endif + +/* Library search path for build tree. */ +# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD) +# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD" +# endif +# if !defined(KWSYS_SHARED_FORWARD_PATH_BUILD) +# define KWSYS_SHARED_FORWARD_PATH_BUILD \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD +# endif + +/* Library search path for install tree. */ +# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL) +# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL" +# endif +# if !defined(KWSYS_SHARED_FORWARD_PATH_INSTALL) +# define KWSYS_SHARED_FORWARD_PATH_INSTALL \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL +# endif + +/* The real executable to which to forward in the build tree. */ +# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD) +# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD" +# endif +# if !defined(KWSYS_SHARED_FORWARD_EXE_BUILD) +# define KWSYS_SHARED_FORWARD_EXE_BUILD \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD +# endif + +/* The real executable to which to forward in the install tree. */ +# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL) +# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL" +# endif +# if !defined(KWSYS_SHARED_FORWARD_EXE_INSTALL) +# define KWSYS_SHARED_FORWARD_EXE_INSTALL \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL +# endif + +/* The configuration name with which this executable was built (Debug/Release). + */ +# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME) +# define KWSYS_SHARED_FORWARD_CONFIG_NAME \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME +# else +# undef KWSYS_SHARED_FORWARD_CONFIG_NAME +# endif + +/* Create command line option to replace executable. */ +# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND) +# if !defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND) +# define KWSYS_SHARED_FORWARD_OPTION_COMMAND \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND +# endif +# else +# undef KWSYS_SHARED_FORWARD_OPTION_COMMAND +# endif + +/* Create command line option to print environment setting and exit. */ +# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT) +# if !defined(KWSYS_SHARED_FORWARD_OPTION_PRINT) +# define KWSYS_SHARED_FORWARD_OPTION_PRINT \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT +# endif +# else +# undef KWSYS_SHARED_FORWARD_OPTION_PRINT +# endif + +/* Create command line option to run ldd or equivalent. */ +# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD) +# if !defined(KWSYS_SHARED_FORWARD_OPTION_LDD) +# define KWSYS_SHARED_FORWARD_OPTION_LDD \ + @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD +# endif +# else +# undef KWSYS_SHARED_FORWARD_OPTION_LDD +# endif + +/* Include needed system headers. */ + +# include <errno.h> +# include <limits.h> +# include <stddef.h> /* size_t */ +# include <stdio.h> +# include <stdlib.h> +# include <string.h> + +# if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> + +# include <io.h> +# include <process.h> +# define KWSYS_SHARED_FORWARD_ESCAPE_ARGV /* re-escape argv for execvp */ +# else +# include <sys/stat.h> +# include <unistd.h> +# endif + +/* Configuration for this platform. */ + +/* The path separator for this platform. */ +# if defined(_WIN32) && !defined(__CYGWIN__) +# define KWSYS_SHARED_FORWARD_PATH_SEP ';' +# define KWSYS_SHARED_FORWARD_PATH_SLASH '\\' +# else +# define KWSYS_SHARED_FORWARD_PATH_SEP ':' +# define KWSYS_SHARED_FORWARD_PATH_SLASH '/' +# endif +static const char kwsys_shared_forward_path_sep[2] = { + KWSYS_SHARED_FORWARD_PATH_SEP, 0 +}; +static const char kwsys_shared_forward_path_slash[2] = { + KWSYS_SHARED_FORWARD_PATH_SLASH, 0 +}; + +/* The maximum length of a file name. */ +# if defined(PATH_MAX) +# define KWSYS_SHARED_FORWARD_MAXPATH PATH_MAX +# elif defined(MAXPATHLEN) +# define KWSYS_SHARED_FORWARD_MAXPATH MAXPATHLEN +# else +# define KWSYS_SHARED_FORWARD_MAXPATH 16384 +# endif + +/* Select the environment variable holding the shared library runtime + search path for this platform and build configuration. Also select + ldd command equivalent. */ + +/* Linux */ +# if defined(__linux) +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" + +/* FreeBSD */ +# elif defined(__FreeBSD__) +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" + +/* OpenBSD */ +# elif defined(__OpenBSD__) +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" + +/* OS X */ +# elif defined(__APPLE__) +# define KWSYS_SHARED_FORWARD_LDD "otool", "-L" +# define KWSYS_SHARED_FORWARD_LDD_N 2 +# define KWSYS_SHARED_FORWARD_LDPATH "DYLD_LIBRARY_PATH" + +/* AIX */ +# elif defined(_AIX) +# define KWSYS_SHARED_FORWARD_LDD "dump", "-H" +# define KWSYS_SHARED_FORWARD_LDD_N 2 +# define KWSYS_SHARED_FORWARD_LDPATH "LIBPATH" + +/* SUN */ +# elif defined(__sun) +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# include <sys/isa_defs.h> +# if defined(_ILP32) +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" +# elif defined(_LP64) +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH_64" +# endif + +/* HP-UX */ +# elif defined(__hpux) +# define KWSYS_SHARED_FORWARD_LDD "chatr" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# if defined(__LP64__) +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" +# else +# define KWSYS_SHARED_FORWARD_LDPATH "SHLIB_PATH" +# endif + +/* SGI MIPS */ +# elif defined(__sgi) && defined(_MIPS_SIM) +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# if _MIPS_SIM == _ABIO32 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" +# elif _MIPS_SIM == _ABIN32 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARYN32_PATH" +# elif _MIPS_SIM == _ABI64 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY64_PATH" +# endif + +/* Cygwin */ +# elif defined(__CYGWIN__) +# define KWSYS_SHARED_FORWARD_LDD \ + "cygcheck" /* TODO: cygwin 1.7 has ldd \ + */ +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# define KWSYS_SHARED_FORWARD_LDPATH "PATH" + +/* Windows */ +# elif defined(_WIN32) +# define KWSYS_SHARED_FORWARD_LDPATH "PATH" + +/* Guess on this unknown system. */ +# else +# define KWSYS_SHARED_FORWARD_LDD "ldd" +# define KWSYS_SHARED_FORWARD_LDD_N 1 +# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" +# endif + +# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV +typedef struct kwsys_sf_arg_info_s +{ + const char* arg; + int size; + int quote; +} kwsys_sf_arg_info; + +static kwsys_sf_arg_info kwsys_sf_get_arg_info(const char* in) +{ + /* Initialize information. */ + kwsys_sf_arg_info info; + + /* String iterator. */ + const char* c; + + /* Keep track of how many backslashes have been encountered in a row. */ + int windows_backslashes = 0; + + /* Start with the length of the original argument, plus one for + either a terminating null or a separating space. */ + info.arg = in; + info.size = (int)strlen(in) + 1; + info.quote = 0; + + /* Scan the string for characters that require escaping or quoting. */ + for (c = in; *c; ++c) { + /* Check whether this character needs quotes. */ + if (strchr(" \t?'#&<>|^", *c)) { + info.quote = 1; + } + + /* On Windows only backslashes and double-quotes need escaping. */ + if (*c == '\\') { + /* Found a backslash. It may need to be escaped later. */ + ++windows_backslashes; + } else if (*c == '"') { + /* Found a double-quote. We need to escape it and all + immediately preceding backslashes. */ + info.size += windows_backslashes + 1; + windows_backslashes = 0; + } else { + /* Found another character. This eliminates the possibility + that any immediately preceding backslashes will be + escaped. */ + windows_backslashes = 0; + } + } + + /* Check whether the argument needs surrounding quotes. */ + if (info.quote) { + /* Surrounding quotes are needed. Allocate space for them. */ + info.size += 2; + + /* We must escape all ending backslashes when quoting on windows. */ + info.size += windows_backslashes; + } + + return info; +} + +static char* kwsys_sf_get_arg(kwsys_sf_arg_info info, char* out) +{ + /* String iterator. */ + const char* c; + + /* Keep track of how many backslashes have been encountered in a row. */ + int windows_backslashes = 0; + + /* Whether the argument must be quoted. */ + if (info.quote) { + /* Add the opening quote for this argument. */ + *out++ = '"'; + } + + /* Scan the string for characters that require escaping or quoting. */ + for (c = info.arg; *c; ++c) { + /* On Windows only backslashes and double-quotes need escaping. */ + if (*c == '\\') { + /* Found a backslash. It may need to be escaped later. */ + ++windows_backslashes; + } else if (*c == '"') { + /* Found a double-quote. Escape all immediately preceding + backslashes. */ + while (windows_backslashes > 0) { + --windows_backslashes; + *out++ = '\\'; + } + + /* Add the backslash to escape the double-quote. */ + *out++ = '\\'; + } else { + /* We encountered a normal character. This eliminates any + escaping needed for preceding backslashes. */ + windows_backslashes = 0; + } + + /* Store this character. */ + *out++ = *c; + } + + if (info.quote) { + /* Add enough backslashes to escape any trailing ones. */ + while (windows_backslashes > 0) { + --windows_backslashes; + *out++ = '\\'; + } + + /* Add the closing quote for this argument. */ + *out++ = '"'; + } + + /* Store a terminating null without incrementing. */ + *out = 0; + + return out; +} +# endif + +/* Function to convert a logical or relative path to a physical full path. */ +static int kwsys_shared_forward_realpath(const char* in_path, char* out_path) +{ +# if defined(_WIN32) && !defined(__CYGWIN__) + /* Implementation for Windows. */ + DWORD n = + GetFullPathNameA(in_path, KWSYS_SHARED_FORWARD_MAXPATH, out_path, 0); + return n > 0 && n <= KWSYS_SHARED_FORWARD_MAXPATH; +# else + /* Implementation for UNIX. */ + return realpath(in_path, out_path) != 0; +# endif +} + +static int kwsys_shared_forward_samepath(const char* file1, const char* file2) +{ +# if defined(_WIN32) + int result = 0; + HANDLE h1 = CreateFileA(file1, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + HANDLE h2 = CreateFileA(file2, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h1 != INVALID_HANDLE_VALUE && h2 != INVALID_HANDLE_VALUE) { + BY_HANDLE_FILE_INFORMATION fi1; + BY_HANDLE_FILE_INFORMATION fi2; + GetFileInformationByHandle(h1, &fi1); + GetFileInformationByHandle(h2, &fi2); + result = (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber && + fi1.nFileIndexHigh == fi2.nFileIndexHigh && + fi1.nFileIndexLow == fi2.nFileIndexLow); + } + CloseHandle(h1); + CloseHandle(h2); + return result; +# else + struct stat fs1, fs2; + return (stat(file1, &fs1) == 0 && stat(file2, &fs2) == 0 && + memcmp(&fs2.st_dev, &fs1.st_dev, sizeof(fs1.st_dev)) == 0 && + memcmp(&fs2.st_ino, &fs1.st_ino, sizeof(fs1.st_ino)) == 0 && + fs2.st_size == fs1.st_size); +# endif +} + +/* Function to report a system error message. */ +static void kwsys_shared_forward_strerror(char* message) +{ +# if defined(_WIN32) && !defined(__CYGWIN__) + /* Implementation for Windows. */ + DWORD original = GetLastError(); + DWORD length = + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, original, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + message, KWSYS_SHARED_FORWARD_MAXPATH, 0); + if (length < 1 || length > KWSYS_SHARED_FORWARD_MAXPATH) { + /* FormatMessage failed. Use a default message. */ + _snprintf(message, KWSYS_SHARED_FORWARD_MAXPATH, + "Error 0x%X (FormatMessage failed with error 0x%X)", original, + GetLastError()); + } +# else + /* Implementation for UNIX. */ + strcpy(message, strerror(errno)); +# endif +} + +/* Functions to execute a child process. */ +static void kwsys_shared_forward_execvp(const char* cmd, + char const* const* argv) +{ +# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV + /* Count the number of arguments. */ + int argc = 0; + { + char const* const* argvc; + for (argvc = argv; *argvc; ++argvc, ++argc) { + } + } + + /* Create the escaped arguments. */ + { + char** nargv = (char**)malloc((argc + 1) * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + kwsys_sf_arg_info info = kwsys_sf_get_arg_info(argv[i]); + nargv[i] = (char*)malloc(info.size); + kwsys_sf_get_arg(info, nargv[i]); + } + nargv[argc] = 0; + + /* Replace the command line to be used. */ + argv = (char const* const*)nargv; + } +# endif + +/* Invoke the child process. */ +# if defined(_MSC_VER) + _execvp(cmd, argv); +# elif defined(__MINGW32__) && !defined(__MINGW64__) + execvp(cmd, argv); +# else + execvp(cmd, (char* const*)argv); +# endif +} + +/* Function to get the directory containing the given file or directory. */ +static void kwsys_shared_forward_dirname(const char* begin, char* result) +{ + /* Find the location of the last slash. */ + int last_slash_index = -1; + const char* end = begin + strlen(begin); + for (; begin <= end && last_slash_index < 0; --end) { + if (*end == '/' || *end == '\\') { + last_slash_index = (int)(end - begin); + } + } + + /* Handle each case of the index of the last slash. */ + if (last_slash_index < 0) { + /* No slashes. */ + strcpy(result, "."); + } else if (last_slash_index == 0) { + /* Only one leading slash. */ + strcpy(result, kwsys_shared_forward_path_slash); + } +# if defined(_WIN32) + else if (last_slash_index == 2 && begin[1] == ':') { + /* Only one leading drive letter and slash. */ + strncpy(result, begin, (size_t)last_slash_index); + result[last_slash_index] = KWSYS_SHARED_FORWARD_PATH_SLASH; + result[last_slash_index + 1] = 0; + } +# endif + else { + /* A non-leading slash. */ + strncpy(result, begin, (size_t)last_slash_index); + result[last_slash_index] = 0; + } +} + +/* Function to check if a file exists and is executable. */ +static int kwsys_shared_forward_is_executable(const char* f) +{ +# if defined(_MSC_VER) +# define KWSYS_SHARED_FORWARD_ACCESS _access +# else +# define KWSYS_SHARED_FORWARD_ACCESS access +# endif +# if defined(X_OK) +# define KWSYS_SHARED_FORWARD_ACCESS_OK X_OK +# else +# define KWSYS_SHARED_FORWARD_ACCESS_OK 04 +# endif + if (KWSYS_SHARED_FORWARD_ACCESS(f, KWSYS_SHARED_FORWARD_ACCESS_OK) == 0) { + return 1; + } else { + return 0; + } +} + +/* Function to locate the executable currently running. */ +static int kwsys_shared_forward_self_path(const char* argv0, char* result) +{ + /* Check whether argv0 has a slash. */ + int has_slash = 0; + const char* p = argv0; + for (; *p && !has_slash; ++p) { + if (*p == '/' || *p == '\\') { + has_slash = 1; + } + } + + if (has_slash) { + /* There is a slash. Use the dirname of the given location. */ + kwsys_shared_forward_dirname(argv0, result); + return 1; + } else { + /* There is no slash. Search the PATH for the executable. */ + const char* path = getenv("PATH"); + const char* begin = path; + const char* end = begin + (begin ? strlen(begin) : 0); + const char* first = begin; + while (first != end) { + /* Store the end of this path entry. */ + const char* last; + + /* Skip all path separators. */ + for (; *first && *first == KWSYS_SHARED_FORWARD_PATH_SEP; ++first) + ; + + /* Find the next separator. */ + for (last = first; *last && *last != KWSYS_SHARED_FORWARD_PATH_SEP; + ++last) + ; + + /* If we got a non-empty directory, look for the executable there. */ + if (first < last) { + /* Determine the length without trailing slash. */ + size_t length = (size_t)(last - first); + if (*(last - 1) == '/' || *(last - 1) == '\\') { + --length; + } + + /* Construct the name of the executable in this location. */ + strncpy(result, first, length); + result[length] = KWSYS_SHARED_FORWARD_PATH_SLASH; + strcpy(result + (length) + 1, argv0); + + /* Check if it exists and is executable. */ + if (kwsys_shared_forward_is_executable(result)) { + /* Found it. */ + result[length] = 0; + return 1; + } + } + + /* Move to the next directory in the path. */ + first = last; + } + } + + /* We could not find the executable. */ + return 0; +} + +/* Function to convert a specified path to a full path. If it is not + already full, it is taken relative to the self path. */ +static int kwsys_shared_forward_fullpath(const char* self_path, + const char* in_path, char* result, + const char* desc) +{ + /* Check the specified path type. */ + if (in_path[0] == '/') { + /* Already a full path. */ + strcpy(result, in_path); + } +# if defined(_WIN32) + else if (in_path[0] && in_path[1] == ':') { + /* Already a full path. */ + strcpy(result, in_path); + } +# endif + else { + /* Relative to self path. */ + char temp_path[KWSYS_SHARED_FORWARD_MAXPATH]; + strcpy(temp_path, self_path); + strcat(temp_path, kwsys_shared_forward_path_slash); + strcat(temp_path, in_path); + if (!kwsys_shared_forward_realpath(temp_path, result)) { + if (desc) { + char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH]; + kwsys_shared_forward_strerror(msgbuf); + fprintf(stderr, "Error converting %s \"%s\" to real path: %s\n", desc, + temp_path, msgbuf); + } + return 0; + } + } + return 1; +} + +/* Function to compute the library search path and executable name + based on the self path. */ +static int kwsys_shared_forward_get_settings(const char* self_path, + char* ldpath, char* exe) +{ + /* Possible search paths. */ + static const char* search_path_build[] = { KWSYS_SHARED_FORWARD_PATH_BUILD, + 0 }; + static const char* search_path_install[] = { + KWSYS_SHARED_FORWARD_PATH_INSTALL, 0 + }; + + /* Chosen paths. */ + const char** search_path; + const char* exe_path; + +/* Get the real name of the build and self paths. */ +# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) + char build_path[] = + KWSYS_SHARED_FORWARD_DIR_BUILD "/" KWSYS_SHARED_FORWARD_CONFIG_NAME; + char self_path_logical[KWSYS_SHARED_FORWARD_MAXPATH]; +# else + char build_path[] = KWSYS_SHARED_FORWARD_DIR_BUILD; + const char* self_path_logical = self_path; +# endif + char build_path_real[KWSYS_SHARED_FORWARD_MAXPATH]; + char self_path_real[KWSYS_SHARED_FORWARD_MAXPATH]; + if (!kwsys_shared_forward_realpath(self_path, self_path_real)) { + char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH]; + kwsys_shared_forward_strerror(msgbuf); + fprintf(stderr, "Error converting self path \"%s\" to real path: %s\n", + self_path, msgbuf); + return 0; + } + + /* Check whether we are running in the build tree or an install tree. */ + if (kwsys_shared_forward_realpath(build_path, build_path_real) && + kwsys_shared_forward_samepath(self_path_real, build_path_real)) { + /* Running in build tree. Use the build path and exe. */ + search_path = search_path_build; +# if defined(_WIN32) + exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD ".exe"; +# else + exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD; +# endif + +# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) + /* Remove the configuration directory from self_path. */ + kwsys_shared_forward_dirname(self_path, self_path_logical); +# endif + } else { + /* Running in install tree. Use the install path and exe. */ + search_path = search_path_install; +# if defined(_WIN32) + exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL ".exe"; +# else + exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL; +# endif + +# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) + /* Use the original self path directory. */ + strcpy(self_path_logical, self_path); +# endif + } + + /* Construct the runtime search path. */ + { + const char** dir; + for (dir = search_path; *dir; ++dir) { + /* Add separator between path components. */ + if (dir != search_path) { + strcat(ldpath, kwsys_shared_forward_path_sep); + } + + /* Add this path component. */ + if (!kwsys_shared_forward_fullpath(self_path_logical, *dir, + ldpath + strlen(ldpath), + "runtime path entry")) { + return 0; + } + } + } + + /* Construct the executable location. */ + if (!kwsys_shared_forward_fullpath(self_path_logical, exe_path, exe, + "executable file")) { + return 0; + } + return 1; +} + +/* Function to print why execution of a command line failed. */ +static void kwsys_shared_forward_print_failure(char const* const* argv) +{ + char msg[KWSYS_SHARED_FORWARD_MAXPATH]; + char const* const* arg = argv; + kwsys_shared_forward_strerror(msg); + fprintf(stderr, "Error running"); + for (; *arg; ++arg) { + fprintf(stderr, " \"%s\"", *arg); + } + fprintf(stderr, ": %s\n", msg); +} + +/* Static storage space to store the updated environment variable. */ +static char kwsys_shared_forward_ldpath[65535] = + KWSYS_SHARED_FORWARD_LDPATH "="; + +/* Main driver function to be called from main. */ +static int @KWSYS_NAMESPACE@_shared_forward_to_real(int argc, char** argv_in) +{ + char const** argv = (char const**)argv_in; + /* Get the directory containing this executable. */ + char self_path[KWSYS_SHARED_FORWARD_MAXPATH]; + if (kwsys_shared_forward_self_path(argv[0], self_path)) { + /* Found this executable. Use it to get the library directory. */ + char exe[KWSYS_SHARED_FORWARD_MAXPATH]; + if (kwsys_shared_forward_get_settings(self_path, + kwsys_shared_forward_ldpath, exe)) { + /* Append the old runtime search path. */ + const char* old_ldpath = getenv(KWSYS_SHARED_FORWARD_LDPATH); + if (old_ldpath) { + strcat(kwsys_shared_forward_ldpath, kwsys_shared_forward_path_sep); + strcat(kwsys_shared_forward_ldpath, old_ldpath); + } + + /* Store the environment variable. */ + putenv(kwsys_shared_forward_ldpath); + +# if defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND) + /* Look for the command line replacement option. */ + if (argc > 1 && + strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_COMMAND) == 0) { + if (argc > 2) { + /* Use the command line given. */ + strcpy(exe, argv[2]); + argv += 2; + argc -= 2; + } else { + /* The option was not given an executable. */ + fprintf(stderr, + "Option " KWSYS_SHARED_FORWARD_OPTION_COMMAND + " must be followed by a command line.\n"); + return 1; + } + } +# endif + +# if defined(KWSYS_SHARED_FORWARD_OPTION_PRINT) + /* Look for the print command line option. */ + if (argc > 1 && + strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_PRINT) == 0) { + fprintf(stdout, "%s\n", kwsys_shared_forward_ldpath); + fprintf(stdout, "%s\n", exe); + return 0; + } +# endif + +# if defined(KWSYS_SHARED_FORWARD_OPTION_LDD) + /* Look for the ldd command line option. */ + if (argc > 1 && strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_LDD) == 0) { +# if defined(KWSYS_SHARED_FORWARD_LDD) + /* Use the named ldd-like executable and arguments. */ + char const* ldd_argv[] = { KWSYS_SHARED_FORWARD_LDD, 0, 0 }; + ldd_argv[KWSYS_SHARED_FORWARD_LDD_N] = exe; + kwsys_shared_forward_execvp(ldd_argv[0], ldd_argv); + + /* Report why execution failed. */ + kwsys_shared_forward_print_failure(ldd_argv); + return 1; +# else + /* We have no ldd-like executable available on this platform. */ + fprintf(stderr, "No ldd-like tool is known to this executable.\n"); + return 1; +# endif + } +# endif + + /* Replace this process with the real executable. */ + argv[0] = exe; + kwsys_shared_forward_execvp(argv[0], argv); + + /* Report why execution failed. */ + kwsys_shared_forward_print_failure(argv); + } else { + /* Could not convert self path to the library directory. */ + } + } else { + /* Could not find this executable. */ + fprintf(stderr, "Error locating executable \"%s\".\n", argv[0]); + } + + /* Avoid unused argument warning. */ + (void)argc; + + /* Exit with failure. */ + return 1; +} + +/* Restore warning stack. */ +# if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wcast-qual") +# pragma clang diagnostic pop +# endif +# endif + +#else +# error "@KWSYS_NAMESPACE@/SharedForward.h should be included only once." +#endif diff --git a/Source/kwsys/String.c b/Source/kwsys/String.c new file mode 100644 index 0000000..daf7ad1 --- /dev/null +++ b/Source/kwsys/String.c @@ -0,0 +1,100 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef KWSYS_STRING_C +/* +All code in this source file is conditionally compiled to work-around +template definition auto-search on VMS. Other source files in this +directory that use the stl string cause the compiler to load this +source to try to get the definition of the string template. This +condition blocks the compiler from seeing the symbols defined here. +*/ +# include "kwsysPrivate.h" +# include KWSYS_HEADER(String.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +# if 0 +# include "String.h.in" +# endif + +/* Select an implementation for strcasecmp. */ +# if defined(_MSC_VER) +# define KWSYS_STRING_USE_STRICMP +# include <string.h> +# elif defined(__GNUC__) +# define KWSYS_STRING_USE_STRCASECMP +# include <strings.h> +# else +/* Table to convert upper case letters to lower case and leave all + other characters alone. */ +static char kwsysString_strcasecmp_tolower[] = { + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', '\010', + '\011', '\012', '\013', '\014', '\015', '\016', '\017', '\020', '\021', + '\022', '\023', '\024', '\025', '\026', '\027', '\030', '\031', '\032', + '\033', '\034', '\035', '\036', '\037', '\040', '\041', '\042', '\043', + '\044', '\045', '\046', '\047', '\050', '\051', '\052', '\053', '\054', + '\055', '\056', '\057', '\060', '\061', '\062', '\063', '\064', '\065', + '\066', '\067', '\070', '\071', '\072', '\073', '\074', '\075', '\076', + '\077', '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', '\160', + '\161', '\162', '\163', '\164', '\165', '\166', '\167', '\170', '\171', + '\172', '\133', '\134', '\135', '\136', '\137', '\140', '\141', '\142', + '\143', '\144', '\145', '\146', '\147', '\150', '\151', '\152', '\153', + '\154', '\155', '\156', '\157', '\160', '\161', '\162', '\163', '\164', + '\165', '\166', '\167', '\170', '\171', '\172', '\173', '\174', '\175', + '\176', '\177', '\200', '\201', '\202', '\203', '\204', '\205', '\206', + '\207', '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', + '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', '\230', + '\231', '\232', '\233', '\234', '\235', '\236', '\237', '\240', '\241', + '\242', '\243', '\244', '\245', '\246', '\247', '\250', '\251', '\252', + '\253', '\254', '\255', '\256', '\257', '\260', '\261', '\262', '\263', + '\264', '\265', '\266', '\267', '\270', '\271', '\272', '\273', '\274', + '\275', '\276', '\277', '\300', '\301', '\302', '\303', '\304', '\305', + '\306', '\307', '\310', '\311', '\312', '\313', '\314', '\315', '\316', + '\317', '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327', + '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', '\340', + '\341', '\342', '\343', '\344', '\345', '\346', '\347', '\350', '\351', + '\352', '\353', '\354', '\355', '\356', '\357', '\360', '\361', '\362', + '\363', '\364', '\365', '\366', '\367', '\370', '\371', '\372', '\373', + '\374', '\375', '\376', '\377' +}; +# endif + +/*--------------------------------------------------------------------------*/ +int kwsysString_strcasecmp(const char* lhs, const char* rhs) +{ +# if defined(KWSYS_STRING_USE_STRICMP) + return _stricmp(lhs, rhs); +# elif defined(KWSYS_STRING_USE_STRCASECMP) + return strcasecmp(lhs, rhs); +# else + const char* const lower = kwsysString_strcasecmp_tolower; + unsigned char const* us1 = (unsigned char const*)lhs; + unsigned char const* us2 = (unsigned char const*)rhs; + int result; + while ((result = lower[*us1] - lower[*us2++], result == 0) && *us1++) { + } + return result; +# endif +} + +/*--------------------------------------------------------------------------*/ +int kwsysString_strncasecmp(const char* lhs, const char* rhs, size_t n) +{ +# if defined(KWSYS_STRING_USE_STRICMP) + return _strnicmp(lhs, rhs, n); +# elif defined(KWSYS_STRING_USE_STRCASECMP) + return strncasecmp(lhs, rhs, n); +# else + const char* const lower = kwsysString_strcasecmp_tolower; + unsigned char const* us1 = (unsigned char const*)lhs; + unsigned char const* us2 = (unsigned char const*)rhs; + int result = 0; + while (n && (result = lower[*us1] - lower[*us2++], result == 0) && *us1++) { + --n; + } + return result; +# endif +} + +#endif /* KWSYS_STRING_C */ diff --git a/Source/kwsys/String.h.in b/Source/kwsys/String.h.in new file mode 100644 index 0000000..7c9348a --- /dev/null +++ b/Source/kwsys/String.h.in @@ -0,0 +1,57 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_String_h +#define @KWSYS_NAMESPACE@_String_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +#include <stddef.h> /* size_t */ + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysString_strcasecmp kwsys_ns(String_strcasecmp) +# define kwsysString_strncasecmp kwsys_ns(String_strncasecmp) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Compare two strings ignoring the case of the characters. The + * integer returned is negative, zero, or positive if the first string + * is found to be less than, equal to, or greater than the second + * string, respectively. + */ +kwsysEXPORT int kwsysString_strcasecmp(const char* lhs, const char* rhs); + +/** + * Identical to String_strcasecmp except that only the first n + * characters are considered. + */ +kwsysEXPORT int kwsysString_strncasecmp(const char* lhs, const char* rhs, + size_t n); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysString_strcasecmp +# undef kwsysString_strncasecmp +# endif +#endif + +#endif diff --git a/Source/kwsys/String.hxx.in b/Source/kwsys/String.hxx.in new file mode 100644 index 0000000..db1cf22 --- /dev/null +++ b/Source/kwsys/String.hxx.in @@ -0,0 +1,65 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_String_hxx +#define @KWSYS_NAMESPACE@_String_hxx + +#include <string> + +namespace @KWSYS_NAMESPACE@ { + +/** \class String + * \brief Short-name version of the STL basic_string class template. + * + * The standard library "string" type is actually a typedef for + * "basic_string<..long argument list..>". This string class is + * simply a subclass of this type with the same interface so that the + * name is shorter in debugging symbols and error messages. + */ +class String : public std::string +{ + /** The original string type. */ + typedef std::string stl_string; + +public: + /** String member types. */ + typedef stl_string::value_type value_type; + typedef stl_string::pointer pointer; + typedef stl_string::reference reference; + typedef stl_string::const_reference const_reference; + typedef stl_string::size_type size_type; + typedef stl_string::difference_type difference_type; + typedef stl_string::iterator iterator; + typedef stl_string::const_iterator const_iterator; + typedef stl_string::reverse_iterator reverse_iterator; + typedef stl_string::const_reverse_iterator const_reverse_iterator; + + /** String constructors. */ + String() + : stl_string() + { + } + String(const value_type* s) + : stl_string(s) + { + } + String(const value_type* s, size_type n) + : stl_string(s, n) + { + } + String(const stl_string& s, size_type pos = 0, size_type n = npos) + : stl_string(s, pos, n) + { + } +}; // End Class: String + +#if defined(__WATCOMC__) +inline bool operator<(String const& l, String const& r) +{ + return (static_cast<std::string const&>(l) < + static_cast<std::string const&>(r)); +} +#endif + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/System.c b/Source/kwsys/System.c new file mode 100644 index 0000000..dbfd2fd --- /dev/null +++ b/Source/kwsys/System.c @@ -0,0 +1,236 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(System.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "System.h.in" +#endif + +#include <ctype.h> /* isspace */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* malloc, free */ +#include <string.h> /* memcpy */ + +#include <stdio.h> + +#if defined(KWSYS_C_HAS_PTRDIFF_T) && KWSYS_C_HAS_PTRDIFF_T +typedef ptrdiff_t kwsysSystem_ptrdiff_t; +#else +typedef int kwsysSystem_ptrdiff_t; +#endif + +static int kwsysSystem__AppendByte(const char* local, char** begin, char** end, + int* size, char c) +{ + /* Allocate space for the character. */ + if ((*end - *begin) >= *size) { + kwsysSystem_ptrdiff_t length = *end - *begin; + char* newBuffer = (char*)malloc((size_t)(*size * 2)); + if (!newBuffer) { + return 0; + } + memcpy(newBuffer, *begin, (size_t)(length) * sizeof(char)); + if (*begin != local) { + free(*begin); + } + *begin = newBuffer; + *end = *begin + length; + *size *= 2; + } + + /* Store the character. */ + *(*end)++ = c; + return 1; +} + +static int kwsysSystem__AppendArgument(char** local, char*** begin, + char*** end, int* size, char* arg_local, + char** arg_begin, char** arg_end, + int* arg_size) +{ + /* Append a null-terminator to the argument string. */ + if (!kwsysSystem__AppendByte(arg_local, arg_begin, arg_end, arg_size, + '\0')) { + return 0; + } + + /* Allocate space for the argument pointer. */ + if ((*end - *begin) >= *size) { + kwsysSystem_ptrdiff_t length = *end - *begin; + char** newPointers = (char**)malloc((size_t)(*size) * 2 * sizeof(char*)); + if (!newPointers) { + return 0; + } + memcpy(newPointers, *begin, (size_t)(length) * sizeof(char*)); + if (*begin != local) { + free(*begin); + } + *begin = newPointers; + *end = *begin + length; + *size *= 2; + } + + /* Allocate space for the argument string. */ + **end = (char*)malloc((size_t)(*arg_end - *arg_begin)); + if (!**end) { + return 0; + } + + /* Store the argument in the command array. */ + memcpy(**end, *arg_begin, (size_t)(*arg_end - *arg_begin)); + ++(*end); + + /* Reset the argument to be empty. */ + *arg_end = *arg_begin; + + return 1; +} + +#define KWSYSPE_LOCAL_BYTE_COUNT 1024 +#define KWSYSPE_LOCAL_ARGS_COUNT 32 +static char** kwsysSystem__ParseUnixCommand(const char* command, int flags) +{ + /* Create a buffer for argument pointers during parsing. */ + char* local_pointers[KWSYSPE_LOCAL_ARGS_COUNT]; + int pointers_size = KWSYSPE_LOCAL_ARGS_COUNT; + char** pointer_begin = local_pointers; + char** pointer_end = pointer_begin; + + /* Create a buffer for argument strings during parsing. */ + char local_buffer[KWSYSPE_LOCAL_BYTE_COUNT]; + int buffer_size = KWSYSPE_LOCAL_BYTE_COUNT; + char* buffer_begin = local_buffer; + char* buffer_end = buffer_begin; + + /* Parse the command string. Try to behave like a UNIX shell. */ + char** newCommand = 0; + const char* c = command; + int in_argument = 0; + int in_escape = 0; + int in_single = 0; + int in_double = 0; + int failed = 0; + for (; *c; ++c) { + if (in_escape) { + /* This character is escaped so do no special handling. */ + if (!in_argument) { + in_argument = 1; + } + if (!kwsysSystem__AppendByte(local_buffer, &buffer_begin, &buffer_end, + &buffer_size, *c)) { + failed = 1; + break; + } + in_escape = 0; + } else if (*c == '\\') { + /* The next character should be escaped. */ + in_escape = 1; + } else if (*c == '\'' && !in_double) { + /* Enter or exit single-quote state. */ + if (in_single) { + in_single = 0; + } else { + in_single = 1; + if (!in_argument) { + in_argument = 1; + } + } + } else if (*c == '"' && !in_single) { + /* Enter or exit double-quote state. */ + if (in_double) { + in_double = 0; + } else { + in_double = 1; + if (!in_argument) { + in_argument = 1; + } + } + } else if (isspace((unsigned char)*c)) { + if (in_argument) { + if (in_single || in_double) { + /* This space belongs to a quoted argument. */ + if (!kwsysSystem__AppendByte(local_buffer, &buffer_begin, + &buffer_end, &buffer_size, *c)) { + failed = 1; + break; + } + } else { + /* This argument has been terminated by whitespace. */ + if (!kwsysSystem__AppendArgument( + local_pointers, &pointer_begin, &pointer_end, &pointers_size, + local_buffer, &buffer_begin, &buffer_end, &buffer_size)) { + failed = 1; + break; + } + in_argument = 0; + } + } + } else { + /* This character belong to an argument. */ + if (!in_argument) { + in_argument = 1; + } + if (!kwsysSystem__AppendByte(local_buffer, &buffer_begin, &buffer_end, + &buffer_size, *c)) { + failed = 1; + break; + } + } + } + + /* Finish the last argument. */ + if (in_argument) { + if (!kwsysSystem__AppendArgument( + local_pointers, &pointer_begin, &pointer_end, &pointers_size, + local_buffer, &buffer_begin, &buffer_end, &buffer_size)) { + failed = 1; + } + } + + /* If we still have memory allocate space for the new command + buffer. */ + if (!failed) { + kwsysSystem_ptrdiff_t n = pointer_end - pointer_begin; + newCommand = (char**)malloc((size_t)(n + 1) * sizeof(char*)); + } + + if (newCommand) { + /* Copy the arguments into the new command buffer. */ + kwsysSystem_ptrdiff_t n = pointer_end - pointer_begin; + memcpy(newCommand, pointer_begin, sizeof(char*) * (size_t)(n)); + newCommand[n] = 0; + } else { + /* Free arguments already allocated. */ + while (pointer_end != pointer_begin) { + free(*(--pointer_end)); + } + } + + /* Free temporary buffers. */ + if (pointer_begin != local_pointers) { + free(pointer_begin); + } + if (buffer_begin != local_buffer) { + free(buffer_begin); + } + + /* The flags argument is currently unused. */ + (void)flags; + + /* Return the final command buffer. */ + return newCommand; +} + +char** kwsysSystem_Parse_CommandForUnix(const char* command, int flags) +{ + /* Validate the flags. */ + if (flags != 0) { + return 0; + } + + /* Forward to our internal implementation. */ + return kwsysSystem__ParseUnixCommand(command, flags); +} diff --git a/Source/kwsys/System.h.in b/Source/kwsys/System.h.in new file mode 100644 index 0000000..a9d4f5e --- /dev/null +++ b/Source/kwsys/System.h.in @@ -0,0 +1,60 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_System_h +#define @KWSYS_NAMESPACE@_System_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysSystem_Parse_CommandForUnix \ + kwsys_ns(System_Parse_CommandForUnix) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Parse a unix-style command line string into separate arguments. + * + * On success, returns a pointer to an array of pointers to individual + * argument strings. Each string is null-terminated and the last + * entry in the array is a NULL pointer (just like argv). It is the + * caller's responsibility to free() the strings and the array of + * pointers to them. + * + * On failure, returns NULL. Failure occurs only on invalid flags or + * when memory cannot be allocated; never due to content of the input + * string. Missing close-quotes are treated as if the necessary + * closing quote appears. + * + * By default single- and double-quoted arguments are supported, and + * any character may be escaped by a backslash. The flags argument is + * reserved for future use, and must be zero (or the call will fail). + */ +kwsysEXPORT char** kwsysSystem_Parse_CommandForUnix(const char* command, + int flags); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !defined(KWSYS_NAMESPACE) && !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysSystem_Parse_CommandForUnix +# endif +#endif + +#endif diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx new file mode 100644 index 0000000..3cf64a8 --- /dev/null +++ b/Source/kwsys/SystemInformation.cxx @@ -0,0 +1,5507 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#if defined(_WIN32) +# define NOMINMAX // use our min,max +# if !defined(_WIN32_WINNT) && defined(_MSC_VER) && _MSC_VER >= 1800 +# define _WIN32_WINNT 0x0600 // vista +# endif +# if !defined(_WIN32_WINNT) && !(defined(_MSC_VER) && _MSC_VER < 1300) +# define _WIN32_WINNT 0x0501 +# endif +# include <winsock.h> // WSADATA, include before sys/types.h +#endif + +#if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +#endif + +// TODO: +// We need an alternative implementation for many functions in this file +// when USE_ASM_INSTRUCTIONS gets defined as 0. +// +// Consider using these on Win32/Win64 for some of them: +// +// IsProcessorFeaturePresent +// http://msdn.microsoft.com/en-us/library/ms724482(VS.85).aspx +// +// GetProcessMemoryInfo +// http://msdn.microsoft.com/en-us/library/ms683219(VS.85).aspx + +#include "kwsysPrivate.h" +#include KWSYS_HEADER(SystemInformation.hxx) +#include KWSYS_HEADER(Process.h) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Process.h.in" +# include "SystemInformation.hxx.in" +#endif + +#include <algorithm> +#include <bitset> +#include <cassert> +#include <fstream> +#include <iostream> +#include <limits> +#include <set> +#include <sstream> +#include <string> +#include <vector> + +#if defined(_WIN32) +# include <windows.h> +# if defined(_MSC_VER) && _MSC_VER >= 1800 +# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# endif +# include <errno.h> +# if defined(KWSYS_SYS_HAS_PSAPI) +# include <psapi.h> +# endif +# if !defined(siginfo_t) +typedef int siginfo_t; +# endif +#else +# include <sys/types.h> + +# include <errno.h> // extern int errno; +# include <fcntl.h> +# include <signal.h> +# include <sys/resource.h> // getrlimit +# include <sys/time.h> +# include <sys/utsname.h> // int uname(struct utsname *buf); +# include <unistd.h> +#endif + +#if defined(__CYGWIN__) && !defined(_WIN32) +# include <windows.h> +# undef _WIN32 +#endif + +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) +# include <netdb.h> +# include <netinet/in.h> +# include <sys/param.h> +# include <sys/socket.h> +# include <sys/sysctl.h> +# if defined(KWSYS_SYS_HAS_IFADDRS_H) +# include <ifaddrs.h> +# include <net/if.h> +# define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN +# endif +#endif + +#if defined(KWSYS_SYS_HAS_MACHINE_CPU_H) +# include <machine/cpu.h> +#endif + +#ifdef __APPLE__ +# include <mach/host_info.h> +# include <mach/mach.h> +# include <mach/mach_types.h> +# include <mach/vm_statistics.h> +# include <netdb.h> +# include <netinet/in.h> +# include <sys/socket.h> +# include <sys/sysctl.h> +# if defined(KWSYS_SYS_HAS_IFADDRS_H) +# include <ifaddrs.h> +# include <net/if.h> +# define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN +# endif +# if !(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - 0 >= 1050) +# undef KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE +# endif +#endif + +#if defined(__linux) || defined(__sun) || defined(_SCO_DS) || \ + defined(__GLIBC__) || defined(__GNU__) +# include <netdb.h> +# include <netinet/in.h> +# include <sys/socket.h> +# if defined(KWSYS_SYS_HAS_IFADDRS_H) +# include <ifaddrs.h> +# include <net/if.h> +# if defined(__LSB_VERSION__) +/* LSB has no getifaddrs */ +# elif defined(__ANDROID_API__) && __ANDROID_API__ < 24 +/* Android has no getifaddrs prior to API 24. */ +# else +# define KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN +# endif +# endif +# if defined(KWSYS_CXX_HAS_RLIMIT64) +using ResourceLimitType = struct rlimit64; +# define GetResourceLimit getrlimit64 +# else +typedef struct rlimit ResourceLimitType; +# define GetResourceLimit getrlimit +# endif +#elif defined(__hpux) +# include <sys/param.h> +# include <sys/pstat.h> +# if defined(KWSYS_SYS_HAS_MPCTL_H) +# include <sys/mpctl.h> +# endif +#endif + +#ifdef __HAIKU__ +# include <OS.h> +#endif + +#if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) +# include <execinfo.h> +# if defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE) +# include <cxxabi.h> +# endif +# if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP) +# include <dlfcn.h> +# endif +#else +# undef KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE +# undef KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP +#endif + +#include <ctype.h> // int isdigit(int c); +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(KWSYS_USE_LONG_LONG) +# if defined(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)(x)) +# endif +#elif defined(KWSYS_USE___INT64) +# if defined(KWSYS_IOS_HAS_OSTREAM___INT64) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)(x)) +# endif +#else +# error "No Long Long" +#endif + +#if defined(KWSYS_CXX_HAS_ATOLL) +# define atoLongLong atoll +#else +# if defined(KWSYS_CXX_HAS__ATOI64) +# define atoLongLong _atoi64 +# elif defined(KWSYS_CXX_HAS_ATOL) +# define atoLongLong atol +# else +# define atoLongLong atoi +# endif +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) && !defined(_WIN64) && \ + !defined(__clang__) +# define USE_ASM_INSTRUCTIONS 1 +#else +# define USE_ASM_INSTRUCTIONS 0 +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__clang__) +# include <intrin.h> +# define USE_CPUID_INTRINSICS 1 +#else +# define USE_CPUID_INTRINSICS 0 +#endif + +#if USE_ASM_INSTRUCTIONS || USE_CPUID_INTRINSICS || \ + defined(KWSYS_CXX_HAS_BORLAND_ASM_CPUID) +# define USE_CPUID 1 +#else +# define USE_CPUID 0 +#endif + +#if USE_CPUID + +# define CPUID_AWARE_COMPILER + +/** + * call CPUID instruction + * + * Will return false if the instruction failed. + */ +static bool call_cpuid(int select, int result[4]) +{ +# if USE_CPUID_INTRINSICS + __cpuid(result, select); + return true; +# else + int tmp[4]; +# if defined(_MSC_VER) + // Use SEH to determine CPUID presence + __try { + _asm { +# ifdef CPUID_AWARE_COMPILER + ; we must push/pop the registers <<CPUID>> writes to, as the + ; optimiser does not know about <<CPUID>>, and so does not expect + ; these registers to change. + push eax + push ebx + push ecx + push edx +# endif + ; <<CPUID>> + mov eax, select +# ifdef CPUID_AWARE_COMPILER + cpuid +# else + _asm _emit 0x0f + _asm _emit 0xa2 +# endif + mov tmp[0 * TYPE int], eax + mov tmp[1 * TYPE int], ebx + mov tmp[2 * TYPE int], ecx + mov tmp[3 * TYPE int], edx + +# ifdef CPUID_AWARE_COMPILER + pop edx + pop ecx + pop ebx + pop eax +# endif + } + } __except (1) { + return false; + } + + memcpy(result, tmp, sizeof(tmp)); +# elif defined(KWSYS_CXX_HAS_BORLAND_ASM_CPUID) + unsigned int a, b, c, d; + __asm { + mov EAX, select; + cpuid + mov a, EAX; + mov b, EBX; + mov c, ECX; + mov d, EDX; + } + + result[0] = a; + result[1] = b; + result[2] = c; + result[3] = d; +# endif + + // The cpuid instruction succeeded. + return true; +# endif +} +#endif + +namespace KWSYS_NAMESPACE { +template <typename T> +T min(T a, T b) +{ + return a < b ? a : b; +} + +extern "C" { +using SigAction = void (*)(int, siginfo_t*, void*); +} + +// Define SystemInformationImplementation class +using DELAY_FUNC = void (*)(unsigned int); + +class SystemInformationImplementation +{ +public: + using LongLong = SystemInformation::LongLong; + SystemInformationImplementation(); + ~SystemInformationImplementation() = default; + + const char* GetVendorString() const; + const char* GetVendorID(); + std::string GetTypeID() const; + std::string GetFamilyID() const; + std::string GetModelID() const; + std::string GetModelName() const; + std::string GetSteppingCode() const; + const char* GetExtendedProcessorName() const; + const char* GetProcessorSerialNumber() const; + int GetProcessorCacheSize() const; + unsigned int GetLogicalProcessorsPerPhysical() const; + float GetProcessorClockFrequency() const; + int GetProcessorAPICID() const; + int GetProcessorCacheXSize(long int) const; + bool DoesCPUSupportFeature(long int) const; + + const char* GetOSName(); + const char* GetHostname(); + int GetFullyQualifiedDomainName(std::string& fqdn); + const char* GetOSRelease(); + const char* GetOSVersion(); + const char* GetOSPlatform(); + + bool Is64Bits() const; + + unsigned int GetNumberOfLogicalCPU() const; // per physical cpu + unsigned int GetNumberOfPhysicalCPU() const; + + bool DoesCPUSupportCPUID(); + + // Retrieve memory information in MiB. + size_t GetTotalVirtualMemory() const; + size_t GetAvailableVirtualMemory() const; + size_t GetTotalPhysicalMemory() const; + size_t GetAvailablePhysicalMemory() const; + + LongLong GetProcessId(); + + // Retrieve memory information in KiB. + LongLong GetHostMemoryTotal(); + LongLong GetHostMemoryAvailable(const char* hostLimitEnvVarName); + LongLong GetHostMemoryUsed(); + + LongLong GetProcMemoryAvailable(const char* hostLimitEnvVarName, + const char* procLimitEnvVarName); + LongLong GetProcMemoryUsed(); + + double GetLoadAverage(); + + // enable/disable stack trace signal handler. + static void SetStackTraceOnError(int enable); + + // get current stack + static std::string GetProgramStack(int firstFrame, int wholePath); + + /** Run the different checks */ + void RunCPUCheck(); + void RunOSCheck(); + void RunMemoryCheck(); + +public: + using ID = struct tagID + + { + + int Type; + + int Family; + + int Model; + + int Revision; + + int ExtendedFamily; + + int ExtendedModel; + + std::string ProcessorName; + + std::string Vendor; + + std::string SerialNumber; + + std::string ModelName; + }; + + using CPUPowerManagement = struct tagCPUPowerManagement + + { + + bool HasVoltageID; + + bool HasFrequencyID; + + bool HasTempSenseDiode; + }; + + using CPUExtendedFeatures = struct tagCPUExtendedFeatures + + { + + bool Has3DNow; + + bool Has3DNowPlus; + + bool SupportsMP; + + bool HasMMXPlus; + + bool HasSSEMMX; + + unsigned int LogicalProcessorsPerPhysical; + + int APIC_ID; + + CPUPowerManagement PowerManagement; + }; + + using CPUFeatures = struct CPUtagFeatures + + { + + bool HasFPU; + + bool HasTSC; + + bool HasMMX; + + bool HasSSE; + + bool HasSSEFP; + + bool HasSSE2; + + bool HasIA64; + + bool HasAPIC; + + bool HasCMOV; + + bool HasMTRR; + + bool HasACPI; + + bool HasSerial; + + bool HasThermal; + + int CPUSpeed; + + int L1CacheSize; + + int L2CacheSize; + + int L3CacheSize; + + CPUExtendedFeatures ExtendedFeatures; + }; + + enum Manufacturer + { + AMD, + Intel, + NSC, + UMC, + Cyrix, + NexGen, + IDT, + Rise, + Transmeta, + Sun, + IBM, + Motorola, + HP, + Hygon, + UnknownManufacturer + }; + +protected: + // For windows + bool RetrieveCPUFeatures(); + bool RetrieveCPUIdentity(); + bool RetrieveCPUCacheDetails(); + bool RetrieveClassicalCPUCacheDetails(); + bool RetrieveCPUClockSpeed(); + bool RetrieveClassicalCPUClockSpeed(); + bool RetrieveCPUExtendedLevelSupport(int); + bool RetrieveExtendedCPUFeatures(); + bool RetrieveProcessorSerialNumber(); + bool RetrieveCPUPowerManagement(); + bool RetrieveClassicalCPUIdentity(); + bool RetrieveExtendedCPUIdentity(); + + // Processor information + Manufacturer ChipManufacturer; + CPUFeatures Features; + ID ChipID; + float CPUSpeedInMHz; + unsigned int NumberOfLogicalCPU; + unsigned int NumberOfPhysicalCPU; + + void CPUCountWindows(); // For windows + unsigned char GetAPICId(); // For windows + bool IsSMTSupported() const; + static LongLong GetCyclesDifference(DELAY_FUNC, unsigned int); // For windows + + // For Linux and Cygwin, /proc/cpuinfo formats are slightly different + bool RetreiveInformationFromCpuInfoFile(); + std::string ExtractValueFromCpuInfoFile(std::string buffer, const char* word, + size_t init = 0); + + bool QueryLinuxMemory(); + bool QueryCygwinMemory(); + + static void Delay(unsigned int); + static void DelayOverhead(unsigned int); + + void FindManufacturer(const std::string& family = ""); + + // For Mac + bool ParseSysCtl(); + int CallSwVers(const char* arg, std::string& ver); + void TrimNewline(std::string&); + std::string ExtractValueFromSysCtl(const char* word); + std::string SysCtlBuffer; + + // For Solaris + bool QuerySolarisMemory(); + bool QuerySolarisProcessor(); + std::string ParseValueFromKStat(const char* arguments); + std::string RunProcess(std::vector<const char*> args); + + // For Haiku OS + bool QueryHaikuInfo(); + + // For QNX + bool QueryQNXMemory(); + bool QueryQNXProcessor(); + + // For OpenBSD, FreeBSD, NetBSD, DragonFly + bool QueryBSDMemory(); + bool QueryBSDProcessor(); + + // For HP-UX + bool QueryHPUXMemory(); + bool QueryHPUXProcessor(); + + // For Microsoft Windows + bool QueryWindowsMemory(); + + // For AIX + bool QueryAIXMemory(); + + bool QueryProcessorBySysconf(); + bool QueryProcessor(); + + // Evaluate the memory information. + bool QueryMemoryBySysconf(); + bool QueryMemory(); + size_t TotalVirtualMemory; + size_t AvailableVirtualMemory; + size_t TotalPhysicalMemory; + size_t AvailablePhysicalMemory; + + size_t CurrentPositionInFile; + + // Operating System information + bool QueryOSInformation(); + std::string OSName; + std::string Hostname; + std::string OSRelease; + std::string OSVersion; + std::string OSPlatform; + bool OSIs64Bit; +}; + +SystemInformation::SystemInformation() +{ + this->Implementation = new SystemInformationImplementation; +} + +SystemInformation::~SystemInformation() +{ + delete this->Implementation; +} + +const char* SystemInformation::GetVendorString() +{ + return this->Implementation->GetVendorString(); +} + +const char* SystemInformation::GetVendorID() +{ + return this->Implementation->GetVendorID(); +} + +std::string SystemInformation::GetTypeID() +{ + return this->Implementation->GetTypeID(); +} + +std::string SystemInformation::GetFamilyID() +{ + return this->Implementation->GetFamilyID(); +} + +std::string SystemInformation::GetModelID() +{ + return this->Implementation->GetModelID(); +} + +std::string SystemInformation::GetModelName() +{ + return this->Implementation->GetModelName(); +} + +std::string SystemInformation::GetSteppingCode() +{ + return this->Implementation->GetSteppingCode(); +} + +const char* SystemInformation::GetExtendedProcessorName() +{ + return this->Implementation->GetExtendedProcessorName(); +} + +const char* SystemInformation::GetProcessorSerialNumber() +{ + return this->Implementation->GetProcessorSerialNumber(); +} + +int SystemInformation::GetProcessorCacheSize() +{ + return this->Implementation->GetProcessorCacheSize(); +} + +unsigned int SystemInformation::GetLogicalProcessorsPerPhysical() +{ + return this->Implementation->GetLogicalProcessorsPerPhysical(); +} + +float SystemInformation::GetProcessorClockFrequency() +{ + return this->Implementation->GetProcessorClockFrequency(); +} + +int SystemInformation::GetProcessorAPICID() +{ + return this->Implementation->GetProcessorAPICID(); +} + +int SystemInformation::GetProcessorCacheXSize(long int l) +{ + return this->Implementation->GetProcessorCacheXSize(l); +} + +bool SystemInformation::DoesCPUSupportFeature(long int i) +{ + return this->Implementation->DoesCPUSupportFeature(i); +} + +std::string SystemInformation::GetCPUDescription() +{ + std::ostringstream oss; + oss << this->GetNumberOfPhysicalCPU() << " core "; + if (this->GetModelName().empty()) { + oss << this->GetProcessorClockFrequency() << " MHz " + << this->GetVendorString() << " " << this->GetExtendedProcessorName(); + } else { + oss << this->GetModelName(); + } + + // remove extra spaces + std::string tmp = oss.str(); + size_t pos; + while ((pos = tmp.find(" ")) != std::string::npos) { + tmp.replace(pos, 2, " "); + } + + return tmp; +} + +const char* SystemInformation::GetOSName() +{ + return this->Implementation->GetOSName(); +} + +const char* SystemInformation::GetHostname() +{ + return this->Implementation->GetHostname(); +} + +std::string SystemInformation::GetFullyQualifiedDomainName() +{ + std::string fqdn; + this->Implementation->GetFullyQualifiedDomainName(fqdn); + return fqdn; +} + +const char* SystemInformation::GetOSRelease() +{ + return this->Implementation->GetOSRelease(); +} + +const char* SystemInformation::GetOSVersion() +{ + return this->Implementation->GetOSVersion(); +} + +const char* SystemInformation::GetOSPlatform() +{ + return this->Implementation->GetOSPlatform(); +} + +int SystemInformation::GetOSIsWindows() +{ +#if defined(_WIN32) + return 1; +#else + return 0; +#endif +} + +int SystemInformation::GetOSIsLinux() +{ +#if defined(__linux) + return 1; +#else + return 0; +#endif +} + +int SystemInformation::GetOSIsApple() +{ +#if defined(__APPLE__) + return 1; +#else + return 0; +#endif +} + +std::string SystemInformation::GetOSDescription() +{ + std::ostringstream oss; + oss << this->GetOSName() << " " << this->GetOSRelease() << " " + << this->GetOSVersion(); + + return oss.str(); +} + +bool SystemInformation::Is64Bits() +{ + return this->Implementation->Is64Bits(); +} + +unsigned int SystemInformation::GetNumberOfLogicalCPU() // per physical cpu +{ + return this->Implementation->GetNumberOfLogicalCPU(); +} + +unsigned int SystemInformation::GetNumberOfPhysicalCPU() +{ + return this->Implementation->GetNumberOfPhysicalCPU(); +} + +bool SystemInformation::DoesCPUSupportCPUID() +{ + return this->Implementation->DoesCPUSupportCPUID(); +} + +// Retrieve memory information in MiB. +size_t SystemInformation::GetTotalVirtualMemory() +{ + return this->Implementation->GetTotalVirtualMemory(); +} + +size_t SystemInformation::GetAvailableVirtualMemory() +{ + return this->Implementation->GetAvailableVirtualMemory(); +} + +size_t SystemInformation::GetTotalPhysicalMemory() +{ + return this->Implementation->GetTotalPhysicalMemory(); +} + +size_t SystemInformation::GetAvailablePhysicalMemory() +{ + return this->Implementation->GetAvailablePhysicalMemory(); +} + +std::string SystemInformation::GetMemoryDescription( + const char* hostLimitEnvVarName, const char* procLimitEnvVarName) +{ + std::ostringstream oss; + oss << "Host Total: " << iostreamLongLong(this->GetHostMemoryTotal()) + << " KiB, Host Available: " + << iostreamLongLong(this->GetHostMemoryAvailable(hostLimitEnvVarName)) + << " KiB, Process Available: " + << iostreamLongLong(this->GetProcMemoryAvailable(hostLimitEnvVarName, + procLimitEnvVarName)) + << " KiB"; + return oss.str(); +} + +// host memory info in units of KiB. +SystemInformation::LongLong SystemInformation::GetHostMemoryTotal() +{ + return this->Implementation->GetHostMemoryTotal(); +} + +SystemInformation::LongLong SystemInformation::GetHostMemoryAvailable( + const char* hostLimitEnvVarName) +{ + return this->Implementation->GetHostMemoryAvailable(hostLimitEnvVarName); +} + +SystemInformation::LongLong SystemInformation::GetHostMemoryUsed() +{ + return this->Implementation->GetHostMemoryUsed(); +} + +// process memory info in units of KiB. +SystemInformation::LongLong SystemInformation::GetProcMemoryAvailable( + const char* hostLimitEnvVarName, const char* procLimitEnvVarName) +{ + return this->Implementation->GetProcMemoryAvailable(hostLimitEnvVarName, + procLimitEnvVarName); +} + +SystemInformation::LongLong SystemInformation::GetProcMemoryUsed() +{ + return this->Implementation->GetProcMemoryUsed(); +} + +double SystemInformation::GetLoadAverage() +{ + return this->Implementation->GetLoadAverage(); +} + +SystemInformation::LongLong SystemInformation::GetProcessId() +{ + return this->Implementation->GetProcessId(); +} + +void SystemInformation::SetStackTraceOnError(int enable) +{ + SystemInformationImplementation::SetStackTraceOnError(enable); +} + +std::string SystemInformation::GetProgramStack(int firstFrame, int wholePath) +{ + return SystemInformationImplementation::GetProgramStack(firstFrame, + wholePath); +} + +/** Run the different checks */ +void SystemInformation::RunCPUCheck() +{ + this->Implementation->RunCPUCheck(); +} + +void SystemInformation::RunOSCheck() +{ + this->Implementation->RunOSCheck(); +} + +void SystemInformation::RunMemoryCheck() +{ + this->Implementation->RunMemoryCheck(); +} + +// SystemInformationImplementation starts here + +#if USE_CPUID +# define STORE_TLBCACHE_INFO(x, y) x = (x < (y)) ? (y) : x +# define TLBCACHE_INFO_UNITS (15) +#endif + +#if USE_ASM_INSTRUCTIONS +# define CLASSICAL_CPU_FREQ_LOOP 10000000 +# define RDTSC_INSTRUCTION _asm _emit 0x0f _asm _emit 0x31 +#endif + +#define INITIAL_APIC_ID_BITS 0xFF000000 +// initial APIC ID for the processor this code is running on. +// Default value = 0xff if HT is not supported + +// Hide implementation details in an anonymous namespace. +namespace { +// ***************************************************************************** +#if defined(__linux) || defined(__APPLE__) +int LoadLines(FILE* file, std::vector<std::string>& lines) +{ + // Load each line in the given file into a the vector. + int nRead = 0; + const int bufSize = 1024; + char buf[bufSize] = { '\0' }; + while (!feof(file) && !ferror(file)) { + errno = 0; + if (fgets(buf, bufSize, file) == nullptr) { + if (ferror(file) && (errno == EINTR)) { + clearerr(file); + } + continue; + } + char* pBuf = buf; + while (*pBuf) { + if (*pBuf == '\n') + *pBuf = '\0'; + pBuf += 1; + } + lines.emplace_back(buf); + ++nRead; + } + if (ferror(file)) { + return 0; + } + return nRead; +} + +# if defined(__linux) +// ***************************************************************************** +int LoadLines(const char* fileName, std::vector<std::string>& lines) +{ + FILE* file = fopen(fileName, "r"); + if (file == nullptr) { + return 0; + } + int nRead = LoadLines(file, lines); + fclose(file); + return nRead; +} +# endif + +// **************************************************************************** +template <typename T> +int NameValue(std::vector<std::string> const& lines, std::string const& name, + T& value) +{ + size_t nLines = lines.size(); + for (size_t i = 0; i < nLines; ++i) { + size_t at = lines[i].find(name); + if (at == std::string::npos) { + continue; + } + std::istringstream is(lines[i].substr(at + name.size())); + is >> value; + return 0; + } + return -1; +} +#endif + +#if defined(__linux) +// **************************************************************************** +template <typename T> +int GetFieldsFromFile(const char* fileName, const char** fieldNames, T* values) +{ + std::vector<std::string> fields; + if (!LoadLines(fileName, fields)) { + return -1; + } + int i = 0; + while (fieldNames[i] != nullptr) { + int ierr = NameValue(fields, fieldNames[i], values[i]); + if (ierr) { + return -(i + 2); + } + i += 1; + } + return 0; +} + +// **************************************************************************** +template <typename T> +int GetFieldFromFile(const char* fileName, const char* fieldName, T& value) +{ + const char* fieldNames[2] = { fieldName, nullptr }; + T values[1] = { T(0) }; + int ierr = GetFieldsFromFile(fileName, fieldNames, values); + if (ierr) { + return ierr; + } + value = values[0]; + return 0; +} +#endif + +// **************************************************************************** +#if defined(__APPLE__) +template <typename T> +int GetFieldsFromCommand(const char* command, const char** fieldNames, + T* values) +{ + FILE* file = popen(command, "r"); + if (file == nullptr) { + return -1; + } + std::vector<std::string> fields; + int nl = LoadLines(file, fields); + pclose(file); + if (nl == 0) { + return -1; + } + int i = 0; + while (fieldNames[i] != nullptr) { + int ierr = NameValue(fields, fieldNames[i], values[i]); + if (ierr) { + return -(i + 2); + } + i += 1; + } + return 0; +} +#endif + +// **************************************************************************** +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, + void* /*sigContext*/) +{ +# if defined(__linux) || defined(__APPLE__) + std::ostringstream oss; + oss << std::endl + << "=========================================================" + << std::endl + << "Process id " << getpid() << " "; + switch (sigNo) { + case SIGINT: + oss << "Caught SIGINT"; + break; + + case SIGTERM: + oss << "Caught SIGTERM"; + break; + + case SIGABRT: + oss << "Caught SIGABRT"; + break; + + case SIGFPE: + oss << "Caught SIGFPE at " << (sigInfo->si_addr == nullptr ? "0x" : "") + << sigInfo->si_addr << " "; + switch (sigInfo->si_code) { +# if defined(FPE_INTDIV) + case FPE_INTDIV: + oss << "integer division by zero"; + break; +# endif + +# if defined(FPE_INTOVF) + case FPE_INTOVF: + oss << "integer overflow"; + break; +# endif + + case FPE_FLTDIV: + oss << "floating point divide by zero"; + break; + + case FPE_FLTOVF: + oss << "floating point overflow"; + break; + + case FPE_FLTUND: + oss << "floating point underflow"; + break; + + case FPE_FLTRES: + oss << "floating point inexact result"; + break; + + case FPE_FLTINV: + oss << "floating point invalid operation"; + break; + +# if defined(FPE_FLTSUB) + case FPE_FLTSUB: + oss << "floating point subscript out of range"; + break; +# endif + + default: + oss << "code " << sigInfo->si_code; + break; + } + break; + + case SIGSEGV: + oss << "Caught SIGSEGV at " << (sigInfo->si_addr == nullptr ? "0x" : "") + << sigInfo->si_addr << " "; + switch (sigInfo->si_code) { + case SEGV_MAPERR: + oss << "address not mapped to object"; + break; + + case SEGV_ACCERR: + oss << "invalid permission for mapped object"; + break; + + default: + oss << "code " << sigInfo->si_code; + break; + } + break; + + case SIGBUS: + oss << "Caught SIGBUS at " << (sigInfo->si_addr == nullptr ? "0x" : "") + << sigInfo->si_addr << " "; + switch (sigInfo->si_code) { + case BUS_ADRALN: + oss << "invalid address alignment"; + break; + +# if defined(BUS_ADRERR) + case BUS_ADRERR: + oss << "nonexistent physical address"; + break; +# endif + +# if defined(BUS_OBJERR) + case BUS_OBJERR: + oss << "object-specific hardware error"; + break; +# endif + +# if defined(BUS_MCEERR_AR) + case BUS_MCEERR_AR: + oss << "Hardware memory error consumed on a machine check; action " + "required."; + break; +# endif + +# if defined(BUS_MCEERR_AO) + case BUS_MCEERR_AO: + oss << "Hardware memory error detected in process but not consumed; " + "action optional."; + break; +# endif + + default: + oss << "code " << sigInfo->si_code; + break; + } + break; + + case SIGILL: + oss << "Caught SIGILL at " << (sigInfo->si_addr == nullptr ? "0x" : "") + << sigInfo->si_addr << " "; + switch (sigInfo->si_code) { + case ILL_ILLOPC: + oss << "illegal opcode"; + break; + +# if defined(ILL_ILLOPN) + case ILL_ILLOPN: + oss << "illegal operand"; + break; +# endif + +# if defined(ILL_ILLADR) + case ILL_ILLADR: + oss << "illegal addressing mode."; + break; +# endif + + case ILL_ILLTRP: + oss << "illegal trap"; + break; + + case ILL_PRVOPC: + oss << "privileged opcode"; + break; + +# if defined(ILL_PRVREG) + case ILL_PRVREG: + oss << "privileged register"; + break; +# endif + +# if defined(ILL_COPROC) + case ILL_COPROC: + oss << "co-processor error"; + break; +# endif + +# if defined(ILL_BADSTK) + case ILL_BADSTK: + oss << "internal stack error"; + break; +# endif + + default: + oss << "code " << sigInfo->si_code; + break; + } + break; + + default: + oss << "Caught " << sigNo << " code " << sigInfo->si_code; + break; + } + oss << std::endl + << "Program Stack:" << std::endl + << SystemInformationImplementation::GetProgramStack(2, 0) + << "=========================================================" + << std::endl; + std::cerr << oss.str() << std::endl; + + // restore the previously registered handlers + // and abort + SystemInformationImplementation::SetStackTraceOnError(0); + abort(); +# else + // avoid warning C4100 + (void)sigNo; + (void)sigInfo; +# endif +} +#endif + +#if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) +# define safes(_arg) ((_arg) ? (_arg) : "???") + +// Description: +// A container for symbol properties. Each instance +// must be Initialized. +class SymbolProperties +{ +public: + SymbolProperties(); + + // Description: + // The SymbolProperties instance must be initialized by + // passing a stack address. + void Initialize(void* address); + + // Description: + // Get the symbol's stack address. + void* GetAddress() const { return this->Address; } + + // Description: + // If not set paths will be removed. eg, from a binary + // or source file. + void SetReportPath(int rp) { this->ReportPath = rp; } + + // Description: + // Set/Get the name of the binary file that the symbol + // is found in. + void SetBinary(const char* binary) { this->Binary = safes(binary); } + + std::string GetBinary() const; + + // Description: + // Set the name of the function that the symbol is found in. + // If c++ demangling is supported it will be demangled. + void SetFunction(const char* function) + { + this->Function = this->Demangle(function); + } + + std::string GetFunction() const { return this->Function; } + + // Description: + // Set/Get the name of the source file where the symbol + // is defined. + void SetSourceFile(const char* sourcefile) + { + this->SourceFile = safes(sourcefile); + } + + std::string GetSourceFile() const + { + return this->GetFileName(this->SourceFile); + } + + // Description: + // Set/Get the line number where the symbol is defined + void SetLineNumber(long linenumber) { this->LineNumber = linenumber; } + long GetLineNumber() const { return this->LineNumber; } + + // Description: + // Set the address where the binary image is mapped + // into memory. + void SetBinaryBaseAddress(void* address) + { + this->BinaryBaseAddress = address; + } + +private: + void* GetRealAddress() const + { + return (void*)((char*)this->Address - (char*)this->BinaryBaseAddress); + } + + std::string GetFileName(const std::string& path) const; + std::string Demangle(const char* symbol) const; + +private: + std::string Binary; + void* BinaryBaseAddress; + void* Address; + std::string SourceFile; + std::string Function; + long LineNumber; + int ReportPath; +}; + +std::ostream& operator<<(std::ostream& os, const SymbolProperties& sp) +{ +# if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP) + os << std::hex << sp.GetAddress() << " : " << sp.GetFunction() << " [(" + << sp.GetBinary() << ") " << sp.GetSourceFile() << ":" << std::dec + << sp.GetLineNumber() << "]"; +# elif defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) + void* addr = sp.GetAddress(); + char** syminfo = backtrace_symbols(&addr, 1); + os << safes(syminfo[0]); + free(syminfo); +# else + (void)os; + (void)sp; +# endif + return os; +} + +SymbolProperties::SymbolProperties() +{ + // not using an initializer list + // to avoid some PGI compiler warnings + this->SetBinary("???"); + this->SetBinaryBaseAddress(nullptr); + this->Address = nullptr; + this->SetSourceFile("???"); + this->SetFunction("???"); + this->SetLineNumber(-1); + this->SetReportPath(0); + // avoid PGI compiler warnings + this->GetRealAddress(); + this->GetFunction(); + this->GetSourceFile(); + this->GetLineNumber(); +} + +std::string SymbolProperties::GetFileName(const std::string& path) const +{ + std::string file(path); + if (!this->ReportPath) { + size_t at = file.rfind("/"); + if (at != std::string::npos) { + file = file.substr(at + 1); + } + } + return file; +} + +std::string SymbolProperties::GetBinary() const +{ +// only linux has proc fs +# if defined(__linux__) + if (this->Binary == "/proc/self/exe") { + std::string binary; + char buf[1024] = { '\0' }; + ssize_t ll = 0; + if ((ll = readlink("/proc/self/exe", buf, 1024)) > 0 && ll < 1024) { + buf[ll] = '\0'; + binary = buf; + } else { + binary = "/proc/self/exe"; + } + return this->GetFileName(binary); + } +# endif + return this->GetFileName(this->Binary); +} + +std::string SymbolProperties::Demangle(const char* symbol) const +{ + std::string result = safes(symbol); +# if defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE) + int status = 0; + size_t bufferLen = 1024; + char* buffer = (char*)malloc(1024); + char* demangledSymbol = + abi::__cxa_demangle(symbol, buffer, &bufferLen, &status); + if (!status) { + result = demangledSymbol; + } + free(buffer); +# else + (void)symbol; +# endif + return result; +} + +void SymbolProperties::Initialize(void* address) +{ + this->Address = address; +# if defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP) + // first fallback option can demangle c++ functions + Dl_info info; + int ierr = dladdr(this->Address, &info); + if (ierr && info.dli_sname && info.dli_saddr) { + this->SetBinary(info.dli_fname); + this->SetFunction(info.dli_sname); + } +# else +// second fallback use builtin backtrace_symbols +// to decode the bactrace. +# endif +} +#endif // don't define this class if we're not using it + +#if defined(_WIN32) || defined(__CYGWIN__) +# define KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes +#endif +#if defined(_MSC_VER) && _MSC_VER < 1310 +# undef KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes +#endif +#if defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes) +double calculateCPULoad(unsigned __int64 idleTicks, + unsigned __int64 totalTicks) +{ + static double previousLoad = -0.0; + static unsigned __int64 previousIdleTicks = 0; + static unsigned __int64 previousTotalTicks = 0; + + unsigned __int64 const idleTicksSinceLastTime = + idleTicks - previousIdleTicks; + unsigned __int64 const totalTicksSinceLastTime = + totalTicks - previousTotalTicks; + + double load; + if (previousTotalTicks == 0 || totalTicksSinceLastTime == 0) { + // No new information. Use previous result. + load = previousLoad; + } else { + // Calculate load since last time. + load = 1.0 - double(idleTicksSinceLastTime) / totalTicksSinceLastTime; + + // Smooth if possible. + if (previousLoad > 0) { + load = 0.25 * load + 0.75 * previousLoad; + } + } + + previousLoad = load; + previousIdleTicks = idleTicks; + previousTotalTicks = totalTicks; + + return load; +} + +unsigned __int64 fileTimeToUInt64(FILETIME const& ft) +{ + LARGE_INTEGER out; + out.HighPart = ft.dwHighDateTime; + out.LowPart = ft.dwLowDateTime; + return out.QuadPart; +} +#endif + +} // anonymous namespace + +SystemInformationImplementation::SystemInformationImplementation() +{ + this->TotalVirtualMemory = 0; + this->AvailableVirtualMemory = 0; + this->TotalPhysicalMemory = 0; + this->AvailablePhysicalMemory = 0; + this->CurrentPositionInFile = 0; + this->ChipManufacturer = UnknownManufacturer; + memset(&this->Features, 0, sizeof(CPUFeatures)); + this->ChipID.Type = 0; + this->ChipID.Family = 0; + this->ChipID.Model = 0; + this->ChipID.Revision = 0; + this->ChipID.ExtendedFamily = 0; + this->ChipID.ExtendedModel = 0; + this->CPUSpeedInMHz = 0; + this->NumberOfLogicalCPU = 0; + this->NumberOfPhysicalCPU = 0; + this->OSName = ""; + this->Hostname = ""; + this->OSRelease = ""; + this->OSVersion = ""; + this->OSPlatform = ""; + this->OSIs64Bit = (sizeof(void*) == 8); +} + +void SystemInformationImplementation::RunCPUCheck() +{ +#ifdef _WIN32 + // Check to see if this processor supports CPUID. + bool supportsCPUID = DoesCPUSupportCPUID(); + + if (supportsCPUID) { + // Retrieve the CPU details. + RetrieveCPUIdentity(); + this->FindManufacturer(); + RetrieveCPUFeatures(); + } + + // These two may be called without support for the CPUID instruction. + // (But if the instruction is there, they should be called *after* + // the above call to RetrieveCPUIdentity... that's why the two if + // blocks exist with the same "if (supportsCPUID)" logic... + // + if (!RetrieveCPUClockSpeed()) { + RetrieveClassicalCPUClockSpeed(); + } + + if (supportsCPUID) { + // Retrieve cache information. + if (!RetrieveCPUCacheDetails()) { + RetrieveClassicalCPUCacheDetails(); + } + + // Retrieve the extended CPU details. + if (!RetrieveExtendedCPUIdentity()) { + RetrieveClassicalCPUIdentity(); + } + + RetrieveExtendedCPUFeatures(); + RetrieveCPUPowerManagement(); + + // Now attempt to retrieve the serial number (if possible). + RetrieveProcessorSerialNumber(); + } + + this->CPUCountWindows(); + +#elif defined(__APPLE__) + this->ParseSysCtl(); +#elif defined(__SVR4) && defined(__sun) + this->QuerySolarisProcessor(); +#elif defined(__HAIKU__) + this->QueryHaikuInfo(); +#elif defined(__QNX__) + this->QueryQNXProcessor(); +#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + this->QueryBSDProcessor(); +#elif defined(__hpux) + this->QueryHPUXProcessor(); +#elif defined(__linux) || defined(__CYGWIN__) + this->RetreiveInformationFromCpuInfoFile(); +#else + this->QueryProcessor(); +#endif +} + +void SystemInformationImplementation::RunOSCheck() +{ + this->QueryOSInformation(); +} + +void SystemInformationImplementation::RunMemoryCheck() +{ +#if defined(__APPLE__) + this->ParseSysCtl(); +#elif defined(__SVR4) && defined(__sun) + this->QuerySolarisMemory(); +#elif defined(__HAIKU__) + this->QueryHaikuInfo(); +#elif defined(__QNX__) + this->QueryQNXMemory(); +#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + this->QueryBSDMemory(); +#elif defined(__CYGWIN__) + this->QueryCygwinMemory(); +#elif defined(_WIN32) + this->QueryWindowsMemory(); +#elif defined(__hpux) + this->QueryHPUXMemory(); +#elif defined(__linux) + this->QueryLinuxMemory(); +#elif defined(_AIX) + this->QueryAIXMemory(); +#else + this->QueryMemory(); +#endif +} + +/** Get the vendor string */ +const char* SystemInformationImplementation::GetVendorString() const +{ + return this->ChipID.Vendor.c_str(); +} + +/** Get the OS Name */ +const char* SystemInformationImplementation::GetOSName() +{ + return this->OSName.c_str(); +} + +/** Get the hostname */ +const char* SystemInformationImplementation::GetHostname() +{ + if (this->Hostname.empty()) { + this->Hostname = "localhost"; +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + char name[255]; + wVersionRequested = MAKEWORD(2, 0); + if (WSAStartup(wVersionRequested, &wsaData) == 0) { + gethostname(name, sizeof(name)); + WSACleanup(); + } + this->Hostname = name; +#else + struct utsname unameInfo; + int errorFlag = uname(&unameInfo); + if (errorFlag == 0) { + this->Hostname = unameInfo.nodename; + } +#endif + } + return this->Hostname.c_str(); +} + +/** Get the FQDN */ +int SystemInformationImplementation::GetFullyQualifiedDomainName( + std::string& fqdn) +{ + // in the event of absolute failure return localhost. + fqdn = "localhost"; + +#if defined(_WIN32) + int ierr; + // TODO - a more robust implementation for windows, see comments + // in unix implementation. + WSADATA wsaData; + WORD ver = MAKEWORD(2, 0); + ierr = WSAStartup(ver, &wsaData); + if (ierr) { + return -1; + } + + char base[256] = { '\0' }; + ierr = gethostname(base, 256); + if (ierr) { + WSACleanup(); + return -2; + } + fqdn = base; + + HOSTENT* hent = gethostbyname(base); + if (hent) { + fqdn = hent->h_name; + } + + WSACleanup(); + return 0; + +#elif defined(KWSYS_SYSTEMINFORMATION_IMPLEMENT_FQDN) + // gethostname typical returns an alias for loopback interface + // we want the fully qualified domain name. Because there are + // any number of interfaces on this system we look for the + // first of these that contains the name returned by gethostname + // and is longer. failing that we return gethostname and indicate + // with a failure code. Return of a failure code is not necessarily + // an indication of an error. for instance gethostname may return + // the fully qualified domain name, or there may not be one if the + // system lives on a private network such as in the case of a cluster + // node. + + int ierr = 0; + char base[NI_MAXHOST]; + ierr = gethostname(base, NI_MAXHOST); + if (ierr) { + return -1; + } + size_t baseSize = strlen(base); + fqdn = base; + + struct ifaddrs* ifas; + struct ifaddrs* ifa; + ierr = getifaddrs(&ifas); + if (ierr) { + return -2; + } + + for (ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) { + int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1; + // Skip Loopback interfaces + if (((fam == AF_INET) || (fam == AF_INET6)) && + !(ifa->ifa_flags & IFF_LOOPBACK)) { + char host[NI_MAXHOST] = { '\0' }; + + const size_t addrlen = (fam == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)); + + ierr = getnameinfo(ifa->ifa_addr, static_cast<socklen_t>(addrlen), host, + NI_MAXHOST, nullptr, 0, NI_NAMEREQD); + if (ierr) { + // don't report the failure now since we may succeed on another + // interface. If all attempts fail then return the failure code. + ierr = -3; + continue; + } + + std::string candidate = host; + if ((candidate.find(base) != std::string::npos) && + baseSize < candidate.size()) { + // success, stop now. + ierr = 0; + fqdn = candidate; + break; + } + } + } + freeifaddrs(ifas); + + return ierr; +#else + /* TODO: Implement on more platforms. */ + fqdn = this->GetHostname(); + return -1; +#endif +} + +/** Get the OS release */ +const char* SystemInformationImplementation::GetOSRelease() +{ + return this->OSRelease.c_str(); +} + +/** Get the OS version */ +const char* SystemInformationImplementation::GetOSVersion() +{ + return this->OSVersion.c_str(); +} + +/** Get the OS platform */ +const char* SystemInformationImplementation::GetOSPlatform() +{ + return this->OSPlatform.c_str(); +} + +/** Get the vendor ID */ +const char* SystemInformationImplementation::GetVendorID() +{ + // Return the vendor ID. + switch (this->ChipManufacturer) { + case Intel: + return "Intel Corporation"; + case AMD: + return "Advanced Micro Devices"; + case NSC: + return "National Semiconductor"; + case Cyrix: + return "Cyrix Corp., VIA Inc."; + case NexGen: + return "NexGen Inc., Advanced Micro Devices"; + case IDT: + return "IDT\\Centaur, Via Inc."; + case UMC: + return "United Microelectronics Corp."; + case Rise: + return "Rise"; + case Transmeta: + return "Transmeta"; + case Sun: + return "Sun Microelectronics"; + case IBM: + return "IBM"; + case Motorola: + return "Motorola"; + case HP: + return "Hewlett-Packard"; + case Hygon: + return "Chengdu Haiguang IC Design Co., Ltd."; + case UnknownManufacturer: + default: + return "Unknown Manufacturer"; + } +} + +/** Return the type ID of the CPU */ +std::string SystemInformationImplementation::GetTypeID() const +{ + std::ostringstream str; + str << this->ChipID.Type; + return str.str(); +} + +/** Return the family of the CPU present */ +std::string SystemInformationImplementation::GetFamilyID() const +{ + std::ostringstream str; + str << this->ChipID.Family; + return str.str(); +} + +// Return the model of CPU present */ +std::string SystemInformationImplementation::GetModelID() const +{ + std::ostringstream str; + str << this->ChipID.Model; + return str.str(); +} + +// Return the model name of CPU present */ +std::string SystemInformationImplementation::GetModelName() const +{ + return this->ChipID.ModelName; +} + +/** Return the stepping code of the CPU present. */ +std::string SystemInformationImplementation::GetSteppingCode() const +{ + std::ostringstream str; + str << this->ChipID.Revision; + return str.str(); +} + +/** Return the stepping code of the CPU present. */ +const char* SystemInformationImplementation::GetExtendedProcessorName() const +{ + return this->ChipID.ProcessorName.c_str(); +} + +/** Return the serial number of the processor + * in hexadecimal: xxxx-xxxx-xxxx-xxxx-xxxx-xxxx. */ +const char* SystemInformationImplementation::GetProcessorSerialNumber() const +{ + return this->ChipID.SerialNumber.c_str(); +} + +/** Return the logical processors per physical */ +unsigned int SystemInformationImplementation::GetLogicalProcessorsPerPhysical() + const +{ + return this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical; +} + +/** Return the processor clock frequency. */ +float SystemInformationImplementation::GetProcessorClockFrequency() const +{ + return this->CPUSpeedInMHz; +} + +/** Return the APIC ID. */ +int SystemInformationImplementation::GetProcessorAPICID() const +{ + return this->Features.ExtendedFeatures.APIC_ID; +} + +/** Return the L1 cache size. */ +int SystemInformationImplementation::GetProcessorCacheSize() const +{ + return this->Features.L1CacheSize; +} + +/** Return the chosen cache size. */ +int SystemInformationImplementation::GetProcessorCacheXSize( + long int dwCacheID) const +{ + switch (dwCacheID) { + case SystemInformation::CPU_FEATURE_L1CACHE: + return this->Features.L1CacheSize; + case SystemInformation::CPU_FEATURE_L2CACHE: + return this->Features.L2CacheSize; + case SystemInformation::CPU_FEATURE_L3CACHE: + return this->Features.L3CacheSize; + } + return -1; +} + +bool SystemInformationImplementation::DoesCPUSupportFeature( + long int dwFeature) const +{ + bool bHasFeature = false; + + // Check for MMX instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_MMX) != 0) && + this->Features.HasMMX) + bHasFeature = true; + + // Check for MMX+ instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_MMX_PLUS) != 0) && + this->Features.ExtendedFeatures.HasMMXPlus) + bHasFeature = true; + + // Check for SSE FP instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_SSE) != 0) && + this->Features.HasSSE) + bHasFeature = true; + + // Check for SSE FP instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_SSE_FP) != 0) && + this->Features.HasSSEFP) + bHasFeature = true; + + // Check for SSE MMX instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_SSE_MMX) != 0) && + this->Features.ExtendedFeatures.HasSSEMMX) + bHasFeature = true; + + // Check for SSE2 instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_SSE2) != 0) && + this->Features.HasSSE2) + bHasFeature = true; + + // Check for 3DNow! instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_AMD_3DNOW) != 0) && + this->Features.ExtendedFeatures.Has3DNow) + bHasFeature = true; + + // Check for 3DNow+ instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS) != 0) && + this->Features.ExtendedFeatures.Has3DNowPlus) + bHasFeature = true; + + // Check for IA64 instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_IA64) != 0) && + this->Features.HasIA64) + bHasFeature = true; + + // Check for MP capable. + if (((dwFeature & SystemInformation::CPU_FEATURE_MP_CAPABLE) != 0) && + this->Features.ExtendedFeatures.SupportsMP) + bHasFeature = true; + + // Check for a serial number for the processor. + if (((dwFeature & SystemInformation::CPU_FEATURE_SERIALNUMBER) != 0) && + this->Features.HasSerial) + bHasFeature = true; + + // Check for a local APIC in the processor. + if (((dwFeature & SystemInformation::CPU_FEATURE_APIC) != 0) && + this->Features.HasAPIC) + bHasFeature = true; + + // Check for CMOV instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_CMOV) != 0) && + this->Features.HasCMOV) + bHasFeature = true; + + // Check for MTRR instructions. + if (((dwFeature & SystemInformation::CPU_FEATURE_MTRR) != 0) && + this->Features.HasMTRR) + bHasFeature = true; + + // Check for L1 cache size. + if (((dwFeature & SystemInformation::CPU_FEATURE_L1CACHE) != 0) && + (this->Features.L1CacheSize != -1)) + bHasFeature = true; + + // Check for L2 cache size. + if (((dwFeature & SystemInformation::CPU_FEATURE_L2CACHE) != 0) && + (this->Features.L2CacheSize != -1)) + bHasFeature = true; + + // Check for L3 cache size. + if (((dwFeature & SystemInformation::CPU_FEATURE_L3CACHE) != 0) && + (this->Features.L3CacheSize != -1)) + bHasFeature = true; + + // Check for ACPI capability. + if (((dwFeature & SystemInformation::CPU_FEATURE_ACPI) != 0) && + this->Features.HasACPI) + bHasFeature = true; + + // Check for thermal monitor support. + if (((dwFeature & SystemInformation::CPU_FEATURE_THERMALMONITOR) != 0) && + this->Features.HasThermal) + bHasFeature = true; + + // Check for temperature sensing diode support. + if (((dwFeature & SystemInformation::CPU_FEATURE_TEMPSENSEDIODE) != 0) && + this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode) + bHasFeature = true; + + // Check for frequency ID support. + if (((dwFeature & SystemInformation::CPU_FEATURE_FREQUENCYID) != 0) && + this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID) + bHasFeature = true; + + // Check for voltage ID support. + if (((dwFeature & SystemInformation::CPU_FEATURE_VOLTAGEID_FREQUENCY) != + 0) && + this->Features.ExtendedFeatures.PowerManagement.HasVoltageID) + bHasFeature = true; + + // Check for FPU support. + if (((dwFeature & SystemInformation::CPU_FEATURE_FPU) != 0) && + this->Features.HasFPU) + bHasFeature = true; + + return bHasFeature; +} + +void SystemInformationImplementation::Delay(unsigned int uiMS) +{ +#ifdef _WIN32 + LARGE_INTEGER Frequency, StartCounter, EndCounter; + __int64 x; + + // Get the frequency of the high performance counter. + if (!QueryPerformanceFrequency(&Frequency)) + return; + x = Frequency.QuadPart / 1000 * uiMS; + + // Get the starting position of the counter. + QueryPerformanceCounter(&StartCounter); + + do { + // Get the ending position of the counter. + QueryPerformanceCounter(&EndCounter); + } while (EndCounter.QuadPart - StartCounter.QuadPart < x); +#endif + (void)uiMS; +} + +bool SystemInformationImplementation::DoesCPUSupportCPUID() +{ +#if USE_CPUID + int dummy[4] = { 0, 0, 0, 0 }; + +# if USE_ASM_INSTRUCTIONS + return call_cpuid(0, dummy); +# else + call_cpuid(0, dummy); + return dummy[0] || dummy[1] || dummy[2] || dummy[3]; +# endif +#else + // Assume no cpuid instruction. + return false; +#endif +} + +bool SystemInformationImplementation::RetrieveCPUFeatures() +{ +#if USE_CPUID + int cpuinfo[4] = { 0, 0, 0, 0 }; + + if (!call_cpuid(1, cpuinfo)) { + return false; + } + + // Retrieve the features of CPU present. + this->Features.HasFPU = + ((cpuinfo[3] & 0x00000001) != 0); // FPU Present --> Bit 0 + this->Features.HasTSC = + ((cpuinfo[3] & 0x00000010) != 0); // TSC Present --> Bit 4 + this->Features.HasAPIC = + ((cpuinfo[3] & 0x00000200) != 0); // APIC Present --> Bit 9 + this->Features.HasMTRR = + ((cpuinfo[3] & 0x00001000) != 0); // MTRR Present --> Bit 12 + this->Features.HasCMOV = + ((cpuinfo[3] & 0x00008000) != 0); // CMOV Present --> Bit 15 + this->Features.HasSerial = + ((cpuinfo[3] & 0x00040000) != 0); // Serial Present --> Bit 18 + this->Features.HasACPI = + ((cpuinfo[3] & 0x00400000) != 0); // ACPI Capable --> Bit 22 + this->Features.HasMMX = + ((cpuinfo[3] & 0x00800000) != 0); // MMX Present --> Bit 23 + this->Features.HasSSE = + ((cpuinfo[3] & 0x02000000) != 0); // SSE Present --> Bit 25 + this->Features.HasSSE2 = + ((cpuinfo[3] & 0x04000000) != 0); // SSE2 Present --> Bit 26 + this->Features.HasThermal = + ((cpuinfo[3] & 0x20000000) != 0); // Thermal Monitor Present --> Bit 29 + this->Features.HasIA64 = + ((cpuinfo[3] & 0x40000000) != 0); // IA64 Present --> Bit 30 + +# if USE_ASM_INSTRUCTIONS + // Retrieve extended SSE capabilities if SSE is available. + if (this->Features.HasSSE) { + + // Attempt to __try some SSE FP instructions. + __try { + // Perform: orps xmm0, xmm0 + _asm + { + _emit 0x0f + _emit 0x56 + _emit 0xc0 + } + + // SSE FP capable processor. + this->Features.HasSSEFP = true; + } __except (1) { + // bad instruction - processor or OS cannot handle SSE FP. + this->Features.HasSSEFP = false; + } + } else { + // Set the advanced SSE capabilities to not available. + this->Features.HasSSEFP = false; + } +# else + this->Features.HasSSEFP = false; +# endif + + // Retrieve Intel specific extended features. + if (this->ChipManufacturer == Intel) { + bool SupportsSMT = + ((cpuinfo[3] & 0x10000000) != 0); // Intel specific: SMT --> Bit 28 + + if ((SupportsSMT) && (this->Features.HasAPIC)) { + // Retrieve APIC information if there is one present. + this->Features.ExtendedFeatures.APIC_ID = + ((cpuinfo[1] & 0xFF000000) >> 24); + } + } + + return true; + +#else + return false; +#endif +} + +/** Find the manufacturer given the vendor id */ +void SystemInformationImplementation::FindManufacturer( + const std::string& family) +{ + if (this->ChipID.Vendor == "GenuineIntel") + this->ChipManufacturer = Intel; // Intel Corp. + else if (this->ChipID.Vendor == "UMC UMC UMC ") + this->ChipManufacturer = UMC; // United Microelectronics Corp. + else if (this->ChipID.Vendor == "AuthenticAMD") + this->ChipManufacturer = AMD; // Advanced Micro Devices + else if (this->ChipID.Vendor == "AMD ISBETTER") + this->ChipManufacturer = AMD; // Advanced Micro Devices (1994) + else if (this->ChipID.Vendor == "HygonGenuine") + this->ChipManufacturer = Hygon; // Chengdu Haiguang IC Design Co., Ltd. + else if (this->ChipID.Vendor == "CyrixInstead") + this->ChipManufacturer = Cyrix; // Cyrix Corp., VIA Inc. + else if (this->ChipID.Vendor == "NexGenDriven") + this->ChipManufacturer = NexGen; // NexGen Inc. (now AMD) + else if (this->ChipID.Vendor == "CentaurHauls") + this->ChipManufacturer = IDT; // IDT/Centaur (now VIA) + else if (this->ChipID.Vendor == "RiseRiseRise") + this->ChipManufacturer = Rise; // Rise + else if (this->ChipID.Vendor == "GenuineTMx86") + this->ChipManufacturer = Transmeta; // Transmeta + else if (this->ChipID.Vendor == "TransmetaCPU") + this->ChipManufacturer = Transmeta; // Transmeta + else if (this->ChipID.Vendor == "Geode By NSC") + this->ChipManufacturer = NSC; // National Semiconductor + else if (this->ChipID.Vendor == "Sun") + this->ChipManufacturer = Sun; // Sun Microelectronics + else if (this->ChipID.Vendor == "IBM") + this->ChipManufacturer = IBM; // IBM Microelectronics + else if (this->ChipID.Vendor == "Hewlett-Packard") + this->ChipManufacturer = HP; // Hewlett-Packard + else if (this->ChipID.Vendor == "Motorola") + this->ChipManufacturer = Motorola; // Motorola Microelectronics + else if (family.substr(0, 7) == "PA-RISC") + this->ChipManufacturer = HP; // Hewlett-Packard + else + this->ChipManufacturer = UnknownManufacturer; // Unknown manufacturer +} + +/** */ +bool SystemInformationImplementation::RetrieveCPUIdentity() +{ +#if USE_CPUID + int localCPUVendor[4]; + int localCPUSignature[4]; + + if (!call_cpuid(0, localCPUVendor)) { + return false; + } + if (!call_cpuid(1, localCPUSignature)) { + return false; + } + + // Process the returned information. + // ; eax = 0 --> eax: maximum value of CPUID instruction. + // ; ebx: part 1 of 3; CPU signature. + // ; edx: part 2 of 3; CPU signature. + // ; ecx: part 3 of 3; CPU signature. + char vbuf[13]; + memcpy(&(vbuf[0]), &(localCPUVendor[1]), sizeof(int)); + memcpy(&(vbuf[4]), &(localCPUVendor[3]), sizeof(int)); + memcpy(&(vbuf[8]), &(localCPUVendor[2]), sizeof(int)); + vbuf[12] = '\0'; + this->ChipID.Vendor = vbuf; + + // Retrieve the family of CPU present. + // ; eax = 1 --> eax: CPU ID - bits 31..16 - unused, bits 15..12 - type, + // bits 11..8 - family, bits 7..4 - model, bits 3..0 - mask revision + // ; ebx: 31..24 - default APIC ID, 23..16 - logical processor ID, + // 15..8 - CFLUSH chunk size , 7..0 - brand ID + // ; edx: CPU feature flags + this->ChipID.ExtendedFamily = + ((localCPUSignature[0] & 0x0FF00000) >> 20); // Bits 27..20 Used + this->ChipID.ExtendedModel = + ((localCPUSignature[0] & 0x000F0000) >> 16); // Bits 19..16 Used + this->ChipID.Type = + ((localCPUSignature[0] & 0x0000F000) >> 12); // Bits 15..12 Used + this->ChipID.Family = + ((localCPUSignature[0] & 0x00000F00) >> 8); // Bits 11..8 Used + this->ChipID.Model = + ((localCPUSignature[0] & 0x000000F0) >> 4); // Bits 7..4 Used + this->ChipID.Revision = + ((localCPUSignature[0] & 0x0000000F) >> 0); // Bits 3..0 Used + + return true; + +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveCPUCacheDetails() +{ +#if USE_CPUID + int L1Cache[4] = { 0, 0, 0, 0 }; + int L2Cache[4] = { 0, 0, 0, 0 }; + + // Check to see if what we are about to do is supported... + if (RetrieveCPUExtendedLevelSupport(0x80000005)) { + if (!call_cpuid(0x80000005, L1Cache)) { + return false; + } + // Save the L1 data cache size (in KB) from ecx: bits 31..24 as well as + // data cache size from edx: bits 31..24. + this->Features.L1CacheSize = ((L1Cache[2] & 0xFF000000) >> 24); + this->Features.L1CacheSize += ((L1Cache[3] & 0xFF000000) >> 24); + } else { + // Store -1 to indicate the cache could not be queried. + this->Features.L1CacheSize = -1; + } + + // Check to see if what we are about to do is supported... + if (RetrieveCPUExtendedLevelSupport(0x80000006)) { + if (!call_cpuid(0x80000006, L2Cache)) { + return false; + } + // Save the L2 unified cache size (in KB) from ecx: bits 31..16. + this->Features.L2CacheSize = ((L2Cache[2] & 0xFFFF0000) >> 16); + } else { + // Store -1 to indicate the cache could not be queried. + this->Features.L2CacheSize = -1; + } + + // Define L3 as being not present as we cannot test for it. + this->Features.L3CacheSize = -1; + +#endif + + // Return failure if we cannot detect either cache with this method. + return ((this->Features.L1CacheSize == -1) && + (this->Features.L2CacheSize == -1)) + ? false + : true; +} + +/** */ +bool SystemInformationImplementation::RetrieveClassicalCPUCacheDetails() +{ +#if USE_CPUID + int TLBCode = -1, TLBData = -1, L1Code = -1, L1Data = -1, L1Trace = -1, + L2Unified = -1, L3Unified = -1; + int TLBCacheData[4] = { 0, 0, 0, 0 }; + int TLBPassCounter = 0; + int TLBCacheUnit = 0; + + do { + if (!call_cpuid(2, TLBCacheData)) { + return false; + } + + int bob = ((TLBCacheData[0] & 0x00FF0000) >> 16); + (void)bob; + // Process the returned TLB and cache information. + for (int nCounter = 0; nCounter < TLBCACHE_INFO_UNITS; nCounter++) { + // First of all - decide which unit we are dealing with. + switch (nCounter) { + // eax: bits 8..15 : bits 16..23 : bits 24..31 + case 0: + TLBCacheUnit = ((TLBCacheData[0] & 0x0000FF00) >> 8); + break; + case 1: + TLBCacheUnit = ((TLBCacheData[0] & 0x00FF0000) >> 16); + break; + case 2: + TLBCacheUnit = ((TLBCacheData[0] & 0xFF000000) >> 24); + break; + + // ebx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31 + case 3: + TLBCacheUnit = ((TLBCacheData[1] & 0x000000FF) >> 0); + break; + case 4: + TLBCacheUnit = ((TLBCacheData[1] & 0x0000FF00) >> 8); + break; + case 5: + TLBCacheUnit = ((TLBCacheData[1] & 0x00FF0000) >> 16); + break; + case 6: + TLBCacheUnit = ((TLBCacheData[1] & 0xFF000000) >> 24); + break; + + // ecx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31 + case 7: + TLBCacheUnit = ((TLBCacheData[2] & 0x000000FF) >> 0); + break; + case 8: + TLBCacheUnit = ((TLBCacheData[2] & 0x0000FF00) >> 8); + break; + case 9: + TLBCacheUnit = ((TLBCacheData[2] & 0x00FF0000) >> 16); + break; + case 10: + TLBCacheUnit = ((TLBCacheData[2] & 0xFF000000) >> 24); + break; + + // edx: bits 0..7 : bits 8..15 : bits 16..23 : bits 24..31 + case 11: + TLBCacheUnit = ((TLBCacheData[3] & 0x000000FF) >> 0); + break; + case 12: + TLBCacheUnit = ((TLBCacheData[3] & 0x0000FF00) >> 8); + break; + case 13: + TLBCacheUnit = ((TLBCacheData[3] & 0x00FF0000) >> 16); + break; + case 14: + TLBCacheUnit = ((TLBCacheData[3] & 0xFF000000) >> 24); + break; + + // Default case - an error has occurred. + default: + return false; + } + + // Now process the resulting unit to see what it means.... + switch (TLBCacheUnit) { + case 0x00: + break; + case 0x01: + STORE_TLBCACHE_INFO(TLBCode, 4); + break; + case 0x02: + STORE_TLBCACHE_INFO(TLBCode, 4096); + break; + case 0x03: + STORE_TLBCACHE_INFO(TLBData, 4); + break; + case 0x04: + STORE_TLBCACHE_INFO(TLBData, 4096); + break; + case 0x06: + STORE_TLBCACHE_INFO(L1Code, 8); + break; + case 0x08: + STORE_TLBCACHE_INFO(L1Code, 16); + break; + case 0x0a: + STORE_TLBCACHE_INFO(L1Data, 8); + break; + case 0x0c: + STORE_TLBCACHE_INFO(L1Data, 16); + break; + case 0x10: + STORE_TLBCACHE_INFO(L1Data, 16); + break; // <-- FIXME: IA-64 Only + case 0x15: + STORE_TLBCACHE_INFO(L1Code, 16); + break; // <-- FIXME: IA-64 Only + case 0x1a: + STORE_TLBCACHE_INFO(L2Unified, 96); + break; // <-- FIXME: IA-64 Only + case 0x22: + STORE_TLBCACHE_INFO(L3Unified, 512); + break; + case 0x23: + STORE_TLBCACHE_INFO(L3Unified, 1024); + break; + case 0x25: + STORE_TLBCACHE_INFO(L3Unified, 2048); + break; + case 0x29: + STORE_TLBCACHE_INFO(L3Unified, 4096); + break; + case 0x39: + STORE_TLBCACHE_INFO(L2Unified, 128); + break; + case 0x3c: + STORE_TLBCACHE_INFO(L2Unified, 256); + break; + case 0x40: + STORE_TLBCACHE_INFO(L2Unified, 0); + break; // <-- FIXME: No integrated L2 cache (P6 core) or L3 cache (P4 + // core). + case 0x41: + STORE_TLBCACHE_INFO(L2Unified, 128); + break; + case 0x42: + STORE_TLBCACHE_INFO(L2Unified, 256); + break; + case 0x43: + STORE_TLBCACHE_INFO(L2Unified, 512); + break; + case 0x44: + STORE_TLBCACHE_INFO(L2Unified, 1024); + break; + case 0x45: + STORE_TLBCACHE_INFO(L2Unified, 2048); + break; + case 0x50: + STORE_TLBCACHE_INFO(TLBCode, 4096); + break; + case 0x51: + STORE_TLBCACHE_INFO(TLBCode, 4096); + break; + case 0x52: + STORE_TLBCACHE_INFO(TLBCode, 4096); + break; + case 0x5b: + STORE_TLBCACHE_INFO(TLBData, 4096); + break; + case 0x5c: + STORE_TLBCACHE_INFO(TLBData, 4096); + break; + case 0x5d: + STORE_TLBCACHE_INFO(TLBData, 4096); + break; + case 0x66: + STORE_TLBCACHE_INFO(L1Data, 8); + break; + case 0x67: + STORE_TLBCACHE_INFO(L1Data, 16); + break; + case 0x68: + STORE_TLBCACHE_INFO(L1Data, 32); + break; + case 0x70: + STORE_TLBCACHE_INFO(L1Trace, 12); + break; + case 0x71: + STORE_TLBCACHE_INFO(L1Trace, 16); + break; + case 0x72: + STORE_TLBCACHE_INFO(L1Trace, 32); + break; + case 0x77: + STORE_TLBCACHE_INFO(L1Code, 16); + break; // <-- FIXME: IA-64 Only + case 0x79: + STORE_TLBCACHE_INFO(L2Unified, 128); + break; + case 0x7a: + STORE_TLBCACHE_INFO(L2Unified, 256); + break; + case 0x7b: + STORE_TLBCACHE_INFO(L2Unified, 512); + break; + case 0x7c: + STORE_TLBCACHE_INFO(L2Unified, 1024); + break; + case 0x7e: + STORE_TLBCACHE_INFO(L2Unified, 256); + break; + case 0x81: + STORE_TLBCACHE_INFO(L2Unified, 128); + break; + case 0x82: + STORE_TLBCACHE_INFO(L2Unified, 256); + break; + case 0x83: + STORE_TLBCACHE_INFO(L2Unified, 512); + break; + case 0x84: + STORE_TLBCACHE_INFO(L2Unified, 1024); + break; + case 0x85: + STORE_TLBCACHE_INFO(L2Unified, 2048); + break; + case 0x88: + STORE_TLBCACHE_INFO(L3Unified, 2048); + break; // <-- FIXME: IA-64 Only + case 0x89: + STORE_TLBCACHE_INFO(L3Unified, 4096); + break; // <-- FIXME: IA-64 Only + case 0x8a: + STORE_TLBCACHE_INFO(L3Unified, 8192); + break; // <-- FIXME: IA-64 Only + case 0x8d: + STORE_TLBCACHE_INFO(L3Unified, 3096); + break; // <-- FIXME: IA-64 Only + case 0x90: + STORE_TLBCACHE_INFO(TLBCode, 262144); + break; // <-- FIXME: IA-64 Only + case 0x96: + STORE_TLBCACHE_INFO(TLBCode, 262144); + break; // <-- FIXME: IA-64 Only + case 0x9b: + STORE_TLBCACHE_INFO(TLBCode, 262144); + break; // <-- FIXME: IA-64 Only + + // Default case - an error has occurred. + default: + return false; + } + } + + // Increment the TLB pass counter. + TLBPassCounter++; + } while ((TLBCacheData[0] & 0x000000FF) > TLBPassCounter); + + // Ok - we now have the maximum TLB, L1, L2, and L3 sizes... + if ((L1Code == -1) && (L1Data == -1) && (L1Trace == -1)) { + this->Features.L1CacheSize = -1; + } else if ((L1Code == -1) && (L1Data == -1) && (L1Trace != -1)) { + this->Features.L1CacheSize = L1Trace; + } else if ((L1Code != -1) && (L1Data == -1)) { + this->Features.L1CacheSize = L1Code; + } else if ((L1Code == -1) && (L1Data != -1)) { + this->Features.L1CacheSize = L1Data; + } else if ((L1Code != -1) && (L1Data != -1)) { + this->Features.L1CacheSize = L1Code + L1Data; + } else { + this->Features.L1CacheSize = -1; + } + + // Ok - we now have the maximum TLB, L1, L2, and L3 sizes... + if (L2Unified == -1) { + this->Features.L2CacheSize = -1; + } else { + this->Features.L2CacheSize = L2Unified; + } + + // Ok - we now have the maximum TLB, L1, L2, and L3 sizes... + if (L3Unified == -1) { + this->Features.L3CacheSize = -1; + } else { + this->Features.L3CacheSize = L3Unified; + } + + return true; + +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveCPUClockSpeed() +{ + bool retrieved = false; + +#if defined(_WIN32) + unsigned int uiRepetitions = 1; + unsigned int uiMSecPerRepetition = 50; + __int64 i64Total = 0; + __int64 i64Overhead = 0; + + // Check if the TSC implementation works at all + if (this->Features.HasTSC && + GetCyclesDifference(SystemInformationImplementation::Delay, + uiMSecPerRepetition) > 0) { + for (unsigned int nCounter = 0; nCounter < uiRepetitions; nCounter++) { + i64Total += GetCyclesDifference(SystemInformationImplementation::Delay, + uiMSecPerRepetition); + i64Overhead += GetCyclesDifference( + SystemInformationImplementation::DelayOverhead, uiMSecPerRepetition); + } + + // Calculate the MHz speed. + i64Total -= i64Overhead; + i64Total /= uiRepetitions; + i64Total /= uiMSecPerRepetition; + i64Total /= 1000; + + // Save the CPU speed. + this->CPUSpeedInMHz = (float)i64Total; + + retrieved = true; + } + + // If RDTSC is not supported, we fallback to trying to read this value + // from the registry: + if (!retrieved) { + HKEY hKey = nullptr; + LONG err = + RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, + KEY_READ, &hKey); + + if (ERROR_SUCCESS == err) { + DWORD dwType = 0; + DWORD data = 0; + DWORD dwSize = sizeof(DWORD); + + err = + RegQueryValueExW(hKey, L"~MHz", 0, &dwType, (LPBYTE)&data, &dwSize); + + if (ERROR_SUCCESS == err) { + this->CPUSpeedInMHz = (float)data; + retrieved = true; + } + + RegCloseKey(hKey); + hKey = nullptr; + } + } +#endif + + return retrieved; +} + +/** */ +bool SystemInformationImplementation::RetrieveClassicalCPUClockSpeed() +{ +#if USE_ASM_INSTRUCTIONS + LARGE_INTEGER liStart, liEnd, liCountsPerSecond; + double dFrequency, dDifference; + + // Attempt to get a starting tick count. + QueryPerformanceCounter(&liStart); + + __try { + _asm { + mov eax, 0x80000000 + mov ebx, CLASSICAL_CPU_FREQ_LOOP + Timer_Loop: + bsf ecx,eax + dec ebx + jnz Timer_Loop + } + } __except (1) { + return false; + } + + // Attempt to get a starting tick count. + QueryPerformanceCounter(&liEnd); + + // Get the difference... NB: This is in seconds.... + QueryPerformanceFrequency(&liCountsPerSecond); + dDifference = (((double)liEnd.QuadPart - (double)liStart.QuadPart) / + (double)liCountsPerSecond.QuadPart); + + // Calculate the clock speed. + if (this->ChipID.Family == 3) { + // 80386 processors.... Loop time is 115 cycles! + dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 115) / dDifference) / 1000000); + } else if (this->ChipID.Family == 4) { + // 80486 processors.... Loop time is 47 cycles! + dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 47) / dDifference) / 1000000); + } else if (this->ChipID.Family == 5) { + // Pentium processors.... Loop time is 43 cycles! + dFrequency = (((CLASSICAL_CPU_FREQ_LOOP * 43) / dDifference) / 1000000); + } + + // Save the clock speed. + this->Features.CPUSpeed = (int)dFrequency; + + return true; + +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveCPUExtendedLevelSupport( + int CPULevelToCheck) +{ + int cpuinfo[4] = { 0, 0, 0, 0 }; + + // The extended CPUID is supported by various vendors starting with the + // following CPU models: + // + // Manufacturer & Chip Name | Family Model Revision + // + // AMD K6, K6-2 | 5 6 x + // Cyrix GXm, Cyrix III "Joshua" | 5 4 x + // IDT C6-2 | 5 8 x + // VIA Cyrix III | 6 5 x + // Transmeta Crusoe | 5 x x + // Intel Pentium 4 | f x x + // + + // We check to see if a supported processor is present... + if (this->ChipManufacturer == AMD) { + if (this->ChipID.Family < 5) + return false; + if ((this->ChipID.Family == 5) && (this->ChipID.Model < 6)) + return false; + } else if (this->ChipManufacturer == Cyrix) { + if (this->ChipID.Family < 5) + return false; + if ((this->ChipID.Family == 5) && (this->ChipID.Model < 4)) + return false; + if ((this->ChipID.Family == 6) && (this->ChipID.Model < 5)) + return false; + } else if (this->ChipManufacturer == IDT) { + if (this->ChipID.Family < 5) + return false; + if ((this->ChipID.Family == 5) && (this->ChipID.Model < 8)) + return false; + } else if (this->ChipManufacturer == Transmeta) { + if (this->ChipID.Family < 5) + return false; + } else if (this->ChipManufacturer == Intel) { + if (this->ChipID.Family < 0xf) { + return false; + } + } + +#if USE_CPUID + if (!call_cpuid(0x80000000, cpuinfo)) { + return false; + } +#endif + + // Now we have to check the level wanted vs level returned... + int nLevelWanted = (CPULevelToCheck & 0x7FFFFFFF); + int nLevelReturn = (cpuinfo[0] & 0x7FFFFFFF); + + // Check to see if the level provided is supported... + if (nLevelWanted > nLevelReturn) { + return false; + } + + return true; +} + +/** */ +bool SystemInformationImplementation::RetrieveExtendedCPUFeatures() +{ + + // Check that we are not using an Intel processor as it does not support + // this. + if (this->ChipManufacturer == Intel) { + return false; + } + + // Check to see if what we are about to do is supported... + if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000001))) { + return false; + } + +#if USE_CPUID + int localCPUExtendedFeatures[4] = { 0, 0, 0, 0 }; + + if (!call_cpuid(0x80000001, localCPUExtendedFeatures)) { + return false; + } + + // Retrieve the extended features of CPU present. + this->Features.ExtendedFeatures.Has3DNow = + ((localCPUExtendedFeatures[3] & 0x80000000) != + 0); // 3DNow Present --> Bit 31. + this->Features.ExtendedFeatures.Has3DNowPlus = + ((localCPUExtendedFeatures[3] & 0x40000000) != + 0); // 3DNow+ Present -- > Bit 30. + this->Features.ExtendedFeatures.HasSSEMMX = + ((localCPUExtendedFeatures[3] & 0x00400000) != + 0); // SSE MMX Present --> Bit 22. + this->Features.ExtendedFeatures.SupportsMP = + ((localCPUExtendedFeatures[3] & 0x00080000) != + 0); // MP Capable -- > Bit 19. + + // Retrieve AMD specific extended features. + if (this->ChipManufacturer == AMD || this->ChipManufacturer == Hygon) { + this->Features.ExtendedFeatures.HasMMXPlus = + ((localCPUExtendedFeatures[3] & 0x00400000) != + 0); // AMD specific: MMX-SSE --> Bit 22 + } + + // Retrieve Cyrix specific extended features. + if (this->ChipManufacturer == Cyrix) { + this->Features.ExtendedFeatures.HasMMXPlus = + ((localCPUExtendedFeatures[3] & 0x01000000) != + 0); // Cyrix specific: Extended MMX --> Bit 24 + } + + return true; + +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveProcessorSerialNumber() +{ + // Check to see if the processor supports the processor serial number. + if (!this->Features.HasSerial) { + return false; + } + +#if USE_CPUID + int SerialNumber[4]; + + if (!call_cpuid(3, SerialNumber)) { + return false; + } + + // Process the returned information. + // ; eax = 3 --> ebx: top 32 bits are the processor signature bits --> NB: + // Transmeta only ?!? + // ; ecx: middle 32 bits are the processor signature bits + // ; edx: bottom 32 bits are the processor signature bits + char sn[128]; + sprintf(sn, "%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x", + ((SerialNumber[1] & 0xff000000) >> 24), + ((SerialNumber[1] & 0x00ff0000) >> 16), + ((SerialNumber[1] & 0x0000ff00) >> 8), + ((SerialNumber[1] & 0x000000ff) >> 0), + ((SerialNumber[2] & 0xff000000) >> 24), + ((SerialNumber[2] & 0x00ff0000) >> 16), + ((SerialNumber[2] & 0x0000ff00) >> 8), + ((SerialNumber[2] & 0x000000ff) >> 0), + ((SerialNumber[3] & 0xff000000) >> 24), + ((SerialNumber[3] & 0x00ff0000) >> 16), + ((SerialNumber[3] & 0x0000ff00) >> 8), + ((SerialNumber[3] & 0x000000ff) >> 0)); + this->ChipID.SerialNumber = sn; + return true; + +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveCPUPowerManagement() +{ + // Check to see if what we are about to do is supported... + if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000007))) { + this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID = false; + this->Features.ExtendedFeatures.PowerManagement.HasVoltageID = false; + this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode = false; + return false; + } + +#if USE_CPUID + int localCPUPowerManagement[4] = { 0, 0, 0, 0 }; + + if (!call_cpuid(0x80000007, localCPUPowerManagement)) { + return false; + } + + // Check for the power management capabilities of the CPU. + this->Features.ExtendedFeatures.PowerManagement.HasTempSenseDiode = + ((localCPUPowerManagement[3] & 0x00000001) != 0); + this->Features.ExtendedFeatures.PowerManagement.HasFrequencyID = + ((localCPUPowerManagement[3] & 0x00000002) != 0); + this->Features.ExtendedFeatures.PowerManagement.HasVoltageID = + ((localCPUPowerManagement[3] & 0x00000004) != 0); + + return true; + +#else + return false; +#endif +} + +#if USE_CPUID +// Used only in USE_CPUID implementation below. +static void SystemInformationStripLeadingSpace(std::string& str) +{ + // Because some manufacturers have leading white space - we have to + // post-process the name. + std::string::size_type pos = str.find_first_not_of(" "); + if (pos != std::string::npos) { + str = str.substr(pos); + } +} +#endif + +/** */ +bool SystemInformationImplementation::RetrieveExtendedCPUIdentity() +{ + // Check to see if what we are about to do is supported... + if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000002))) + return false; + if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000003))) + return false; + if (!RetrieveCPUExtendedLevelSupport(static_cast<int>(0x80000004))) + return false; + +#if USE_CPUID + int CPUExtendedIdentity[12]; + + if (!call_cpuid(0x80000002, CPUExtendedIdentity)) { + return false; + } + if (!call_cpuid(0x80000003, CPUExtendedIdentity + 4)) { + return false; + } + if (!call_cpuid(0x80000004, CPUExtendedIdentity + 8)) { + return false; + } + + // Process the returned information. + char nbuf[49]; + memcpy(&(nbuf[0]), &(CPUExtendedIdentity[0]), sizeof(int)); + memcpy(&(nbuf[4]), &(CPUExtendedIdentity[1]), sizeof(int)); + memcpy(&(nbuf[8]), &(CPUExtendedIdentity[2]), sizeof(int)); + memcpy(&(nbuf[12]), &(CPUExtendedIdentity[3]), sizeof(int)); + memcpy(&(nbuf[16]), &(CPUExtendedIdentity[4]), sizeof(int)); + memcpy(&(nbuf[20]), &(CPUExtendedIdentity[5]), sizeof(int)); + memcpy(&(nbuf[24]), &(CPUExtendedIdentity[6]), sizeof(int)); + memcpy(&(nbuf[28]), &(CPUExtendedIdentity[7]), sizeof(int)); + memcpy(&(nbuf[32]), &(CPUExtendedIdentity[8]), sizeof(int)); + memcpy(&(nbuf[36]), &(CPUExtendedIdentity[9]), sizeof(int)); + memcpy(&(nbuf[40]), &(CPUExtendedIdentity[10]), sizeof(int)); + memcpy(&(nbuf[44]), &(CPUExtendedIdentity[11]), sizeof(int)); + nbuf[48] = '\0'; + this->ChipID.ProcessorName = nbuf; + this->ChipID.ModelName = nbuf; + + // Because some manufacturers have leading white space - we have to + // post-process the name. + SystemInformationStripLeadingSpace(this->ChipID.ProcessorName); + return true; +#else + return false; +#endif +} + +/** */ +bool SystemInformationImplementation::RetrieveClassicalCPUIdentity() +{ + // Start by decided which manufacturer we are using.... + switch (this->ChipManufacturer) { + case Intel: + // Check the family / model / revision to determine the CPU ID. + switch (this->ChipID.Family) { + case 3: + this->ChipID.ProcessorName = "Newer i80386 family"; + break; + case 4: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "i80486DX-25/33"; + break; + case 1: + this->ChipID.ProcessorName = "i80486DX-50"; + break; + case 2: + this->ChipID.ProcessorName = "i80486SX"; + break; + case 3: + this->ChipID.ProcessorName = "i80486DX2"; + break; + case 4: + this->ChipID.ProcessorName = "i80486SL"; + break; + case 5: + this->ChipID.ProcessorName = "i80486SX2"; + break; + case 7: + this->ChipID.ProcessorName = "i80486DX2 WriteBack"; + break; + case 8: + this->ChipID.ProcessorName = "i80486DX4"; + break; + case 9: + this->ChipID.ProcessorName = "i80486DX4 WriteBack"; + break; + default: + this->ChipID.ProcessorName = "Unknown 80486 family"; + return false; + } + break; + case 5: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "P5 A-Step"; + break; + case 1: + this->ChipID.ProcessorName = "P5"; + break; + case 2: + this->ChipID.ProcessorName = "P54C"; + break; + case 3: + this->ChipID.ProcessorName = "P24T OverDrive"; + break; + case 4: + this->ChipID.ProcessorName = "P55C"; + break; + case 7: + this->ChipID.ProcessorName = "P54C"; + break; + case 8: + this->ChipID.ProcessorName = "P55C (0.25micron)"; + break; + default: + this->ChipID.ProcessorName = "Unknown Pentium family"; + return false; + } + break; + case 6: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "P6 A-Step"; + break; + case 1: + this->ChipID.ProcessorName = "P6"; + break; + case 3: + this->ChipID.ProcessorName = "Pentium II (0.28 micron)"; + break; + case 5: + this->ChipID.ProcessorName = "Pentium II (0.25 micron)"; + break; + case 6: + this->ChipID.ProcessorName = "Pentium II With On-Die L2 Cache"; + break; + case 7: + this->ChipID.ProcessorName = "Pentium III (0.25 micron)"; + break; + case 8: + this->ChipID.ProcessorName = + "Pentium III (0.18 micron) With 256 KB On-Die L2 Cache "; + break; + case 0xa: + this->ChipID.ProcessorName = + "Pentium III (0.18 micron) With 1 Or 2 MB On-Die L2 Cache "; + break; + case 0xb: + this->ChipID.ProcessorName = "Pentium III (0.13 micron) With " + "256 Or 512 KB On-Die L2 Cache "; + break; + case 23: + this->ChipID.ProcessorName = + "Intel(R) Core(TM)2 Duo CPU T9500 @ 2.60GHz"; + break; + default: + this->ChipID.ProcessorName = "Unknown P6 family"; + return false; + } + break; + case 7: + this->ChipID.ProcessorName = "Intel Merced (IA-64)"; + break; + case 0xf: + // Check the extended family bits... + switch (this->ChipID.ExtendedFamily) { + case 0: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "Pentium IV (0.18 micron)"; + break; + case 1: + this->ChipID.ProcessorName = "Pentium IV (0.18 micron)"; + break; + case 2: + this->ChipID.ProcessorName = "Pentium IV (0.13 micron)"; + break; + default: + this->ChipID.ProcessorName = "Unknown Pentium 4 family"; + return false; + } + break; + case 1: + this->ChipID.ProcessorName = "Intel McKinley (IA-64)"; + break; + default: + this->ChipID.ProcessorName = "Pentium"; + } + break; + default: + this->ChipID.ProcessorName = "Unknown Intel family"; + return false; + } + break; + + case AMD: + // Check the family / model / revision to determine the CPU ID. + switch (this->ChipID.Family) { + case 4: + switch (this->ChipID.Model) { + case 3: + this->ChipID.ProcessorName = "80486DX2"; + break; + case 7: + this->ChipID.ProcessorName = "80486DX2 WriteBack"; + break; + case 8: + this->ChipID.ProcessorName = "80486DX4"; + break; + case 9: + this->ChipID.ProcessorName = "80486DX4 WriteBack"; + break; + case 0xe: + this->ChipID.ProcessorName = "5x86"; + break; + case 0xf: + this->ChipID.ProcessorName = "5x86WB"; + break; + default: + this->ChipID.ProcessorName = "Unknown 80486 family"; + return false; + } + break; + case 5: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "SSA5 (PR75, PR90 = PR100)"; + break; + case 1: + this->ChipID.ProcessorName = "5k86 (PR120 = PR133)"; + break; + case 2: + this->ChipID.ProcessorName = "5k86 (PR166)"; + break; + case 3: + this->ChipID.ProcessorName = "5k86 (PR200)"; + break; + case 6: + this->ChipID.ProcessorName = "K6 (0.30 micron)"; + break; + case 7: + this->ChipID.ProcessorName = "K6 (0.25 micron)"; + break; + case 8: + this->ChipID.ProcessorName = "K6-2"; + break; + case 9: + this->ChipID.ProcessorName = "K6-III"; + break; + case 0xd: + this->ChipID.ProcessorName = "K6-2+ or K6-III+ (0.18 micron)"; + break; + default: + this->ChipID.ProcessorName = "Unknown 80586 family"; + return false; + } + break; + case 6: + switch (this->ChipID.Model) { + case 1: + this->ChipID.ProcessorName = "Athlon- (0.25 micron)"; + break; + case 2: + this->ChipID.ProcessorName = "Athlon- (0.18 micron)"; + break; + case 3: + this->ChipID.ProcessorName = "Duron- (SF core)"; + break; + case 4: + this->ChipID.ProcessorName = "Athlon- (Thunderbird core)"; + break; + case 6: + this->ChipID.ProcessorName = "Athlon- (Palomino core)"; + break; + case 7: + this->ChipID.ProcessorName = "Duron- (Morgan core)"; + break; + case 8: + if (this->Features.ExtendedFeatures.SupportsMP) + this->ChipID.ProcessorName = "Athlon - MP (Thoroughbred core)"; + else + this->ChipID.ProcessorName = "Athlon - XP (Thoroughbred core)"; + break; + default: + this->ChipID.ProcessorName = "Unknown K7 family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown AMD family"; + return false; + } + break; + + case Hygon: + this->ChipID.ProcessorName = "Unknown Hygon family"; + return false; + + case Transmeta: + switch (this->ChipID.Family) { + case 5: + switch (this->ChipID.Model) { + case 4: + this->ChipID.ProcessorName = "Crusoe TM3x00 and TM5x00"; + break; + default: + this->ChipID.ProcessorName = "Unknown Crusoe family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown Transmeta family"; + return false; + } + break; + + case Rise: + switch (this->ChipID.Family) { + case 5: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "mP6 (0.25 micron)"; + break; + case 2: + this->ChipID.ProcessorName = "mP6 (0.18 micron)"; + break; + default: + this->ChipID.ProcessorName = "Unknown Rise family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown Rise family"; + return false; + } + break; + + case UMC: + switch (this->ChipID.Family) { + case 4: + switch (this->ChipID.Model) { + case 1: + this->ChipID.ProcessorName = "U5D"; + break; + case 2: + this->ChipID.ProcessorName = "U5S"; + break; + default: + this->ChipID.ProcessorName = "Unknown UMC family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown UMC family"; + return false; + } + break; + + case IDT: + switch (this->ChipID.Family) { + case 5: + switch (this->ChipID.Model) { + case 4: + this->ChipID.ProcessorName = "C6"; + break; + case 8: + this->ChipID.ProcessorName = "C2"; + break; + case 9: + this->ChipID.ProcessorName = "C3"; + break; + default: + this->ChipID.ProcessorName = "Unknown IDT\\Centaur family"; + return false; + } + break; + case 6: + switch (this->ChipID.Model) { + case 6: + this->ChipID.ProcessorName = "VIA Cyrix III - Samuel"; + break; + default: + this->ChipID.ProcessorName = "Unknown IDT\\Centaur family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown IDT\\Centaur family"; + return false; + } + break; + + case Cyrix: + switch (this->ChipID.Family) { + case 4: + switch (this->ChipID.Model) { + case 4: + this->ChipID.ProcessorName = "MediaGX GX = GXm"; + break; + case 9: + this->ChipID.ProcessorName = "5x86"; + break; + default: + this->ChipID.ProcessorName = "Unknown Cx5x86 family"; + return false; + } + break; + case 5: + switch (this->ChipID.Model) { + case 2: + this->ChipID.ProcessorName = "Cx6x86"; + break; + case 4: + this->ChipID.ProcessorName = "MediaGX GXm"; + break; + default: + this->ChipID.ProcessorName = "Unknown Cx6x86 family"; + return false; + } + break; + case 6: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "6x86MX"; + break; + case 5: + this->ChipID.ProcessorName = "Cyrix M2 Core"; + break; + case 6: + this->ChipID.ProcessorName = "WinChip C5A Core"; + break; + case 7: + this->ChipID.ProcessorName = "WinChip C5B\\C5C Core"; + break; + case 8: + this->ChipID.ProcessorName = "WinChip C5C-T Core"; + break; + default: + this->ChipID.ProcessorName = "Unknown 6x86MX\\Cyrix III family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown Cyrix family"; + return false; + } + break; + + case NexGen: + switch (this->ChipID.Family) { + case 5: + switch (this->ChipID.Model) { + case 0: + this->ChipID.ProcessorName = "Nx586 or Nx586FPU"; + break; + default: + this->ChipID.ProcessorName = "Unknown NexGen family"; + return false; + } + break; + default: + this->ChipID.ProcessorName = "Unknown NexGen family"; + return false; + } + break; + + case NSC: + this->ChipID.ProcessorName = "Cx486SLC \\ DLC \\ Cx486S A-Step"; + break; + + case Sun: + case IBM: + case Motorola: + case HP: + case UnknownManufacturer: + default: + this->ChipID.ProcessorName = + "Unknown family"; // We cannot identify the processor. + return false; + } + + return true; +} + +/** Extract a value from the CPUInfo file */ +std::string SystemInformationImplementation::ExtractValueFromCpuInfoFile( + std::string buffer, const char* word, size_t init) +{ + size_t pos = buffer.find(word, init); + if (pos != std::string::npos) { + this->CurrentPositionInFile = pos; + pos = buffer.find(":", pos); + size_t pos2 = buffer.find("\n", pos); + if (pos != std::string::npos && pos2 != std::string::npos) { + // It may happen that the beginning matches, but this is still not the + // requested key. + // An example is looking for "cpu" when "cpu family" comes first. So we + // check that + // we have only spaces from here to pos, otherwise we search again. + for (size_t i = this->CurrentPositionInFile + strlen(word); i < pos; + ++i) { + if (buffer[i] != ' ' && buffer[i] != '\t') { + return this->ExtractValueFromCpuInfoFile(buffer, word, pos2); + } + } + return buffer.substr(pos + 2, pos2 - pos - 2); + } + } + this->CurrentPositionInFile = std::string::npos; + return ""; +} + +/** Query for the cpu status */ +bool SystemInformationImplementation::RetreiveInformationFromCpuInfoFile() +{ + this->NumberOfLogicalCPU = 0; + this->NumberOfPhysicalCPU = 0; + std::string buffer; + + FILE* fd = fopen("/proc/cpuinfo", "r"); + if (!fd) { + std::cout << "Problem opening /proc/cpuinfo" << std::endl; + return false; + } + + size_t fileSize = 0; + while (!feof(fd)) { + buffer += static_cast<char>(fgetc(fd)); + fileSize++; + } + fclose(fd); + buffer.resize(fileSize - 2); + // Number of logical CPUs (combination of multiple processors, multi-core + // and SMT) + size_t pos = buffer.find("processor\t"); + while (pos != std::string::npos) { + this->NumberOfLogicalCPU++; + pos = buffer.find("processor\t", pos + 1); + } + +#ifdef __linux + // Count sockets. + std::set<int> PhysicalIDs; + std::string idc = this->ExtractValueFromCpuInfoFile(buffer, "physical id"); + while (this->CurrentPositionInFile != std::string::npos) { + int id = atoi(idc.c_str()); + PhysicalIDs.insert(id); + idc = this->ExtractValueFromCpuInfoFile(buffer, "physical id", + this->CurrentPositionInFile + 1); + } + uint64_t NumberOfSockets = PhysicalIDs.size(); + NumberOfSockets = std::max(NumberOfSockets, (uint64_t)1); + // Physical ids returned by Linux don't distinguish cores. + // We want to record the total number of cores in this->NumberOfPhysicalCPU + // (checking only the first proc) + std::string Cores = this->ExtractValueFromCpuInfoFile(buffer, "cpu cores"); + auto NumberOfCoresPerSocket = (unsigned int)atoi(Cores.c_str()); + NumberOfCoresPerSocket = std::max(NumberOfCoresPerSocket, 1u); + this->NumberOfPhysicalCPU = + NumberOfCoresPerSocket * (unsigned int)NumberOfSockets; + +#else // __CYGWIN__ + // does not have "physical id" entries, neither "cpu cores" + // this has to be fixed for hyper-threading. + std::string cpucount = + this->ExtractValueFromCpuInfoFile(buffer, "cpu count"); + this->NumberOfPhysicalCPU = this->NumberOfLogicalCPU = + atoi(cpucount.c_str()); +#endif + // gotta have one, and if this is 0 then we get a / by 0n + // better to have a bad answer than a crash + if (this->NumberOfPhysicalCPU <= 0) { + this->NumberOfPhysicalCPU = 1; + } + // LogicalProcessorsPerPhysical>1 => SMT. + this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = + this->NumberOfLogicalCPU / this->NumberOfPhysicalCPU; + + // CPU speed (checking only the first processor) + std::string CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "cpu MHz"); + if (!CPUSpeed.empty()) { + this->CPUSpeedInMHz = static_cast<float>(atof(CPUSpeed.c_str())); + } +#ifdef __linux + else { + // Linux Sparc: CPU speed is in Hz and encoded in hexadecimal + CPUSpeed = this->ExtractValueFromCpuInfoFile(buffer, "Cpu0ClkTck"); + this->CPUSpeedInMHz = + static_cast<float>(strtoull(CPUSpeed.c_str(), nullptr, 16)) / 1000000.0f; + } +#endif + + // Chip family + std::string familyStr = + this->ExtractValueFromCpuInfoFile(buffer, "cpu family"); + if (familyStr.empty()) { + familyStr = this->ExtractValueFromCpuInfoFile(buffer, "CPU architecture"); + } + this->ChipID.Family = atoi(familyStr.c_str()); + + // Chip Vendor + this->ChipID.Vendor = this->ExtractValueFromCpuInfoFile(buffer, "vendor_id"); + this->FindManufacturer(familyStr); + + // second try for setting family + if (this->ChipID.Family == 0 && this->ChipManufacturer == HP) { + if (familyStr == "PA-RISC 1.1a") + this->ChipID.Family = 0x11a; + else if (familyStr == "PA-RISC 2.0") + this->ChipID.Family = 0x200; + // If you really get CMake to work on a machine not belonging to + // any of those families I owe you a dinner if you get it to + // contribute nightly builds regularly. + } + + // Chip Model + this->ChipID.Model = + atoi(this->ExtractValueFromCpuInfoFile(buffer, "model").c_str()); + if (!this->RetrieveClassicalCPUIdentity()) { + // Some platforms (e.g. PA-RISC) tell us their CPU name here. + // Note: x86 does not. + std::string cpuname = this->ExtractValueFromCpuInfoFile(buffer, "cpu"); + if (!cpuname.empty()) { + this->ChipID.ProcessorName = cpuname; + } + } + + // Chip revision + std::string cpurev = this->ExtractValueFromCpuInfoFile(buffer, "stepping"); + if (cpurev.empty()) { + cpurev = this->ExtractValueFromCpuInfoFile(buffer, "CPU revision"); + } + this->ChipID.Revision = atoi(cpurev.c_str()); + + // Chip Model Name + this->ChipID.ModelName = + this->ExtractValueFromCpuInfoFile(buffer, "model name"); + + // L1 Cache size + // Different architectures may show different names for the caches. + // Sum up everything we find. + std::vector<const char*> cachename; + cachename.clear(); + + cachename.push_back("cache size"); // e.g. x86 + cachename.push_back("I-cache"); // e.g. PA-RISC + cachename.push_back("D-cache"); // e.g. PA-RISC + + this->Features.L1CacheSize = 0; + for (auto& index : cachename) { + std::string cacheSize = this->ExtractValueFromCpuInfoFile(buffer, index); + if (!cacheSize.empty()) { + pos = cacheSize.find(" KB"); + if (pos != std::string::npos) { + cacheSize = cacheSize.substr(0, pos); + } + this->Features.L1CacheSize += atoi(cacheSize.c_str()); + } + } + + // processor feature flags (probably x86 specific) + std::string cpuflags = this->ExtractValueFromCpuInfoFile(buffer, "flags"); + if (!cpurev.empty()) { + // now we can match every flags as space + flag + space + cpuflags = " " + cpuflags + " "; + if ((cpuflags.find(" fpu ") != std::string::npos)) { + this->Features.HasFPU = true; + } + if ((cpuflags.find(" tsc ") != std::string::npos)) { + this->Features.HasTSC = true; + } + if ((cpuflags.find(" mmx ") != std::string::npos)) { + this->Features.HasMMX = true; + } + if ((cpuflags.find(" sse ") != std::string::npos)) { + this->Features.HasSSE = true; + } + if ((cpuflags.find(" sse2 ") != std::string::npos)) { + this->Features.HasSSE2 = true; + } + if ((cpuflags.find(" apic ") != std::string::npos)) { + this->Features.HasAPIC = true; + } + if ((cpuflags.find(" cmov ") != std::string::npos)) { + this->Features.HasCMOV = true; + } + if ((cpuflags.find(" mtrr ") != std::string::npos)) { + this->Features.HasMTRR = true; + } + if ((cpuflags.find(" acpi ") != std::string::npos)) { + this->Features.HasACPI = true; + } + if ((cpuflags.find(" 3dnow ") != std::string::npos)) { + this->Features.ExtendedFeatures.Has3DNow = true; + } + } + + return true; +} + +bool SystemInformationImplementation::QueryProcessorBySysconf() +{ +#if defined(_SC_NPROC_ONLN) && !defined(_SC_NPROCESSORS_ONLN) +// IRIX names this slightly different +# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN +#endif + +#ifdef _SC_NPROCESSORS_ONLN + long c = sysconf(_SC_NPROCESSORS_ONLN); + if (c <= 0) { + return false; + } + + this->NumberOfPhysicalCPU = static_cast<unsigned int>(c); + this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU; + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryProcessor() +{ + return this->QueryProcessorBySysconf(); +} + +/** +Get total system RAM in units of KiB. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetHostMemoryTotal() +{ +#if defined(_WIN32) +# if defined(_MSC_VER) && _MSC_VER < 1300 + MEMORYSTATUS stat; + stat.dwLength = sizeof(stat); + GlobalMemoryStatus(&stat); + return stat.dwTotalPhys / 1024; +# else + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + return statex.ullTotalPhys / 1024; +# endif +#elif defined(__linux) + SystemInformation::LongLong memTotal = 0; + int ierr = GetFieldFromFile("/proc/meminfo", "MemTotal:", memTotal); + if (ierr) { + return -1; + } + return memTotal; +#elif defined(__APPLE__) + uint64_t mem; + size_t len = sizeof(mem); + int ierr = sysctlbyname("hw.memsize", &mem, &len, nullptr, 0); + if (ierr) { + return -1; + } + return mem / 1024; +#else + return 0; +#endif +} + +/** +Get total system RAM in units of KiB. This may differ from the +host total if a host-wide resource limit is applied. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetHostMemoryAvailable( + const char* hostLimitEnvVarName) +{ + SystemInformation::LongLong memTotal = this->GetHostMemoryTotal(); + + // the following mechanism is provided for systems that + // apply resource limits across groups of processes. + // this is of use on certain SMP systems (eg. SGI UV) + // where the host has a large amount of ram but a given user's + // access to it is severely restricted. The system will + // apply a limit across a set of processes. Units are in KiB. + if (hostLimitEnvVarName) { + const char* hostLimitEnvVarValue = getenv(hostLimitEnvVarName); + if (hostLimitEnvVarValue) { + SystemInformation::LongLong hostLimit = + atoLongLong(hostLimitEnvVarValue); + if (hostLimit > 0) { + memTotal = min(hostLimit, memTotal); + } + } + } + + return memTotal; +} + +/** +Get total system RAM in units of KiB. This may differ from the +host total if a per-process resource limit is applied. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetProcMemoryAvailable( + const char* hostLimitEnvVarName, const char* procLimitEnvVarName) +{ + SystemInformation::LongLong memAvail = + this->GetHostMemoryAvailable(hostLimitEnvVarName); + + // the following mechanism is provide for systems where rlimits + // are not employed. Units are in KiB. + if (procLimitEnvVarName) { + const char* procLimitEnvVarValue = getenv(procLimitEnvVarName); + if (procLimitEnvVarValue) { + SystemInformation::LongLong procLimit = + atoLongLong(procLimitEnvVarValue); + if (procLimit > 0) { + memAvail = min(procLimit, memAvail); + } + } + } + +#if defined(__linux) + int ierr; + ResourceLimitType rlim; + ierr = GetResourceLimit(RLIMIT_DATA, &rlim); + if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) { + memAvail = + min((SystemInformation::LongLong)rlim.rlim_cur / 1024, memAvail); + } + + ierr = GetResourceLimit(RLIMIT_AS, &rlim); + if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) { + memAvail = + min((SystemInformation::LongLong)rlim.rlim_cur / 1024, memAvail); + } +#elif defined(__APPLE__) + struct rlimit rlim; + int ierr; + ierr = getrlimit(RLIMIT_DATA, &rlim); + if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) { + memAvail = + min((SystemInformation::LongLong)rlim.rlim_cur / 1024, memAvail); + } + + ierr = getrlimit(RLIMIT_RSS, &rlim); + if ((ierr == 0) && (rlim.rlim_cur != RLIM_INFINITY)) { + memAvail = + min((SystemInformation::LongLong)rlim.rlim_cur / 1024, memAvail); + } +#endif + + return memAvail; +} + +/** +Get RAM used by all processes in the host, in units of KiB. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetHostMemoryUsed() +{ +#if defined(_WIN32) +# if defined(_MSC_VER) && _MSC_VER < 1300 + MEMORYSTATUS stat; + stat.dwLength = sizeof(stat); + GlobalMemoryStatus(&stat); + return (stat.dwTotalPhys - stat.dwAvailPhys) / 1024; +# else + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + return (statex.ullTotalPhys - statex.ullAvailPhys) / 1024; +# endif +#elif defined(__linux) + // First try to use MemAvailable, but it only works on newer kernels + const char* names2[3] = { "MemTotal:", "MemAvailable:", nullptr }; + SystemInformation::LongLong values2[2] = { SystemInformation::LongLong(0) }; + int ierr = GetFieldsFromFile("/proc/meminfo", names2, values2); + if (ierr) { + const char* names4[5] = { "MemTotal:", "MemFree:", "Buffers:", "Cached:", + nullptr }; + SystemInformation::LongLong values4[4] = { SystemInformation::LongLong( + 0) }; + ierr = GetFieldsFromFile("/proc/meminfo", names4, values4); + if (ierr) { + return ierr; + } + SystemInformation::LongLong& memTotal = values4[0]; + SystemInformation::LongLong& memFree = values4[1]; + SystemInformation::LongLong& memBuffers = values4[2]; + SystemInformation::LongLong& memCached = values4[3]; + return memTotal - memFree - memBuffers - memCached; + } + SystemInformation::LongLong& memTotal = values2[0]; + SystemInformation::LongLong& memAvail = values2[1]; + return memTotal - memAvail; +#elif defined(__APPLE__) + SystemInformation::LongLong psz = getpagesize(); + if (psz < 1) { + return -1; + } + const char* names[3] = { "Pages wired down:", "Pages active:", nullptr }; + SystemInformation::LongLong values[2] = { SystemInformation::LongLong(0) }; + int ierr = GetFieldsFromCommand("vm_stat", names, values); + if (ierr) { + return -1; + } + SystemInformation::LongLong& vmWired = values[0]; + SystemInformation::LongLong& vmActive = values[1]; + return ((vmActive + vmWired) * psz) / 1024; +#else + return 0; +#endif +} + +/** +Get system RAM used by the process associated with the given +process id in units of KiB. +*/ +SystemInformation::LongLong +SystemInformationImplementation::GetProcMemoryUsed() +{ +#if defined(_WIN32) && defined(KWSYS_SYS_HAS_PSAPI) + long pid = GetCurrentProcessId(); + HANDLE hProc; + hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); + if (hProc == 0) { + return -1; + } + PROCESS_MEMORY_COUNTERS pmc; + int ok = GetProcessMemoryInfo(hProc, &pmc, sizeof(pmc)); + CloseHandle(hProc); + if (!ok) { + return -2; + } + return pmc.WorkingSetSize / 1024; +#elif defined(__linux) + SystemInformation::LongLong memUsed = 0; + int ierr = GetFieldFromFile("/proc/self/status", "VmRSS:", memUsed); + if (ierr) { + return -1; + } + return memUsed; +#elif defined(__APPLE__) + SystemInformation::LongLong memUsed = 0; + pid_t pid = getpid(); + std::ostringstream oss; + oss << "ps -o rss= -p " << pid; + FILE* file = popen(oss.str().c_str(), "r"); + if (file == nullptr) { + return -1; + } + oss.str(""); + while (!feof(file) && !ferror(file)) { + char buf[256] = { '\0' }; + errno = 0; + size_t nRead = fread(buf, 1, 256, file); + if (ferror(file) && (errno == EINTR)) { + clearerr(file); + } + if (nRead) + oss << buf; + } + int ierr = ferror(file); + pclose(file); + if (ierr) { + return -2; + } + std::istringstream iss(oss.str()); + iss >> memUsed; + return memUsed; +#else + return 0; +#endif +} + +double SystemInformationImplementation::GetLoadAverage() +{ +#if defined(KWSYS_CXX_HAS_GETLOADAVG) + double loadavg[3] = { 0.0, 0.0, 0.0 }; + if (getloadavg(loadavg, 3) > 0) { + return loadavg[0]; + } + return -0.0; +#elif defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes) + // Old windows.h headers do not provide GetSystemTimes. + typedef BOOL(WINAPI * GetSystemTimesType)(LPFILETIME, LPFILETIME, + LPFILETIME); + static GetSystemTimesType pGetSystemTimes = + (GetSystemTimesType)GetProcAddress(GetModuleHandleW(L"kernel32"), + "GetSystemTimes"); + FILETIME idleTime, kernelTime, userTime; + if (pGetSystemTimes && pGetSystemTimes(&idleTime, &kernelTime, &userTime)) { + unsigned __int64 const idleTicks = fileTimeToUInt64(idleTime); + unsigned __int64 const totalTicks = + fileTimeToUInt64(kernelTime) + fileTimeToUInt64(userTime); + return calculateCPULoad(idleTicks, totalTicks) * GetNumberOfPhysicalCPU(); + } + return -0.0; +#else + // Not implemented on this platform. + return -0.0; +#endif +} + +/** +Get the process id of the running process. +*/ +SystemInformation::LongLong SystemInformationImplementation::GetProcessId() +{ +#if defined(_WIN32) + return GetCurrentProcessId(); +#elif defined(__linux) || defined(__APPLE__) || defined(__OpenBSD__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) + return getpid(); +#else + return -1; +#endif +} + +/** + * Used in GetProgramStack(...) below + */ +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && defined(_MSC_VER) && \ + _MSC_VER >= 1800 +# define KWSYS_SYSTEMINFORMATION_HAS_DBGHELP +# define TRACE_MAX_STACK_FRAMES 1024 +# define TRACE_MAX_FUNCTION_NAME_LENGTH 1024 +# pragma warning(push) +# pragma warning(disable : 4091) /* 'typedef ': ignored on left of '' */ +# include "dbghelp.h" +# pragma warning(pop) +#endif + +/** +return current program stack in a string +demangle cxx symbols if possible. +*/ +std::string SystemInformationImplementation::GetProgramStack(int firstFrame, + int wholePath) +{ + std::ostringstream oss; + std::string programStack = ""; + +#ifdef KWSYS_SYSTEMINFORMATION_HAS_DBGHELP + (void)wholePath; + + void* stack[TRACE_MAX_STACK_FRAMES]; + HANDLE process = GetCurrentProcess(); + SymInitialize(process, nullptr, TRUE); + WORD numberOfFrames = + CaptureStackBackTrace(firstFrame, TRACE_MAX_STACK_FRAMES, stack, nullptr); + SYMBOL_INFO* symbol = static_cast<SYMBOL_INFO*>( + malloc(sizeof(SYMBOL_INFO) + + (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR))); + symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + DWORD displacement; + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + for (int i = 0; i < numberOfFrames; i++) { + DWORD64 address = reinterpret_cast<DWORD64>(stack[i]); + SymFromAddr(process, address, nullptr, symbol); + if (SymGetLineFromAddr64(process, address, &displacement, &line)) { + oss << " at " << symbol->Name << " in " << line.FileName << " line " + << line.LineNumber << std::endl; + } else { + oss << " at " << symbol->Name << std::endl; + } + } + free(symbol); + +#else + programStack += "" +# if !defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) + "WARNING: The stack could not be examined " + "because backtrace is not supported.\n" +# elif !defined(KWSYS_SYSTEMINFORMATION_HAS_DEBUG_BUILD) + "WARNING: The stack trace will not use advanced " + "capabilities because this is a release build.\n" +# else +# if !defined(KWSYS_SYSTEMINFORMATION_HAS_SYMBOL_LOOKUP) + "WARNING: Function names will not be demangled " + "because dladdr is not available.\n" +# endif +# if !defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE) + "WARNING: Function names will not be demangled " + "because cxxabi is not available.\n" +# endif +# endif + ; + +# if defined(KWSYS_SYSTEMINFORMATION_HAS_BACKTRACE) + void* stackSymbols[256]; + int nFrames = backtrace(stackSymbols, 256); + for (int i = firstFrame; i < nFrames; ++i) { + SymbolProperties symProps; + symProps.SetReportPath(wholePath); + symProps.Initialize(stackSymbols[i]); + oss << symProps << std::endl; + } +# else + (void)firstFrame; + (void)wholePath; +# endif +#endif + + programStack += oss.str(); + + return programStack; +} + +/** +when set print stack trace in response to common signals. +*/ +void SystemInformationImplementation::SetStackTraceOnError(int enable) +{ +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) + static int saOrigValid = 0; + static struct sigaction saABRTOrig; + static struct sigaction saSEGVOrig; + static struct sigaction saTERMOrig; + static struct sigaction saINTOrig; + static struct sigaction saILLOrig; + static struct sigaction saBUSOrig; + static struct sigaction saFPEOrig; + + if (enable && !saOrigValid) { + // save the current actions + sigaction(SIGABRT, nullptr, &saABRTOrig); + sigaction(SIGSEGV, nullptr, &saSEGVOrig); + sigaction(SIGTERM, nullptr, &saTERMOrig); + sigaction(SIGINT, nullptr, &saINTOrig); + sigaction(SIGILL, nullptr, &saILLOrig); + sigaction(SIGBUS, nullptr, &saBUSOrig); + sigaction(SIGFPE, nullptr, &saFPEOrig); + + // enable read, disable write + saOrigValid = 1; + + // install ours + struct sigaction sa; + sa.sa_sigaction = (SigAction)StacktraceSignalHandler; + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; +# ifdef SA_RESTART + sa.sa_flags |= SA_RESTART; +# endif + sigemptyset(&sa.sa_mask); + + sigaction(SIGABRT, &sa, nullptr); + sigaction(SIGSEGV, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGILL, &sa, nullptr); + sigaction(SIGBUS, &sa, nullptr); + sigaction(SIGFPE, &sa, nullptr); + } else if (!enable && saOrigValid) { + // restore previous actions + sigaction(SIGABRT, &saABRTOrig, nullptr); + sigaction(SIGSEGV, &saSEGVOrig, nullptr); + sigaction(SIGTERM, &saTERMOrig, nullptr); + sigaction(SIGINT, &saINTOrig, nullptr); + sigaction(SIGILL, &saILLOrig, nullptr); + sigaction(SIGBUS, &saBUSOrig, nullptr); + sigaction(SIGFPE, &saFPEOrig, nullptr); + + // enable write, disable read + saOrigValid = 0; + } +#else + // avoid warning C4100 + (void)enable; +#endif +} + +bool SystemInformationImplementation::QueryWindowsMemory() +{ +#if defined(_WIN32) +# if defined(_MSC_VER) && _MSC_VER < 1300 + MEMORYSTATUS ms; + unsigned long tv, tp, av, ap; + ms.dwLength = sizeof(ms); + GlobalMemoryStatus(&ms); +# define MEM_VAL(value) dw##value +# else + MEMORYSTATUSEX ms; + DWORDLONG tv, tp, av, ap; + ms.dwLength = sizeof(ms); + if (0 == GlobalMemoryStatusEx(&ms)) { + return 0; + } +# define MEM_VAL(value) ull##value +# endif + tv = ms.MEM_VAL(TotalPageFile); + tp = ms.MEM_VAL(TotalPhys); + av = ms.MEM_VAL(AvailPageFile); + ap = ms.MEM_VAL(AvailPhys); + this->TotalVirtualMemory = tv >> 10 >> 10; + this->TotalPhysicalMemory = tp >> 10 >> 10; + this->AvailableVirtualMemory = av >> 10 >> 10; + this->AvailablePhysicalMemory = ap >> 10 >> 10; + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryLinuxMemory() +{ +#if defined(__linux) + unsigned long tv = 0; + unsigned long tp = 0; + unsigned long av = 0; + unsigned long ap = 0; + + char buffer[1024]; // for reading lines + + int linuxMajor = 0; + int linuxMinor = 0; + + // Find the Linux kernel version first + struct utsname unameInfo; + int errorFlag = uname(&unameInfo); + if (errorFlag != 0) { + std::cout << "Problem calling uname(): " << strerror(errno) << std::endl; + return false; + } + + if (strlen(unameInfo.release) >= 3) { + // release looks like "2.6.3-15mdk-i686-up-4GB" + char majorChar = unameInfo.release[0]; + char minorChar = unameInfo.release[2]; + + if (isdigit(majorChar)) { + linuxMajor = majorChar - '0'; + } + + if (isdigit(minorChar)) { + linuxMinor = minorChar - '0'; + } + } + + FILE* fd = fopen("/proc/meminfo", "r"); + if (!fd) { + std::cout << "Problem opening /proc/meminfo" << std::endl; + return false; + } + + if (linuxMajor >= 3 || ((linuxMajor >= 2) && (linuxMinor >= 6))) { + // new /proc/meminfo format since kernel 2.6.x + // Rigorously, this test should check from the developing version 2.5.x + // that introduced the new format... + + enum + { + mMemTotal, + mMemFree, + mBuffers, + mCached, + mSwapTotal, + mSwapFree + }; + const char* format[6] = { "MemTotal:%lu kB", "MemFree:%lu kB", + "Buffers:%lu kB", "Cached:%lu kB", + "SwapTotal:%lu kB", "SwapFree:%lu kB" }; + bool have[6] = { false, false, false, false, false, false }; + unsigned long value[6]; + int count = 0; + while (fgets(buffer, static_cast<int>(sizeof(buffer)), fd)) { + for (int i = 0; i < 6; ++i) { + if (!have[i] && sscanf(buffer, format[i], &value[i]) == 1) { + have[i] = true; + ++count; + } + } + } + if (count == 6) { + this->TotalPhysicalMemory = value[mMemTotal] / 1024; + this->AvailablePhysicalMemory = + (value[mMemFree] + value[mBuffers] + value[mCached]) / 1024; + this->TotalVirtualMemory = value[mSwapTotal] / 1024; + this->AvailableVirtualMemory = value[mSwapFree] / 1024; + } else { + std::cout << "Problem parsing /proc/meminfo" << std::endl; + fclose(fd); + return false; + } + } else { + // /proc/meminfo format for kernel older than 2.6.x + + unsigned long temp; + unsigned long cachedMem; + unsigned long buffersMem; + // Skip "total: used:..." + char* r = fgets(buffer, static_cast<int>(sizeof(buffer)), fd); + int status = 0; + if (r == buffer) { + status += fscanf(fd, "Mem: %lu %lu %lu %lu %lu %lu\n", &tp, &temp, &ap, + &temp, &buffersMem, &cachedMem); + } + if (status == 6) { + status += fscanf(fd, "Swap: %lu %lu %lu\n", &tv, &temp, &av); + } + if (status == 9) { + this->TotalVirtualMemory = tv >> 10 >> 10; + this->TotalPhysicalMemory = tp >> 10 >> 10; + this->AvailableVirtualMemory = av >> 10 >> 10; + this->AvailablePhysicalMemory = + (ap + buffersMem + cachedMem) >> 10 >> 10; + } else { + std::cout << "Problem parsing /proc/meminfo" << std::endl; + fclose(fd); + return false; + } + } + fclose(fd); + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryCygwinMemory() +{ +#ifdef __CYGWIN__ + // _SC_PAGE_SIZE does return the mmap() granularity on Cygwin, + // see http://cygwin.com/ml/cygwin/2006-06/msg00350.html + // Therefore just use 4096 as the page size of Windows. + long m = sysconf(_SC_PHYS_PAGES); + if (m < 0) { + return false; + } + this->TotalPhysicalMemory = m >> 8; + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryAIXMemory() +{ +#if defined(_AIX) && defined(_SC_AIX_REALMEM) + long c = sysconf(_SC_AIX_REALMEM); + if (c <= 0) { + return false; + } + + this->TotalPhysicalMemory = c / 1024; + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryMemoryBySysconf() +{ +#if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + // Assume the mmap() granularity as returned by _SC_PAGESIZE is also + // the system page size. The only known system where this isn't true + // is Cygwin. + long p = sysconf(_SC_PHYS_PAGES); + long m = sysconf(_SC_PAGESIZE); + + if (p < 0 || m < 0) { + return false; + } + + // assume pagesize is a power of 2 and smaller 1 MiB + size_t pagediv = (1024 * 1024 / m); + + this->TotalPhysicalMemory = p; + this->TotalPhysicalMemory /= pagediv; + +# if defined(_SC_AVPHYS_PAGES) + p = sysconf(_SC_AVPHYS_PAGES); + if (p < 0) { + return false; + } + + this->AvailablePhysicalMemory = p; + this->AvailablePhysicalMemory /= pagediv; +# endif + + return true; +#else + return false; +#endif +} + +/** Query for the memory status */ +bool SystemInformationImplementation::QueryMemory() +{ + return this->QueryMemoryBySysconf(); +} + +/** */ +size_t SystemInformationImplementation::GetTotalVirtualMemory() const +{ + return this->TotalVirtualMemory; +} + +/** */ +size_t SystemInformationImplementation::GetAvailableVirtualMemory() const +{ + return this->AvailableVirtualMemory; +} + +size_t SystemInformationImplementation::GetTotalPhysicalMemory() const +{ + return this->TotalPhysicalMemory; +} + +/** */ +size_t SystemInformationImplementation::GetAvailablePhysicalMemory() const +{ + return this->AvailablePhysicalMemory; +} + +/** Get Cycle differences */ +SystemInformation::LongLong +SystemInformationImplementation::GetCyclesDifference(DELAY_FUNC DelayFunction, + unsigned int uiParameter) +{ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + unsigned __int64 stamp1, stamp2; + + stamp1 = __rdtsc(); + DelayFunction(uiParameter); + stamp2 = __rdtsc(); + + return stamp2 - stamp1; +#elif USE_ASM_INSTRUCTIONS + + unsigned int edx1, eax1; + unsigned int edx2, eax2; + + // Calculate the frequency of the CPU instructions. + __try { + _asm { + push uiParameter ; push parameter param + mov ebx, DelayFunction ; store func in ebx + + RDTSC_INSTRUCTION + + mov esi, eax ; esi = eax + mov edi, edx ; edi = edx + + call ebx ; call the delay functions + + RDTSC_INSTRUCTION + + pop ebx + + mov edx2, edx ; edx2 = edx + mov eax2, eax ; eax2 = eax + + mov edx1, edi ; edx2 = edi + mov eax1, esi ; eax2 = esi + } + } __except (1) { + return -1; + } + + return ((((__int64)edx2 << 32) + eax2) - (((__int64)edx1 << 32) + eax1)); + +#else + (void)DelayFunction; + (void)uiParameter; + return -1; +#endif +} + +/** Compute the delay overhead */ +void SystemInformationImplementation::DelayOverhead(unsigned int uiMS) +{ +#if defined(_WIN32) + LARGE_INTEGER Frequency, StartCounter, EndCounter; + __int64 x; + + // Get the frequency of the high performance counter. + if (!QueryPerformanceFrequency(&Frequency)) { + return; + } + x = Frequency.QuadPart / 1000 * uiMS; + + // Get the starting position of the counter. + QueryPerformanceCounter(&StartCounter); + + do { + // Get the ending position of the counter. + QueryPerformanceCounter(&EndCounter); + } while (EndCounter.QuadPart - StartCounter.QuadPart == x); +#endif + (void)uiMS; +} + +/** Works only for windows */ +bool SystemInformationImplementation::IsSMTSupported() const +{ + return this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical > 1; +} + +/** Return the APIC Id. Works only for windows. */ +unsigned char SystemInformationImplementation::GetAPICId() +{ + int Regs[4] = { 0, 0, 0, 0 }; + +#if USE_CPUID + if (!this->IsSMTSupported()) { + return static_cast<unsigned char>(-1); // HT not supported + } // Logical processor = 1 + call_cpuid(1, Regs); +#endif + + return static_cast<unsigned char>((Regs[1] & INITIAL_APIC_ID_BITS) >> 24); +} + +/** Count the number of CPUs. Works only on windows. */ +void SystemInformationImplementation::CPUCountWindows() +{ +#if defined(_WIN32) + this->NumberOfPhysicalCPU = 0; + this->NumberOfLogicalCPU = 0; + + typedef BOOL(WINAPI * GetLogicalProcessorInformationType)( + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + static GetLogicalProcessorInformationType pGetLogicalProcessorInformation = + (GetLogicalProcessorInformationType)GetProcAddress( + GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation"); + + if (!pGetLogicalProcessorInformation) { + // Fallback to approximate implementation on ancient Windows versions. + SYSTEM_INFO info; + ZeroMemory(&info, sizeof(info)); + GetSystemInfo(&info); + this->NumberOfPhysicalCPU = + static_cast<unsigned int>(info.dwNumberOfProcessors); + this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU; + return; + } + + std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> ProcInfo; + { + DWORD Length = 0; + DWORD rc = pGetLogicalProcessorInformation(nullptr, &Length); + assert(FALSE == rc); + (void)rc; // Silence unused variable warning in Borland C++ 5.81 + assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER); + ProcInfo.resize(Length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + rc = pGetLogicalProcessorInformation(&ProcInfo[0], &Length); + assert(rc != FALSE); + (void)rc; // Silence unused variable warning in Borland C++ 5.81 + } + + typedef std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>::iterator + pinfoIt_t; + for (pinfoIt_t it = ProcInfo.begin(); it != ProcInfo.end(); ++it) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION PInfo = *it; + if (PInfo.Relationship != RelationProcessorCore) { + continue; + } + + std::bitset<std::numeric_limits<ULONG_PTR>::digits> ProcMask( + (unsigned long long)PInfo.ProcessorMask); + unsigned int count = (unsigned int)ProcMask.count(); + if (count == 0) { // I think this should never happen, but just to be safe. + continue; + } + this->NumberOfPhysicalCPU++; + this->NumberOfLogicalCPU += (unsigned int)count; + this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = count; + } + this->NumberOfPhysicalCPU = std::max(1u, this->NumberOfPhysicalCPU); + this->NumberOfLogicalCPU = std::max(1u, this->NumberOfLogicalCPU); +#else +#endif +} + +/** Return the number of logical CPUs on the system */ +unsigned int SystemInformationImplementation::GetNumberOfLogicalCPU() const +{ + return this->NumberOfLogicalCPU; +} + +/** Return the number of physical CPUs on the system */ +unsigned int SystemInformationImplementation::GetNumberOfPhysicalCPU() const +{ + return this->NumberOfPhysicalCPU; +} + +/** For Mac use sysctlbyname calls to find system info */ +bool SystemInformationImplementation::ParseSysCtl() +{ +#if defined(__APPLE__) + char retBuf[128]; + int err = 0; + uint64_t value = 0; + size_t len = sizeof(value); + sysctlbyname("hw.memsize", &value, &len, nullptr, 0); + this->TotalPhysicalMemory = static_cast<size_t>(value / 1048576); + + // Parse values for Mac + this->AvailablePhysicalMemory = 0; + vm_statistics_data_t vmstat; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, + &count) == KERN_SUCCESS) { + len = sizeof(value); + err = sysctlbyname("hw.pagesize", &value, &len, nullptr, 0); + int64_t available_memory = + (vmstat.free_count + vmstat.inactive_count) * value; + this->AvailablePhysicalMemory = + static_cast<size_t>(available_memory / 1048576); + } + +# ifdef VM_SWAPUSAGE + // Virtual memory. + int mib[2] = { CTL_VM, VM_SWAPUSAGE }; + unsigned int miblen = + static_cast<unsigned int>(sizeof(mib) / sizeof(mib[0])); + struct xsw_usage swap; + len = sizeof(swap); + err = sysctl(mib, miblen, &swap, &len, nullptr, 0); + if (err == 0) { + this->AvailableVirtualMemory = + static_cast<size_t>(swap.xsu_avail / 1048576); + this->TotalVirtualMemory = static_cast<size_t>(swap.xsu_total / 1048576); + } +# else + this->AvailableVirtualMemory = 0; + this->TotalVirtualMemory = 0; +# endif + + // CPU Info + len = sizeof(this->NumberOfPhysicalCPU); + sysctlbyname("hw.physicalcpu", &this->NumberOfPhysicalCPU, &len, nullptr, 0); + len = sizeof(this->NumberOfLogicalCPU); + sysctlbyname("hw.logicalcpu", &this->NumberOfLogicalCPU, &len, nullptr, 0); + + int cores_per_package = 0; + len = sizeof(cores_per_package); + err = sysctlbyname("machdep.cpu.cores_per_package", &cores_per_package, &len, + nullptr, 0); + // That name was not found, default to 1 + this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = + err != 0 ? 1 : static_cast<unsigned char>(cores_per_package); + + len = sizeof(value); + sysctlbyname("hw.cpufrequency", &value, &len, nullptr, 0); + this->CPUSpeedInMHz = static_cast<float>(value) / 1000000; + + // Chip family + len = sizeof(this->ChipID.Family); + // Seems only the intel chips will have this name so if this fails it is + // probably a PPC machine + err = + sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, nullptr, 0); + if (err != 0) // Go back to names we know but are less descriptive + { + this->ChipID.Family = 0; + ::memset(retBuf, 0, 128); + len = 32; + err = sysctlbyname("hw.machine", &retBuf, &len, nullptr, 0); + std::string machineBuf(retBuf); + if (machineBuf.find_first_of("Power") != std::string::npos) { + this->ChipID.Vendor = "IBM"; + len = sizeof(this->ChipID.Family); + err = sysctlbyname("hw.cputype", &this->ChipID.Family, &len, nullptr, 0); + len = sizeof(this->ChipID.Model); + err = + sysctlbyname("hw.cpusubtype", &this->ChipID.Model, &len, nullptr, 0); + this->FindManufacturer(); + } + } else // Should be an Intel Chip. + { + len = sizeof(this->ChipID.Family); + err = sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, + nullptr, 0); + + ::memset(retBuf, 0, 128); + len = 128; + err = sysctlbyname("machdep.cpu.vendor", retBuf, &len, nullptr, 0); + // Chip Vendor + this->ChipID.Vendor = retBuf; + this->FindManufacturer(); + + // Chip Model + len = sizeof(value); + err = sysctlbyname("machdep.cpu.model", &value, &len, nullptr, 0); + this->ChipID.Model = static_cast<int>(value); + + // Chip Stepping + len = sizeof(value); + value = 0; + err = sysctlbyname("machdep.cpu.stepping", &value, &len, nullptr, 0); + if (!err) { + this->ChipID.Revision = static_cast<int>(value); + } + + // feature string + char* buf = nullptr; + size_t allocSize = 128; + + err = 0; + len = 0; + + // sysctlbyname() will return with err==0 && len==0 if the buffer is too + // small + while (err == 0 && len == 0) { + delete[] buf; + allocSize *= 2; + buf = new char[allocSize]; + if (!buf) { + break; + } + buf[0] = ' '; + len = allocSize - 2; // keep space for leading and trailing space + err = sysctlbyname("machdep.cpu.features", buf + 1, &len, nullptr, 0); + } + if (!err && buf && len) { + // now we can match every flags as space + flag + space + buf[len + 1] = ' '; + std::string cpuflags(buf, len + 2); + + if ((cpuflags.find(" FPU ") != std::string::npos)) { + this->Features.HasFPU = true; + } + if ((cpuflags.find(" TSC ") != std::string::npos)) { + this->Features.HasTSC = true; + } + if ((cpuflags.find(" MMX ") != std::string::npos)) { + this->Features.HasMMX = true; + } + if ((cpuflags.find(" SSE ") != std::string::npos)) { + this->Features.HasSSE = true; + } + if ((cpuflags.find(" SSE2 ") != std::string::npos)) { + this->Features.HasSSE2 = true; + } + if ((cpuflags.find(" APIC ") != std::string::npos)) { + this->Features.HasAPIC = true; + } + if ((cpuflags.find(" CMOV ") != std::string::npos)) { + this->Features.HasCMOV = true; + } + if ((cpuflags.find(" MTRR ") != std::string::npos)) { + this->Features.HasMTRR = true; + } + if ((cpuflags.find(" ACPI ") != std::string::npos)) { + this->Features.HasACPI = true; + } + } + delete[] buf; + } + + // brand string + ::memset(retBuf, 0, sizeof(retBuf)); + len = sizeof(retBuf); + err = sysctlbyname("machdep.cpu.brand_string", retBuf, &len, nullptr, 0); + if (!err) { + this->ChipID.ProcessorName = retBuf; + this->ChipID.ModelName = retBuf; + } + + // Cache size + len = sizeof(value); + err = sysctlbyname("hw.l1icachesize", &value, &len, nullptr, 0); + this->Features.L1CacheSize = static_cast<int>(value); + len = sizeof(value); + err = sysctlbyname("hw.l2cachesize", &value, &len, nullptr, 0); + this->Features.L2CacheSize = static_cast<int>(value); + + return true; +#else + return false; +#endif +} + +/** Extract a value from sysctl command */ +std::string SystemInformationImplementation::ExtractValueFromSysCtl( + const char* word) +{ + size_t pos = this->SysCtlBuffer.find(word); + if (pos != std::string::npos) { + pos = this->SysCtlBuffer.find(": ", pos); + size_t pos2 = this->SysCtlBuffer.find("\n", pos); + if (pos != std::string::npos && pos2 != std::string::npos) { + return this->SysCtlBuffer.substr(pos + 2, pos2 - pos - 2); + } + } + return ""; +} + +/** Run a given process */ +std::string SystemInformationImplementation::RunProcess( + std::vector<const char*> args) +{ + std::string buffer; + + // Run the application + kwsysProcess* gp = kwsysProcess_New(); + kwsysProcess_SetCommand(gp, args.data()); + kwsysProcess_SetOption(gp, kwsysProcess_Option_HideWindow, 1); + + kwsysProcess_Execute(gp); + + char* data = nullptr; + int length; + double timeout = 255; + int pipe; // pipe id as returned by kwsysProcess_WaitForData() + + while ((static_cast<void>( + pipe = kwsysProcess_WaitForData(gp, &data, &length, &timeout)), + (pipe == kwsysProcess_Pipe_STDOUT || + pipe == kwsysProcess_Pipe_STDERR))) // wait for 1s + { + buffer.append(data, length); + } + kwsysProcess_WaitForExit(gp, nullptr); + + int result = 0; + switch (kwsysProcess_GetState(gp)) { + case kwsysProcess_State_Exited: { + result = kwsysProcess_GetExitValue(gp); + } break; + case kwsysProcess_State_Error: { + std::cerr << "Error: Could not run " << args[0] << ":\n"; + std::cerr << kwsysProcess_GetErrorString(gp) << "\n"; + } break; + case kwsysProcess_State_Exception: { + std::cerr << "Error: " << args[0] << " terminated with an exception: " + << kwsysProcess_GetExceptionString(gp) << "\n"; + } break; + case kwsysProcess_State_Starting: + case kwsysProcess_State_Executing: + case kwsysProcess_State_Expired: + case kwsysProcess_State_Killed: { + // Should not get here. + std::cerr << "Unexpected ending state after running " << args[0] + << std::endl; + } break; + } + kwsysProcess_Delete(gp); + if (result) { + std::cerr << "Error " << args[0] << " returned :" << result << "\n"; + } + return buffer; +} + +std::string SystemInformationImplementation::ParseValueFromKStat( + const char* arguments) +{ + std::vector<std::string> args_string; + std::string command = arguments; + size_t start = std::string::npos; + size_t pos = command.find(' ', 0); + while (pos != std::string::npos) { + bool inQuotes = false; + // Check if we are between quotes + size_t b0 = command.find('"', 0); + size_t b1 = command.find('"', b0 + 1); + while (b0 != std::string::npos && b1 != std::string::npos && b1 > b0) { + if (pos > b0 && pos < b1) { + inQuotes = true; + break; + } + b0 = command.find('"', b1 + 1); + b1 = command.find('"', b0 + 1); + } + + if (!inQuotes) { + args_string.push_back(command.substr(start + 1, pos - start - 1)); + std::string& arg = args_string.back(); + + // Remove the quotes if any + arg.erase(std::remove(arg.begin(), arg.end(), '"'), arg.end()); + start = pos; + } + pos = command.find(' ', pos + 1); + } + args_string.push_back(command.substr(start + 1, command.size() - start - 1)); + + std::vector<const char*> args; + args.reserve(3 + args_string.size()); + args.push_back("kstat"); + args.push_back("-p"); + for (auto& i : args_string) { + args.push_back(i.c_str()); + } + args.push_back(nullptr); + + std::string buffer = this->RunProcess(args); + + std::string value; + for (size_t i = buffer.size() - 1; i > 0; i--) { + if (buffer[i] == ' ' || buffer[i] == '\t') { + break; + } + if (buffer[i] != '\n' && buffer[i] != '\r') { + value.insert(0u, 1, buffer[i]); + } + } + return value; +} + +/** Querying for system information from Solaris */ +bool SystemInformationImplementation::QuerySolarisMemory() +{ +#if defined(__SVR4) && defined(__sun) +// Solaris allows querying this value by sysconf, but if this is +// a 32 bit process on a 64 bit host the returned memory will be +// limited to 4GiB. So if this is a 32 bit process or if the sysconf +// method fails use the kstat interface. +# if SIZEOF_VOID_P == 8 + if (this->QueryMemoryBySysconf()) { + return true; + } +# endif + + char* tail; + unsigned long totalMemory = + strtoul(this->ParseValueFromKStat("-s physmem").c_str(), &tail, 0); + this->TotalPhysicalMemory = totalMemory / 128; + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QuerySolarisProcessor() +{ + if (!this->QueryProcessorBySysconf()) { + return false; + } + + // Parse values + this->CPUSpeedInMHz = static_cast<float>( + atoi(this->ParseValueFromKStat("-s clock_MHz").c_str())); + + // Chip family + this->ChipID.Family = 0; + + // Chip Model + this->ChipID.ProcessorName = this->ParseValueFromKStat("-s cpu_type"); + this->ChipID.Model = 0; + + // Chip Vendor + if (this->ChipID.ProcessorName != "i386") { + this->ChipID.Vendor = "Sun"; + this->FindManufacturer(); + } + + return true; +} + +/** Querying for system information from Haiku OS */ +bool SystemInformationImplementation::QueryHaikuInfo() +{ +#if defined(__HAIKU__) + + // CPU count + system_info info; + get_system_info(&info); + this->NumberOfPhysicalCPU = info.cpu_count; + + // CPU speed + uint32 topologyNodeCount = 0; + cpu_topology_node_info* topology = 0; + get_cpu_topology_info(0, &topologyNodeCount); + if (topologyNodeCount != 0) + topology = new cpu_topology_node_info[topologyNodeCount]; + get_cpu_topology_info(topology, &topologyNodeCount); + + for (uint32 i = 0; i < topologyNodeCount; i++) { + if (topology[i].type == B_TOPOLOGY_CORE) { + this->CPUSpeedInMHz = + topology[i].data.core.default_frequency / 1000000.0f; + break; + } + } + + delete[] topology; + + // Physical Memory + this->TotalPhysicalMemory = (info.max_pages * B_PAGE_SIZE) / (1024 * 1024); + this->AvailablePhysicalMemory = this->TotalPhysicalMemory - + ((info.used_pages * B_PAGE_SIZE) / (1024 * 1024)); + + // NOTE: get_system_info_etc is currently a private call so just set to 0 + // until it becomes public + this->TotalVirtualMemory = 0; + this->AvailableVirtualMemory = 0; + + // Retrieve cpuid_info union for cpu 0 + cpuid_info cpu_info; + get_cpuid(&cpu_info, 0, 0); + + // Chip Vendor + // Use a temporary buffer so that we can add NULL termination to the string + char vbuf[13]; + strncpy(vbuf, cpu_info.eax_0.vendor_id, 12); + vbuf[12] = '\0'; + this->ChipID.Vendor = vbuf; + + this->FindManufacturer(); + + // Retrieve cpuid_info union for cpu 0 this time using a register value of 1 + get_cpuid(&cpu_info, 1, 0); + + this->NumberOfLogicalCPU = cpu_info.eax_1.logical_cpus; + + // Chip type + this->ChipID.Type = cpu_info.eax_1.type; + + // Chip family + this->ChipID.Family = cpu_info.eax_1.family; + + // Chip Model + this->ChipID.Model = cpu_info.eax_1.model; + + // Chip Revision + this->ChipID.Revision = cpu_info.eax_1.stepping; + + // Chip Extended Family + this->ChipID.ExtendedFamily = cpu_info.eax_1.extended_family; + + // Chip Extended Model + this->ChipID.ExtendedModel = cpu_info.eax_1.extended_model; + + // Get ChipID.ProcessorName from other information already gathered + this->RetrieveClassicalCPUIdentity(); + + // Cache size + this->Features.L1CacheSize = 0; + this->Features.L2CacheSize = 0; + + return true; + +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryQNXMemory() +{ +#if defined(__QNX__) + std::string buffer; + std::vector<const char*> args; + args.clear(); + + args.push_back("showmem"); + args.push_back("-S"); + args.push_back(0); + buffer = this->RunProcess(args); + args.clear(); + + size_t pos = buffer.find("System RAM:"); + if (pos == std::string::npos) + return false; + pos = buffer.find(":", pos); + size_t pos2 = buffer.find("M (", pos); + if (pos2 == std::string::npos) + return false; + + pos++; + while (buffer[pos] == ' ') + pos++; + + this->TotalPhysicalMemory = atoi(buffer.substr(pos, pos2 - pos).c_str()); + return true; +#endif + return false; +} + +bool SystemInformationImplementation::QueryBSDMemory() +{ +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + int ctrl[2] = { CTL_HW, HW_PHYSMEM }; +# if defined(HW_PHYSMEM64) + int64_t k; + ctrl[1] = HW_PHYSMEM64; +# else + int k; +# endif + size_t sz = sizeof(k); + + if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) { + return false; + } + + this->TotalPhysicalMemory = k >> 10 >> 10; + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryQNXProcessor() +{ +#if defined(__QNX__) + // the output on my QNX 6.4.1 looks like this: + // Processor1: 686 Pentium II Stepping 3 2175MHz FPU + std::string buffer; + std::vector<const char*> args; + args.clear(); + + args.push_back("pidin"); + args.push_back("info"); + args.push_back(0); + buffer = this->RunProcess(args); + args.clear(); + + size_t pos = buffer.find("Processor1:"); + if (pos == std::string::npos) + return false; + + size_t pos2 = buffer.find("MHz", pos); + if (pos2 == std::string::npos) + return false; + + size_t pos3 = pos2; + while (buffer[pos3] != ' ') + --pos3; + + this->CPUSpeedInMHz = atoi(buffer.substr(pos3 + 1, pos2 - pos3 - 1).c_str()); + + pos2 = buffer.find(" Stepping", pos); + if (pos2 != std::string::npos) { + pos2 = buffer.find(" ", pos2 + 1); + if (pos2 != std::string::npos && pos2 < pos3) { + this->ChipID.Revision = + atoi(buffer.substr(pos2 + 1, pos3 - pos2).c_str()); + } + } + + this->NumberOfPhysicalCPU = 0; + do { + pos = buffer.find("\nProcessor", pos + 1); + ++this->NumberOfPhysicalCPU; + } while (pos != std::string::npos); + this->NumberOfLogicalCPU = 1; + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryBSDProcessor() +{ +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) + int k; + size_t sz = sizeof(k); + int ctrl[2] = { CTL_HW, HW_NCPU }; + + if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) { + return false; + } + + this->NumberOfPhysicalCPU = k; + this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU; + +# if defined(HW_CPUSPEED) + ctrl[1] = HW_CPUSPEED; + + if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) { + return false; + } + + this->CPUSpeedInMHz = (float)k; +# endif + +# if defined(CPU_SSE) + ctrl[0] = CTL_MACHDEP; + ctrl[1] = CPU_SSE; + + if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) { + return false; + } + + this->Features.HasSSE = (k > 0); +# endif + +# if defined(CPU_SSE2) + ctrl[0] = CTL_MACHDEP; + ctrl[1] = CPU_SSE2; + + if (sysctl(ctrl, 2, &k, &sz, nullptr, 0) != 0) { + return false; + } + + this->Features.HasSSE2 = (k > 0); +# endif + +# if defined(CPU_CPUVENDOR) + ctrl[0] = CTL_MACHDEP; + ctrl[1] = CPU_CPUVENDOR; + char vbuf[25]; + ::memset(vbuf, 0, sizeof(vbuf)); + sz = sizeof(vbuf) - 1; + if (sysctl(ctrl, 2, vbuf, &sz, nullptr, 0) != 0) { + return false; + } + + this->ChipID.Vendor = vbuf; + this->FindManufacturer(); +# endif + + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryHPUXMemory() +{ +#if defined(__hpux) + unsigned long tv = 0; + unsigned long tp = 0; + unsigned long av = 0; + unsigned long ap = 0; + struct pst_static pst; + struct pst_dynamic pdy; + + unsigned long ps = 0; + if (pstat_getstatic(&pst, sizeof(pst), (size_t)1, 0) == -1) { + return false; + } + + ps = pst.page_size; + tp = pst.physical_memory * ps; + tv = (pst.physical_memory + pst.pst_maxmem) * ps; + if (pstat_getdynamic(&pdy, sizeof(pdy), (size_t)1, 0) == -1) { + return false; + } + + ap = tp - pdy.psd_rm * ps; + av = tv - pdy.psd_vm; + this->TotalVirtualMemory = tv >> 10 >> 10; + this->TotalPhysicalMemory = tp >> 10 >> 10; + this->AvailableVirtualMemory = av >> 10 >> 10; + this->AvailablePhysicalMemory = ap >> 10 >> 10; + return true; +#else + return false; +#endif +} + +bool SystemInformationImplementation::QueryHPUXProcessor() +{ +#if defined(__hpux) +# if defined(KWSYS_SYS_HAS_MPCTL_H) + int c = mpctl(MPC_GETNUMSPUS_SYS, 0, 0); + if (c <= 0) { + return false; + } + + this->NumberOfPhysicalCPU = c; + this->NumberOfLogicalCPU = this->NumberOfPhysicalCPU; + + long t = sysconf(_SC_CPU_VERSION); + + if (t == -1) { + return false; + } + + switch (t) { + case CPU_PA_RISC1_0: + this->ChipID.Vendor = "Hewlett-Packard"; + this->ChipID.Family = 0x100; + break; + case CPU_PA_RISC1_1: + this->ChipID.Vendor = "Hewlett-Packard"; + this->ChipID.Family = 0x110; + break; + case CPU_PA_RISC2_0: + this->ChipID.Vendor = "Hewlett-Packard"; + this->ChipID.Family = 0x200; + break; +# if defined(CPU_HP_INTEL_EM_1_0) || defined(CPU_IA64_ARCHREV_0) +# ifdef CPU_HP_INTEL_EM_1_0 + case CPU_HP_INTEL_EM_1_0: +# endif +# ifdef CPU_IA64_ARCHREV_0 + case CPU_IA64_ARCHREV_0: +# endif + this->ChipID.Vendor = "GenuineIntel"; + this->Features.HasIA64 = true; + break; +# endif + default: + return false; + } + + this->FindManufacturer(); + + return true; +# else + return false; +# endif +#else + return false; +#endif +} + +/** Query the operating system information */ +bool SystemInformationImplementation::QueryOSInformation() +{ +#if defined(_WIN32) + + this->OSName = "Windows"; + + OSVERSIONINFOEXW osvi; + BOOL bIsWindows64Bit; + BOOL bOsVersionInfoEx; + char operatingSystem[256]; + + // Try calling GetVersionEx using the OSVERSIONINFOEX structure. + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +# endif + bOsVersionInfoEx = GetVersionExW((OSVERSIONINFOW*)&osvi); + if (!bOsVersionInfoEx) { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + if (!GetVersionExW((OSVERSIONINFOW*)&osvi)) { + return false; + } + } +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +# endif + + switch (osvi.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + // Test for the product. + if (osvi.dwMajorVersion <= 4) { + this->OSRelease = "NT"; + } + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { + this->OSRelease = "2000"; + } + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + this->OSRelease = "XP"; + } + // XP Professional x64 + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { + this->OSRelease = "XP"; + } +# ifdef VER_NT_WORKSTATION + // Test for product type. + if (bOsVersionInfoEx) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) { + this->OSRelease = "Vista"; + } + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) { + this->OSRelease = "7"; + } +// VER_SUITE_PERSONAL may not be defined +# ifdef VER_SUITE_PERSONAL + else { + if (osvi.wSuiteMask & VER_SUITE_PERSONAL) { + this->OSRelease += " Personal"; + } else { + this->OSRelease += " Professional"; + } + } +# endif + } else if (osvi.wProductType == VER_NT_SERVER) { + // Check for .NET Server instead of Windows XP. + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + this->OSRelease = ".NET"; + } + + // Continue with the type detection. + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { + this->OSRelease += " DataCenter Server"; + } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { + this->OSRelease += " Advanced Server"; + } else { + this->OSRelease += " Server"; + } + } + + sprintf(operatingSystem, "%ls (Build %ld)", osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF); + this->OSVersion = operatingSystem; + } else +# endif // VER_NT_WORKSTATION + { + HKEY hKey; + wchar_t szProductType[80]; + DWORD dwBufLen; + + // Query the registry to retrieve information. + RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0, + KEY_QUERY_VALUE, &hKey); + RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr, + (LPBYTE)szProductType, &dwBufLen); + RegCloseKey(hKey); + + if (lstrcmpiW(L"WINNT", szProductType) == 0) { + this->OSRelease += " Professional"; + } + if (lstrcmpiW(L"LANMANNT", szProductType) == 0) { + // Decide between Windows 2000 Advanced Server and Windows .NET + // Enterprise Server. + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + this->OSRelease += " Standard Server"; + } else { + this->OSRelease += " Server"; + } + } + if (lstrcmpiW(L"SERVERNT", szProductType) == 0) { + // Decide between Windows 2000 Advanced Server and Windows .NET + // Enterprise Server. + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + this->OSRelease += " Enterprise Server"; + } else { + this->OSRelease += " Advanced Server"; + } + } + } + + // Display version, service pack (if any), and build number. + if (osvi.dwMajorVersion <= 4) { + // NB: NT 4.0 and earlier. + sprintf(operatingSystem, "version %ld.%ld %ls (Build %ld)", + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF); + this->OSVersion = operatingSystem; + } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + // Windows XP and .NET server. + typedef BOOL(CALLBACK * LPFNPROC)(HANDLE, BOOL*); + HINSTANCE hKernelDLL; + LPFNPROC DLLProc; + + // Load the Kernel32 DLL. + hKernelDLL = LoadLibraryW(L"kernel32"); + if (hKernelDLL != nullptr) { + // Only XP and .NET Server support IsWOW64Process so... Load + // dynamically! + DLLProc = (LPFNPROC)GetProcAddress(hKernelDLL, "IsWow64Process"); + + // If the function address is valid, call the function. + if (DLLProc != nullptr) + (DLLProc)(GetCurrentProcess(), &bIsWindows64Bit); + else + bIsWindows64Bit = false; + + // Free the DLL module. + FreeLibrary(hKernelDLL); + } + } else { + // Windows 2000 and everything else. + sprintf(operatingSystem, "%ls (Build %ld)", osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF); + this->OSVersion = operatingSystem; + } + break; + + case VER_PLATFORM_WIN32_WINDOWS: + // Test for the product. + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) { + this->OSRelease = "95"; + if (osvi.szCSDVersion[1] == 'C') { + this->OSRelease += "OSR 2.5"; + } else if (osvi.szCSDVersion[1] == 'B') { + this->OSRelease += "OSR 2"; + } + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) { + this->OSRelease = "98"; + if (osvi.szCSDVersion[1] == 'A') { + this->OSRelease += "SE"; + } + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) { + this->OSRelease = "Me"; + } + break; + + case VER_PLATFORM_WIN32s: + this->OSRelease = "Win32s"; + break; + + default: + this->OSRelease = "Unknown"; + break; + } + + // Get the hostname + WORD wVersionRequested; + WSADATA wsaData; + char name[255]; + wVersionRequested = MAKEWORD(2, 0); + + if (WSAStartup(wVersionRequested, &wsaData) == 0) { + gethostname(name, sizeof(name)); + WSACleanup(); + } + this->Hostname = name; + + const char* arch = getenv("PROCESSOR_ARCHITECTURE"); + const char* wow64 = getenv("PROCESSOR_ARCHITEW6432"); + if (arch) { + this->OSPlatform = arch; + } + + if (wow64) { + // the PROCESSOR_ARCHITEW6432 is only defined when running 32bit programs + // on 64bit OS + this->OSIs64Bit = true; + } else if (arch) { + // all values other than x86 map to 64bit architectures + this->OSIs64Bit = (strncmp(arch, "x86", 3) != 0); + } + +#else + + struct utsname unameInfo; + int errorFlag = uname(&unameInfo); + if (errorFlag == 0) { + this->OSName = unameInfo.sysname; + this->Hostname = unameInfo.nodename; + this->OSRelease = unameInfo.release; + this->OSVersion = unameInfo.version; + this->OSPlatform = unameInfo.machine; + + // This is still insufficient to capture 64bit architecture such + // powerpc and possible mips and sparc + if (this->OSPlatform.find_first_of("64") != std::string::npos) { + this->OSIs64Bit = true; + } + } + +# ifdef __APPLE__ + this->OSName = "Unknown Apple OS"; + this->OSRelease = "Unknown product version"; + this->OSVersion = "Unknown build version"; + + this->CallSwVers("-productName", this->OSName); + this->CallSwVers("-productVersion", this->OSRelease); + this->CallSwVers("-buildVersion", this->OSVersion); +# endif + +#endif + + return true; +} + +int SystemInformationImplementation::CallSwVers(const char* arg, + std::string& ver) +{ +#ifdef __APPLE__ + std::vector<const char*> args; + args.push_back("sw_vers"); + args.push_back(arg); + args.push_back(nullptr); + ver = this->RunProcess(args); + this->TrimNewline(ver); +#else + // avoid C4100 + (void)arg; + (void)ver; +#endif + return 0; +} + +void SystemInformationImplementation::TrimNewline(std::string& output) +{ + // remove \r + std::string::size_type pos = 0; + while ((pos = output.find("\r", pos)) != std::string::npos) { + output.erase(pos); + } + + // remove \n + pos = 0; + while ((pos = output.find("\n", pos)) != std::string::npos) { + output.erase(pos); + } +} + +/** Return true if the machine is 64 bits */ +bool SystemInformationImplementation::Is64Bits() const +{ + return this->OSIs64Bit; +} +} diff --git a/Source/kwsys/SystemInformation.hxx.in b/Source/kwsys/SystemInformation.hxx.in new file mode 100644 index 0000000..fc42e9d --- /dev/null +++ b/Source/kwsys/SystemInformation.hxx.in @@ -0,0 +1,170 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_SystemInformation_h +#define @KWSYS_NAMESPACE@_SystemInformation_h + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <stddef.h> /* size_t */ +#include <string> + +namespace @KWSYS_NAMESPACE@ { + +// forward declare the implementation class +class SystemInformationImplementation; + +class @KWSYS_NAMESPACE@_EXPORT SystemInformation +{ +#if @KWSYS_USE_LONG_LONG@ + typedef long long LongLong; +#elif @KWSYS_USE___INT64@ + typedef __int64 LongLong; +#else +# error "No Long Long" +#endif + friend class SystemInformationImplementation; + SystemInformationImplementation* Implementation; + +public: + // possible parameter values for DoesCPUSupportFeature() + static const long int CPU_FEATURE_MMX = 1 << 0; + static const long int CPU_FEATURE_MMX_PLUS = 1 << 1; + static const long int CPU_FEATURE_SSE = 1 << 2; + static const long int CPU_FEATURE_SSE2 = 1 << 3; + static const long int CPU_FEATURE_AMD_3DNOW = 1 << 4; + static const long int CPU_FEATURE_AMD_3DNOW_PLUS = 1 << 5; + static const long int CPU_FEATURE_IA64 = 1 << 6; + static const long int CPU_FEATURE_MP_CAPABLE = 1 << 7; + static const long int CPU_FEATURE_HYPERTHREAD = 1 << 8; + static const long int CPU_FEATURE_SERIALNUMBER = 1 << 9; + static const long int CPU_FEATURE_APIC = 1 << 10; + static const long int CPU_FEATURE_SSE_FP = 1 << 11; + static const long int CPU_FEATURE_SSE_MMX = 1 << 12; + static const long int CPU_FEATURE_CMOV = 1 << 13; + static const long int CPU_FEATURE_MTRR = 1 << 14; + static const long int CPU_FEATURE_L1CACHE = 1 << 15; + static const long int CPU_FEATURE_L2CACHE = 1 << 16; + static const long int CPU_FEATURE_L3CACHE = 1 << 17; + static const long int CPU_FEATURE_ACPI = 1 << 18; + static const long int CPU_FEATURE_THERMALMONITOR = 1 << 19; + static const long int CPU_FEATURE_TEMPSENSEDIODE = 1 << 20; + static const long int CPU_FEATURE_FREQUENCYID = 1 << 21; + static const long int CPU_FEATURE_VOLTAGEID_FREQUENCY = 1 << 22; + static const long int CPU_FEATURE_FPU = 1 << 23; + +public: + SystemInformation(); + ~SystemInformation(); + + SystemInformation(const SystemInformation&) = delete; + SystemInformation& operator=(const SystemInformation&) = delete; + + const char* GetVendorString(); + const char* GetVendorID(); + std::string GetTypeID(); + std::string GetFamilyID(); + std::string GetModelID(); + std::string GetModelName(); + std::string GetSteppingCode(); + const char* GetExtendedProcessorName(); + const char* GetProcessorSerialNumber(); + int GetProcessorCacheSize(); + unsigned int GetLogicalProcessorsPerPhysical(); + float GetProcessorClockFrequency(); + int GetProcessorAPICID(); + int GetProcessorCacheXSize(long int); + bool DoesCPUSupportFeature(long int); + + // returns an informative general description of the cpu + // on this system. + std::string GetCPUDescription(); + + const char* GetHostname(); + std::string GetFullyQualifiedDomainName(); + + const char* GetOSName(); + const char* GetOSRelease(); + const char* GetOSVersion(); + const char* GetOSPlatform(); + + int GetOSIsWindows(); + int GetOSIsLinux(); + int GetOSIsApple(); + + // returns an informative general description of the os + // on this system. + std::string GetOSDescription(); + + // returns if the operating system is 64bit or not. + bool Is64Bits(); + + unsigned int GetNumberOfLogicalCPU(); + unsigned int GetNumberOfPhysicalCPU(); + + bool DoesCPUSupportCPUID(); + + // Retrieve id of the current running process + LongLong GetProcessId(); + + // Retrieve memory information in MiB. + size_t GetTotalVirtualMemory(); + size_t GetAvailableVirtualMemory(); + size_t GetTotalPhysicalMemory(); + size_t GetAvailablePhysicalMemory(); + + // returns an informative general description if the installed and + // available ram on this system. See the GetHostMemoryTotal, and + // Get{Host,Proc}MemoryAvailable methods for more information. + std::string GetMemoryDescription(const char* hostLimitEnvVarName = nullptr, + const char* procLimitEnvVarName = nullptr); + + // Retrieve amount of physical memory installed on the system in KiB + // units. + LongLong GetHostMemoryTotal(); + + // Get total system RAM in units of KiB available colectivley to all + // processes in a process group. An example of a process group + // are the processes comprising an mpi program which is running in + // parallel. The amount of memory reported may differ from the host + // total if a host wide resource limit is applied. Such reource limits + // are reported to us via an application specified environment variable. + LongLong GetHostMemoryAvailable(const char* hostLimitEnvVarName = nullptr); + + // Get total system RAM in units of KiB available to this process. + // This may differ from the host available if a per-process resource + // limit is applied. per-process memory limits are applied on unix + // system via rlimit API. Resource limits that are not imposed via + // rlimit API may be reported to us via an application specified + // environment variable. + LongLong GetProcMemoryAvailable(const char* hostLimitEnvVarName = nullptr, + const char* procLimitEnvVarName = nullptr); + + // Get the system RAM used by all processes on the host, in units of KiB. + LongLong GetHostMemoryUsed(); + + // Get system RAM used by this process id in units of KiB. + LongLong GetProcMemoryUsed(); + + // Return the load average of the machine or -0.0 if it cannot + // be determined. + double GetLoadAverage(); + + // enable/disable stack trace signal handler. In order to + // produce an informative stack trace the application should + // be dynamically linked and compiled with debug symbols. + static void SetStackTraceOnError(int enable); + + // format and return the current program stack in a string. In + // order to produce an informative stack trace the application + // should be dynamically linked and compiled with debug symbols. + static std::string GetProgramStack(int firstFrame, int wholePath); + + /** Run the different checks */ + void RunCPUCheck(); + void RunOSCheck(); + void RunMemoryCheck(); +}; + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx new file mode 100644 index 0000000..3fa1745 --- /dev/null +++ b/Source/kwsys/SystemTools.cxx @@ -0,0 +1,4727 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef __osf__ +# define _OSF_SOURCE +# define _POSIX_C_SOURCE 199506L +# define _XOPEN_SOURCE_EXTENDED +#endif + +#if defined(_WIN32) && \ + (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) || \ + defined(__MINGW32__)) +# define KWSYS_WINDOWS_DIRS +#else +# if defined(__SUNPRO_CC) +# include <fcntl.h> +# endif +#endif + +#include "kwsysPrivate.h" +#include KWSYS_HEADER(RegularExpression.hxx) +#include KWSYS_HEADER(SystemTools.hxx) +#include KWSYS_HEADER(Directory.hxx) +#include KWSYS_HEADER(FStream.hxx) +#include KWSYS_HEADER(Encoding.h) +#include KWSYS_HEADER(Encoding.hxx) + +#include <fstream> +#include <iostream> +#include <set> +#include <sstream> +#include <utility> +#include <vector> + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Directory.hxx.in" +# include "Encoding.hxx.in" +# include "FStream.hxx.in" +# include "RegularExpression.hxx.in" +# include "SystemTools.hxx.in" +#endif + +#ifdef _MSC_VER +# pragma warning(disable : 4786) +#endif + +#if defined(__sgi) && !defined(__GNUC__) +# pragma set woff 1375 /* base class destructor not virtual */ +#endif + +#include <ctype.h> +#include <errno.h> +#ifdef __QNX__ +# include <malloc.h> /* for malloc/free on QNX */ +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#if defined(_WIN32) && !defined(_MSC_VER) && defined(__GNUC__) +# include <strings.h> /* for strcasecmp */ +#endif + +#ifdef _MSC_VER +# define umask _umask // Note this is still umask on Borland +#endif + +// support for realpath call +#ifndef _WIN32 +# include <limits.h> +# include <pwd.h> +# include <sys/ioctl.h> +# include <sys/time.h> +# include <sys/wait.h> +# include <unistd.h> +# include <utime.h> +# ifndef __VMS +# include <sys/param.h> +# include <termios.h> +# endif +# include <signal.h> /* sigprocmask */ +#endif + +#ifdef __linux +# include <linux/fs.h> +#endif + +// Windows API. +#if defined(_WIN32) +# include <windows.h> +# include <winioctl.h> +# ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +# endif +# if defined(_MSC_VER) && _MSC_VER >= 1800 +# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# endif +#elif defined(__CYGWIN__) +# include <windows.h> +# undef _WIN32 +#endif + +#if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H +extern char** environ; +#endif + +#ifdef __CYGWIN__ +# include <sys/cygwin.h> +#endif + +// getpwnam doesn't exist on Windows and Cray Xt3/Catamount +// same for TIOCGWINSZ +#if defined(_WIN32) || defined(__LIBCATAMOUNT__) || \ + (defined(HAVE_GETPWNAM) && HAVE_GETPWNAM == 0) +# undef HAVE_GETPWNAM +# undef HAVE_TTY_INFO +#else +# define HAVE_GETPWNAM 1 +# define HAVE_TTY_INFO 1 +#endif + +#define VTK_URL_PROTOCOL_REGEX "([a-zA-Z0-9]*)://(.*)" +#define VTK_URL_REGEX \ + "([a-zA-Z0-9]*)://(([A-Za-z0-9]+)(:([^:@]+))?@)?([^:@/]*)(:([0-9]+))?/" \ + "(.+)?" +#define VTK_URL_BYTE_REGEX "%[0-9a-fA-F][0-9a-fA-F]" +#ifdef _MSC_VER +# include <sys/utime.h> +#else +# include <utime.h> +#endif + +// This is a hack to prevent warnings about these functions being +// declared but not referenced. +#if defined(__sgi) && !defined(__GNUC__) +# include <sys/termios.h> +namespace KWSYS_NAMESPACE { +class SystemToolsHack +{ +public: + enum + { + Ref1 = sizeof(cfgetospeed(0)), + Ref2 = sizeof(cfgetispeed(0)), + Ref3 = sizeof(tcgetattr(0, 0)), + Ref4 = sizeof(tcsetattr(0, 0, 0)), + Ref5 = sizeof(cfsetospeed(0, 0)), + Ref6 = sizeof(cfsetispeed(0, 0)) + }; +}; +} +#endif + +#if defined(_WIN32) && \ + (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) || \ + defined(__MINGW32__)) +# include <direct.h> +# include <io.h> +# define _unlink unlink +#endif + +/* The maximum length of a file name. */ +#if defined(PATH_MAX) +# define KWSYS_SYSTEMTOOLS_MAXPATH PATH_MAX +#elif defined(MAXPATHLEN) +# define KWSYS_SYSTEMTOOLS_MAXPATH MAXPATHLEN +#else +# define KWSYS_SYSTEMTOOLS_MAXPATH 16384 +#endif +#if defined(__WATCOMC__) +# include <direct.h> +# define _mkdir mkdir +# define _rmdir rmdir +# define _getcwd getcwd +# define _chdir chdir +#endif + +#if defined(__BEOS__) && !defined(__ZETA__) +# include <be/kernel/OS.h> +# include <be/storage/Path.h> + +// BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. +static inline void usleep(unsigned int msec) +{ + ::snooze(msec); +} + +// BeOS 5 also doesn't have realpath(), but its C++ API offers something close. +static inline char* realpath(const char* path, char* resolved_path) +{ + const size_t maxlen = KWSYS_SYSTEMTOOLS_MAXPATH; + snprintf(resolved_path, maxlen, "%s", path); + BPath normalized(resolved_path, nullptr, true); + const char* resolved = normalized.Path(); + if (resolved != nullptr) // nullptr == No such file. + { + if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) { + return resolved_path; + } + } + return nullptr; // something went wrong. +} +#endif + +#ifdef _WIN32 +static time_t windows_filetime_to_posix_time(const FILETIME& ft) +{ + LARGE_INTEGER date; + date.HighPart = ft.dwHighDateTime; + date.LowPart = ft.dwLowDateTime; + + // removes the diff between 1970 and 1601 + date.QuadPart -= ((LONGLONG)(369 * 365 + 89) * 24 * 3600 * 10000000); + + // converts back from 100-nanoseconds to seconds + return date.QuadPart / 10000000; +} +#endif + +#ifdef KWSYS_WINDOWS_DIRS +# include <wctype.h> + +inline int Mkdir(const std::string& dir) +{ + return _wmkdir( + KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str()); +} +inline int Rmdir(const std::string& dir) +{ + return _wrmdir( + KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str()); +} +inline const char* Getcwd(char* buf, unsigned int len) +{ + std::vector<wchar_t> w_buf(len); + if (_wgetcwd(&w_buf[0], len)) { + size_t nlen = kwsysEncoding_wcstombs(buf, &w_buf[0], len); + if (nlen == static_cast<size_t>(-1)) { + return 0; + } + if (nlen < len) { + // make sure the drive letter is capital + if (nlen > 1 && buf[1] == ':') { + buf[0] = toupper(buf[0]); + } + return buf; + } + } + return 0; +} +inline int Chdir(const std::string& dir) +{ +# if defined(__BORLANDC__) + return chdir(dir.c_str()); +# else + return _wchdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str()); +# endif +} +inline void Realpath(const std::string& path, std::string& resolved_path, + std::string* errorMessage = 0) +{ + std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path); + wchar_t* ptemp; + wchar_t fullpath[MAX_PATH]; + DWORD bufferLen = GetFullPathNameW( + tmp.c_str(), sizeof(fullpath) / sizeof(fullpath[0]), fullpath, &ptemp); + if (bufferLen < sizeof(fullpath) / sizeof(fullpath[0])) { + resolved_path = KWSYS_NAMESPACE::Encoding::ToNarrow(fullpath); + KWSYS_NAMESPACE::SystemTools::ConvertToUnixSlashes(resolved_path); + } else if (errorMessage) { + if (bufferLen) { + *errorMessage = "Destination path buffer size too small."; + } else if (unsigned int errorId = GetLastError()) { + LPSTR message = nullptr; + DWORD size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message, 0, nullptr); + *errorMessage = std::string(message, size); + LocalFree(message); + } else { + *errorMessage = "Unknown error."; + } + + resolved_path = ""; + } else { + resolved_path = path; + } +} +#else +# include <sys/types.h> + +# include <fcntl.h> +# include <unistd.h> +inline int Mkdir(const std::string& dir) +{ + return mkdir(dir.c_str(), 00777); +} +inline int Rmdir(const std::string& dir) +{ + return rmdir(dir.c_str()); +} +inline const char* Getcwd(char* buf, unsigned int len) +{ + return getcwd(buf, len); +} + +inline int Chdir(const std::string& dir) +{ + return chdir(dir.c_str()); +} +inline void Realpath(const std::string& path, std::string& resolved_path, + std::string* errorMessage = nullptr) +{ + char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH]; + + errno = 0; + char* ret = realpath(path.c_str(), resolved_name); + if (ret) { + resolved_path = ret; + } else if (errorMessage) { + if (errno) { + *errorMessage = strerror(errno); + } else { + *errorMessage = "Unknown error."; + } + + resolved_path = ""; + } else { + // if path resolution fails, return what was passed in + resolved_path = path; + } +} +#endif + +#if !defined(_WIN32) && defined(__COMO__) +// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE. +extern "C" { +extern FILE* popen(__const char* __command, __const char* __modes) __THROW; +extern int pclose(FILE* __stream) __THROW; +extern char* realpath(__const char* __restrict __name, + char* __restrict __resolved) __THROW; +extern char* strdup(__const char* __s) __THROW; +extern int putenv(char* __string) __THROW; +} +#endif + +namespace KWSYS_NAMESPACE { + +double SystemTools::GetTime() +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return (429.4967296 * ft.dwHighDateTime + 0.0000001 * ft.dwLowDateTime - + 11644473600.0); +#else + struct timeval t; + gettimeofday(&t, nullptr); + return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec); +#endif +} + +/* Type of character storing the environment. */ +#if defined(_WIN32) +typedef wchar_t envchar; +#else +using envchar = char; +#endif + +/* Order by environment key only (VAR from VAR=VALUE). */ +struct kwsysEnvCompare +{ + bool operator()(const envchar* l, const envchar* r) const + { +#if defined(_WIN32) + const wchar_t* leq = wcschr(l, L'='); + const wchar_t* req = wcschr(r, L'='); + size_t llen = leq ? (leq - l) : wcslen(l); + size_t rlen = req ? (req - r) : wcslen(r); + if (llen == rlen) { + return wcsncmp(l, r, llen) < 0; + } else { + return wcscmp(l, r) < 0; + } +#else + const char* leq = strchr(l, '='); + const char* req = strchr(r, '='); + size_t llen = leq ? static_cast<size_t>(leq - l) : strlen(l); + size_t rlen = req ? static_cast<size_t>(req - r) : strlen(r); + if (llen == rlen) { + return strncmp(l, r, llen) < 0; + } else { + return strcmp(l, r) < 0; + } +#endif + } +}; + +class kwsysEnvSet : public std::set<const envchar*, kwsysEnvCompare> +{ +public: + class Free + { + const envchar* Env; + + public: + Free(const envchar* env) + : Env(env) + { + } + ~Free() { free(const_cast<envchar*>(this->Env)); } + + Free(const Free&) = delete; + Free& operator=(const Free&) = delete; + }; + + const envchar* Release(const envchar* env) + { + const envchar* old = nullptr; + auto i = this->find(env); + if (i != this->end()) { + old = *i; + this->erase(i); + } + return old; + } +}; + +#ifdef _WIN32 +struct SystemToolsPathCaseCmp +{ + bool operator()(std::string const& l, std::string const& r) const + { +# ifdef _MSC_VER + return _stricmp(l.c_str(), r.c_str()) < 0; +# elif defined(__GNUC__) + return strcasecmp(l.c_str(), r.c_str()) < 0; +# else + return SystemTools::Strucmp(l.c_str(), r.c_str()) < 0; +# endif + } +}; +#endif + +/** + * SystemTools static variables singleton class. + */ +class SystemToolsStatic +{ +public: + using StringMap = std::map<std::string, std::string>; +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP + /** + * Path translation table from dir to refdir + * Each time 'dir' will be found it will be replace by 'refdir' + */ + StringMap TranslationMap; +#endif +#ifdef _WIN32 + static std::string GetCasePathName(std::string const& pathIn); + static std::string GetActualCaseForPathCached(std::string const& path); + static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; + std::map<std::string, std::string> EnvMap; +#endif +#ifdef __CYGWIN__ + StringMap Cyg2Win32Map; +#endif + + /** + * Actual implementation of ReplaceString. + */ + static void ReplaceString(std::string& source, const char* replace, + size_t replaceSize, const std::string& with); + + /** + * Actual implementation of FileIsFullPath. + */ + static bool FileIsFullPath(const char*, size_t); + + /** + * Find a filename (file or directory) in the system PATH, with + * optional extra paths. + */ + static std::string FindName( + const std::string& name, + const std::vector<std::string>& userPaths = std::vector<std::string>(), + bool no_system_path = false); +}; + +#ifdef _WIN32 +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) +{ + std::string casePath; + + // First check if the file is relative. We don't fix relative paths since the + // real case depends on the root directory and the given path fragment may + // have meaning elsewhere in the project. + if (!SystemTools::FileIsFullPath(pathIn)) { + // This looks unnecessary, but it allows for the return value optimization + // since all return paths return the same local variable. + casePath = pathIn; + return casePath; + } + + std::vector<std::string> path_components; + SystemTools::SplitPath(pathIn, path_components); + + // Start with root component. + std::vector<std::string>::size_type idx = 0; + casePath = path_components[idx++]; + // make sure drive letter is always upper case + if (casePath.size() > 1 && casePath[1] == ':') { + casePath[0] = toupper(casePath[0]); + } + const char* sep = ""; + + // If network path, fill casePath with server/share so FindFirstFile + // will work after that. Maybe someday call other APIs to get + // actual case of servers and shares. + if (path_components.size() > 2 && path_components[0] == "//") { + casePath += path_components[idx++]; + casePath += "/"; + casePath += path_components[idx++]; + sep = "/"; + } + + // Convert case of all components that exist. + bool converting = true; + for (; idx < path_components.size(); idx++) { + casePath += sep; + sep = "/"; + + if (converting) { + // If path component contains wildcards, we skip matching + // because these filenames are not allowed on windows, + // and we do not want to match a different file. + if (path_components[idx].find('*') != std::string::npos || + path_components[idx].find('?') != std::string::npos) { + converting = false; + } else { + std::string test_str = casePath; + test_str += path_components[idx]; + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + path_components[idx] = Encoding::ToNarrow(findData.cFileName); + ::FindClose(hFind); + } else { + converting = false; + } + } + } + + casePath += path_components[idx]; + } + return casePath; +} + +std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) +{ + // Check to see if actual case has already been called + // for this path, and the result is stored in the PathCaseMap + auto& pcm = SystemTools::Statics->PathCaseMap; + { + auto itr = pcm.find(p); + if (itr != pcm.end()) { + return itr->second; + } + } + std::string casePath = SystemToolsStatic::GetCasePathName(p); + if (casePath.size() <= MAX_PATH) { + pcm[p] = casePath; + } + return casePath; +} +#endif + +// adds the elements of the env variable path to the arg passed in +void SystemTools::GetPath(std::vector<std::string>& path, const char* env) +{ + size_t const old_size = path.size(); +#if defined(_WIN32) && !defined(__CYGWIN__) + const char pathSep = ';'; +#else + const char pathSep = ':'; +#endif + if (!env) { + env = "PATH"; + } + std::string pathEnv; + if (!SystemTools::GetEnv(env, pathEnv)) { + return; + } + + // A hack to make the below algorithm work. + if (!pathEnv.empty() && pathEnv.back() != pathSep) { + pathEnv += pathSep; + } + std::string::size_type start = 0; + bool done = false; + while (!done) { + std::string::size_type endpos = pathEnv.find(pathSep, start); + if (endpos != std::string::npos) { + path.push_back(pathEnv.substr(start, endpos - start)); + start = endpos + 1; + } else { + done = true; + } + } + for (auto i = path.begin() + old_size; i != path.end(); ++i) { + SystemTools::ConvertToUnixSlashes(*i); + } +} + +#if defined(_WIN32) +const char* SystemToolsStatic::GetEnvBuffered(const char* key) +{ + std::string env; + if (SystemTools::GetEnv(key, env)) { + std::string& menv = SystemTools::Statics->EnvMap[key]; + if (menv != env) { + menv = std::move(env); + } + return menv.c_str(); + } + return nullptr; +} +#endif + +const char* SystemTools::GetEnv(const char* key) +{ +#if defined(_WIN32) + return SystemToolsStatic::GetEnvBuffered(key); +#else + return getenv(key); +#endif +} + +const char* SystemTools::GetEnv(const std::string& key) +{ +#if defined(_WIN32) + return SystemToolsStatic::GetEnvBuffered(key.c_str()); +#else + return getenv(key.c_str()); +#endif +} + +bool SystemTools::GetEnv(const char* key, std::string& result) +{ +#if defined(_WIN32) + const std::wstring wkey = Encoding::ToWide(key); + const wchar_t* wv = _wgetenv(wkey.c_str()); + if (wv) { + result = Encoding::ToNarrow(wv); + return true; + } +#else + const char* v = getenv(key); + if (v) { + result = v; + return true; + } +#endif + return false; +} + +bool SystemTools::GetEnv(const std::string& key, std::string& result) +{ + return SystemTools::GetEnv(key.c_str(), result); +} + +bool SystemTools::HasEnv(const char* key) +{ +#if defined(_WIN32) + const std::wstring wkey = Encoding::ToWide(key); + const wchar_t* v = _wgetenv(wkey.c_str()); +#else + const char* v = getenv(key); +#endif + return v != nullptr; +} + +bool SystemTools::HasEnv(const std::string& key) +{ + return SystemTools::HasEnv(key.c_str()); +} + +#if KWSYS_CXX_HAS_UNSETENV +/* unsetenv("A") removes A from the environment. + On older platforms it returns void instead of int. */ +static int kwsysUnPutEnv(const std::string& env) +{ + size_t pos = env.find('='); + if (pos != std::string::npos) { + std::string name = env.substr(0, pos); + unsetenv(name.c_str()); + } else { + unsetenv(env.c_str()); + } + return 0; +} + +#elif defined(__CYGWIN__) || defined(__GLIBC__) +/* putenv("A") removes A from the environment. It must not put the + memory in the environment because it does not have any "=" syntax. */ +static int kwsysUnPutEnv(const std::string& env) +{ + int err = 0; + size_t pos = env.find('='); + size_t const len = pos == std::string::npos ? env.size() : pos; + size_t const sz = len + 1; + char local_buf[256]; + char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf; + if (!buf) { + return -1; + } + strncpy(buf, env.c_str(), len); + buf[len] = 0; + if (putenv(buf) < 0 && errno != EINVAL) { + err = errno; + } + if (buf != local_buf) { + free(buf); + } + if (err) { + errno = err; + return -1; + } + return 0; +} + +#elif defined(_WIN32) +/* putenv("A=") places "A=" in the environment, which is as close to + removal as we can get with the putenv API. We have to leak the + most recent value placed in the environment for each variable name + on program exit in case exit routines access it. */ + +static kwsysEnvSet kwsysUnPutEnvSet; + +static int kwsysUnPutEnv(std::string const& env) +{ + std::wstring wEnv = Encoding::ToWide(env); + size_t const pos = wEnv.find('='); + size_t const len = pos == std::string::npos ? wEnv.size() : pos; + wEnv.resize(len + 1, L'='); + wchar_t* newEnv = _wcsdup(wEnv.c_str()); + if (!newEnv) { + return -1; + } + kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv)); + kwsysUnPutEnvSet.insert(newEnv); + return _wputenv(newEnv); +} + +#else +/* Manipulate the "environ" global directly. */ +static int kwsysUnPutEnv(const std::string& env) +{ + size_t pos = env.find('='); + size_t const len = pos == std::string::npos ? env.size() : pos; + int in = 0; + int out = 0; + while (environ[in]) { + if (strlen(environ[in]) > len && environ[in][len] == '=' && + strncmp(env.c_str(), environ[in], len) == 0) { + ++in; + } else { + environ[out++] = environ[in++]; + } + } + while (out < in) { + environ[out++] = 0; + } + return 0; +} +#endif + +#if KWSYS_CXX_HAS_SETENV + +/* setenv("A", "B", 1) will set A=B in the environment and makes its + own copies of the strings. */ +bool SystemTools::PutEnv(const std::string& env) +{ + size_t pos = env.find('='); + if (pos != std::string::npos) { + std::string name = env.substr(0, pos); + return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0; + } else { + return kwsysUnPutEnv(env) == 0; + } +} + +bool SystemTools::UnPutEnv(const std::string& env) +{ + return kwsysUnPutEnv(env) == 0; +} + +#else + +/* putenv("A=B") will set A=B in the environment. Most putenv implementations + put their argument directly in the environment. They never free the memory + on program exit. Keep an active set of pointers to memory we allocate and + pass to putenv, one per environment key. At program exit remove any + environment values that may still reference memory we allocated. Then free + the memory. This will not affect any environment values we never set. */ + +# ifdef __INTEL_COMPILER +# pragma warning disable 444 /* base has non-virtual destructor */ +# endif + +class kwsysEnv : public kwsysEnvSet +{ +public: + ~kwsysEnv() + { + for (iterator i = this->begin(); i != this->end(); ++i) { +# if defined(_WIN32) + const std::string s = Encoding::ToNarrow(*i); + kwsysUnPutEnv(s); +# else + kwsysUnPutEnv(*i); +# endif + free(const_cast<envchar*>(*i)); + } + } + bool Put(const char* env) + { +# if defined(_WIN32) + const std::wstring wEnv = Encoding::ToWide(env); + wchar_t* newEnv = _wcsdup(wEnv.c_str()); +# else + char* newEnv = strdup(env); +# endif + Free oldEnv(this->Release(newEnv)); + this->insert(newEnv); +# if defined(_WIN32) + return _wputenv(newEnv) == 0; +# else + return putenv(newEnv) == 0; +# endif + } + bool UnPut(const char* env) + { +# if defined(_WIN32) + const std::wstring wEnv = Encoding::ToWide(env); + Free oldEnv(this->Release(wEnv.c_str())); +# else + Free oldEnv(this->Release(env)); +# endif + return kwsysUnPutEnv(env) == 0; + } +}; + +static kwsysEnv kwsysEnvInstance; + +bool SystemTools::PutEnv(const std::string& env) +{ + return kwsysEnvInstance.Put(env.c_str()); +} + +bool SystemTools::UnPutEnv(const std::string& env) +{ + return kwsysEnvInstance.UnPut(env.c_str()); +} + +#endif + +const char* SystemTools::GetExecutableExtension() +{ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS) + return ".exe"; +#else + return ""; +#endif +} + +FILE* SystemTools::Fopen(const std::string& file, const char* mode) +{ +#ifdef _WIN32 + return _wfopen(Encoding::ToWindowsExtendedPath(file).c_str(), + Encoding::ToWide(mode).c_str()); +#else + return fopen(file.c_str(), mode); +#endif +} + +bool SystemTools::MakeDirectory(const char* path, const mode_t* mode) +{ + if (!path) { + return false; + } + return SystemTools::MakeDirectory(std::string(path), mode); +} + +bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode) +{ + if (SystemTools::PathExists(path)) { + return SystemTools::FileIsDirectory(path); + } + if (path.empty()) { + return false; + } + std::string dir = path; + SystemTools::ConvertToUnixSlashes(dir); + + std::string::size_type pos = 0; + std::string topdir; + while ((pos = dir.find('/', pos)) != std::string::npos) { + topdir = dir.substr(0, pos); + + if (Mkdir(topdir) == 0 && mode != nullptr) { + SystemTools::SetPermissions(topdir, *mode); + } + + ++pos; + } + topdir = dir; + if (Mkdir(topdir) != 0) { + // There is a bug in the Borland Run time library which makes MKDIR + // return EACCES when it should return EEXISTS + // if it is some other error besides directory exists + // then return false + if ((errno != EEXIST) +#ifdef __BORLANDC__ + && (errno != EACCES) +#endif + ) { + return false; + } + } else if (mode != nullptr) { + SystemTools::SetPermissions(topdir, *mode); + } + + return true; +} + +// replace replace with with as many times as it shows up in source. +// write the result into source. +void SystemTools::ReplaceString(std::string& source, + const std::string& replace, + const std::string& with) +{ + // do while hangs if replaceSize is 0 + if (replace.empty()) { + return; + } + + SystemToolsStatic::ReplaceString(source, replace.c_str(), replace.size(), + with); +} + +void SystemTools::ReplaceString(std::string& source, const char* replace, + const char* with) +{ + // do while hangs if replaceSize is 0 + if (!*replace) { + return; + } + + SystemToolsStatic::ReplaceString(source, replace, strlen(replace), + with ? with : ""); +} + +void SystemToolsStatic::ReplaceString(std::string& source, const char* replace, + size_t replaceSize, + const std::string& with) +{ + const char* src = source.c_str(); + char* searchPos = const_cast<char*>(strstr(src, replace)); + + // get out quick if string is not found + if (!searchPos) { + return; + } + + // perform replacements until done + char* orig = strdup(src); + char* currentPos = orig; + searchPos = searchPos - src + orig; + + // initialize the result + source.erase(source.begin(), source.end()); + do { + *searchPos = '\0'; + source += currentPos; + currentPos = searchPos + replaceSize; + // replace + source += with; + searchPos = strstr(currentPos, replace); + } while (searchPos); + + // copy any trailing text + source += currentPos; + free(orig); +} + +#if defined(_WIN32) && !defined(__CYGWIN__) + +# if defined(KEY_WOW64_32KEY) && defined(KEY_WOW64_64KEY) +# define KWSYS_ST_KEY_WOW64_32KEY KEY_WOW64_32KEY +# define KWSYS_ST_KEY_WOW64_64KEY KEY_WOW64_64KEY +# else +# define KWSYS_ST_KEY_WOW64_32KEY 0x0200 +# define KWSYS_ST_KEY_WOW64_64KEY 0x0100 +# endif + +static bool SystemToolsParseRegistryKey(const std::string& key, + HKEY& primaryKey, std::string& second, + std::string& valuename) +{ + std::string primary = key; + + size_t start = primary.find('\\'); + if (start == std::string::npos) { + return false; + } + + size_t valuenamepos = primary.find(';'); + if (valuenamepos != std::string::npos) { + valuename = primary.substr(valuenamepos + 1); + } + + second = primary.substr(start + 1, valuenamepos - start - 1); + primary = primary.substr(0, start); + + if (primary == "HKEY_CURRENT_USER") { + primaryKey = HKEY_CURRENT_USER; + } + if (primary == "HKEY_CURRENT_CONFIG") { + primaryKey = HKEY_CURRENT_CONFIG; + } + if (primary == "HKEY_CLASSES_ROOT") { + primaryKey = HKEY_CLASSES_ROOT; + } + if (primary == "HKEY_LOCAL_MACHINE") { + primaryKey = HKEY_LOCAL_MACHINE; + } + if (primary == "HKEY_USERS") { + primaryKey = HKEY_USERS; + } + + return true; +} + +static DWORD SystemToolsMakeRegistryMode(DWORD mode, + SystemTools::KeyWOW64 view) +{ + // only add the modes when on a system that supports Wow64. + static FARPROC wow64p = + GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"); + if (wow64p == nullptr) { + return mode; + } + + if (view == SystemTools::KeyWOW64_32) { + return mode | KWSYS_ST_KEY_WOW64_32KEY; + } else if (view == SystemTools::KeyWOW64_64) { + return mode | KWSYS_ST_KEY_WOW64_64KEY; + } + return mode; +} +#endif + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::GetRegistrySubKeys(const std::string& key, + std::vector<std::string>& subkeys, + KeyWOW64 view) +{ + HKEY primaryKey = HKEY_CURRENT_USER; + std::string second; + std::string valuename; + if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) { + return false; + } + + HKEY hKey; + if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, + SystemToolsMakeRegistryMode(KEY_READ, view), + &hKey) != ERROR_SUCCESS) { + return false; + } else { + wchar_t name[1024]; + DWORD dwNameSize = sizeof(name) / sizeof(name[0]); + + DWORD i = 0; + while (RegEnumKeyW(hKey, i, name, dwNameSize) == ERROR_SUCCESS) { + subkeys.push_back(Encoding::ToNarrow(name)); + ++i; + } + + RegCloseKey(hKey); + } + + return true; +} +#else +bool SystemTools::GetRegistrySubKeys(const std::string&, + std::vector<std::string>&, KeyWOW64) +{ + return false; +} +#endif + +// Read a registry value. +// Example : +// HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath +// => will return the data of the "default" value of the key +// HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root +// => will return the data of the "Root" value of the key + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::ReadRegistryValue(const std::string& key, std::string& value, + KeyWOW64 view) +{ + bool valueset = false; + HKEY primaryKey = HKEY_CURRENT_USER; + std::string second; + std::string valuename; + if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) { + return false; + } + + HKEY hKey; + if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, + SystemToolsMakeRegistryMode(KEY_READ, view), + &hKey) != ERROR_SUCCESS) { + return false; + } else { + DWORD dwType, dwSize; + dwSize = 1023; + wchar_t data[1024]; + if (RegQueryValueExW(hKey, Encoding::ToWide(valuename).c_str(), nullptr, + &dwType, (BYTE*)data, &dwSize) == ERROR_SUCCESS) { + if (dwType == REG_SZ) { + value = Encoding::ToNarrow(data); + valueset = true; + } else if (dwType == REG_EXPAND_SZ) { + wchar_t expanded[1024]; + DWORD dwExpandedSize = sizeof(expanded) / sizeof(expanded[0]); + if (ExpandEnvironmentStringsW(data, expanded, dwExpandedSize)) { + value = Encoding::ToNarrow(expanded); + valueset = true; + } + } + } + + RegCloseKey(hKey); + } + + return valueset; +} +#else +bool SystemTools::ReadRegistryValue(const std::string&, std::string&, KeyWOW64) +{ + return false; +} +#endif + +// Write a registry value. +// Example : +// HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath +// => will set the data of the "default" value of the key +// HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root +// => will set the data of the "Root" value of the key + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::WriteRegistryValue(const std::string& key, + const std::string& value, KeyWOW64 view) +{ + HKEY primaryKey = HKEY_CURRENT_USER; + std::string second; + std::string valuename; + if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) { + return false; + } + + HKEY hKey; + DWORD dwDummy; + wchar_t lpClass[] = L""; + if (RegCreateKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, lpClass, + REG_OPTION_NON_VOLATILE, + SystemToolsMakeRegistryMode(KEY_WRITE, view), nullptr, + &hKey, &dwDummy) != ERROR_SUCCESS) { + return false; + } + + std::wstring wvalue = Encoding::ToWide(value); + if (RegSetValueExW(hKey, Encoding::ToWide(valuename).c_str(), 0, REG_SZ, + (CONST BYTE*)wvalue.c_str(), + (DWORD)(sizeof(wchar_t) * (wvalue.size() + 1))) == + ERROR_SUCCESS) { + return true; + } + return false; +} +#else +bool SystemTools::WriteRegistryValue(const std::string&, const std::string&, + KeyWOW64) +{ + return false; +} +#endif + +// Delete a registry value. +// Example : +// HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath +// => will delete the data of the "default" value of the key +// HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root +// => will delete the data of the "Root" value of the key + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::DeleteRegistryValue(const std::string& key, KeyWOW64 view) +{ + HKEY primaryKey = HKEY_CURRENT_USER; + std::string second; + std::string valuename; + if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) { + return false; + } + + HKEY hKey; + if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, + SystemToolsMakeRegistryMode(KEY_WRITE, view), + &hKey) != ERROR_SUCCESS) { + return false; + } else { + if (RegDeleteValue(hKey, (LPTSTR)valuename.c_str()) == ERROR_SUCCESS) { + RegCloseKey(hKey); + return true; + } + } + return false; +} +#else +bool SystemTools::DeleteRegistryValue(const std::string&, KeyWOW64) +{ + return false; +} +#endif + +bool SystemTools::SameFile(const std::string& file1, const std::string& file2) +{ +#ifdef _WIN32 + HANDLE hFile1, hFile2; + + hFile1 = + CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + hFile2 = + CreateFileW(Encoding::ToWide(file2).c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile1 == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE) { + if (hFile1 != INVALID_HANDLE_VALUE) { + CloseHandle(hFile1); + } + if (hFile2 != INVALID_HANDLE_VALUE) { + CloseHandle(hFile2); + } + return false; + } + + BY_HANDLE_FILE_INFORMATION fiBuf1; + BY_HANDLE_FILE_INFORMATION fiBuf2; + GetFileInformationByHandle(hFile1, &fiBuf1); + GetFileInformationByHandle(hFile2, &fiBuf2); + CloseHandle(hFile1); + CloseHandle(hFile2); + return (fiBuf1.dwVolumeSerialNumber == fiBuf2.dwVolumeSerialNumber && + fiBuf1.nFileIndexHigh == fiBuf2.nFileIndexHigh && + fiBuf1.nFileIndexLow == fiBuf2.nFileIndexLow); +#else + struct stat fileStat1, fileStat2; + if (stat(file1.c_str(), &fileStat1) == 0 && + stat(file2.c_str(), &fileStat2) == 0) { + // see if the files are the same file + // check the device inode and size + if (memcmp(&fileStat2.st_dev, &fileStat1.st_dev, + sizeof(fileStat1.st_dev)) == 0 && + memcmp(&fileStat2.st_ino, &fileStat1.st_ino, + sizeof(fileStat1.st_ino)) == 0 && + fileStat2.st_size == fileStat1.st_size) { + return true; + } + } + return false; +#endif +} + +bool SystemTools::PathExists(const std::string& path) +{ + if (path.empty()) { + return false; + } +#if defined(__CYGWIN__) + // Convert path to native windows path if possible. + char winpath[MAX_PATH]; + if (SystemTools::PathCygwinToWin32(path.c_str(), winpath)) { + return (GetFileAttributesA(winpath) != INVALID_FILE_ATTRIBUTES); + } + struct stat st; + return lstat(path.c_str(), &st) == 0; +#elif defined(_WIN32) + return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) != + INVALID_FILE_ATTRIBUTES); +#else + struct stat st; + return lstat(path.c_str(), &st) == 0; +#endif +} + +bool SystemTools::FileExists(const char* filename) +{ + if (!filename) { + return false; + } + return SystemTools::FileExists(std::string(filename)); +} + +bool SystemTools::FileExists(const std::string& filename) +{ + if (filename.empty()) { + return false; + } +#if defined(__CYGWIN__) + // Convert filename to native windows path if possible. + char winpath[MAX_PATH]; + if (SystemTools::PathCygwinToWin32(filename.c_str(), winpath)) { + return (GetFileAttributesA(winpath) != INVALID_FILE_ATTRIBUTES); + } + return access(filename.c_str(), R_OK) == 0; +#elif defined(_WIN32) + DWORD attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(filename).c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { + return false; + } + + if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { + // Using 0 instead of GENERIC_READ as it allows reading of file attributes + // even if we do not have permission to read the file itself + HANDLE handle = + CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(), 0, 0, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + if (handle == INVALID_HANDLE_VALUE) { + return false; + } + + CloseHandle(handle); + } + + return true; +#else +// SCO OpenServer 5.0.7/3.2's command has 711 permission. +# if defined(_SCO_DS) + return access(filename.c_str(), F_OK) == 0; +# else + return access(filename.c_str(), R_OK) == 0; +# endif +#endif +} + +bool SystemTools::FileExists(const char* filename, bool isFile) +{ + if (!filename) { + return false; + } + return SystemTools::FileExists(std::string(filename), isFile); +} + +bool SystemTools::FileExists(const std::string& filename, bool isFile) +{ + if (SystemTools::FileExists(filename)) { + // If isFile is set return not FileIsDirectory, + // so this will only be true if it is a file + return !isFile || !SystemTools::FileIsDirectory(filename); + } + return false; +} + +bool SystemTools::TestFileAccess(const char* filename, + TestFilePermissions permissions) +{ + if (!filename) { + return false; + } + return SystemTools::TestFileAccess(std::string(filename), permissions); +} + +bool SystemTools::TestFileAccess(const std::string& filename, + TestFilePermissions permissions) +{ + if (filename.empty()) { + return false; + } +#if defined(_WIN32) && !defined(__CYGWIN__) + // If execute set, change to read permission (all files on Windows + // are executable if they are readable). The CRT will always fail + // if you pass an execute bit. + if (permissions & TEST_FILE_EXECUTE) { + permissions &= ~TEST_FILE_EXECUTE; + permissions |= TEST_FILE_READ; + } + return _waccess(Encoding::ToWindowsExtendedPath(filename).c_str(), + permissions) == 0; +#else + return access(filename.c_str(), permissions) == 0; +#endif +} + +int SystemTools::Stat(const char* path, SystemTools::Stat_t* buf) +{ + if (!path) { + errno = EFAULT; + return -1; + } + return SystemTools::Stat(std::string(path), buf); +} + +int SystemTools::Stat(const std::string& path, SystemTools::Stat_t* buf) +{ + if (path.empty()) { + errno = ENOENT; + return -1; + } +#if defined(_WIN32) && !defined(__CYGWIN__) + // Ideally we should use Encoding::ToWindowsExtendedPath to support + // long paths, but _wstat64 rejects paths with '?' in them, thinking + // they are wildcards. + std::wstring const& wpath = Encoding::ToWide(path); +# if defined(__BORLANDC__) + return _wstati64(wpath.c_str(), buf); +# else + return _wstat64(wpath.c_str(), buf); +# endif +#else + return stat(path.c_str(), buf); +#endif +} + +#ifdef __CYGWIN__ +bool SystemTools::PathCygwinToWin32(const char* path, char* win32_path) +{ + auto itr = SystemTools::Statics->Cyg2Win32Map.find(path); + if (itr != SystemTools::Statics->Cyg2Win32Map.end()) { + strncpy(win32_path, itr->second.c_str(), MAX_PATH); + } else { + if (cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, win32_path, MAX_PATH) != + 0) { + win32_path[0] = 0; + } + SystemTools::Statics->Cyg2Win32Map.insert( + SystemToolsStatic::StringMap::value_type(path, win32_path)); + } + return win32_path[0] != 0; +} +#endif + +bool SystemTools::Touch(const std::string& filename, bool create) +{ + if (!SystemTools::PathExists(filename)) { + if (create) { + FILE* file = Fopen(filename, "a+b"); + if (file) { + fclose(file); + return true; + } + return false; + } else { + return true; + } + } +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE h = CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(), + FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (!h) { + return false; + } + FILETIME mtime; + GetSystemTimeAsFileTime(&mtime); + if (!SetFileTime(h, 0, 0, &mtime)) { + CloseHandle(h); + return false; + } + CloseHandle(h); +#elif KWSYS_CXX_HAS_UTIMENSAT + // utimensat is only available on newer Unixes and macOS 10.13+ + if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) { + return false; + } +#else + // fall back to utimes + if (utimes(filename.c_str(), nullptr) < 0) { + return false; + } +#endif + return true; +} + +bool SystemTools::FileTimeCompare(const std::string& f1, const std::string& f2, + int* result) +{ + // Default to same time. + *result = 0; +#if !defined(_WIN32) || defined(__CYGWIN__) + // POSIX version. Use stat function to get file modification time. + struct stat s1; + if (stat(f1.c_str(), &s1) != 0) { + return false; + } + struct stat s2; + if (stat(f2.c_str(), &s2) != 0) { + return false; + } +# if KWSYS_CXX_STAT_HAS_ST_MTIM + // Compare using nanosecond resolution. + if (s1.st_mtim.tv_sec < s2.st_mtim.tv_sec) { + *result = -1; + } else if (s1.st_mtim.tv_sec > s2.st_mtim.tv_sec) { + *result = 1; + } else if (s1.st_mtim.tv_nsec < s2.st_mtim.tv_nsec) { + *result = -1; + } else if (s1.st_mtim.tv_nsec > s2.st_mtim.tv_nsec) { + *result = 1; + } +# elif KWSYS_CXX_STAT_HAS_ST_MTIMESPEC + // Compare using nanosecond resolution. + if (s1.st_mtimespec.tv_sec < s2.st_mtimespec.tv_sec) { + *result = -1; + } else if (s1.st_mtimespec.tv_sec > s2.st_mtimespec.tv_sec) { + *result = 1; + } else if (s1.st_mtimespec.tv_nsec < s2.st_mtimespec.tv_nsec) { + *result = -1; + } else if (s1.st_mtimespec.tv_nsec > s2.st_mtimespec.tv_nsec) { + *result = 1; + } +# else + // Compare using 1 second resolution. + if (s1.st_mtime < s2.st_mtime) { + *result = -1; + } else if (s1.st_mtime > s2.st_mtime) { + *result = 1; + } +# endif +#else + // Windows version. Get the modification time from extended file attributes. + WIN32_FILE_ATTRIBUTE_DATA f1d; + WIN32_FILE_ATTRIBUTE_DATA f2d; + if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f1).c_str(), + GetFileExInfoStandard, &f1d)) { + return false; + } + if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(), + GetFileExInfoStandard, &f2d)) { + return false; + } + + // Compare the file times using resolution provided by system call. + *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime); +#endif + return true; +} + +// Return a capitalized string (i.e the first letter is uppercased, all other +// are lowercased) +std::string SystemTools::Capitalized(const std::string& s) +{ + std::string n; + if (s.empty()) { + return n; + } + n.resize(s.size()); + n[0] = static_cast<std::string::value_type>(toupper(s[0])); + for (size_t i = 1; i < s.size(); i++) { + n[i] = static_cast<std::string::value_type>(tolower(s[i])); + } + return n; +} + +// Return capitalized words +std::string SystemTools::CapitalizedWords(const std::string& s) +{ + std::string n(s); + for (size_t i = 0; i < s.size(); i++) { +#if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG) + // MS has an assert that will fail if s[i] < 0; setting + // LC_CTYPE using setlocale() does *not* help. Painful. + if ((int)s[i] >= 0 && isalpha(s[i]) && + (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1])))) +#else + if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1]))) +#endif + { + n[i] = static_cast<std::string::value_type>(toupper(s[i])); + } + } + return n; +} + +// Return uncapitalized words +std::string SystemTools::UnCapitalizedWords(const std::string& s) +{ + std::string n(s); + for (size_t i = 0; i < s.size(); i++) { +#if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG) + // MS has an assert that will fail if s[i] < 0; setting + // LC_CTYPE using setlocale() does *not* help. Painful. + if ((int)s[i] >= 0 && isalpha(s[i]) && + (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1])))) +#else + if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1]))) +#endif + { + n[i] = static_cast<std::string::value_type>(tolower(s[i])); + } + } + return n; +} + +// only works for words with at least two letters +std::string SystemTools::AddSpaceBetweenCapitalizedWords(const std::string& s) +{ + std::string n; + if (!s.empty()) { + n.reserve(s.size()); + n += s[0]; + for (size_t i = 1; i < s.size(); i++) { + if (isupper(s[i]) && !isspace(s[i - 1]) && !isupper(s[i - 1])) { + n += ' '; + } + n += s[i]; + } + } + return n; +} + +char* SystemTools::AppendStrings(const char* str1, const char* str2) +{ + if (!str1) { + return SystemTools::DuplicateString(str2); + } + if (!str2) { + return SystemTools::DuplicateString(str1); + } + size_t len1 = strlen(str1); + char* newstr = new char[len1 + strlen(str2) + 1]; + if (!newstr) { + return nullptr; + } + strcpy(newstr, str1); + strcat(newstr + len1, str2); + return newstr; +} + +char* SystemTools::AppendStrings(const char* str1, const char* str2, + const char* str3) +{ + if (!str1) { + return SystemTools::AppendStrings(str2, str3); + } + if (!str2) { + return SystemTools::AppendStrings(str1, str3); + } + if (!str3) { + return SystemTools::AppendStrings(str1, str2); + } + + size_t len1 = strlen(str1), len2 = strlen(str2); + char* newstr = new char[len1 + len2 + strlen(str3) + 1]; + if (!newstr) { + return nullptr; + } + strcpy(newstr, str1); + strcat(newstr + len1, str2); + strcat(newstr + len1 + len2, str3); + return newstr; +} + +// Return a lower case string +std::string SystemTools::LowerCase(const std::string& s) +{ + std::string n; + n.resize(s.size()); + for (size_t i = 0; i < s.size(); i++) { + n[i] = static_cast<std::string::value_type>(tolower(s[i])); + } + return n; +} + +// Return a lower case string +std::string SystemTools::UpperCase(const std::string& s) +{ + std::string n; + n.resize(s.size()); + for (size_t i = 0; i < s.size(); i++) { + n[i] = static_cast<std::string::value_type>(toupper(s[i])); + } + return n; +} + +// Count char in string +size_t SystemTools::CountChar(const char* str, char c) +{ + size_t count = 0; + + if (str) { + while (*str) { + if (*str == c) { + ++count; + } + ++str; + } + } + return count; +} + +// Remove chars in string +char* SystemTools::RemoveChars(const char* str, const char* toremove) +{ + if (!str) { + return nullptr; + } + char* clean_str = new char[strlen(str) + 1]; + char* ptr = clean_str; + while (*str) { + const char* str2 = toremove; + while (*str2 && *str != *str2) { + ++str2; + } + if (!*str2) { + *ptr++ = *str; + } + ++str; + } + *ptr = '\0'; + return clean_str; +} + +// Remove chars in string +char* SystemTools::RemoveCharsButUpperHex(const char* str) +{ + if (!str) { + return nullptr; + } + char* clean_str = new char[strlen(str) + 1]; + char* ptr = clean_str; + while (*str) { + if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'F')) { + *ptr++ = *str; + } + ++str; + } + *ptr = '\0'; + return clean_str; +} + +// Replace chars in string +char* SystemTools::ReplaceChars(char* str, const char* toreplace, + char replacement) +{ + if (str) { + char* ptr = str; + while (*ptr) { + const char* ptr2 = toreplace; + while (*ptr2) { + if (*ptr == *ptr2) { + *ptr = replacement; + } + ++ptr2; + } + ++ptr; + } + } + return str; +} + +// Returns if string starts with another string +bool SystemTools::StringStartsWith(const char* str1, const char* str2) +{ + if (!str1 || !str2) { + return false; + } + size_t len1 = strlen(str1), len2 = strlen(str2); + return len1 >= len2 && !strncmp(str1, str2, len2) ? true : false; +} + +// Returns if string starts with another string +bool SystemTools::StringStartsWith(const std::string& str1, const char* str2) +{ + if (!str2) { + return false; + } + size_t len1 = str1.size(), len2 = strlen(str2); + return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false; +} + +// Returns if string ends with another string +bool SystemTools::StringEndsWith(const char* str1, const char* str2) +{ + if (!str1 || !str2) { + return false; + } + size_t len1 = strlen(str1), len2 = strlen(str2); + return len1 >= len2 && !strncmp(str1 + (len1 - len2), str2, len2) ? true + : false; +} + +// Returns if string ends with another string +bool SystemTools::StringEndsWith(const std::string& str1, const char* str2) +{ + if (!str2) { + return false; + } + size_t len1 = str1.size(), len2 = strlen(str2); + return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2) + ? true + : false; +} + +// Returns a pointer to the last occurrence of str2 in str1 +const char* SystemTools::FindLastString(const char* str1, const char* str2) +{ + if (!str1 || !str2) { + return nullptr; + } + + size_t len1 = strlen(str1), len2 = strlen(str2); + if (len1 >= len2) { + const char* ptr = str1 + len1 - len2; + do { + if (!strncmp(ptr, str2, len2)) { + return ptr; + } + } while (ptr-- != str1); + } + + return nullptr; +} + +// Duplicate string +char* SystemTools::DuplicateString(const char* str) +{ + if (str) { + char* newstr = new char[strlen(str) + 1]; + return strcpy(newstr, str); + } + return nullptr; +} + +// Return a cropped string +std::string SystemTools::CropString(const std::string& s, size_t max_len) +{ + if (s.empty() || max_len == 0 || max_len >= s.size()) { + return s; + } + + std::string n; + n.reserve(max_len); + + size_t middle = max_len / 2; + + n += s.substr(0, middle); + n += s.substr(s.size() - (max_len - middle)); + + if (max_len > 2) { + n[middle] = '.'; + if (max_len > 3) { + n[middle - 1] = '.'; + if (max_len > 4) { + n[middle + 1] = '.'; + } + } + } + + return n; +} + +std::vector<std::string> SystemTools::SplitString(const std::string& p, + char sep, bool isPath) +{ + std::string path = p; + std::vector<std::string> paths; + if (path.empty()) { + return paths; + } + if (isPath && path[0] == '/') { + path.erase(path.begin()); + paths.emplace_back("/"); + } + std::string::size_type pos1 = 0; + std::string::size_type pos2 = path.find(sep, pos1 + 1); + while (pos2 != std::string::npos) { + paths.push_back(path.substr(pos1, pos2 - pos1)); + pos1 = pos2 + 1; + pos2 = path.find(sep, pos1 + 1); + } + paths.push_back(path.substr(pos1, pos2 - pos1)); + + return paths; +} + +int SystemTools::EstimateFormatLength(const char* format, va_list ap) +{ + if (!format) { + return 0; + } + + // Quick-hack attempt at estimating the length of the string. + // Should never under-estimate. + + // Start with the length of the format string itself. + + size_t length = strlen(format); + + // Increase the length for every argument in the format. + + const char* cur = format; + while (*cur) { + if (*cur++ == '%') { + // Skip "%%" since it doesn't correspond to a va_arg. + if (*cur != '%') { + while (!int(isalpha(*cur))) { + ++cur; + } + switch (*cur) { + case 's': { + // Check the length of the string. + char* s = va_arg(ap, char*); + if (s) { + length += strlen(s); + } + } break; + case 'e': + case 'f': + case 'g': { + // Assume the argument contributes no more than 64 characters. + length += 64; + + // Eat the argument. + static_cast<void>(va_arg(ap, double)); + } break; + default: { + // Assume the argument contributes no more than 64 characters. + length += 64; + + // Eat the argument. + static_cast<void>(va_arg(ap, int)); + } break; + } + } + + // Move past the characters just tested. + ++cur; + } + } + + return static_cast<int>(length); +} + +std::string SystemTools::EscapeChars(const char* str, + const char* chars_to_escape, + char escape_char) +{ + std::string n; + if (str) { + if (!chars_to_escape || !*chars_to_escape) { + n.append(str); + } else { + n.reserve(strlen(str)); + while (*str) { + const char* ptr = chars_to_escape; + while (*ptr) { + if (*str == *ptr) { + n += escape_char; + break; + } + ++ptr; + } + n += *str; + ++str; + } + } + } + return n; +} + +#ifdef __VMS +static void ConvertVMSToUnix(std::string& path) +{ + std::string::size_type rootEnd = path.find(":["); + std::string::size_type pathEnd = path.find("]"); + if (rootEnd != std::string::npos) { + std::string root = path.substr(0, rootEnd); + std::string pathPart = path.substr(rootEnd + 2, pathEnd - rootEnd - 2); + const char* pathCString = pathPart.c_str(); + const char* pos0 = pathCString; + for (std::string::size_type pos = 0; *pos0; ++pos) { + if (*pos0 == '.') { + pathPart[pos] = '/'; + } + pos0++; + } + path = "/" + root + "/" + pathPart; + } +} +#endif + +// convert windows slashes to unix slashes +void SystemTools::ConvertToUnixSlashes(std::string& path) +{ + if (path.empty()) { + return; + } + + const char* pathCString = path.c_str(); + bool hasDoubleSlash = false; +#ifdef __VMS + ConvertVMSToUnix(path); +#else + const char* pos0 = pathCString; + for (std::string::size_type pos = 0; *pos0; ++pos) { + if (*pos0 == '\\') { + path[pos] = '/'; + } + + // Also, reuse the loop to check for slash followed by another slash + if (!hasDoubleSlash && *(pos0 + 1) == '/' && *(pos0 + 2) == '/') { +# ifdef _WIN32 + // However, on windows if the first characters are both slashes, + // then keep them that way, so that network paths can be handled. + if (pos > 0) { + hasDoubleSlash = true; + } +# else + hasDoubleSlash = true; +# endif + } + + pos0++; + } + + if (hasDoubleSlash) { + SystemTools::ReplaceString(path, "//", "/"); + } +#endif + + // remove any trailing slash + // if there is a tilda ~ then replace it with HOME + pathCString = path.c_str(); + if (pathCString[0] == '~' && + (pathCString[1] == '/' || pathCString[1] == '\0')) { + std::string homeEnv; + if (SystemTools::GetEnv("HOME", homeEnv)) { + path.replace(0, 1, homeEnv); + } + } +#ifdef HAVE_GETPWNAM + else if (pathCString[0] == '~') { + std::string::size_type idx = path.find_first_of("/\0"); + std::string user = path.substr(1, idx - 1); + passwd* pw = getpwnam(user.c_str()); + if (pw) { + path.replace(0, idx, pw->pw_dir); + } + } +#endif + // remove trailing slash if the path is more than + // a single / + pathCString = path.c_str(); + size_t size = path.size(); + if (size > 1 && path.back() == '/') { + // if it is c:/ then do not remove the trailing slash + if (!((size == 3 && pathCString[1] == ':'))) { + path.resize(size - 1); + } + } +} + +#ifdef _WIN32 +std::wstring SystemTools::ConvertToWindowsExtendedPath( + const std::string& source) +{ + return Encoding::ToWindowsExtendedPath(source); +} +#endif + +// change // to /, and escape any spaces in the path +std::string SystemTools::ConvertToUnixOutputPath(const std::string& path) +{ + std::string ret = path; + + // remove // except at the beginning might be a cygwin drive + std::string::size_type pos = 1; + while ((pos = ret.find("//", pos)) != std::string::npos) { + ret.erase(pos, 1); + } + // escape spaces and () in the path + if (ret.find_first_of(" ") != std::string::npos) { + std::string result; + char lastch = 1; + for (const char* ch = ret.c_str(); *ch != '\0'; ++ch) { + // if it is already escaped then don't try to escape it again + if ((*ch == ' ') && lastch != '\\') { + result += '\\'; + } + result += *ch; + lastch = *ch; + } + ret = result; + } + return ret; +} + +std::string SystemTools::ConvertToOutputPath(const std::string& path) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + return SystemTools::ConvertToWindowsOutputPath(path); +#else + return SystemTools::ConvertToUnixOutputPath(path); +#endif +} + +// remove double slashes not at the start +std::string SystemTools::ConvertToWindowsOutputPath(const std::string& path) +{ + std::string ret; + // make it big enough for all of path and double quotes + ret.reserve(path.size() + 3); + // put path into the string + ret = path; + std::string::size_type pos = 0; + // first convert all of the slashes + while ((pos = ret.find('/', pos)) != std::string::npos) { + ret[pos] = '\\'; + pos++; + } + // check for really small paths + if (ret.size() < 2) { + return ret; + } + // now clean up a bit and remove double slashes + // Only if it is not the first position in the path which is a network + // path on windows + pos = 1; // start at position 1 + if (ret[0] == '\"') { + pos = 2; // if the string is already quoted then start at 2 + if (ret.size() < 3) { + return ret; + } + } + while ((pos = ret.find("\\\\", pos)) != std::string::npos) { + ret.erase(pos, 1); + } + // now double quote the path if it has spaces in it + // and is not already double quoted + if (ret.find(' ') != std::string::npos && ret[0] != '\"') { + ret.insert(static_cast<std::string::size_type>(0), + static_cast<std::string::size_type>(1), '\"'); + ret.append(1, '\"'); + } + return ret; +} + +/** + * Append the filename from the path source to the directory name dir. + */ +static std::string FileInDir(const std::string& source, const std::string& dir) +{ + std::string new_destination = dir; + SystemTools::ConvertToUnixSlashes(new_destination); + return new_destination + '/' + SystemTools::GetFilenameName(source); +} + +bool SystemTools::CopyFileIfDifferent(const std::string& source, + const std::string& destination) +{ + // special check for a destination that is a directory + // FilesDiffer does not handle file to directory compare + if (SystemTools::FileIsDirectory(destination)) { + const std::string new_destination = FileInDir(source, destination); + if (!SystemTools::ComparePath(new_destination, destination)) { + return SystemTools::CopyFileIfDifferent(source, new_destination); + } + } else { + // source and destination are files so do a copy if they + // are different + if (SystemTools::FilesDiffer(source, destination)) { + return SystemTools::CopyFileAlways(source, destination); + } + } + // at this point the files must be the same so return true + return true; +} + +#define KWSYS_ST_BUFFER 4096 + +bool SystemTools::FilesDiffer(const std::string& source, + const std::string& destination) +{ + +#if defined(_WIN32) + WIN32_FILE_ATTRIBUTE_DATA statSource; + if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(source).c_str(), + GetFileExInfoStandard, &statSource) == 0) { + return true; + } + + WIN32_FILE_ATTRIBUTE_DATA statDestination; + if (GetFileAttributesExW( + Encoding::ToWindowsExtendedPath(destination).c_str(), + GetFileExInfoStandard, &statDestination) == 0) { + return true; + } + + if (statSource.nFileSizeHigh != statDestination.nFileSizeHigh || + statSource.nFileSizeLow != statDestination.nFileSizeLow) { + return true; + } + + if (statSource.nFileSizeHigh == 0 && statSource.nFileSizeLow == 0) { + return false; + } + off_t nleft = + ((__int64)statSource.nFileSizeHigh << 32) + statSource.nFileSizeLow; + +#else + + struct stat statSource; + if (stat(source.c_str(), &statSource) != 0) { + return true; + } + + struct stat statDestination; + if (stat(destination.c_str(), &statDestination) != 0) { + return true; + } + + if (statSource.st_size != statDestination.st_size) { + return true; + } + + if (statSource.st_size == 0) { + return false; + } + off_t nleft = statSource.st_size; +#endif + +#if defined(_WIN32) + kwsys::ifstream finSource(source.c_str(), (std::ios::binary | std::ios::in)); + kwsys::ifstream finDestination(destination.c_str(), + (std::ios::binary | std::ios::in)); +#else + kwsys::ifstream finSource(source.c_str()); + kwsys::ifstream finDestination(destination.c_str()); +#endif + if (!finSource || !finDestination) { + return true; + } + + // Compare the files a block at a time. + char source_buf[KWSYS_ST_BUFFER]; + char dest_buf[KWSYS_ST_BUFFER]; + while (nleft > 0) { + // Read a block from each file. + std::streamsize nnext = (nleft > KWSYS_ST_BUFFER) + ? KWSYS_ST_BUFFER + : static_cast<std::streamsize>(nleft); + finSource.read(source_buf, nnext); + finDestination.read(dest_buf, nnext); + + // If either failed to read assume they are different. + if (static_cast<std::streamsize>(finSource.gcount()) != nnext || + static_cast<std::streamsize>(finDestination.gcount()) != nnext) { + return true; + } + + // If this block differs the file differs. + if (memcmp(static_cast<const void*>(source_buf), + static_cast<const void*>(dest_buf), + static_cast<size_t>(nnext)) != 0) { + return true; + } + + // Update the byte count remaining. + nleft -= nnext; + } + + // No differences found. + return false; +} + +bool SystemTools::TextFilesDiffer(const std::string& path1, + const std::string& path2) +{ + kwsys::ifstream if1(path1.c_str()); + kwsys::ifstream if2(path2.c_str()); + if (!if1 || !if2) { + return true; + } + + for (;;) { + std::string line1, line2; + bool hasData1 = GetLineFromStream(if1, line1); + bool hasData2 = GetLineFromStream(if2, line2); + if (hasData1 != hasData2) { + return true; + } + if (!hasData1) { + break; + } + if (line1 != line2) { + return true; + } + } + return false; +} + +/** + * Blockwise copy source to destination file + */ +static bool CopyFileContentBlockwise(const std::string& source, + const std::string& destination) +{ + // Open files + kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary); + if (!fin) { + return false; + } + + // try and remove the destination file so that read only destination files + // can be written to. + // If the remove fails continue so that files in read only directories + // that do not allow file removal can be modified. + SystemTools::RemoveFile(destination); + + kwsys::ofstream fout(destination.c_str(), + std::ios::out | std::ios::trunc | std::ios::binary); + if (!fout) { + return false; + } + + // This copy loop is very sensitive on certain platforms with + // slightly broken stream libraries (like HPUX). Normally, it is + // incorrect to not check the error condition on the fin.read() + // before using the data, but the fin.gcount() will be zero if an + // error occurred. Therefore, the loop should be safe everywhere. + while (fin) { + const int bufferSize = 4096; + char buffer[bufferSize]; + + fin.read(buffer, bufferSize); + if (fin.gcount()) { + fout.write(buffer, fin.gcount()); + } else { + break; + } + } + + // Make sure the operating system has finished writing the file + // before closing it. This will ensure the file is finished before + // the check below. + fout.flush(); + + fin.close(); + fout.close(); + + if (!fout) { + return false; + } + + return true; +} + +/** + * Clone the source file to the destination file + * + * If available, the Linux FICLONE ioctl is used to create a check + * copy-on-write clone of the source file. + * + * The method returns false for the following cases: + * - The code has not been compiled on Linux or the ioctl was unknown + * - The source and destination is on different file systems + * - The underlying filesystem does not support file cloning + * - An unspecified error occurred + */ +static bool CloneFileContent(const std::string& source, + const std::string& destination) +{ +#if defined(__linux) && defined(FICLONE) + int in = open(source.c_str(), O_RDONLY); + if (in < 0) { + return false; + } + + SystemTools::RemoveFile(destination); + + int out = + open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (out < 0) { + close(in); + return false; + } + + int result = ioctl(out, FICLONE, in); + close(in); + close(out); + + if (result < 0) { + return false; + } + + return true; +#else + (void)source; + (void)destination; + return false; +#endif +} + +/** + * Copy a file named by "source" to the file named by "destination". + */ +bool SystemTools::CopyFileAlways(const std::string& source, + const std::string& destination) +{ + mode_t perm = 0; + bool perms = SystemTools::GetPermissions(source, perm); + std::string real_destination = destination; + + if (SystemTools::FileIsDirectory(source)) { + SystemTools::MakeDirectory(destination); + } else { + // If destination is a directory, try to create a file with the same + // name as the source in that directory. + + std::string destination_dir; + if (SystemTools::FileIsDirectory(destination)) { + destination_dir = real_destination; + SystemTools::ConvertToUnixSlashes(real_destination); + real_destination += '/'; + std::string source_name = source; + real_destination += SystemTools::GetFilenameName(source_name); + } else { + destination_dir = SystemTools::GetFilenamePath(destination); + } + // If files are the same do not copy + if (SystemTools::SameFile(source, real_destination)) { + return true; + } + + // Create destination directory + + SystemTools::MakeDirectory(destination_dir); + + if (!CloneFileContent(source, real_destination)) { + // if cloning did not succeed, fall back to blockwise copy + if (!CopyFileContentBlockwise(source, real_destination)) { + return false; + } + } + } + if (perms) { + if (!SystemTools::SetPermissions(real_destination, perm)) { + return false; + } + } + return true; +} + +bool SystemTools::CopyAFile(const std::string& source, + const std::string& destination, bool always) +{ + if (always) { + return SystemTools::CopyFileAlways(source, destination); + } else { + return SystemTools::CopyFileIfDifferent(source, destination); + } +} + +/** + * Copy a directory content from "source" directory to the directory named by + * "destination". + */ +bool SystemTools::CopyADirectory(const std::string& source, + const std::string& destination, bool always) +{ + Directory dir; + if (dir.Load(source) == 0) { + return false; + } + size_t fileNum; + if (!SystemTools::MakeDirectory(destination)) { + return false; + } + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..")) { + std::string fullPath = source; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if (SystemTools::FileIsDirectory(fullPath)) { + std::string fullDestPath = destination; + fullDestPath += "/"; + fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if (!SystemTools::CopyADirectory(fullPath, fullDestPath, always)) { + return false; + } + } else { + if (!SystemTools::CopyAFile(fullPath, destination, always)) { + return false; + } + } + } + } + + return true; +} + +// return size of file; also returns zero if no file exists +unsigned long SystemTools::FileLength(const std::string& filename) +{ + unsigned long length = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, &fs) != 0) { + /* To support the full 64-bit file size, use fs.nFileSizeHigh + * and fs.nFileSizeLow to construct the 64 bit size + + length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow; + */ + length = static_cast<unsigned long>(fs.nFileSizeLow); + } +#else + struct stat fs; + if (stat(filename.c_str(), &fs) == 0) { + length = static_cast<unsigned long>(fs.st_size); + } +#endif + return length; +} + +int SystemTools::Strucmp(const char* l, const char* r) +{ + int lc; + int rc; + do { + lc = tolower(*l++); + rc = tolower(*r++); + } while (lc == rc && lc); + return lc - rc; +} + +// return file's modified time +long int SystemTools::ModifiedTime(const std::string& filename) +{ + long int mt = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, &fs) != 0) { + mt = windows_filetime_to_posix_time(fs.ftLastWriteTime); + } +#else + struct stat fs; + if (stat(filename.c_str(), &fs) == 0) { + mt = static_cast<long int>(fs.st_mtime); + } +#endif + return mt; +} + +// return file's creation time +long int SystemTools::CreationTime(const std::string& filename) +{ + long int ct = 0; +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA fs; + if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(), + GetFileExInfoStandard, &fs) != 0) { + ct = windows_filetime_to_posix_time(fs.ftCreationTime); + } +#else + struct stat fs; + if (stat(filename.c_str(), &fs) == 0) { + ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0; + } +#endif + return ct; +} + +std::string SystemTools::GetLastSystemError() +{ + int e = errno; + return strerror(e); +} + +bool SystemTools::RemoveFile(const std::string& source) +{ +#ifdef _WIN32 + std::wstring const& ws = Encoding::ToWindowsExtendedPath(source); + if (DeleteFileW(ws.c_str())) { + return true; + } + DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { + return true; + } + if (err != ERROR_ACCESS_DENIED) { + return false; + } + /* The file may be read-only. Try adding write permission. */ + mode_t mode; + if (!SystemTools::GetPermissions(source, mode) || + !SystemTools::SetPermissions(source, S_IWRITE)) { + SetLastError(err); + return false; + } + + const DWORD DIRECTORY_SOFT_LINK_ATTRS = + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT; + DWORD attrs = GetFileAttributesW(ws.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && + (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS && + RemoveDirectoryW(ws.c_str())) { + return true; + } + if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND || + GetLastError() == ERROR_PATH_NOT_FOUND) { + return true; + } + /* Try to restore the original permissions. */ + SystemTools::SetPermissions(source, mode); + SetLastError(err); + return false; +#else + return unlink(source.c_str()) == 0 || errno == ENOENT; +#endif +} + +bool SystemTools::RemoveADirectory(const std::string& source) +{ + // Add write permission to the directory so we can modify its + // content to remove files and directories from it. + mode_t mode; + if (SystemTools::GetPermissions(source, mode)) { +#if defined(_WIN32) && !defined(__CYGWIN__) + mode |= S_IWRITE; +#else + mode |= S_IWUSR; +#endif + SystemTools::SetPermissions(source, mode); + } + + Directory dir; + dir.Load(source); + size_t fileNum; + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..")) { + std::string fullPath = source; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if (SystemTools::FileIsDirectory(fullPath) && + !SystemTools::FileIsSymlink(fullPath)) { + if (!SystemTools::RemoveADirectory(fullPath)) { + return false; + } + } else { + if (!SystemTools::RemoveFile(fullPath)) { + return false; + } + } + } + } + + return (Rmdir(source) == 0); +} + +/** + */ +size_t SystemTools::GetMaximumFilePathLength() +{ + return KWSYS_SYSTEMTOOLS_MAXPATH; +} + +/** + * Find the file the given name. Searches the given path and then + * the system search path. Returns the full path to the file if it is + * found. Otherwise, the empty string is returned. + */ +std::string SystemToolsStatic::FindName( + const std::string& name, const std::vector<std::string>& userPaths, + bool no_system_path) +{ + // Add the system search path to our path first + std::vector<std::string> path; + if (!no_system_path) { + SystemTools::GetPath(path, "CMAKE_FILE_PATH"); + SystemTools::GetPath(path); + } + // now add the additional paths + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); + // now look for the file + std::string tryPath; + for (std::string const& p : path) { + tryPath = p; + if (tryPath.empty() || tryPath.back() != '/') { + tryPath += '/'; + } + tryPath += name; + if (SystemTools::FileExists(tryPath)) { + return tryPath; + } + } + // Couldn't find the file. + return ""; +} + +/** + * Find the file the given name. Searches the given path and then + * the system search path. Returns the full path to the file if it is + * found. Otherwise, the empty string is returned. + */ +std::string SystemTools::FindFile(const std::string& name, + const std::vector<std::string>& userPaths, + bool no_system_path) +{ + std::string tryPath = + SystemToolsStatic::FindName(name, userPaths, no_system_path); + if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) { + return SystemTools::CollapseFullPath(tryPath); + } + // Couldn't find the file. + return ""; +} + +/** + * Find the directory the given name. Searches the given path and then + * the system search path. Returns the full path to the directory if it is + * found. Otherwise, the empty string is returned. + */ +std::string SystemTools::FindDirectory( + const std::string& name, const std::vector<std::string>& userPaths, + bool no_system_path) +{ + std::string tryPath = + SystemToolsStatic::FindName(name, userPaths, no_system_path); + if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) { + return SystemTools::CollapseFullPath(tryPath); + } + // Couldn't find the file. + return ""; +} + +/** + * Find the executable with the given name. Searches the given path and then + * the system search path. Returns the full path to the executable if it is + * found. Otherwise, the empty string is returned. + */ +std::string SystemTools::FindProgram(const char* nameIn, + const std::vector<std::string>& userPaths, + bool no_system_path) +{ + if (!nameIn || !*nameIn) { + return ""; + } + return SystemTools::FindProgram(std::string(nameIn), userPaths, + no_system_path); +} + +std::string SystemTools::FindProgram(const std::string& name, + const std::vector<std::string>& userPaths, + bool no_system_path) +{ + std::string tryPath; + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) + std::vector<std::string> extensions; + // check to see if the name already has a .xxx at + // the end of it + // on windows try .com then .exe + if (name.size() <= 3 || name[name.size() - 4] != '.') { + extensions.emplace_back(".com"); + extensions.emplace_back(".exe"); + + // first try with extensions if the os supports them + for (std::string const& ext : extensions) { + tryPath = name; + tryPath += ext; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + } + } +#endif + + // now try just the name + if (SystemTools::FileExists(name, true)) { + return SystemTools::CollapseFullPath(name); + } + // now construct the path + std::vector<std::string> path; + // Add the system search path to our path. + if (!no_system_path) { + SystemTools::GetPath(path); + } + // now add the additional paths + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); + // Add a trailing slash to all paths to aid the search process. + for (std::string& p : path) { + if (p.empty() || p.back() != '/') { + p += '/'; + } + } + // Try each path + for (std::string& p : path) { +#ifdef _WIN32 + // Remove double quotes from the path on windows + SystemTools::ReplaceString(p, "\"", ""); +#endif +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) + // first try with extensions + for (std::string const& ext : extensions) { + tryPath = p; + tryPath += name; + tryPath += ext; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + } +#endif + // now try it without them + tryPath = p; + tryPath += name; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + } + // Couldn't find the program. + return ""; +} + +std::string SystemTools::FindProgram(const std::vector<std::string>& names, + const std::vector<std::string>& path, + bool noSystemPath) +{ + for (std::string const& name : names) { + // Try to find the program. + std::string result = SystemTools::FindProgram(name, path, noSystemPath); + if (!result.empty()) { + return result; + } + } + return ""; +} + +/** + * Find the library with the given name. Searches the given path and then + * the system search path. Returns the full path to the library if it is + * found. Otherwise, the empty string is returned. + */ +std::string SystemTools::FindLibrary(const std::string& name, + const std::vector<std::string>& userPaths) +{ + // See if the executable exists as written. + if (SystemTools::FileExists(name, true)) { + return SystemTools::CollapseFullPath(name); + } + + // Add the system search path to our path. + std::vector<std::string> path; + SystemTools::GetPath(path); + // now add the additional paths + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); + // Add a trailing slash to all paths to aid the search process. + for (std::string& p : path) { + if (p.empty() || p.back() != '/') { + p += '/'; + } + } + std::string tryPath; + for (std::string const& p : path) { +#if defined(__APPLE__) + tryPath = p; + tryPath += name; + tryPath += ".framework"; + if (SystemTools::FileIsDirectory(tryPath)) { + return SystemTools::CollapseFullPath(tryPath); + } +#endif +#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) + tryPath = p; + tryPath += name; + tryPath += ".lib"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } +#else + tryPath = p; + tryPath += "lib"; + tryPath += name; + tryPath += ".so"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + tryPath = p; + tryPath += "lib"; + tryPath += name; + tryPath += ".a"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + tryPath = p; + tryPath += "lib"; + tryPath += name; + tryPath += ".sl"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + tryPath = p; + tryPath += "lib"; + tryPath += name; + tryPath += ".dylib"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } + tryPath = p; + tryPath += "lib"; + tryPath += name; + tryPath += ".dll"; + if (SystemTools::FileExists(tryPath, true)) { + return SystemTools::CollapseFullPath(tryPath); + } +#endif + } + + // Couldn't find the library. + return ""; +} + +std::string SystemTools::GetRealPath(const std::string& path, + std::string* errorMessage) +{ + std::string ret; + Realpath(path, ret, errorMessage); + return ret; +} + +bool SystemTools::FileIsDirectory(const std::string& inName) +{ + if (inName.empty()) { + return false; + } + size_t length = inName.size(); + const char* name = inName.c_str(); + + // Remove any trailing slash from the name except in a root component. + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + size_t last = length - 1; + if (last > 0 && (name[last] == '/' || name[last] == '\\') && + strcmp(name, "/") != 0 && name[last - 1] != ':') { + if (last < sizeof(local_buffer)) { + memcpy(local_buffer, name, last); + local_buffer[last] = '\0'; + name = local_buffer; + } else { + string_buffer.append(name, last); + name = string_buffer.c_str(); + } + } + +// Now check the file node type. +#if defined(_WIN32) + DWORD attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) { + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; +#else + struct stat fs; + if (stat(name, &fs) == 0) { + return S_ISDIR(fs.st_mode); +#endif + } else { + return false; + } +} + +bool SystemTools::FileIsSymlink(const std::string& name) +{ +#if defined(_WIN32) + std::wstring path = Encoding::ToWindowsExtendedPath(name); + DWORD attr = GetFileAttributesW(path.c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) { + if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + // FILE_ATTRIBUTE_REPARSE_POINT means: + // * a file or directory that has an associated reparse point, or + // * a file that is a symbolic link. + HANDLE hFile = CreateFileW( + path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + DWORD bytesReturned = 0; + if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, + nullptr)) { + CloseHandle(hFile); + // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be + // a symbolic link if it is not a reparse point. + return GetLastError() == ERROR_NOT_A_REPARSE_POINT; + } + CloseHandle(hFile); + ULONG reparseTag = + reinterpret_cast<PREPARSE_GUID_DATA_BUFFER>(&buffer[0])->ReparseTag; + return (reparseTag == IO_REPARSE_TAG_SYMLINK) || + (reparseTag == IO_REPARSE_TAG_MOUNT_POINT); + } + return false; + } else { + return false; + } +#else + struct stat fs; + if (lstat(name.c_str(), &fs) == 0) { + return S_ISLNK(fs.st_mode); + } else { + return false; + } +#endif +} + +bool SystemTools::FileIsFIFO(const std::string& name) +{ +#if defined(_WIN32) + HANDLE hFile = + CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + const DWORD type = GetFileType(hFile); + CloseHandle(hFile); + return type == FILE_TYPE_PIPE; +#else + struct stat fs; + if (lstat(name.c_str(), &fs) == 0) { + return S_ISFIFO(fs.st_mode); + } else { + return false; + } +#endif +} + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::CreateSymlink(const std::string&, const std::string&) +{ + return false; +} +#else +bool SystemTools::CreateSymlink(const std::string& origName, + const std::string& newName) +{ + return symlink(origName.c_str(), newName.c_str()) >= 0; +} +#endif + +#if defined(_WIN32) && !defined(__CYGWIN__) +bool SystemTools::ReadSymlink(const std::string&, std::string&) +{ + return false; +} +#else +bool SystemTools::ReadSymlink(const std::string& newName, + std::string& origName) +{ + char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1]; + int count = static_cast<int>( + readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH)); + if (count >= 0) { + // Add null-terminator. + buf[count] = 0; + origName = buf; + return true; + } else { + return false; + } +} +#endif + +int SystemTools::ChangeDirectory(const std::string& dir) +{ + return Chdir(dir); +} + +std::string SystemTools::GetCurrentWorkingDirectory(bool collapse) +{ + char buf[2048]; + const char* cwd = Getcwd(buf, 2048); + std::string path; + if (cwd) { + path = cwd; + } + if (collapse) { + return SystemTools::CollapseFullPath(path); + } + return path; +} + +std::string SystemTools::GetProgramPath(const std::string& in_name) +{ + std::string dir, file; + SystemTools::SplitProgramPath(in_name, dir, file); + return dir; +} + +bool SystemTools::SplitProgramPath(const std::string& in_name, + std::string& dir, std::string& file, bool) +{ + dir = in_name; + file = ""; + SystemTools::ConvertToUnixSlashes(dir); + + if (!SystemTools::FileIsDirectory(dir)) { + std::string::size_type slashPos = dir.rfind("/"); + if (slashPos != std::string::npos) { + file = dir.substr(slashPos + 1); + dir = dir.substr(0, slashPos); + } else { + file = dir; + dir = ""; + } + } + if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) { + std::string oldDir = in_name; + SystemTools::ConvertToUnixSlashes(oldDir); + dir = in_name; + return false; + } + return true; +} + +bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, + std::string& errorMsg, const char* exeName, + const char* buildDir, + const char* installPrefix) +{ + std::vector<std::string> failures; + std::string self = argv0 ? argv0 : ""; + failures.push_back(self); + SystemTools::ConvertToUnixSlashes(self); + self = SystemTools::FindProgram(self); + if (!SystemTools::FileExists(self)) { + if (buildDir) { + std::string intdir = "."; +#ifdef CMAKE_INTDIR + intdir = CMAKE_INTDIR; +#endif + self = buildDir; + self += "/bin/"; + self += intdir; + self += "/"; + self += exeName; + self += SystemTools::GetExecutableExtension(); + } + } + if (installPrefix) { + if (!SystemTools::FileExists(self)) { + failures.push_back(self); + self = installPrefix; + self += "/bin/"; + self += exeName; + } + } + if (!SystemTools::FileExists(self)) { + failures.push_back(self); + std::ostringstream msg; + msg << "Can not find the command line program "; + if (exeName) { + msg << exeName; + } + msg << "\n"; + if (argv0) { + msg << " argv[0] = \"" << argv0 << "\"\n"; + } + msg << " Attempted paths:\n"; + for (std::string const& ff : failures) { + msg << " \"" << ff << "\"\n"; + } + errorMsg = msg.str(); + return false; + } + pathOut = self; + return true; +} + +std::string SystemTools::CollapseFullPath(const std::string& in_relative) +{ + return SystemTools::CollapseFullPath(in_relative, nullptr); +} + +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP +void SystemTools::AddTranslationPath(const std::string& a, + const std::string& b) +{ + std::string path_a = a; + std::string path_b = b; + SystemTools::ConvertToUnixSlashes(path_a); + SystemTools::ConvertToUnixSlashes(path_b); + // First check this is a directory path, since we don't want the table to + // grow too fat + if (SystemTools::FileIsDirectory(path_a)) { + // Make sure the path is a full path and does not contain no '..' + // Ken--the following code is incorrect. .. can be in a valid path + // for example /home/martink/MyHubba...Hubba/Src + if (SystemTools::FileIsFullPath(path_b) && + path_b.find("..") == std::string::npos) { + // Before inserting make sure path ends with '/' + if (!path_a.empty() && path_a.back() != '/') { + path_a += '/'; + } + if (!path_b.empty() && path_b.back() != '/') { + path_b += '/'; + } + if (!(path_a == path_b)) { + SystemTools::Statics->TranslationMap.insert( + SystemToolsStatic::StringMap::value_type(std::move(path_a), + std::move(path_b))); + } + } + } +} + +void SystemTools::AddKeepPath(const std::string& dir) +{ + std::string cdir; + Realpath(SystemTools::CollapseFullPath(dir), cdir); + SystemTools::AddTranslationPath(cdir, dir); +} + +void SystemTools::CheckTranslationPath(std::string& path) +{ + // Do not translate paths that are too short to have meaningful + // translations. + if (path.size() < 2) { + return; + } + + // Always add a trailing slash before translation. It does not + // matter if this adds an extra slash, but we do not want to + // translate part of a directory (like the foo part of foo-dir). + path += '/'; + + // In case a file was specified we still have to go through this: + // Now convert any path found in the table back to the one desired: + for (auto const& pair : SystemTools::Statics->TranslationMap) { + // We need to check of the path is a substring of the other path + if (path.find(pair.first) == 0) { + path = path.replace(0, pair.first.size(), pair.second); + } + } + + // Remove the trailing slash we added before. + path.pop_back(); +} +#endif + +static void SystemToolsAppendComponents( + std::vector<std::string>& out_components, + std::vector<std::string>::iterator first, + std::vector<std::string>::iterator last) +{ + static const std::string up = ".."; + static const std::string cur = "."; + for (std::vector<std::string>::const_iterator i = first; i != last; ++i) { + if (*i == up) { + // Remove the previous component if possible. Ignore ../ components + // that try to go above the root. Keep ../ components if they are + // at the beginning of a relative path (base path is relative). + if (out_components.size() > 1 && out_components.back() != up) { + out_components.resize(out_components.size() - 1); + } else if (!out_components.empty() && out_components[0].empty()) { + out_components.emplace_back(std::move(*i)); + } + } else if (!i->empty() && *i != cur) { + out_components.emplace_back(std::move(*i)); + } + } +} + +std::string SystemTools::CollapseFullPath(const std::string& in_path, + const char* in_base) +{ + // Use the current working directory as a base path. + char buf[2048]; + const char* res_in_base = in_base; + if (!res_in_base) { + if (const char* cwd = Getcwd(buf, 2048)) { + res_in_base = cwd; + } else { + res_in_base = ""; + } + } + + return SystemTools::CollapseFullPath(in_path, std::string(res_in_base)); +} + +std::string SystemTools::CollapseFullPath(const std::string& in_path, + const std::string& in_base) +{ + // Collect the output path components. + std::vector<std::string> out_components; + + // Split the input path components. + std::vector<std::string> path_components; + SystemTools::SplitPath(in_path, path_components); + out_components.reserve(path_components.size()); + + // If the input path is relative, start with a base path. + if (path_components[0].empty()) { + std::vector<std::string> base_components; + // Use the given base path. + SystemTools::SplitPath(in_base, base_components); + + // Append base path components to the output path. + out_components.push_back(base_components[0]); + SystemToolsAppendComponents(out_components, base_components.begin() + 1, + base_components.end()); + } + + // Append input path components to the output path. + SystemToolsAppendComponents(out_components, path_components.begin(), + path_components.end()); + + // Transform the path back to a string. + std::string newPath = SystemTools::JoinPath(out_components); + +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP + // Update the translation table with this potentially new path. I am not + // sure why this line is here, it seems really questionable, but yet I + // would put good money that if I remove it something will break, basically + // from what I can see it created a mapping from the collapsed path, to be + // replaced by the input path, which almost completely does the opposite of + // this function, the only thing preventing this from happening a lot is + // that if the in_path has a .. in it, then it is not added to the + // translation table. So for most calls this either does nothing due to the + // .. or it adds a translation between identical paths as nothing was + // collapsed, so I am going to try to comment it out, and see what hits the + // fan, hopefully quickly. + // Commented out line below: + // SystemTools::AddTranslationPath(newPath, in_path); + + SystemTools::CheckTranslationPath(newPath); +#endif +#ifdef _WIN32 + newPath = SystemTools::Statics->GetActualCaseForPathCached(newPath); + SystemTools::ConvertToUnixSlashes(newPath); +#endif + // Return the reconstructed path. + return newPath; +} + +// compute the relative path from here to there +std::string SystemTools::RelativePath(const std::string& local, + const std::string& remote) +{ + if (!SystemTools::FileIsFullPath(local)) { + return ""; + } + if (!SystemTools::FileIsFullPath(remote)) { + return ""; + } + + std::string l = SystemTools::CollapseFullPath(local); + std::string r = SystemTools::CollapseFullPath(remote); + + // split up both paths into arrays of strings using / as a separator + std::vector<std::string> localSplit = SystemTools::SplitString(l, '/', true); + std::vector<std::string> remoteSplit = + SystemTools::SplitString(r, '/', true); + std::vector<std::string> + commonPath; // store shared parts of path in this array + std::vector<std::string> finalPath; // store the final relative path here + // count up how many matching directory names there are from the start + unsigned int sameCount = 0; + while (((sameCount <= (localSplit.size() - 1)) && + (sameCount <= (remoteSplit.size() - 1))) && +// for Windows and Apple do a case insensitive string compare +#if defined(_WIN32) || defined(__APPLE__) + SystemTools::Strucmp(localSplit[sameCount].c_str(), + remoteSplit[sameCount].c_str()) == 0 +#else + localSplit[sameCount] == remoteSplit[sameCount] +#endif + ) { + // put the common parts of the path into the commonPath array + commonPath.push_back(localSplit[sameCount]); + // erase the common parts of the path from the original path arrays + localSplit[sameCount] = ""; + remoteSplit[sameCount] = ""; + sameCount++; + } + + // If there is nothing in common at all then just return the full + // path. This is the case only on windows when the paths have + // different drive letters. On unix two full paths always at least + // have the root "/" in common so we will return a relative path + // that passes through the root directory. + if (sameCount == 0) { + return remote; + } + + // for each entry that is not common in the local path + // add a ../ to the finalpath array, this gets us out of the local + // path into the remote dir + for (std::string const& lp : localSplit) { + if (!lp.empty()) { + finalPath.emplace_back("../"); + } + } + // for each entry that is not common in the remote path add it + // to the final path. + for (std::string const& rp : remoteSplit) { + if (!rp.empty()) { + finalPath.push_back(rp); + } + } + std::string relativePath; // result string + // now turn the array of directories into a unix path by puttint / + // between each entry that does not already have one + for (std::string const& fp : finalPath) { + if (!relativePath.empty() && relativePath.back() != '/') { + relativePath += '/'; + } + relativePath += fp; + } + return relativePath; +} + +std::string SystemTools::GetActualCaseForPath(const std::string& p) +{ +#ifdef _WIN32 + return SystemToolsStatic::GetCasePathName(p); +#else + return p; +#endif +} + +const char* SystemTools::SplitPathRootComponent(const std::string& p, + std::string* root) +{ + // Identify the root component. + const char* c = p.c_str(); + if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) { + // Network path. + if (root) { + *root = "//"; + } + c += 2; + } else if (c[0] == '/' || c[0] == '\\') { + // Unix path (or Windows path w/out drive letter). + if (root) { + *root = "/"; + } + c += 1; + } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) { + // Windows path. + if (root) { + (*root) = "_:/"; + (*root)[0] = c[0]; + } + c += 3; + } else if (c[0] && c[1] == ':') { + // Path relative to a windows drive working directory. + if (root) { + (*root) = "_:"; + (*root)[0] = c[0]; + } + c += 2; + } else if (c[0] == '~') { + // Home directory. The returned root should always have a + // trailing slash so that appending components as + // c[0]c[1]/c[2]/... works. The remaining path returned should + // skip the first slash if it exists: + // + // "~" : root = "~/" , return "" + // "~/ : root = "~/" , return "" + // "~/x : root = "~/" , return "x" + // "~u" : root = "~u/", return "" + // "~u/" : root = "~u/", return "" + // "~u/x" : root = "~u/", return "x" + size_t n = 1; + while (c[n] && c[n] != '/') { + ++n; + } + if (root) { + root->assign(c, n); + *root += '/'; + } + if (c[n] == '/') { + ++n; + } + c += n; + } else { + // Relative path. + if (root) { + *root = ""; + } + } + + // Return the remaining path. + return c; +} + +void SystemTools::SplitPath(const std::string& p, + std::vector<std::string>& components, + bool expand_home_dir) +{ + const char* c; + components.clear(); + + // Identify the root component. + { + std::string root; + c = SystemTools::SplitPathRootComponent(p, &root); + + // Expand home directory references if requested. + if (expand_home_dir && !root.empty() && root[0] == '~') { + std::string homedir; + root = root.substr(0, root.size() - 1); + if (root.size() == 1) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!SystemTools::GetEnv("USERPROFILE", homedir)) +#endif + SystemTools::GetEnv("HOME", homedir); + } +#ifdef HAVE_GETPWNAM + else if (passwd* pw = getpwnam(root.c_str() + 1)) { + if (pw->pw_dir) { + homedir = pw->pw_dir; + } + } +#endif + if (!homedir.empty() && + (homedir.back() == '/' || homedir.back() == '\\')) { + homedir.resize(homedir.size() - 1); + } + SystemTools::SplitPath(homedir, components); + } else { + components.push_back(root); + } + } + + // Parse the remaining components. + const char* first = c; + const char* last = first; + for (; *last; ++last) { + if (*last == '/' || *last == '\\') { + // End of a component. Save it. + components.emplace_back(first, last); + first = last + 1; + } + } + + // Save the last component unless there were no components. + if (last != c) { + components.emplace_back(first, last); + } +} + +std::string SystemTools::JoinPath(const std::vector<std::string>& components) +{ + return SystemTools::JoinPath(components.begin(), components.end()); +} + +std::string SystemTools::JoinPath( + std::vector<std::string>::const_iterator first, + std::vector<std::string>::const_iterator last) +{ + // Construct result in a single string. + std::string result; + size_t len = 0; + for (auto i = first; i != last; ++i) { + len += 1 + i->size(); + } + result.reserve(len); + + // The first two components do not add a slash. + if (first != last) { + result.append(*first++); + } + if (first != last) { + result.append(*first++); + } + + // All remaining components are always separated with a slash. + while (first != last) { + result.push_back('/'); + result.append((*first++)); + } + + // Return the concatenated result. + return result; +} + +bool SystemTools::ComparePath(const std::string& c1, const std::string& c2) +{ +#if defined(_WIN32) || defined(__APPLE__) +# ifdef _MSC_VER + return _stricmp(c1.c_str(), c2.c_str()) == 0; +# elif defined(__APPLE__) || defined(__GNUC__) + return strcasecmp(c1.c_str(), c2.c_str()) == 0; +# else + return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0; +# endif +#else + return c1 == c2; +#endif +} + +bool SystemTools::Split(const std::string& str, + std::vector<std::string>& lines, char separator) +{ + std::string data(str); + std::string::size_type lpos = 0; + while (lpos < data.length()) { + std::string::size_type rpos = data.find_first_of(separator, lpos); + if (rpos == std::string::npos) { + // String ends at end of string without a separator. + lines.push_back(data.substr(lpos)); + return false; + } else { + // String ends in a separator, remove the character. + lines.push_back(data.substr(lpos, rpos - lpos)); + } + lpos = rpos + 1; + } + return true; +} + +bool SystemTools::Split(const std::string& str, + std::vector<std::string>& lines) +{ + std::string data(str); + std::string::size_type lpos = 0; + while (lpos < data.length()) { + std::string::size_type rpos = data.find_first_of('\n', lpos); + if (rpos == std::string::npos) { + // Line ends at end of string without a newline. + lines.push_back(data.substr(lpos)); + return false; + } + if ((rpos > lpos) && (data[rpos - 1] == '\r')) { + // Line ends in a "\r\n" pair, remove both characters. + lines.push_back(data.substr(lpos, (rpos - 1) - lpos)); + } else { + // Line ends in a "\n", remove the character. + lines.push_back(data.substr(lpos, rpos - lpos)); + } + lpos = rpos + 1; + } + return true; +} + +/** + * Return path of a full filename (no trailing slashes). + * Warning: returned path is converted to Unix slashes format. + */ +std::string SystemTools::GetFilenamePath(const std::string& filename) +{ + std::string fn = filename; + SystemTools::ConvertToUnixSlashes(fn); + + std::string::size_type slash_pos = fn.rfind("/"); + if (slash_pos != std::string::npos) { + std::string ret = fn.substr(0, slash_pos); + if (ret.size() == 2 && ret[1] == ':') { + return ret + '/'; + } + if (ret.empty()) { + return "/"; + } + return ret; + } else { + return ""; + } +} + +/** + * Return file name of a full filename (i.e. file name without path). + */ +std::string SystemTools::GetFilenameName(const std::string& filename) +{ +#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES) + const char* separators = "/\\"; +#else + char separators = '/'; +#endif + std::string::size_type slash_pos = filename.find_last_of(separators); + if (slash_pos != std::string::npos) { + return filename.substr(slash_pos + 1); + } else { + return filename; + } +} + +/** + * Return file extension of a full filename (dot included). + * Warning: this is the longest extension (for example: .tar.gz) + */ +std::string SystemTools::GetFilenameExtension(const std::string& filename) +{ + std::string name = SystemTools::GetFilenameName(filename); + std::string::size_type dot_pos = name.find('.'); + if (dot_pos != std::string::npos) { + return name.substr(dot_pos); + } else { + return ""; + } +} + +/** + * Return file extension of a full filename (dot included). + * Warning: this is the shortest extension (for example: .gz of .tar.gz) + */ +std::string SystemTools::GetFilenameLastExtension(const std::string& filename) +{ + std::string name = SystemTools::GetFilenameName(filename); + std::string::size_type dot_pos = name.rfind('.'); + if (dot_pos != std::string::npos) { + return name.substr(dot_pos); + } else { + return ""; + } +} + +/** + * Return file name without extension of a full filename (i.e. without path). + * Warning: it considers the longest extension (for example: .tar.gz) + */ +std::string SystemTools::GetFilenameWithoutExtension( + const std::string& filename) +{ + std::string name = SystemTools::GetFilenameName(filename); + std::string::size_type dot_pos = name.find('.'); + if (dot_pos != std::string::npos) { + return name.substr(0, dot_pos); + } else { + return name; + } +} + +/** + * Return file name without extension of a full filename (i.e. without path). + * Warning: it considers the last extension (for example: removes .gz + * from .tar.gz) + */ +std::string SystemTools::GetFilenameWithoutLastExtension( + const std::string& filename) +{ + std::string name = SystemTools::GetFilenameName(filename); + std::string::size_type dot_pos = name.rfind('.'); + if (dot_pos != std::string::npos) { + return name.substr(0, dot_pos); + } else { + return name; + } +} + +bool SystemTools::FileHasSignature(const char* filename, const char* signature, + long offset) +{ + if (!filename || !signature) { + return false; + } + + FILE* fp = Fopen(filename, "rb"); + if (!fp) { + return false; + } + + fseek(fp, offset, SEEK_SET); + + bool res = false; + size_t signature_len = strlen(signature); + char* buffer = new char[signature_len]; + + if (fread(buffer, 1, signature_len, fp) == signature_len) { + res = (!strncmp(buffer, signature, signature_len) ? true : false); + } + + delete[] buffer; + + fclose(fp); + return res; +} + +SystemTools::FileTypeEnum SystemTools::DetectFileType(const char* filename, + unsigned long length, + double percent_bin) +{ + if (!filename || percent_bin < 0) { + return SystemTools::FileTypeUnknown; + } + + if (SystemTools::FileIsDirectory(filename)) { + return SystemTools::FileTypeUnknown; + } + + FILE* fp = Fopen(filename, "rb"); + if (!fp) { + return SystemTools::FileTypeUnknown; + } + + // Allocate buffer and read bytes + + auto* buffer = new unsigned char[length]; + size_t read_length = fread(buffer, 1, length, fp); + fclose(fp); + if (read_length == 0) { + delete[] buffer; + return SystemTools::FileTypeUnknown; + } + + // Loop over contents and count + + size_t text_count = 0; + + const unsigned char* ptr = buffer; + const unsigned char* buffer_end = buffer + read_length; + + while (ptr != buffer_end) { + if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' || + *ptr == '\t') { + text_count++; + } + ptr++; + } + + delete[] buffer; + + double current_percent_bin = (static_cast<double>(read_length - text_count) / + static_cast<double>(read_length)); + + if (current_percent_bin >= percent_bin) { + return SystemTools::FileTypeBinary; + } + + return SystemTools::FileTypeText; +} + +bool SystemTools::LocateFileInDir(const char* filename, const char* dir, + std::string& filename_found, + int try_filename_dirs) +{ + if (!filename || !dir) { + return false; + } + + // Get the basename of 'filename' + + std::string filename_base = SystemTools::GetFilenameName(filename); + + // Check if 'dir' is really a directory + // If win32 and matches something like C:, accept it as a dir + + std::string real_dir; + if (!SystemTools::FileIsDirectory(dir)) { +#if defined(_WIN32) + size_t dir_len = strlen(dir); + if (dir_len < 2 || dir[dir_len - 1] != ':') { +#endif + real_dir = SystemTools::GetFilenamePath(dir); + dir = real_dir.c_str(); +#if defined(_WIN32) + } +#endif + } + + // Try to find the file in 'dir' + + bool res = false; + if (!filename_base.empty() && dir) { + size_t dir_len = strlen(dir); + int need_slash = + (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\'); + + std::string temp = dir; + if (need_slash) { + temp += "/"; + } + temp += filename_base; + + if (SystemTools::FileExists(temp)) { + res = true; + filename_found = temp; + } + + // If not found, we can try harder by appending part of the file to + // to the directory to look inside. + // Example: if we were looking for /foo/bar/yo.txt in /d1/d2, then + // try to find yo.txt in /d1/d2/bar, then /d1/d2/foo/bar, etc. + + else if (try_filename_dirs) { + std::string filename_dir(filename); + std::string filename_dir_base; + std::string filename_dir_bases; + do { + filename_dir = SystemTools::GetFilenamePath(filename_dir); + filename_dir_base = SystemTools::GetFilenameName(filename_dir); +#if defined(_WIN32) + if (filename_dir_base.empty() || filename_dir_base.back() == ':') +#else + if (filename_dir_base.empty()) +#endif + { + break; + } + + filename_dir_bases = filename_dir_base + "/" + filename_dir_bases; + + temp = dir; + if (need_slash) { + temp += "/"; + } + temp += filename_dir_bases; + + res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(), + filename_found, 0); + + } while (!res && !filename_dir_base.empty()); + } + } + + return res; +} + +bool SystemTools::FileIsFullPath(const std::string& in_name) +{ + return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size()); +} + +bool SystemTools::FileIsFullPath(const char* in_name) +{ + return SystemToolsStatic::FileIsFullPath( + in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0); +} + +bool SystemToolsStatic::FileIsFullPath(const char* in_name, size_t len) +{ +#if defined(_WIN32) || defined(__CYGWIN__) + // On Windows, the name must be at least two characters long. + if (len < 2) { + return false; + } + if (in_name[1] == ':') { + return true; + } + if (in_name[0] == '\\') { + return true; + } +#else + // On UNIX, the name must be at least one character long. + if (len < 1) { + return false; + } +#endif +#if !defined(_WIN32) + if (in_name[0] == '~') { + return true; + } +#endif + // On UNIX, the name must begin in a '/'. + // On Windows, if the name begins in a '/', then it is a full + // network path. + if (in_name[0] == '/') { + return true; + } + return false; +} + +bool SystemTools::GetShortPath(const std::string& path, std::string& shortPath) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + std::string tempPath = path; // create a buffer + + // if the path passed in has quotes around it, first remove the quotes + if (!path.empty() && path[0] == '"' && path.back() == '"') { + tempPath = path.substr(1, path.length() - 2); + } + + std::wstring wtempPath = Encoding::ToWide(tempPath); + DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0); + std::vector<wchar_t> buffer(ret); + if (ret != 0) { + ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0], + static_cast<DWORD>(buffer.size())); + } + + if (ret == 0) { + return false; + } else { + shortPath = Encoding::ToNarrow(&buffer[0]); + return true; + } +#else + shortPath = path; + return true; +#endif +} + +std::string SystemTools::GetCurrentDateTime(const char* format) +{ + char buf[1024]; + time_t t; + time(&t); + strftime(buf, sizeof(buf), format, localtime(&t)); + return std::string(buf); +} + +std::string SystemTools::MakeCidentifier(const std::string& s) +{ + std::string str(s); + if (str.find_first_of("0123456789") == 0) { + str = "_" + str; + } + + std::string permited_chars("_" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"); + std::string::size_type pos = 0; + while ((pos = str.find_first_not_of(permited_chars, pos)) != + std::string::npos) { + str[pos] = '_'; + } + return str; +} + +// Convenience function around std::getline which removes a trailing carriage +// return and can truncate the buffer as needed. Returns true +// if any data were read before the end-of-file was reached. +bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, + bool* has_newline /* = 0 */, + long sizeLimit /* = -1 */) +{ + // Start with an empty line. + line = ""; + + // Early short circuit return if stream is no good. Just return + // false and the empty line. (Probably means caller tried to + // create a file stream with a non-existent file name...) + // + if (!is) { + if (has_newline) { + *has_newline = false; + } + return false; + } + + std::getline(is, line); + bool haveData = !line.empty() || !is.eof(); + if (!line.empty()) { + // Avoid storing a carriage return character. + if (line.back() == '\r') { + line.resize(line.size() - 1); + } + + // if we read too much then truncate the buffer + if (sizeLimit >= 0 && line.size() >= static_cast<size_t>(sizeLimit)) { + line.resize(sizeLimit); + } + } + + // Return the results. + if (has_newline) { + *has_newline = !is.eof(); + } + return haveData; +} + +int SystemTools::GetTerminalWidth() +{ + int width = -1; +#ifdef HAVE_TTY_INFO + struct winsize ws; + std::string columns; /* Unix98 environment variable */ + if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) { + width = ws.ws_col; + } + if (!isatty(STDOUT_FILENO)) { + width = -1; + } + if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) { + long t; + char* endptr; + t = strtol(columns.c_str(), &endptr, 0); + if (endptr && !*endptr && (t > 0) && (t < 1000)) { + width = static_cast<int>(t); + } + } + if (width < 9) { + width = -1; + } +#endif + return width; +} + +bool SystemTools::GetPermissions(const char* file, mode_t& mode) +{ + if (!file) { + return false; + } + return SystemTools::GetPermissions(std::string(file), mode); +} + +bool SystemTools::GetPermissions(const std::string& file, mode_t& mode) +{ +#if defined(_WIN32) + DWORD attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { + return false; + } + if ((attr & FILE_ATTRIBUTE_READONLY) != 0) { + mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6)); + } else { + mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) | + (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6)); + } + if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6)); + } else { + mode |= S_IFREG; + } + size_t dotPos = file.rfind('.'); + const char* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos); + if (ext && + (Strucmp(ext, ".exe") == 0 || Strucmp(ext, ".com") == 0 || + Strucmp(ext, ".cmd") == 0 || Strucmp(ext, ".bat") == 0)) { + mode |= (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6)); + } +#else + struct stat st; + if (stat(file.c_str(), &st) < 0) { + return false; + } + mode = st.st_mode; +#endif + return true; +} + +bool SystemTools::SetPermissions(const char* file, mode_t mode, + bool honor_umask) +{ + if (!file) { + return false; + } + return SystemTools::SetPermissions(std::string(file), mode, honor_umask); +} + +bool SystemTools::SetPermissions(const std::string& file, mode_t mode, + bool honor_umask) +{ + if (!SystemTools::PathExists(file)) { + return false; + } + if (honor_umask) { + mode_t currentMask = umask(0); + umask(currentMask); + mode &= ~currentMask; + } +#ifdef _WIN32 + if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0) +#else + if (chmod(file.c_str(), mode) < 0) +#endif + { + return false; + } + + return true; +} + +std::string SystemTools::GetParentDirectory(const std::string& fileOrDir) +{ + return SystemTools::GetFilenamePath(fileOrDir); +} + +bool SystemTools::IsSubDirectory(const std::string& cSubdir, + const std::string& cDir) +{ + if (cDir.empty()) { + return false; + } + std::string subdir = cSubdir; + std::string dir = cDir; + SystemTools::ConvertToUnixSlashes(subdir); + SystemTools::ConvertToUnixSlashes(dir); + if (subdir.size() <= dir.size() || dir.empty()) { + return false; + } + bool isRootPath = dir.back() == '/'; // like "/" or "C:/" + size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size(); + if (subdir[expectedSlashPosition] != '/') { + return false; + } + std::string s = subdir.substr(0, dir.size()); + return SystemTools::ComparePath(s, dir); +} + +void SystemTools::Delay(unsigned int msec) +{ +#ifdef _WIN32 + Sleep(msec); +#else + // The sleep function gives 1 second resolution and the usleep + // function gives 1e-6 second resolution but on some platforms has a + // maximum sleep time of 1 second. This could be re-implemented to + // use select with masked signals or pselect to mask signals + // atomically. If select is given empty sets and zero as the max + // file descriptor but a non-zero timeout it can be used to block + // for a precise amount of time. + if (msec >= 1000) { + sleep(msec / 1000); + usleep((msec % 1000) * 1000); + } else { + usleep(msec * 1000); + } +#endif +} + +std::string SystemTools::GetOperatingSystemNameAndVersion() +{ + std::string res; + +#ifdef _WIN32 + char buffer[256]; + + OSVERSIONINFOEXA osvi; + BOOL bOsVersionInfoEx; + + ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +# endif + bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi); + if (!bOsVersionInfoEx) { + return 0; + } +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +# endif + + switch (osvi.dwPlatformId) { + // Test for the Windows NT product family. + + case VER_PLATFORM_WIN32_NT: + + // Test for the specific product family. + if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + res += "Microsoft Windows 10"; + } else { + res += "Microsoft Windows Server 2016 family"; + } + } + + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + res += "Microsoft Windows 8.1"; + } else { + res += "Microsoft Windows Server 2012 R2 family"; + } + } + + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + res += "Microsoft Windows 8"; + } else { + res += "Microsoft Windows Server 2012 family"; + } + } + + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + res += "Microsoft Windows 7"; + } else { + res += "Microsoft Windows Server 2008 R2 family"; + } + } + + if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + res += "Microsoft Windows Vista"; + } else { + res += "Microsoft Windows Server 2008 family"; + } + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { + res += "Microsoft Windows Server 2003 family"; + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + res += "Microsoft Windows XP"; + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { + res += "Microsoft Windows 2000"; + } + + if (osvi.dwMajorVersion <= 4) { + res += "Microsoft Windows NT"; + } + + // Test for specific product on Windows NT 4.0 SP6 and later. + + if (bOsVersionInfoEx) { + // Test for the workstation type. + + if (osvi.wProductType == VER_NT_WORKSTATION) { + if (osvi.dwMajorVersion == 4) { + res += " Workstation 4.0"; + } else if (osvi.dwMajorVersion == 5) { + if (osvi.wSuiteMask & VER_SUITE_PERSONAL) { + res += " Home Edition"; + } else { + res += " Professional"; + } + } + } + + // Test for the server type. + + else if (osvi.wProductType == VER_NT_SERVER) { + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { + res += " Datacenter Edition"; + } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { + res += " Enterprise Edition"; + } else if (osvi.wSuiteMask == VER_SUITE_BLADE) { + res += " Web Edition"; + } else { + res += " Standard Edition"; + } + } + + else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { + res += " Datacenter Server"; + } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { + res += " Advanced Server"; + } else { + res += " Server"; + } + } + + else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0 + { + if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { + res += " Server 4.0, Enterprise Edition"; + } else { + res += " Server 4.0"; + } + } + } + } + + // Test for specific product on Windows NT 4.0 SP5 and earlier + + else { + HKEY hKey; +# define BUFSIZE 80 + wchar_t szProductType[BUFSIZE]; + DWORD dwBufLen = BUFSIZE; + LONG lRet; + + lRet = + RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_QUERY_VALUE, &hKey); + if (lRet != ERROR_SUCCESS) { + return 0; + } + + lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr, + (LPBYTE)szProductType, &dwBufLen); + + if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) { + return 0; + } + + RegCloseKey(hKey); + + if (lstrcmpiW(L"WINNT", szProductType) == 0) { + res += " Workstation"; + } + if (lstrcmpiW(L"LANMANNT", szProductType) == 0) { + res += " Server"; + } + if (lstrcmpiW(L"SERVERNT", szProductType) == 0) { + res += " Advanced Server"; + } + + res += " "; + sprintf(buffer, "%ld", osvi.dwMajorVersion); + res += buffer; + res += "."; + sprintf(buffer, "%ld", osvi.dwMinorVersion); + res += buffer; + } + + // Display service pack (if any) and build number. + + if (osvi.dwMajorVersion == 4 && + lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) { + HKEY hKey; + LONG lRet; + + // Test for SP6 versus SP6a. + + lRet = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009", + 0, KEY_QUERY_VALUE, &hKey); + + if (lRet == ERROR_SUCCESS) { + res += " Service Pack 6a (Build "; + sprintf(buffer, "%ld", osvi.dwBuildNumber & 0xFFFF); + res += buffer; + res += ")"; + } else // Windows NT 4.0 prior to SP6a + { + res += " "; + res += osvi.szCSDVersion; + res += " (Build "; + sprintf(buffer, "%ld", osvi.dwBuildNumber & 0xFFFF); + res += buffer; + res += ")"; + } + + RegCloseKey(hKey); + } else // Windows NT 3.51 and earlier or Windows 2000 and later + { + res += " "; + res += osvi.szCSDVersion; + res += " (Build "; + sprintf(buffer, "%ld", osvi.dwBuildNumber & 0xFFFF); + res += buffer; + res += ")"; + } + + break; + + // Test for the Windows 95 product family. + + case VER_PLATFORM_WIN32_WINDOWS: + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) { + res += "Microsoft Windows 95"; + if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') { + res += " OSR2"; + } + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) { + res += "Microsoft Windows 98"; + if (osvi.szCSDVersion[1] == 'A') { + res += " SE"; + } + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) { + res += "Microsoft Windows Millennium Edition"; + } + break; + + case VER_PLATFORM_WIN32s: + + res += "Microsoft Win32s"; + break; + } +#endif + + return res; +} + +bool SystemTools::ParseURLProtocol(const std::string& URL, + std::string& protocol, + std::string& dataglom, bool decode) +{ + // match 0 entire url + // match 1 protocol + // match 2 dataglom following protocol:// + kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX); + + if (!urlRe.find(URL)) + return false; + + protocol = urlRe.match(1); + dataglom = urlRe.match(2); + + if (decode) { + dataglom = DecodeURL(dataglom); + } + + return true; +} + +bool SystemTools::ParseURL(const std::string& URL, std::string& protocol, + std::string& username, std::string& password, + std::string& hostname, std::string& dataport, + std::string& database, bool decode) +{ + kwsys::RegularExpression urlRe(VTK_URL_REGEX); + if (!urlRe.find(URL)) + return false; + + // match 0 URL + // match 1 protocol + // match 2 mangled user + // match 3 username + // match 4 mangled password + // match 5 password + // match 6 hostname + // match 7 mangled port + // match 8 dataport + // match 9 database name + + protocol = urlRe.match(1); + username = urlRe.match(3); + password = urlRe.match(5); + hostname = urlRe.match(6); + dataport = urlRe.match(8); + database = urlRe.match(9); + + if (decode) { + username = DecodeURL(username); + password = DecodeURL(password); + hostname = DecodeURL(hostname); + dataport = DecodeURL(dataport); + database = DecodeURL(database); + } + + return true; +} + +// ---------------------------------------------------------------------- +std::string SystemTools::DecodeURL(const std::string& url) +{ + kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX); + std::string ret; + for (size_t i = 0; i < url.length(); i++) { + if (urlByteRe.find(url.substr(i, 3))) { + ret += + static_cast<char>(strtoul(url.substr(i + 1, 2).c_str(), nullptr, 16)); + i += 2; + } else { + ret += url[i]; + } + } + return ret; +} + +// ---------------------------------------------------------------------- +// These must NOT be initialized. Default initialization to zero is +// necessary. +static unsigned int SystemToolsManagerCount; +SystemToolsStatic* SystemTools::Statics; + +// SystemToolsManager manages the SystemTools singleton. +// SystemToolsManager should be included in any translation unit +// that will use SystemTools or that implements the singleton +// pattern. It makes sure that the SystemTools singleton is created +// before and destroyed after all other singletons in CMake. + +SystemToolsManager::SystemToolsManager() +{ + if (++SystemToolsManagerCount == 1) { + SystemTools::ClassInitialize(); + } +} + +SystemToolsManager::~SystemToolsManager() +{ + if (--SystemToolsManagerCount == 0) { + SystemTools::ClassFinalize(); + } +} + +#if defined(__VMS) +// On VMS we configure the run time C library to be more UNIX like. +// http://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html +extern "C" int decc$feature_get_index(char* name); +extern "C" int decc$feature_set_value(int index, int mode, int value); +static int SetVMSFeature(char* name, int value) +{ + int i; + errno = 0; + i = decc$feature_get_index(name); + return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0); +} +#endif + +void SystemTools::ClassInitialize() +{ +#ifdef __VMS + SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1); +#endif + + // Create statics singleton instance + SystemTools::Statics = new SystemToolsStatic; + +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP +// Add some special translation paths for unix. These are not added +// for windows because drive letters need to be maintained. Also, +// there are not sym-links and mount points on windows anyway. +# if !defined(_WIN32) || defined(__CYGWIN__) + // The tmp path is frequently a logical path so always keep it: + SystemTools::AddKeepPath("/tmp/"); + + // If the current working directory is a logical path then keep the + // logical name. + std::string pwd_str; + if (SystemTools::GetEnv("PWD", pwd_str)) { + char buf[2048]; + if (const char* cwd = Getcwd(buf, 2048)) { + // The current working directory may be a logical path. Find + // the shortest logical path that still produces the correct + // physical path. + std::string cwd_changed; + std::string pwd_changed; + + // Test progressively shorter logical-to-physical mappings. + std::string cwd_str = cwd; + std::string pwd_path; + Realpath(pwd_str, pwd_path); + while (cwd_str == pwd_path && cwd_str != pwd_str) { + // The current pair of paths is a working logical mapping. + cwd_changed = cwd_str; + pwd_changed = pwd_str; + + // Strip off one directory level and see if the logical + // mapping still works. + pwd_str = SystemTools::GetFilenamePath(pwd_str); + cwd_str = SystemTools::GetFilenamePath(cwd_str); + Realpath(pwd_str, pwd_path); + } + + // Add the translation to keep the logical path name. + if (!cwd_changed.empty() && !pwd_changed.empty()) { + SystemTools::AddTranslationPath(cwd_changed, pwd_changed); + } + } + } +# endif +#endif +} + +void SystemTools::ClassFinalize() +{ + delete SystemTools::Statics; +} + +} // namespace KWSYS_NAMESPACE + +#if defined(_MSC_VER) && defined(_DEBUG) +# include <crtdbg.h> +# include <stdio.h> +# include <stdlib.h> +namespace KWSYS_NAMESPACE { + +static int SystemToolsDebugReport(int, char* message, int* ret) +{ + if (ret) { + // Pretend user clicked on Retry button in popup. + *ret = 1; + } + fprintf(stderr, "%s", message); + fflush(stderr); + return 1; // no further reporting required +} + +void SystemTools::EnableMSVCDebugHook() +{ + if (SystemTools::HasEnv("DART_TEST_FROM_DART") || + SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) { + _CrtSetReportHook(SystemToolsDebugReport); + } +} + +} // namespace KWSYS_NAMESPACE +#else +namespace KWSYS_NAMESPACE { +void SystemTools::EnableMSVCDebugHook() +{ +} +} // namespace KWSYS_NAMESPACE +#endif diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in new file mode 100644 index 0000000..d4a93fa --- /dev/null +++ b/Source/kwsys/SystemTools.hxx.in @@ -0,0 +1,991 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_SystemTools_hxx +#define @KWSYS_NAMESPACE@_SystemTools_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <iosfwd> +#include <map> +#include <string> +#include <vector> + +#include <sys/types.h> +// include sys/stat.h after sys/types.h +#include <sys/stat.h> + +#if !defined(_WIN32) || defined(__CYGWIN__) +# include <unistd.h> // For access permissions for use with access() +#endif + +// Required for va_list +#include <stdarg.h> +// Required for FILE* +#include <stdio.h> +#if !defined(va_list) +// Some compilers move va_list into the std namespace and there is no way to +// tell that this has been done. Playing with things being included before or +// after stdarg.h does not solve things because we do not have control over +// what the user does. This hack solves this problem by moving va_list to our +// own namespace that is local for kwsys. +namespace std { +} // Required for platforms that do not have std namespace +namespace @KWSYS_NAMESPACE@_VA_LIST { +using namespace std; +typedef va_list hack_va_list; +} +namespace @KWSYS_NAMESPACE@ { +typedef @KWSYS_NAMESPACE@_VA_LIST::hack_va_list va_list; +} +#endif // va_list + +namespace @KWSYS_NAMESPACE@ { + +class SystemToolsStatic; + +/** \class SystemToolsManager + * \brief Use to make sure SystemTools is initialized before it is used + * and is the last static object destroyed + */ +class @KWSYS_NAMESPACE@_EXPORT SystemToolsManager +{ +public: + SystemToolsManager(); + ~SystemToolsManager(); + + SystemToolsManager(const SystemToolsManager&) = delete; + SystemToolsManager& operator=(const SystemToolsManager&) = delete; +}; + +// This instance will show up in any translation unit that uses +// SystemTools. It will make sure SystemTools is initialized +// before it is used and is the last static object destroyed. +static SystemToolsManager SystemToolsManagerInstance; + +// Flags for use with TestFileAccess. Use a typedef in case any operating +// system in the future needs a special type. These are flags that may be +// combined using the | operator. +typedef int TestFilePermissions; +#if defined(_WIN32) && !defined(__CYGWIN__) +// On Windows (VC and Borland), no system header defines these constants... +static const TestFilePermissions TEST_FILE_OK = 0; +static const TestFilePermissions TEST_FILE_READ = 4; +static const TestFilePermissions TEST_FILE_WRITE = 2; +static const TestFilePermissions TEST_FILE_EXECUTE = 1; +#else +// Standard POSIX constants +static const TestFilePermissions TEST_FILE_OK = F_OK; +static const TestFilePermissions TEST_FILE_READ = R_OK; +static const TestFilePermissions TEST_FILE_WRITE = W_OK; +static const TestFilePermissions TEST_FILE_EXECUTE = X_OK; +#endif + +/** \class SystemTools + * \brief A collection of useful platform-independent system functions. + */ +class @KWSYS_NAMESPACE@_EXPORT SystemTools +{ +public: + /** ----------------------------------------------------------------- + * String Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Replace symbols in str that are not valid in C identifiers as + * defined by the 1999 standard, ie. anything except [A-Za-z0-9_]. + * They are replaced with `_' and if the first character is a digit + * then an underscore is prepended. Note that this can produce + * identifiers that the standard reserves (_[A-Z].* and __.*). + */ + static std::string MakeCidentifier(const std::string& s); + + static std::string MakeCindentifier(const std::string& s) + { + return MakeCidentifier(s); + } + + /** + * Replace replace all occurrences of the string in the source string. + */ + static void ReplaceString(std::string& source, const char* replace, + const char* with); + static void ReplaceString(std::string& source, const std::string& replace, + const std::string& with); + + /** + * Return a capitalized string (i.e the first letter is uppercased, + * all other are lowercased). + */ + static std::string Capitalized(const std::string&); + + /** + * Return a 'capitalized words' string (i.e the first letter of each word + * is uppercased all other are left untouched though). + */ + static std::string CapitalizedWords(const std::string&); + + /** + * Return a 'uncapitalized words' string (i.e the first letter of each word + * is lowercased all other are left untouched though). + */ + static std::string UnCapitalizedWords(const std::string&); + + /** + * Return a lower case string + */ + static std::string LowerCase(const std::string&); + + /** + * Return a lower case string + */ + static std::string UpperCase(const std::string&); + + /** + * Count char in string + */ + static size_t CountChar(const char* str, char c); + + /** + * Remove some characters from a string. + * Return a pointer to the new resulting string (allocated with 'new') + */ + static char* RemoveChars(const char* str, const char* toremove); + + /** + * Remove remove all but 0->9, A->F characters from a string. + * Return a pointer to the new resulting string (allocated with 'new') + */ + static char* RemoveCharsButUpperHex(const char* str); + + /** + * Replace some characters by another character in a string (in-place) + * Return a pointer to string + */ + static char* ReplaceChars(char* str, const char* toreplace, + char replacement); + + /** + * Returns true if str1 starts (respectively ends) with str2 + */ + static bool StringStartsWith(const char* str1, const char* str2); + static bool StringStartsWith(const std::string& str1, const char* str2); + static bool StringEndsWith(const char* str1, const char* str2); + static bool StringEndsWith(const std::string& str1, const char* str2); + + /** + * Returns a pointer to the last occurrence of str2 in str1 + */ + static const char* FindLastString(const char* str1, const char* str2); + + /** + * Make a duplicate of the string similar to the strdup C function + * but use new to create the 'new' string, so one can use + * 'delete' to remove it. Returns 0 if the input is empty. + */ + static char* DuplicateString(const char* str); + + /** + * Return the string cropped to a given length by removing chars in the + * center of the string and replacing them with an ellipsis (...) + */ + static std::string CropString(const std::string&, size_t max_len); + + /** split a path by separator into an array of strings, default is /. + If isPath is true then the string is treated like a path and if + s starts with a / then the first element of the returned array will + be /, so /foo/bar will be [/, foo, bar] + */ + static std::vector<std::string> SplitString(const std::string& s, + char separator = '/', + bool isPath = false); + /** + * Perform a case-independent string comparison + */ + static int Strucmp(const char* s1, const char* s2); + + /** + * Split a string on its newlines into multiple lines + * Return false only if the last line stored had no newline + */ + static bool Split(const std::string& s, std::vector<std::string>& l); + static bool Split(const std::string& s, std::vector<std::string>& l, + char separator); + + /** + * Return string with space added between capitalized words + * (i.e. EatMyShorts becomes Eat My Shorts ) + * (note that IEatShorts becomes IEat Shorts) + */ + static std::string AddSpaceBetweenCapitalizedWords(const std::string&); + + /** + * Append two or more strings and produce new one. + * Programmer must 'delete []' the resulting string, which was allocated + * with 'new'. + * Return 0 if inputs are empty or there was an error + */ + static char* AppendStrings(const char* str1, const char* str2); + static char* AppendStrings(const char* str1, const char* str2, + const char* str3); + + /** + * Estimate the length of the string that will be produced + * from printing the given format string and arguments. The + * returned length will always be at least as large as the string + * that will result from printing. + * WARNING: since va_arg is called to iterate of the argument list, + * you will not be able to use this 'ap' anymore from the beginning. + * It's up to you to call va_end though. + */ + static int EstimateFormatLength(const char* format, va_list ap); + + /** + * Escape specific characters in 'str'. + */ + static std::string EscapeChars(const char* str, const char* chars_to_escape, + char escape_char = '\\'); + + /** ----------------------------------------------------------------- + * Filename Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Replace Windows file system slashes with Unix-style slashes. + */ + static void ConvertToUnixSlashes(std::string& path); + +#ifdef _WIN32 + /** Calls Encoding::ToWindowsExtendedPath. */ + static std::wstring ConvertToWindowsExtendedPath(const std::string&); +#endif + + /** + * For windows this calls ConvertToWindowsOutputPath and for unix + * it calls ConvertToUnixOutputPath + */ + static std::string ConvertToOutputPath(const std::string&); + + /** + * Convert the path to a string that can be used in a unix makefile. + * double slashes are removed, and spaces are escaped. + */ + static std::string ConvertToUnixOutputPath(const std::string&); + + /** + * Convert the path to string that can be used in a windows project or + * makefile. Double slashes are removed if they are not at the start of + * the string, the slashes are converted to windows style backslashes, and + * if there are spaces in the string it is double quoted. + */ + static std::string ConvertToWindowsOutputPath(const std::string&); + + /** + * Return true if a path with the given name exists in the current directory. + */ + static bool PathExists(const std::string& path); + + /** + * Return true if a file exists in the current directory. + * If isFile = true, then make sure the file is a file and + * not a directory. If isFile = false, then return true + * if it is a file or a directory. Note that the file will + * also be checked for read access. (Currently, this check + * for read access is only done on POSIX systems.) + */ + static bool FileExists(const char* filename, bool isFile); + static bool FileExists(const std::string& filename, bool isFile); + static bool FileExists(const char* filename); + static bool FileExists(const std::string& filename); + + /** + * Test if a file exists and can be accessed with the requested + * permissions. Symbolic links are followed. Returns true if + * the access test was successful. + * + * On POSIX systems (including Cygwin), this maps to the access + * function. On Windows systems, all existing files are + * considered readable, and writable files are considered to + * have the read-only file attribute cleared. + */ + static bool TestFileAccess(const char* filename, + TestFilePermissions permissions); + static bool TestFileAccess(const std::string& filename, + TestFilePermissions permissions); +/** + * Cross platform wrapper for stat struct + */ +#if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(__BORLANDC__) + typedef struct stati64 Stat_t; +# else + typedef struct _stat64 Stat_t; +# endif +#else + typedef struct stat Stat_t; +#endif + + /** + * Cross platform wrapper for stat system call + * + * On Windows this may not work for paths longer than 250 characters + * due to limitations of the underlying '_wstat64' call. + */ + static int Stat(const char* path, Stat_t* buf); + static int Stat(const std::string& path, Stat_t* buf); + +/** + * Converts Cygwin path to Win32 path. Uses dictionary container for + * caching and calls to cygwin_conv_to_win32_path from Cygwin dll + * for actual translation. Returns true on success, else false. + */ +#ifdef __CYGWIN__ + static bool PathCygwinToWin32(const char* path, char* win32_path); +#endif + + /** + * Return file length + */ + static unsigned long FileLength(const std::string& filename); + + /** + Change the modification time or create a file + */ + static bool Touch(const std::string& filename, bool create); + + /** + * Compare file modification times. + * Return true for successful comparison and false for error. + * When true is returned, result has -1, 0, +1 for + * f1 older, same, or newer than f2. + */ + static bool FileTimeCompare(const std::string& f1, const std::string& f2, + int* result); + + /** + * Get the file extension (including ".") needed for an executable + * on the current platform ("" for unix, ".exe" for Windows). + */ + static const char* GetExecutableExtension(); + + /** + * Given a path on a Windows machine, return the actual case of + * the path as it exists on disk. Path components that do not + * exist on disk are returned unchanged. Relative paths are always + * returned unchanged. Drive letters are always made upper case. + * This does nothing on non-Windows systems but return the path. + */ + static std::string GetActualCaseForPath(const std::string& path); + + /** + * Given the path to a program executable, get the directory part of + * the path with the file stripped off. If there is no directory + * part, the empty string is returned. + */ + static std::string GetProgramPath(const std::string&); + static bool SplitProgramPath(const std::string& in_name, std::string& dir, + std::string& file, bool errorReport = true); + + /** + * Given argv[0] for a unix program find the full path to a running + * executable. argv0 can be null for windows WinMain programs + * in this case GetModuleFileName will be used to find the path + * to the running executable. If argv0 is not a full path, + * then this will try to find the full path. If the path is not + * found false is returned, if found true is returned. An error + * message of the attempted paths is stored in errorMsg. + * exeName is the name of the executable. + * buildDir is a possibly null path to the build directory. + * installPrefix is a possibly null pointer to the install directory. + */ + static bool FindProgramPath(const char* argv0, std::string& pathOut, + std::string& errorMsg, + const char* exeName = nullptr, + const char* buildDir = nullptr, + const char* installPrefix = nullptr); + + /** + * Given a path to a file or directory, convert it to a full path. + * This collapses away relative paths relative to the cwd argument + * (which defaults to the current working directory). The full path + * is returned. + */ + static std::string CollapseFullPath(const std::string& in_relative); + static std::string CollapseFullPath(const std::string& in_relative, + const char* in_base); + static std::string CollapseFullPath(const std::string& in_relative, + const std::string& in_base); + + /** + * Get the real path for a given path, removing all symlinks. In + * the event of an error (non-existent path, permissions issue, + * etc.) the original path is returned if errorMessage pointer is + * nullptr. Otherwise empty string is returned and errorMessage + * contains error description. + */ + static std::string GetRealPath(const std::string& path, + std::string* errorMessage = nullptr); + + /** + * Split a path name into its root component and the rest of the + * path. The root component is one of the following: + * "/" = UNIX full path + * "c:/" = Windows full path (can be any drive letter) + * "c:" = Windows drive-letter relative path (can be any drive letter) + * "//" = Network path + * "~/" = Home path for current user + * "~u/" = Home path for user 'u' + * "" = Relative path + * + * A pointer to the rest of the path after the root component is + * returned. The root component is stored in the "root" string if + * given. + */ + static const char* SplitPathRootComponent(const std::string& p, + std::string* root = nullptr); + + /** + * Split a path name into its basic components. The first component + * always exists and is the root returned by SplitPathRootComponent. + * The remaining components form the path. If there is a trailing + * slash then the last component is the empty string. The + * components can be recombined as "c[0]c[1]/c[2]/.../c[n]" to + * produce the original path. Home directory references are + * automatically expanded if expand_home_dir is true and this + * platform supports them. + * + * This does *not* normalize the input path. All components are + * preserved, including empty ones. Typically callers should use + * this only on paths that have already been normalized. + */ + static void SplitPath(const std::string& p, + std::vector<std::string>& components, + bool expand_home_dir = true); + + /** + * Join components of a path name into a single string. See + * SplitPath for the format of the components. + * + * This does *not* normalize the input path. All components are + * preserved, including empty ones. Typically callers should use + * this only on paths that have already been normalized. + */ + static std::string JoinPath(const std::vector<std::string>& components); + static std::string JoinPath(std::vector<std::string>::const_iterator first, + std::vector<std::string>::const_iterator last); + + /** + * Compare a path or components of a path. + */ + static bool ComparePath(const std::string& c1, const std::string& c2); + + /** + * Return path of a full filename (no trailing slashes) + */ + static std::string GetFilenamePath(const std::string&); + + /** + * Return file name of a full filename (i.e. file name without path) + */ + static std::string GetFilenameName(const std::string&); + + /** + * Return longest file extension of a full filename (dot included) + */ + static std::string GetFilenameExtension(const std::string&); + + /** + * Return shortest file extension of a full filename (dot included) + */ + static std::string GetFilenameLastExtension(const std::string& filename); + + /** + * Return file name without extension of a full filename + */ + static std::string GetFilenameWithoutExtension(const std::string&); + + /** + * Return file name without its last (shortest) extension + */ + static std::string GetFilenameWithoutLastExtension(const std::string&); + + /** + * Return whether the path represents a full path (not relative) + */ + static bool FileIsFullPath(const std::string&); + static bool FileIsFullPath(const char*); + + /** + * For windows return the short path for the given path, + * Unix just a pass through + */ + static bool GetShortPath(const std::string& path, std::string& result); + + /** + * Read line from file. Make sure to read a full line and truncates it if + * requested via sizeLimit. Returns true if any data were read before the + * end-of-file was reached. If the has_newline argument is specified, it will + * be true when the line read had a newline character. + */ + static bool GetLineFromStream(std::istream& istr, std::string& line, + bool* has_newline = nullptr, + long sizeLimit = -1); + + /** + * Get the parent directory of the directory or file + */ + static std::string GetParentDirectory(const std::string& fileOrDir); + + /** + * Check if the given file or directory is in subdirectory of dir + */ + static bool IsSubDirectory(const std::string& fileOrDir, + const std::string& dir); + + /** ----------------------------------------------------------------- + * File Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Open a file considering unicode. + */ + static FILE* Fopen(const std::string& file, const char* mode); + +/** + * Visual C++ does not define mode_t (note that Borland does, however). + */ +#if defined(_MSC_VER) + typedef unsigned short mode_t; +#endif + + /** + * Make a new directory if it is not there. This function + * can make a full path even if none of the directories existed + * prior to calling this function. + */ + static bool MakeDirectory(const char* path, const mode_t* mode = nullptr); + static bool MakeDirectory(const std::string& path, + const mode_t* mode = nullptr); + + /** + * Copy the source file to the destination file only + * if the two files differ. + */ + static bool CopyFileIfDifferent(const std::string& source, + const std::string& destination); + + /** + * Compare the contents of two files. Return true if different + */ + static bool FilesDiffer(const std::string& source, + const std::string& destination); + + /** + * Compare the contents of two files, ignoring line ending differences. + * Return true if different + */ + static bool TextFilesDiffer(const std::string& path1, + const std::string& path2); + + /** + * Return true if the two files are the same file + */ + static bool SameFile(const std::string& file1, const std::string& file2); + + /** + * Copy a file. + */ + static bool CopyFileAlways(const std::string& source, + const std::string& destination); + + /** + * Copy a file. If the "always" argument is true the file is always + * copied. If it is false, the file is copied only if it is new or + * has changed. + */ + static bool CopyAFile(const std::string& source, + const std::string& destination, bool always = true); + + /** + * Copy content directory to another directory with all files and + * subdirectories. If the "always" argument is true all files are + * always copied. If it is false, only files that have changed or + * are new are copied. + */ + static bool CopyADirectory(const std::string& source, + const std::string& destination, + bool always = true); + + /** + * Remove a file + */ + static bool RemoveFile(const std::string& source); + + /** + * Remove a directory + */ + static bool RemoveADirectory(const std::string& source); + + /** + * Get the maximum full file path length + */ + static size_t GetMaximumFilePathLength(); + + /** + * Find a file in the system PATH, with optional extra paths + */ + static std::string FindFile( + const std::string& name, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); + + /** + * Find a directory in the system PATH, with optional extra paths + */ + static std::string FindDirectory( + const std::string& name, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); + + /** + * Find an executable in the system PATH, with optional extra paths + */ + static std::string FindProgram( + const char* name, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); + static std::string FindProgram( + const std::string& name, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); + static std::string FindProgram( + const std::vector<std::string>& names, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); + + /** + * Find a library in the system PATH, with optional extra paths + */ + static std::string FindLibrary(const std::string& name, + const std::vector<std::string>& path); + + /** + * Return true if the file is a directory + */ + static bool FileIsDirectory(const std::string& name); + + /** + * Return true if the file is a symlink + */ + static bool FileIsSymlink(const std::string& name); + + /** + * Return true if the file is a FIFO + */ + static bool FileIsFIFO(const std::string& name); + + /** + * Return true if the file has a given signature (first set of bytes) + */ + static bool FileHasSignature(const char* filename, const char* signature, + long offset = 0); + + /** + * Attempt to detect and return the type of a file. + * Up to 'length' bytes are read from the file, if more than 'percent_bin' % + * of the bytes are non-textual elements, the file is considered binary, + * otherwise textual. Textual elements are bytes in the ASCII [0x20, 0x7E] + * range, but also \\n, \\r, \\t. + * The algorithm is simplistic, and should probably check for usual file + * extensions, 'magic' signature, unicode, etc. + */ + enum FileTypeEnum + { + FileTypeUnknown, + FileTypeBinary, + FileTypeText + }; + static SystemTools::FileTypeEnum DetectFileType(const char* filename, + unsigned long length = 256, + double percent_bin = 0.05); + + /** + * Create a symbolic link if the platform supports it. Returns whether + * creation succeeded. + */ + static bool CreateSymlink(const std::string& origName, + const std::string& newName); + + /** + * Read the contents of a symbolic link. Returns whether reading + * succeeded. + */ + static bool ReadSymlink(const std::string& newName, std::string& origName); + + /** + * Try to locate the file 'filename' in the directory 'dir'. + * If 'filename' is a fully qualified filename, the basename of the file is + * used to check for its existence in 'dir'. + * If 'dir' is not a directory, GetFilenamePath() is called on 'dir' to + * get its directory first (thus, you can pass a filename as 'dir', as + * a convenience). + * 'filename_found' is assigned the fully qualified name/path of the file + * if it is found (not touched otherwise). + * If 'try_filename_dirs' is true, try to find the file using the + * components of its path, i.e. if we are looking for c:/foo/bar/bill.txt, + * first look for bill.txt in 'dir', then in 'dir'/bar, then in 'dir'/foo/bar + * etc. + * Return true if the file was found, false otherwise. + */ + static bool LocateFileInDir(const char* filename, const char* dir, + std::string& filename_found, + int try_filename_dirs = 0); + + /** compute the relative path from local to remote. local must + be a directory. remote can be a file or a directory. + Both remote and local must be full paths. Basically, if + you are in directory local and you want to access the file in remote + what is the relative path to do that. For example: + /a/b/c/d to /a/b/c1/d1 -> ../../c1/d1 + from /usr/src to /usr/src/test/blah/foo.cpp -> test/blah/foo.cpp + */ + static std::string RelativePath(const std::string& local, + const std::string& remote); + + /** + * Return file's modified time + */ + static long int ModifiedTime(const std::string& filename); + + /** + * Return file's creation time (Win32: works only for NTFS, not FAT) + */ + static long int CreationTime(const std::string& filename); + + /** + * Get and set permissions of the file. If honor_umask is set, the umask + * is queried and applied to the given permissions. Returns false if + * failure. + * + * WARNING: A non-thread-safe method is currently used to get the umask + * if a honor_umask parameter is set to true. + */ + static bool GetPermissions(const char* file, mode_t& mode); + static bool GetPermissions(const std::string& file, mode_t& mode); + static bool SetPermissions(const char* file, mode_t mode, + bool honor_umask = false); + static bool SetPermissions(const std::string& file, mode_t mode, + bool honor_umask = false); + + /** ----------------------------------------------------------------- + * Time Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** Get current time in seconds since Posix Epoch (Jan 1, 1970). */ + static double GetTime(); + + /** + * Get current date/time + */ + static std::string GetCurrentDateTime(const char* format); + + /** ----------------------------------------------------------------- + * Registry Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Specify access to the 32-bit or 64-bit application view of + * registry values. The default is to match the currently running + * binary type. + */ + enum KeyWOW64 + { + KeyWOW64_Default, + KeyWOW64_32, + KeyWOW64_64 + }; + + /** + * Get a list of subkeys. + */ + static bool GetRegistrySubKeys(const std::string& key, + std::vector<std::string>& subkeys, + KeyWOW64 view = KeyWOW64_Default); + + /** + * Read a registry value + */ + static bool ReadRegistryValue(const std::string& key, std::string& value, + KeyWOW64 view = KeyWOW64_Default); + + /** + * Write a registry value + */ + static bool WriteRegistryValue(const std::string& key, + const std::string& value, + KeyWOW64 view = KeyWOW64_Default); + + /** + * Delete a registry value + */ + static bool DeleteRegistryValue(const std::string& key, + KeyWOW64 view = KeyWOW64_Default); + + /** ----------------------------------------------------------------- + * Environment Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Add the paths from the environment variable PATH to the + * string vector passed in. If env is set then the value + * of env will be used instead of PATH. + */ + static void GetPath(std::vector<std::string>& path, + const char* env = nullptr); + + /** + * Read an environment variable + */ + static const char* GetEnv(const char* key); + static const char* GetEnv(const std::string& key); + static bool GetEnv(const char* key, std::string& result); + static bool GetEnv(const std::string& key, std::string& result); + static bool HasEnv(const char* key); + static bool HasEnv(const std::string& key); + + /** Put a string into the environment + of the form var=value */ + static bool PutEnv(const std::string& env); + + /** Remove a string from the environment. + Input is of the form "var" or "var=value" (value is ignored). */ + static bool UnPutEnv(const std::string& env); + + /** + * Get current working directory CWD + */ + static std::string GetCurrentWorkingDirectory(bool collapse = true); + + /** + * Change directory to the directory specified + */ + static int ChangeDirectory(const std::string& dir); + + /** + * Get the result of strerror(errno) + */ + static std::string GetLastSystemError(); + + /** + * When building DEBUG with MSVC, this enables a hook that prevents + * error dialogs from popping up if the program is being run from + * DART. + */ + static void EnableMSVCDebugHook(); + + /** + * Get the width of the terminal window. The code may or may not work, so + * make sure you have some reasonable defaults prepared if the code returns + * some bogus size. + */ + static int GetTerminalWidth(); + +#if @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP + /** + * Add an entry in the path translation table. + */ + static void AddTranslationPath(const std::string& dir, + const std::string& refdir); + + /** + * If dir is different after CollapseFullPath is called, + * Then insert it into the path translation table + */ + static void AddKeepPath(const std::string& dir); + + /** + * Update path by going through the Path Translation table; + */ + static void CheckTranslationPath(std::string& path); +#endif + + /** + * Delay the execution for a specified amount of time specified + * in milliseconds + */ + static void Delay(unsigned int msec); + + /** + * Get the operating system name and version + * This is implemented for Win32 only for the moment + */ + static std::string GetOperatingSystemNameAndVersion(); + + /** ----------------------------------------------------------------- + * URL Manipulation Routines + * ----------------------------------------------------------------- + */ + + /** + * Parse a character string : + * protocol://dataglom + * and fill protocol as appropriate. + * decode the dataglom using DecodeURL if set to true. + * Return false if the URL does not have the required form, true otherwise. + */ + static bool ParseURLProtocol(const std::string& URL, std::string& protocol, + std::string& dataglom, bool decode = false); + + /** + * Parse a string (a URL without protocol prefix) with the form: + * protocol://[[username[':'password]'@']hostname[':'dataport]]'/'[datapath] + * and fill protocol, username, password, hostname, dataport, and datapath + * when values are found. + * decode all string except the protocol using DecodeUrl if set to true. + * Return true if the string matches the format; false otherwise. + */ + static bool ParseURL(const std::string& URL, std::string& protocol, + std::string& username, std::string& password, + std::string& hostname, std::string& dataport, + std::string& datapath, bool decode = false); + + /** + * Decode the percent-encoded string from an URL or an URI + * into their correct char values. + * Does not perform any other sort of validation. + * Return the decoded string + */ + static std::string DecodeURL(const std::string& url); + +private: + /** + * Allocate the stl map that serve as the Path Translation table. + */ + static void ClassInitialize(); + + /** + * Deallocate the stl map that serve as the Path Translation table. + */ + static void ClassFinalize(); + + /** + * This method prevents warning on SGI + */ + SystemToolsManager* GetSystemToolsManager() + { + return &SystemToolsManagerInstance; + } + + static SystemToolsStatic* Statics; + friend class SystemToolsStatic; + friend class SystemToolsManager; +}; + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/Terminal.c b/Source/kwsys/Terminal.c new file mode 100644 index 0000000..4d1b46c --- /dev/null +++ b/Source/kwsys/Terminal.c @@ -0,0 +1,423 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Terminal.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Terminal.h.in" +#endif + +/* Configure support for this platform. */ +#if defined(_WIN32) || defined(__CYGWIN__) +# define KWSYS_TERMINAL_SUPPORT_CONSOLE +#endif +#if !defined(_WIN32) +# define KWSYS_TERMINAL_ISATTY_WORKS +#endif + +/* Include needed system APIs. */ + +#include <stdarg.h> /* va_list */ +#include <stdlib.h> /* getenv */ +#include <string.h> /* strcmp */ + +#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) +# include <io.h> /* _get_osfhandle */ +# include <windows.h> /* SetConsoleTextAttribute */ +#endif + +#if defined(KWSYS_TERMINAL_ISATTY_WORKS) +# include <unistd.h> /* isatty */ +#else +# include <sys/stat.h> /* fstat */ +#endif + +static int kwsysTerminalStreamIsVT100(FILE* stream, int default_vt100, + int default_tty); +static void kwsysTerminalSetVT100Color(FILE* stream, int color); +#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) +static HANDLE kwsysTerminalGetStreamHandle(FILE* stream); +static void kwsysTerminalSetConsoleColor(HANDLE hOut, + CONSOLE_SCREEN_BUFFER_INFO* hOutInfo, + FILE* stream, int color); +#endif + +void kwsysTerminal_cfprintf(int color, FILE* stream, const char* format, ...) +{ + /* Setup the stream with the given color if possible. */ + int pipeIsConsole = 0; + int pipeIsVT100 = 0; + int default_vt100 = color & kwsysTerminal_Color_AssumeVT100; + int default_tty = color & kwsysTerminal_Color_AssumeTTY; +#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) + CONSOLE_SCREEN_BUFFER_INFO hOutInfo; + HANDLE hOut = kwsysTerminalGetStreamHandle(stream); + if (GetConsoleScreenBufferInfo(hOut, &hOutInfo)) { + pipeIsConsole = 1; + kwsysTerminalSetConsoleColor(hOut, &hOutInfo, stream, color); + } +#endif + if (!pipeIsConsole && + kwsysTerminalStreamIsVT100(stream, default_vt100, default_tty)) { + pipeIsVT100 = 1; + kwsysTerminalSetVT100Color(stream, color); + } + + /* Format the text into the stream. */ + { + va_list var_args; + va_start(var_args, format); + vfprintf(stream, format, var_args); + va_end(var_args); + } + +/* Restore the normal color state for the stream. */ +#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) + if (pipeIsConsole) { + kwsysTerminalSetConsoleColor(hOut, &hOutInfo, stream, + kwsysTerminal_Color_Normal); + } +#endif + if (pipeIsVT100) { + kwsysTerminalSetVT100Color(stream, kwsysTerminal_Color_Normal); + } +} + +/* Detect cases when a stream is definitely not interactive. */ +#if !defined(KWSYS_TERMINAL_ISATTY_WORKS) +static int kwsysTerminalStreamIsNotInteractive(FILE* stream) +{ + /* The given stream is definitely not interactive if it is a regular + file. */ + struct stat stream_stat; + if (fstat(fileno(stream), &stream_stat) == 0) { + if (stream_stat.st_mode & S_IFREG) { + return 1; + } + } + return 0; +} +#endif + +/* List of terminal names known to support VT100 color escape sequences. */ +static const char* kwsysTerminalVT100Names[] = { "Eterm", + "alacritty", + "alacritty-direct", + "ansi", + "color-xterm", + "con132x25", + "con132x30", + "con132x43", + "con132x60", + "con80x25", + "con80x28", + "con80x30", + "con80x43", + "con80x50", + "con80x60", + "cons25", + "console", + "cygwin", + "dtterm", + "eterm-color", + "gnome", + "gnome-256color", + "konsole", + "konsole-256color", + "kterm", + "linux", + "msys", + "linux-c", + "mach-color", + "mlterm", + "putty", + "putty-256color", + "rxvt", + "rxvt-256color", + "rxvt-cygwin", + "rxvt-cygwin-native", + "rxvt-unicode", + "rxvt-unicode-256color", + "screen", + "screen-256color", + "screen-256color-bce", + "screen-bce", + "screen-w", + "screen.linux", + "st-256color", + "tmux", + "tmux-256color", + "vt100", + "xterm", + "xterm-16color", + "xterm-256color", + "xterm-88color", + "xterm-color", + "xterm-debian", + "xterm-kitty", + "xterm-termite", + 0 }; + +/* Detect whether a stream is displayed in a VT100-compatible terminal. */ +static int kwsysTerminalStreamIsVT100(FILE* stream, int default_vt100, + int default_tty) +{ + /* Force color according to http://bixense.com/clicolors/ convention. */ + { + const char* clicolor_force = getenv("CLICOLOR_FORCE"); + if (clicolor_force && *clicolor_force && + strcmp(clicolor_force, "0") != 0) { + return 1; + } + } + + /* GNU make 4.1+ may tell us that its output is destined for a TTY. */ + { + const char* termout = getenv("MAKE_TERMOUT"); + if (termout && *termout != '\0') { + return 1; + } + } + + /* If running inside emacs the terminal is not VT100. Some emacs + seem to claim the TERM is xterm even though they do not support + VT100 escapes. */ + { + const char* emacs = getenv("EMACS"); + if (emacs && *emacs == 't') { + return 0; + } + } + + /* Check for a valid terminal. */ + if (!default_vt100) { + const char** t = 0; + const char* term = getenv("TERM"); + if (term) { + for (t = kwsysTerminalVT100Names; *t && strcmp(term, *t) != 0; ++t) { + } + } + if (!(t && *t)) { + return 0; + } + } + +#if defined(KWSYS_TERMINAL_ISATTY_WORKS) + /* Make sure the stream is a tty. */ + (void)default_tty; + return isatty(fileno(stream)) ? 1 : 0; +#else + /* Check for cases in which the stream is definitely not a tty. */ + if (kwsysTerminalStreamIsNotInteractive(stream)) { + return 0; + } + + /* Use the provided default for whether this is a tty. */ + return default_tty; +#endif +} + +/* VT100 escape sequence strings. */ +#if defined(__MVS__) +/* if building on z/OS (aka MVS), assume we are using EBCDIC */ +# define ESCAPE_CHAR "\47" +#else +# define ESCAPE_CHAR "\33" +#endif + +#define KWSYS_TERMINAL_VT100_NORMAL ESCAPE_CHAR "[0m" +#define KWSYS_TERMINAL_VT100_BOLD ESCAPE_CHAR "[1m" +#define KWSYS_TERMINAL_VT100_UNDERLINE ESCAPE_CHAR "[4m" +#define KWSYS_TERMINAL_VT100_BLINK ESCAPE_CHAR "[5m" +#define KWSYS_TERMINAL_VT100_INVERSE ESCAPE_CHAR "[7m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_BLACK ESCAPE_CHAR "[30m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_RED ESCAPE_CHAR "[31m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_GREEN ESCAPE_CHAR "[32m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW ESCAPE_CHAR "[33m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_BLUE ESCAPE_CHAR "[34m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA ESCAPE_CHAR "[35m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_CYAN ESCAPE_CHAR "[36m" +#define KWSYS_TERMINAL_VT100_FOREGROUND_WHITE ESCAPE_CHAR "[37m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_BLACK ESCAPE_CHAR "[40m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_RED ESCAPE_CHAR "[41m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_GREEN ESCAPE_CHAR "[42m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW ESCAPE_CHAR "[43m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_BLUE ESCAPE_CHAR "[44m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA ESCAPE_CHAR "[45m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_CYAN ESCAPE_CHAR "[46m" +#define KWSYS_TERMINAL_VT100_BACKGROUND_WHITE ESCAPE_CHAR "[47m" + +/* Write VT100 escape sequences to the stream for the given color. */ +static void kwsysTerminalSetVT100Color(FILE* stream, int color) +{ + if (color == kwsysTerminal_Color_Normal) { + fprintf(stream, KWSYS_TERMINAL_VT100_NORMAL); + return; + } + + switch (color & kwsysTerminal_Color_ForegroundMask) { + case kwsysTerminal_Color_Normal: + fprintf(stream, KWSYS_TERMINAL_VT100_NORMAL); + break; + case kwsysTerminal_Color_ForegroundBlack: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_BLACK); + break; + case kwsysTerminal_Color_ForegroundRed: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_RED); + break; + case kwsysTerminal_Color_ForegroundGreen: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_GREEN); + break; + case kwsysTerminal_Color_ForegroundYellow: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW); + break; + case kwsysTerminal_Color_ForegroundBlue: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_BLUE); + break; + case kwsysTerminal_Color_ForegroundMagenta: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA); + break; + case kwsysTerminal_Color_ForegroundCyan: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_CYAN); + break; + case kwsysTerminal_Color_ForegroundWhite: + fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_WHITE); + break; + } + switch (color & kwsysTerminal_Color_BackgroundMask) { + case kwsysTerminal_Color_BackgroundBlack: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_BLACK); + break; + case kwsysTerminal_Color_BackgroundRed: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_RED); + break; + case kwsysTerminal_Color_BackgroundGreen: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_GREEN); + break; + case kwsysTerminal_Color_BackgroundYellow: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW); + break; + case kwsysTerminal_Color_BackgroundBlue: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_BLUE); + break; + case kwsysTerminal_Color_BackgroundMagenta: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA); + break; + case kwsysTerminal_Color_BackgroundCyan: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_CYAN); + break; + case kwsysTerminal_Color_BackgroundWhite: + fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_WHITE); + break; + } + if (color & kwsysTerminal_Color_ForegroundBold) { + fprintf(stream, KWSYS_TERMINAL_VT100_BOLD); + } +} + +#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) + +# define KWSYS_TERMINAL_MASK_FOREGROUND \ + (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | \ + FOREGROUND_INTENSITY) +# define KWSYS_TERMINAL_MASK_BACKGROUND \ + (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | \ + BACKGROUND_INTENSITY) + +/* Get the Windows handle for a FILE stream. */ +static HANDLE kwsysTerminalGetStreamHandle(FILE* stream) +{ + /* Get the C-library file descriptor from the stream. */ + int fd = fileno(stream); + +# if defined(__CYGWIN__) + /* Cygwin seems to have an extra pipe level. If the file descriptor + corresponds to stdout or stderr then obtain the matching windows + handle directly. */ + if (fd == fileno(stdout)) { + return GetStdHandle(STD_OUTPUT_HANDLE); + } else if (fd == fileno(stderr)) { + return GetStdHandle(STD_ERROR_HANDLE); + } +# endif + + /* Get the underlying Windows handle for the descriptor. */ + return (HANDLE)_get_osfhandle(fd); +} + +/* Set color attributes in a Windows console. */ +static void kwsysTerminalSetConsoleColor(HANDLE hOut, + CONSOLE_SCREEN_BUFFER_INFO* hOutInfo, + FILE* stream, int color) +{ + WORD attributes = 0; + switch (color & kwsysTerminal_Color_ForegroundMask) { + case kwsysTerminal_Color_Normal: + attributes |= hOutInfo->wAttributes & KWSYS_TERMINAL_MASK_FOREGROUND; + break; + case kwsysTerminal_Color_ForegroundBlack: + attributes |= 0; + break; + case kwsysTerminal_Color_ForegroundRed: + attributes |= FOREGROUND_RED; + break; + case kwsysTerminal_Color_ForegroundGreen: + attributes |= FOREGROUND_GREEN; + break; + case kwsysTerminal_Color_ForegroundYellow: + attributes |= FOREGROUND_RED | FOREGROUND_GREEN; + break; + case kwsysTerminal_Color_ForegroundBlue: + attributes |= FOREGROUND_BLUE; + break; + case kwsysTerminal_Color_ForegroundMagenta: + attributes |= FOREGROUND_RED | FOREGROUND_BLUE; + break; + case kwsysTerminal_Color_ForegroundCyan: + attributes |= FOREGROUND_BLUE | FOREGROUND_GREEN; + break; + case kwsysTerminal_Color_ForegroundWhite: + attributes |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + break; + } + switch (color & kwsysTerminal_Color_BackgroundMask) { + case kwsysTerminal_Color_Normal: + attributes |= hOutInfo->wAttributes & KWSYS_TERMINAL_MASK_BACKGROUND; + break; + case kwsysTerminal_Color_BackgroundBlack: + attributes |= 0; + break; + case kwsysTerminal_Color_BackgroundRed: + attributes |= BACKGROUND_RED; + break; + case kwsysTerminal_Color_BackgroundGreen: + attributes |= BACKGROUND_GREEN; + break; + case kwsysTerminal_Color_BackgroundYellow: + attributes |= BACKGROUND_RED | BACKGROUND_GREEN; + break; + case kwsysTerminal_Color_BackgroundBlue: + attributes |= BACKGROUND_BLUE; + break; + case kwsysTerminal_Color_BackgroundMagenta: + attributes |= BACKGROUND_RED | BACKGROUND_BLUE; + break; + case kwsysTerminal_Color_BackgroundCyan: + attributes |= BACKGROUND_BLUE | BACKGROUND_GREEN; + break; + case kwsysTerminal_Color_BackgroundWhite: + attributes |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; + break; + } + if (color & kwsysTerminal_Color_ForegroundBold) { + attributes |= FOREGROUND_INTENSITY; + } + if (color & kwsysTerminal_Color_BackgroundBold) { + attributes |= BACKGROUND_INTENSITY; + } + fflush(stream); + SetConsoleTextAttribute(hOut, attributes); +} +#endif diff --git a/Source/kwsys/Terminal.h.in b/Source/kwsys/Terminal.h.in new file mode 100644 index 0000000..1a2c745 --- /dev/null +++ b/Source/kwsys/Terminal.h.in @@ -0,0 +1,170 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_Terminal_h +#define @KWSYS_NAMESPACE@_Terminal_h + +#include <@KWSYS_NAMESPACE@/Configure.h> + +#include <stdio.h> /* For file stream type FILE. */ + +/* Redefine all public interface symbol names to be in the proper + namespace. These macros are used internally to kwsys only, and are + not visible to user code. Use kwsysHeaderDump.pl to reproduce + these macros after making changes to the interface. */ +#if !defined(KWSYS_NAMESPACE) +# define kwsys_ns(x) @KWSYS_NAMESPACE@##x +# define kwsysEXPORT @KWSYS_NAMESPACE@_EXPORT +#endif +#if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# define kwsysTerminal_cfprintf kwsys_ns(Terminal_cfprintf) +# define kwsysTerminal_Color_e kwsys_ns(Terminal_Color_e) +# define kwsysTerminal_Color_Normal kwsys_ns(Terminal_Color_Normal) +# define kwsysTerminal_Color_ForegroundBlack \ + kwsys_ns(Terminal_Color_ForegroundBlack) +# define kwsysTerminal_Color_ForegroundRed \ + kwsys_ns(Terminal_Color_ForegroundRed) +# define kwsysTerminal_Color_ForegroundGreen \ + kwsys_ns(Terminal_Color_ForegroundGreen) +# define kwsysTerminal_Color_ForegroundYellow \ + kwsys_ns(Terminal_Color_ForegroundYellow) +# define kwsysTerminal_Color_ForegroundBlue \ + kwsys_ns(Terminal_Color_ForegroundBlue) +# define kwsysTerminal_Color_ForegroundMagenta \ + kwsys_ns(Terminal_Color_ForegroundMagenta) +# define kwsysTerminal_Color_ForegroundCyan \ + kwsys_ns(Terminal_Color_ForegroundCyan) +# define kwsysTerminal_Color_ForegroundWhite \ + kwsys_ns(Terminal_Color_ForegroundWhite) +# define kwsysTerminal_Color_ForegroundMask \ + kwsys_ns(Terminal_Color_ForegroundMask) +# define kwsysTerminal_Color_BackgroundBlack \ + kwsys_ns(Terminal_Color_BackgroundBlack) +# define kwsysTerminal_Color_BackgroundRed \ + kwsys_ns(Terminal_Color_BackgroundRed) +# define kwsysTerminal_Color_BackgroundGreen \ + kwsys_ns(Terminal_Color_BackgroundGreen) +# define kwsysTerminal_Color_BackgroundYellow \ + kwsys_ns(Terminal_Color_BackgroundYellow) +# define kwsysTerminal_Color_BackgroundBlue \ + kwsys_ns(Terminal_Color_BackgroundBlue) +# define kwsysTerminal_Color_BackgroundMagenta \ + kwsys_ns(Terminal_Color_BackgroundMagenta) +# define kwsysTerminal_Color_BackgroundCyan \ + kwsys_ns(Terminal_Color_BackgroundCyan) +# define kwsysTerminal_Color_BackgroundWhite \ + kwsys_ns(Terminal_Color_BackgroundWhite) +# define kwsysTerminal_Color_BackgroundMask \ + kwsys_ns(Terminal_Color_BackgroundMask) +# define kwsysTerminal_Color_ForegroundBold \ + kwsys_ns(Terminal_Color_ForegroundBold) +# define kwsysTerminal_Color_BackgroundBold \ + kwsys_ns(Terminal_Color_BackgroundBold) +# define kwsysTerminal_Color_AssumeTTY kwsys_ns(Terminal_Color_AssumeTTY) +# define kwsysTerminal_Color_AssumeVT100 kwsys_ns(Terminal_Color_AssumeVT100) +# define kwsysTerminal_Color_AttributeMask \ + kwsys_ns(Terminal_Color_AttributeMask) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Write colored and formatted text to a stream. Color is used only + * for streams supporting it. The color specification is constructed + * by bitwise-OR-ing enumeration values. At most one foreground and + * one background value may be given. + * + * Whether the a stream supports color is usually automatically + * detected, but with two exceptions: + * + * - When the stream is displayed in a terminal supporting VT100 + * color but using an intermediate pipe for communication the + * detection of a tty fails. (This typically occurs for a shell + * running in an rxvt terminal in MSYS.) If the caller knows this + * to be the case, the attribute Color_AssumeTTY may be included in + * the color specification. + * + * - When the stream is displayed in a terminal whose TERM + * environment variable is not set or is set to a value that is not + * known to support VT100 colors. If the caller knows this to be + * the case, the attribute Color_AssumeVT100 may be included in the + * color specification. + */ +kwsysEXPORT void kwsysTerminal_cfprintf(int color, FILE* stream, + const char* format, ...); +enum kwsysTerminal_Color_e +{ + /* Normal Text */ + kwsysTerminal_Color_Normal = 0, + + /* Foreground Color */ + kwsysTerminal_Color_ForegroundBlack = 0x1, + kwsysTerminal_Color_ForegroundRed = 0x2, + kwsysTerminal_Color_ForegroundGreen = 0x3, + kwsysTerminal_Color_ForegroundYellow = 0x4, + kwsysTerminal_Color_ForegroundBlue = 0x5, + kwsysTerminal_Color_ForegroundMagenta = 0x6, + kwsysTerminal_Color_ForegroundCyan = 0x7, + kwsysTerminal_Color_ForegroundWhite = 0x8, + kwsysTerminal_Color_ForegroundMask = 0xF, + + /* Background Color */ + kwsysTerminal_Color_BackgroundBlack = 0x10, + kwsysTerminal_Color_BackgroundRed = 0x20, + kwsysTerminal_Color_BackgroundGreen = 0x30, + kwsysTerminal_Color_BackgroundYellow = 0x40, + kwsysTerminal_Color_BackgroundBlue = 0x50, + kwsysTerminal_Color_BackgroundMagenta = 0x60, + kwsysTerminal_Color_BackgroundCyan = 0x70, + kwsysTerminal_Color_BackgroundWhite = 0x80, + kwsysTerminal_Color_BackgroundMask = 0xF0, + + /* Attributes */ + kwsysTerminal_Color_ForegroundBold = 0x100, + kwsysTerminal_Color_BackgroundBold = 0x200, + kwsysTerminal_Color_AssumeTTY = 0x400, + kwsysTerminal_Color_AssumeVT100 = 0x800, + kwsysTerminal_Color_AttributeMask = 0xF00 +}; + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +/* If we are building a kwsys .c or .cxx file, let it use these macros. + Otherwise, undefine them to keep the namespace clean. */ +#if !defined(KWSYS_NAMESPACE) +# undef kwsys_ns +# undef kwsysEXPORT +# if !@KWSYS_NAMESPACE@_NAME_IS_KWSYS +# undef kwsysTerminal_cfprintf +# undef kwsysTerminal_Color_e +# undef kwsysTerminal_Color_Normal +# undef kwsysTerminal_Color_ForegroundBlack +# undef kwsysTerminal_Color_ForegroundRed +# undef kwsysTerminal_Color_ForegroundGreen +# undef kwsysTerminal_Color_ForegroundYellow +# undef kwsysTerminal_Color_ForegroundBlue +# undef kwsysTerminal_Color_ForegroundMagenta +# undef kwsysTerminal_Color_ForegroundCyan +# undef kwsysTerminal_Color_ForegroundWhite +# undef kwsysTerminal_Color_ForegroundMask +# undef kwsysTerminal_Color_BackgroundBlack +# undef kwsysTerminal_Color_BackgroundRed +# undef kwsysTerminal_Color_BackgroundGreen +# undef kwsysTerminal_Color_BackgroundYellow +# undef kwsysTerminal_Color_BackgroundBlue +# undef kwsysTerminal_Color_BackgroundMagenta +# undef kwsysTerminal_Color_BackgroundCyan +# undef kwsysTerminal_Color_BackgroundWhite +# undef kwsysTerminal_Color_BackgroundMask +# undef kwsysTerminal_Color_ForegroundBold +# undef kwsysTerminal_Color_BackgroundBold +# undef kwsysTerminal_Color_AssumeTTY +# undef kwsysTerminal_Color_AssumeVT100 +# undef kwsysTerminal_Color_AttributeMask +# endif +#endif + +#endif diff --git a/Source/kwsys/hash_fun.hxx.in b/Source/kwsys/hash_fun.hxx.in new file mode 100644 index 0000000..8626c2a --- /dev/null +++ b/Source/kwsys/hash_fun.hxx.in @@ -0,0 +1,166 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +/* + * Copyright (c) 1996 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ +#ifndef @KWSYS_NAMESPACE@_hash_fun_hxx +#define @KWSYS_NAMESPACE@_hash_fun_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> + +#include <stddef.h> // size_t +#include <string> + +namespace @KWSYS_NAMESPACE@ { + +template <class _Key> +struct hash +{ +}; + +inline size_t _stl_hash_string(const char* __s) +{ + unsigned long __h = 0; + for (; *__s; ++__s) + __h = 5 * __h + *__s; + + return size_t(__h); +} + +template <> +struct hash<char*> +{ + size_t operator()(const char* __s) const { return _stl_hash_string(__s); } +}; + +template <> +struct hash<const char*> +{ + size_t operator()(const char* __s) const { return _stl_hash_string(__s); } +}; + +template <> +struct hash<std::string> +{ + size_t operator()(const std::string& __s) const + { + return _stl_hash_string(__s.c_str()); + } +}; + +#if !defined(__BORLANDC__) +template <> +struct hash<const std::string> +{ + size_t operator()(const std::string& __s) const + { + return _stl_hash_string(__s.c_str()); + } +}; +#endif + +template <> +struct hash<char> +{ + size_t operator()(char __x) const { return __x; } +}; + +template <> +struct hash<unsigned char> +{ + size_t operator()(unsigned char __x) const { return __x; } +}; + +template <> +struct hash<signed char> +{ + size_t operator()(unsigned char __x) const { return __x; } +}; + +template <> +struct hash<short> +{ + size_t operator()(short __x) const { return __x; } +}; + +template <> +struct hash<unsigned short> +{ + size_t operator()(unsigned short __x) const { return __x; } +}; + +template <> +struct hash<int> +{ + size_t operator()(int __x) const { return __x; } +}; + +template <> +struct hash<unsigned int> +{ + size_t operator()(unsigned int __x) const { return __x; } +}; + +template <> +struct hash<long> +{ + size_t operator()(long __x) const { return __x; } +}; + +template <> +struct hash<unsigned long> +{ + size_t operator()(unsigned long __x) const { return __x; } +}; + +// use long long or __int64 +#if @KWSYS_USE_LONG_LONG@ +template <> +struct hash<long long> +{ + size_t operator()(long long __x) const { return __x; } +}; + +template <> +struct hash<unsigned long long> +{ + size_t operator()(unsigned long long __x) const { return __x; } +}; +#elif @KWSYS_USE___INT64@ +template <> +struct hash<__int64> +{ + size_t operator()(__int64 __x) const { return __x; } +}; +template <> +struct hash<unsigned __int64> +{ + size_t operator()(unsigned __int64 __x) const { return __x; } +}; +#endif // use long long or __int64 + +} // namespace @KWSYS_NAMESPACE@ + +#endif diff --git a/Source/kwsys/hash_map.hxx.in b/Source/kwsys/hash_map.hxx.in new file mode 100644 index 0000000..5f04e9c --- /dev/null +++ b/Source/kwsys/hash_map.hxx.in @@ -0,0 +1,423 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +/* + * Copyright (c) 1996 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ +#ifndef @KWSYS_NAMESPACE@_hash_map_hxx +#define @KWSYS_NAMESPACE@_hash_map_hxx + +#include <@KWSYS_NAMESPACE@/hashtable.hxx> + +#include <@KWSYS_NAMESPACE@/hash_fun.hxx> + +#include <functional> // equal_to + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4284) +# pragma warning(disable : 4786) +#endif + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +# pragma set woff 1174 +# pragma set woff 1375 +#endif + +namespace @KWSYS_NAMESPACE@ { + +// select1st is an extension: it is not part of the standard. +template <class T1, class T2> +struct hash_select1st +{ + const T1& operator()(const std::pair<T1, T2>& __x) const + { + return __x.first; + } +}; + +// Forward declaration of equality operator; needed for friend declaration. + +template <class _Key, class _Tp, class _HashFcn = hash<_Key>, + class _EqualKey = std::equal_to<_Key>, + class _Alloc = std::allocator<char> > +class hash_map; + +template <class _Key, class _Tp, class _HashFn, class _EqKey, class _Alloc> +bool operator==(const hash_map<_Key, _Tp, _HashFn, _EqKey, _Alloc>&, + const hash_map<_Key, _Tp, _HashFn, _EqKey, _Alloc>&); + +template <class _Key, class _Tp, class _HashFcn, class _EqualKey, class _Alloc> +class hash_map +{ +private: + typedef hashtable<std::pair<const _Key, _Tp>, _Key, _HashFcn, + hash_select1st<const _Key, _Tp>, _EqualKey, _Alloc> + _Ht; + _Ht _M_ht; + +public: + typedef typename _Ht::key_type key_type; + typedef _Tp data_type; + typedef _Tp mapped_type; + typedef typename _Ht::value_type value_type; + typedef typename _Ht::hasher hasher; + typedef typename _Ht::key_equal key_equal; + + typedef typename _Ht::size_type size_type; + typedef typename _Ht::difference_type difference_type; + typedef typename _Ht::pointer pointer; + typedef typename _Ht::const_pointer const_pointer; + typedef typename _Ht::reference reference; + typedef typename _Ht::const_reference const_reference; + + typedef typename _Ht::iterator iterator; + typedef typename _Ht::const_iterator const_iterator; + + typedef typename _Ht::allocator_type allocator_type; + + hasher hash_funct() const { return _M_ht.hash_funct(); } + key_equal key_eq() const { return _M_ht.key_eq(); } + allocator_type get_allocator() const { return _M_ht.get_allocator(); } + +public: + hash_map() + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + } + explicit hash_map(size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + } + hash_map(size_type __n, const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + } + hash_map(size_type __n, const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + } + + template <class _InputIterator> + hash_map(_InputIterator __f, _InputIterator __l) + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_map(_InputIterator __f, _InputIterator __l, size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_map(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_map(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + _M_ht.insert_unique(__f, __l); + } + +public: + size_type size() const { return _M_ht.size(); } + size_type max_size() const { return _M_ht.max_size(); } + bool empty() const { return _M_ht.empty(); } + void swap(hash_map& __hs) { _M_ht.swap(__hs._M_ht); } + + friend bool operator==<>(const hash_map&, const hash_map&); + + iterator begin() { return _M_ht.begin(); } + iterator end() { return _M_ht.end(); } + const_iterator begin() const { return _M_ht.begin(); } + const_iterator end() const { return _M_ht.end(); } + +public: + std::pair<iterator, bool> insert(const value_type& __obj) + { + return _M_ht.insert_unique(__obj); + } + template <class _InputIterator> + void insert(_InputIterator __f, _InputIterator __l) + { + _M_ht.insert_unique(__f, __l); + } + std::pair<iterator, bool> insert_noresize(const value_type& __obj) + { + return _M_ht.insert_unique_noresize(__obj); + } + + iterator find(const key_type& __key) { return _M_ht.find(__key); } + const_iterator find(const key_type& __key) const + { + return _M_ht.find(__key); + } + + _Tp& operator[](const key_type& __key) + { + return _M_ht.find_or_insert(value_type(__key, _Tp())).second; + } + + size_type count(const key_type& __key) const { return _M_ht.count(__key); } + + std::pair<iterator, iterator> equal_range(const key_type& __key) + { + return _M_ht.equal_range(__key); + } + std::pair<const_iterator, const_iterator> equal_range( + const key_type& __key) const + { + return _M_ht.equal_range(__key); + } + + size_type erase(const key_type& __key) { return _M_ht.erase(__key); } + void erase(iterator __it) { _M_ht.erase(__it); } + void erase(iterator __f, iterator __l) { _M_ht.erase(__f, __l); } + void clear() { _M_ht.clear(); } + + void resize(size_type __hint) { _M_ht.resize(__hint); } + size_type bucket_count() const { return _M_ht.bucket_count(); } + size_type max_bucket_count() const { return _M_ht.max_bucket_count(); } + size_type elems_in_bucket(size_type __n) const + { + return _M_ht.elems_in_bucket(__n); + } +}; + +template <class _Key, class _Tp, class _HashFcn, class _EqlKey, class _Alloc> +bool operator==(const hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm1, + const hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm2) +{ + return __hm1._M_ht == __hm2._M_ht; +} + +template <class _Key, class _Tp, class _HashFcn, class _EqlKey, class _Alloc> +inline bool operator!=( + const hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm1, + const hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm2) +{ + return !(__hm1 == __hm2); +} + +template <class _Key, class _Tp, class _HashFcn, class _EqlKey, class _Alloc> +inline void swap(hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm1, + hash_map<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm2) +{ + __hm1.swap(__hm2); +} + +// Forward declaration of equality operator; needed for friend declaration. + +template <class _Key, class _Tp, class _HashFcn = hash<_Key>, + class _EqualKey = std::equal_to<_Key>, + class _Alloc = std::allocator<char> > +class hash_multimap; + +template <class _Key, class _Tp, class _HF, class _EqKey, class _Alloc> +bool operator==(const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm1, + const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm2); + +template <class _Key, class _Tp, class _HashFcn, class _EqualKey, class _Alloc> +class hash_multimap +{ +private: + typedef hashtable<std::pair<const _Key, _Tp>, _Key, _HashFcn, + hash_select1st<const _Key, _Tp>, _EqualKey, _Alloc> + _Ht; + _Ht _M_ht; + +public: + typedef typename _Ht::key_type key_type; + typedef _Tp data_type; + typedef _Tp mapped_type; + typedef typename _Ht::value_type value_type; + typedef typename _Ht::hasher hasher; + typedef typename _Ht::key_equal key_equal; + + typedef typename _Ht::size_type size_type; + typedef typename _Ht::difference_type difference_type; + typedef typename _Ht::pointer pointer; + typedef typename _Ht::const_pointer const_pointer; + typedef typename _Ht::reference reference; + typedef typename _Ht::const_reference const_reference; + + typedef typename _Ht::iterator iterator; + typedef typename _Ht::const_iterator const_iterator; + + typedef typename _Ht::allocator_type allocator_type; + + hasher hash_funct() const { return _M_ht.hash_funct(); } + key_equal key_eq() const { return _M_ht.key_eq(); } + allocator_type get_allocator() const { return _M_ht.get_allocator(); } + +public: + hash_multimap() + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + } + explicit hash_multimap(size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + } + hash_multimap(size_type __n, const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + } + hash_multimap(size_type __n, const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + } + + template <class _InputIterator> + hash_multimap(_InputIterator __f, _InputIterator __l) + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multimap(_InputIterator __f, _InputIterator __l, size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multimap(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multimap(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + _M_ht.insert_equal(__f, __l); + } + +public: + size_type size() const { return _M_ht.size(); } + size_type max_size() const { return _M_ht.max_size(); } + bool empty() const { return _M_ht.empty(); } + void swap(hash_multimap& __hs) { _M_ht.swap(__hs._M_ht); } + + friend bool operator==<>(const hash_multimap&, const hash_multimap&); + + iterator begin() { return _M_ht.begin(); } + iterator end() { return _M_ht.end(); } + const_iterator begin() const { return _M_ht.begin(); } + const_iterator end() const { return _M_ht.end(); } + +public: + iterator insert(const value_type& __obj) + { + return _M_ht.insert_equal(__obj); + } + template <class _InputIterator> + void insert(_InputIterator __f, _InputIterator __l) + { + _M_ht.insert_equal(__f, __l); + } + iterator insert_noresize(const value_type& __obj) + { + return _M_ht.insert_equal_noresize(__obj); + } + + iterator find(const key_type& __key) { return _M_ht.find(__key); } + const_iterator find(const key_type& __key) const + { + return _M_ht.find(__key); + } + + size_type count(const key_type& __key) const { return _M_ht.count(__key); } + + std::pair<iterator, iterator> equal_range(const key_type& __key) + { + return _M_ht.equal_range(__key); + } + std::pair<const_iterator, const_iterator> equal_range( + const key_type& __key) const + { + return _M_ht.equal_range(__key); + } + + size_type erase(const key_type& __key) { return _M_ht.erase(__key); } + void erase(iterator __it) { _M_ht.erase(__it); } + void erase(iterator __f, iterator __l) { _M_ht.erase(__f, __l); } + void clear() { _M_ht.clear(); } + +public: + void resize(size_type __hint) { _M_ht.resize(__hint); } + size_type bucket_count() const { return _M_ht.bucket_count(); } + size_type max_bucket_count() const { return _M_ht.max_bucket_count(); } + size_type elems_in_bucket(size_type __n) const + { + return _M_ht.elems_in_bucket(__n); + } +}; + +template <class _Key, class _Tp, class _HF, class _EqKey, class _Alloc> +bool operator==(const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm1, + const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm2) +{ + return __hm1._M_ht == __hm2._M_ht; +} + +template <class _Key, class _Tp, class _HF, class _EqKey, class _Alloc> +inline bool operator!=( + const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm1, + const hash_multimap<_Key, _Tp, _HF, _EqKey, _Alloc>& __hm2) +{ + return !(__hm1 == __hm2); +} + +template <class _Key, class _Tp, class _HashFcn, class _EqlKey, class _Alloc> +inline void swap(hash_multimap<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm1, + hash_multimap<_Key, _Tp, _HashFcn, _EqlKey, _Alloc>& __hm2) +{ + __hm1.swap(__hm2); +} + +} // namespace @KWSYS_NAMESPACE@ + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +# pragma reset woff 1174 +# pragma reset woff 1375 +#endif + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif diff --git a/Source/kwsys/hash_set.hxx.in b/Source/kwsys/hash_set.hxx.in new file mode 100644 index 0000000..f4a37ee --- /dev/null +++ b/Source/kwsys/hash_set.hxx.in @@ -0,0 +1,392 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +/* + * Copyright (c) 1996 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ +#ifndef @KWSYS_NAMESPACE@_hash_set_hxx +#define @KWSYS_NAMESPACE@_hash_set_hxx + +#include <@KWSYS_NAMESPACE@/hashtable.hxx> + +#include <@KWSYS_NAMESPACE@/hash_fun.hxx> + +#include <functional> // equal_to + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4284) +# pragma warning(disable : 4786) +#endif + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +# pragma set woff 1174 +# pragma set woff 1375 +#endif + +namespace @KWSYS_NAMESPACE@ { + +// identity is an extension: it is not part of the standard. +template <class _Tp> +struct _Identity +{ + const _Tp& operator()(const _Tp& __x) const { return __x; } +}; + +// Forward declaration of equality operator; needed for friend declaration. + +template <class _Value, class _HashFcn = hash<_Value>, + class _EqualKey = std::equal_to<_Value>, + class _Alloc = std::allocator<char> > +class hash_set; + +template <class _Value, class _HashFcn, class _EqualKey, class _Alloc> +bool operator==(const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs2); + +template <class _Value, class _HashFcn, class _EqualKey, class _Alloc> +class hash_set +{ +private: + typedef hashtable<_Value, _Value, _HashFcn, _Identity<_Value>, _EqualKey, + _Alloc> + _Ht; + _Ht _M_ht; + +public: + typedef typename _Ht::key_type key_type; + typedef typename _Ht::value_type value_type; + typedef typename _Ht::hasher hasher; + typedef typename _Ht::key_equal key_equal; + + typedef typename _Ht::size_type size_type; + typedef typename _Ht::difference_type difference_type; + typedef typename _Ht::const_pointer pointer; + typedef typename _Ht::const_pointer const_pointer; + typedef typename _Ht::const_reference reference; + typedef typename _Ht::const_reference const_reference; + + typedef typename _Ht::const_iterator iterator; + typedef typename _Ht::const_iterator const_iterator; + + typedef typename _Ht::allocator_type allocator_type; + + hasher hash_funct() const { return _M_ht.hash_funct(); } + key_equal key_eq() const { return _M_ht.key_eq(); } + allocator_type get_allocator() const { return _M_ht.get_allocator(); } + +public: + hash_set() + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + } + explicit hash_set(size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + } + hash_set(size_type __n, const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + } + hash_set(size_type __n, const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + } + + template <class _InputIterator> + hash_set(_InputIterator __f, _InputIterator __l) + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_set(_InputIterator __f, _InputIterator __l, size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_set(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + _M_ht.insert_unique(__f, __l); + } + template <class _InputIterator> + hash_set(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + _M_ht.insert_unique(__f, __l); + } + +public: + size_type size() const { return _M_ht.size(); } + size_type max_size() const { return _M_ht.max_size(); } + bool empty() const { return _M_ht.empty(); } + void swap(hash_set& __hs) { _M_ht.swap(__hs._M_ht); } + + friend bool operator==<>(const hash_set&, const hash_set&); + + iterator begin() const { return _M_ht.begin(); } + iterator end() const { return _M_ht.end(); } + +public: + std::pair<iterator, bool> insert(const value_type& __obj) + { + typedef typename _Ht::iterator _Ht_iterator; + std::pair<_Ht_iterator, bool> __p = _M_ht.insert_unique(__obj); + return std::pair<iterator, bool>(__p.first, __p.second); + } + template <class _InputIterator> + void insert(_InputIterator __f, _InputIterator __l) + { + _M_ht.insert_unique(__f, __l); + } + std::pair<iterator, bool> insert_noresize(const value_type& __obj) + { + typedef typename _Ht::iterator _Ht_iterator; + std::pair<_Ht_iterator, bool> __p = _M_ht.insert_unique_noresize(__obj); + return std::pair<iterator, bool>(__p.first, __p.second); + } + + iterator find(const key_type& __key) const { return _M_ht.find(__key); } + + size_type count(const key_type& __key) const { return _M_ht.count(__key); } + + std::pair<iterator, iterator> equal_range(const key_type& __key) const + { + return _M_ht.equal_range(__key); + } + + size_type erase(const key_type& __key) { return _M_ht.erase(__key); } + void erase(iterator __it) { _M_ht.erase(__it); } + void erase(iterator __f, iterator __l) { _M_ht.erase(__f, __l); } + void clear() { _M_ht.clear(); } + +public: + void resize(size_type __hint) { _M_ht.resize(__hint); } + size_type bucket_count() const { return _M_ht.bucket_count(); } + size_type max_bucket_count() const { return _M_ht.max_bucket_count(); } + size_type elems_in_bucket(size_type __n) const + { + return _M_ht.elems_in_bucket(__n); + } +}; + +template <class _Value, class _HashFcn, class _EqualKey, class _Alloc> +bool operator==(const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + return __hs1._M_ht == __hs2._M_ht; +} + +template <class _Value, class _HashFcn, class _EqualKey, class _Alloc> +inline bool operator!=( + const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_set<_Value, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + return !(__hs1 == __hs2); +} + +template <class _Val, class _HashFcn, class _EqualKey, class _Alloc> +inline void swap(hash_set<_Val, _HashFcn, _EqualKey, _Alloc>& __hs1, + hash_set<_Val, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + __hs1.swap(__hs2); +} + +template <class _Value, class _HashFcn = hash<_Value>, + class _EqualKey = std::equal_to<_Value>, + class _Alloc = std::allocator<char> > +class hash_multiset; + +template <class _Val, class _HashFcn, class _EqualKey, class _Alloc> +bool operator==(const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs2); + +template <class _Value, class _HashFcn, class _EqualKey, class _Alloc> +class hash_multiset +{ +private: + typedef hashtable<_Value, _Value, _HashFcn, _Identity<_Value>, _EqualKey, + _Alloc> + _Ht; + _Ht _M_ht; + +public: + typedef typename _Ht::key_type key_type; + typedef typename _Ht::value_type value_type; + typedef typename _Ht::hasher hasher; + typedef typename _Ht::key_equal key_equal; + + typedef typename _Ht::size_type size_type; + typedef typename _Ht::difference_type difference_type; + typedef typename _Ht::const_pointer pointer; + typedef typename _Ht::const_pointer const_pointer; + typedef typename _Ht::const_reference reference; + typedef typename _Ht::const_reference const_reference; + + typedef typename _Ht::const_iterator iterator; + typedef typename _Ht::const_iterator const_iterator; + + typedef typename _Ht::allocator_type allocator_type; + + hasher hash_funct() const { return _M_ht.hash_funct(); } + key_equal key_eq() const { return _M_ht.key_eq(); } + allocator_type get_allocator() const { return _M_ht.get_allocator(); } + +public: + hash_multiset() + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + } + explicit hash_multiset(size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + } + hash_multiset(size_type __n, const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + } + hash_multiset(size_type __n, const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + } + + template <class _InputIterator> + hash_multiset(_InputIterator __f, _InputIterator __l) + : _M_ht(100, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multiset(_InputIterator __f, _InputIterator __l, size_type __n) + : _M_ht(__n, hasher(), key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multiset(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf) + : _M_ht(__n, __hf, key_equal(), allocator_type()) + { + _M_ht.insert_equal(__f, __l); + } + template <class _InputIterator> + hash_multiset(_InputIterator __f, _InputIterator __l, size_type __n, + const hasher& __hf, const key_equal& __eql, + const allocator_type& __a = allocator_type()) + : _M_ht(__n, __hf, __eql, __a) + { + _M_ht.insert_equal(__f, __l); + } + +public: + size_type size() const { return _M_ht.size(); } + size_type max_size() const { return _M_ht.max_size(); } + bool empty() const { return _M_ht.empty(); } + void swap(hash_multiset& hs) { _M_ht.swap(hs._M_ht); } + + friend bool operator==<>(const hash_multiset&, const hash_multiset&); + + iterator begin() const { return _M_ht.begin(); } + iterator end() const { return _M_ht.end(); } + +public: + iterator insert(const value_type& __obj) + { + return _M_ht.insert_equal(__obj); + } + template <class _InputIterator> + void insert(_InputIterator __f, _InputIterator __l) + { + _M_ht.insert_equal(__f, __l); + } + iterator insert_noresize(const value_type& __obj) + { + return _M_ht.insert_equal_noresize(__obj); + } + + iterator find(const key_type& __key) const { return _M_ht.find(__key); } + + size_type count(const key_type& __key) const { return _M_ht.count(__key); } + + std::pair<iterator, iterator> equal_range(const key_type& __key) const + { + return _M_ht.equal_range(__key); + } + + size_type erase(const key_type& __key) { return _M_ht.erase(__key); } + void erase(iterator __it) { _M_ht.erase(__it); } + void erase(iterator __f, iterator __l) { _M_ht.erase(__f, __l); } + void clear() { _M_ht.clear(); } + +public: + void resize(size_type __hint) { _M_ht.resize(__hint); } + size_type bucket_count() const { return _M_ht.bucket_count(); } + size_type max_bucket_count() const { return _M_ht.max_bucket_count(); } + size_type elems_in_bucket(size_type __n) const + { + return _M_ht.elems_in_bucket(__n); + } +}; + +template <class _Val, class _HashFcn, class _EqualKey, class _Alloc> +bool operator==(const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + return __hs1._M_ht == __hs2._M_ht; +} + +template <class _Val, class _HashFcn, class _EqualKey, class _Alloc> +inline bool operator!=( + const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs1, + const hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + return !(__hs1 == __hs2); +} + +template <class _Val, class _HashFcn, class _EqualKey, class _Alloc> +inline void swap(hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs1, + hash_multiset<_Val, _HashFcn, _EqualKey, _Alloc>& __hs2) +{ + __hs1.swap(__hs2); +} + +} // namespace @KWSYS_NAMESPACE@ + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +# pragma reset woff 1174 +# pragma reset woff 1375 +#endif + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif diff --git a/Source/kwsys/hashtable.hxx.in b/Source/kwsys/hashtable.hxx.in new file mode 100644 index 0000000..8c4b002 --- /dev/null +++ b/Source/kwsys/hashtable.hxx.in @@ -0,0 +1,995 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +/* + * Copyright (c) 1996 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ +#ifdef __BORLANDC__ +# pragma warn - 8027 /* 'for' not inlined. */ +# pragma warn - 8026 /* 'exception' not inlined. */ +#endif + +#ifndef @KWSYS_NAMESPACE@_hashtable_hxx +# define @KWSYS_NAMESPACE@_hashtable_hxx + +# include <@KWSYS_NAMESPACE@/Configure.hxx> + +# include <algorithm> // lower_bound +# include <iterator> // iterator_traits +# include <memory> // allocator +# include <stddef.h> // size_t +# include <utility> // pair +# include <vector> // vector + +# if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4284) +# pragma warning(disable : 4786) +# pragma warning(disable : 4512) /* no assignment operator for class */ +# endif +# if defined(__sgi) && !defined(__GNUC__) +# pragma set woff 3970 /* pointer to int conversion */ 3321 3968 +# endif + +// In C++11, clang will warn about using dynamic exception specifications +// as they are deprecated. But as this class is trying to faithfully +// mimic unordered_set and unordered_map, we want to keep the 'throw()' +// decorations below. So we suppress the warning. +# if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wdeprecated") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" +# endif +# endif + +namespace @KWSYS_NAMESPACE@ { + +template <class _Val> +struct _Hashtable_node +{ + _Hashtable_node* _M_next; + _Val _M_val; + void public_method_to_quiet_warning_about_all_methods_private(); + +private: + void operator=(_Hashtable_node<_Val> const&) = delete; +}; + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc = std::allocator<char> > +class hashtable; + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc> +struct _Hashtable_iterator; + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc> +struct _Hashtable_const_iterator; + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc> +struct _Hashtable_iterator +{ + typedef hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc> + _Hashtable; + typedef _Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, + _Alloc> + iterator; + typedef _Hashtable_const_iterator<_Val, _Key, _HashFcn, _ExtractKey, + _EqualKey, _Alloc> + const_iterator; + typedef _Hashtable_node<_Val> _Node; + + typedef std::forward_iterator_tag iterator_category; + typedef _Val value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef _Val& reference; + typedef _Val* pointer; + + _Node* _M_cur; + _Hashtable* _M_ht; + + _Hashtable_iterator(_Node* __n, _Hashtable* __tab) + : _M_cur(__n) + , _M_ht(__tab) + { + } + _Hashtable_iterator() {} + reference operator*() const { return _M_cur->_M_val; } + pointer operator->() const { return &(operator*()); } + iterator& operator++(); + iterator operator++(int); + bool operator==(const iterator& __it) const { return _M_cur == __it._M_cur; } + bool operator!=(const iterator& __it) const { return _M_cur != __it._M_cur; } +}; + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc> +struct _Hashtable_const_iterator +{ + typedef hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc> + _Hashtable; + typedef _Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, + _Alloc> + iterator; + typedef _Hashtable_const_iterator<_Val, _Key, _HashFcn, _ExtractKey, + _EqualKey, _Alloc> + const_iterator; + typedef _Hashtable_node<_Val> _Node; + + typedef std::forward_iterator_tag iterator_category; + typedef _Val value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef const _Val& reference; + typedef const _Val* pointer; + + const _Node* _M_cur; + const _Hashtable* _M_ht; + + _Hashtable_const_iterator(const _Node* __n, const _Hashtable* __tab) + : _M_cur(__n) + , _M_ht(__tab) + { + } + _Hashtable_const_iterator() {} + _Hashtable_const_iterator(const iterator& __it) + : _M_cur(__it._M_cur) + , _M_ht(__it._M_ht) + { + } + reference operator*() const { return _M_cur->_M_val; } + pointer operator->() const { return &(operator*()); } + const_iterator& operator++(); + const_iterator operator++(int); + bool operator==(const const_iterator& __it) const + { + return _M_cur == __it._M_cur; + } + bool operator!=(const const_iterator& __it) const + { + return _M_cur != __it._M_cur; + } +}; + +// Note: assumes long is at least 32 bits. +enum +{ + _stl_num_primes = 31 +}; + +// create a function with a static local to that function that returns +// the static +static inline const unsigned long* get_stl_prime_list() +{ + + static const unsigned long _stl_prime_list[_stl_num_primes] = { + 5ul, 11ul, 23ul, 53ul, 97ul, + 193ul, 389ul, 769ul, 1543ul, 3079ul, + 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, + 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, + 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, + 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, + 4294967291ul + }; + + return &_stl_prime_list[0]; +} + +static inline size_t _stl_next_prime(size_t __n) +{ + const unsigned long* __first = get_stl_prime_list(); + const unsigned long* __last = get_stl_prime_list() + (int)_stl_num_primes; + const unsigned long* pos = std::lower_bound(__first, __last, __n); + return pos == __last ? *(__last - 1) : *pos; +} + +// Forward declaration of operator==. + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +class hashtable; + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +bool operator==(const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht1, + const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht2); + +// Hashtables handle allocators a bit differently than other containers +// do. If we're using standard-conforming allocators, then a hashtable +// unconditionally has a member variable to hold its allocator, even if +// it so happens that all instances of the allocator type are identical. +// This is because, for hashtables, this extra storage is negligible. +// Additionally, a base class wouldn't serve any other purposes; it +// wouldn't, for example, simplify the exception-handling code. + +template <class _Val, class _Key, class _HashFcn, class _ExtractKey, + class _EqualKey, class _Alloc> +class hashtable +{ +public: + typedef _Key key_type; + typedef _Val value_type; + typedef _HashFcn hasher; + typedef _EqualKey key_equal; + + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + + hasher hash_funct() const { return _M_hash; } + key_equal key_eq() const { return _M_equals; } + +private: + typedef _Hashtable_node<_Val> _Node; + +public: + typedef typename _Alloc::template rebind<_Val>::other allocator_type; + allocator_type get_allocator() const { return _M_node_allocator; } + +private: + typedef + typename _Alloc::template rebind<_Node>::other _M_node_allocator_type; + typedef + typename _Alloc::template rebind<_Node*>::other _M_node_ptr_allocator_type; + typedef std::vector<_Node*, _M_node_ptr_allocator_type> _M_buckets_type; + +private: + _M_node_allocator_type _M_node_allocator; + hasher _M_hash; + key_equal _M_equals; + _ExtractKey _M_get_key; + _M_buckets_type _M_buckets; + size_type _M_num_elements; + + _Node* _M_get_node() { return _M_node_allocator.allocate(1); } + void _M_put_node(_Node* __p) { _M_node_allocator.deallocate(__p, 1); } + +public: + typedef _Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, + _Alloc> + iterator; + typedef _Hashtable_const_iterator<_Val, _Key, _HashFcn, _ExtractKey, + _EqualKey, _Alloc> + const_iterator; + + friend struct _Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, + _EqualKey, _Alloc>; + friend struct _Hashtable_const_iterator<_Val, _Key, _HashFcn, _ExtractKey, + _EqualKey, _Alloc>; + +public: + hashtable(size_type __n, const _HashFcn& __hf, const _EqualKey& __eql, + const _ExtractKey& __ext, + const allocator_type& __a = allocator_type()) + : _M_node_allocator(__a) + , _M_hash(__hf) + , _M_equals(__eql) + , _M_get_key(__ext) + , _M_buckets(__a) + , _M_num_elements(0) + { + _M_initialize_buckets(__n); + } + + hashtable(size_type __n, const _HashFcn& __hf, const _EqualKey& __eql, + const allocator_type& __a = allocator_type()) + : _M_node_allocator(__a) + , _M_hash(__hf) + , _M_equals(__eql) + , _M_get_key(_ExtractKey()) + , _M_buckets(__a) + , _M_num_elements(0) + { + _M_initialize_buckets(__n); + } + + hashtable(const hashtable& __ht) + : _M_node_allocator(__ht.get_allocator()) + , _M_hash(__ht._M_hash) + , _M_equals(__ht._M_equals) + , _M_get_key(__ht._M_get_key) + , _M_buckets(__ht.get_allocator()) + , _M_num_elements(0) + { + _M_copy_from(__ht); + } + + hashtable& operator=(const hashtable& __ht) + { + if (&__ht != this) { + clear(); + _M_hash = __ht._M_hash; + _M_equals = __ht._M_equals; + _M_get_key = __ht._M_get_key; + _M_copy_from(__ht); + } + return *this; + } + + ~hashtable() { clear(); } + + size_type size() const { return _M_num_elements; } + size_type max_size() const { return size_type(-1); } + bool empty() const { return size() == 0; } + + void swap(hashtable& __ht) + { + std::swap(_M_hash, __ht._M_hash); + std::swap(_M_equals, __ht._M_equals); + std::swap(_M_get_key, __ht._M_get_key); + _M_buckets.swap(__ht._M_buckets); + std::swap(_M_num_elements, __ht._M_num_elements); + } + + iterator begin() + { + for (size_type __n = 0; __n < _M_buckets.size(); ++__n) + if (_M_buckets[__n]) + return iterator(_M_buckets[__n], this); + return end(); + } + + iterator end() { return iterator(nullptr, this); } + + const_iterator begin() const + { + for (size_type __n = 0; __n < _M_buckets.size(); ++__n) + if (_M_buckets[__n]) + return const_iterator(_M_buckets[__n], this); + return end(); + } + + const_iterator end() const { return const_iterator(nullptr, this); } + + friend bool operator==<>(const hashtable&, const hashtable&); + +public: + size_type bucket_count() const { return _M_buckets.size(); } + + size_type max_bucket_count() const + { + return get_stl_prime_list()[(int)_stl_num_primes - 1]; + } + + size_type elems_in_bucket(size_type __bucket) const + { + size_type __result = 0; + for (_Node* __cur = _M_buckets[__bucket]; __cur; __cur = __cur->_M_next) + __result += 1; + return __result; + } + + std::pair<iterator, bool> insert_unique(const value_type& __obj) + { + resize(_M_num_elements + 1); + return insert_unique_noresize(__obj); + } + + iterator insert_equal(const value_type& __obj) + { + resize(_M_num_elements + 1); + return insert_equal_noresize(__obj); + } + + std::pair<iterator, bool> insert_unique_noresize(const value_type& __obj); + iterator insert_equal_noresize(const value_type& __obj); + + template <class _InputIterator> + void insert_unique(_InputIterator __f, _InputIterator __l) + { + insert_unique( + __f, __l, + typename std::iterator_traits<_InputIterator>::iterator_category()); + } + + template <class _InputIterator> + void insert_equal(_InputIterator __f, _InputIterator __l) + { + insert_equal( + __f, __l, + typename std::iterator_traits<_InputIterator>::iterator_category()); + } + + template <class _InputIterator> + void insert_unique(_InputIterator __f, _InputIterator __l, + std::input_iterator_tag) + { + for (; __f != __l; ++__f) + insert_unique(*__f); + } + + template <class _InputIterator> + void insert_equal(_InputIterator __f, _InputIterator __l, + std::input_iterator_tag) + { + for (; __f != __l; ++__f) + insert_equal(*__f); + } + + template <class _ForwardIterator> + void insert_unique(_ForwardIterator __f, _ForwardIterator __l, + std::forward_iterator_tag) + { + size_type __n = 0; + std::distance(__f, __l, __n); + resize(_M_num_elements + __n); + for (; __n > 0; --__n, ++__f) + insert_unique_noresize(*__f); + } + + template <class _ForwardIterator> + void insert_equal(_ForwardIterator __f, _ForwardIterator __l, + std::forward_iterator_tag) + { + size_type __n = 0; + std::distance(__f, __l, __n); + resize(_M_num_elements + __n); + for (; __n > 0; --__n, ++__f) + insert_equal_noresize(*__f); + } + + reference find_or_insert(const value_type& __obj); + + iterator find(const key_type& __key) + { + size_type __n = _M_bkt_num_key(__key); + _Node* __first; + for (__first = _M_buckets[__n]; + __first && !_M_equals(_M_get_key(__first->_M_val), __key); + __first = __first->_M_next) { + } + return iterator(__first, this); + } + + const_iterator find(const key_type& __key) const + { + size_type __n = _M_bkt_num_key(__key); + const _Node* __first; + for (__first = _M_buckets[__n]; + __first && !_M_equals(_M_get_key(__first->_M_val), __key); + __first = __first->_M_next) { + } + return const_iterator(__first, this); + } + + size_type count(const key_type& __key) const + { + const size_type __n = _M_bkt_num_key(__key); + size_type __result = 0; + + for (const _Node* __cur = _M_buckets[__n]; __cur; __cur = __cur->_M_next) + if (_M_equals(_M_get_key(__cur->_M_val), __key)) + ++__result; + return __result; + } + + std::pair<iterator, iterator> equal_range(const key_type& __key); + + std::pair<const_iterator, const_iterator> equal_range( + const key_type& __key) const; + + size_type erase(const key_type& __key); + void erase(const iterator& __it); + void erase(iterator __first, iterator __last); + + void erase(const const_iterator& __it); + void erase(const_iterator __first, const_iterator __last); + + void resize(size_type __num_elements_hint); + void clear(); + +private: + size_type _M_next_size(size_type __n) const { return _stl_next_prime(__n); } + + void _M_initialize_buckets(size_type __n) + { + const size_type __n_buckets = _M_next_size(__n); + _M_buckets.reserve(__n_buckets); + _M_buckets.insert(_M_buckets.end(), __n_buckets, (_Node*)nullptr); + _M_num_elements = 0; + } + + size_type _M_bkt_num_key(const key_type& __key) const + { + return _M_bkt_num_key(__key, _M_buckets.size()); + } + + size_type _M_bkt_num(const value_type& __obj) const + { + return _M_bkt_num_key(_M_get_key(__obj)); + } + + size_type _M_bkt_num_key(const key_type& __key, size_t __n) const + { + return _M_hash(__key) % __n; + } + + size_type _M_bkt_num(const value_type& __obj, size_t __n) const + { + return _M_bkt_num_key(_M_get_key(__obj), __n); + } + + void construct(_Val* p, const _Val& v) { new (p) _Val(v); } + void destroy(_Val* p) + { + (void)p; + p->~_Val(); + } + + _Node* _M_new_node(const value_type& __obj) + { + _Node* __n = _M_get_node(); + __n->_M_next = nullptr; + try { + construct(&__n->_M_val, __obj); + return __n; + } catch (...) { + _M_put_node(__n); + throw; + } + } + + void _M_delete_node(_Node* __n) + { + destroy(&__n->_M_val); + _M_put_node(__n); + } + + void _M_erase_bucket(const size_type __n, _Node* __first, _Node* __last); + void _M_erase_bucket(const size_type __n, _Node* __last); + + void _M_copy_from(const hashtable& __ht); +}; + +template <class _Val, class _Key, class _HF, class _ExK, class _EqK, + class _All> +_Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>& +_Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>::operator++() +{ + const _Node* __old = _M_cur; + _M_cur = _M_cur->_M_next; + if (!_M_cur) { + size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val); + while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size()) + _M_cur = _M_ht->_M_buckets[__bucket]; + } + return *this; +} + +template <class _Val, class _Key, class _HF, class _ExK, class _EqK, + class _All> +inline _Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All> +_Hashtable_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>::operator++(int) +{ + iterator __tmp = *this; + ++*this; + return __tmp; +} + +template <class _Val, class _Key, class _HF, class _ExK, class _EqK, + class _All> +_Hashtable_const_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>& +_Hashtable_const_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>::operator++() +{ + const _Node* __old = _M_cur; + _M_cur = _M_cur->_M_next; + if (!_M_cur) { + size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val); + while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size()) + _M_cur = _M_ht->_M_buckets[__bucket]; + } + return *this; +} + +template <class _Val, class _Key, class _HF, class _ExK, class _EqK, + class _All> +inline _Hashtable_const_iterator<_Val, _Key, _HF, _ExK, _EqK, _All> +_Hashtable_const_iterator<_Val, _Key, _HF, _ExK, _EqK, _All>::operator++(int) +{ + const_iterator __tmp = *this; + ++*this; + return __tmp; +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +bool operator==(const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht1, + const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht2) +{ + typedef typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::_Node _Node; + if (__ht1._M_buckets.size() != __ht2._M_buckets.size()) + return false; + for (int __n = 0; __n < __ht1._M_buckets.size(); ++__n) { + _Node* __cur1 = __ht1._M_buckets[__n]; + _Node* __cur2 = __ht2._M_buckets[__n]; + for (; __cur1 && __cur2 && __cur1->_M_val == __cur2->_M_val; + __cur1 = __cur1->_M_next, __cur2 = __cur2->_M_next) { + } + if (__cur1 || __cur2) + return false; + } + return true; +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +inline bool operator!=(const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht1, + const hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>& __ht2) +{ + return !(__ht1 == __ht2); +} + +template <class _Val, class _Key, class _HF, class _Extract, class _EqKey, + class _All> +inline void swap(hashtable<_Val, _Key, _HF, _Extract, _EqKey, _All>& __ht1, + hashtable<_Val, _Key, _HF, _Extract, _EqKey, _All>& __ht2) +{ + __ht1.swap(__ht2); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +std::pair<typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::iterator, bool> +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::insert_unique_noresize( + const value_type& __obj) +{ + const size_type __n = _M_bkt_num(__obj); + _Node* __first = _M_buckets[__n]; + + for (_Node* __cur = __first; __cur; __cur = __cur->_M_next) + if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj))) + return std::pair<iterator, bool>(iterator(__cur, this), false); + + _Node* __tmp = _M_new_node(__obj); + __tmp->_M_next = __first; + _M_buckets[__n] = __tmp; + ++_M_num_elements; + return std::pair<iterator, bool>(iterator(__tmp, this), true); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::iterator +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::insert_equal_noresize( + const value_type& __obj) +{ + const size_type __n = _M_bkt_num(__obj); + _Node* __first = _M_buckets[__n]; + + for (_Node* __cur = __first; __cur; __cur = __cur->_M_next) + if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj))) { + _Node* __tmp = _M_new_node(__obj); + __tmp->_M_next = __cur->_M_next; + __cur->_M_next = __tmp; + ++_M_num_elements; + return iterator(__tmp, this); + } + + _Node* __tmp = _M_new_node(__obj); + __tmp->_M_next = __first; + _M_buckets[__n] = __tmp; + ++_M_num_elements; + return iterator(__tmp, this); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::reference +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::find_or_insert( + const value_type& __obj) +{ + resize(_M_num_elements + 1); + + size_type __n = _M_bkt_num(__obj); + _Node* __first = _M_buckets[__n]; + + for (_Node* __cur = __first; __cur; __cur = __cur->_M_next) + if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj))) + return __cur->_M_val; + + _Node* __tmp = _M_new_node(__obj); + __tmp->_M_next = __first; + _M_buckets[__n] = __tmp; + ++_M_num_elements; + return __tmp->_M_val; +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +std::pair<typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::iterator, + typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::iterator> +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::equal_range(const key_type& __key) +{ + typedef std::pair<iterator, iterator> _Pii; + const size_type __n = _M_bkt_num_key(__key); + + for (_Node* __first = _M_buckets[__n]; __first; __first = __first->_M_next) + if (_M_equals(_M_get_key(__first->_M_val), __key)) { + for (_Node* __cur = __first->_M_next; __cur; __cur = __cur->_M_next) + if (!_M_equals(_M_get_key(__cur->_M_val), __key)) + return _Pii(iterator(__first, this), iterator(__cur, this)); + for (size_type __m = __n + 1; __m < _M_buckets.size(); ++__m) + if (_M_buckets[__m]) + return _Pii(iterator(__first, this), + iterator(_M_buckets[__m], this)); + return _Pii(iterator(__first, this), end()); + } + return _Pii(end(), end()); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +std::pair<typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::const_iterator, + typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::const_iterator> +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::equal_range( + const key_type& __key) const +{ + typedef std::pair<const_iterator, const_iterator> _Pii; + const size_type __n = _M_bkt_num_key(__key); + + for (const _Node* __first = _M_buckets[__n]; __first; + __first = __first->_M_next) { + if (_M_equals(_M_get_key(__first->_M_val), __key)) { + for (const _Node* __cur = __first->_M_next; __cur; + __cur = __cur->_M_next) + if (!_M_equals(_M_get_key(__cur->_M_val), __key)) + return _Pii(const_iterator(__first, this), + const_iterator(__cur, this)); + for (size_type __m = __n + 1; __m < _M_buckets.size(); ++__m) + if (_M_buckets[__m]) + return _Pii(const_iterator(__first, this), + const_iterator(_M_buckets[__m], this)); + return _Pii(const_iterator(__first, this), end()); + } + } + return _Pii(end(), end()); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +typename hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::size_type +hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::erase(const key_type& __key) +{ + const size_type __n = _M_bkt_num_key(__key); + _Node* __first = _M_buckets[__n]; + size_type __erased = 0; + + if (__first) { + _Node* __cur = __first; + _Node* __next = __cur->_M_next; + while (__next) { + if (_M_equals(_M_get_key(__next->_M_val), __key)) { + __cur->_M_next = __next->_M_next; + _M_delete_node(__next); + __next = __cur->_M_next; + ++__erased; + --_M_num_elements; + } else { + __cur = __next; + __next = __cur->_M_next; + } + } + if (_M_equals(_M_get_key(__first->_M_val), __key)) { + _M_buckets[__n] = __first->_M_next; + _M_delete_node(__first); + ++__erased; + --_M_num_elements; + } + } + return __erased; +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::erase(const iterator& __it) +{ + _Node* __p = __it._M_cur; + if (__p) { + const size_type __n = _M_bkt_num(__p->_M_val); + _Node* __cur = _M_buckets[__n]; + + if (__cur == __p) { + _M_buckets[__n] = __cur->_M_next; + _M_delete_node(__cur); + --_M_num_elements; + } else { + _Node* __next = __cur->_M_next; + while (__next) { + if (__next == __p) { + __cur->_M_next = __next->_M_next; + _M_delete_node(__next); + --_M_num_elements; + break; + } else { + __cur = __next; + __next = __cur->_M_next; + } + } + } + } +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::erase(iterator __first, + iterator __last) +{ + size_type __f_bucket = + __first._M_cur ? _M_bkt_num(__first._M_cur->_M_val) : _M_buckets.size(); + size_type __l_bucket = + __last._M_cur ? _M_bkt_num(__last._M_cur->_M_val) : _M_buckets.size(); + + if (__first._M_cur == __last._M_cur) + return; + else if (__f_bucket == __l_bucket) + _M_erase_bucket(__f_bucket, __first._M_cur, __last._M_cur); + else { + _M_erase_bucket(__f_bucket, __first._M_cur, nullptr); + for (size_type __n = __f_bucket + 1; __n < __l_bucket; ++__n) + _M_erase_bucket(__n, nullptr); + if (__l_bucket != _M_buckets.size()) + _M_erase_bucket(__l_bucket, __last._M_cur); + } +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +inline void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::erase( + const_iterator __first, const_iterator __last) +{ + erase(iterator(const_cast<_Node*>(__first._M_cur), + const_cast<hashtable*>(__first._M_ht)), + iterator(const_cast<_Node*>(__last._M_cur), + const_cast<hashtable*>(__last._M_ht))); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +inline void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::erase( + const const_iterator& __it) +{ + erase(iterator(const_cast<_Node*>(__it._M_cur), + const_cast<hashtable*>(__it._M_ht))); +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::resize( + size_type __num_elements_hint) +{ + const size_type __old_n = _M_buckets.size(); + if (__num_elements_hint > __old_n) { + const size_type __n = _M_next_size(__num_elements_hint); + if (__n > __old_n) { + _M_buckets_type __tmp(__n, (_Node*)(nullptr), + _M_buckets.get_allocator()); + try { + for (size_type __bucket = 0; __bucket < __old_n; ++__bucket) { + _Node* __first = _M_buckets[__bucket]; + while (__first) { + size_type __new_bucket = _M_bkt_num(__first->_M_val, __n); + _M_buckets[__bucket] = __first->_M_next; + __first->_M_next = __tmp[__new_bucket]; + __tmp[__new_bucket] = __first; + __first = _M_buckets[__bucket]; + } + } + _M_buckets.swap(__tmp); + } catch (...) { + for (size_type __bucket = 0; __bucket < __tmp.size(); ++__bucket) { + while (__tmp[__bucket]) { + _Node* __next = __tmp[__bucket]->_M_next; + _M_delete_node(__tmp[__bucket]); + __tmp[__bucket] = __next; + } + } + throw; + } + } + } +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::_M_erase_bucket( + const size_type __n, _Node* __first, _Node* __last) +{ + _Node* __cur = _M_buckets[__n]; + if (__cur == __first) + _M_erase_bucket(__n, __last); + else { + _Node* __next; + for (__next = __cur->_M_next; __next != __first; + __cur = __next, __next = __cur->_M_next) + ; + while (__next != __last) { + __cur->_M_next = __next->_M_next; + _M_delete_node(__next); + __next = __cur->_M_next; + --_M_num_elements; + } + } +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::_M_erase_bucket( + const size_type __n, _Node* __last) +{ + _Node* __cur = _M_buckets[__n]; + while (__cur != __last) { + _Node* __next = __cur->_M_next; + _M_delete_node(__cur); + __cur = __next; + _M_buckets[__n] = __cur; + --_M_num_elements; + } +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::clear() +{ + for (size_type __i = 0; __i < _M_buckets.size(); ++__i) { + _Node* __cur = _M_buckets[__i]; + while (__cur != nullptr) { + _Node* __next = __cur->_M_next; + _M_delete_node(__cur); + __cur = __next; + } + _M_buckets[__i] = nullptr; + } + _M_num_elements = 0; +} + +template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All> +void hashtable<_Val, _Key, _HF, _Ex, _Eq, _All>::_M_copy_from( + const hashtable& __ht) +{ + _M_buckets.clear(); + _M_buckets.reserve(__ht._M_buckets.size()); + _M_buckets.insert(_M_buckets.end(), __ht._M_buckets.size(), (_Node*)nullptr); + try { + for (size_type __i = 0; __i < __ht._M_buckets.size(); ++__i) { + const _Node* __cur = __ht._M_buckets[__i]; + if (__cur) { + _Node* __copy = _M_new_node(__cur->_M_val); + _M_buckets[__i] = __copy; + + for (_Node* __next = __cur->_M_next; __next; + __cur = __next, __next = __cur->_M_next) { + __copy->_M_next = _M_new_node(__next->_M_val); + __copy = __copy->_M_next; + } + } + } + _M_num_elements = __ht._M_num_elements; + } catch (...) { + clear(); + throw; + } +} + +} // namespace @KWSYS_NAMESPACE@ + +// Undo warning suppression. +# if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wdeprecated") +# pragma clang diagnostic pop +# endif +# endif + +# if defined(_MSC_VER) +# pragma warning(pop) +# endif + +#endif diff --git a/Source/kwsys/kwsysHeaderDump.pl b/Source/kwsys/kwsysHeaderDump.pl new file mode 100755 index 0000000..e3391e7 --- /dev/null +++ b/Source/kwsys/kwsysHeaderDump.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing#kwsys for details. + +if ( $#ARGV+1 < 2 ) +{ + print "Usage: ./kwsysHeaderDump.pl <name> <header>\n"; + exit(1); +} + +$name = $ARGV[0]; +$max = 0; +open(INFILE, $ARGV[1]); +while (chomp ($line = <INFILE>)) +{ + if (($line !~ /^\#/) && + ($line =~ s/.*kwsys${name}_([A-Za-z0-9_]*).*/\1/) && + ($i{$line}++ < 1)) + { + push(@lines, "$line"); + if (length($line) > $max) + { + $max = length($line); + } + } +} +close(INFILE); + +$width = $max + 13; +print sprintf("#define %-${width}s kwsys_ns(${name})\n", "kwsys${name}"); +foreach $l (@lines) +{ + print sprintf("#define %-${width}s kwsys_ns(${name}_$l)\n", + "kwsys${name}_$l"); +} +print "\n"; +print sprintf("# undef kwsys${name}\n"); +foreach $l (@lines) +{ + print sprintf("# undef kwsys${name}_$l\n"); +} diff --git a/Source/kwsys/kwsysPlatformTests.cmake b/Source/kwsys/kwsysPlatformTests.cmake new file mode 100644 index 0000000..89be4b8 --- /dev/null +++ b/Source/kwsys/kwsysPlatformTests.cmake @@ -0,0 +1,216 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing#kwsys for details. + +set(KWSYS_PLATFORM_TEST_FILE_C kwsysPlatformTestsC.c) +set(KWSYS_PLATFORM_TEST_FILE_CXX kwsysPlatformTestsCXX.cxx) + +macro(KWSYS_PLATFORM_TEST lang var description invert) + if(NOT DEFINED ${var}_COMPILED) + message(STATUS "${description}") + set(maybe_cxx_standard "") + if(CMAKE_VERSION VERSION_LESS 3.8 AND CMAKE_CXX_STANDARD) + set(maybe_cxx_standard "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") + endif() + try_compile(${var}_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${KWSYS_PLATFORM_TEST_FILE_${lang}} + COMPILE_DEFINITIONS -DTEST_${var} ${KWSYS_PLATFORM_TEST_DEFINES} ${KWSYS_PLATFORM_TEST_EXTRA_FLAGS} + CMAKE_FLAGS "-DLINK_LIBRARIES:STRING=${KWSYS_PLATFORM_TEST_LINK_LIBRARIES}" + ${maybe_cxx_standard} + OUTPUT_VARIABLE OUTPUT) + if(${var}_COMPILED) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + endif() + if(${invert} MATCHES INVERT) + if(${var}_COMPILED) + message(STATUS "${description} - no") + else() + message(STATUS "${description} - yes") + endif() + else() + if(${var}_COMPILED) + message(STATUS "${description} - yes") + else() + message(STATUS "${description} - no") + endif() + endif() + endif() + if(${invert} MATCHES INVERT) + if(${var}_COMPILED) + set(${var} 0) + else() + set(${var} 1) + endif() + else() + if(${var}_COMPILED) + set(${var} 1) + else() + set(${var} 0) + endif() + endif() +endmacro() + +macro(KWSYS_PLATFORM_TEST_RUN lang var description invert) + if(NOT DEFINED ${var}) + message(STATUS "${description}") + try_run(${var} ${var}_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${KWSYS_PLATFORM_TEST_FILE_${lang}} + COMPILE_DEFINITIONS -DTEST_${var} ${KWSYS_PLATFORM_TEST_DEFINES} ${KWSYS_PLATFORM_TEST_EXTRA_FLAGS} + OUTPUT_VARIABLE OUTPUT) + + # Note that ${var} will be a 0 return value on success. + if(${var}_COMPILED) + if(${var}) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} compiled but failed to run with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled and ran with the following output:\n${OUTPUT}\n\n") + endif() + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + set(${var} -1 CACHE INTERNAL "${description} failed to compile.") + endif() + + if(${invert} MATCHES INVERT) + if(${var}_COMPILED) + if(${var}) + message(STATUS "${description} - yes") + else() + message(STATUS "${description} - no") + endif() + else() + message(STATUS "${description} - failed to compile") + endif() + else() + if(${var}_COMPILED) + if(${var}) + message(STATUS "${description} - no") + else() + message(STATUS "${description} - yes") + endif() + else() + message(STATUS "${description} - failed to compile") + endif() + endif() + endif() + + if(${invert} MATCHES INVERT) + if(${var}_COMPILED) + if(${var}) + set(${var} 1) + else() + set(${var} 0) + endif() + else() + set(${var} 1) + endif() + else() + if(${var}_COMPILED) + if(${var}) + set(${var} 0) + else() + set(${var} 1) + endif() + else() + set(${var} 0) + endif() + endif() +endmacro() + +macro(KWSYS_PLATFORM_C_TEST var description invert) + set(KWSYS_PLATFORM_TEST_DEFINES ${KWSYS_PLATFORM_C_TEST_DEFINES}) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS ${KWSYS_PLATFORM_C_TEST_EXTRA_FLAGS}) + KWSYS_PLATFORM_TEST(C "${var}" "${description}" "${invert}") + set(KWSYS_PLATFORM_TEST_DEFINES) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS) +endmacro() + +macro(KWSYS_PLATFORM_C_TEST_RUN var description invert) + set(KWSYS_PLATFORM_TEST_DEFINES ${KWSYS_PLATFORM_C_TEST_DEFINES}) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS ${KWSYS_PLATFORM_C_TEST_EXTRA_FLAGS}) + KWSYS_PLATFORM_TEST_RUN(C "${var}" "${description}" "${invert}") + set(KWSYS_PLATFORM_TEST_DEFINES) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS) +endmacro() + +macro(KWSYS_PLATFORM_CXX_TEST var description invert) + set(KWSYS_PLATFORM_TEST_DEFINES ${KWSYS_PLATFORM_CXX_TEST_DEFINES}) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS ${KWSYS_PLATFORM_CXX_TEST_EXTRA_FLAGS}) + set(KWSYS_PLATFORM_TEST_LINK_LIBRARIES ${KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES}) + KWSYS_PLATFORM_TEST(CXX "${var}" "${description}" "${invert}") + set(KWSYS_PLATFORM_TEST_DEFINES) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS) + set(KWSYS_PLATFORM_TEST_LINK_LIBRARIES) +endmacro() + +macro(KWSYS_PLATFORM_CXX_TEST_RUN var description invert) + set(KWSYS_PLATFORM_TEST_DEFINES ${KWSYS_PLATFORM_CXX_TEST_DEFINES}) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS ${KWSYS_PLATFORM_CXX_TEST_EXTRA_FLAGS}) + KWSYS_PLATFORM_TEST_RUN(CXX "${var}" "${description}" "${invert}") + set(KWSYS_PLATFORM_TEST_DEFINES) + set(KWSYS_PLATFORM_TEST_EXTRA_FLAGS) +endmacro() + +#----------------------------------------------------------------------------- +# KWSYS_PLATFORM_INFO_TEST(lang var description) +# +# Compile test named by ${var} and store INFO strings extracted from binary. +macro(KWSYS_PLATFORM_INFO_TEST lang var description) + # We can implement this macro on CMake 2.6 and above. + if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.6) + set(${var} "") + else() + # Choose a location for the result binary. + set(KWSYS_PLATFORM_INFO_FILE + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/${var}.bin) + + # Compile the test binary. + if(NOT EXISTS ${KWSYS_PLATFORM_INFO_FILE}) + message(STATUS "${description}") + try_compile(${var}_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${KWSYS_PLATFORM_TEST_FILE_${lang}} + COMPILE_DEFINITIONS -DTEST_${var} + ${KWSYS_PLATFORM_${lang}_TEST_DEFINES} + ${KWSYS_PLATFORM_${lang}_TEST_EXTRA_FLAGS} + OUTPUT_VARIABLE OUTPUT + COPY_FILE ${KWSYS_PLATFORM_INFO_FILE} + ) + if(${var}_COMPILED) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + endif() + if(${var}_COMPILED) + message(STATUS "${description} - compiled") + else() + message(STATUS "${description} - failed") + endif() + endif() + + # Parse info strings out of the compiled binary. + if(${var}_COMPILED) + file(STRINGS ${KWSYS_PLATFORM_INFO_FILE} ${var} REGEX "INFO:[A-Za-z0-9]+\\[[^]]*\\]") + else() + set(${var} "") + endif() + + set(KWSYS_PLATFORM_INFO_FILE) + endif() +endmacro() diff --git a/Source/kwsys/kwsysPlatformTestsC.c b/Source/kwsys/kwsysPlatformTestsC.c new file mode 100644 index 0000000..b0cf7ad --- /dev/null +++ b/Source/kwsys/kwsysPlatformTestsC.c @@ -0,0 +1,108 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +/* + Macros to define main() in a cross-platform way. + + Usage: + + int KWSYS_PLATFORM_TEST_C_MAIN() + { + return 0; + } + + int KWSYS_PLATFORM_TEST_C_MAIN_ARGS(argc, argv) + { + (void)argc; (void)argv; + return 0; + } +*/ +#if defined(__CLASSIC_C__) +# define KWSYS_PLATFORM_TEST_C_MAIN() main() +# define KWSYS_PLATFORM_TEST_C_MAIN_ARGS(argc, argv) \ + main(argc, argv) int argc; \ + char* argv[]; +#else +# define KWSYS_PLATFORM_TEST_C_MAIN() main(void) +# define KWSYS_PLATFORM_TEST_C_MAIN_ARGS(argc, argv) \ + main(int argc, char* argv[]) +#endif + +#ifdef TEST_KWSYS_C_HAS_PTRDIFF_T +# include <stddef.h> +int f(ptrdiff_t n) +{ + return n > 0; +} +int KWSYS_PLATFORM_TEST_C_MAIN() +{ + char* p = 0; + ptrdiff_t d = p - p; + (void)d; + return f(p - p); +} +#endif + +#ifdef TEST_KWSYS_C_HAS_SSIZE_T +# include <unistd.h> +int f(ssize_t n) +{ + return (int)n; +} +int KWSYS_PLATFORM_TEST_C_MAIN() +{ + ssize_t n = 0; + return f(n); +} +#endif + +#ifdef TEST_KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC +# if defined(__APPLE__) +# include <AvailabilityMacros.h> +# if MAC_OS_X_VERSION_MIN_REQUIRED < 101200 +# error "clock_gettime not available on macOS < 10.12" +# endif +# endif +# include <time.h> +int KWSYS_PLATFORM_TEST_C_MAIN() +{ + struct timespec ts; + return clock_gettime(CLOCK_MONOTONIC, &ts); +} +#endif + +#ifdef TEST_KWSYS_C_TYPE_MACROS +char* info_macros = +# if defined(__SIZEOF_SHORT__) + "INFO:macro[__SIZEOF_SHORT__]\n" +# endif +# if defined(__SIZEOF_INT__) + "INFO:macro[__SIZEOF_INT__]\n" +# endif +# if defined(__SIZEOF_LONG__) + "INFO:macro[__SIZEOF_LONG__]\n" +# endif +# if defined(__SIZEOF_LONG_LONG__) + "INFO:macro[__SIZEOF_LONG_LONG__]\n" +# endif +# if defined(__SHORT_MAX__) + "INFO:macro[__SHORT_MAX__]\n" +# endif +# if defined(__INT_MAX__) + "INFO:macro[__INT_MAX__]\n" +# endif +# if defined(__LONG_MAX__) + "INFO:macro[__LONG_MAX__]\n" +# endif +# if defined(__LONG_LONG_MAX__) + "INFO:macro[__LONG_LONG_MAX__]\n" +# endif + ""; + +int KWSYS_PLATFORM_TEST_C_MAIN_ARGS(argc, argv) +{ + int require = 0; + require += info_macros[argc]; + (void)argv; + return require; +} +#endif diff --git a/Source/kwsys/kwsysPlatformTestsCXX.cxx b/Source/kwsys/kwsysPlatformTestsCXX.cxx new file mode 100644 index 0000000..cfd5666 --- /dev/null +++ b/Source/kwsys/kwsysPlatformTestsCXX.cxx @@ -0,0 +1,335 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef TEST_KWSYS_CXX_HAS_CSTDIO +# include <cstdio> +int main() +{ + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_LONG_LONG +long long f(long long n) +{ + return n; +} +int main() +{ + long long n = 0; + return static_cast<int>(f(n)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS___INT64 +__int64 f(__int64 n) +{ + return n; +} +int main() +{ + __int64 n = 0; + return static_cast<int>(f(n)); +} +#endif + +#ifdef TEST_KWSYS_CXX_STAT_HAS_ST_MTIM +# include <sys/types.h> + +# include <sys/stat.h> +# include <unistd.h> +int main() +{ + struct stat stat1; + (void)stat1.st_mtim.tv_sec; + (void)stat1.st_mtim.tv_nsec; + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_STAT_HAS_ST_MTIMESPEC +# include <sys/types.h> + +# include <sys/stat.h> +# include <unistd.h> +int main() +{ + struct stat stat1; + (void)stat1.st_mtimespec.tv_sec; + (void)stat1.st_mtimespec.tv_nsec; + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_SAME_LONG_AND___INT64 +void function(long**) +{ +} +int main() +{ + __int64** p = 0; + function(p); + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_SAME_LONG_LONG_AND___INT64 +void function(long long**) +{ +} +int main() +{ + __int64** p = 0; + function(p); + return 0; +} +#endif + +#ifdef TEST_KWSYS_IOS_HAS_ISTREAM_LONG_LONG +# include <iostream> +int test_istream(std::istream& is, long long& x) +{ + return (is >> x) ? 1 : 0; +} +int main() +{ + long long x = 0; + return test_istream(std::cin, x); +} +#endif + +#ifdef TEST_KWSYS_IOS_HAS_OSTREAM_LONG_LONG +# include <iostream> +int test_ostream(std::ostream& os, long long x) +{ + return (os << x) ? 1 : 0; +} +int main() +{ + long long x = 0; + return test_ostream(std::cout, x); +} +#endif + +#ifdef TEST_KWSYS_IOS_HAS_ISTREAM___INT64 +# include <iostream> +int test_istream(std::istream& is, __int64& x) +{ + return (is >> x) ? 1 : 0; +} +int main() +{ + __int64 x = 0; + return test_istream(std::cin, x); +} +#endif + +#ifdef TEST_KWSYS_IOS_HAS_OSTREAM___INT64 +# include <iostream> +int test_ostream(std::ostream& os, __int64 x) +{ + return (os << x) ? 1 : 0; +} +int main() +{ + __int64 x = 0; + return test_ostream(std::cout, x); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_SETENV +# include <stdlib.h> +int main() +{ + return setenv("A", "B", 1); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_UNSETENV +# include <stdlib.h> +int main() +{ + unsetenv("A"); + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H +# include <stdlib.h> +int main() +{ + char* e = environ[0]; + return e ? 0 : 1; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_GETLOADAVG +// Match feature definitions from SystemInformation.cxx +# if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +# endif +# include <stdlib.h> +int main() +{ + double loadavg[3] = { 0.0, 0.0, 0.0 }; + return getloadavg(loadavg, 3); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_RLIMIT64 +# include <sys/resource.h> +int main() +{ + struct rlimit64 rlim; + return getrlimit64(0, &rlim); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_ATOLL +# include <stdlib.h> +int main() +{ + const char* str = "1024"; + return static_cast<int>(atoll(str)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_ATOL +# include <stdlib.h> +int main() +{ + const char* str = "1024"; + return static_cast<int>(atol(str)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS__ATOI64 +# include <stdlib.h> +int main() +{ + const char* str = "1024"; + return static_cast<int>(_atoi64(str)); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_UTIMES +# include <sys/time.h> +int main() +{ + struct timeval* current_time = 0; + return utimes("/example", current_time); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_UTIMENSAT +# include <fcntl.h> +# include <sys/stat.h> +# if defined(__APPLE__) +# include <AvailabilityMacros.h> +# if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 +# error "utimensat not available on macOS < 10.13" +# endif +# endif +int main() +{ + struct timespec times[2] = { { 0, UTIME_OMIT }, { 0, UTIME_NOW } }; + return utimensat(AT_FDCWD, "/example", times, AT_SYMLINK_NOFOLLOW); +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_BACKTRACE +# if defined(__PATHSCALE__) || defined(__PATHCC__) || \ + (defined(__LSB_VERSION__) && (__LSB_VERSION__ < 41)) +backtrace does not work with this compiler or os +# endif +# if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +# endif +# include <execinfo.h> +int main() +{ + void* stackSymbols[256]; + backtrace(stackSymbols, 256); + backtrace_symbols(&stackSymbols[0], 1); + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_DLADDR +# if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +# endif +# include <dlfcn.h> +int main() +{ + Dl_info info; + int ierr = dladdr((void*)main, &info); + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_CXXABI +# if (defined(__GNUC__) || defined(__PGI)) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +# endif +# if defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5130 && __linux && \ + __SUNPRO_CC_COMPAT == 'G' +# include <iostream> +# endif +# include <cxxabi.h> +int main() +{ + int status = 0; + size_t bufferLen = 512; + char buffer[512] = { '\0' }; + const char* function = "_ZN5kwsys17SystemInformation15GetProgramStackEii"; + char* demangledFunction = + abi::__cxa_demangle(function, buffer, &bufferLen, &status); + return status; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_BORLAND_ASM +int main() +{ + int a = 1; + __asm { + xor EBX, EBX; + mov a, EBX; + } + + return a; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_BORLAND_ASM_CPUID +int main() +{ + int a = 0; + __asm { + xor EAX, EAX; + cpuid; + mov a, EAX; + } + + return a; +} +#endif + +#ifdef TEST_KWSYS_STL_HAS_WSTRING +# include <string> +void f(std::wstring*) +{ +} +int main() +{ + return 0; +} +#endif + +#ifdef TEST_KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H +# include <ext/stdio_filebuf.h> +int main() +{ + return 0; +} +#endif diff --git a/Source/kwsys/kwsysPrivate.h b/Source/kwsys/kwsysPrivate.h new file mode 100644 index 0000000..dd9c127 --- /dev/null +++ b/Source/kwsys/kwsysPrivate.h @@ -0,0 +1,34 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef KWSYS_NAMESPACE +# error "Do not include kwsysPrivate.h outside of kwsys c and cxx files." +#endif + +#ifndef _kwsysPrivate_h +# define _kwsysPrivate_h + +/* + Define KWSYS_HEADER macro to help the c and cxx files include kwsys + headers from the configured namespace directory. The macro can be + used like this: + + #include KWSYS_HEADER(Directory.hxx) + #include KWSYS_HEADER(std/vector) +*/ +/* clang-format off */ +#define KWSYS_HEADER(x) KWSYS_HEADER0(KWSYS_NAMESPACE/x) +/* clang-format on */ +# define KWSYS_HEADER0(x) KWSYS_HEADER1(x) +# define KWSYS_HEADER1(x) <x> + +/* + Define KWSYS_NAMESPACE_STRING to be a string constant containing the + name configured for this instance of the kwsys library. +*/ +# define KWSYS_NAMESPACE_STRING KWSYS_NAMESPACE_STRING0(KWSYS_NAMESPACE) +# define KWSYS_NAMESPACE_STRING0(x) KWSYS_NAMESPACE_STRING1(x) +# define KWSYS_NAMESPACE_STRING1(x) # x + +#else +# error "kwsysPrivate.h included multiple times." +#endif diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx new file mode 100644 index 0000000..79ebe1a --- /dev/null +++ b/Source/kwsys/testCommandLineArguments.cxx @@ -0,0 +1,209 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(CommandLineArguments.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "CommandLineArguments.hxx.in" +#endif + +#include <iostream> +#include <vector> + +#include <stddef.h> /* size_t */ +#include <string.h> /* strcmp */ + +static void* random_ptr = reinterpret_cast<void*>(0x123); + +static int argument(const char* arg, const char* value, void* call_data) +{ + std::cout << "Got argument: \"" << arg << "\" value: \"" + << (value ? value : "(null)") << "\"" << std::endl; + if (call_data != random_ptr) { + std::cerr << "Problem processing call_data" << std::endl; + return 0; + } + return 1; +} + +static int unknown_argument(const char* argument, void* call_data) +{ + std::cout << "Got unknown argument: \"" << argument << "\"" << std::endl; + if (call_data != random_ptr) { + std::cerr << "Problem processing call_data" << std::endl; + return 0; + } + return 1; +} + +static bool CompareTwoItemsOnList(bool i1, bool i2) +{ + return i1 == i2; +} +static bool CompareTwoItemsOnList(int i1, int i2) +{ + return i1 == i2; +} +static bool CompareTwoItemsOnList(double i1, double i2) +{ + return i1 == i2; +} +static bool CompareTwoItemsOnList(const char* i1, const char* i2) +{ + return strcmp(i1, i2) == 0; +} +static bool CompareTwoItemsOnList(const std::string& i1, const std::string& i2) +{ + return i1 == i2; +} + +int testCommandLineArguments(int argc, char* argv[]) +{ + // Example run: ./testCommandLineArguments --some-int-variable 4 + // --another-bool-variable --some-bool-variable=yes + // --some-stl-string-variable=foobar --set-bool-arg1 --set-bool-arg2 + // --some-string-variable=hello + + int res = 0; + kwsys::CommandLineArguments arg; + arg.Initialize(argc, argv); + + // For error handling + arg.SetClientData(random_ptr); + arg.SetUnknownArgumentCallback(unknown_argument); + + int some_int_variable = 10; + double some_double_variable = 10.10; + char* some_string_variable = nullptr; + std::string some_stl_string_variable; + bool some_bool_variable = false; + bool some_bool_variable1 = false; + bool bool_arg1 = false; + int bool_arg2 = 0; + + std::vector<int> numbers_argument; + int valid_numbers[] = { 5, 1, 8, 3, 7, 1, 3, 9, 7, 1 }; + + std::vector<double> doubles_argument; + double valid_doubles[] = { 12.5, 1.31, 22 }; + + std::vector<bool> bools_argument; + bool valid_bools[] = { true, true, false }; + + std::vector<char*> strings_argument; + const char* valid_strings[] = { "andy", "bill", "brad", "ken" }; + + std::vector<std::string> stl_strings_argument; + std::string valid_stl_strings[] = { "ken", "brad", "bill", "andy" }; + + using argT = kwsys::CommandLineArguments; + + arg.AddArgument("--some-int-variable", argT::SPACE_ARGUMENT, + &some_int_variable, "Set some random int variable"); + arg.AddArgument("--some-double-variable", argT::CONCAT_ARGUMENT, + &some_double_variable, "Set some random double variable"); + arg.AddArgument("--some-string-variable", argT::EQUAL_ARGUMENT, + &some_string_variable, "Set some random string variable"); + arg.AddArgument("--some-stl-string-variable", argT::EQUAL_ARGUMENT, + &some_stl_string_variable, + "Set some random stl string variable"); + arg.AddArgument("--some-bool-variable", argT::EQUAL_ARGUMENT, + &some_bool_variable, "Set some random bool variable"); + arg.AddArgument("--another-bool-variable", argT::NO_ARGUMENT, + &some_bool_variable1, "Set some random bool variable 1"); + arg.AddBooleanArgument("--set-bool-arg1", &bool_arg1, + "Test AddBooleanArgument 1"); + arg.AddBooleanArgument("--set-bool-arg2", &bool_arg2, + "Test AddBooleanArgument 2"); + arg.AddArgument("--some-multi-argument", argT::MULTI_ARGUMENT, + &numbers_argument, "Some multiple values variable"); + arg.AddArgument("-N", argT::SPACE_ARGUMENT, &doubles_argument, + "Some explicit multiple values variable"); + arg.AddArgument("-BB", argT::CONCAT_ARGUMENT, &bools_argument, + "Some explicit multiple values variable"); + arg.AddArgument("-SS", argT::EQUAL_ARGUMENT, &strings_argument, + "Some explicit multiple values variable"); + arg.AddArgument("-SSS", argT::MULTI_ARGUMENT, &stl_strings_argument, + "Some explicit multiple values variable"); + + arg.AddCallback("-A", argT::NO_ARGUMENT, argument, random_ptr, + "Some option -A. This option has a multiline comment. It " + "should demonstrate how the code splits lines."); + arg.AddCallback("-B", argT::SPACE_ARGUMENT, argument, random_ptr, + "Option -B takes argument with space"); + arg.AddCallback("-C", argT::EQUAL_ARGUMENT, argument, random_ptr, + "Option -C takes argument after ="); + arg.AddCallback("-D", argT::CONCAT_ARGUMENT, argument, random_ptr, + "This option takes concatenated argument"); + arg.AddCallback("--long1", argT::NO_ARGUMENT, argument, random_ptr, "-A"); + arg.AddCallback("--long2", argT::SPACE_ARGUMENT, argument, random_ptr, "-B"); + arg.AddCallback("--long3", argT::EQUAL_ARGUMENT, argument, random_ptr, + "Same as -C but a bit different"); + arg.AddCallback("--long4", argT::CONCAT_ARGUMENT, argument, random_ptr, + "-C"); + + if (!arg.Parse()) { + std::cerr << "Problem parsing arguments" << std::endl; + res = 1; + } + std::cout << "Help: " << arg.GetHelp() << std::endl; + + std::cout << "Some int variable was set to: " << some_int_variable + << std::endl; + std::cout << "Some double variable was set to: " << some_double_variable + << std::endl; + if (some_string_variable && + strcmp(some_string_variable, "test string with space") == 0) { + std::cout << "Some string variable was set to: " << some_string_variable + << std::endl; + delete[] some_string_variable; + } else { + std::cerr << "Problem setting string variable" << std::endl; + res = 1; + } + size_t cc; +#define CompareTwoLists(list1, list_valid, lsize) \ + do { \ + if (list1.size() != lsize) { \ + std::cerr << "Problem setting " #list1 ". Size is: " << list1.size() \ + << " should be: " << lsize << std::endl; \ + res = 1; \ + } else { \ + std::cout << #list1 " argument set:"; \ + for (cc = 0; cc < lsize; ++cc) { \ + std::cout << " " << list1[cc]; \ + if (!CompareTwoItemsOnList(list1[cc], list_valid[cc])) { \ + std::cerr << "Problem setting " #list1 ". Value of " << cc \ + << " is: [" << list1[cc] << "] <> [" << list_valid[cc] \ + << "]" << std::endl; \ + res = 1; \ + break; \ + } \ + } \ + std::cout << std::endl; \ + } \ + } while (0) + CompareTwoLists(numbers_argument, valid_numbers, 10); + CompareTwoLists(doubles_argument, valid_doubles, 3); + CompareTwoLists(bools_argument, valid_bools, 3); + CompareTwoLists(strings_argument, valid_strings, 4); + CompareTwoLists(stl_strings_argument, valid_stl_strings, 4); + + std::cout << "Some STL String variable was set to: " + << some_stl_string_variable << std::endl; + std::cout << "Some bool variable was set to: " << some_bool_variable + << std::endl; + std::cout << "Some bool variable was set to: " << some_bool_variable1 + << std::endl; + std::cout << "bool_arg1 variable was set to: " << bool_arg1 << std::endl; + std::cout << "bool_arg2 variable was set to: " << bool_arg2 << std::endl; + std::cout << std::endl; + + for (cc = 0; cc < strings_argument.size(); ++cc) { + delete[] strings_argument[cc]; + strings_argument[cc] = nullptr; + } + return res; +} diff --git a/Source/kwsys/testCommandLineArguments1.cxx b/Source/kwsys/testCommandLineArguments1.cxx new file mode 100644 index 0000000..cbc3002 --- /dev/null +++ b/Source/kwsys/testCommandLineArguments1.cxx @@ -0,0 +1,93 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(CommandLineArguments.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "CommandLineArguments.hxx.in" +#endif + +#include <iostream> +#include <vector> + +#include <assert.h> /* assert */ +#include <string.h> /* strcmp */ + +int testCommandLineArguments1(int argc, char* argv[]) +{ + kwsys::CommandLineArguments arg; + arg.Initialize(argc, argv); + + int n = 0; + char* m = nullptr; + std::string p; + int res = 0; + + using argT = kwsys::CommandLineArguments; + arg.AddArgument("-n", argT::SPACE_ARGUMENT, &n, "Argument N"); + arg.AddArgument("-m", argT::EQUAL_ARGUMENT, &m, "Argument M"); + arg.AddBooleanArgument("-p", &p, "Argument P"); + + arg.StoreUnusedArguments(true); + + if (!arg.Parse()) { + std::cerr << "Problem parsing arguments" << std::endl; + res = 1; + } + if (n != 24) { + std::cout << "Problem setting N. Value of N: " << n << std::endl; + res = 1; + } + if (!m || strcmp(m, "test value") != 0) { + std::cout << "Problem setting M. Value of M: " << m << std::endl; + res = 1; + } + if (p != "1") { + std::cout << "Problem setting P. Value of P: " << p << std::endl; + res = 1; + } + std::cout << "Value of N: " << n << std::endl; + std::cout << "Value of M: " << m << std::endl; + std::cout << "Value of P: " << p << std::endl; + if (m) { + delete[] m; + } + + char** newArgv = nullptr; + int newArgc = 0; + arg.GetUnusedArguments(&newArgc, &newArgv); + int cc; + const char* valid_unused_args[9] = { nullptr, + "--ignored", + "--second-ignored", + "third-ignored", + "some", + "junk", + "at", + "the", + "end" }; + if (newArgc != 9) { + std::cerr << "Bad number of unused arguments: " << newArgc << std::endl; + res = 1; + } + for (cc = 0; cc < newArgc; ++cc) { + assert(newArgv[cc]); /* Quiet Clang scan-build. */ + std::cout << "Unused argument[" << cc << "] = [" << newArgv[cc] << "]" + << std::endl; + if (cc >= 9) { + std::cerr << "Too many unused arguments: " << cc << std::endl; + res = 1; + } else if (valid_unused_args[cc] && + strcmp(valid_unused_args[cc], newArgv[cc]) != 0) { + std::cerr << "Bad unused argument [" << cc << "] \"" << newArgv[cc] + << "\" should be: \"" << valid_unused_args[cc] << "\"" + << std::endl; + res = 1; + } + } + arg.DeleteRemainingArguments(newArgc, &newArgv); + + return res; +} diff --git a/Source/kwsys/testConfigure.cxx b/Source/kwsys/testConfigure.cxx new file mode 100644 index 0000000..a3c2ed3 --- /dev/null +++ b/Source/kwsys/testConfigure.cxx @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying +file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Configure.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Configure.hxx.in" +#endif + +static bool testFallthrough(int n) +{ + int r = 0; + switch (n) { + case 1: + ++r; + KWSYS_FALLTHROUGH; + default: + ++r; + } + return r == 2; +} + +int testConfigure(int, char* []) +{ + bool res = true; + res = testFallthrough(1) && res; + return res ? 0 : 1; +} diff --git a/Source/kwsys/testConsoleBuf.cxx b/Source/kwsys/testConsoleBuf.cxx new file mode 100644 index 0000000..4b7ddf0 --- /dev/null +++ b/Source/kwsys/testConsoleBuf.cxx @@ -0,0 +1,782 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +// Ignore Windows version levels defined by command-line flags. This +// source needs access to all APIs available on the host in order for +// the test to run properly. The test binary is not installed anyway. +#undef _WIN32_WINNT +#undef NTDDI_VERSION + +#include KWSYS_HEADER(Encoding.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Encoding.hxx.in" +#endif + +#if defined(_WIN32) + +# include <algorithm> +# include <iomanip> +# include <iostream> +# include <stdexcept> +# include <string.h> +# include <wchar.h> +# include <windows.h> + +# include "testConsoleBuf.hxx" + +# if defined(_MSC_VER) && _MSC_VER >= 1800 +# define KWSYS_WINDOWS_DEPRECATED_GetVersion +# endif +// يونيكود +static const WCHAR UnicodeInputTestString[] = + L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!"; +static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE; + +static const DWORD waitTimeout = 10 * 1000; +static STARTUPINFO startupInfo; +static PROCESS_INFORMATION processInfo; +static HANDLE beforeInputEvent; +static HANDLE afterOutputEvent; +static std::string encodedInputTestString; +static std::string encodedTestString; + +static void displayError(DWORD errorCode) +{ + std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl; + LPWSTR message; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) { + std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message) + << std::endl; + HeapFree(GetProcessHeap(), 0, message); + } else { + std::cerr << "FormatMessage() failed with error: 0x" << GetLastError() + << "!" << std::endl; + } + std::cerr.unsetf(std::ios::hex); +} + +std::basic_streambuf<char>* errstream(const char* unused) +{ + static_cast<void>(unused); + return std::cerr.rdbuf(); +} + +std::basic_streambuf<wchar_t>* errstream(const wchar_t* unused) +{ + static_cast<void>(unused); + return std::wcerr.rdbuf(); +} + +template <typename T> +static void dumpBuffers(const T* expected, const T* received, size_t size) +{ + std::basic_ostream<T> err(errstream(expected)); + err << "Expected output: '" << std::basic_string<T>(expected, size) << "'" + << std::endl; + if (err.fail()) { + err.clear(); + err << "--- Error while outputting ---" << std::endl; + } + err << "Received output: '" << std::basic_string<T>(received, size) << "'" + << std::endl; + if (err.fail()) { + err.clear(); + err << "--- Error while outputting ---" << std::endl; + } + std::cerr << "Expected output | Received output" << std::endl; + for (size_t i = 0; i < size; i++) { + std::cerr << std::setbase(16) << std::setfill('0') << " " + << "0x" << std::setw(8) << static_cast<unsigned int>(expected[i]) + << " | " + << "0x" << std::setw(8) + << static_cast<unsigned int>(received[i]); + if (static_cast<unsigned int>(expected[i]) != + static_cast<unsigned int>(received[i])) { + std::cerr << " MISMATCH!"; + } + std::cerr << std::endl; + } + std::cerr << std::endl; +} + +static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr) +{ + BOOL bInheritHandles = FALSE; + DWORD dwCreationFlags = 0; + memset(&processInfo, 0, sizeof(processInfo)); + memset(&startupInfo, 0, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + startupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.wShowWindow = SW_HIDE; + if (hIn || hOut || hErr) { + startupInfo.dwFlags |= STARTF_USESTDHANDLES; + startupInfo.hStdInput = hIn; + startupInfo.hStdOutput = hOut; + startupInfo.hStdError = hErr; + bInheritHandles = TRUE; + } + + WCHAR cmd[MAX_PATH]; + if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) { + std::cerr << "GetModuleFileName failed!" << std::endl; + return false; + } + WCHAR* p = cmd + wcslen(cmd); + while (p > cmd && *p != L'\\') + p--; + *(p + 1) = 0; + wcscat(cmd, cmdConsoleBufChild); + wcscat(cmd, L".exe"); + + bool success = + CreateProcessW(nullptr, // No module name (use command line) + cmd, // Command line + nullptr, // Process handle not inheritable + nullptr, // Thread handle not inheritable + bInheritHandles, // Set handle inheritance + dwCreationFlags, + nullptr, // Use parent's environment block + nullptr, // Use parent's starting directory + &startupInfo, // Pointer to STARTUPINFO structure + &processInfo) != + 0; // Pointer to PROCESS_INFORMATION structure + if (!success) { + DWORD lastError = GetLastError(); + std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")" + << std::endl; + displayError(lastError); + } + return success; +} + +static void finishProcess(bool success) +{ + if (success) { + success = + WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0; + }; + if (!success) { + TerminateProcess(processInfo.hProcess, 1); + } + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); +} + +static bool createPipe(PHANDLE readPipe, PHANDLE writePipe) +{ + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.bInheritHandle = TRUE; + securityAttributes.lpSecurityDescriptor = nullptr; + return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false + : true; +} + +static void finishPipe(HANDLE readPipe, HANDLE writePipe) +{ + if (readPipe != INVALID_HANDLE_VALUE) { + CloseHandle(readPipe); + } + if (writePipe != INVALID_HANDLE_VALUE) { + CloseHandle(writePipe); + } +} + +static HANDLE createFile(LPCWSTR fileName) +{ + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.bInheritHandle = TRUE; + securityAttributes.lpSecurityDescriptor = nullptr; + + HANDLE file = + CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE, + 0, // do not share + &securityAttributes, + CREATE_ALWAYS, // overwrite existing + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + nullptr); // no template + if (file == INVALID_HANDLE_VALUE) { + DWORD lastError = GetLastError(); + std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")" + << std::endl; + displayError(lastError); + } + return file; +} + +static void finishFile(HANDLE file) +{ + if (file != INVALID_HANDLE_VALUE) { + CloseHandle(file); + } +} + +# ifndef MAPVK_VK_TO_VSC +# define MAPVK_VK_TO_VSC (0) +# endif + +static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr) +{ + inputBuffer[0].EventType = KEY_EVENT; + inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE; + inputBuffer[0].Event.KeyEvent.wRepeatCount = 1; + SHORT keyCode = VkKeyScanW(chr); + if (keyCode == -1) { + // Character can't be entered with current keyboard layout + // Just set any, it doesn't really matter + keyCode = 'K'; + } + inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode); + inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey( + inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC); + inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr; + inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0; + if ((HIBYTE(keyCode) & 1) == 1) { + inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; + } + if ((HIBYTE(keyCode) & 2) == 2) { + inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED; + } + if ((HIBYTE(keyCode) & 4) == 4) { + inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED; + } + inputBuffer[1].EventType = inputBuffer[0].EventType; + inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE; + inputBuffer[1].Event.KeyEvent.wRepeatCount = 1; + inputBuffer[1].Event.KeyEvent.wVirtualKeyCode = + inputBuffer[0].Event.KeyEvent.wVirtualKeyCode; + inputBuffer[1].Event.KeyEvent.wVirtualScanCode = + inputBuffer[0].Event.KeyEvent.wVirtualScanCode; + inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar = + inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar; + inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0; +} + +static int testPipe() +{ + int didFail = 1; + HANDLE inPipeRead = INVALID_HANDLE_VALUE; + HANDLE inPipeWrite = INVALID_HANDLE_VALUE; + HANDLE outPipeRead = INVALID_HANDLE_VALUE; + HANDLE outPipeWrite = INVALID_HANDLE_VALUE; + HANDLE errPipeRead = INVALID_HANDLE_VALUE; + HANDLE errPipeWrite = INVALID_HANDLE_VALUE; + UINT currentCodepage = GetConsoleCP(); + char buffer[200]; + char buffer2[200]; + try { + if (!createPipe(&inPipeRead, &inPipeWrite) || + !createPipe(&outPipeRead, &outPipeWrite) || + !createPipe(&errPipeRead, &errPipeWrite)) { + throw std::runtime_error("createFile failed!"); + } + if (TestCodepage == CP_ACP) { + TestCodepage = GetACP(); + } + if (!SetConsoleCP(TestCodepage)) { + throw std::runtime_error("SetConsoleCP failed!"); + } + + DWORD bytesWritten = 0; + if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(), + (DWORD)encodedInputTestString.size(), &bytesWritten, + nullptr) || + bytesWritten == 0) { + throw std::runtime_error("WriteFile failed!"); + } + + if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) { + try { + DWORD status; + if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != + WAIT_OBJECT_0) { + std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "WaitForSingleObject returned unexpected status 0x" + << status << std::endl; + std::cerr.unsetf(std::ios::hex); + throw std::runtime_error("WaitForSingleObject failed!"); + } + DWORD bytesRead = 0; + if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead, + nullptr) || + bytesRead == 0) { + throw std::runtime_error("ReadFile#1 failed!"); + } + buffer[bytesRead] = 0; + if ((bytesRead < + encodedTestString.size() + 1 + encodedInputTestString.size() && + !ReadFile(outPipeRead, buffer + bytesRead, + sizeof(buffer) - bytesRead, &bytesRead, nullptr)) || + bytesRead == 0) { + throw std::runtime_error("ReadFile#2 failed!"); + } + if (memcmp(buffer, encodedTestString.c_str(), + encodedTestString.size()) == 0 && + memcmp(buffer + encodedTestString.size() + 1, + encodedInputTestString.c_str(), + encodedInputTestString.size()) == 0) { + bytesRead = 0; + if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead, + nullptr) || + bytesRead == 0) { + throw std::runtime_error("ReadFile#3 failed!"); + } + buffer2[bytesRead] = 0; + didFail = encodedTestString.compare(0, std::string::npos, buffer2, + encodedTestString.size()) == 0 + ? 0 + : 1; + } + if (didFail != 0) { + std::cerr << "Pipe's output didn't match expected output!" + << std::endl; + dumpBuffers<char>(encodedTestString.c_str(), buffer, + encodedTestString.size()); + dumpBuffers<char>(encodedInputTestString.c_str(), + buffer + encodedTestString.size() + 1, + encodedInputTestString.size()); + dumpBuffers<char>(encodedTestString.c_str(), buffer2, + encodedTestString.size()); + } + } catch (const std::runtime_error& ex) { + DWORD lastError = GetLastError(); + std::cerr << "In function testPipe, line " << __LINE__ << ": " + << ex.what() << std::endl; + displayError(lastError); + } + finishProcess(didFail == 0); + } + } catch (const std::runtime_error& ex) { + DWORD lastError = GetLastError(); + std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what() + << std::endl; + displayError(lastError); + } + finishPipe(inPipeRead, inPipeWrite); + finishPipe(outPipeRead, outPipeWrite); + finishPipe(errPipeRead, errPipeWrite); + SetConsoleCP(currentCodepage); + return didFail; +} + +static int testFile() +{ + int didFail = 1; + HANDLE inFile = INVALID_HANDLE_VALUE; + HANDLE outFile = INVALID_HANDLE_VALUE; + HANDLE errFile = INVALID_HANDLE_VALUE; + try { + if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE || + (outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE || + (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) { + throw std::runtime_error("createFile failed!"); + } + DWORD bytesWritten = 0; + char buffer[200]; + char buffer2[200]; + + int length; + if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, + -1, buffer, sizeof(buffer), nullptr, + nullptr)) == 0) { + throw std::runtime_error("WideCharToMultiByte failed!"); + } + buffer[length - 1] = '\n'; + if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) || + bytesWritten == 0) { + throw std::runtime_error("WriteFile failed!"); + } + if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + throw std::runtime_error("SetFilePointer failed!"); + } + + if (createProcess(inFile, outFile, errFile)) { + DWORD bytesRead = 0; + try { + DWORD status; + if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != + WAIT_OBJECT_0) { + std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "WaitForSingleObject returned unexpected status 0x" + << status << std::endl; + std::cerr.unsetf(std::ios::hex); + throw std::runtime_error("WaitForSingleObject failed!"); + } + if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER) { + throw std::runtime_error("SetFilePointer#1 failed!"); + } + if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) || + bytesRead == 0) { + throw std::runtime_error("ReadFile#1 failed!"); + } + buffer[bytesRead] = 0; + if (memcmp(buffer, encodedTestString.c_str(), + encodedTestString.size()) == 0 && + memcmp(buffer + encodedTestString.size() + 1, + encodedInputTestString.c_str(), + encodedInputTestString.size()) == 0) { + bytesRead = 0; + if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER) { + throw std::runtime_error("SetFilePointer#2 failed!"); + } + + if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead, + nullptr) || + bytesRead == 0) { + throw std::runtime_error("ReadFile#2 failed!"); + } + buffer2[bytesRead] = 0; + didFail = encodedTestString.compare(0, std::string::npos, buffer2, + encodedTestString.size()) == 0 + ? 0 + : 1; + } + if (didFail != 0) { + std::cerr << "File's output didn't match expected output!" + << std::endl; + dumpBuffers<char>(encodedTestString.c_str(), buffer, + encodedTestString.size()); + dumpBuffers<char>(encodedInputTestString.c_str(), + buffer + encodedTestString.size() + 1, + encodedInputTestString.size()); + dumpBuffers<char>(encodedTestString.c_str(), buffer2, + encodedTestString.size()); + } + } catch (const std::runtime_error& ex) { + DWORD lastError = GetLastError(); + std::cerr << "In function testFile, line " << __LINE__ << ": " + << ex.what() << std::endl; + displayError(lastError); + } + finishProcess(didFail == 0); + } + } catch (const std::runtime_error& ex) { + DWORD lastError = GetLastError(); + std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what() + << std::endl; + displayError(lastError); + } + finishFile(inFile); + finishFile(outFile); + finishFile(errFile); + return didFail; +} + +# ifndef _WIN32_WINNT_VISTA +# define _WIN32_WINNT_VISTA 0x0600 +# endif + +static int testConsole() +{ + int didFail = 1; + HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE); + HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE); + HANDLE hIn = parentIn; + HANDLE hOut = parentOut; + DWORD consoleMode; + bool newConsole = false; + bool forceNewConsole = false; + bool restoreConsole = false; + LPCWSTR TestFaceName = L"Lucida Console"; + const DWORD TestFontFamily = 0x00000036; + const DWORD TestFontSize = 0x000c0000; + HKEY hConsoleKey; + WCHAR FaceName[200]; + FaceName[0] = 0; + DWORD FaceNameSize = sizeof(FaceName); + DWORD FontFamily = TestFontFamily; + DWORD FontSize = TestFontSize; +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +# endif + const bool isVistaOrGreater = + LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA); +# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +# endif + if (!isVistaOrGreater) { + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE, + &hConsoleKey) == ERROR_SUCCESS) { + DWORD dwordSize = sizeof(DWORD); + if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr, + (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) { + if (FontFamily != TestFontFamily) { + RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr, + (LPBYTE)FaceName, &FaceNameSize); + RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr, + (LPBYTE)&FontSize, &dwordSize); + + RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD, + (BYTE*)&TestFontFamily, sizeof(TestFontFamily)); + RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, + (BYTE*)TestFaceName, + (DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR))); + RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, + (BYTE*)&TestFontSize, sizeof(TestFontSize)); + + restoreConsole = true; + forceNewConsole = true; + } + } else { + std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl; + } + RegCloseKey(hConsoleKey); + } else { + std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" + << std::endl; + } + } + if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) { + // Not a real console, let's create new one. + FreeConsole(); + if (!AllocConsole()) { + std::cerr << "AllocConsole failed!" << std::endl; + return didFail; + } + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.bInheritHandle = TRUE; + securityAttributes.lpSecurityDescriptor = nullptr; + hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, + OPEN_EXISTING, 0, nullptr); + if (hIn == INVALID_HANDLE_VALUE) { + DWORD lastError = GetLastError(); + std::cerr << "CreateFile(CONIN$)" << std::endl; + displayError(lastError); + } + hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, + OPEN_EXISTING, 0, nullptr); + if (hOut == INVALID_HANDLE_VALUE) { + DWORD lastError = GetLastError(); + std::cerr << "CreateFile(CONOUT$)" << std::endl; + displayError(lastError); + } + SetStdHandle(STD_INPUT_HANDLE, hIn); + SetStdHandle(STD_OUTPUT_HANDLE, hOut); + SetStdHandle(STD_ERROR_HANDLE, hOut); + newConsole = true; + } + +# if _WIN32_WINNT >= _WIN32_WINNT_VISTA + if (isVistaOrGreater) { + CONSOLE_FONT_INFOEX consoleFont; + memset(&consoleFont, 0, sizeof(consoleFont)); + consoleFont.cbSize = sizeof(consoleFont); + HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); + typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)( + HANDLE hConsoleOutput, BOOL bMaximumWindow, + PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx); + typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)( + HANDLE hConsoleOutput, BOOL bMaximumWindow, + PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx); + GetCurrentConsoleFontExFunc getConsoleFont = + (GetCurrentConsoleFontExFunc)GetProcAddress(kernel32, + "GetCurrentConsoleFontEx"); + SetCurrentConsoleFontExFunc setConsoleFont = + (SetCurrentConsoleFontExFunc)GetProcAddress(kernel32, + "SetCurrentConsoleFontEx"); + if (getConsoleFont(hOut, FALSE, &consoleFont)) { + if (consoleFont.FontFamily != TestFontFamily) { + consoleFont.FontFamily = TestFontFamily; + wcscpy(consoleFont.FaceName, TestFaceName); + if (!setConsoleFont(hOut, FALSE, &consoleFont)) { + std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl; + } + } + } else { + std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl; + } + } else { +# endif + if (restoreConsole && + RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE, + &hConsoleKey) == ERROR_SUCCESS) { + RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD, + (BYTE*)&FontFamily, sizeof(FontFamily)); + if (FaceName[0] != 0) { + RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName, + FaceNameSize); + } else { + RegDeleteValueW(hConsoleKey, L"FaceName"); + } + RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize, + sizeof(FontSize)); + RegCloseKey(hConsoleKey); + } +# if _WIN32_WINNT >= _WIN32_WINNT_VISTA + } +# endif + + if (createProcess(nullptr, nullptr, nullptr)) { + try { + DWORD status; + if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) != + WAIT_OBJECT_0) { + std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "WaitForSingleObject returned unexpected status 0x" + << status << std::endl; + std::cerr.unsetf(std::ios::hex); + throw std::runtime_error("WaitForSingleObject#1 failed!"); + } + INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) / + sizeof(UnicodeInputTestString[0])) * + 2]; + memset(&inputBuffer, 0, sizeof(inputBuffer)); + unsigned int i; + for (i = 0; i < (sizeof(UnicodeInputTestString) / + sizeof(UnicodeInputTestString[0]) - + 1); + i++) { + writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]); + } + writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN); + DWORD eventsWritten = 0; + // We need to wait a bit before writing to console so child process have + // started waiting for input on stdin. + Sleep(300); + if (!WriteConsoleInputW(hIn, inputBuffer, + sizeof(inputBuffer) / sizeof(inputBuffer[0]), + &eventsWritten) || + eventsWritten == 0) { + throw std::runtime_error("WriteConsoleInput failed!"); + } + if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != + WAIT_OBJECT_0) { + std::cerr.setf(std::ios::hex, std::ios::basefield); + std::cerr << "WaitForSingleObject returned unexpected status 0x" + << status << std::endl; + std::cerr.unsetf(std::ios::hex); + throw std::runtime_error("WaitForSingleObject#2 failed!"); + } + CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; + if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) { + throw std::runtime_error("GetConsoleScreenBufferInfo failed!"); + } + + COORD coord; + DWORD charsRead = 0; + coord.X = 0; + coord.Y = screenBufferInfo.dwCursorPosition.Y - 4; + WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4]; + if (!ReadConsoleOutputCharacterW(hOut, outputBuffer, + screenBufferInfo.dwSize.X * 4, coord, + &charsRead) || + charsRead == 0) { + delete[] outputBuffer; + throw std::runtime_error("ReadConsoleOutputCharacter failed!"); + } + std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString); + std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); + std::wstring wideInputTestString = + kwsys::Encoding::ToWide(encodedInputTestString); + if (memcmp(outputBuffer, wideTestString.c_str(), + wideTestString.size() * sizeof(wchar_t)) == 0 && + memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1, + wideTestString.c_str(), + wideTestString.size() * sizeof(wchar_t)) == 0 && + memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2, + UnicodeInputTestString, + sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 && + memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3, + wideInputTestString.c_str(), + (wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) { + didFail = 0; + } else { + std::cerr << "Console's output didn't match expected output!" + << std::endl; + dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer, + wideTestString.size()); + dumpBuffers<wchar_t>(wideTestString.c_str(), + outputBuffer + screenBufferInfo.dwSize.X * 1, + wideTestString.size()); + dumpBuffers<wchar_t>( + UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2, + (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR)); + dumpBuffers<wchar_t>(wideInputTestString.c_str(), + outputBuffer + screenBufferInfo.dwSize.X * 3, + wideInputTestString.size() - 1); + } + delete[] outputBuffer; + } catch (const std::runtime_error& ex) { + DWORD lastError = GetLastError(); + std::cerr << "In function testConsole, line " << __LINE__ << ": " + << ex.what() << std::endl; + displayError(lastError); + } + finishProcess(didFail == 0); + } + if (newConsole) { + SetStdHandle(STD_INPUT_HANDLE, parentIn); + SetStdHandle(STD_OUTPUT_HANDLE, parentOut); + SetStdHandle(STD_ERROR_HANDLE, parentErr); + CloseHandle(hIn); + CloseHandle(hOut); + FreeConsole(); + } + return didFail; +} + +#endif + +int testConsoleBuf(int, char* []) +{ + int ret = 0; + +#if defined(_WIN32) + beforeInputEvent = CreateEventW(nullptr, + FALSE, // auto-reset event + FALSE, // initial state is nonsignaled + BeforeInputEventName); // object name + if (!beforeInputEvent) { + std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl; + return 1; + } + + afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName); + if (!afterOutputEvent) { + std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl; + return 1; + } + + encodedTestString = kwsys::Encoding::ToNarrow(std::wstring( + UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); + encodedInputTestString = kwsys::Encoding::ToNarrow( + std::wstring(UnicodeInputTestString, + sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1)); + encodedInputTestString += "\n"; + + ret |= testPipe(); + ret |= testFile(); + ret |= testConsole(); + + CloseHandle(beforeInputEvent); + CloseHandle(afterOutputEvent); +#endif + + return ret; +} diff --git a/Source/kwsys/testConsoleBuf.hxx b/Source/kwsys/testConsoleBuf.hxx new file mode 100644 index 0000000..e93cb4f --- /dev/null +++ b/Source/kwsys/testConsoleBuf.hxx @@ -0,0 +1,17 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef testConsoleBuf_hxx +#define testConsoleBuf_hxx + +static const wchar_t cmdConsoleBufChild[] = L"testConsoleBufChild"; + +static const wchar_t BeforeInputEventName[] = L"BeforeInputEvent"; +static const wchar_t AfterOutputEventName[] = L"AfterOutputEvent"; + +// यूनिकोड είναι здорово! +static const wchar_t UnicodeTestString[] = + L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 " + L"\u03B5\u03AF\u03BD\0\u03B1\u03B9 " + L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!"; + +#endif diff --git a/Source/kwsys/testConsoleBufChild.cxx b/Source/kwsys/testConsoleBufChild.cxx new file mode 100644 index 0000000..3c8fdc2 --- /dev/null +++ b/Source/kwsys/testConsoleBufChild.cxx @@ -0,0 +1,55 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +#include KWSYS_HEADER(ConsoleBuf.hxx) +#include KWSYS_HEADER(Encoding.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "ConsoleBuf.hxx.in" +# include "Encoding.hxx.in" +#endif + +#include <iostream> + +#include "testConsoleBuf.hxx" + +int main(int argc, const char* argv[]) +{ +#if defined(_WIN32) + kwsys::ConsoleBuf::Manager out(std::cout); + kwsys::ConsoleBuf::Manager err(std::cerr, true); + kwsys::ConsoleBuf::Manager in(std::cin); + + if (argc > 1) { + std::cout << argv[1] << std::endl; + std::cerr << argv[1] << std::endl; + } else { + std::string str = kwsys::Encoding::ToNarrow(std::wstring( + UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); + std::cout << str << std::endl; + std::cerr << str << std::endl; + } + + std::string input; + HANDLE event = OpenEventW(EVENT_MODIFY_STATE, FALSE, BeforeInputEventName); + if (event) { + SetEvent(event); + CloseHandle(event); + } + + std::cin >> input; + std::cout << input << std::endl; + event = OpenEventW(EVENT_MODIFY_STATE, FALSE, AfterOutputEventName); + if (event) { + SetEvent(event); + CloseHandle(event); + } +#else + static_cast<void>(argc); + static_cast<void>(argv); +#endif + return 0; +} diff --git a/Source/kwsys/testDirectory.cxx b/Source/kwsys/testDirectory.cxx new file mode 100644 index 0000000..b1ab0c8 --- /dev/null +++ b/Source/kwsys/testDirectory.cxx @@ -0,0 +1,110 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying +file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Directory.hxx) +#include KWSYS_HEADER(Encoding.hxx) +#include KWSYS_HEADER(SystemTools.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Directory.hxx.in" +# include "Encoding.hxx.in" +# include "SystemTools.hxx.in" +#endif + +#include <fstream> +#include <iostream> +#include <sstream> + +#include <testSystemTools.h> + +int _doLongPathTest() +{ + using namespace kwsys; + static const int LONG_PATH_THRESHOLD = 512; + int res = 0; + std::string topdir(TEST_SYSTEMTOOLS_BINARY_DIR "/directory_testing/"); + std::stringstream testpathstrm; + std::string testdirpath; + std::string extendedtestdirpath; + + testpathstrm << topdir; + size_t pathlen = testpathstrm.str().length(); + testpathstrm.seekp(0, std::ios_base::end); + while (pathlen < LONG_PATH_THRESHOLD) { + testpathstrm << "0123456789/"; + pathlen = testpathstrm.str().length(); + } + + testdirpath = testpathstrm.str(); +#ifdef _WIN32 + extendedtestdirpath = + Encoding::ToNarrow(SystemTools::ConvertToWindowsExtendedPath(testdirpath)); +#else + extendedtestdirpath = testdirpath; +#endif + + if (SystemTools::MakeDirectory(extendedtestdirpath)) { + std::ofstream testfile1( + (extendedtestdirpath + "longfilepathtest1.txt").c_str()); + std::ofstream testfile2( + (extendedtestdirpath + "longfilepathtest2.txt").c_str()); + testfile1 << "foo"; + testfile2 << "bar"; + testfile1.close(); + testfile2.close(); + + Directory testdir; + // Set res to failure if the directory doesn't load + res += !testdir.Load(testdirpath); + // Increment res failure if the directory appears empty + res += testdir.GetNumberOfFiles() == 0; + // Increment res failures if the path has changed from + // what was provided. + res += testdirpath != testdir.GetPath(); + + SystemTools::RemoveADirectory(topdir); + } else { + std::cerr << "Failed to create directory with long path: " + << extendedtestdirpath << std::endl; + res += 1; + } + return res; +} + +int _copyDirectoryTest() +{ + using namespace kwsys; + const std::string source(TEST_SYSTEMTOOLS_BINARY_DIR + "/directory_testing/copyDirectoryTestSrc"); + if (SystemTools::PathExists(source)) { + std::cerr << source << " shouldn't exist before test" << std::endl; + return 1; + } + const std::string destination(TEST_SYSTEMTOOLS_BINARY_DIR + "/directory_testing/copyDirectoryTestDst"); + if (SystemTools::PathExists(destination)) { + std::cerr << destination << " shouldn't exist before test" << std::endl; + return 2; + } + const bool copysuccess = SystemTools::CopyADirectory(source, destination); + const bool destinationexists = SystemTools::PathExists(destination); + if (copysuccess) { + std::cerr << "CopyADirectory should have returned false" << std::endl; + SystemTools::RemoveADirectory(destination); + return 3; + } + if (destinationexists) { + std::cerr << "CopyADirectory returned false, but destination directory" + << " has been created" << std::endl; + SystemTools::RemoveADirectory(destination); + return 4; + } + return 0; +} + +int testDirectory(int, char* []) +{ + return _doLongPathTest() + _copyDirectoryTest(); +} diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx new file mode 100644 index 0000000..703ad4d --- /dev/null +++ b/Source/kwsys/testDynamicLoader.cxx @@ -0,0 +1,133 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +#include KWSYS_HEADER(DynamicLoader.hxx) + +#if defined(__BEOS__) || defined(__HAIKU__) +# include <be/kernel/OS.h> /* disable_debugger() API. */ +#endif + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "DynamicLoader.hxx.in" +#endif + +#include <iostream> +#include <string> + +// Include with <> instead of "" to avoid getting any in-source copy +// left on disk. +#include <testSystemTools.h> + +static std::string GetLibName(const char* lname, const char* subdir = nullptr) +{ + // Construct proper name of lib + std::string slname; + slname = RUNTIME_OUTPUT_DIRECTORY; + if (subdir) { + slname += "/"; + slname += subdir; + } +#ifdef CMAKE_INTDIR + slname += "/"; + slname += CMAKE_INTDIR; +#endif + slname += "/"; + slname += kwsys::DynamicLoader::LibPrefix(); + slname += lname; + slname += kwsys::DynamicLoader::LibExtension(); + + return slname; +} + +/* libname = Library name (proper prefix, proper extension) + * System = symbol to lookup in libname + * r1: should OpenLibrary succeed ? + * r2: should GetSymbolAddress succeed ? + * r3: should CloseLibrary succeed ? + */ +static int TestDynamicLoader(const char* libname, const char* symbol, int r1, + int r2, int r3, int flags = 0) +{ + std::cerr << "Testing: " << libname << std::endl; + kwsys::DynamicLoader::LibraryHandle l = + kwsys::DynamicLoader::OpenLibrary(libname, flags); + // If result is incompatible with expectation just fails (xor): + if ((r1 && !l) || (!r1 && l)) { + std::cerr << "OpenLibrary: " << kwsys::DynamicLoader::LastError() + << std::endl; + return 1; + } + kwsys::DynamicLoader::SymbolPointer f = + kwsys::DynamicLoader::GetSymbolAddress(l, symbol); + if ((r2 && !f) || (!r2 && f)) { + std::cerr << "GetSymbolAddress: " << kwsys::DynamicLoader::LastError() + << std::endl; + return 1; + } +#ifndef __APPLE__ + int s = kwsys::DynamicLoader::CloseLibrary(l); + if ((r3 && !s) || (!r3 && s)) { + std::cerr << "CloseLibrary: " << kwsys::DynamicLoader::LastError() + << std::endl; + return 1; + } +#else + (void)r3; +#endif + return 0; +} + +int testDynamicLoader(int argc, char* argv[]) +{ +#if defined(_WIN32) + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); +#elif defined(__BEOS__) || defined(__HAIKU__) + disable_debugger(1); +#endif + int res = 0; + if (argc == 3) { + // User specify a libname and symbol to check. + res = TestDynamicLoader(argv[1], argv[2], 1, 1, 1); + return res; + } + +// dlopen() on Syllable before 11/22/2007 doesn't return 0 on error +#ifndef __SYLLABLE__ + // Make sure that inexistent lib is giving correct result + res += TestDynamicLoader("azerty_", "foo_bar", 0, 0, 0); + // Make sure that random binary file cannot be assimilated as dylib + res += TestDynamicLoader(TEST_SYSTEMTOOLS_SOURCE_DIR "/testSystemTools.bin", + "wp", 0, 0, 0); +#endif + +#ifdef __linux__ + // This one is actually fun to test, since dlopen is by default + // loaded...wonder why :) + res += TestDynamicLoader("foobar.lib", "dlopen", 0, 1, 0); + res += TestDynamicLoader("libdl.so", "dlopen", 1, 1, 1); + res += TestDynamicLoader("libdl.so", "TestDynamicLoader", 1, 0, 1); +#endif + // Now try on the generated library + std::string libname = GetLibName(KWSYS_NAMESPACE_STRING "TestDynload"); + res += TestDynamicLoader(libname.c_str(), "dummy", 1, 0, 1); + res += TestDynamicLoader(libname.c_str(), "TestDynamicLoaderSymbolPointer", + 1, 1, 1); + res += TestDynamicLoader(libname.c_str(), "_TestDynamicLoaderSymbolPointer", + 1, 0, 1); + res += TestDynamicLoader(libname.c_str(), "TestDynamicLoaderData", 1, 1, 1); + res += TestDynamicLoader(libname.c_str(), "_TestDynamicLoaderData", 1, 0, 1); + +#ifdef _WIN32 + libname = GetLibName(KWSYS_NAMESPACE_STRING "TestDynloadUse", "dynloaddir"); + res += TestDynamicLoader(libname.c_str(), "dummy", 0, 0, 0); + res += TestDynamicLoader(libname.c_str(), "TestLoad", 1, 1, 1, + kwsys::DynamicLoader::SearchBesideLibrary); + res += TestDynamicLoader(libname.c_str(), "_TestLoad", 1, 0, 1, + kwsys::DynamicLoader::SearchBesideLibrary); +#endif + + return res; +} diff --git a/Source/kwsys/testDynload.c b/Source/kwsys/testDynload.c new file mode 100644 index 0000000..c49f747 --- /dev/null +++ b/Source/kwsys/testDynload.c @@ -0,0 +1,13 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef _WIN32 +# define DL_EXPORT __declspec(dllexport) +#else +# define DL_EXPORT +#endif + +DL_EXPORT int TestDynamicLoaderData = 0; + +DL_EXPORT void TestDynamicLoaderSymbolPointer() +{ +} diff --git a/Source/kwsys/testDynloadImpl.c b/Source/kwsys/testDynloadImpl.c new file mode 100644 index 0000000..2b9069b --- /dev/null +++ b/Source/kwsys/testDynloadImpl.c @@ -0,0 +1,10 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ + +#include "testDynloadImpl.h" + +int TestDynamicLoaderImplData = 0; + +void TestDynamicLoaderImplSymbolPointer() +{ +} diff --git a/Source/kwsys/testDynloadImpl.h b/Source/kwsys/testDynloadImpl.h new file mode 100644 index 0000000..d0c9dfb --- /dev/null +++ b/Source/kwsys/testDynloadImpl.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef _WIN32 +# ifdef BUILDING_TestDynloadImpl +# define DLIMPL_EXPORT __declspec(dllexport) +# else +# define DLIMPL_EXPORT __declspec(dllimport) +# endif +#else +# define DLIMPL_EXPORT +#endif + +DLIMPL_EXPORT int TestDynamicLoaderImplData; + +DLIMPL_EXPORT void TestDynamicLoaderImplSymbolPointer(); diff --git a/Source/kwsys/testDynloadUse.c b/Source/kwsys/testDynloadUse.c new file mode 100644 index 0000000..5402add --- /dev/null +++ b/Source/kwsys/testDynloadUse.c @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "testDynloadImpl.h" + +#ifdef _WIN32 +# define DL_EXPORT __declspec(dllexport) +#else +# define DL_EXPORT +#endif + +DL_EXPORT int TestLoad() +{ + TestDynamicLoaderImplSymbolPointer(); + return TestDynamicLoaderImplData; +} diff --git a/Source/kwsys/testEncode.c b/Source/kwsys/testEncode.c new file mode 100644 index 0000000..b7b6dd8 --- /dev/null +++ b/Source/kwsys/testEncode.c @@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(MD5.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "MD5.h.in" +#endif + +#include <stdio.h> +#include <string.h> + +static const unsigned char testMD5input1[] = + " A quick brown fox jumps over the lazy dog.\n" + " This is sample text for MD5 sum input.\n"; +static const char testMD5output1[] = "8f146af46ed4f267921bb937d4d3500c"; + +static const int testMD5input2len = 28; +static const unsigned char testMD5input2[] = "the cow jumped over the moon"; +static const char testMD5output2[] = "a2ad137b746138fae4e5adca9c85d3ae"; + +static int testMD5_1(kwsysMD5* md5) +{ + char md5out[33]; + kwsysMD5_Initialize(md5); + kwsysMD5_Append(md5, testMD5input1, -1); + kwsysMD5_FinalizeHex(md5, md5out); + md5out[32] = 0; + printf("md5sum 1: expected [%s]\n" + " got [%s]\n", + testMD5output1, md5out); + return (strcmp(md5out, testMD5output1) != 0) ? 1 : 0; +} + +static int testMD5_2(kwsysMD5* md5) +{ + unsigned char digest[16]; + char md5out[33]; + kwsysMD5_Initialize(md5); + kwsysMD5_Append(md5, testMD5input2, testMD5input2len); + kwsysMD5_Finalize(md5, digest); + kwsysMD5_DigestToHex(digest, md5out); + md5out[32] = 0; + printf("md5sum 2: expected [%s]\n" + " got [%s]\n", + testMD5output2, md5out); + return (strcmp(md5out, testMD5output2) != 0) ? 1 : 0; +} + +int testEncode(int argc, char* argv[]) +{ + int result = 0; + (void)argc; + (void)argv; + + /* Test MD5 digest. */ + { + kwsysMD5* md5 = kwsysMD5_New(); + result |= testMD5_1(md5); + result |= testMD5_2(md5); + kwsysMD5_Delete(md5); + } + + return result; +} diff --git a/Source/kwsys/testEncoding.cxx b/Source/kwsys/testEncoding.cxx new file mode 100644 index 0000000..d672aed --- /dev/null +++ b/Source/kwsys/testEncoding.cxx @@ -0,0 +1,285 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +#if defined(_MSC_VER) +# pragma warning(disable : 4786) +#endif + +#include KWSYS_HEADER(Encoding.hxx) +#include KWSYS_HEADER(Encoding.h) + +#include <algorithm> +#include <iostream> +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "Encoding.h.in" +# include "Encoding.hxx.in" +#endif + +static const unsigned char helloWorldStrings[][32] = { + // English + { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', 0 }, + // Japanese + { 0xE3, 0x81, 0x93, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0xAB, 0xE3, 0x81, + 0xA1, 0xE3, 0x81, 0xAF, 0xE4, 0xB8, 0x96, 0xE7, 0x95, 0x8C, 0 }, + // Arabic + { 0xD9, 0x85, 0xD8, 0xB1, 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xA7, 0x20, 0xD8, + 0xA7, 0xD9, 0x84, 0xD8, 0xB9, 0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x85, 0 }, + // Yiddish + { 0xD7, 0x94, 0xD7, 0xA2, 0xD7, 0x9C, 0xD7, 0x90, 0x20, 0xD7, + 0x95, 0xD7, 0x95, 0xD7, 0xA2, 0xD7, 0x9C, 0xD7, 0x98, 0 }, + // Russian + { 0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, 0xB5, + 0xD1, 0x82, 0x20, 0xD0, 0xBC, 0xD0, 0xB8, 0xD1, 0x80, 0 }, + // Latin + { 0x4D, 0x75, 0x6E, 0x64, 0x75, 0x73, 0x20, 0x73, 0x61, 0x6C, 0x76, 0x65, + 0 }, + // Swahili + { 0x68, 0x75, 0x6A, 0x61, 0x6D, 0x62, 0x6F, 0x20, 0x44, 0x75, 0x6E, 0x69, + 0x61, 0 }, + // Icelandic + { 0x48, 0x61, 0x6C, 0x6C, 0xC3, 0xB3, 0x20, 0x68, 0x65, 0x69, 0x6D, 0x75, + 0x72, 0 }, + { 0 } +}; + +static int testHelloWorldEncoding() +{ + int ret = 0; + for (int i = 0; helloWorldStrings[i][0] != 0; i++) { + std::string str = reinterpret_cast<const char*>(helloWorldStrings[i]); + std::cout << str << std::endl; + std::wstring wstr = kwsys::Encoding::ToWide(str); + std::string str2 = kwsys::Encoding::ToNarrow(wstr); + wchar_t* c_wstr = kwsysEncoding_DupToWide(str.c_str()); + char* c_str2 = kwsysEncoding_DupToNarrow(c_wstr); + if (!wstr.empty() && (str != str2 || strcmp(c_str2, str.c_str()))) { + std::cout << "converted string was different: " << str2 << std::endl; + std::cout << "converted string was different: " << c_str2 << std::endl; + ret++; + } + free(c_wstr); + free(c_str2); + } + return ret; +} + +static int testRobustEncoding() +{ + // test that the conversion functions handle invalid + // unicode correctly/gracefully + + // we manipulate the format flags of stdout, remember + // the original state here to restore before return + std::ios::fmtflags const& flags = std::cout.flags(); + + int ret = 0; + char cstr[] = { (char)-1, 0 }; + // this conversion could fail + std::wstring wstr = kwsys::Encoding::ToWide(cstr); + + wstr = kwsys::Encoding::ToWide(nullptr); + if (!wstr.empty()) { + const wchar_t* wcstr = wstr.c_str(); + std::cout << "ToWide(NULL) returned"; + for (size_t i = 0; i < wstr.size(); i++) { + std::cout << " " << std::hex << (int)wcstr[i]; + } + std::cout << std::endl; + ret++; + } + wstr = kwsys::Encoding::ToWide(""); + if (!wstr.empty()) { + const wchar_t* wcstr = wstr.c_str(); + std::cout << "ToWide(\"\") returned"; + for (size_t i = 0; i < wstr.size(); i++) { + std::cout << " " << std::hex << (int)wcstr[i]; + } + std::cout << std::endl; + ret++; + } + +#ifdef _WIN32 + // 16 bit wchar_t - we make an invalid surrogate pair + wchar_t cwstr[] = { 0xD801, 0xDA00, 0 }; + // this conversion could fail + std::string win_str = kwsys::Encoding::ToNarrow(cwstr); +#endif + + std::string str = kwsys::Encoding::ToNarrow(nullptr); + if (!str.empty()) { + std::cout << "ToNarrow(NULL) returned " << str << std::endl; + ret++; + } + + str = kwsys::Encoding::ToNarrow(L""); + if (!wstr.empty()) { + std::cout << "ToNarrow(\"\") returned " << str << std::endl; + ret++; + } + + std::cout.flags(flags); + return ret; +} + +static int testWithNulls() +{ + int ret = 0; + std::vector<std::string> strings; + strings.push_back(std::string("ab") + '\0' + 'c'); + strings.push_back(std::string("d") + '\0' + '\0' + 'e'); + strings.push_back(std::string() + '\0' + 'f'); + strings.push_back(std::string() + '\0' + '\0' + "gh"); + strings.push_back(std::string("ij") + '\0'); + strings.push_back(std::string("k") + '\0' + '\0'); + strings.push_back(std::string("\0\0\0\0", 4) + "lmn" + + std::string("\0\0\0\0", 4)); + for (auto& string : strings) { + std::wstring wstr = kwsys::Encoding::ToWide(string); + std::string str = kwsys::Encoding::ToNarrow(wstr); + std::string s(string); + std::replace(s.begin(), s.end(), '\0', ' '); + std::cout << "'" << s << "' (" << string.size() << ")" << std::endl; + if (str != string) { + std::replace(str.begin(), str.end(), '\0', ' '); + std::cout << "string with null was different: '" << str << "' (" + << str.size() << ")" << std::endl; + ret++; + } + } + return ret; +} + +static int testCommandLineArguments() +{ + int status = 0; + + char const* argv[2] = { "./app.exe", (char const*)helloWorldStrings[1] }; + + kwsys::Encoding::CommandLineArguments args(2, argv); + kwsys::Encoding::CommandLineArguments arg2 = + kwsys::Encoding::CommandLineArguments(args); + + char const* const* u8_argv = args.argv(); + for (int i = 0; i < args.argc(); i++) { + char const* u8_arg = u8_argv[i]; + if (strcmp(argv[i], u8_arg) != 0) { + std::cout << "argv[" << i << "] " << argv[i] << " != " << u8_arg + << std::endl; + status++; + } + } + + kwsys::Encoding::CommandLineArguments args3 = + kwsys::Encoding::CommandLineArguments::Main(2, argv); + + return status; +} + +static int testToWindowsExtendedPath() +{ +#ifdef _WIN32 + int ret = 0; + if (kwsys::Encoding::ToWindowsExtendedPath( + "L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "L:/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"L:/Local Mojo/Hex Power Pack/Iffy Voodoo\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath( + "//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo") != + L"\\\\?\\UNC\\Foo\\Local Mojo\\Hex Power Pack\\Iffy Voodoo") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"//Foo/Local Mojo/Hex Power Pack/Iffy Voodoo\"" + << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("//") != L"//") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"//\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\") != L"\\\\.\\") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X") != L"\\\\.\\X") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X:") != L"\\\\?\\X:") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X:\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("\\\\.\\X:\\") != + L"\\\\?\\X:\\") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"\\\\.\\X:\\\"" << std::endl; + ++ret; + } + + if (kwsys::Encoding::ToWindowsExtendedPath("NUL") != L"\\\\.\\NUL") { + std::cout << "Problem with ToWindowsExtendedPath " + << "\"NUL\"" << std::endl; + ++ret; + } + + return ret; +#else + return 0; +#endif +} + +int testEncoding(int, char* []) +{ + const char* loc = setlocale(LC_ALL, ""); + if (loc) { + std::cout << "Locale: " << loc << std::endl; + } else { + std::cout << "Locale: None" << std::endl; + } + + int ret = 0; + + ret |= testHelloWorldEncoding(); + ret |= testRobustEncoding(); + ret |= testCommandLineArguments(); + ret |= testWithNulls(); + ret |= testToWindowsExtendedPath(); + + return ret; +} diff --git a/Source/kwsys/testFStream.cxx b/Source/kwsys/testFStream.cxx new file mode 100644 index 0000000..5009e98 --- /dev/null +++ b/Source/kwsys/testFStream.cxx @@ -0,0 +1,113 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +#if defined(_MSC_VER) +# pragma warning(disable : 4786) +#endif + +#include KWSYS_HEADER(FStream.hxx) +#include <string.h> +#ifdef __BORLANDC__ +# include <mem.h> /* memcmp */ +#endif + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "FStream.hxx.in" +#endif + +#include <iostream> + +static int testNoFile() +{ + kwsys::ifstream in_file("NoSuchFile.txt"); + if (in_file) { + return 1; + } + + return 0; +} + +static const int num_test_files = 7; +static const int max_test_file_size = 45; + +static kwsys::FStream::BOM expected_bom[num_test_files] = { + kwsys::FStream::BOM_None, kwsys::FStream::BOM_None, + kwsys::FStream::BOM_UTF8, kwsys::FStream::BOM_UTF16LE, + kwsys::FStream::BOM_UTF16BE, kwsys::FStream::BOM_UTF32LE, + kwsys::FStream::BOM_UTF32BE +}; + +static unsigned char expected_bom_data[num_test_files][5] = { + { 0 }, + { 0 }, + { 3, 0xEF, 0xBB, 0xBF }, + { 2, 0xFF, 0xFE }, + { 2, 0xFE, 0xFF }, + { 4, 0xFF, 0xFE, 0x00, 0x00 }, + { 4, 0x00, 0x00, 0xFE, 0xFF }, +}; + +static unsigned char file_data[num_test_files][max_test_file_size] = { + { 1, 'H' }, + { 11, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }, + { 11, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }, + { 22, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x20, + 0x00, 0x57, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64, 0x00 }, + { 22, 0x00, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00, + 0x20, 0x00, 0x57, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64 }, + { 44, 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, + 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x57, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, + 0x00, 0x6C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00 }, + { 44, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x64 }, +}; + +static int testBOM() +{ + // test various encodings in binary mode + for (int i = 0; i < num_test_files; i++) { + { + kwsys::ofstream out("bom.txt", kwsys::ofstream::binary); + out.write(reinterpret_cast<const char*>(expected_bom_data[i] + 1), + *expected_bom_data[i]); + out.write(reinterpret_cast<const char*>(file_data[i] + 1), + file_data[i][0]); + } + + kwsys::ifstream in("bom.txt", kwsys::ofstream::binary); + kwsys::FStream::BOM bom = kwsys::FStream::ReadBOM(in); + if (bom != expected_bom[i]) { + std::cout << "Unexpected BOM " << i << std::endl; + return 1; + } + char data[max_test_file_size]; + in.read(data, file_data[i][0]); + if (!in.good()) { + std::cout << "Unable to read data " << i << std::endl; + return 1; + } + + if (memcmp(data, file_data[i] + 1, file_data[i][0]) != 0) { + std::cout << "Incorrect read data " << i << std::endl; + return 1; + } + } + + return 0; +} + +int testFStream(int, char* []) +{ + int ret = 0; + + ret |= testNoFile(); + ret |= testBOM(); + + return ret; +} diff --git a/Source/kwsys/testFail.c b/Source/kwsys/testFail.c new file mode 100644 index 0000000..82caeac --- /dev/null +++ b/Source/kwsys/testFail.c @@ -0,0 +1,24 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int testFail(int argc, char* argv[]) +{ + char* env = getenv("DASHBOARD_TEST_FROM_CTEST"); + int oldCtest = 0; + if (env) { + if (strcmp(env, "1") == 0) { + oldCtest = 1; + } + printf("DASHBOARD_TEST_FROM_CTEST = %s\n", env); + } + printf("%s: This test intentionally fails\n", argv[0]); + if (oldCtest) { + printf("The version of ctest is not able to handle intentionally failing " + "tests, so pass.\n"); + return 0; + } + return argc; +} diff --git a/Source/kwsys/testHashSTL.cxx b/Source/kwsys/testHashSTL.cxx new file mode 100644 index 0000000..18cae7f --- /dev/null +++ b/Source/kwsys/testHashSTL.cxx @@ -0,0 +1,64 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(hash_map.hxx) +#include KWSYS_HEADER(hash_set.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "hash_map.hxx.in" +# include "hash_set.hxx.in" +#endif + +#include <iostream> + +#if defined(_MSC_VER) +# pragma warning(disable : 4786) +#endif + +#if defined(__sgi) && !defined(__GNUC__) +# pragma set woff 1468 /* inline function cannot be explicitly instantiated \ + */ +#endif + +template class kwsys::hash_map<const char*, int>; +template class kwsys::hash_set<int>; + +static bool test_hash_map() +{ + using mtype = kwsys::hash_map<const char*, int>; + mtype m; + const char* keys[] = { "hello", "world" }; + m[keys[0]] = 1; + m.insert(mtype::value_type(keys[1], 2)); + int sum = 0; + for (auto& mi : m) { + std::cout << "Found entry [" << mi.first << "," << mi.second << "]" + << std::endl; + sum += mi.second; + } + return sum == 3; +} + +static bool test_hash_set() +{ + using stype = kwsys::hash_set<int>; + stype s; + s.insert(1); + s.insert(2); + int sum = 0; + for (int si : s) { + std::cout << "Found entry [" << si << "]" << std::endl; + sum += si; + } + return sum == 3; +} + +int testHashSTL(int, char* []) +{ + bool result = true; + result = test_hash_map() && result; + result = test_hash_set() && result; + return result ? 0 : 1; +} diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c new file mode 100644 index 0000000..39aaa23 --- /dev/null +++ b/Source/kwsys/testProcess.c @@ -0,0 +1,728 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Process.h) +#include KWSYS_HEADER(Encoding.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Encoding.h.in" +# include "Process.h.in" +#endif + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(_WIN32) +# include <windows.h> +#else +# include <signal.h> +# include <unistd.h> +#endif + +#if defined(__BORLANDC__) +# pragma warn - 8060 /* possibly incorrect assignment */ +#endif + +/* Platform-specific sleep functions. */ + +#if defined(__BEOS__) && !defined(__ZETA__) +/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */ +# include <be/kernel/OS.h> +static inline void testProcess_usleep(unsigned int usec) +{ + snooze(usec); +} +#elif defined(_WIN32) +/* Windows can only sleep in millisecond intervals. */ +static void testProcess_usleep(unsigned int usec) +{ + Sleep(usec / 1000); +} +#else +# define testProcess_usleep usleep +#endif + +#if defined(_WIN32) +static void testProcess_sleep(unsigned int sec) +{ + Sleep(sec * 1000); +} +#else +static void testProcess_sleep(unsigned int sec) +{ + sleep(sec); +} +#endif + +int runChild(const char* cmd[], int state, int exception, int value, int share, + int output, int delay, double timeout, int poll, int repeat, + int disown, int createNewGroup, unsigned int interruptDelay); + +static int test1(int argc, const char* argv[]) +{ + /* This is a very basic functional test of kwsysProcess. It is repeated + numerous times to verify that there are no resource leaks in kwsysProcess + that eventually lead to an error. Many versions of OS X will fail after + 256 leaked file handles, so 257 iterations seems to be a good test. On + the other hand, too many iterations will cause the test to time out - + especially if the test is instrumented with e.g. valgrind. + + If you have problems with this test timing out on your system, or want to + run more than 257 iterations, you can change the number of iterations by + setting the KWSYS_TEST_PROCESS_1_COUNT environment variable. */ + (void)argc; + (void)argv; + fprintf(stdout, "Output on stdout from test returning 0.\n"); + fprintf(stderr, "Output on stderr from test returning 0.\n"); + return 0; +} + +static int test2(int argc, const char* argv[]) +{ + (void)argc; + (void)argv; + fprintf(stdout, "Output on stdout from test returning 123.\n"); + fprintf(stderr, "Output on stderr from test returning 123.\n"); + return 123; +} + +static int test3(int argc, const char* argv[]) +{ + (void)argc; + (void)argv; + fprintf(stdout, "Output before sleep on stdout from timeout test.\n"); + fprintf(stderr, "Output before sleep on stderr from timeout test.\n"); + fflush(stdout); + fflush(stderr); + testProcess_sleep(15); + fprintf(stdout, "Output after sleep on stdout from timeout test.\n"); + fprintf(stderr, "Output after sleep on stderr from timeout test.\n"); + return 0; +} + +static int test4(int argc, const char* argv[]) +{ +#ifndef CRASH_USING_ABORT + /* Prepare a pointer to an invalid address. Don't use null, because + dereferencing null is undefined behaviour and compilers are free to + do whatever they want. ex: Clang will warn at compile time, or even + optimize away the write. We hope to 'outsmart' them by using + 'volatile' and a slightly larger address, based on a runtime value. */ + volatile int* invalidAddress = 0; + invalidAddress += argc ? 1 : 2; +#endif + +#if defined(_WIN32) + /* Avoid error diagnostic popups since we are crashing on purpose. */ + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); +#elif defined(__BEOS__) || defined(__HAIKU__) + /* Avoid error diagnostic popups since we are crashing on purpose. */ + disable_debugger(1); +#endif + (void)argc; + (void)argv; + fprintf(stdout, "Output before crash on stdout from crash test.\n"); + fprintf(stderr, "Output before crash on stderr from crash test.\n"); + fflush(stdout); + fflush(stderr); +#ifdef CRASH_USING_ABORT + abort(); +#else + assert(invalidAddress); /* Quiet Clang scan-build. */ + /* Provoke deliberate crash by writing to the invalid address. */ + *invalidAddress = 0; +#endif + fprintf(stdout, "Output after crash on stdout from crash test.\n"); + fprintf(stderr, "Output after crash on stderr from crash test.\n"); + return 0; +} + +static int test5(int argc, const char* argv[]) +{ + int r; + const char* cmd[4]; + (void)argc; + cmd[0] = argv[0]; + cmd[1] = "run"; + cmd[2] = "4"; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before recursive test.\n"); + fprintf(stderr, "Output on stderr before recursive test.\n"); + fflush(stdout); + fflush(stderr); + r = runChild(cmd, kwsysProcess_State_Exception, +#ifdef CRASH_USING_ABORT + kwsysProcess_Exception_Other, +#else + kwsysProcess_Exception_Fault, +#endif + 1, 1, 1, 0, 15, 0, 1, 0, 0, 0); + fprintf(stdout, "Output on stdout after recursive test.\n"); + fprintf(stderr, "Output on stderr after recursive test.\n"); + fflush(stdout); + fflush(stderr); + return r; +} + +#define TEST6_SIZE (4096 * 2) +static void test6(int argc, const char* argv[]) +{ + int i; + char runaway[TEST6_SIZE + 1]; + (void)argc; + (void)argv; + for (i = 0; i < TEST6_SIZE; ++i) { + runaway[i] = '.'; + } + runaway[TEST6_SIZE] = '\n'; + + /* Generate huge amounts of output to test killing. */ + for (;;) { + fwrite(runaway, 1, TEST6_SIZE + 1, stdout); + fflush(stdout); + } +} + +/* Define MINPOLL to be one more than the number of times output is + written. Define MAXPOLL to be the largest number of times a loop + delaying 1/10th of a second should ever have to poll. */ +#define MINPOLL 5 +#define MAXPOLL 20 +static int test7(int argc, const char* argv[]) +{ + (void)argc; + (void)argv; + fprintf(stdout, "Output on stdout before sleep.\n"); + fprintf(stderr, "Output on stderr before sleep.\n"); + fflush(stdout); + fflush(stderr); + /* Sleep for 1 second. */ + testProcess_sleep(1); + fprintf(stdout, "Output on stdout after sleep.\n"); + fprintf(stderr, "Output on stderr after sleep.\n"); + fflush(stdout); + fflush(stderr); + return 0; +} + +static int test8(int argc, const char* argv[]) +{ + /* Create a disowned grandchild to test handling of processes + that exit before their children. */ + int r; + const char* cmd[4]; + (void)argc; + cmd[0] = argv[0]; + cmd[1] = "run"; + cmd[2] = "108"; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before grandchild test.\n"); + fprintf(stderr, "Output on stderr before grandchild test.\n"); + fflush(stdout); + fflush(stderr); + r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None, + 1, 1, 1, 0, 10, 0, 1, 1, 0, 0); + fprintf(stdout, "Output on stdout after grandchild test.\n"); + fprintf(stderr, "Output on stderr after grandchild test.\n"); + fflush(stdout); + fflush(stderr); + return r; +} + +static int test8_grandchild(int argc, const char* argv[]) +{ + (void)argc; + (void)argv; + fprintf(stdout, "Output on stdout from grandchild before sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild before sleep.\n"); + fflush(stdout); + fflush(stderr); + /* TODO: Instead of closing pipes here leave them open to make sure + the grandparent can stop listening when the parent exits. This + part of the test cannot be enabled until the feature is + implemented. */ + fclose(stdout); + fclose(stderr); + testProcess_sleep(15); + return 0; +} + +static int test9(int argc, const char* argv[]) +{ + /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this + process. Here, we start a child process that sleeps for a long time + while ignoring signals. The test is successful if this process waits + for the child to return before exiting from the Ctrl+C handler. + + WARNING: This test will falsely pass if the share parameter of runChild + was set to 0 when invoking the test9 process. */ + int r; + const char* cmd[4]; + (void)argc; + cmd[0] = argv[0]; + cmd[1] = "run"; + cmd[2] = "109"; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before grandchild test.\n"); + fprintf(stderr, "Output on stderr before grandchild test.\n"); + fflush(stdout); + fflush(stderr); + r = runChild(cmd, kwsysProcess_State_Exited, kwsysProcess_Exception_None, 0, + 1, 1, 0, 30, 0, 1, 0, 0, 0); + /* This sleep will avoid a race condition between this function exiting + normally and our Ctrl+C handler exiting abnormally after the process + exits. */ + testProcess_sleep(1); + fprintf(stdout, "Output on stdout after grandchild test.\n"); + fprintf(stderr, "Output on stderr after grandchild test.\n"); + fflush(stdout); + fflush(stderr); + return r; +} + +#if defined(_WIN32) +static BOOL WINAPI test9_grandchild_handler(DWORD dwCtrlType) +{ + /* Ignore all Ctrl+C/Break signals. We must use an actual handler function + instead of using SetConsoleCtrlHandler(NULL, TRUE) so that we can also + ignore Ctrl+Break in addition to Ctrl+C. */ + (void)dwCtrlType; + return TRUE; +} +#endif + +static int test9_grandchild(int argc, const char* argv[]) +{ + /* The grandchild just sleeps for a few seconds while ignoring signals. */ + (void)argc; + (void)argv; +#if defined(_WIN32) + if (!SetConsoleCtrlHandler(test9_grandchild_handler, TRUE)) { + return 1; + } +#else + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, 0) < 0) { + return 1; + } +#endif + fprintf(stdout, "Output on stdout from grandchild before sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild before sleep.\n"); + fflush(stdout); + fflush(stderr); + /* Sleep for 9 seconds. */ + testProcess_sleep(9); + fprintf(stdout, "Output on stdout from grandchild after sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild after sleep.\n"); + fflush(stdout); + fflush(stderr); + return 0; +} + +static int test10(int argc, const char* argv[]) +{ + /* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this + process. Here, we start a child process that sleeps for a long time and + processes signals normally. However, this grandchild is created in a new + process group - ensuring that Ctrl+C we receive is sent to our process + groups. We make sure it exits anyway. */ + int r; + const char* cmd[4]; + (void)argc; + cmd[0] = argv[0]; + cmd[1] = "run"; + cmd[2] = "110"; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before grandchild test.\n"); + fprintf(stderr, "Output on stderr before grandchild test.\n"); + fflush(stdout); + fflush(stderr); + r = + runChild(cmd, kwsysProcess_State_Exception, + kwsysProcess_Exception_Interrupt, 0, 1, 1, 0, 30, 0, 1, 0, 1, 0); + fprintf(stdout, "Output on stdout after grandchild test.\n"); + fprintf(stderr, "Output on stderr after grandchild test.\n"); + fflush(stdout); + fflush(stderr); + return r; +} + +static int test10_grandchild(int argc, const char* argv[]) +{ + /* The grandchild just sleeps for a few seconds and handles signals. */ + (void)argc; + (void)argv; + fprintf(stdout, "Output on stdout from grandchild before sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild before sleep.\n"); + fflush(stdout); + fflush(stderr); + /* Sleep for 6 seconds. */ + testProcess_sleep(6); + fprintf(stdout, "Output on stdout from grandchild after sleep.\n"); + fprintf(stderr, "Output on stderr from grandchild after sleep.\n"); + fflush(stdout); + fflush(stderr); + return 0; +} + +static int runChild2(kwsysProcess* kp, const char* cmd[], int state, + int exception, int value, int share, int output, + int delay, double timeout, int poll, int disown, + int createNewGroup, unsigned int interruptDelay) +{ + int result = 0; + char* data = 0; + int length = 0; + double userTimeout = 0; + double* pUserTimeout = 0; + kwsysProcess_SetCommand(kp, cmd); + if (timeout >= 0) { + kwsysProcess_SetTimeout(kp, timeout); + } + if (share) { + kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDOUT, 1); + kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDERR, 1); + } + if (disown) { + kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1); + } + if (createNewGroup) { + kwsysProcess_SetOption(kp, kwsysProcess_Option_CreateProcessGroup, 1); + } + kwsysProcess_Execute(kp); + + if (poll) { + pUserTimeout = &userTimeout; + } + + if (interruptDelay) { + testProcess_sleep(interruptDelay); + kwsysProcess_Interrupt(kp); + } + + if (!share && !disown) { + int p; + while ((p = kwsysProcess_WaitForData(kp, &data, &length, pUserTimeout))) { + if (output) { + if (poll && p == kwsysProcess_Pipe_Timeout) { + fprintf(stdout, "WaitForData timeout reached.\n"); + fflush(stdout); + + /* Count the number of times we polled without getting data. + If it is excessive then kill the child and fail. */ + if (++poll >= MAXPOLL) { + fprintf(stdout, "Poll count reached limit %d.\n", MAXPOLL); + kwsysProcess_Kill(kp); + } + } else { + fwrite(data, 1, (size_t)length, stdout); + fflush(stdout); + } + } + if (poll) { + /* Delay to avoid busy loop during polling. */ + testProcess_usleep(100000); + } + if (delay) { +/* Purposely sleeping only on Win32 to let pipe fill up. */ +#if defined(_WIN32) + testProcess_usleep(100000); +#endif + } + } + } + + if (disown) { + kwsysProcess_Disown(kp); + } else { + kwsysProcess_WaitForExit(kp, 0); + } + + switch (kwsysProcess_GetState(kp)) { + case kwsysProcess_State_Starting: + printf("No process has been executed.\n"); + break; + case kwsysProcess_State_Executing: + printf("The process is still executing.\n"); + break; + case kwsysProcess_State_Expired: + printf("Child was killed when timeout expired.\n"); + break; + case kwsysProcess_State_Exited: + printf("Child exited with value = %d\n", kwsysProcess_GetExitValue(kp)); + result = ((exception != kwsysProcess_GetExitException(kp)) || + (value != kwsysProcess_GetExitValue(kp))); + break; + case kwsysProcess_State_Killed: + printf("Child was killed by parent.\n"); + break; + case kwsysProcess_State_Exception: + printf("Child terminated abnormally: %s\n", + kwsysProcess_GetExceptionString(kp)); + result = ((exception != kwsysProcess_GetExitException(kp)) || + (value != kwsysProcess_GetExitValue(kp))); + break; + case kwsysProcess_State_Disowned: + printf("Child was disowned.\n"); + break; + case kwsysProcess_State_Error: + printf("Error in administrating child process: [%s]\n", + kwsysProcess_GetErrorString(kp)); + break; + } + + if (result) { + if (exception != kwsysProcess_GetExitException(kp)) { + fprintf(stderr, + "Mismatch in exit exception. " + "Should have been %d, was %d.\n", + exception, kwsysProcess_GetExitException(kp)); + } + if (value != kwsysProcess_GetExitValue(kp)) { + fprintf(stderr, + "Mismatch in exit value. " + "Should have been %d, was %d.\n", + value, kwsysProcess_GetExitValue(kp)); + } + } + + if (kwsysProcess_GetState(kp) != state) { + fprintf(stderr, + "Mismatch in state. " + "Should have been %d, was %d.\n", + state, kwsysProcess_GetState(kp)); + result = 1; + } + + /* We should have polled more times than there were data if polling + was enabled. */ + if (poll && poll < MINPOLL) { + fprintf(stderr, "Poll count is %d, which is less than %d.\n", poll, + MINPOLL); + result = 1; + } + + return result; +} + +/** + * Runs a child process and blocks until it returns. Arguments as follows: + * + * cmd = Command line to run. + * state = Expected return value of kwsysProcess_GetState after exit. + * exception = Expected return value of kwsysProcess_GetExitException. + * value = Expected return value of kwsysProcess_GetExitValue. + * share = Whether to share stdout/stderr child pipes with our pipes + * by way of kwsysProcess_SetPipeShared. If false, new pipes + * are created. + * output = If !share && !disown, whether to write the child's stdout + * and stderr output to our stdout. + * delay = If !share && !disown, adds an additional short delay to + * the pipe loop to allow the pipes to fill up; Windows only. + * timeout = Non-zero to sets a timeout in seconds via + * kwsysProcess_SetTimeout. + * poll = If !share && !disown, we count the number of 0.1 second + * intervals where the child pipes had no new data. We fail + * if not in the bounds of MINPOLL/MAXPOLL. + * repeat = Number of times to run the process. + * disown = If set, the process is disowned. + * createNewGroup = If set, the process is created in a new process group. + * interruptDelay = If non-zero, number of seconds to delay before + * interrupting the process. Note that this delay will occur + * BEFORE any reading/polling of pipes occurs and before any + * detachment occurs. + */ +int runChild(const char* cmd[], int state, int exception, int value, int share, + int output, int delay, double timeout, int poll, int repeat, + int disown, int createNewGroup, unsigned int interruptDelay) +{ + int result = 1; + kwsysProcess* kp = kwsysProcess_New(); + if (!kp) { + fprintf(stderr, "kwsysProcess_New returned NULL!\n"); + return 1; + } + while (repeat-- > 0) { + result = runChild2(kp, cmd, state, exception, value, share, output, delay, + timeout, poll, disown, createNewGroup, interruptDelay); + if (result) { + break; + } + } + kwsysProcess_Delete(kp); + return result; +} + +int main(int argc, const char* argv[]) +{ + int n = 0; + +#ifdef _WIN32 + int i; + char new_args[10][_MAX_PATH]; + LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &argc); + for (i = 0; i < argc; i++) { + kwsysEncoding_wcstombs(new_args[i], w_av[i], _MAX_PATH); + argv[i] = new_args[i]; + } + LocalFree(w_av); +#endif + +#if 0 + { + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + DuplicateHandle(GetCurrentProcess(), out, + GetCurrentProcess(), &out, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + SetStdHandle(STD_OUTPUT_HANDLE, out); + } + { + HANDLE out = GetStdHandle(STD_ERROR_HANDLE); + DuplicateHandle(GetCurrentProcess(), out, + GetCurrentProcess(), &out, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + SetStdHandle(STD_ERROR_HANDLE, out); + } +#endif + if (argc == 2) { + n = atoi(argv[1]); + } else if (argc == 3 && strcmp(argv[1], "run") == 0) { + n = atoi(argv[2]); + } + /* Check arguments. */ + if (((n >= 1 && n <= 10) || n == 108 || n == 109 || n == 110) && argc == 3) { + /* This is the child process for a requested test number. */ + switch (n) { + case 1: + return test1(argc, argv); + case 2: + return test2(argc, argv); + case 3: + return test3(argc, argv); + case 4: + return test4(argc, argv); + case 5: + return test5(argc, argv); + case 6: + test6(argc, argv); + return 0; + case 7: + return test7(argc, argv); + case 8: + return test8(argc, argv); + case 9: + return test9(argc, argv); + case 10: + return test10(argc, argv); + case 108: + return test8_grandchild(argc, argv); + case 109: + return test9_grandchild(argc, argv); + case 110: + return test10_grandchild(argc, argv); + } + fprintf(stderr, "Invalid test number %d.\n", n); + return 1; + } else if (n >= 1 && n <= 10) { + /* This is the parent process for a requested test number. */ + int states[10] = { + kwsysProcess_State_Exited, kwsysProcess_State_Exited, + kwsysProcess_State_Expired, kwsysProcess_State_Exception, + kwsysProcess_State_Exited, kwsysProcess_State_Expired, + kwsysProcess_State_Exited, kwsysProcess_State_Exited, + kwsysProcess_State_Expired, /* Ctrl+C handler test */ + kwsysProcess_State_Exception /* Process group test */ + }; + int exceptions[10] = { + kwsysProcess_Exception_None, kwsysProcess_Exception_None, + kwsysProcess_Exception_None, +#ifdef CRASH_USING_ABORT + kwsysProcess_Exception_Other, +#else + kwsysProcess_Exception_Fault, +#endif + kwsysProcess_Exception_None, kwsysProcess_Exception_None, + kwsysProcess_Exception_None, kwsysProcess_Exception_None, + kwsysProcess_Exception_None, kwsysProcess_Exception_Interrupt + }; + int values[10] = { 0, 123, 1, 1, 0, 0, 0, 0, 1, 1 }; + int shares[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + int outputs[10] = { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 }; + int delays[10] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; + double timeouts[10] = { 10, 10, 10, 30, 30, 10, -1, 10, 6, 4 }; + int polls[10] = { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }; + int repeat[10] = { 257, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + int createNewGroups[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + unsigned int interruptDelays[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 }; + int r; + const char* cmd[4]; +#ifdef _WIN32 + char* argv0 = 0; +#endif + char* test1IterationsStr = getenv("KWSYS_TEST_PROCESS_1_COUNT"); + if (test1IterationsStr) { + long int test1Iterations = strtol(test1IterationsStr, 0, 10); + if (test1Iterations > 10 && test1Iterations != LONG_MAX) { + repeat[0] = (int)test1Iterations; + } + } +#ifdef _WIN32 + if (n == 0 && (argv0 = strdup(argv[0]))) { + /* Try converting to forward slashes to see if it works. */ + char* c; + for (c = argv0; *c; ++c) { + if (*c == '\\') { + *c = '/'; + } + } + cmd[0] = argv0; + } else { + cmd[0] = argv[0]; + } +#else + cmd[0] = argv[0]; +#endif + cmd[1] = "run"; + cmd[2] = argv[1]; + cmd[3] = 0; + fprintf(stdout, "Output on stdout before test %d.\n", n); + fprintf(stderr, "Output on stderr before test %d.\n", n); + fflush(stdout); + fflush(stderr); + r = runChild(cmd, states[n - 1], exceptions[n - 1], values[n - 1], + shares[n - 1], outputs[n - 1], delays[n - 1], timeouts[n - 1], + polls[n - 1], repeat[n - 1], 0, createNewGroups[n - 1], + interruptDelays[n - 1]); + fprintf(stdout, "Output on stdout after test %d.\n", n); + fprintf(stderr, "Output on stderr after test %d.\n", n); + fflush(stdout); + fflush(stderr); +#if defined(_WIN32) + free(argv0); +#endif + return r; + } else if (argc > 2 && strcmp(argv[1], "0") == 0) { + /* This is the special debugging test to run a given command + line. */ + const char** cmd = argv + 2; + int state = kwsysProcess_State_Exited; + int exception = kwsysProcess_Exception_None; + int value = 0; + double timeout = 0; + int r = + runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0, 0, 0); + return r; + } else { + /* Improper usage. */ + fprintf(stdout, "Usage: %s <test number>\n", argv[0]); + return 1; + } +} diff --git a/Source/kwsys/testSharedForward.c.in b/Source/kwsys/testSharedForward.c.in new file mode 100644 index 0000000..b3eb413 --- /dev/null +++ b/Source/kwsys/testSharedForward.c.in @@ -0,0 +1,27 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#if defined(CMAKE_INTDIR) +# define CONFIG_DIR_PRE CMAKE_INTDIR "/" +# define CONFIG_DIR_POST "/" CMAKE_INTDIR +#else +# define CONFIG_DIR_PRE "" +# define CONFIG_DIR_POST "" +#endif +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "@EXEC_DIR@" +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL 0 +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD \ + CONFIG_DIR_PRE "@KWSYS_NAMESPACE@TestProcess" +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL \ + "@KWSYS_NAMESPACE@TestProcess" +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command" +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print" +#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd" +#if defined(CMAKE_INTDIR) +# define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR +#endif +#include <@KWSYS_NAMESPACE@/SharedForward.h> +int main(int argc, char** argv) +{ + return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv); +} diff --git a/Source/kwsys/testSystemInformation.cxx b/Source/kwsys/testSystemInformation.cxx new file mode 100644 index 0000000..154517e --- /dev/null +++ b/Source/kwsys/testSystemInformation.cxx @@ -0,0 +1,106 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(SystemInformation.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "SystemInformation.hxx.in" +#endif + +#include <iostream> + +#if defined(KWSYS_USE_LONG_LONG) +# if defined(KWSYS_IOS_HAS_OSTREAM_LONG_LONG) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#elif defined(KWSYS_USE___INT64) +# if defined(KWSYS_IOS_HAS_OSTREAM___INT64) +# define iostreamLongLong(x) (x) +# else +# define iostreamLongLong(x) ((long)x) +# endif +#else +# error "No Long Long" +#endif + +#define printMethod(info, m) std::cout << #m << ": " << info.m() << "\n" + +#define printMethod2(info, m, unit) \ + std::cout << #m << ": " << info.m() << " " << unit << "\n" + +#define printMethod3(info, m, unit) \ + std::cout << #m << ": " << iostreamLongLong(info.m) << " " << unit << "\n" + +int testSystemInformation(int, char* []) +{ + std::cout << "CTEST_FULL_OUTPUT\n"; // avoid truncation + + kwsys::SystemInformation info; + info.RunCPUCheck(); + info.RunOSCheck(); + info.RunMemoryCheck(); + printMethod(info, GetOSName); + printMethod(info, GetOSIsLinux); + printMethod(info, GetOSIsApple); + printMethod(info, GetOSIsWindows); + printMethod(info, GetHostname); + printMethod(info, GetFullyQualifiedDomainName); + printMethod(info, GetOSRelease); + printMethod(info, GetOSVersion); + printMethod(info, GetOSPlatform); + printMethod(info, Is64Bits); + printMethod(info, GetVendorString); + printMethod(info, GetVendorID); + printMethod(info, GetTypeID); + printMethod(info, GetFamilyID); + printMethod(info, GetModelID); + printMethod(info, GetExtendedProcessorName); + printMethod(info, GetSteppingCode); + printMethod(info, GetProcessorSerialNumber); + printMethod2(info, GetProcessorCacheSize, "KB"); + printMethod(info, GetLogicalProcessorsPerPhysical); + printMethod2(info, GetProcessorClockFrequency, "MHz"); + printMethod(info, GetNumberOfLogicalCPU); + printMethod(info, GetNumberOfPhysicalCPU); + printMethod(info, DoesCPUSupportCPUID); + printMethod(info, GetProcessorAPICID); + printMethod2(info, GetTotalVirtualMemory, "MB"); + printMethod2(info, GetAvailableVirtualMemory, "MB"); + printMethod2(info, GetTotalPhysicalMemory, "MB"); + printMethod2(info, GetAvailablePhysicalMemory, "MB"); + printMethod3(info, GetHostMemoryTotal(), "KiB"); + printMethod3(info, GetHostMemoryAvailable("KWSHL"), "KiB"); + printMethod3(info, GetProcMemoryAvailable("KWSHL", "KWSPL"), "KiB"); + printMethod3(info, GetHostMemoryUsed(), "KiB"); + printMethod3(info, GetProcMemoryUsed(), "KiB"); + printMethod(info, GetLoadAverage); + + for (long int i = 0; i <= 31; i++) { + if (info.DoesCPUSupportFeature(static_cast<long int>(1) << i)) { + std::cout << "CPU feature " << i << "\n"; + } + } + + /* test stack trace + */ + std::cout << "Program Stack:" << std::endl + << kwsys::SystemInformation::GetProgramStack(0, 0) << std::endl + << std::endl; + + /* test segv handler + info.SetStackTraceOnError(1); + double *d = (double*)100; + *d=0; + */ + + /* test abort handler + info.SetStackTraceOnError(1); + abort(); + */ + + return 0; +} diff --git a/Source/kwsys/testSystemTools.bin b/Source/kwsys/testSystemTools.bin Binary files differnew file mode 100644 index 0000000..961a404 --- /dev/null +++ b/Source/kwsys/testSystemTools.bin diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx new file mode 100644 index 0000000..670e8dc --- /dev/null +++ b/Source/kwsys/testSystemTools.cxx @@ -0,0 +1,1166 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" + +#if defined(_MSC_VER) +# pragma warning(disable : 4786) +#endif + +#include KWSYS_HEADER(FStream.hxx) +#include KWSYS_HEADER(SystemTools.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "FStream.hxx.in" +# include "SystemTools.hxx.in" +#endif + +// Include with <> instead of "" to avoid getting any in-source copy +// left on disk. +#include <testSystemTools.h> + +#include <iostream> +#include <sstream> +#include <stdlib.h> /* free */ +#include <string.h> /* strcmp */ +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <io.h> /* _umask (MSVC) / umask (Borland) */ +# ifdef _MSC_VER +# define umask _umask // Note this is still umask on Borland +# endif +#endif +#include <sys/stat.h> /* umask (POSIX), _S_I* constants (Windows) */ +// Visual C++ does not define mode_t (note that Borland does, however). +#if defined(_MSC_VER) +typedef unsigned short mode_t; +#endif + +static const char* toUnixPaths[][2] = { + { "/usr/local/bin/passwd", "/usr/local/bin/passwd" }, + { "/usr/lo cal/bin/pa sswd", "/usr/lo cal/bin/pa sswd" }, + { "/usr/lo\\ cal/bin/pa\\ sswd", "/usr/lo/ cal/bin/pa/ sswd" }, + { "c:/usr/local/bin/passwd", "c:/usr/local/bin/passwd" }, + { "c:/usr/lo cal/bin/pa sswd", "c:/usr/lo cal/bin/pa sswd" }, + { "c:/usr/lo\\ cal/bin/pa\\ sswd", "c:/usr/lo/ cal/bin/pa/ sswd" }, + { "\\usr\\local\\bin\\passwd", "/usr/local/bin/passwd" }, + { "\\usr\\lo cal\\bin\\pa sswd", "/usr/lo cal/bin/pa sswd" }, + { "\\usr\\lo\\ cal\\bin\\pa\\ sswd", "/usr/lo/ cal/bin/pa/ sswd" }, + { "c:\\usr\\local\\bin\\passwd", "c:/usr/local/bin/passwd" }, + { "c:\\usr\\lo cal\\bin\\pa sswd", "c:/usr/lo cal/bin/pa sswd" }, + { "c:\\usr\\lo\\ cal\\bin\\pa\\ sswd", "c:/usr/lo/ cal/bin/pa/ sswd" }, + { "\\\\usr\\local\\bin\\passwd", "//usr/local/bin/passwd" }, + { "\\\\usr\\lo cal\\bin\\pa sswd", "//usr/lo cal/bin/pa sswd" }, + { "\\\\usr\\lo\\ cal\\bin\\pa\\ sswd", "//usr/lo/ cal/bin/pa/ sswd" }, + { nullptr, nullptr } +}; + +static bool CheckConvertToUnixSlashes(std::string const& input, + std::string const& output) +{ + std::string result = input; + kwsys::SystemTools::ConvertToUnixSlashes(result); + if (result != output) { + std::cerr << "Problem with ConvertToUnixSlashes - input: " << input + << " output: " << result << " expected: " << output << std::endl; + return false; + } + return true; +} + +static const char* checkEscapeChars[][4] = { + { "1 foo 2 bar 2", "12", "\\", "\\1 foo \\2 bar \\2" }, + { " {} ", "{}", "#", " #{#} " }, + { nullptr, nullptr, nullptr, nullptr } +}; + +static bool CheckEscapeChars(std::string const& input, + const char* chars_to_escape, char escape_char, + std::string const& output) +{ + std::string result = kwsys::SystemTools::EscapeChars( + input.c_str(), chars_to_escape, escape_char); + if (result != output) { + std::cerr << "Problem with CheckEscapeChars - input: " << input + << " output: " << result << " expected: " << output << std::endl; + return false; + } + return true; +} + +static bool CheckFileOperations() +{ + bool res = true; + const std::string testNonExistingFile(TEST_SYSTEMTOOLS_SOURCE_DIR + "/testSystemToolsNonExistingFile"); + const std::string testDotFile(TEST_SYSTEMTOOLS_SOURCE_DIR "/."); + const std::string testBinFile(TEST_SYSTEMTOOLS_SOURCE_DIR + "/testSystemTools.bin"); + const std::string testTxtFile(TEST_SYSTEMTOOLS_SOURCE_DIR + "/testSystemTools.cxx"); + const std::string testNewDir(TEST_SYSTEMTOOLS_BINARY_DIR + "/testSystemToolsNewDir"); + const std::string testNewFile(testNewDir + "/testNewFile.txt"); + + if (kwsys::SystemTools::DetectFileType(testNonExistingFile.c_str()) != + kwsys::SystemTools::FileTypeUnknown) { + std::cerr << "Problem with DetectFileType - failed to detect type of: " + << testNonExistingFile << std::endl; + res = false; + } + + if (kwsys::SystemTools::DetectFileType(testDotFile.c_str()) != + kwsys::SystemTools::FileTypeUnknown) { + std::cerr << "Problem with DetectFileType - failed to detect type of: " + << testDotFile << std::endl; + res = false; + } + + if (kwsys::SystemTools::DetectFileType(testBinFile.c_str()) != + kwsys::SystemTools::FileTypeBinary) { + std::cerr << "Problem with DetectFileType - failed to detect type of: " + << testBinFile << std::endl; + res = false; + } + + if (kwsys::SystemTools::DetectFileType(testTxtFile.c_str()) != + kwsys::SystemTools::FileTypeText) { + std::cerr << "Problem with DetectFileType - failed to detect type of: " + << testTxtFile << std::endl; + res = false; + } + + if (kwsys::SystemTools::FileLength(testBinFile) != 766) { + std::cerr << "Problem with FileLength - incorrect length for: " + << testBinFile << std::endl; + res = false; + } + + kwsys::SystemTools::Stat_t buf; + if (kwsys::SystemTools::Stat(testTxtFile.c_str(), &buf) != 0) { + std::cerr << "Problem with Stat - unable to stat text file: " + << testTxtFile << std::endl; + res = false; + } + + if (kwsys::SystemTools::Stat(testBinFile, &buf) != 0) { + std::cerr << "Problem with Stat - unable to stat bin file: " << testBinFile + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::MakeDirectory(testNewDir)) { + std::cerr << "Problem with MakeDirectory for: " << testNewDir << std::endl; + res = false; + } + // calling it again should just return true + if (!kwsys::SystemTools::MakeDirectory(testNewDir)) { + std::cerr << "Problem with second call to MakeDirectory for: " + << testNewDir << std::endl; + res = false; + } + // calling with 0 pointer should return false + if (kwsys::SystemTools::MakeDirectory(nullptr)) { + std::cerr << "Problem with MakeDirectory(0)" << std::endl; + res = false; + } + // calling with an empty string should return false + if (kwsys::SystemTools::MakeDirectory(std::string())) { + std::cerr << "Problem with MakeDirectory(std::string())" << std::endl; + res = false; + } + // check existence + if (!kwsys::SystemTools::FileExists(testNewDir.c_str(), false)) { + std::cerr << "Problem with FileExists as C string and not file for: " + << testNewDir << std::endl; + res = false; + } + // check existence + if (!kwsys::SystemTools::PathExists(testNewDir)) { + std::cerr << "Problem with PathExists for: " << testNewDir << std::endl; + res = false; + } + // remove it + if (!kwsys::SystemTools::RemoveADirectory(testNewDir)) { + std::cerr << "Problem with RemoveADirectory for: " << testNewDir + << std::endl; + res = false; + } + // check existence + if (kwsys::SystemTools::FileExists(testNewDir.c_str(), false)) { + std::cerr << "After RemoveADirectory: " + << "Problem with FileExists as C string and not file for: " + << testNewDir << std::endl; + res = false; + } + // check existence + if (kwsys::SystemTools::PathExists(testNewDir)) { + std::cerr << "After RemoveADirectory: " + << "Problem with PathExists for: " << testNewDir << std::endl; + res = false; + } + // create it using the char* version + if (!kwsys::SystemTools::MakeDirectory(testNewDir.c_str())) { + std::cerr << "Problem with second call to MakeDirectory as C string for: " + << testNewDir << std::endl; + res = false; + } + + if (!kwsys::SystemTools::Touch(testNewFile, true)) { + std::cerr << "Problem with Touch for: " << testNewFile << std::endl; + res = false; + } + // calling MakeDirectory with something that is no file should fail + if (kwsys::SystemTools::MakeDirectory(testNewFile)) { + std::cerr << "Problem with to MakeDirectory for: " << testNewFile + << std::endl; + res = false; + } + + // calling with 0 pointer should return false + if (kwsys::SystemTools::FileExists(nullptr)) { + std::cerr << "Problem with FileExists(0)" << std::endl; + res = false; + } + if (kwsys::SystemTools::FileExists(nullptr, true)) { + std::cerr << "Problem with FileExists(0) as file" << std::endl; + res = false; + } + // calling with an empty string should return false + if (kwsys::SystemTools::FileExists(std::string())) { + std::cerr << "Problem with FileExists(std::string())" << std::endl; + res = false; + } + // FileExists(x, true) should return false on a directory + if (kwsys::SystemTools::FileExists(testNewDir, true)) { + std::cerr << "Problem with FileExists as file for: " << testNewDir + << std::endl; + res = false; + } + if (kwsys::SystemTools::FileExists(testNewDir.c_str(), true)) { + std::cerr << "Problem with FileExists as C string and file for: " + << testNewDir << std::endl; + res = false; + } + // FileExists(x, false) should return true even on a directory + if (!kwsys::SystemTools::FileExists(testNewDir, false)) { + std::cerr << "Problem with FileExists as not file for: " << testNewDir + << std::endl; + res = false; + } + if (!kwsys::SystemTools::FileExists(testNewDir.c_str(), false)) { + std::cerr << "Problem with FileExists as C string and not file for: " + << testNewDir << std::endl; + res = false; + } + // should work, was created as new file before + if (!kwsys::SystemTools::FileExists(testNewFile)) { + std::cerr << "Problem with FileExists for: " << testNewFile << std::endl; + res = false; + } + if (!kwsys::SystemTools::FileExists(testNewFile.c_str())) { + std::cerr << "Problem with FileExists as C string for: " << testNewFile + << std::endl; + res = false; + } + if (!kwsys::SystemTools::FileExists(testNewFile, true)) { + std::cerr << "Problem with FileExists as file for: " << testNewFile + << std::endl; + res = false; + } + if (!kwsys::SystemTools::FileExists(testNewFile.c_str(), true)) { + std::cerr << "Problem with FileExists as C string and file for: " + << testNewFile << std::endl; + res = false; + } + + // calling with an empty string should return false + if (kwsys::SystemTools::PathExists(std::string())) { + std::cerr << "Problem with PathExists(std::string())" << std::endl; + res = false; + } + // PathExists(x) should return true on a directory + if (!kwsys::SystemTools::PathExists(testNewDir)) { + std::cerr << "Problem with PathExists for: " << testNewDir << std::endl; + res = false; + } + // should work, was created as new file before + if (!kwsys::SystemTools::PathExists(testNewFile)) { + std::cerr << "Problem with PathExists for: " << testNewFile << std::endl; + res = false; + } + +// Reset umask +#if defined(_WIN32) && !defined(__CYGWIN__) + // NOTE: Windows doesn't support toggling _S_IREAD. + mode_t fullMask = _S_IWRITE; +#else + // On a normal POSIX platform, we can toggle all permissions. + mode_t fullMask = S_IRWXU | S_IRWXG | S_IRWXO; +#endif + mode_t orig_umask = umask(fullMask); + + // Test file permissions without umask + mode_t origPerm, thisPerm; + if (!kwsys::SystemTools::GetPermissions(testNewFile, origPerm)) { + std::cerr << "Problem with GetPermissions (1) for: " << testNewFile + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::SetPermissions(testNewFile, 0)) { + std::cerr << "Problem with SetPermissions (1) for: " << testNewFile + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::GetPermissions(testNewFile, thisPerm)) { + std::cerr << "Problem with GetPermissions (2) for: " << testNewFile + << std::endl; + res = false; + } + + if ((thisPerm & fullMask) != 0) { + std::cerr << "SetPermissions failed to set permissions (1) for: " + << testNewFile << ": actual = " << thisPerm + << "; expected = " << 0 << std::endl; + res = false; + } + + // While we're at it, check proper TestFileAccess functionality. + if (kwsys::SystemTools::TestFileAccess(testNewFile, + kwsys::TEST_FILE_WRITE)) { + std::cerr + << "TestFileAccess incorrectly indicated that this is a writable file:" + << testNewFile << std::endl; + res = false; + } + + if (!kwsys::SystemTools::TestFileAccess(testNewFile, kwsys::TEST_FILE_OK)) { + std::cerr + << "TestFileAccess incorrectly indicated that this file does not exist:" + << testNewFile << std::endl; + res = false; + } + + // Test restoring/setting full permissions. + if (!kwsys::SystemTools::SetPermissions(testNewFile, fullMask)) { + std::cerr << "Problem with SetPermissions (2) for: " << testNewFile + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::GetPermissions(testNewFile, thisPerm)) { + std::cerr << "Problem with GetPermissions (3) for: " << testNewFile + << std::endl; + res = false; + } + + if ((thisPerm & fullMask) != fullMask) { + std::cerr << "SetPermissions failed to set permissions (2) for: " + << testNewFile << ": actual = " << thisPerm + << "; expected = " << fullMask << std::endl; + res = false; + } + + // Test setting file permissions while honoring umask + if (!kwsys::SystemTools::SetPermissions(testNewFile, fullMask, true)) { + std::cerr << "Problem with SetPermissions (3) for: " << testNewFile + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::GetPermissions(testNewFile, thisPerm)) { + std::cerr << "Problem with GetPermissions (4) for: " << testNewFile + << std::endl; + res = false; + } + + if ((thisPerm & fullMask) != 0) { + std::cerr << "SetPermissions failed to honor umask for: " << testNewFile + << ": actual = " << thisPerm << "; expected = " << 0 + << std::endl; + res = false; + } + + // Restore umask + umask(orig_umask); + + // Restore file permissions + if (!kwsys::SystemTools::SetPermissions(testNewFile, origPerm)) { + std::cerr << "Problem with SetPermissions (4) for: " << testNewFile + << std::endl; + res = false; + } + + // Remove the test file + if (!kwsys::SystemTools::RemoveFile(testNewFile)) { + std::cerr << "Problem with RemoveFile: " << testNewFile << std::endl; + res = false; + } + + std::string const testFileMissing(testNewDir + "/testMissingFile.txt"); + if (!kwsys::SystemTools::RemoveFile(testFileMissing)) { + std::string const& msg = kwsys::SystemTools::GetLastSystemError(); + std::cerr << "RemoveFile(\"" << testFileMissing << "\") failed: " << msg + << "\n"; + res = false; + } + + std::string const testFileMissingDir(testNewDir + "/missing/file.txt"); + if (!kwsys::SystemTools::RemoveFile(testFileMissingDir)) { + std::string const& msg = kwsys::SystemTools::GetLastSystemError(); + std::cerr << "RemoveFile(\"" << testFileMissingDir << "\") failed: " << msg + << "\n"; + res = false; + } + + kwsys::SystemTools::Touch(testNewFile, true); + if (!kwsys::SystemTools::RemoveADirectory(testNewDir)) { + std::cerr << "Problem with RemoveADirectory for: " << testNewDir + << std::endl; + res = false; + } + +#ifdef KWSYS_TEST_SYSTEMTOOLS_LONG_PATHS + // Perform the same file and directory creation and deletion tests but + // with paths > 256 characters in length. + + const std::string testNewLongDir( + TEST_SYSTEMTOOLS_BINARY_DIR + "/" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "01234567890123"); + const std::string testNewLongFile( + testNewLongDir + + "/" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "0123456789.txt"); + + if (!kwsys::SystemTools::MakeDirectory(testNewLongDir)) { + std::cerr << "Problem with MakeDirectory for: " << testNewLongDir + << std::endl; + res = false; + } + + if (!kwsys::SystemTools::Touch(testNewLongFile.c_str(), true)) { + std::cerr << "Problem with Touch for: " << testNewLongFile << std::endl; + res = false; + } + + if (!kwsys::SystemTools::RemoveFile(testNewLongFile)) { + std::cerr << "Problem with RemoveFile: " << testNewLongFile << std::endl; + res = false; + } + + kwsys::SystemTools::Touch(testNewLongFile.c_str(), true); + if (!kwsys::SystemTools::RemoveADirectory(testNewLongDir)) { + std::cerr << "Problem with RemoveADirectory for: " << testNewLongDir + << std::endl; + res = false; + } +#endif + + return res; +} + +static bool CheckStringOperations() +{ + bool res = true; + + std::string test = "mary had a little lamb."; + if (kwsys::SystemTools::CapitalizedWords(test) != + "Mary Had A Little Lamb.") { + std::cerr << "Problem with CapitalizedWords " << '"' << test << '"' + << std::endl; + res = false; + } + + test = "Mary Had A Little Lamb."; + if (kwsys::SystemTools::UnCapitalizedWords(test) != + "mary had a little lamb.") { + std::cerr << "Problem with UnCapitalizedWords " << '"' << test << '"' + << std::endl; + res = false; + } + + test = "MaryHadTheLittleLamb."; + if (kwsys::SystemTools::AddSpaceBetweenCapitalizedWords(test) != + "Mary Had The Little Lamb.") { + std::cerr << "Problem with AddSpaceBetweenCapitalizedWords " << '"' << test + << '"' << std::endl; + res = false; + } + + char* cres = + kwsys::SystemTools::AppendStrings("Mary Had A", " Little Lamb."); + if (strcmp(cres, "Mary Had A Little Lamb.")) { + std::cerr << "Problem with AppendStrings " + << "\"Mary Had A\" \" Little Lamb.\"" << std::endl; + res = false; + } + delete[] cres; + + cres = kwsys::SystemTools::AppendStrings("Mary Had", " A ", "Little Lamb."); + if (strcmp(cres, "Mary Had A Little Lamb.")) { + std::cerr << "Problem with AppendStrings " + << "\"Mary Had\" \" A \" \"Little Lamb.\"" << std::endl; + res = false; + } + delete[] cres; + + if (kwsys::SystemTools::CountChar("Mary Had A Little Lamb.", 'a') != 3) { + std::cerr << "Problem with CountChar " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + + cres = kwsys::SystemTools::RemoveChars("Mary Had A Little Lamb.", "aeiou"); + if (strcmp(cres, "Mry Hd A Lttl Lmb.")) { + std::cerr << "Problem with RemoveChars " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + delete[] cres; + + cres = kwsys::SystemTools::RemoveCharsButUpperHex("Mary Had A Little Lamb."); + if (strcmp(cres, "A")) { + std::cerr << "Problem with RemoveCharsButUpperHex " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + delete[] cres; + + char* cres2 = strdup("Mary Had A Little Lamb."); + kwsys::SystemTools::ReplaceChars(cres2, "aeiou", 'X'); + if (strcmp(cres2, "MXry HXd A LXttlX LXmb.")) { + std::cerr << "Problem with ReplaceChars " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + free(cres2); + + if (!kwsys::SystemTools::StringStartsWith("Mary Had A Little Lamb.", + "Mary ")) { + std::cerr << "Problem with StringStartsWith " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + + if (!kwsys::SystemTools::StringEndsWith("Mary Had A Little Lamb.", + " Lamb.")) { + std::cerr << "Problem with StringEndsWith " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + + cres = kwsys::SystemTools::DuplicateString("Mary Had A Little Lamb."); + if (strcmp(cres, "Mary Had A Little Lamb.")) { + std::cerr << "Problem with DuplicateString " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + delete[] cres; + + test = "Mary Had A Little Lamb."; + if (kwsys::SystemTools::CropString(test, 13) != "Mary ...Lamb.") { + std::cerr << "Problem with CropString " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + + std::vector<std::string> lines; + kwsys::SystemTools::Split("Mary Had A Little Lamb.", lines, ' '); + if (lines[0] != "Mary" || lines[1] != "Had" || lines[2] != "A" || + lines[3] != "Little" || lines[4] != "Lamb.") { + std::cerr << "Problem with Split " + << "\"Mary Had A Little Lamb.\"" << std::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsOutputPath( + "L://Local Mojo/Hex Power Pack/Iffy Voodoo") != + "\"L:\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"") { + std::cerr << "Problem with ConvertToWindowsOutputPath " + << "\"L://Local Mojo/Hex Power Pack/Iffy Voodoo\"" << std::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToWindowsOutputPath( + "//grayson/Local Mojo/Hex Power Pack/Iffy Voodoo") != + "\"\\\\grayson\\Local Mojo\\Hex Power Pack\\Iffy Voodoo\"") { + std::cerr << "Problem with ConvertToWindowsOutputPath " + << "\"//grayson/Local Mojo/Hex Power Pack/Iffy Voodoo\"" + << std::endl; + res = false; + } + + if (kwsys::SystemTools::ConvertToUnixOutputPath( + "//Local Mojo/Hex Power Pack/Iffy Voodoo") != + "//Local\\ Mojo/Hex\\ Power\\ Pack/Iffy\\ Voodoo") { + std::cerr << "Problem with ConvertToUnixOutputPath " + << "\"//Local Mojo/Hex Power Pack/Iffy Voodoo\"" << std::endl; + res = false; + } + + return res; +} + +static bool CheckPutEnv(const std::string& env, const char* name, + const char* value) +{ + if (!kwsys::SystemTools::PutEnv(env)) { + std::cerr << "PutEnv(\"" << env << "\") failed!" << std::endl; + return false; + } + std::string v = "(null)"; + kwsys::SystemTools::GetEnv(name, v); + if (v != value) { + std::cerr << "GetEnv(\"" << name << "\") returned \"" << v << "\", not \"" + << value << "\"!" << std::endl; + return false; + } + return true; +} + +static bool CheckUnPutEnv(const char* env, const char* name) +{ + if (!kwsys::SystemTools::UnPutEnv(env)) { + std::cerr << "UnPutEnv(\"" << env << "\") failed!" << std::endl; + return false; + } + std::string v; + if (kwsys::SystemTools::GetEnv(name, v)) { + std::cerr << "GetEnv(\"" << name << "\") returned \"" << v + << "\", not (null)!" << std::endl; + return false; + } + return true; +} + +static bool CheckEnvironmentOperations() +{ + bool res = true; + res &= CheckPutEnv("A=B", "A", "B"); + res &= CheckPutEnv("B=C", "B", "C"); + res &= CheckPutEnv("C=D", "C", "D"); + res &= CheckPutEnv("D=E", "D", "E"); + res &= CheckUnPutEnv("A", "A"); + res &= CheckUnPutEnv("B=", "B"); + res &= CheckUnPutEnv("C=D", "C"); + /* Leave "D=E" in environment so a memory checker can test for leaks. */ + return res; +} + +static bool CheckRelativePath(const std::string& local, + const std::string& remote, + const std::string& expected) +{ + std::string result = kwsys::SystemTools::RelativePath(local, remote); + if (!kwsys::SystemTools::ComparePath(expected, result)) { + std::cerr << "RelativePath(" << local << ", " << remote << ") yielded " + << result << " instead of " << expected << std::endl; + return false; + } + return true; +} + +static bool CheckRelativePaths() +{ + bool res = true; + res &= CheckRelativePath("/usr/share", "/bin/bash", "../../bin/bash"); + res &= CheckRelativePath("/usr/./share/", "/bin/bash", "../../bin/bash"); + res &= CheckRelativePath("/usr//share/", "/bin/bash", "../../bin/bash"); + res &= + CheckRelativePath("/usr/share/../bin/", "/bin/bash", "../../bin/bash"); + res &= CheckRelativePath("/usr/share", "/usr/share//bin", "bin"); + return res; +} + +static bool CheckCollapsePath(const std::string& path, + const std::string& expected, + const char* base = nullptr) +{ + std::string result = kwsys::SystemTools::CollapseFullPath(path, base); + if (!kwsys::SystemTools::ComparePath(expected, result)) { + std::cerr << "CollapseFullPath(" << path << ") yielded " << result + << " instead of " << expected << std::endl; + return false; + } + return true; +} + +static bool CheckCollapsePath() +{ + bool res = true; + res &= CheckCollapsePath("/usr/share/*", "/usr/share/*"); + res &= CheckCollapsePath("C:/Windows/*", "C:/Windows/*"); + res &= CheckCollapsePath("/usr/share/../lib", "/usr/lib"); + res &= CheckCollapsePath("/usr/share/./lib", "/usr/share/lib"); + res &= CheckCollapsePath("/usr/share/../../lib", "/lib"); + res &= CheckCollapsePath("/usr/share/.././../lib", "/lib"); + res &= CheckCollapsePath("/../lib", "/lib"); + res &= CheckCollapsePath("/../lib/", "/lib"); + res &= CheckCollapsePath("/", "/"); + res &= CheckCollapsePath("C:/", "C:/"); + res &= CheckCollapsePath("C:/../", "C:/"); + res &= CheckCollapsePath("C:/../../", "C:/"); + res &= CheckCollapsePath("../b", "../../b", "../"); + res &= CheckCollapsePath("../a/../b", "../b", "../rel"); + res &= CheckCollapsePath("a/../b", "../rel/b", "../rel"); + return res; +} + +static std::string StringVectorToString(const std::vector<std::string>& vec) +{ + std::stringstream ss; + ss << "vector("; + for (auto i = vec.begin(); i != vec.end(); ++i) { + if (i != vec.begin()) { + ss << ", "; + } + ss << *i; + } + ss << ")"; + return ss.str(); +} + +static bool CheckGetPath() +{ + const char* envName = "S"; +#ifdef _WIN32 + const char* envValue = "C:\\Somewhere\\something;D:\\Temp"; +#else + const char* envValue = "/Somewhere/something:/tmp"; +#endif + const char* registryPath = "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MyApp; MyKey]"; + + std::vector<std::string> originalPaths; + originalPaths.emplace_back(registryPath); + + std::vector<std::string> expectedPaths; + expectedPaths.emplace_back(registryPath); +#ifdef _WIN32 + expectedPaths.push_back("C:/Somewhere/something"); + expectedPaths.push_back("D:/Temp"); +#else + expectedPaths.emplace_back("/Somewhere/something"); + expectedPaths.emplace_back("/tmp"); +#endif + + bool res = true; + res &= CheckPutEnv(std::string(envName) + "=" + envValue, envName, envValue); + + std::vector<std::string> paths = originalPaths; + kwsys::SystemTools::GetPath(paths, envName); + + if (paths != expectedPaths) { + std::cerr << "GetPath(" << StringVectorToString(originalPaths) << ", " + << envName << ") yielded " << StringVectorToString(paths) + << " instead of " << StringVectorToString(expectedPaths) + << std::endl; + res = false; + } + + res &= CheckUnPutEnv(envName, envName); + return res; +} + +static bool CheckGetFilenameName() +{ + const char* windowsFilepath = "C:\\somewhere\\something"; + const char* unixFilepath = "/somewhere/something"; + +#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES) + std::string expectedWindowsFilename = "something"; +#else + std::string expectedWindowsFilename = "C:\\somewhere\\something"; +#endif + std::string expectedUnixFilename = "something"; + + bool res = true; + std::string filename = kwsys::SystemTools::GetFilenameName(windowsFilepath); + if (filename != expectedWindowsFilename) { + std::cerr << "GetFilenameName(" << windowsFilepath << ") yielded " + << filename << " instead of " << expectedWindowsFilename + << std::endl; + res = false; + } + + filename = kwsys::SystemTools::GetFilenameName(unixFilepath); + if (filename != expectedUnixFilename) { + std::cerr << "GetFilenameName(" << unixFilepath << ") yielded " << filename + << " instead of " << expectedUnixFilename << std::endl; + res = false; + } + return res; +} + +static bool CheckFind() +{ + bool res = true; + const std::string testFindFileName("testFindFile.txt"); + const std::string testFindFile(TEST_SYSTEMTOOLS_BINARY_DIR "/" + + testFindFileName); + + if (!kwsys::SystemTools::Touch(testFindFile, true)) { + std::cerr << "Problem with Touch for: " << testFindFile << std::endl; + // abort here as the existence of the file only makes the test meaningful + return false; + } + + std::vector<std::string> searchPaths; + searchPaths.emplace_back(TEST_SYSTEMTOOLS_BINARY_DIR); + if (kwsys::SystemTools::FindFile(testFindFileName, searchPaths, true) + .empty()) { + std::cerr << "Problem with FindFile without system paths for: " + << testFindFileName << std::endl; + res = false; + } + if (kwsys::SystemTools::FindFile(testFindFileName, searchPaths, false) + .empty()) { + std::cerr << "Problem with FindFile with system paths for: " + << testFindFileName << std::endl; + res = false; + } + + return res; +} + +static bool CheckIsSubDirectory() +{ + bool res = true; + + if (kwsys::SystemTools::IsSubDirectory("/foo", "/") == false) { + std::cerr << "Problem with IsSubDirectory (root - unix): " << std::endl; + res = false; + } + if (kwsys::SystemTools::IsSubDirectory("c:/foo", "c:/") == false) { + std::cerr << "Problem with IsSubDirectory (root - dos): " << std::endl; + res = false; + } + if (kwsys::SystemTools::IsSubDirectory("/foo/bar", "/foo") == false) { + std::cerr << "Problem with IsSubDirectory (deep): " << std::endl; + res = false; + } + if (kwsys::SystemTools::IsSubDirectory("/foo", "/foo") == true) { + std::cerr << "Problem with IsSubDirectory (identity): " << std::endl; + res = false; + } + if (kwsys::SystemTools::IsSubDirectory("/fooo", "/foo") == true) { + std::cerr << "Problem with IsSubDirectory (substring): " << std::endl; + res = false; + } + if (kwsys::SystemTools::IsSubDirectory("/foo/", "/foo") == true) { + std::cerr << "Problem with IsSubDirectory (prepended slash): " + << std::endl; + res = false; + } + + return res; +} + +static bool CheckGetLineFromStream() +{ + const std::string fileWithFiveCharsOnFirstLine(TEST_SYSTEMTOOLS_SOURCE_DIR + "/README.rst"); + + kwsys::ifstream file(fileWithFiveCharsOnFirstLine.c_str(), std::ios::in); + + if (!file) { + std::cerr << "Problem opening: " << fileWithFiveCharsOnFirstLine + << std::endl; + return false; + } + + std::string line; + bool has_newline = false; + bool result; + + file.seekg(0, std::ios::beg); + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line.size() != 5) { + std::cerr << "First line does not have five characters: " << line.size() + << std::endl; + return false; + } + + file.seekg(0, std::ios::beg); + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line.size() != 5) { + std::cerr << "First line does not have five characters after rewind: " + << line.size() << std::endl; + return false; + } + + bool ret = true; + + for (size_t size = 1; size <= 5; ++size) { + file.seekg(0, std::ios::beg); + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, + static_cast<long>(size)); + if (!result || line.size() != size) { + std::cerr << "Should have read " << size << " characters but got " + << line.size() << std::endl; + ret = false; + } + } + + return ret; +} + +static bool CheckGetLineFromStreamLongLine() +{ + const std::string fileWithLongLine("longlines.txt"); + std::string firstLine, secondLine; + // First line: large buffer, containing a carriage return for some reason. + firstLine.assign(2050, ' '); + firstLine += "\rfirst"; + secondLine.assign(2050, 'y'); + secondLine += "second"; + + // Create file with long lines. + { + kwsys::ofstream out(fileWithLongLine.c_str(), std::ios::binary); + if (!out) { + std::cerr << "Problem opening for write: " << fileWithLongLine + << std::endl; + return false; + } + out << firstLine << "\r\n\n" << secondLine << "\n"; + } + + kwsys::ifstream file(fileWithLongLine.c_str(), std::ios::binary); + if (!file) { + std::cerr << "Problem opening: " << fileWithLongLine << std::endl; + return false; + } + + std::string line; + bool has_newline = false; + bool result; + + // Read first line. + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != firstLine) { + std::cerr << "First line does not match, expected " << firstLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from first line" << std::endl; + return false; + } + + // Read empty line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || !line.empty()) { + std::cerr << "Expected successful read with an empty line, got " + << line.size() << " characters" << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read for an empty line" << std::endl; + return false; + } + + // Read second line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != secondLine) { + std::cerr << "Second line does not match, expected " << secondLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from second line" << std::endl; + return false; + } + + return true; +} + +static bool writeFile(const char* fileName, const char* data) +{ + kwsys::ofstream out(fileName, std::ios::binary); + out << data; + if (!out) { + std::cerr << "Failed to write file: " << fileName << std::endl; + return false; + } + return true; +} + +static std::string readFile(const char* fileName) +{ + kwsys::ifstream in(fileName, std::ios::binary); + std::stringstream sstr; + sstr << in.rdbuf(); + std::string data = sstr.str(); + if (!in) { + std::cerr << "Failed to read file: " << fileName << std::endl; + return std::string(); + } + return data; +} + +struct +{ + const char* a; + const char* b; + bool differ; +} diff_test_cases[] = { { "one", "one", false }, + { "one", "two", true }, + { "", "", false }, + { "\n", "\r\n", false }, + { "one\n", "one\n", false }, + { "one\r\n", "one\n", false }, + { "one\n", "one", false }, + { "one\ntwo", "one\ntwo", false }, + { "one\ntwo", "one\r\ntwo", false } }; + +static bool CheckTextFilesDiffer() +{ + const int num_test_cases = + sizeof(diff_test_cases) / sizeof(diff_test_cases[0]); + for (int i = 0; i < num_test_cases; ++i) { + if (!writeFile("file_a", diff_test_cases[i].a) || + !writeFile("file_b", diff_test_cases[i].b)) { + return false; + } + if (kwsys::SystemTools::TextFilesDiffer("file_a", "file_b") != + diff_test_cases[i].differ) { + std::cerr << "Incorrect TextFilesDiffer result for test case " << i + 1 + << "." << std::endl; + return false; + } + } + + return true; +} + +static bool CheckCopyFileIfDifferent() +{ + bool ret = true; + const int num_test_cases = + sizeof(diff_test_cases) / sizeof(diff_test_cases[0]); + for (int i = 0; i < num_test_cases; ++i) { + if (!writeFile("file_a", diff_test_cases[i].a) || + !writeFile("file_b", diff_test_cases[i].b)) { + return false; + } + const char* cptarget = + i < 4 ? TEST_SYSTEMTOOLS_BINARY_DIR "/file_b" : "file_b"; + if (!kwsys::SystemTools::CopyFileIfDifferent("file_a", cptarget)) { + std::cerr << "CopyFileIfDifferent() returned false for test case " + << i + 1 << "." << std::endl; + ret = false; + continue; + } + std::string bdata = readFile("file_b"); + if (diff_test_cases[i].a != bdata) { + std::cerr << "Incorrect CopyFileIfDifferent file contents in test case " + << i + 1 << "." << std::endl; + ret = false; + continue; + } + } + + if (!kwsys::SystemTools::MakeDirectory("dir_a") || + !kwsys::SystemTools::MakeDirectory("dir_b")) { + return false; + } + + if (!kwsys::SystemTools::CopyFileIfDifferent("dir_a/", "dir_b")) { + ret = false; + } + + return ret; +} + +static bool CheckURLParsing() +{ + bool ret = true; + std::string url = "http://user:pw@hostname:42/full/url.com"; + + std::string protocol, username, password, hostname, dataport, database; + kwsys::SystemTools::ParseURL(url, protocol, username, password, hostname, + dataport, database); + if (protocol != "http" || username != "user" || password != "pw" || + hostname != "hostname" || dataport != "42" || + database != "full/url.com") { + std::cerr << "Incorrect URL parsing" << std::endl; + ret = false; + } + + std::string uri = + "file://hostname/path/to/" + "a%20file%20with%20str%C3%A0ng%C3%A8%20ch%40r%20and%20s%C2%B5aces"; + kwsys::SystemTools::ParseURL(uri, protocol, username, password, hostname, + dataport, database, true); + if (protocol != "file" || hostname != "hostname" || + database != "path/to/a file with stràngè ch@r and sµaces") { + std::cerr << "Incorrect URL parsing or decoding" << std::endl; + ret = false; + } + return ret; +} + +int testSystemTools(int, char* []) +{ + bool res = true; + + int cc; + for (cc = 0; toUnixPaths[cc][0]; cc++) { + res &= CheckConvertToUnixSlashes(toUnixPaths[cc][0], toUnixPaths[cc][1]); + } + + // Special check for ~ + std::string output; + if (kwsys::SystemTools::GetEnv("HOME", output)) { + output += "/foo bar/lala"; + res &= CheckConvertToUnixSlashes("~/foo bar/lala", output); + } + + for (cc = 0; checkEscapeChars[cc][0]; cc++) { + res &= CheckEscapeChars(checkEscapeChars[cc][0], checkEscapeChars[cc][1], + *checkEscapeChars[cc][2], checkEscapeChars[cc][3]); + } + + res &= CheckFileOperations(); + + res &= CheckStringOperations(); + + res &= CheckEnvironmentOperations(); + + res &= CheckRelativePaths(); + + res &= CheckCollapsePath(); + + res &= CheckGetPath(); + + res &= CheckFind(); + + res &= CheckIsSubDirectory(); + + res &= CheckGetLineFromStream(); + + res &= CheckGetLineFromStreamLongLine(); + + res &= CheckGetFilenameName(); + + res &= CheckTextFilesDiffer(); + + res &= CheckCopyFileIfDifferent(); + + res &= CheckURLParsing(); + + return res ? 0 : 1; +} diff --git a/Source/kwsys/testSystemTools.h.in b/Source/kwsys/testSystemTools.h.in new file mode 100644 index 0000000..e4b89a7 --- /dev/null +++ b/Source/kwsys/testSystemTools.h.in @@ -0,0 +1,12 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifndef @KWSYS_NAMESPACE@_testSystemtools_h +#define @KWSYS_NAMESPACE@_testSystemtools_h + +#define RUNTIME_OUTPUT_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@" + +#define TEST_SYSTEMTOOLS_SOURCE_DIR "@TEST_SYSTEMTOOLS_SOURCE_DIR@" +#define TEST_SYSTEMTOOLS_BINARY_DIR "@TEST_SYSTEMTOOLS_BINARY_DIR@" +#cmakedefine KWSYS_TEST_SYSTEMTOOLS_LONG_PATHS + +#endif diff --git a/Source/kwsys/testTerminal.c b/Source/kwsys/testTerminal.c new file mode 100644 index 0000000..652830c --- /dev/null +++ b/Source/kwsys/testTerminal.c @@ -0,0 +1,22 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(Terminal.h) + +/* Work-around CMake dependency scanning limitation. This must + duplicate the above list of headers. */ +#if 0 +# include "Terminal.h.in" +#endif + +int testTerminal(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + kwsysTerminal_cfprintf(kwsysTerminal_Color_ForegroundYellow | + kwsysTerminal_Color_BackgroundBlue | + kwsysTerminal_Color_AssumeTTY, + stdout, "Hello %s!", "World"); + fprintf(stdout, "\n"); + return 0; +} |