From: Michal MalĂ˝ Date: Thu, 26 Mar 2015 15:22:29 +0000 (+0100) Subject: - Add generic support for reading MTH files X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=730ff828620032f589fc60f8cbe1ce4235b07437;p=libHPCS.git - Add generic support for reading MTH files - Use ICU on Unix systems to read UTF-16 encoded files - Implement MTH files reading on Unix - Add dummy support for MTH files reading on Windows --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c503d17..4aa0dbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,12 @@ include(TestBigEndian) project(libHPCS) -add_definitions("-std=c89 -Wall") +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) +add_definitions("-std=c89 -Wall -Wextra") + +if (CMAKE_BUILD_TYPE EQUAL "DEBUG") + add_definitions("-ggdb -g3") +endif() test_big_endian(HAVE_BIG_ENDIAN) if (${HAVE_BIG_ENDIAN}) @@ -12,13 +17,20 @@ else() add_definitions(-D_HPCS_LITTLE_ENDIAN) endif() +find_package(ICU 54 REQUIRED COMPONENTS uc io) + set(libHPCS_SRCS libhpcs.c) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${ICU_INCLUDE_DIRS}) + add_library(HPCS SHARED ${libHPCS_SRCS}) +target_link_libraries(HPCS PRIVATE ${ICU_LIBRARIES}) set_target_properties(HPCS - PROPERTIES VERSION 1.0 - SOVERSION 1.0) + PROPERTIES VERSION 2.0 + SOVERSION 2.0) if (${BuildTestTool}) set(libHPCS_test_SRCS diff --git a/FindICU.cmake b/FindICU.cmake new file mode 100644 index 0000000..1646660 --- /dev/null +++ b/FindICU.cmake @@ -0,0 +1,313 @@ +# This module can find the International Components for Unicode (ICU) Library +# +# Requirements: +# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args) +# +# The following variables will be defined for your use: +# - ICU_FOUND : were all of your specified components found (include dependencies)? +# - ICU_INCLUDE_DIRS : ICU include directory +# - ICU_LIBRARIES : ICU libraries +# - ICU_VERSION : complete version of ICU (x.y.z) +# - ICU_MAJOR_VERSION : major version of ICU +# - ICU_MINOR_VERSION : minor version of ICU +# - ICU_PATCH_VERSION : patch version of ICU +# - ICU__FOUND : were found? (FALSE for non specified component if it is not a dependency) +# +# For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways: +# - run cmake with -DICU_ROOT= +# - define an environment variable with the same name before running cmake +# With cmake-gui, before pressing "Configure": +# 1) Press "Add Entry" button +# 2) Add a new entry defined as: +# - Name: ICU_ROOT +# - Type: choose PATH in the selection list +# - Press "..." button and select the root installation of ICU +# +# Example Usage: +# +# 1. Copy this file in the root of your project source directory +# 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt: +# set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) +# 3. Finally call find_package() once, here are some examples to pick from +# +# Require ICU 4.4 or later +# find_package(ICU 4.4 REQUIRED) +# +# if(ICU_FOUND) +# include_directories(${ICU_INCLUDE_DIRS}) +# add_executable(myapp myapp.c) +# target_link_libraries(myapp ${ICU_LIBRARIES}) +# endif(ICU_FOUND) + +#============================================================================= +# Copyright (c) 2011-2013, julp +# +# Distributed under the OSI-approved BSD License +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#============================================================================= + +find_package(PkgConfig QUIET) + +########## Private ########## +if(NOT DEFINED ICU_PUBLIC_VAR_NS) + set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables +endif(NOT DEFINED ICU_PUBLIC_VAR_NS) +if(NOT DEFINED ICU_PRIVATE_VAR_NS) + set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables +endif(NOT DEFINED ICU_PRIVATE_VAR_NS) +if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables +endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + +function(icudebug _VARNAME) + if(${ICU_PUBLIC_VAR_NS}_DEBUG) + if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}") + else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ") + endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + endif(${ICU_PUBLIC_VAR_NS}_DEBUG) +endfunction(icudebug) + +set(${ICU_PRIVATE_VAR_NS}_ROOT "") +if(DEFINED ENV{ICU_ROOT}) + set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}") +endif(DEFINED ENV{ICU_ROOT}) +if (DEFINED ICU_ROOT) + set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}") +endif(DEFINED ICU_ROOT) + +set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES ) +set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES ) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64") + list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64") +endif(CMAKE_SIZEOF_VOID_P EQUAL 8) +list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin") +list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib") + +set(${ICU_PRIVATE_VAR_NS}_COMPONENTS ) +# ... +macro(icu_declare_component _NAME) + list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME}) + set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN}) +endmacro(icu_declare_component) + +icu_declare_component(data icudata) +icu_declare_component(uc icuuc) # Common and Data libraries +icu_declare_component(i18n icui18n icuin) # Internationalization library +icu_declare_component(io icuio ustdio) # Stream and I/O Library +icu_declare_component(le icule) # Layout library +icu_declare_component(lx iculx) # Paragraph Layout library + +########## Public ########## +set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE) +set(${ICU_PUBLIC_VAR_NS}_LIBRARIES ) +set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS ) +set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "") +foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS}) + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro +endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + +# Check components +if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least + set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) +else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}") + endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) +endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + +# Includes +find_path( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + NAMES unicode/utypes.h utypes.h + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES "include" + DOC "Include directories for ICU" +) + +if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + ########## ########## + if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + else() + message(FATAL_ERROR "ICU version header not found") + endif() + + if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3 + # [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0) + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*") + # + # Since version 4.9.1, ICU release version numbering was totaly changed, see: + # - http://site.icu-project.org/download/49 + # - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU + # + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[ + # [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0 + set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if + if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}") + endif() + else() + message(FATAL_ERROR "failed to detect ICU version") + endif() + set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}") + ########## ########## + + # Check dependencies (implies pkg-config) + if(PKG_CONFIG_FOUND) + set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP}) + pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET) + + if(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES}) + string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY}) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY}) + endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY) + endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + endif(PKG_CONFIG_FOUND) + + # Check libraries + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES ) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES ) + foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d") + endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME) + + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Release libraries for ICU" + ) + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Debug libraries for ICU" + ) + + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) + set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE) + else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + else() # both found + set( + ${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + ) + endif() + list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + + # Try to find out compiler flags + find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}) + if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + + # Check find_package arguments + include(FindPackageHandleStandardArgs) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args( + ${ICU_PUBLIC_VAR_NS} + REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION + ) + else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + message(FATAL_ERROR "Could not find ICU include directory") + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + +mark_as_advanced( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + ${ICU_PUBLIC_VAR_NS}_LIBRARIES +) + +# IN (args) +icudebug("FIND_COMPONENTS") +icudebug("FIND_REQUIRED") +icudebug("FIND_QUIETLY") +icudebug("FIND_VERSION") +# OUT +# Found +icudebug("FOUND") +icudebug("UC_FOUND") +icudebug("I18N_FOUND") +icudebug("IO_FOUND") +icudebug("LE_FOUND") +icudebug("LX_FOUND") +icudebug("DATA_FOUND") +# Flags +icudebug("C_FLAGS") +icudebug("CPP_FLAGS") +icudebug("CXX_FLAGS") +icudebug("C_SHARED_FLAGS") +icudebug("CPP_SHARED_FLAGS") +icudebug("CXX_SHARED_FLAGS") +# Linking +icudebug("INCLUDE_DIRS") +icudebug("LIBRARIES") +# Version +icudebug("MAJOR_VERSION") +icudebug("MINOR_VERSION") +icudebug("PATCH_VERSION") +icudebug("VERSION") diff --git a/include/libhpcs.h b/include/libhpcs.h index 07a2858..02f53a0 100644 --- a/include/libhpcs.h +++ b/include/libhpcs.h @@ -29,7 +29,8 @@ enum HPCS_RetCode { HPCS_E_NULLPTR, HPCS_E_CANT_OPEN, HPCS_E_PARSE_ERROR, - HPCS_E_UNKNOWN_TYPE + HPCS_E_UNKNOWN_TYPE, + HPCS_E_NOTIMPL }; struct HPCS_Date { @@ -68,10 +69,23 @@ struct HPCS_MeasuredData { size_t data_count; }; -LIBHPCS_API struct HPCS_MeasuredData* LIBHPCS_CC hpcs_alloc(); -LIBHPCS_API void LIBHPCS_CC hpcs_free(struct HPCS_MeasuredData* const mdata); +struct HPCS_MethodInfoBlock { + char* name; + char* value; +}; + +struct HPCS_MethodInfo { + struct HPCS_MethodInfoBlock* blocks; + size_t count; +}; + +LIBHPCS_API struct HPCS_MeasuredData* LIBHPCS_CC hpcs_alloc_mdata(); +LIBHPCS_API struct HPCS_MethodInfo* LIBHPCS_CC hpcs_alloc_minfo(); +LIBHPCS_API void LIBHPCS_CC hpcs_free_mdata(struct HPCS_MeasuredData* const mdata); +LIBHPCS_API void LIBHPCS_CC hpcs_free_minfo(struct HPCS_MethodInfo* const minfo); LIBHPCS_API char* LIBHPCS_CC hpcs_error_to_string(const enum HPCS_RetCode); -LIBHPCS_API enum HPCS_RetCode LIBHPCS_CC hpcs_read_file(const char* filename, struct HPCS_MeasuredData* mdata); +LIBHPCS_API enum HPCS_RetCode LIBHPCS_CC hpcs_read_mdata(const char* filename, struct HPCS_MeasuredData* mdata); +LIBHPCS_API enum HPCS_RetCode LIBHPCS_CC hpcs_read_minfo(const char* filename, struct HPCS_MethodInfo* minfo); #ifdef __cplusplus } diff --git a/libhpcs.c b/libhpcs.c index 147a989..7dd5226 100644 --- a/libhpcs.c +++ b/libhpcs.c @@ -1,6 +1,12 @@ #include "include/libhpcs.h" #include "libhpcs_p.h" +#ifdef _WIN32 +/* Blank for now */ +#else +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -8,7 +14,7 @@ extern "C" { #include #include -struct HPCS_MeasuredData* hpcs_alloc() +struct HPCS_MeasuredData* hpcs_alloc_mdata() { struct HPCS_MeasuredData* mdata = malloc(sizeof(struct HPCS_MeasuredData)); if (mdata == NULL) @@ -26,6 +32,18 @@ struct HPCS_MeasuredData* hpcs_alloc() return mdata; } +struct HPCS_MethodInfo* hpcs_alloc_minfo() +{ + struct HPCS_MethodInfo* minfo = malloc(sizeof(struct HPCS_MeasuredData)); + if (minfo == NULL) + return NULL; + + minfo->blocks = NULL; + minfo->count = 0; + + return minfo; +} + char* hpcs_error_to_string(const enum HPCS_RetCode err) { char* msg; @@ -58,7 +76,7 @@ char* hpcs_error_to_string(const enum HPCS_RetCode err) } } -void hpcs_free(struct HPCS_MeasuredData* const mdata) +void hpcs_free_mdata(struct HPCS_MeasuredData* const mdata) { if (mdata == NULL) return; @@ -73,7 +91,23 @@ void hpcs_free(struct HPCS_MeasuredData* const mdata) free(mdata); } -enum HPCS_RetCode hpcs_read_file(const char* filename, struct HPCS_MeasuredData* mdata) +void hpcs_free_minfo(struct HPCS_MethodInfo* const minfo) +{ + size_t idx; + + if (minfo == NULL) + return; + + for (idx = 0; idx < minfo->count; idx++) { + free(minfo->blocks[idx].name); + free(minfo->blocks[idx].value); + } + + free(minfo->blocks); + free(minfo); +} + +enum HPCS_RetCode hpcs_read_mdata(const char* filename, struct HPCS_MeasuredData* mdata) { FILE* datafile; enum HPCS_ParseCode pret; @@ -194,6 +228,25 @@ out: return ret; } +enum HPCS_RetCode hpcs_read_minfo(const char* filename, struct HPCS_MethodInfo* minfo) +{ + enum HPCS_ParseCode pret; + HPCS_UFH fh; + + if (minfo == NULL) + return HPCS_E_NULLPTR; + + fh = open_data_file(filename); + if (fh == NULL) + return HPCS_E_CANT_OPEN; + + pret = read_method_info_file(fh, minfo); + if (pret != PARSE_OK) + return HPCS_E_PARSE_ERROR; + + return HPCS_OK; +} + static enum HPCS_ParseCode autodetect_file_type(FILE* datafile, enum HPCS_FileType* file_type, const bool p_means_pressure) { char* type_id; @@ -284,6 +337,34 @@ static void guess_sampling_rate(struct HPCS_MeasuredData* mdata) } } + +static enum HPCS_ParseCode next_native_line(HPCS_UFH fh, HPCS_NChar* line, int32_t length) +{ +#ifdef _WIN32 + return __win32_next_native_line(fh, line, length); +#else + return __unix_next_native_line(fh, line, length); +#endif +} + +static HPCS_UFH open_data_file(const char* filename) +{ +#ifdef _WIN32 + return __win32_open_data_file(filename); +#else + return __unix_open_data_file(filename); +#endif +} + +static enum HPCS_ParseCode parse_native_method_info_line(char** name, char** value, HPCS_NChar* line) +{ +#ifdef _WIN32 + return __win32_parse_native_method_info_line(name, value, line); +#else + return __unix_parse_native_method_info_line(name, value, line); +#endif +} + static enum HPCS_ParseCode read_dad_wavelength(FILE* datafile, struct HPCS_Wavelength* const measured, struct HPCS_Wavelength* const reference) { char* start_idx, *interv_idx, *end_idx, *temp, *str; @@ -496,6 +577,44 @@ static uint8_t month_to_number(const char* month) return 0; } +static enum HPCS_ParseCode read_method_info_file(HPCS_UFH fh, struct HPCS_MethodInfo* minfo) +{ + HPCS_NChar line[64]; + size_t allocated = 0; + + while (next_native_line(fh, line, 64) == PARSE_OK) { + enum HPCS_ParseCode pret; + char* name = NULL; + char* value = NULL; + + pret = parse_native_method_info_line(&name, &value, line); + if (pret != PARSE_OK) + return pret; + + if (minfo->count+1 > allocated) { + size_t to_allocate; + if (allocated == 0) + to_allocate = 256; + else + to_allocate = allocated * 2; + + struct HPCS_MethodInfoBlock* new_blocks = realloc(minfo->blocks, to_allocate * sizeof(struct HPCS_MethodInfoBlock)); + if (new_blocks == NULL) + return PARSE_E_NO_MEM; + else { + minfo->blocks = new_blocks; + allocated = to_allocate; + } + } + + minfo->blocks[minfo->count].name = name; + minfo->blocks[minfo->count].value = value; + minfo->count++; + } + + return PARSE_OK; +} + static enum HPCS_ParseCode read_signal(FILE* datafile, struct HPCS_TVPair** pairs, size_t* pairs_count, const HPCS_step step, const double sampling_rate) { @@ -665,6 +784,121 @@ static enum HPCS_ParseCode read_string_at_offset(FILE* datafile, const HPCS_offs return PARSE_OK; } +/** Platform-specific functions */ + +#ifdef _WIN32 +static enum HPCS_ParseCode __win32_next_native_line(HPCS_UFH fh, HPCS_NChar* line, int32_t length) +{ + /* Not implemented */ + return PARSE_E_CANT_READ; +} + +static HPCS_UFH __win32_open_data_file(const char* filename) +{ + /* Not implemented */ + return NULL; +} + +static enum HPCS_ParseCode __win32_parse_native_method_info_line(char** name, char** value, UChar* line) +{ + /* Not implemented */ + return PARSE_E_CANT_READ; +} +#else +static void __unix_hpcs_initialize() +{ + /* Initialize all Unicode strings */ + __ICU_INIT_STRING(EQUALITY_SIGN, "\\x3d"); + __ICU_INIT_STRING(CR_LF, "\\x0d\\x0a"); +} + +static void __unix_hpcs_destroy() +{ + free(EQUALITY_SIGN); + free(CR_LF); +} + +static enum HPCS_ParseCode __unix_icu_to_utf8(char** target, const UChar* s) +{ + UChar32 c; + UErrorCode uec = U_ZERO_ERROR; + int32_t utf8_size = 0; + int32_t idx = 0; +#ifndef NDEBUG + int32_t wrt_size; +#define pWrt_size &wrt_size +#else +#define pWrt_size NULL +#endif + + do { + U16_NEXT(s, idx, -1, c); + utf8_size += U8_LENGTH(c); + } while (c != 0); + + *target = malloc(utf8_size); + if (*target == NULL) + return PARSE_E_NO_MEM; + + u_strToUTF8(*target, utf8_size, pWrt_size, s, -1, &uec); + + PR_DEBUGF("Memory allocated: %d, Units written: %d, UEC: %x\n", utf8_size, wrt_size, uec); + PR_DEBUGF("Resulting string: %s\n", *target); + + if (U_FAILURE(uec)) { + PR_DEBUGF("ICU error: %s\n", u_errorName(uec)); + free(*target); + return PARSE_E_CANT_READ; + } + + return PARSE_OK; +} + +static enum HPCS_ParseCode __unix_next_native_line(UFILE* fh, UChar* line, int32_t length) +{ + if (u_fgets(line, length, fh) == NULL) + return PARSE_E_CANT_READ; + + return PARSE_OK; +} + +static UFILE* __unix_open_data_file(const char* filename) +{ + return u_fopen(filename, "r", "en_US", "UTF-16"); +} + +static enum HPCS_ParseCode __unix_parse_native_method_info_line(char** name, char** value, UChar* line) +{ + UChar* u_name; + UChar* u_value; + UChar* saveptr; + UChar* newline; + enum HPCS_ParseCode ret; + + u_name = u_strtok_r(line, EQUALITY_SIGN, &saveptr); + if (u_name == NULL) + return PARSE_E_NOT_FOUND; + u_value = u_strtok_r(NULL, EQUALITY_SIGN, &saveptr); + if (u_value == NULL) { + free(u_name); + return PARSE_E_NOT_FOUND; + } + /* Remove the trailing \n from value if present */ + newline = u_strrstr(u_value, CR_LF); + if (newline != NULL) + *newline = (UChar)0; + + ret = __unix_icu_to_utf8(name, u_name); + if (ret != PARSE_OK) + return ret; + ret = __unix_icu_to_utf8(value, u_value); + if (ret != PARSE_OK) + return ret; + + return PARSE_OK; +} +#endif + #ifdef __cplusplus } #endif diff --git a/libhpcs_p.h b/libhpcs_p.h index f0b4b6b..43f96d1 100644 --- a/libhpcs_p.h +++ b/libhpcs_p.h @@ -11,6 +11,15 @@ typedef int bool; #endif #include +#ifdef _WIN32 +#error "Not defined yet!" +#else +#include +#include +#define HPCS_NChar UChar +#define HPCS_UFH UFILE* +#endif + enum HPCS_DataCheckCode { DCHECK_GOT_MARKER, DCHECK_EOF, @@ -107,20 +116,52 @@ const char* HPCS_E_PARSE_ERROR_STR = "Cannot parse the specified file, it might const char* HPCS_E_UNKNOWN_TYPE_STR = "The specified file contains an unknown type of measurement."; const char* HPCS_E__UNKNOWN_EC_STR = "Unknown error code."; +#ifdef _WIN32 +#error "Not implemented yet" +#else +/* ICU strings declarations */ +UChar* EQUALITY_SIGN; +UChar* CR_LF; +#endif + static enum HPCS_ParseCode autodetect_file_type(FILE* datafile, enum HPCS_FileType* file_type, const bool p_means_pressure); static enum HPCS_DataCheckCode check_for_marker(const char* segment, size_t* const next_marker_idx); static HPCS_step guess_current_step(const struct HPCS_MeasuredData* mdata); static HPCS_step guess_elec_sigstep(const struct HPCS_MeasuredData *mdata); static bool guess_p_meaning(const struct HPCS_MeasuredData* mdata); static void guess_sampling_rate(struct HPCS_MeasuredData* mdata); +static enum HPCS_ParseCode next_native_line(HPCS_UFH fh, HPCS_NChar* line, int32_t length); +static HPCS_UFH open_data_file(const char* filename); +static enum HPCS_ParseCode parse_native_method_info_line(char** name, char** value, HPCS_NChar* line); static enum HPCS_ParseCode read_dad_wavelength(FILE* datafile, struct HPCS_Wavelength* const measured, struct HPCS_Wavelength* const reference); static uint8_t month_to_number(const char* month); static enum HPCS_ParseCode read_date(FILE* datafile, struct HPCS_Date* date); +static enum HPCS_ParseCode read_method_info_file(HPCS_UFH fh, struct HPCS_MethodInfo* minfo); static enum HPCS_ParseCode read_signal(FILE* datafile, struct HPCS_TVPair** pairs, size_t* pairs_count, const HPCS_step step, const double sampling_rate); static enum HPCS_ParseCode read_sampling_rate(FILE* datafile, double* sampling_rate); static enum HPCS_ParseCode read_string_at_offset(FILE* datafile, const HPCS_offset, char** const result); +/** Platform-specific functions */ +#ifdef _WIN32 +static enum HPCS_ParseCode __win32_next_native_line(HPCS_UFH, HPCS_NChar* line, int32_t length); +static HPCS_UFH __win32_open_data_file(const char* filename); +static enum HPCS_ParseCode __win32_parse_native_method_info_line(char** name, char** value, HPCS_NChar* line); +#else +static void __attribute((constructor)) __unix_hpcs_initialize(); +static void __attribute((destructor)) __unix_hpcs_destroy(); +static enum HPCS_ParseCode __unix_icu_to_utf8(char** target, const UChar* s); +static HPCS_UFH __unix_open_data_file(const char* filename); +static enum HPCS_ParseCode __unix_next_native_line(UFILE* fh, UChar* line, int32_t length); +static enum HPCS_ParseCode __unix_parse_native_method_info_line(char** name, char** value, UChar* line); + +#define __ICU_INIT_STRING(dst, s) do { \ + UChar temp[64]; \ + int32_t length = u_unescape(s, temp, sizeof(temp)); \ + dst = calloc(length + 1, sizeof(UChar)); \ + u_strcpy(dst, temp); \ +} while(0) +#endif #ifdef _HPCS_LITTLE_ENDIAN #define be_to_cpu(bytes) reverse_endianness((char*)bytes, sizeof(bytes)); @@ -148,7 +189,7 @@ void reverse_endianness(char* bytes, size_t sz) { #define PR_DEBUGF(fmt, ...) fprintf(stderr, "[%s()] "fmt, __func__, __VA_ARGS__) #define PR_DEBUG(msg) fprintf(stderr, "[%s()] "msg, __func__) #else - #define PR_DEBUGF(fmt, msg) ((void)0) + #define PR_DEBUGF(fmt, ...) ((void)0) #define PR_DEBUG(msg) ((void)0) #endif diff --git a/test_tool.c b/test_tool.c index fb86a5c..1447961 100644 --- a/test_tool.c +++ b/test_tool.c @@ -1,26 +1,22 @@ #include #include #include +#include #include "include/libhpcs.h" -int main(int argc, char** argv) +int read_data(const char* path) { struct HPCS_MeasuredData* mdata; enum HPCS_RetCode hret; size_t di; - - if (argc < 2) { - printf("No filename specified\n"); - return EXIT_FAILURE; - } - mdata = hpcs_alloc(); + mdata = hpcs_alloc_mdata(); if (mdata == NULL) { printf("Out of memory\n"); return EXIT_FAILURE; } - hret = hpcs_read_file(argv[1], mdata); + hret = hpcs_read_mdata(path, mdata); if (hret != HPCS_OK) { printf("Cannot parse file: %s\n", hpcs_error_to_string(hret)); return EXIT_FAILURE; @@ -29,7 +25,56 @@ int main(int argc, char** argv) for (di = 0; di < mdata->data_count; di++) printf("Time: %.17lg, Value: %.17lg\n", mdata->data[di].time, mdata->data[di].value); - hpcs_free(mdata); + hpcs_free_mdata(mdata); return EXIT_SUCCESS; } + +int read_info(const char* path) +{ + struct HPCS_MethodInfo* minfo; + enum HPCS_RetCode hret; + size_t di; + + minfo = hpcs_alloc_minfo(); + if (minfo == NULL) { + printf("Out of memory\n"); + return EXIT_FAILURE; + } + + hret = hpcs_read_minfo(path, minfo); + if (hret != HPCS_OK) { + printf("Cannot parse file: %s\n", hpcs_error_to_string(hret)); + return EXIT_FAILURE; + } + + for (di = 0; di < minfo->count; di++) { + struct HPCS_MethodInfoBlock* b = &minfo->blocks[di]; + printf("Name: %s = Value: %s\n", b->name, b->value); + } + + hpcs_free_minfo(minfo); + + return EXIT_SUCCESS; +} + +int main(int argc, char** argv) +{ + if (argc < 3) { + printf("Not enough arguments\n"); + printf("Usage: test_tool MODE FILE\n"); + printf("MODE: d - read data file\n" + " i - method info\n" + "FILE: path\n"); + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "d") == 0) + return read_data(argv[2]); + else if (strcmp(argv[1], "i") == 0) + return read_info(argv[2]); + else { + printf("Invalid mode argument\n"); + return EXIT_FAILURE; + } +}