From: Timo Röhling Date: Thu, 12 Feb 2026 16:40:11 +0000 (+0100) Subject: New upstream version 0.9.4 X-Git-Tag: archive/raspbian/0.9.4-1+rpi1^2~2^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=3ccd96987df6c0caf70aa0d72d19d865ca1743eb;p=libfyaml.git New upstream version 0.9.4 --- diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml new file mode 100644 index 0000000..8cd5a6f --- /dev/null +++ b/.github/workflows/cmake.yaml @@ -0,0 +1,200 @@ +name: CMake CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + # =========================================== + # Main build matrix - full test suite + # =========================================== + build: + name: ${{ matrix.os }} - ${{ matrix.compiler }} - ${{ matrix.build_type }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + compiler: [gcc, clang, msvc] + build_type: [Debug, Release] + exclude: &exclude-list + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + - os: macos-latest + compiler: msvc + include: &include-list + - os: ubuntu-latest + compiler: gcc + cc: gcc + cxx: g++ + - os: ubuntu-latest + compiler: clang + cc: clang-18 + cxx: clang++-18 + - os: macos-latest + compiler: gcc + cc: gcc + cxx: g++ + - os: macos-latest + compiler: clang + cc: clang + cxx: clang++ + - os: windows-latest + compiler: msvc + cc: cl + cxx: cl + + steps: + - &checkout { + uses: actions/checkout@v4 + } + + - &setup-msvc { + name: Setup MSBuild, + if: runner.os == 'Windows', + uses: microsoft/setup-msbuild@v2, + } + + - &install-ubuntu { + name: "Install dependencies (Ubuntu)", + if: runner.os == 'Linux', + run: "sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y cmake ninja-build pkg-config check libyaml-dev libltdl-dev ${{ matrix.cc }} ${{ matrix.cxx }}" + } + + - &install-macos { + name: "Install dependencies (macOS)", + if: runner.os == 'macOS', + run: "brew update && brew install cmake ninja pkg-config check libyaml gcc@13" + } + + - &install-windows { + name: "Install dependencies (Windows)", + if: runner.os == 'Windows', + run: "choco install cmake ninja pkgconfiglite", + } + + - &set-env { + name: "Set compiler environment (Unix)", + if: runner.os != 'Windows', + run: "echo \"CC=${{ matrix.cc }}\" >> $GITHUB_ENV && echo \"CXX=${{ matrix.cxx }}\" >> $GITHUB_ENV" + } + + - &configure-unix { + name: "Configure (Unix)", + if: runner.os != 'Windows', + run: "cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}" + } + + - &configure-windows { + name: "Configure (Windows)", + if: runner.os == 'Windows', + run: "cmake -B build -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}" + } + + - &build { + name: Build, + run: "cmake --build build --config ${{ matrix.build_type }}" + } + + - &test-unix { + name: Test, + if: runner.os != 'Windows', + working-directory: build, + run: "ctest --output-on-failure", + } + + - &upload-logs { + name: "Upload test logs on failure", + if: failure(), + uses: actions/upload-artifact@v4, + with: { + name: "test-logs-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.build_type }}", + path: "build/Testing/Temporary/\nbuild/**/*.log" + } + } + + # =========================================== + # ASAN builds - Debug only + # =========================================== + asan: + name: ASAN - ${{ matrix.os }} - ${{ matrix.compiler }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + compiler: [gcc, clang] + build_type: [Debug] + exclude: + - os: macos-latest + compiler: gcc + include: + - os: ubuntu-latest + compiler: gcc + cc: gcc + cxx: g++ + - os: ubuntu-latest + compiler: clang + cc: clang-18 + cxx: clang++-18 + - os: macos-latest + compiler: clang + cc: clang + cxx: clang++ + + steps: + - *checkout + - *setup-msvc + - *install-ubuntu + - *install-macos + - *install-windows + - *set-env + - &configure-unix-asan { + name: "Configure ASAN (Unix)", + if: runner.os != 'Windows', + run: "cmake -B build -G Ninja -DENABLE_ASAN=On -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}" + } + - *build + - *test-unix + - *upload-logs + + # =========================================== + # Smoke tests - pinned latest versions + # =========================================== + smoke-pinned: + name: Smoke - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, ubuntu-22.04, macos-14, windows-2022, ubuntu-24.04-arm] + build_type: [Release] + + steps: + - *checkout + - *setup-msvc + - *install-ubuntu + - *install-macos + - *install-windows + - *set-env + - &configure-unix-smoke { + name: "Configure smoke (Unix)", + if: runner.os != 'Windows', + run: "cmake -B build -G Ninja -DBUILD_TESTING=Off -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}", + } + - &configure-windows-smoke { + name: "Configure smoke (Windows)", + if: runner.os == 'Windows', + run: "cmake -B build -A x64 -DBUILD_TESTING=Off -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}" + } + - *build + - *upload-logs diff --git a/.libtool-version b/.libtool-version index 7e093fb..3e95e1d 100644 --- a/.libtool-version +++ b/.libtool-version @@ -1 +1 @@ -2:0:0 +3:0:1 diff --git a/.tarball-version b/.tarball-version index 965065d..a602fc9 100644 --- a/.tarball-version +++ b/.tarball-version @@ -1 +1 @@ -0.9.3 +0.9.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index ae5157b..473f220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,108 @@ All notable changes to libfyaml will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.9.4] - 2026-02-03 + +### Major: Full Windows Support + +This release adds **full native Windows support**. libfyaml now builds and runs natively on Windows with MSVC, clang-cl, and Clang compilers. + +**Native Windows builds:** +- Full support for building on Windows using MSVC, clang-cl, or GCC +- Works with Visual Studio, VSCode, and other Windows development tools +- All tests pass on Windows + +**Cross-compilation from Linux:** +- Support for msvc-wine to install MSVC redistributables on Linux +- Compile using `cl.exe` via Wine +- Compile using `clang-cl` without Wine +- Compile using Clang cross-compilation triplet + +### Major: Comment Support Now Stable + +Comment parsing and emission has been moved out of experimental status. Comments can now be reliably preserved and manipulated through the API. + +### Added + +- `fy_node_set_style()`: Set the style of a node (block, flow, plain, etc.) - Fixes #78 +- `fy_token_set_comment()`: Attach comments to tokens programmatically +- `fy_event_to_string()`: Convert events to string representation +- `fy_diag_get_collect_errors()`: Query if error collection is enabled +- `fy_atom_lines_containing()`: Get lines containing an atom (for diagnostics) +- `fy_memstream`: Portable `open_memstream` alternative for cross-platform support +- CMake-based CI workflow with improved matrix coverage +- Emscripten platform detection for `endian.h` + +### Changed + +- libclang now defaults to OFF (will be enabled when reflection features are ready) +- Document start token is now preserved (may contain comments) +- Walk methods now handle error paths more systematically +- Removed non-existent experimental function declarations from `libfyaml.h` + +### Fixed + +- **#193**: Token creation now properly clears memory to avoid undefined behavior on invalid input +- **#186**: Reference loop nesting now respected when checking link validity +- **#185**: Fixed crash when setting document root to NULL; also fixed input size clamping for corrupted input +- **#184**: Walk memory leak fix with improved debugging infrastructure +- **#183, #191**: Error out early on `FYECF_EXTENDED_CFG` with helper emit methods (prevents crash) +- **#182**: Walk expression unref bug fix with debug infrastructure +- **#181**: Walk double-free on node delete +- **#178, #177**: Walk methods now handle error paths systematically (recursive alias resolution) +- **#176**: Off-by-one error in `fy_accel_grow` +- **#175**: Parser crash on corrupted UTF-8 at end of file +- **#174**: Superfluous document end marker with explicit version/tag directives +- **#173, #172**: Depth limit for node copy (prevents stack overflow under fuzzing) +- **#143**: Document root now correctly marked as attached +- Emit state now properly reset at end of document (fixes multi-document stream markers) +- Flow quoting error on ANY style (test was backwards) +- Empty file `fdopen` issue on some platforms +- Empty stream `realloc(0)` undefined behavior +- Removed jarring notice when alias is declared multiple times (valid YAML) + +### Platform Support + +**Supported platforms**: Linux, macOS, FreeBSD, OpenBSD, NetBSD, and **Windows**. + +**Windows-specific:** +- Full native MSVC support (32-bit and 64-bit) +- clang-cl and Clang cross-compilation support +- msvc-wine support for Linux-based Windows cross-compilation +- Proper CRLF (DOS line ending) handling +- Fixed 32-bit MSVC intrinsics (`_BitScanForward64`, `_BitScanReverse64`, `__popcnt64`) + +**Portability fixes:** +- Fixed void pointer arithmetic (GCC extension) for strict C compliance +- Fixed GCC ternary operator extension (`x ? : y` -> `x ? x : y`) +- Fixed `\e` escape sequence (GCC/clang extension) -> `\x1b` +- Fixed enum comparison warnings across platforms +- Align mremap initial size to page boundary (fixes BSD crashes) + +**macOS:** +- Fixed ASAN support (requires `-fsanitize=address` at link time) +- Added extra ASAN flags for Apple's clang (alloca poisoning disabled) + +**CI/Build:** +- New CMake-based GitHub Actions workflow +- Improved build matrix coverage +- Fixed distcheck breakage + +### Internal + +- Walk expression debug infrastructure for easier debugging +- Portable `fy_memstream` wrapper for `open_memstream` +- Use `fy_align_alloc/free` wrappers in allocator +- Fixed allocator `get_caps` return type (enum, not int) +- Atomic counter function instead of macro +- General warning cleanup pass + +### Statistics + +- 58 commits since v0.9.3 +- 18 bug fix issues closed +- Full Windows platform support added + ## [0.9.3] - 2026-01-14 ### Added @@ -174,6 +276,7 @@ Jose Luis Blanco-Claraco, Andrey Somov, Orange_233, Martin Diehl Initial public release with comprehensive YAML 1.2 support. +[0.9.4]: https://github.com/pantoniou/libfyaml/compare/v0.9.3...v0.9.4 [0.9.3]: https://github.com/pantoniou/libfyaml/compare/v0.9.2...v0.9.3 [0.9.2]: https://github.com/pantoniou/libfyaml/compare/v0.9.1...v0.9.2 [0.9.1]: https://github.com/pantoniou/libfyaml/compare/v0.9...v0.9.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 135e3d2..dbcca1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.14) if (POLICY CMP0048) cmake_policy(SET CMP0048 NEW) # Allow project(xxx VERSION a.b.c) @@ -43,6 +43,10 @@ else() set(VERSION_EXTRA "") endif() +# Optional MSVC cross-compilation support via msvc-wine +# Usage: cmake -DMSVC_WINE_DIR=/path/to/msvc [-DMSVC_WINE_COMPILER=cl|clang-cl|clang] .. +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/MSVCCrossCompile.cmake OPTIONAL) + project(libfyaml LANGUAGES C ASM VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) # Must use GNUInstallDirs to install libraries into correct locations on all platforms @@ -85,7 +89,7 @@ macro(add_libclang_to_target target_name link_scope) endif() # Add libclang libraries - target_link_libraries(${target_name} ${link_scope} ${LIBCLANG_LIBS}) + target_link_libraries(${target_name} ${link_scope} ${LIBCLANG_LIBS}) endif() endmacro() @@ -97,7 +101,7 @@ option(ENABLE_ASAN "Enable ASAN support" OFF) option(ENABLE_NETWORK "Enable tests requiring network access" ON) option(ENABLE_DEVMODE "Enable development mode only debugging" OFF) option(BUILD_TESTING "Build tests" ON) -option(ENABLE_LIBCLANG "Enable libclang support for reflection" ON) +option(ENABLE_LIBCLANG "Enable libclang support for reflection" OFF) # Path to LLVM installation (optional, overrides automatic detection) set(LLVM_ROOT "" CACHE PATH "Path to LLVM installation directory (e.g., /usr/lib/llvm-15)") @@ -140,11 +144,21 @@ else() endif() # Check for required dependencies -set(CMAKE_THREAD_PREFER_PTHREAD TRUE) -set(THREADS_PREFER_PTHREAD_FLAG TRUE) -find_package(Threads REQUIRED) -if(NOT CMAKE_USE_PTHREADS_INIT) - message(FATAL_ERROR "Missing required pthread support") +if(NOT WIN32) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads REQUIRED) + if(NOT CMAKE_USE_PTHREADS_INIT) + message(FATAL_ERROR "Missing required pthread support") + endif() +else() + # Windows uses native threading APIs + set(CMAKE_USE_WIN32_THREADS_INIT TRUE) +endif() + +# detect if we're on clang-cl +if(WIN32 AND MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(CLANG_CL TRUE) endif() # Check for headers and functions @@ -162,6 +176,11 @@ check_c_source_compiles(" int main() { return 0; } " HAVE_DECL_ENVIRON) +# find PkgConfig +if(NOT CMAKE_CROSSCOMPILING) + find_package(PkgConfig) +endif() + # Check for optional dependencies # libyaml detection - try CMake config first, then pkg-config @@ -173,7 +192,6 @@ if(yaml_FOUND) set(LIBYAML_LIBRARIES yaml) message(STATUS "Found libyaml via CMake config") else() - find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(LIBYAML yaml-0.1) if(LIBYAML_FOUND) @@ -185,10 +203,10 @@ else() endif() endif() -# check framework detection - try CMake config first, then pkg-config +# check framework detection - try CMake config first, then pkg-config, then FetchContent set(HAVE_CHECK 0) set(HAVE_COMPATIBLE_CHECK 0) -if(BUILD_TESTING) +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) find_package(check QUIET CONFIG) if(check_FOUND) set(HAVE_CHECK 1) @@ -223,7 +241,6 @@ if(BUILD_TESTING) endif() else() # Fall back to pkg-config - find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(CHECK check) if(CHECK_FOUND) @@ -256,6 +273,28 @@ if(BUILD_TESTING) endif() endif() endif() + + # Fall back to FetchContent if check not found (useful on Windows) + if(NOT HAVE_CHECK) + include(FetchContent) + message(STATUS "check framework not found, fetching via FetchContent...") + FetchContent_Declare( + check + GIT_REPOSITORY https://github.com/libcheck/check.git + GIT_TAG 0.15.2 + ) + # Disable check's own tests and docs + set(CHECK_ENABLE_TESTS OFF CACHE BOOL "" FORCE) + set(CHECK_ENABLE_GCOV OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(check) + + set(HAVE_CHECK 1) + set(CHECK_LIBRARIES check) + set(CHECK_INCLUDE_DIRS "${check_SOURCE_DIR}/src" "${check_BINARY_DIR}" "${check_BINARY_DIR}/src") + set(HAVE_SRUNNER_SET_TAP 1) # 0.15.2 has srunner_set_tap + set(HAVE_COMPATIBLE_CHECK 1) + message(STATUS "Found check framework via FetchContent") + endif() endif() # Check for libclang (for reflection support) @@ -390,6 +429,14 @@ else() endif() # ENABLE_LIBCLANG +# check for bash +find_program(BASH_EXECUTABLE bash) +if(BASH_EXECUTABLE) + set(HAVE_BASH 1) +else() + set(HAVE_BASH 0) +endif() + # Check for tools find_program(GIT_EXECUTABLE git) if(GIT_EXECUTABLE) @@ -412,7 +459,7 @@ else() set(HAVE_DOCKER 0) endif() -# Test suite URLs +# Test suite URLs and commits if(NOT TESTSUITEURL) set(TESTSUITEURL "https://github.com/yaml/yaml-test-suite") endif() @@ -426,6 +473,23 @@ if(NOT JSONTESTSUITECHECKOUT) set(JSONTESTSUITECHECKOUT "d64aefb55228d9584d3e5b2433f720ea8fd00c82") endif() +# FetchContent for test suites (downloaded at configure time when network is available) +include(FetchContent) +if(ENABLE_NETWORK) + FetchContent_Declare( + yaml_test_suite + GIT_REPOSITORY ${TESTSUITEURL} + GIT_TAG ${TESTSUITECHECKOUT} + GIT_SHALLOW TRUE + ) + FetchContent_Declare( + json_test_suite + GIT_REPOSITORY ${JSONTESTSUITEURL} + GIT_TAG ${JSONTESTSUITECHECKOUT} + GIT_SHALLOW FALSE + ) +endif() + # SIMD capability detection set(TARGET_HAS_SSE2 FALSE) set(TARGET_HAS_SSE41 FALSE) @@ -433,7 +497,11 @@ set(TARGET_HAS_AVX2 FALSE) set(TARGET_HAS_AVX512 FALSE) set(TARGET_HAS_NEON FALSE) -if(TARGET_CPU_ANY_X86 AND NOT ENABLE_PORTABLE_TARGET) +# Disable SIMD assembly on Windows (uses Unix assembly syntax) +# Use portable implementations instead +if(WIN32) + message(STATUS "Windows detected: using portable SIMD implementations") +elseif(TARGET_CPU_ANY_X86 AND NOT ENABLE_PORTABLE_TARGET) check_c_source_compiles(" #include int main() { __m128i a = _mm_setzero_si128(); return 0; } @@ -496,9 +564,30 @@ if(ENABLE_ASAN) endif() endif() +set(ASAN_C_FLAGS) +if(HAVE_ASAN) + set(ASAN_C_FLAGS "-fsanitize=address" "-fno-omit-frame-pointer") +endif() + # Build common compiler flags for all targets set(COMMON_C_FLAGS) -set(COMMON_C_DEFINITIONS _GNU_SOURCE) +if(WIN32) + set(COMMON_C_DEFINITIONS WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS) +else() + set(COMMON_C_DEFINITIONS _GNU_SOURCE) +endif() + +# MSVC requires C11 for stdatomic.h +if(MSVC) + # Use /std:c17 for better C11/C17 support in MSVC + set(CMAKE_C_STANDARD 17) + set(CMAKE_C_STANDARD_REQUIRED ON) + + # Enable experimental C11 atomics in MSVC (only) + if(NOT CLANG_CL) + add_compile_options(/experimental:c11atomics) + endif() +endif() if(NOT MSVC) list(APPEND COMMON_C_FLAGS -Wall -Wsign-compare) @@ -557,8 +646,10 @@ endif() # the options for the libs set(LIB_OPTS "") -if(BUILD_SHARED_LIBS) - list(APPEND LIB_OPTS "-fPIC") +if(NOT MSVC AND NOT WIN32) + if(BUILD_SHARED_LIBS) + list(APPEND LIB_OPTS "-fPIC") + endif() endif() # Network tests @@ -615,6 +706,7 @@ set(UTILHDRS src/util/fy-align.h src/util/fy-bit64.h src/util/fy-vlsize.h + src/util/fy-win32.h ) set(LIBSRCS @@ -802,6 +894,26 @@ foreach(blake3_target ${BLAKE3_TARGETS}) target_sources(fyaml PRIVATE $) endforeach() +# On Windows, use a .def file to export only the public API symbols +# This is needed because libfyaml uses GCC-style suffix attributes for FY_EXPORT +# which are incompatible with MSVC's __declspec prefix requirement +if(WIN32 AND BUILD_SHARED_LIBS) + # Generate the .def file from the public header during build + set(GENERATED_DEF_FILE "${CMAKE_CURRENT_BINARY_DIR}/fyaml.def") + add_custom_command( + OUTPUT ${GENERATED_DEF_FILE} + COMMAND ${CMAKE_COMMAND} + -DINPUT_HEADER=${CMAKE_CURRENT_SOURCE_DIR}/include/libfyaml.h + -DOUTPUT_DEF=${GENERATED_DEF_FILE} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate-def.cmake + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/include/libfyaml.h + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate-def.cmake + COMMENT "Generating fyaml.def from libfyaml.h" + ) + target_sources(fyaml PRIVATE "${GENERATED_DEF_FILE}") +endif() + add_library(libfyaml::libfyaml ALIAS fyaml) # Always build a static library for internal tools (they need access to internal APIs) @@ -839,10 +951,12 @@ target_include_directories(fyaml_static ${CMAKE_CURRENT_BINARY_DIR} ) -target_link_libraries(fyaml_static - PUBLIC - Threads::Threads -) +if(NOT WIN32) + target_link_libraries(fyaml_static + PUBLIC + Threads::Threads + ) +endif() # Add libclang support if available add_libclang_to_target(fyaml_static INTERFACE) @@ -875,7 +989,7 @@ if(NOT MSVC) endif() if(HAVE_ASAN) - target_compile_options(fyaml_static PRIVATE -fsanitize=address -fno-omit-frame-pointer) + target_compile_options(fyaml_static PRIVATE ${ASAN_C_FLAGS}) # Don't add link options here - executables will link dynamically against libasan endif() endif() @@ -899,24 +1013,20 @@ target_include_directories(fyaml ${CMAKE_CURRENT_SOURCE_DIR}/src/thread ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator ${CMAKE_CURRENT_SOURCE_DIR}/src/blake3 - ${CMAKE_CURRENT_SOURCE_DIR}/src/generic - ${CMAKE_CURRENT_SOURCE_DIR}/src/reflection ) -target_link_libraries(fyaml - PUBLIC - Threads::Threads -) +if(NOT WIN32) + target_link_libraries(fyaml + PUBLIC + Threads::Threads + ) +else() + # Windows needs no extra libraries for threading (uses kernel32) +endif() # Add libclang support if available add_libclang_to_target(fyaml PRIVATE) -# Generate config.h -configure_file( - ${CMAKE_SOURCE_DIR}/cmake/config.h.in - ${CMAKE_BINARY_DIR}/config.h -) - # Generate config.h configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in" @@ -933,34 +1043,55 @@ target_compile_definitions(fyaml PRIVATE target_compile_definitions(fyaml PRIVATE ${COMMON_C_DEFINITIONS}) target_compile_options(fyaml PRIVATE ${COMMON_C_FLAGS}) -# Shared options between GCC and CLANG -if(NOT MSVC) - target_compile_options(fyaml PRIVATE ${LIB_OPTS}) +# Shared options between GCC and CLANG (skip for Windows cross-compilation) +if(NOT MSVC AND NOT WIN32) target_compile_options(fyaml PRIVATE -fvisibility=hidden) + if(BUILD_SHARED_LIBS) + target_compile_options(fyaml PRIVATE -fPIC) + endif() + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(fyaml PRIVATE -O2) endif() if(HAVE_ASAN) - target_compile_options(fyaml PRIVATE -fsanitize=address -fno-omit-frame-pointer) + target_compile_options(fyaml PRIVATE ${ASAN_C_FLAGS} -fno-omit-frame-pointer) # Use INTERFACE to propagate link flags to executables linking against this library - target_link_options(fyaml INTERFACE -fsanitize=address) + target_link_options(fyaml PRIVATE ${ASAN_C_FLAGS} INTERFACE ${ASAN_C_FLAGS}) endif() endif() +# on windows we need a fall-back getopt +if(WIN32) + set(GETOPT_SOURCES src/getopt/getopt.c) + set(GETOPT_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/src/getopt") +else() + set(GETOPT_SOURCES) + set(GETOPT_INCLUDES) +endif() + # Build fy-tool -add_executable(fy-tool +set(FY_TOOL_SOURCES src/tool/fy-tool.c src/tool/fy-tool-util.c src/tool/fy-tool-dump.c - src/valgrind/fy-valgrind.h + ${GETOPT_SOURCES} ) -target_include_directories(fy-tool PRIVATE +set(FY_TOOL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind + ${GETOPT_INCLUDES} ) +if(WIN32) + list(APPEND FY_TOOL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src/util") +endif() + +add_executable(fy-tool ${FY_TOOL_SOURCES}) +target_include_directories(fy-tool PRIVATE ${FY_TOOL_INCLUDE_DIRS}) +target_compile_definitions(fy-tool PRIVATE ${COMMON_C_DEFINITIONS}) + target_link_libraries(fy-tool PRIVATE fyaml) if(ENABLE_STATIC_TOOLS AND NOT MSVC) @@ -972,28 +1103,29 @@ endif() # System libraries are dynamically linked (works with or without ASAN) # Skip when cross-compiling since these tools cannot be executed on build host -if(NOT CROSS_COMPILING) +set(INTERNAL_TOOL_INCLUDES + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind + ${CMAKE_CURRENT_SOURCE_DIR}/src/lib + ${CMAKE_CURRENT_SOURCE_DIR}/src/util + ${CMAKE_CURRENT_SOURCE_DIR}/src/xxhash + ${CMAKE_CURRENT_SOURCE_DIR}/src/thread + ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator + ${CMAKE_CURRENT_BINARY_DIR} + ${LIBYAML_INCLUDE_DIRS} + ${GETOPT_INCLUDES} +) # libfyaml-parser requires libyaml if(HAVE_LIBYAML) add_executable(libfyaml-parser src/internal/libfyaml-parser.c - src/valgrind/fy-valgrind.h + ${GETOPT_SOURCES} ) target_compile_definitions(libfyaml-parser PRIVATE ${COMMON_C_DEFINITIONS}) target_compile_options(libfyaml-parser PRIVATE ${COMMON_C_FLAGS}) - target_include_directories(libfyaml-parser PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind - ${CMAKE_CURRENT_SOURCE_DIR}/src/lib - ${CMAKE_CURRENT_SOURCE_DIR}/src/util - ${CMAKE_CURRENT_SOURCE_DIR}/src/xxhash - ${CMAKE_CURRENT_SOURCE_DIR}/src/thread - ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator - ${CMAKE_CURRENT_BINARY_DIR} - ${LIBYAML_INCLUDE_DIRS} - ) + target_include_directories(libfyaml-parser PRIVATE ${INTERNAL_TOOL_INCLUDES}) target_link_directories(libfyaml-parser PRIVATE ${LIBYAML_LIBRARY_DIRS}) if(NOT MSVC) # Embed libfyaml code statically, allow dynamic linking for system libraries @@ -1011,7 +1143,7 @@ if(HAVE_LIBYAML) ) endif() if(HAVE_ASAN) - target_link_options(libfyaml-parser PRIVATE -fsanitize=address) + target_link_options(libfyaml-parser PRIVATE ${ASAN_C_FLAGS}) endif() else() target_link_libraries(libfyaml-parser PRIVATE fyaml_static ${LIBYAML_LIBRARIES}) @@ -1024,17 +1156,12 @@ endif() add_executable(fy-thread src/internal/fy-thread.c - src/valgrind/fy-valgrind.h -) -target_include_directories(fy-thread PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind - ${CMAKE_CURRENT_SOURCE_DIR}/src/lib - ${CMAKE_CURRENT_SOURCE_DIR}/src/util - ${CMAKE_CURRENT_SOURCE_DIR}/src/thread - ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator - ${CMAKE_CURRENT_BINARY_DIR} + ${GETOPT_SOURCES} ) +target_include_directories(fy-thread PRIVATE ${INTERNAL_TOOL_INCLUDES}) +target_compile_definitions(fy-thread PRIVATE + ${COMMON_C_DEFINITIONS} + FY_STATIC) if(NOT MSVC) if(APPLE) target_link_libraries(fy-thread PRIVATE @@ -1048,7 +1175,7 @@ if(NOT MSVC) ) endif() if(HAVE_ASAN) - target_link_options(fy-thread PRIVATE -fsanitize=address) + target_link_options(fy-thread PRIVATE ${ASAN_C_FLAGS}) endif() else() target_link_libraries(fy-thread PRIVATE fyaml_static) @@ -1060,18 +1187,15 @@ add_dependencies(fy-thread fyaml_static) add_executable(fy-b3sum src/internal/fy-b3sum.c - src/valgrind/fy-valgrind.h + ${GETOPT_SOURCES} ) target_include_directories(fy-b3sum PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind - ${CMAKE_CURRENT_SOURCE_DIR}/src/lib - ${CMAKE_CURRENT_SOURCE_DIR}/src/util ${CMAKE_CURRENT_SOURCE_DIR}/src/blake3 - ${CMAKE_CURRENT_SOURCE_DIR}/src/thread - ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator - ${CMAKE_CURRENT_BINARY_DIR} + ${INTERNAL_TOOL_INCLUDES} ) +target_compile_definitions(fy-b3sum PRIVATE + ${COMMON_C_DEFINITIONS} + FY_STATIC) if(NOT MSVC) if(APPLE) target_link_libraries(fy-b3sum PRIVATE @@ -1085,7 +1209,7 @@ if(NOT MSVC) ) endif() if(HAVE_ASAN) - target_link_options(fy-b3sum PRIVATE -fsanitize=address) + target_link_options(fy-b3sum PRIVATE ${ASAN_C_FLAGS}) endif() else() target_link_libraries(fy-b3sum PRIVATE fyaml_static) @@ -1097,18 +1221,12 @@ add_dependencies(fy-b3sum fyaml_static) add_executable(fy-allocators src/internal/fy-allocators.c - src/valgrind/fy-valgrind.h -) -target_include_directories(fy-allocators PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src/valgrind - ${CMAKE_CURRENT_SOURCE_DIR}/src/lib - ${CMAKE_CURRENT_SOURCE_DIR}/src/util - ${CMAKE_CURRENT_SOURCE_DIR}/src/xxhash - ${CMAKE_CURRENT_SOURCE_DIR}/src/thread - ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator - ${CMAKE_CURRENT_BINARY_DIR} + ${GETOPT_SOURCES} ) +target_include_directories(fy-allocators PRIVATE ${INTERNAL_TOOL_INCLUDES}) +target_compile_definitions(fy-allocators PRIVATE + ${COMMON_C_DEFINITIONS} + FY_STATIC) if(NOT MSVC) if(APPLE) target_link_libraries(fy-allocators PRIVATE @@ -1122,7 +1240,7 @@ if(NOT MSVC) ) endif() if(HAVE_ASAN) - target_link_options(fy-allocators PRIVATE -fsanitize=address) + target_link_options(fy-allocators PRIVATE ${ASAN_C_FLAGS}) endif() else() target_link_libraries(fy-allocators PRIVATE fyaml_static) @@ -1132,10 +1250,14 @@ if(HAVE_LIBCLANG) endif() add_dependencies(fy-allocators fyaml_static) -endif() # NOT CROSS_COMPILING (internal tools) +if(BUILD_TESTING AND NOT CROSS_COMPILING AND HAVE_CHECK AND HAVE_BASH) + set(CAN_TEST 1) +else() + set(CAN_TEST 0) +endif() # Testing -if(BUILD_TESTING AND NOT CROSS_COMPILING) +if(CAN_TEST) # Include TAP subtest registration functions include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/add-tap-subtests.cmake) @@ -1145,36 +1267,6 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) # Create src directory for compatibility with test scripts # Tests expect tools at build/src/ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src) - add_custom_command(TARGET fy-tool POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/fy-tool - ${CMAKE_CURRENT_BINARY_DIR}/src/fy-tool - COMMENT "Creating symlink for test compatibility" - ) - - # Create symlinks for internal tools - add_custom_command(TARGET fy-thread POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/fy-thread - ${CMAKE_CURRENT_BINARY_DIR}/src/fy-thread - ) - add_custom_command(TARGET fy-b3sum POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/fy-b3sum - ${CMAKE_CURRENT_BINARY_DIR}/src/fy-b3sum - ) - add_custom_command(TARGET fy-allocators POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/fy-allocators - ${CMAKE_CURRENT_BINARY_DIR}/src/fy-allocators - ) - if(HAVE_LIBYAML) - add_custom_command(TARGET libfyaml-parser POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/libfyaml-parser - ${CMAKE_CURRENT_BINARY_DIR}/src/libfyaml-parser - ) - endif() # Build libfyaml-test if check is available if(HAVE_COMPATIBLE_CHECK) @@ -1189,6 +1281,7 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) test/libfyaml-test-private-id.c test/libfyaml-test-parser.c test/libfyaml-test-thread.c + ${GETOPT_SOURCES} ) target_compile_options(libfyaml-test PRIVATE ${COMMON_C_FLAGS}) @@ -1202,6 +1295,7 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) ${CMAKE_CURRENT_SOURCE_DIR}/src/allocator ${CMAKE_CURRENT_BINARY_DIR} ${CHECK_INCLUDE_DIRS} + ${GETOPT_INCLUDES} ) # Link against static library to access private symbols @@ -1221,7 +1315,7 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) ) endif() if(HAVE_ASAN) - target_link_options(libfyaml-test PRIVATE -fsanitize=address) + target_link_options(libfyaml-test PRIVATE ${ASAN_C_FLAGS}) endif() else() target_link_libraries(libfyaml-test PRIVATE fyaml_static ${CHECK_LIBRARIES}) @@ -1247,14 +1341,11 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test ) - # Add libfyaml.test + # Add the libfyaml-test binary as the libfyaml test add_test(NAME libfyaml - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test/libfyaml.test + COMMAND $ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test ) - set_tests_properties(libfyaml PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test" - ) endif() # Error tests - register individual subtests @@ -1265,28 +1356,18 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) add_testemitter_tests(testemitter-streaming "--streaming") add_testemitter_tests(testemitter-restreaming "--streaming --recreating") - # Network and git-dependent tests - if(HAVE_NETWORK AND HAVE_GIT) - # YAML test suite - setup fixture - add_test(NAME yaml-test-suite-setup - COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_BINARY_DIR}/test - ${CMAKE_COMMAND} - -DGIT_EXECUTABLE=${GIT_EXECUTABLE} - -DTESTSUITEURL=${TESTSUITEURL} - -DTESTSUITECHECKOUT=${TESTSUITECHECKOUT} - -DTEST_DIR=${CMAKE_CURRENT_BINARY_DIR}/test - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/setup-yaml-test-suite.cmake - ) - set_tests_properties(yaml-test-suite-setup PROPERTIES - FIXTURES_SETUP yaml-test-suite-data - ) + # Network-dependent tests using FetchContent + if(HAVE_NETWORK) + # Download test suites at configure time via FetchContent + message(STATUS "Fetching YAML test suite...") + FetchContent_MakeAvailable(yaml_test_suite) + message(STATUS "Fetching JSON test suite...") + FetchContent_MakeAvailable(json_test_suite) # YAML test suite tests - register individual subtests - # Three modes (handled automatically by add_testsuite_tests): - # 1. If test-suite-data exists: scan filesystem and register tests - # 2. If cmake/testsuite-tests.cmake exists: use pre-generated list - # 3. Otherwise: use monolithic test (re-run cmake after downloading data) - # To generate cmake/testsuite-tests.cmake, see cmake/README-testsuite-generation.md + # Two modes (handled automatically by add_testsuite_tests): + # 1. If test-suite-data exists (via FetchContent symlink): scan filesystem + # 2. If cmake/testsuite-tests.cmake exists: use pre-generated list (offline builds) add_testsuite_tests(testsuite testsuite.test) add_testsuite_tests(testsuite-evstream testsuite-evstream.test) add_testsuite_tests(testsuite-resolution testsuite-resolution.test) @@ -1294,32 +1375,10 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) add_testsuite_tests(testsuite-json testsuite-json.test) endif() - # JSON test suite - setup fixture - add_test(NAME json-test-suite-setup - COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_BINARY_DIR}/test - ${CMAKE_COMMAND} - -DGIT_EXECUTABLE=${GIT_EXECUTABLE} - -DTESTSUITEURL=${JSONTESTSUITEURL} - -DTESTSUITECHECKOUT=${JSONTESTSUITECHECKOUT} - -DTEST_DIR=${CMAKE_CURRENT_BINARY_DIR}/test - -DREPO_NAME=json-test-suite-data - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/setup-yaml-test-suite.cmake - ) - set_tests_properties(json-test-suite-setup PROPERTIES - FIXTURES_SETUP json-test-suite-data - ) - # JSON test suite - register individual subtests - # Three modes (handled automatically by add_jsontestsuite_tests): - # 1. If json-test-suite-data exists: scan filesystem and register tests - # 2. If cmake/jsontestsuite-tests.cmake exists: use pre-generated list - # 3. Otherwise: use monolithic test (re-run cmake after downloading data) - # To generate cmake/jsontestsuite-tests.cmake, see cmake/README-testsuite-generation.md add_jsontestsuite_tests() endif() - set(TEST_DRIVER ${CMAKE_SOURCE_DIR}/build-aux/tap-driver.sh) - # Build all test dependencies as part of default 'all' target # Note: CMake's 'make test' target doesn't trigger builds by design. # Use 'make all test' or 'make check-am' or 'make build_and_test' for complete workflow. @@ -1331,49 +1390,6 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) list(APPEND TEST_DEPENDENCIES libfyaml-parser) endif() - add_custom_target(build_tests ALL - DEPENDS ${TEST_DEPENDENCIES} - COMMENT "Building test dependencies" - ) - - # Convenience target that builds and runs tests in one command - add_custom_target(build_and_test - COMMAND ${CMAKE_CTEST_COMMAND} - DEPENDS ${TEST_DEPENDENCIES} - COMMENT "Building and running tests" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - - # Autotools-compatible test runner using test-driver - # NOTE: Named 'check-am' instead of 'check' to avoid conflicts with check library target - add_custom_target(check-am - COMMENT "Running tests via Automake test-driver" - ) - - if(HAVE_COMPATIBLE_CHECK) - set(TEST_EXECUTABLES - libfyaml-test - ) - - foreach(exe IN LISTS TEST_EXECUTABLES) - add_custom_target(check_${exe} - COMMAND ${CMAKE_COMMAND} -E env TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR} TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR} SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test - ${TEST_DRIVER} - --test-name ${exe} - --log-file ${exe}.log - --trs-file ${exe}.trs - --comments - --color-tests yes - --expect-failure 0 - -- ${CMAKE_CURRENT_BINARY_DIR}/test/${exe} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - COMMENT "Running ${exe}" - ) - add_dependencies(check_${exe} ${exe}) - add_dependencies(check-am check_${exe}) - endforeach() - endif() - set(TEST_SUITES testsuite.test jsontestsuite.test @@ -1389,28 +1405,6 @@ if(BUILD_TESTING AND NOT CROSS_COMPILING) list(APPEND TEST_SUITES testsuite-json.test) endif() - if(HAVE_LIBCLANG) - list(APPEND TEST_SUITES testreflection.test) - endif() - - foreach(suite IN LISTS TEST_SUITES) - add_custom_target(check_${suite} - COMMAND ${CMAKE_COMMAND} -E env TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR} TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR} SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test - ${TEST_DRIVER} - --test-name ${suite} - --log-file ${suite}.log - --trs-file ${suite}.trs - --comments - --color-tests yes - --expect-failure 0 - -- ${CMAKE_SOURCE_DIR}/test/${suite} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - COMMENT "Running ${suite}" - ) - add_dependencies(check_${suite} fy-tool) - add_dependencies(check-am check_${suite}) - endforeach() - endif() # Documentation @@ -1467,15 +1461,17 @@ install(TARGETS fyaml fy-tool PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -# Create symlinks for fy-tool -install(CODE " - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-dump WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-filter WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-testsuite WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-join WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-ypath WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-compose WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) -") +# Create symlinks for fy-tool (not on Windows - symlinks require special privileges) +if(NOT WIN32) + install(CODE " + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-dump WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-filter WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-testsuite WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-join WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-ypath WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink fy-tool fy-compose WORKING_DIRECTORY \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + ") +endif() install(EXPORT libfyaml-targets FILE libfyaml-targets.cmake @@ -1540,6 +1536,7 @@ message(STATUS "Target system: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCE message(STATUS "Cross compiling: ${CROSS_COMPILING}") message(STATUS "C compiler: ${CMAKE_C_COMPILER}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "HAVE_BASH: ${HAVE_BASH}") message(STATUS "HAVE_CHECK: ${HAVE_CHECK}") message(STATUS "HAVE_COMPATIBLE_CHECK: ${HAVE_COMPATIBLE_CHECK}") message(STATUS "HAVE_LIBYAML: ${HAVE_LIBYAML}") @@ -1563,16 +1560,9 @@ message(STATUS "TESTSUITEURL: ${TESTSUITEURL}") message(STATUS "TESTSUITECHECKOUT: ${TESTSUITECHECKOUT}") message(STATUS "JSONTESTSUITEURL: ${JSONTESTSUITEURL}") message(STATUS "JSONTESTSUITECHECKOUT: ${JSONTESTSUITECHECKOUT}") +message(STATUS "CAN_TEST: ${CAN_TEST}") message(STATUS "") -if(BUILD_TESTING AND NOT CROSS_COMPILING) - message(STATUS "Test targets:") - message(STATUS " make all test - Build everything then run CTest (2464 individual tests)") - message(STATUS " make build_and_test - Convenience target: build and test in one command") - message(STATUS " make check-am - Autotools-compatible TAP test runner (~10 test scripts)") - message(STATUS "") - message(STATUS "NOTE: 'make test' alone will fail if binaries aren't built. Use 'make all test'.") - message(STATUS "") -elseif(CROSS_COMPILING) +if(CROSS_COMPILING) message(STATUS "Tests disabled when cross-compiling (target binaries cannot execute on build host)") message(STATUS "") endif() diff --git a/Makefile.am b/Makefile.am index e593fea..f79c659 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,14 +7,61 @@ tarball-version: echo $(VERSION) > .tarball-version # files to keep in the distribution (in case you want to boostrap) -EXTRA_DIST=bootstrap.sh \ - build-aux/shave.in build-aux/shave-libtool.in \ - build-aux/git-version-gen \ - README.md LICENSE \ - Dockerfile Dockerfile-build-deb \ - Dockerfile.alpine Dockerfile.centos \ - cmake CMakeLists.txt \ - examples +EXTRA_DIST = \ + .dockerignore \ + .github/workflows/ci.yaml \ + .gitignore \ + .libtool-version \ + AUTHORS \ + CHANGELOG.md \ + CMakeLists.txt \ + Dockerfile \ + Dockerfile-build-deb \ + Dockerfile.alpine \ + Dockerfile.centos \ + LICENSE \ + Makefile.am \ + README.md \ + bootstrap.sh \ + build-aux/git-version-gen \ + build-aux/shave-libtool.in \ + build-aux/shave.in \ + cmake/CheckClangBlocks.cmake \ + cmake/CheckLibClang.cmake \ + cmake/MSVCCrossCompile.cmake \ + cmake/add-tap-subtests.cmake \ + cmake/clang-windows-toolchain.cmake \ + cmake/config.h.in \ + cmake/fyaml-config.cmake.in \ + cmake/generate-def.cmake \ + cmake/generate-jsontestsuite-list.sh \ + cmake/generate-testsuite-list.sh \ + cmake/libfyaml-config.cmake.in \ + cmake/libfyaml.pc.in \ + cmake/run-single-tap-test.sh \ + cmake/tags.cmake \ + examples/CMakeLists.txt \ + examples/README.md \ + examples/basic-parsing.c \ + examples/build-from-scratch.c \ + examples/config.yaml \ + examples/document-manipulation.c \ + examples/event-streaming.c \ + examples/invoice.yaml \ + examples/path-queries.c \ + examples/quick-start.c \ + libfyaml.pc.in \ + m4/ax_check_compile_flag.m4 \ + m4/ax_check_define.m4 \ + m4/ax_check_enable_debug.m4 \ + m4/ax_check_flag.m4 \ + m4/ax_cxx_compile_stdcxx.m4 \ + m4/ax_cxx_compile_stdcxx_11.m4 \ + m4/ax_define_dir.m4 \ + m4/ax_is_release.m4 \ + m4/ax_pthread.m4 \ + m4/ax_tls.m4 \ + m4/shave.m4 MAINTAINERCLEANFILES = \ Makefile.in src/Makefile.in config.h.in configure \ @@ -47,6 +94,7 @@ maintainer-clean-local: # extra file to put in the distribution EXTRA_DIST += \ scripts/create-virtual-env \ + scripts/get-gpg-key-id-by-email.sh \ scripts/install-linuxdoc.sh \ scripts/run-check-errors.sh \ scripts/run-compare-dump.sh \ diff --git a/README.md b/README.md index bdd7af2..c115d47 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # libfyaml -[![CI Status](https://github.com/pantoniou/libfyaml/workflows/Standard%20Automake%20CI/badge.svg)](https://github.com/pantoniou/libfyaml/actions) +[![Autotools CI](https://github.com/pantoniou/libfyaml/workflows/Standard%20Automake%20CI/badge.svg)](https://github.com/pantoniou/libfyaml/actions?query=workflow%3A%22Standard+Automake+CI%22) +[![CMake CI](https://github.com/pantoniou/libfyaml/workflows/CMake%20CI/badge.svg)](https://github.com/pantoniou/libfyaml/actions?query=workflow%3A%22CMake+CI%22) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Language: C](https://img.shields.io/badge/language-C-brightgreen.svg)](https://en.wikipedia.org/wiki/C_(programming_language)) @@ -558,12 +559,26 @@ make doc-latexpdf ## Platform Support -**Primary Platform**: Linux (Debian/Ubuntu, Fedora, Arch, etc.) -**Also Supported**: macOS (via Homebrew) -**Not Yet Supported**: Windows (contributions welcome!) +**Natively Supported Platforms**: +- Linux (Debian/Ubuntu, Fedora, Arch, Alpine, RHEL/Rocky, etc.) +- macOS (Xcode, Homebrew) +- Windows (native MSVC, clang-cl, Clang) +- FreeBSD, NetBSD, OpenBSD -**Architectures**: x86, x86_64, ARM, ARM64, and others -**Compilers**: GCC, Clang, ICC +**Architectures**: x86, x86_64, ARM, ARM64 are tested in CI, but we are portable, all should work as +long as Linux support is available. + +**Compilers**: +- GCC +- Clang +- MSVC (native Windows) +- clang-cl (Windows) +- Xcode (macOS) + +**Special windows cross-compilation support**: +- Windows targets via Wine + MSVC +- Windows targets via clang-cl cross-compile +- Windows targets via Clang cross-compile --- @@ -620,7 +635,6 @@ ctest --progress -j`nproc` ## Missing Features Current limitations: -- **Windows support**: Not yet implemented (contributions welcome) - **Unicode**: UTF-8 only, no wide character input support --- diff --git a/cmake/MSVCCrossCompile.cmake b/cmake/MSVCCrossCompile.cmake new file mode 100644 index 0000000..f60a5a1 --- /dev/null +++ b/cmake/MSVCCrossCompile.cmake @@ -0,0 +1,281 @@ +# MSVCCrossCompile.cmake - Auto-configure MSVC cross-compilation from msvc-wine +# +# Usage: Set MSVC_WINE_DIR to the msvc-wine installation directory, then include +# this file before project(). The module will auto-detect versions and set up +# the environment. +# +# Options: +# MSVC_WINE_DIR - Path to msvc-wine installation (required) +# MSVC_WINE_COMPILER - Compiler to use: "cl" (Wine) or "clang-cl" (default: cl) +# MSVC_WINE_ARCH - Target architecture: "x64", "x86", "arm64" (default: x64) +# MSVC_VERSION - Override MSVC toolchain version (auto-detected if not set) +# WINSDK_VERSION - Override Windows SDK version (auto-detected if not set) +# +# Example: +# cmake -DMSVC_WINE_DIR=/path/to/msvc -DMSVC_WINE_COMPILER=clang-cl .. + +if(NOT DEFINED MSVC_WINE_DIR) + return() +endif() + +# Normalize path +get_filename_component(MSVC_WINE_DIR "${MSVC_WINE_DIR}" ABSOLUTE) + +if(NOT EXISTS "${MSVC_WINE_DIR}") + message(FATAL_ERROR "MSVC_WINE_DIR does not exist: ${MSVC_WINE_DIR}") +endif() + +# Set defaults +if(NOT DEFINED MSVC_WINE_COMPILER) + set(MSVC_WINE_COMPILER "cl") +endif() + +if(NOT DEFINED MSVC_WINE_ARCH) + set(MSVC_WINE_ARCH "x64") +endif() + +# Map architecture to library subdirectory +if(MSVC_WINE_ARCH STREQUAL "x64") + set(_msvc_lib_arch "x64") + set(_msvc_bin_arch "x64") + set(_clang_triple "x86_64-windows-msvc") +elseif(MSVC_WINE_ARCH STREQUAL "x86") + set(_msvc_lib_arch "x86") + set(_msvc_bin_arch "x86") + set(_clang_triple "i686-windows-msvc") +elseif(MSVC_WINE_ARCH STREQUAL "arm64") + set(_msvc_lib_arch "arm64") + set(_msvc_bin_arch "arm64") + set(_clang_triple "aarch64-windows-msvc") +else() + message(FATAL_ERROR "Unsupported MSVC_WINE_ARCH: ${MSVC_WINE_ARCH}") +endif() + +# Auto-detect MSVC version if not specified +if(NOT DEFINED MSVC_VERSION) + file(GLOB _msvc_versions "${MSVC_WINE_DIR}/vc/tools/msvc/*") + list(LENGTH _msvc_versions _msvc_version_count) + if(_msvc_version_count EQUAL 0) + message(FATAL_ERROR "No MSVC toolchain found in ${MSVC_WINE_DIR}/vc/tools/msvc/") + endif() + # Sort and take the latest version + list(SORT _msvc_versions COMPARE NATURAL ORDER DESCENDING) + list(GET _msvc_versions 0 _msvc_version_path) + get_filename_component(MSVC_VERSION "${_msvc_version_path}" NAME) + message(STATUS "Auto-detected MSVC version: ${MSVC_VERSION}") +endif() + +# Auto-detect Windows SDK version if not specified +if(NOT DEFINED WINSDK_VERSION) + file(GLOB _sdk_versions "${MSVC_WINE_DIR}/kits/10/include/*") + list(LENGTH _sdk_versions _sdk_version_count) + if(_sdk_version_count EQUAL 0) + message(FATAL_ERROR "No Windows SDK found in ${MSVC_WINE_DIR}/kits/10/include/") + endif() + # Sort and take the latest version + list(SORT _sdk_versions COMPARE NATURAL ORDER DESCENDING) + list(GET _sdk_versions 0 _sdk_version_path) + get_filename_component(WINSDK_VERSION "${_sdk_version_path}" NAME) + message(STATUS "Auto-detected Windows SDK version: ${WINSDK_VERSION}") +endif() + +# Verify paths exist +set(_msvc_include "${MSVC_WINE_DIR}/vc/tools/msvc/${MSVC_VERSION}/include") +set(_msvc_lib "${MSVC_WINE_DIR}/vc/tools/msvc/${MSVC_VERSION}/lib/${_msvc_lib_arch}") +set(_sdk_include_shared "${MSVC_WINE_DIR}/kits/10/include/${WINSDK_VERSION}/shared") +set(_sdk_include_ucrt "${MSVC_WINE_DIR}/kits/10/include/${WINSDK_VERSION}/ucrt") +set(_sdk_include_um "${MSVC_WINE_DIR}/kits/10/include/${WINSDK_VERSION}/um") +set(_sdk_lib_ucrt "${MSVC_WINE_DIR}/kits/10/lib/${WINSDK_VERSION}/ucrt/${_msvc_lib_arch}") +set(_sdk_lib_um "${MSVC_WINE_DIR}/kits/10/lib/${WINSDK_VERSION}/um/${_msvc_lib_arch}") + +foreach(_path ${_msvc_include} ${_msvc_lib} ${_sdk_include_shared} ${_sdk_include_ucrt} ${_sdk_include_um} ${_sdk_lib_ucrt} ${_sdk_lib_um}) + if(NOT EXISTS "${_path}") + message(FATAL_ERROR "Required path does not exist: ${_path}") + endif() +endforeach() + +# Build INCLUDE and LIB paths (semicolon-separated for Windows/MSVC) +set(MSVC_WINE_INCLUDE "${_msvc_include};${_sdk_include_shared};${_sdk_include_ucrt};${_sdk_include_um}") +set(MSVC_WINE_LIB "${_msvc_lib};${_sdk_lib_ucrt};${_sdk_lib_um}") + +# Build compiler/linker flags for include/lib paths +# clang-cl uses -imsvc for system includes, regular clang uses -I (not -isystem to avoid replacing builtins) +set(_include_flags_clangcl "") +set(_include_flags_clang "") +foreach(_inc ${_msvc_include} ${_sdk_include_shared} ${_sdk_include_ucrt} ${_sdk_include_um}) + string(APPEND _include_flags_clangcl " -imsvc \"${_inc}\"") + string(APPEND _include_flags_clang " -I\"${_inc}\"") +endforeach() + +# Linker flags: /LIBPATH: for lld-link (clang-cl), -L for regular clang +set(_lib_flags_lldlink "") +set(_lib_flags_clang "") +foreach(_lib ${_msvc_lib} ${_sdk_lib_ucrt} ${_sdk_lib_um}) + string(APPEND _lib_flags_lldlink " /LIBPATH:\"${_lib}\"") + string(APPEND _lib_flags_clang " -L\"${_lib}\"") +endforeach() + +message(STATUS "MSVC cross-compile configuration:") +message(STATUS " MSVC_WINE_DIR: ${MSVC_WINE_DIR}") +message(STATUS " MSVC_WINE_COMPILER: ${MSVC_WINE_COMPILER}") +message(STATUS " MSVC_WINE_ARCH: ${MSVC_WINE_ARCH}") +message(STATUS " MSVC_VERSION: ${MSVC_VERSION}") +message(STATUS " WINSDK_VERSION: ${WINSDK_VERSION}") + +# Set environment variables for the build (used by cl.exe via Wine) +set(ENV{INCLUDE} "${MSVC_WINE_INCLUDE}") +set(ENV{LIB} "${MSVC_WINE_LIB}") + +message(STATUS " MSVC_WINE_INCLUDE: ${MSVC_WINE_INCLUDE}") +message(STATUS " MSVC_WINE_LIB: ${MSVC_WINE_LIB}") + +# Configure based on compiler choice +if(MSVC_WINE_COMPILER STREQUAL "cl") + # Use cl.exe via Wine + set(_bin_dir "${MSVC_WINE_DIR}/bin/${_msvc_bin_arch}") + if(NOT EXISTS "${_bin_dir}/cl") + message(FATAL_ERROR "cl wrapper not found in ${_bin_dir}") + endif() + + # Default to Release for Wine builds (Debug requires debug runtime DLLs) + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + message(STATUS " Defaulting to Release build (Debug requires VCRUNTIME140D.dll)") + endif() + + # Prepend to PATH + set(ENV{PATH} "${_bin_dir}:$ENV{PATH}") + + set(CMAKE_SYSTEM_NAME Windows CACHE STRING "" FORCE) + set(CMAKE_C_COMPILER "${_bin_dir}/cl" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER "${_bin_dir}/cl" CACHE STRING "" FORCE) + set(CMAKE_LINKER "${_bin_dir}/link" CACHE STRING "" FORCE) + set(CMAKE_RC_COMPILER "${_bin_dir}/rc" CACHE STRING "" FORCE) + set(CMAKE_MT "${_bin_dir}/mt" CACHE STRING "" FORCE) + +elseif(MSVC_WINE_COMPILER STREQUAL "clang-cl") + # Use clang-cl (native cross-compilation, no Wine needed) + # Default to Release (Debug has linker flag issues with lld-link) + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + message(STATUS " Defaulting to Release build") + endif() + + find_program(CLANG_CL_EXECUTABLE NAMES clang-cl clang-cl-18 clang-cl-17 clang-cl-16 clang-cl-15) + if(NOT CLANG_CL_EXECUTABLE) + message(FATAL_ERROR "clang-cl not found. Install with: apt install clang") + endif() + + find_program(LLD_LINK_EXECUTABLE NAMES lld-link lld-link-18 lld-link-17 lld-link-16 lld-link-15) + if(NOT LLD_LINK_EXECUTABLE) + message(FATAL_ERROR "lld-link not found. Install with: apt install lld") + endif() + + find_program(LLVM_RC_EXECUTABLE NAMES llvm-rc llvm-rc-18 llvm-rc-17 llvm-rc-16 llvm-rc-15) + if(NOT LLVM_RC_EXECUTABLE) + message(FATAL_ERROR "llvm-rc not found. Install with: apt install llvm") + endif() + + find_program(LLVM_MT_EXECUTABLE NAMES llvm-mt llvm-mt-18 llvm-mt-17 llvm-mt-16 llvm-mt-15) + if(NOT LLVM_MT_EXECUTABLE) + message(FATAL_ERROR "llvm-mt not found. Install with: apt install llvm") + endif() + + set(CMAKE_SYSTEM_NAME Windows CACHE STRING "" FORCE) + set(CMAKE_C_COMPILER "${CLANG_CL_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER "${CLANG_CL_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_LINKER "${LLD_LINK_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_RC_COMPILER "${LLVM_RC_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_MT "${LLVM_MT_EXECUTABLE}" CACHE STRING "" FORCE) + + # Pass include paths via compiler flags (env vars don't persist to build) + set(CMAKE_C_FLAGS_INIT "${_include_flags_clangcl}" CACHE STRING "" FORCE) + set(CMAKE_CXX_FLAGS_INIT "${_include_flags_clangcl}" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS_INIT "${_lib_flags_lldlink}" CACHE STRING "" FORCE) + set(CMAKE_SHARED_LINKER_FLAGS_INIT "${_lib_flags_lldlink}" CACHE STRING "" FORCE) + set(CMAKE_STATIC_LINKER_FLAGS_INIT "" CACHE STRING "" FORCE) + + message(STATUS " Using clang-cl: ${CLANG_CL_EXECUTABLE}") + message(STATUS " Using lld-link: ${LLD_LINK_EXECUTABLE}") + message(STATUS " Using llvm-rc: ${LLVM_RC_EXECUTABLE}") + message(STATUS " Using llvm-mt: ${LLVM_MT_EXECUTABLE}") + +elseif(MSVC_WINE_COMPILER STREQUAL "clang") + # Use regular clang with Windows target triplet (no Wine needed) + # Default to Release + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + message(STATUS " Defaulting to Release build") + endif() + + # Find clang + find_program(CLANG_EXECUTABLE NAMES clang clang-18 clang-17 clang-16 clang-15) + if(NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "clang not found. Install with: apt install clang") + endif() + + find_program(CLANGXX_EXECUTABLE NAMES clang++ clang++-18 clang++-17 clang++-16 clang++-15) + if(NOT CLANGXX_EXECUTABLE) + message(FATAL_ERROR "clang++ not found. Install with: apt install clang") + endif() + + find_program(LLD_LINK_EXECUTABLE NAMES lld-link lld-link-18 lld-link-17 lld-link-16 lld-link-15) + if(NOT LLD_LINK_EXECUTABLE) + message(FATAL_ERROR "lld-link not found. Install with: apt install lld") + endif() + + find_program(LLVM_RC_EXECUTABLE NAMES llvm-rc llvm-rc-18 llvm-rc-17 llvm-rc-16 llvm-rc-15) + if(NOT LLVM_RC_EXECUTABLE) + message(FATAL_ERROR "llvm-rc not found. Install with: apt install llvm") + endif() + + find_program(LLVM_MT_EXECUTABLE NAMES llvm-mt llvm-mt-18 llvm-mt-17 llvm-mt-16 llvm-mt-15) + if(NOT LLVM_MT_EXECUTABLE) + message(FATAL_ERROR "llvm-mt not found. Install with: apt install llvm") + endif() + + set(CMAKE_SYSTEM_NAME Windows CACHE STRING "" FORCE) + set(CMAKE_SYSTEM_PROCESSOR AMD64 CACHE STRING "" FORCE) + set(CMAKE_C_COMPILER "${CLANG_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER "${CLANGXX_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_C_COMPILER_TARGET "x86_64-windows-msvc" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER_TARGET "x86_64-windows-msvc" CACHE STRING "" FORCE) + set(CMAKE_LINKER "${LLD_LINK_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_RC_COMPILER "${LLVM_RC_EXECUTABLE}" CACHE STRING "" FORCE) + set(CMAKE_MT "${LLVM_MT_EXECUTABLE}" CACHE STRING "" FORCE) + + # Configure linking - clang needs explicit link command for lld-link + set(CMAKE_C_LINK_EXECUTABLE " -out: " CACHE STRING "" FORCE) + set(CMAKE_CXX_LINK_EXECUTABLE " -out: " CACHE STRING "" FORCE) + set(CMAKE_C_CREATE_SHARED_LIBRARY " -dll -out: -implib: " CACHE STRING "" FORCE) + set(CMAKE_CXX_CREATE_SHARED_LIBRARY " -dll -out: -implib: " CACHE STRING "" FORCE) + + # Disable Unix-specific flags + set(CMAKE_C_COMPILE_OPTIONS_PIC "" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILE_OPTIONS_PIC "" CACHE STRING "" FORCE) + set(CMAKE_SHARED_LIBRARY_C_FLAGS "" CACHE STRING "" FORCE) + set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "" CACHE STRING "" FORCE) + + # Use env launcher to set INCLUDE/LIB for each compiler invocation + # (clang with MSVC target reads INCLUDE env var, -I flags don't work the same way) + string(REPLACE ";" "\\;" _include_env "${MSVC_WINE_INCLUDE}") + string(REPLACE ";" "\\;" _lib_env "${MSVC_WINE_LIB}") + set(CMAKE_C_COMPILER_LAUNCHER "env" "INCLUDE=${_include_env}" "LIB=${_lib_env}" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER_LAUNCHER "env" "INCLUDE=${_include_env}" "LIB=${_lib_env}" CACHE STRING "" FORCE) + + set(CMAKE_C_FLAGS_INIT "-fuse-ld=lld" CACHE STRING "" FORCE) + set(CMAKE_CXX_FLAGS_INIT "-fuse-ld=lld" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS_INIT "${_lib_flags_clang}" CACHE STRING "" FORCE) + set(CMAKE_SHARED_LINKER_FLAGS_INIT "${_lib_flags_clang}" CACHE STRING "" FORCE) + + message(STATUS " Using clang: ${CLANG_EXECUTABLE}") + message(STATUS " Using lld-link: ${LLD_LINK_EXECUTABLE}") + message(STATUS " Using llvm-rc: ${LLVM_RC_EXECUTABLE}") + message(STATUS " Using llvm-mt: ${LLVM_MT_EXECUTABLE}") + +else() + message(FATAL_ERROR "Unknown MSVC_WINE_COMPILER: ${MSVC_WINE_COMPILER} (use 'cl', 'clang-cl', or 'clang')") +endif() + +# Export variables for use in the project +set(MSVC_WINE_CONFIGURED TRUE CACHE INTERNAL "MSVC Wine cross-compilation configured") diff --git a/cmake/add-tap-subtests.cmake b/cmake/add-tap-subtests.cmake index 638ee2c..58cf816 100644 --- a/cmake/add-tap-subtests.cmake +++ b/cmake/add-tap-subtests.cmake @@ -1,12 +1,104 @@ # Helper function to add individual TAP subtests from test scripts # This mimics the behavior of autotools tap-driver.sh by registering each subtest individually +# Helper function to add a TAP test with fy-tool path via generator expression +# This enables proper support for multi-config generators (Visual Studio, Xcode) +# by passing the actual tool path at runtime rather than relying on hardcoded paths. +function(add_tap_test test_name suite_name test_id) + cmake_parse_arguments(TAP "DISABLED;WILL_FAIL" "" "LABELS;EXTRA_ENV" ${ARGN}) + + add_test( + NAME "${test_name}" + COMMAND "${BASH_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh" + "$" + "${suite_name}" "${test_id}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test + ) + + # Build environment + set(base_env + "TEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test" + "YAML_TEST_SUITE=${yaml_test_suite_SOURCE_DIR}" + "JSON_TEST_SUITE=${json_test_suite_SOURCE_DIR}" + ) + if(TAP_EXTRA_ENV) + list(APPEND base_env ${TAP_EXTRA_ENV}) + endif() + + set_tests_properties("${test_name}" PROPERTIES DEPENDS fy-tool) + + set_tests_properties("${test_name}" PROPERTIES ENVIRONMENT "${base_env}") + + if(TAP_LABELS) + set_tests_properties("${test_name}" PROPERTIES LABELS "${TAP_LABELS}") + endif() + if(TAP_DISABLED) + set_tests_properties("${test_name}" PROPERTIES DISABLED TRUE) + endif() + if(TAP_WILL_FAIL) + set_tests_properties("${test_name}" PROPERTIES WILL_FAIL TRUE) + endif() +endfunction() + +# Helper macro to register a single testsuite test +# Used by add_testsuite_tests to avoid code duplication +macro(_register_testsuite_test test_name test_dir test_id skip_list xfail_list extra_env) + if(EXISTS "${test_dir}/===") + # Read description from === file + file(READ "${test_dir}/===" _test_desc) + string(STRIP "${_test_desc}" _test_desc) + + # Build test name with description if available + if(_test_desc) + set(_test_name_full "${test_name}/${test_id} - ${_test_desc}") + else() + set(_test_name_full "${test_name}/${test_id}") + endif() + + # Set basic properties + set(_test_labels "${test_name}") + set(_test_disabled FALSE) + + # Check if test should be skipped + if(test_id IN_LIST skip_list) + set(_test_disabled TRUE) + set(_test_labels "${test_name};skipped") + endif() + + # Check if test is expected to fail (xfail) + if(test_id IN_LIST xfail_list) + set(_test_labels "${test_name};xfail") + endif() + + if(_test_disabled) + add_tap_test("${_test_name_full}" "${test_name}" "${test_id}" + LABELS "${_test_labels}" + EXTRA_ENV "${extra_env}" + DISABLED + ) + else() + add_tap_test("${_test_name_full}" "${test_name}" "${test_id}" + LABELS "${_test_labels}" + EXTRA_ENV "${extra_env}" + ) + endif() + endif() +endmacro() + # Function to run a single test from testerrors.test function(add_testerrors_tests) - file(GLOB error_dirs "${CMAKE_CURRENT_SOURCE_DIR}/test/test-errors/[0-9][0-9][0-9][0-9]") + # Use wildcard glob and filter with regex (Windows-compatible) + file(GLOB all_dirs "${CMAKE_CURRENT_SOURCE_DIR}/test/test-errors/*") - foreach(dir ${error_dirs}) + foreach(dir ${all_dirs}) + if(NOT IS_DIRECTORY "${dir}") + continue() + endif() get_filename_component(test_id "${dir}" NAME) + # Filter: must be 4 digits + if(NOT test_id MATCHES "^[0-9][0-9][0-9][0-9]$") + continue() + endif() # Read description from === file set(desc_file "${dir}/===") @@ -24,16 +116,7 @@ function(add_testerrors_tests) set(test_name_full "testerrors/${test_id}") endif() - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - testerrors "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set properties - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test" + add_tap_test("${test_name_full}" testerrors "${test_id}" LABELS "testerrors" ) endforeach() @@ -46,26 +129,17 @@ function(add_testemitter_tests test_name extra_args) foreach(yaml_file ${yaml_files}) get_filename_component(test_id "${yaml_file}" NAME) - add_test( - NAME "${test_name}/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - "${test_name}" "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - set_tests_properties("${test_name}/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;EXTRA_DUMP_ARGS=${extra_args}" + add_tap_test("${test_name}/${test_id}" "${test_name}" "${test_id}" LABELS "${test_name}" + EXTRA_ENV "EXTRA_DUMP_ARGS=${extra_args}" ) endforeach() endfunction() # Function to add testsuite tests (yaml test suite) -# Can work either by scanning the test-suite-data directory (if it exists) -# or by using a pre-generated list of tests from testsuite-tests.cmake +# Scans test-suite-data directory (populated by FetchContent at configure time) function(add_testsuite_tests test_name test_script) - set(test_dir "${CMAKE_CURRENT_BINARY_DIR}/test/test-suite-data") - set(use_pregenerated_list FALSE) + set(test_dir "${yaml_test_suite_SOURCE_DIR}") # Define skip/xfail lists based on test suite variant # These match the lists in test/*.test scripts @@ -83,358 +157,173 @@ function(add_testsuite_tests test_name test_script) set(xfail_list "") endif() - # Check if we should use the pre-generated list - if(NOT EXISTS "${test_dir}") - # Try to use pre-generated list if available - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/testsuite-tests.cmake") - message(STATUS "Using pre-generated test list from cmake/testsuite-tests.cmake for ${test_name}") - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/testsuite-tests.cmake") - set(use_pregenerated_list TRUE) - else() - # Neither data nor pre-generated list exists, register monolithic fallback - message(STATUS "Test suite data not found - registering monolithic ${test_name} test") - message(STATUS "Run 'ctest -R yaml-test-suite-setup' then re-run cmake to register individual subtests") + set(extra_env "JQ=${JQ_EXECUTABLE}") - add_test(NAME ${test_name} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test/${test_script} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - set_tests_properties(${test_name} PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - FIXTURES_REQUIRED yaml-test-suite-data - ) - return() + message(STATUS "Registering individual ${test_name} subtests") + + # Use wildcard glob and filter with regex (Windows-compatible) + file(GLOB all_base_dirs "${test_dir}/*") + + foreach(base_test ${all_base_dirs}) + if(NOT IS_DIRECTORY "${base_test}") + continue() + endif() + get_filename_component(base_id "${base_test}" NAME) + # Filter: must be 4 alphanumeric chars (YAML test suite IDs like "229Q", "2JQS") + if(NOT base_id MATCHES "^[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]$") + continue() endif() - else() - message(STATUS "Registering individual ${test_name} subtests from filesystem") - endif() - if(use_pregenerated_list) - # Use pre-generated TESTSUITE_ALL_TESTS list - foreach(test_id ${TESTSUITE_ALL_TESTS}) - # test_id is either "BASE" or "BASE/SUBTEST" - string(REPLACE "/" ";" test_parts "${test_id}") - list(LENGTH test_parts parts_count) - - add_test( - NAME "${test_name}/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - "${test_name}" "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) + # Register base test + _register_testsuite_test("${test_name}" "${base_test}" "${base_id}" + "${skip_list}" "${xfail_list}" "${extra_env}") - set_tests_properties("${test_name}/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "${test_name}" - FIXTURES_REQUIRED "yaml-test-suite-data" - ) - endforeach() - else() - # Scan the filesystem (original behavior) - file(GLOB base_tests "${test_dir}/[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]") - - foreach(base_test ${base_tests}) - get_filename_component(test_id "${base_test}" NAME) - - # Check for main test - if(EXISTS "${base_test}/===") - # Read description from === file - file(READ "${base_test}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "${test_name}/${test_id} - ${test_desc}") - else() - set(test_name_full "${test_name}/${test_id}") - endif() - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - "${test_name}" "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set basic properties - set(test_labels "${test_name}") - set(test_disabled FALSE) - - # Check if test should be skipped - if(test_id IN_LIST skip_list) - set(test_disabled TRUE) - set(test_labels "${test_name};skipped") - endif() - - # Check if test is expected to fail (xfail) - # Note: TAP xfail (TODO) tests still pass (exit 0), they just mark the result as TODO - # So we don't use WILL_FAIL here, just add a label for filtering - if(test_id IN_LIST xfail_list) - set(test_labels "${test_name};xfail") - endif() - - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "${test_labels}" - FIXTURES_REQUIRED "yaml-test-suite-data" - DISABLED ${test_disabled} - ) + # Check for subtests (2-digit or 3-digit subdirectories) + file(GLOB all_subtests "${base_test}/*") + foreach(subtest ${all_subtests}) + if(NOT IS_DIRECTORY "${subtest}") + continue() + endif() + get_filename_component(subtest_id "${subtest}" NAME) + # Filter: must be 2 or 3 digits + if(NOT subtest_id MATCHES "^[0-9][0-9][0-9]?$") + continue() endif() - # Check for 2-digit subtests - file(GLOB subtests_2d "${base_test}/[0-9][0-9]") - foreach(subtest ${subtests_2d}) - if(EXISTS "${subtest}/===") - get_filename_component(subtest_id "${subtest}" NAME) - set(full_test_id "${test_id}/${subtest_id}") - - # Read description from === file - file(READ "${subtest}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "${test_name}/${full_test_id} - ${test_desc}") - else() - set(test_name_full "${test_name}/${full_test_id}") - endif() - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - "${test_name}" "${full_test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set basic properties - set(test_labels "${test_name}") - set(test_disabled FALSE) - - # Check if test should be skipped - if(full_test_id IN_LIST skip_list) - set(test_disabled TRUE) - set(test_labels "${test_name};skipped") - endif() - - # Check if test is expected to fail (xfail) - # Note: TAP xfail (TODO) tests still pass (exit 0), they just mark the result as TODO - # So we don't use WILL_FAIL here, just add a label for filtering - if(full_test_id IN_LIST xfail_list) - set(test_labels "${test_name};xfail") - endif() - - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "${test_labels}" - FIXTURES_REQUIRED "yaml-test-suite-data" - DISABLED ${test_disabled} - ) - endif() - endforeach() - - # Check for 3-digit subtests - file(GLOB subtests_3d "${base_test}/[0-9][0-9][0-9]") - foreach(subtest ${subtests_3d}) - if(EXISTS "${subtest}/===") - get_filename_component(subtest_id "${subtest}" NAME) - set(full_test_id "${test_id}/${subtest_id}") - - # Read description from === file - file(READ "${subtest}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "${test_name}/${full_test_id} - ${test_desc}") - else() - set(test_name_full "${test_name}/${full_test_id}") - endif() - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - "${test_name}" "${full_test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set basic properties - set(test_labels "${test_name}") - set(test_disabled FALSE) - - # Check if test should be skipped - if(full_test_id IN_LIST skip_list) - set(test_disabled TRUE) - set(test_labels "${test_name};skipped") - endif() - - # Check if test is expected to fail (xfail) - # Note: TAP xfail (TODO) tests still pass (exit 0), they just mark the result as TODO - # So we don't use WILL_FAIL here, just add a label for filtering - if(full_test_id IN_LIST xfail_list) - set(test_labels "${test_name};xfail") - endif() - - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "${test_labels}" - FIXTURES_REQUIRED "yaml-test-suite-data" - DISABLED ${test_disabled} - ) - endif() - endforeach() + set(full_test_id "${base_id}/${subtest_id}") + _register_testsuite_test("${test_name}" "${subtest}" "${full_test_id}" + "${skip_list}" "${xfail_list}" "${extra_env}") endforeach() - endif() + endforeach() endfunction() # Function to add jsontestsuite tests -# Can work either by scanning the json-test-suite-data directory (if it exists) -# or by using a pre-generated list of tests from jsontestsuite-tests.cmake +# Scans json-test-suite-data directory (populated by FetchContent at configure time) function(add_jsontestsuite_tests) - set(test_dir "${CMAKE_CURRENT_BINARY_DIR}/test/json-test-suite-data/test_parsing") - set(use_pregenerated_list FALSE) - - # Check if we should use the pre-generated list - if(NOT EXISTS "${test_dir}") - # Try to use pre-generated list if available - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/jsontestsuite-tests.cmake") - message(STATUS "Using pre-generated test list from cmake/jsontestsuite-tests.cmake") - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/jsontestsuite-tests.cmake") - set(use_pregenerated_list TRUE) + set(test_dir "${json_test_suite_SOURCE_DIR}/test_parsing") + + message(STATUS "Registering individual jsontestsuite subtests") + + # Scan for all .json files and categorize by prefix + file(GLOB all_tests "${test_dir}/*.json") + foreach(test_file ${all_tests}) + get_filename_component(test_id "${test_file}" NAME) + + # Categorize by first character: y=pass, n=fail, i=impl-defined + string(SUBSTRING "${test_id}" 0 1 prefix) + if(prefix STREQUAL "y") + set(label "jsontestsuite;jsontestsuite-pass") + elseif(prefix STREQUAL "n") + set(label "jsontestsuite;jsontestsuite-fail") + elseif(prefix STREQUAL "i") + set(label "jsontestsuite;jsontestsuite-impl") else() - # Neither data nor pre-generated list exists, register monolithic fallback - message(STATUS "JSON test suite data not found - registering monolithic jsontestsuite test") - message(STATUS "Run 'ctest -R json-test-suite-setup' then re-run cmake to register individual subtests") - - add_test(NAME jsontestsuite - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test/jsontestsuite.test - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - set_tests_properties(jsontestsuite PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - FIXTURES_REQUIRED json-test-suite-data - ) - return() + continue() endif() - else() - message(STATUS "Registering individual jsontestsuite subtests from filesystem") - endif() - if(use_pregenerated_list) - # Use pre-generated lists - # Expected to pass (y_*.json) - foreach(test_id ${JSONTESTSUITE_PASS_TESTS}) - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-pass" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() - - # Expected to fail (n_*.json) - foreach(test_id ${JSONTESTSUITE_FAIL_TESTS}) - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-fail" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() - - # Implementation defined (i_*.json) - foreach(test_id ${JSONTESTSUITE_IMPL_TESTS}) - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) + add_tap_test("jsontestsuite/${test_id}" jsontestsuite "${test_id}" + LABELS "${label}" + EXTRA_ENV "JQ=${JQ_EXECUTABLE}" + ) + endforeach() +endfunction() - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-impl" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() - else() - # Scan the filesystem (original behavior) - # Expected to pass (y_*.json) - file(GLOB pass_tests "${test_dir}/y_*.json") - foreach(test_file ${pass_tests}) - get_filename_component(test_id "${test_file}" NAME) - - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) +# Helper macro for checking if a reflection test should be skipped based on env file +macro(_should_skip_reflection_test test_path result_var) + set(${result_var} FALSE) + set(_env_file "${test_path}/env") + if(EXISTS "${_env_file}") + file(READ "${_env_file}" _env_contents) + string(STRIP "${_env_contents}" _env_contents) + + # Parse env file for requirements + if(_env_contents MATCHES "intbits=([0-9]+)") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_intbits}") + set(${result_var} TRUE) + endif() + endif() + if(_env_contents MATCHES "shortbits=([0-9]+)") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_shortbits}") + set(${result_var} TRUE) + endif() + endif() + if(_env_contents MATCHES "longbits=([0-9]+)") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_longbits}") + set(${result_var} TRUE) + endif() + endif() + if(_env_contents MATCHES "charbits=([0-9]+)") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_charbits}") + set(${result_var} TRUE) + endif() + endif() + if(_env_contents MATCHES "longlongbits=([0-9]+)") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_longlongbits}") + set(${result_var} TRUE) + endif() + endif() + if(_env_contents MATCHES "charsigned=([yn])") + if(NOT "${CMAKE_MATCH_1}" STREQUAL "${_refl_charsigned}") + set(${result_var} TRUE) + endif() + endif() + endif() +endmacro() - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-pass" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() +# Helper macro to register a single reflection test +macro(_register_reflection_test test_dir test_id xfail_list) + if(EXISTS "${test_dir}/===") + # Read description from === file + file(READ "${test_dir}/===" _test_desc) + string(STRIP "${_test_desc}" _test_desc) - # Expected to fail (n_*.json) - file(GLOB fail_tests "${test_dir}/n_*.json") - foreach(test_file ${fail_tests}) - get_filename_component(test_id "${test_file}" NAME) + # Build test name with description if available + if(_test_desc) + set(_test_name_full "testreflection/${test_id} - ${_test_desc}") + else() + set(_test_name_full "testreflection/${test_id}") + endif() - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) + # Check if test should be skipped based on environment + _should_skip_reflection_test("${test_dir}" _test_should_skip) - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-fail" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() + # Set basic properties + set(_test_labels "testreflection") + set(_test_disabled FALSE) + set(_test_will_fail FALSE) - # Implementation defined (i_*.json) - file(GLOB impl_tests "${test_dir}/i_*.json") - foreach(test_file ${impl_tests}) - get_filename_component(test_id "${test_file}" NAME) + if(_test_should_skip) + set(_test_disabled TRUE) + set(_test_labels "testreflection;skipped") + endif() - add_test( - NAME "jsontestsuite/${test_id}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - jsontestsuite "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) + if(test_id IN_LIST xfail_list) + set(_test_labels "testreflection;xfail") + set(_test_will_fail TRUE) + endif() - set_tests_properties("jsontestsuite/${test_id}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test;JQ=${JQ_EXECUTABLE}" - LABELS "jsontestsuite;jsontestsuite-impl" - FIXTURES_REQUIRED "json-test-suite-data" - ) - endforeach() + # Register the test with appropriate flags + if(_test_disabled AND _test_will_fail) + add_tap_test("${_test_name_full}" testreflection "${test_id}" + LABELS "${_test_labels}" DISABLED WILL_FAIL) + elseif(_test_disabled) + add_tap_test("${_test_name_full}" testreflection "${test_id}" + LABELS "${_test_labels}" DISABLED) + elseif(_test_will_fail) + add_tap_test("${_test_name_full}" testreflection "${test_id}" + LABELS "${_test_labels}" WILL_FAIL) + else() + add_tap_test("${_test_name_full}" testreflection "${test_id}" + LABELS "${_test_labels}") + endif() endif() -endfunction() +endmacro() # Function to add reflection tests function(add_testreflection_tests) set(test_dir "${CMAKE_CURRENT_SOURCE_DIR}/test/reflection-data") # Skip/xfail lists from testreflection.test - # xfaillist="025PB08/00 025UXE6/00 025VL2J/00" set(xfail_list "025PB08/00" "025UXE6/00" "025VL2J/00") # Probe system characteristics (matching testreflection.test logic) @@ -445,12 +334,12 @@ function(add_testreflection_tests) check_type_size("unsigned char" SIZEOF_UCHAR) check_type_size("long long" SIZEOF_LONGLONG) - # Calculate bit sizes - math(EXPR intbits "${SIZEOF_UINT} * 8") - math(EXPR shortbits "${SIZEOF_USHORT} * 8") - math(EXPR longbits "${SIZEOF_ULONG} * 8") - math(EXPR charbits "${SIZEOF_UCHAR} * 8") - set(longlongbits 64) # Always 64 bits as per testreflection.test + # Calculate bit sizes (store in _refl_ prefixed vars for macro access) + math(EXPR _refl_intbits "${SIZEOF_UINT} * 8") + math(EXPR _refl_shortbits "${SIZEOF_USHORT} * 8") + math(EXPR _refl_longbits "${SIZEOF_ULONG} * 8") + math(EXPR _refl_charbits "${SIZEOF_UCHAR} * 8") + set(_refl_longlongbits 64) # Check if char is signed include(CheckCSourceRuns) @@ -459,232 +348,45 @@ function(add_testreflection_tests) int main() { return (CHAR_MIN < 0) ? 0 : 1; } " CHAR_IS_SIGNED) if(CHAR_IS_SIGNED) - set(charsigned "y") + set(_refl_charsigned "y") else() - set(charsigned "n") + set(_refl_charsigned "n") endif() message(STATUS "Reflection test system characteristics:") - message(STATUS " intbits=${intbits} shortbits=${shortbits} longbits=${longbits}") - message(STATUS " charbits=${charbits} longlongbits=${longlongbits} charsigned=${charsigned}") - - # Helper function to check if test should be skipped based on env file - macro(should_skip_test test_path result_var) - set(${result_var} FALSE) - set(env_file "${test_path}/env") - if(EXISTS "${env_file}") - file(READ "${env_file}" env_contents) - string(STRIP "${env_contents}" env_contents) - - # Parse env file for requirements - if(env_contents MATCHES "intbits=([0-9]+)") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${intbits}") - set(${result_var} TRUE) - endif() - endif() - if(env_contents MATCHES "shortbits=([0-9]+)") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${shortbits}") - set(${result_var} TRUE) - endif() - endif() - if(env_contents MATCHES "longbits=([0-9]+)") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${longbits}") - set(${result_var} TRUE) - endif() - endif() - if(env_contents MATCHES "charbits=([0-9]+)") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${charbits}") - set(${result_var} TRUE) - endif() - endif() - if(env_contents MATCHES "longlongbits=([0-9]+)") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${longlongbits}") - set(${result_var} TRUE) - endif() - endif() - if(env_contents MATCHES "charsigned=([yn])") - if(NOT "${CMAKE_MATCH_1}" STREQUAL "${charsigned}") - set(${result_var} TRUE) - endif() - endif() - endif() - endmacro() - - # Scan reflection-data directory for tests - # Note: CMake GLOB doesn't support [A-Z0-9] ranges like shell globs do - # We need to use a wildcard and filter in the loop - file(GLOB base_tests "${test_dir}/*") - - # Filter to only include directories matching the pattern [0-9][0-9][0-9][A-Z][A-Z0-9][A-Z0-9][A-Z0-9] - set(filtered_base_tests) - foreach(test_candidate ${base_tests}) - if(IS_DIRECTORY "${test_candidate}") - get_filename_component(test_name "${test_candidate}" NAME) - # Check if the name matches the pattern: 3 digits followed by 4 alphanumeric chars (starting with letter) - if(test_name MATCHES "^[0-9][0-9][0-9][A-Z][A-Z0-9][A-Z0-9][A-Z0-9]$") - list(APPEND filtered_base_tests "${test_candidate}") - endif() - endif() - endforeach() - set(base_tests ${filtered_base_tests}) + message(STATUS " intbits=${_refl_intbits} shortbits=${_refl_shortbits} longbits=${_refl_longbits}") + message(STATUS " charbits=${_refl_charbits} longlongbits=${_refl_longlongbits} charsigned=${_refl_charsigned}") - foreach(base_test ${base_tests}) - get_filename_component(test_id "${base_test}" NAME) + # Use wildcard glob and filter with regex (Windows-compatible) + file(GLOB all_base_dirs "${test_dir}/*") - # Check for main test (base directory with === file) - if(EXISTS "${base_test}/===") - # Read description from === file - file(READ "${base_test}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "testreflection/${test_id} - ${test_desc}") - else() - set(test_name_full "testreflection/${test_id}") - endif() - - # Check if test should be skipped based on environment - should_skip_test("${base_test}" test_should_skip) - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - testreflection "${test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) + foreach(base_test ${all_base_dirs}) + if(NOT IS_DIRECTORY "${base_test}") + continue() + endif() + get_filename_component(base_id "${base_test}" NAME) + # Filter: 3 digits followed by 4 alphanumeric chars (starting with letter) + if(NOT base_id MATCHES "^[0-9][0-9][0-9][A-Z][A-Z0-9][A-Z0-9][A-Z0-9]$") + continue() + endif() - # Set basic properties - set(test_labels "testreflection") - set(test_disabled FALSE) - set(test_will_fail FALSE) + # Register base test + _register_reflection_test("${base_test}" "${base_id}" "${xfail_list}") - # Check if test should be skipped - if(test_should_skip) - set(test_disabled TRUE) - set(test_labels "testreflection;skipped") + # Check for subtests (2-digit or 3-digit subdirectories) + file(GLOB all_subtests "${base_test}/*") + foreach(subtest ${all_subtests}) + if(NOT IS_DIRECTORY "${subtest}") + continue() endif() - - # Check if test is expected to fail (xfail) - if(test_id IN_LIST xfail_list) - set(test_labels "testreflection;xfail") - set(test_will_fail TRUE) + get_filename_component(subtest_id "${subtest}" NAME) + # Filter: must be 2 or 3 digits + if(NOT subtest_id MATCHES "^[0-9][0-9][0-9]?$") + continue() endif() - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test" - LABELS "${test_labels}" - DISABLED ${test_disabled} - WILL_FAIL ${test_will_fail} - ) - endif() - - # Check for 2-digit subtests - file(GLOB subtests_2d "${base_test}/[0-9][0-9]") - foreach(subtest ${subtests_2d}) - if(EXISTS "${subtest}/===") - get_filename_component(subtest_id "${subtest}" NAME) - set(full_test_id "${test_id}/${subtest_id}") - - # Read description from === file - file(READ "${subtest}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "testreflection/${full_test_id} - ${test_desc}") - else() - set(test_name_full "testreflection/${full_test_id}") - endif() - - # Check if test should be skipped based on environment - should_skip_test("${subtest}" test_should_skip) - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - testreflection "${full_test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set basic properties - set(test_labels "testreflection") - set(test_disabled FALSE) - set(test_will_fail FALSE) - - # Check if test should be skipped - if(test_should_skip) - set(test_disabled TRUE) - set(test_labels "testreflection;skipped") - endif() - - # Check if test is expected to fail (xfail) - if(full_test_id IN_LIST xfail_list) - set(test_labels "testreflection;xfail") - set(test_will_fail TRUE) - endif() - - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test" - LABELS "${test_labels}" - DISABLED ${test_disabled} - WILL_FAIL ${test_will_fail} - ) - endif() - endforeach() - - # Check for 3-digit subtests - file(GLOB subtests_3d "${base_test}/[0-9][0-9][0-9]") - foreach(subtest ${subtests_3d}) - if(EXISTS "${subtest}/===") - get_filename_component(subtest_id "${subtest}" NAME) - set(full_test_id "${test_id}/${subtest_id}") - - # Read description from === file - file(READ "${subtest}/===" test_desc) - string(STRIP "${test_desc}" test_desc) - - # Build test name with description if available - if(test_desc) - set(test_name_full "testreflection/${full_test_id} - ${test_desc}") - else() - set(test_name_full "testreflection/${full_test_id}") - endif() - - # Check if test should be skipped based on environment - should_skip_test("${subtest}" test_should_skip) - - add_test( - NAME "${test_name_full}" - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/run-single-tap-test.sh - testreflection "${full_test_id}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test - ) - - # Set basic properties - set(test_labels "testreflection") - set(test_disabled FALSE) - set(test_will_fail FALSE) - - # Check if test should be skipped - if(test_should_skip) - set(test_disabled TRUE) - set(test_labels "testreflection;skipped") - endif() - - # Check if test is expected to fail (xfail) - if(full_test_id IN_LIST xfail_list) - set(test_labels "testreflection;xfail") - set(test_will_fail TRUE) - endif() - - set_tests_properties("${test_name_full}" PROPERTIES - ENVIRONMENT "TOP_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};TOP_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR};SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}/test;BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}/test" - LABELS "${test_labels}" - DISABLED ${test_disabled} - WILL_FAIL ${test_will_fail} - ) - endif() + set(full_test_id "${base_id}/${subtest_id}") + _register_reflection_test("${subtest}" "${full_test_id}" "${xfail_list}") endforeach() endforeach() endfunction() diff --git a/cmake/clang-windows-toolchain.cmake b/cmake/clang-windows-toolchain.cmake new file mode 100644 index 0000000..f243548 --- /dev/null +++ b/cmake/clang-windows-toolchain.cmake @@ -0,0 +1,26 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR AMD64) + +set(CMAKE_C_COMPILER clang) +set(CMAKE_CXX_COMPILER clang++) + +set(triple x86_64-windows-msvc) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) + +set(CMAKE_LINKER lld-link) +set(CMAKE_C_LINK_EXECUTABLE " -out: ") +set(CMAKE_CXX_LINK_EXECUTABLE " -out: ") + +set(CMAKE_C_CREATE_SHARED_LIBRARY " -dll -out: -implib: ") +set(CMAKE_CXX_CREATE_SHARED_LIBRARY " -dll -out: -implib: ") + +# Disable Unix-specific flags +set(CMAKE_C_COMPILE_OPTIONS_PIC "") +set(CMAKE_CXX_COMPILE_OPTIONS_PIC "") +set(CMAKE_SHARED_LIBRARY_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "") + +# Add -fuse-ld=lld to ensure clang uses lld +set(CMAKE_C_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_CXX_FLAGS_INIT "-fuse-ld=lld") diff --git a/cmake/generate-def.cmake b/cmake/generate-def.cmake new file mode 100644 index 0000000..a83911d --- /dev/null +++ b/cmake/generate-def.cmake @@ -0,0 +1,65 @@ +# generate-def.cmake +# Generates fyaml.def from libfyaml.h by extracting FY_EXPORT functions +# +# Usage: cmake -DINPUT_HEADER=path/to/libfyaml.h -DOUTPUT_DEF=path/to/fyaml.def -P generate-def.cmake + +if(NOT INPUT_HEADER) + message(FATAL_ERROR "INPUT_HEADER not specified") +endif() + +if(NOT OUTPUT_DEF) + message(FATAL_ERROR "OUTPUT_DEF not specified") +endif() + +# Functions declared with FY_EXPORT but not implemented as standalone functions +# (they may be inline, macros, or not implemented) +# These are determined by linker errors - if the symbol is unresolved, add it here +set(EXCLUDE_FUNCTIONS) + +# Read the header file +file(READ "${INPUT_HEADER}" HEADER_CONTENT) + +# Find all function names that have FY_EXPORT +# Pattern: function_name(...) followed by FY_EXPORT on same or next line +# There may be FY_FORMAT(...) or other attributes between function and FY_EXPORT +# We look for: word( ... ) whitespace [optional FY_FORMAT(...)] whitespace FY_EXPORT +string(REGEX MATCHALL "[a-z_][a-z0-9_]*[ \t]*\\([^)]*\\)[\r\n\t ]+(FY_FORMAT\\([^)]*\\)[\r\n\t ]+)?FY_EXPORT" MATCHES "${HEADER_CONTENT}") + +# Extract just the function names +set(FUNCTION_NAMES "") +foreach(MATCH ${MATCHES}) + # Extract function name (everything before the opening paren) + string(REGEX REPLACE "[ \t]*\\(.*" "" FUNC_NAME "${MATCH}") + list(APPEND FUNCTION_NAMES "${FUNC_NAME}") +endforeach() + +# Remove duplicates and sort +list(REMOVE_DUPLICATES FUNCTION_NAMES) +list(SORT FUNCTION_NAMES) + +# Remove excluded functions +foreach(EXCLUDE ${EXCLUDE_FUNCTIONS}) + list(REMOVE_ITEM FUNCTION_NAMES "${EXCLUDE}") +endforeach() + +# Generate the .def file content +set(DEF_CONTENT "; fyaml.def - Auto-generated module definition file for fyaml.dll +; Generated from libfyaml.h - DO NOT EDIT MANUALLY +; +; This file exports the public API symbols marked with FY_EXPORT. +; Regenerate with: cmake -DINPUT_HEADER=include/libfyaml.h -DOUTPUT_DEF=src/lib/fyaml.def -P cmake/generate-def.cmake + +LIBRARY fyaml +EXPORTS +") + +foreach(FUNC ${FUNCTION_NAMES}) + string(APPEND DEF_CONTENT " ${FUNC}\n") +endforeach() + +# Write the output file +file(WRITE "${OUTPUT_DEF}" "${DEF_CONTENT}") + +# Report +list(LENGTH FUNCTION_NAMES NUM_FUNCS) +message(STATUS "Generated ${OUTPUT_DEF} with ${NUM_FUNCS} exported functions") diff --git a/cmake/jsontestsuite-tests.cmake b/cmake/jsontestsuite-tests.cmake deleted file mode 100644 index 3737900..0000000 --- a/cmake/jsontestsuite-tests.cmake +++ /dev/null @@ -1,342 +0,0 @@ -# Auto-generated file - do not edit manually -# Generated by cmake/generate-jsontestsuite-list.sh -# -# This file contains lists of tests from the JSON test suite -# It allows registering individual tests without requiring the test data -# to be downloaded first. - -# Test suite checkout: d64aefb55228d9584d3e5b2433f720ea8fd00c82 -# Generated on: 2025-10-24 08:05:59 UTC -# Pass tests: 95 -# Fail tests: 188 -# Impl tests: 35 - -# Tests expected to pass (y_*.json) -set(JSONTESTSUITE_PASS_TESTS - "y_array_arraysWithSpaces.json" - "y_array_empty.json" - "y_array_empty-string.json" - "y_array_ending_with_newline.json" - "y_array_false.json" - "y_array_heterogeneous.json" - "y_array_null.json" - "y_array_with_1_and_newline.json" - "y_array_with_leading_space.json" - "y_array_with_several_null.json" - "y_array_with_trailing_space.json" - "y_number_0e+1.json" - "y_number_0e1.json" - "y_number_after_space.json" - "y_number_double_close_to_zero.json" - "y_number_int_with_exp.json" - "y_number.json" - "y_number_minus_zero.json" - "y_number_negative_int.json" - "y_number_negative_one.json" - "y_number_negative_zero.json" - "y_number_real_capital_e.json" - "y_number_real_capital_e_neg_exp.json" - "y_number_real_capital_e_pos_exp.json" - "y_number_real_exponent.json" - "y_number_real_fraction_exponent.json" - "y_number_real_neg_exp.json" - "y_number_real_pos_exponent.json" - "y_number_simple_int.json" - "y_number_simple_real.json" - "y_object_basic.json" - "y_object_duplicated_key_and_value.json" - "y_object_duplicated_key.json" - "y_object_empty.json" - "y_object_empty_key.json" - "y_object_escaped_null_in_key.json" - "y_object_extreme_numbers.json" - "y_object.json" - "y_object_long_strings.json" - "y_object_simple.json" - "y_object_string_unicode.json" - "y_object_with_newlines.json" - "y_string_1_2_3_bytes_UTF-8_sequences.json" - "y_string_accepted_surrogate_pair.json" - "y_string_accepted_surrogate_pairs.json" - "y_string_allowed_escapes.json" - "y_string_backslash_and_u_escaped_zero.json" - "y_string_backslash_doublequotes.json" - "y_string_comments.json" - "y_string_double_escape_a.json" - "y_string_double_escape_n.json" - "y_string_escaped_control_character.json" - "y_string_escaped_noncharacter.json" - "y_string_in_array.json" - "y_string_in_array_with_leading_space.json" - "y_string_last_surrogates_1_and_2.json" - "y_string_nbsp_uescaped.json" - "y_string_nonCharacterInUTF-8_U+10FFFF.json" - "y_string_nonCharacterInUTF-8_U+FFFF.json" - "y_string_null_escape.json" - "y_string_one-byte-utf-8.json" - "y_string_pi.json" - "y_string_reservedCharacterInUTF-8_U+1BFFF.json" - "y_string_simple_ascii.json" - "y_string_space.json" - "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json" - "y_string_three-byte-utf-8.json" - "y_string_two-byte-utf-8.json" - "y_string_u+2028_line_sep.json" - "y_string_u+2029_par_sep.json" - "y_string_uescaped_newline.json" - "y_string_uEscape.json" - "y_string_unescaped_char_delete.json" - "y_string_unicode_2.json" - "y_string_unicodeEscapedBackslash.json" - "y_string_unicode_escaped_double_quote.json" - "y_string_unicode.json" - "y_string_unicode_U+10FFFE_nonchar.json" - "y_string_unicode_U+1FFFE_nonchar.json" - "y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json" - "y_string_unicode_U+2064_invisible_plus.json" - "y_string_unicode_U+FDD0_nonchar.json" - "y_string_unicode_U+FFFE_nonchar.json" - "y_string_utf8.json" - "y_string_with_del_character.json" - "y_structure_lonely_false.json" - "y_structure_lonely_int.json" - "y_structure_lonely_negative_real.json" - "y_structure_lonely_null.json" - "y_structure_lonely_string.json" - "y_structure_lonely_true.json" - "y_structure_string_empty.json" - "y_structure_trailing_newline.json" - "y_structure_true_in_array.json" - "y_structure_whitespace_array.json" -) - -# Tests expected to fail (n_*.json) -set(JSONTESTSUITE_FAIL_TESTS - "n_array_1_true_without_comma.json" - "n_array_a_invalid_utf8.json" - "n_array_colon_instead_of_comma.json" - "n_array_comma_after_close.json" - "n_array_comma_and_number.json" - "n_array_double_comma.json" - "n_array_double_extra_comma.json" - "n_array_extra_close.json" - "n_array_extra_comma.json" - "n_array_incomplete_invalid_value.json" - "n_array_incomplete.json" - "n_array_inner_array_no_comma.json" - "n_array_invalid_utf8.json" - "n_array_items_separated_by_semicolon.json" - "n_array_just_comma.json" - "n_array_just_minus.json" - "n_array_missing_value.json" - "n_array_newlines_unclosed.json" - "n_array_number_and_comma.json" - "n_array_number_and_several_commas.json" - "n_array_spaces_vertical_tab_formfeed.json" - "n_array_star_inside.json" - "n_array_unclosed.json" - "n_array_unclosed_trailing_comma.json" - "n_array_unclosed_with_new_lines.json" - "n_array_unclosed_with_object_inside.json" - "n_incomplete_false.json" - "n_incomplete_null.json" - "n_incomplete_true.json" - "n_multidigit_number_then_00.json" - "n_number_0.1.2.json" - "n_number_-01.json" - "n_number_0.3e+.json" - "n_number_0.3e.json" - "n_number_0_capital_E+.json" - "n_number_0_capital_E.json" - "n_number_0.e1.json" - "n_number_0e+.json" - "n_number_0e.json" - "n_number_1_000.json" - "n_number_1.0e+.json" - "n_number_1.0e-.json" - "n_number_1.0e.json" - "n_number_-1.0..json" - "n_number_1eE2.json" - "n_number_+1.json" - "n_number_.-1.json" - "n_number_2.e+3.json" - "n_number_2.e-3.json" - "n_number_2.e3.json" - "n_number_.2e-3.json" - "n_number_-2..json" - "n_number_9.e+.json" - "n_number_expression.json" - "n_number_hex_1_digit.json" - "n_number_hex_2_digits.json" - "n_number_infinity.json" - "n_number_+Inf.json" - "n_number_Inf.json" - "n_number_invalid+-.json" - "n_number_invalid-negative-real.json" - "n_number_invalid-utf-8-in-bigger-int.json" - "n_number_invalid-utf-8-in-exponent.json" - "n_number_invalid-utf-8-in-int.json" - "n_number_++.json" - "n_number_minus_infinity.json" - "n_number_minus_sign_with_trailing_garbage.json" - "n_number_minus_space_1.json" - "n_number_-NaN.json" - "n_number_NaN.json" - "n_number_neg_int_starting_with_zero.json" - "n_number_neg_real_without_int_part.json" - "n_number_neg_with_garbage_at_end.json" - "n_number_real_garbage_after_e.json" - "n_number_real_with_invalid_utf8_after_e.json" - "n_number_real_without_fractional_part.json" - "n_number_starting_with_dot.json" - "n_number_U+FF11_fullwidth_digit_one.json" - "n_number_with_alpha_char.json" - "n_number_with_alpha.json" - "n_number_with_leading_zero.json" - "n_object_bad_value.json" - "n_object_bracket_key.json" - "n_object_comma_instead_of_colon.json" - "n_object_double_colon.json" - "n_object_emoji.json" - "n_object_garbage_at_end.json" - "n_object_key_with_single_quotes.json" - "n_object_lone_continuation_byte_in_key_and_trailing_comma.json" - "n_object_missing_colon.json" - "n_object_missing_key.json" - "n_object_missing_semicolon.json" - "n_object_missing_value.json" - "n_object_no-colon.json" - "n_object_non_string_key_but_huge_number_instead.json" - "n_object_non_string_key.json" - "n_object_repeated_null_null.json" - "n_object_several_trailing_commas.json" - "n_object_single_quote.json" - "n_object_trailing_comma.json" - "n_object_trailing_comment.json" - "n_object_trailing_comment_open.json" - "n_object_trailing_comment_slash_open_incomplete.json" - "n_object_trailing_comment_slash_open.json" - "n_object_two_commas_in_a_row.json" - "n_object_unquoted_key.json" - "n_object_unterminated-value.json" - "n_object_with_single_string.json" - "n_object_with_trailing_garbage.json" - "n_single_space.json" - "n_string_1_surrogate_then_escape.json" - "n_string_1_surrogate_then_escape_u1.json" - "n_string_1_surrogate_then_escape_u1x.json" - "n_string_1_surrogate_then_escape_u.json" - "n_string_accentuated_char_no_quotes.json" - "n_string_backslash_00.json" - "n_string_escaped_backslash_bad.json" - "n_string_escaped_ctrl_char_tab.json" - "n_string_escaped_emoji.json" - "n_string_escape_x.json" - "n_string_incomplete_escaped_character.json" - "n_string_incomplete_escape.json" - "n_string_incomplete_surrogate_escape_invalid.json" - "n_string_incomplete_surrogate.json" - "n_string_invalid_backslash_esc.json" - "n_string_invalid_unicode_escape.json" - "n_string_invalid_utf8_after_escape.json" - "n_string_invalid-utf-8-in-escape.json" - "n_string_leading_uescaped_thinspace.json" - "n_string_no_quotes_with_bad_escape.json" - "n_string_single_doublequote.json" - "n_string_single_quote.json" - "n_string_single_string_no_double_quotes.json" - "n_string_start_escape_unclosed.json" - "n_string_unescaped_ctrl_char.json" - "n_string_unescaped_newline.json" - "n_string_unescaped_tab.json" - "n_string_unicode_CapitalU.json" - "n_string_with_trailing_garbage.json" - "n_structure_100000_opening_arrays.json" - "n_structure_angle_bracket_..json" - "n_structure_angle_bracket_null.json" - "n_structure_array_trailing_garbage.json" - "n_structure_array_with_extra_array_close.json" - "n_structure_array_with_unclosed_string.json" - "n_structure_ascii-unicode-identifier.json" - "n_structure_capitalized_True.json" - "n_structure_close_unopened_array.json" - "n_structure_comma_instead_of_closing_brace.json" - "n_structure_double_array.json" - "n_structure_end_array.json" - "n_structure_incomplete_UTF8_BOM.json" - "n_structure_lone-invalid-utf-8.json" - "n_structure_lone-open-bracket.json" - "n_structure_no_data.json" - "n_structure_null-byte-outside-string.json" - "n_structure_number_with_trailing_garbage.json" - "n_structure_object_followed_by_closing_object.json" - "n_structure_object_unclosed_no_value.json" - "n_structure_object_with_comment.json" - "n_structure_object_with_trailing_garbage.json" - "n_structure_open_array_apostrophe.json" - "n_structure_open_array_comma.json" - "n_structure_open_array_object.json" - "n_structure_open_array_open_object.json" - "n_structure_open_array_open_string.json" - "n_structure_open_array_string.json" - "n_structure_open_object_close_array.json" - "n_structure_open_object_comma.json" - "n_structure_open_object.json" - "n_structure_open_object_open_array.json" - "n_structure_open_object_open_string.json" - "n_structure_open_object_string_with_apostrophes.json" - "n_structure_open_open.json" - "n_structure_single_eacute.json" - "n_structure_single_star.json" - "n_structure_trailing_#.json" - "n_structure_U+2060_word_joined.json" - "n_structure_uescaped_LF_before_string.json" - "n_structure_unclosed_array.json" - "n_structure_unclosed_array_partial_null.json" - "n_structure_unclosed_array_unfinished_false.json" - "n_structure_unclosed_array_unfinished_true.json" - "n_structure_unclosed_object.json" - "n_structure_unicode-identifier.json" - "n_structure_UTF8_BOM_no_data.json" - "n_structure_whitespace_formfeed.json" - "n_structure_whitespace_U+2060_word_joiner.json" -) - -# Implementation-defined tests (i_*.json) -set(JSONTESTSUITE_IMPL_TESTS - "i_number_double_huge_neg_exp.json" - "i_number_huge_exp.json" - "i_number_neg_int_huge_exp.json" - "i_number_pos_double_huge_exp.json" - "i_number_real_neg_overflow.json" - "i_number_real_pos_overflow.json" - "i_number_real_underflow.json" - "i_number_too_big_neg_int.json" - "i_number_too_big_pos_int.json" - "i_number_very_big_negative_int.json" - "i_object_key_lone_2nd_surrogate.json" - "i_string_1st_surrogate_but_2nd_missing.json" - "i_string_1st_valid_surrogate_2nd_invalid.json" - "i_string_incomplete_surrogate_and_escape_valid.json" - "i_string_incomplete_surrogate_pair.json" - "i_string_incomplete_surrogates_escape_valid.json" - "i_string_invalid_lonely_surrogate.json" - "i_string_invalid_surrogate.json" - "i_string_invalid_utf-8.json" - "i_string_inverted_surrogates_U+1D11E.json" - "i_string_iso_latin_1.json" - "i_string_lone_second_surrogate.json" - "i_string_lone_utf8_continuation_byte.json" - "i_string_not_in_unicode_range.json" - "i_string_overlong_sequence_2_bytes.json" - "i_string_overlong_sequence_6_bytes.json" - "i_string_overlong_sequence_6_bytes_null.json" - "i_string_truncated-utf-8.json" - "i_string_utf16BE_no_BOM.json" - "i_string_utf16LE_no_BOM.json" - "i_string_UTF-16LE_with_BOM.json" - "i_string_UTF-8_invalid_sequence.json" - "i_string_UTF8_surrogate_U+D800.json" - "i_structure_500_nested_arrays.json" - "i_structure_UTF-8_BOM_empty_object.json" -) diff --git a/cmake/run-single-tap-test.sh b/cmake/run-single-tap-test.sh index 5382f56..211b3d7 100755 --- a/cmake/run-single-tap-test.sh +++ b/cmake/run-single-tap-test.sh @@ -1,26 +1,67 @@ #!/usr/bin/env bash # Wrapper script to run a single TAP subtest -# Usage: run-single-tap-test.sh - -test_suite="$1" -test_id="$2" +# Usage: run-single-tap-test.sh + +FY_TOOL="$1" +test_suite="$2" +test_id="$3" + +if [ x"$FY_TOOL" = x -o x"$TEST_DIR" = x -o x"$test_suite" = x -o x"$test_id" = x ]; then + echo "Error: FY_TOOL, TEST_DIR, test_suite, test_id must exist" +fi + +function is_windows_bash() { + case "$OSTYPE" in + msys*|cygwin*) return 0 ;; + *) return 1 ;; + esac +} + +# convert to proper posix paths +if is_windows_bash; then + FY_TOOL=`cygpath $FY_TOOL` + TEST_DIR=`cygpath $TEST_DIR` + YAML_TEST_SUITE=`cygpath $YAML_TEST_SUITE` + JSON_TEST_SUITE=`cygpath $JSON_TEST_SUITE` +fi + +# if not given in the environment try to adjust +if [ x"$YAML_TEST_SUITE" = x ]; then + YAML_TEST_SUITE="${TEST_DIR}/test-suite-data" +fi + +if [ x"$JSON_TEST_SUITE" = x ]; then + JSON_TEST_SUITE="${TEST_DIR}/json-test-suite-data" +fi + +# Validate tool exists +if [ ! -x "$FY_TOOL" ]; then + echo "Error: fy-tool not found or not executable: $FY_TOOL" >&2 + exit 1 +fi + +# Validate TEST_DIR exists +if [ ! -d "$TEST_DIR" ]; then + echo "Error: TEST_DIR not found: $TEST_DIR" >&2 + exit 1 +fi case "$test_suite" in testerrors) - dir="${SRCDIR}/test-errors/${test_id}" + dir="${TEST_DIR}/test-errors/${test_id}" desctxt=$(cat 2>/dev/null "$dir/===") t=$(mktemp) res="not ok" pass_yaml=0 - ${TOP_BUILDDIR}/src/fy-tool --dump -r --no-streaming "$dir/in.yaml" >"$t" 2>&1 + ${FY_TOOL} --dump -r --no-streaming "$dir/in.yaml" >"$t" 2>&1 if [ $? -eq 0 ]; then pass_yaml=1 fi - errmsg=$(cat "$t" | head -n1 | sed -e 's/^[^:]*//') + errmsg=$(cat "$t" | head -n1 | sed -e 's/^.*\(:[0-9]\{1,\}:[0-9]\{1,\}:\)/\1/') echo "errmsg: $errmsg" >&2 # Replace with error message @@ -33,7 +74,7 @@ case "$test_suite" in # diff is pointless under valgrind if [ "x$USE_VALGRIND" == "x" ]; then diff_err=0 - diff -u "$dir/test.error" "$t" + diff -uw "$dir/test.error" "$t" if [ $? -eq 0 ]; then res="ok" else @@ -56,7 +97,7 @@ case "$test_suite" in ;; testemitter) - f="${SRCDIR}/emitter-examples/${test_id}" + f="${TEST_DIR}/emitter-examples/${test_id}" t1=$(mktemp) t2=$(mktemp) @@ -64,10 +105,10 @@ case "$test_suite" in res="not ok" pass_parse=0 - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers "$f" >"$t1" + ${FY_TOOL} --testsuite --disable-flow-markers "$f" >"$t1" if [ $? -eq 0 ]; then - ${TOP_BUILDDIR}/src/fy-tool --dump "$f" | \ - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers - >"$t2" + ${FY_TOOL} --dump "$f" | \ + ${FY_TOOL} --testsuite --disable-flow-markers - >"$t2" if [ $? -eq 0 ]; then pass_parse=1 fi @@ -96,7 +137,7 @@ case "$test_suite" in ;; testemitter-streaming) - f="${SRCDIR}/emitter-examples/${test_id}" + f="${TEST_DIR}/emitter-examples/${test_id}" t1=$(mktemp) t2=$(mktemp) @@ -104,10 +145,10 @@ case "$test_suite" in res="not ok" pass_parse=0 - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers "$f" >"$t1" + ${FY_TOOL} --testsuite --disable-flow-markers "$f" >"$t1" if [ $? -eq 0 ]; then - ${TOP_BUILDDIR}/src/fy-tool --dump --streaming "$f" | \ - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers - >"$t2" + ${FY_TOOL} --dump --streaming "$f" | \ + ${FY_TOOL} --testsuite --disable-flow-markers - >"$t2" if [ $? -eq 0 ]; then pass_parse=1 fi @@ -136,7 +177,7 @@ case "$test_suite" in ;; testemitter-restreaming) - f="${SRCDIR}/emitter-examples/${test_id}" + f="${TEST_DIR}/emitter-examples/${test_id}" t1=$(mktemp) t2=$(mktemp) @@ -144,10 +185,10 @@ case "$test_suite" in res="not ok" pass_parse=0 - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers "$f" >"$t1" + ${FY_TOOL} --testsuite --disable-flow-markers "$f" >"$t1" if [ $? -eq 0 ]; then - ${TOP_BUILDDIR}/src/fy-tool --dump --streaming --recreating "$f" | \ - ${TOP_BUILDDIR}/src/fy-tool --testsuite --disable-flow-markers - >"$t2" + ${FY_TOOL} --dump --streaming --recreating "$f" | \ + ${FY_TOOL} --testsuite --disable-flow-markers - >"$t2" if [ $? -eq 0 ]; then pass_parse=1 fi @@ -176,7 +217,7 @@ case "$test_suite" in ;; testsuite|testsuite-json|testsuite-resolution) - tst="test-suite-data/${test_id}" + tst="${YAML_TEST_SUITE}/${test_id}" desctxt=$(cat 2>/dev/null "$tst/===") t=$(mktemp) @@ -184,7 +225,7 @@ case "$test_suite" in res="not ok" pass_yaml=0 - ${TOP_BUILDDIR}/src/fy-tool --testsuite "$tst/in.yaml" >"$t" + ${FY_TOOL} --testsuite "$tst/in.yaml" >"$t" if [ $? -eq 0 ]; then pass_yaml=1 fi @@ -222,7 +263,7 @@ case "$test_suite" in ;; testsuite-evstream) - tst="test-suite-data/${test_id}" + tst="${YAML_TEST_SUITE}/${test_id}" desctxt=$(cat 2>/dev/null "$tst/===") # Skip tests that are expected to fail @@ -245,7 +286,7 @@ case "$test_suite" in res="not ok" # run the test using document-event-stream - ${TOP_BUILDDIR}/src/fy-tool --testsuite --document-event-stream "$tst/in.yaml" >"$t" 2>/dev/null + ${FY_TOOL} --testsuite --document-event-stream "$tst/in.yaml" >"$t" 2>/dev/null if [ $? -eq 0 ]; then diff -u "$tst/test.event" "$t" if [ $? -eq 0 ]; then @@ -266,13 +307,13 @@ case "$test_suite" in jsontestsuite) tf="$test_id" - f="json-test-suite-data/test_parsing/${tf}" + f="${JSON_TEST_SUITE}/test_parsing/${tf}" # Determine expected result based on prefix case "$tf" in y_*) # Expected to pass - ${TOP_BUILDDIR}/src/fy-tool --testsuite --streaming "$f" >/dev/null 2>&1 + ${FY_TOOL} --testsuite --streaming "$f" >/dev/null 2>&1 if [ $? -eq 0 ]; then res="ok" else @@ -281,7 +322,7 @@ case "$test_suite" in ;; n_*) # Expected to fail - ${TOP_BUILDDIR}/src/fy-tool --testsuite --streaming "$f" >/dev/null 2>&1 + ${FY_TOOL} --testsuite --streaming "$f" >/dev/null 2>&1 if [ $? -eq 0 ]; then res="not ok" else @@ -290,7 +331,7 @@ case "$test_suite" in ;; i_*) # Implementation defined - we'll accept either result as ok - ${TOP_BUILDDIR}/src/fy-tool --testsuite --streaming "$f" >/dev/null 2>&1 + ${FY_TOOL} --testsuite --streaming "$f" >/dev/null 2>&1 res="ok" ;; *) diff --git a/cmake/setup-yaml-test-suite.cmake b/cmake/setup-yaml-test-suite.cmake deleted file mode 100644 index c8c7f2b..0000000 --- a/cmake/setup-yaml-test-suite.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# Script to set up test suite data in an idempotent way - -if(NOT DEFINED REPO_NAME) - set(REPO_NAME "test-suite-data") -endif() - -set(REPO_DIR "${TEST_DIR}/${REPO_NAME}") - -# Check if the repository already exists -if(EXISTS "${REPO_DIR}/.git") - message(STATUS "Repository ${REPO_NAME} already exists, updating...") - execute_process( - COMMAND ${GIT_EXECUTABLE} fetch -q origin - WORKING_DIRECTORY ${REPO_DIR} - RESULT_VARIABLE GIT_FETCH_RESULT - ) - if(NOT GIT_FETCH_RESULT EQUAL 0) - message(WARNING "Failed to fetch updates for ${REPO_NAME}") - endif() -else() - message(STATUS "Cloning ${REPO_NAME}...") - execute_process( - COMMAND ${GIT_EXECUTABLE} clone -q "${TESTSUITEURL}" ${REPO_NAME} - WORKING_DIRECTORY ${TEST_DIR} - RESULT_VARIABLE GIT_CLONE_RESULT - ) - if(NOT GIT_CLONE_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to clone ${REPO_NAME}") - endif() -endif() - -# Checkout the specific commit -execute_process( - COMMAND ${GIT_EXECUTABLE} checkout -q --detach ${TESTSUITECHECKOUT} - WORKING_DIRECTORY ${REPO_DIR} - RESULT_VARIABLE GIT_CHECKOUT_RESULT -) -if(NOT GIT_CHECKOUT_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to checkout ${TESTSUITECHECKOUT} in ${REPO_NAME}") -endif() - -message(STATUS "Test suite ${REPO_NAME} set up successfully") diff --git a/cmake/testsuite-tests.cmake b/cmake/testsuite-tests.cmake deleted file mode 100644 index 284aead..0000000 --- a/cmake/testsuite-tests.cmake +++ /dev/null @@ -1,771 +0,0 @@ -# Auto-generated file - do not edit manually -# Generated by cmake/generate-testsuite-list.sh -# -# This file contains lists of tests from the YAML test suite -# It allows registering individual tests without requiring the test data -# to be downloaded first. - -# Test suite checkout: 6e6c296ae9c9d2d5c4134b4b64d01b29ac19ff6f -# Generated on: 2025-10-24 08:04:38 UTC -# Base tests: 350 - -# List of base test IDs -set(TESTSUITE_BASE_TESTS - 229Q - 236B - 26DV - 27NA - 2AUY - 2CMS - 2EBW - 2G84 - 2JQS - 2LFX - 2SXE - 2XXW - 33X3 - 35KP - 36F6 - 3ALJ - 3GZX - 3HFZ - 3MYT - 3R3P - 3RLN - 3UYS - 4ABK - 4CQQ - 4EJS - 4FJ6 - 4GC6 - 4H7K - 4HVU - 4JVG - 4MUZ - 4Q9F - 4QFQ - 4RWC - 4UYU - 4V8U - 4WA9 - 4ZYM - 52DL - 54T7 - 55WF - 565N - 57H4 - 58MP - 5BVJ - 5C5M - 5GBF - 5KJE - 5LLU - 5MUD - 5NYZ - 5T43 - 5TRB - 5TYM - 5U3A - 5WE3 - 62EZ - 652Z - 65WH - 6BCT - 6BFJ - 6CA3 - 6CK3 - 6FWR - 6H3V - 6HB6 - 6JQW - 6JTT - 6JWB - 6KGN - 6LVF - 6M2F - 6PBE - 6S55 - 6SLA - 6VJK - 6WLZ - 6WPF - 6XDY - 6ZKB - 735Y - 74H7 - 753E - 7A4E - 7BMT - 7BUB - 7FWL - 7LBH - 7MNF - 7T8X - 7TMG - 7W2P - 7Z25 - 7ZZ5 - 82AN - 87E4 - 8CWC - 8G76 - 8KB6 - 8MK2 - 8QBE - 8UDB - 8XDJ - 8XYN - 93JH - 93WF - 96L6 - 96NN - 98YD - 9BXH - 9C9N - 9CWY - 9DXL - 9FMG - 9HCY - 9J7A - 9JBA - 9KAX - 9KBC - 9MAG - 9MMA - 9MMW - 9MQT - 9SA2 - 9SHH - 9TFX - 9U5K - 9WXW - 9YRD - A2M4 - A6F9 - A984 - AB8U - AVM7 - AZ63 - AZW3 - B3HG - B63P - BD7L - BEC7 - BF9H - BS4K - BU8L - C2DT - C2SP - C4HZ - CC74 - CFD4 - CML9 - CN3R - CPZ3 - CQ3W - CT4Q - CTN5 - CUP7 - CVW2 - CXX2 - D49Q - D83L - D88J - D9TU - DBG4 - DC7X - DE56 - DFF7 - DHP8 - DK3J - DK4H - DK95 - DMG6 - DWX9 - E76Z - EB22 - EHF6 - EW3V - EX5H - EXG3 - F2C7 - F3CP - F6MC - F8F9 - FBC9 - FH7J - FP8R - FQ7F - FRK4 - FTA2 - FUP4 - G4RS - G5U8 - G7JE - G992 - G9HC - GDY7 - GH63 - GT5M - H2RW - H3Z8 - H7J7 - H7TQ - HM87 - HMK4 - HMQ5 - HRE5 - HS5T - HU3P - HWV9 - J3BT - J5UC - J7PZ - J7VC - J9HZ - JEF9 - JHB9 - JKF3 - JQ4R - JR7V - JS2J - JTV5 - JY7Z - K3WX - K4SU - K527 - K54U - K858 - KH5V - KK5P - KMK3 - KS4U - KSS4 - L24T - L383 - L94M - L9U5 - LE5A - LHL4 - LP6E - LQZ7 - LX3P - M29M - M2N8 - M5C3 - M5DY - M6YH - M7A3 - M7NX - M9B4 - MJS9 - MUS6 - MXS3 - MYW6 - MZX3 - N4JP - N782 - NAT4 - NB6Z - NHX8 - NJ66 - NKF9 - NP9H - P2AD - P2EQ - P76L - P94K - PBJ2 - PRH3 - PUW8 - PW8X - Q4CL - Q5MG - Q88A - Q8AD - Q9WF - QB6E - QF4Y - QLJ7 - QT73 - R4YG - R52L - RHX7 - RLU9 - RR7F - RTP8 - RXY3 - RZP5 - RZT7 - S3PD - S4GJ - S4JQ - S4T7 - S7BG - S98Z - S9E8 - SBG9 - SF5V - SKE5 - SM9W - SR86 - SSW6 - SU5Z - SU74 - SY6V - SYW4 - T26H - T4YY - T5N4 - T833 - TD5N - TE2A - TL85 - TS54 - U3C3 - U3XV - U44R - U99R - U9NS - UDM2 - UDR7 - UGM3 - UKK6 - UT92 - UV7Q - V55R - V9D5 - VJP3 - W42U - W4TN - W5VH - W9L4 - WZ62 - X38W - X4QW - X8DW - XLQ9 - XV9V - XW4D - Y2GN - Y79Y - YD5X - YJV2 - Z67P - Z9M4 - ZCZ6 - ZF4X - ZH7C - ZK9H - ZL4Z - ZVH3 - ZWK4 - ZXT5 -) - -# List of all tests (including subtests) -# Format: BASE or BASE/SUBTEST -set(TESTSUITE_ALL_TESTS - "229Q" - "236B" - "26DV" - "27NA" - "2AUY" - "2CMS" - "2EBW" - "2G84/00" - "2G84/01" - "2G84/02" - "2G84/03" - "2JQS" - "2LFX" - "2SXE" - "2XXW" - "33X3" - "35KP" - "36F6" - "3ALJ" - "3GZX" - "3HFZ" - "3MYT" - "3R3P" - "3RLN/00" - "3RLN/01" - "3RLN/02" - "3RLN/03" - "3RLN/04" - "3RLN/05" - "3UYS" - "4ABK" - "4CQQ" - "4EJS" - "4FJ6" - "4GC6" - "4H7K" - "4HVU" - "4JVG" - "4MUZ/00" - "4MUZ/01" - "4MUZ/02" - "4Q9F" - "4QFQ" - "4RWC" - "4UYU" - "4V8U" - "4WA9" - "4ZYM" - "52DL" - "54T7" - "55WF" - "565N" - "57H4" - "58MP" - "5BVJ" - "5C5M" - "5GBF" - "5KJE" - "5LLU" - "5MUD" - "5NYZ" - "5T43" - "5TRB" - "5TYM" - "5U3A" - "5WE3" - "62EZ" - "652Z" - "65WH" - "6BCT" - "6BFJ" - "6CA3" - "6CK3" - "6FWR" - "6H3V" - "6HB6" - "6JQW" - "6JTT" - "6JWB" - "6KGN" - "6LVF" - "6M2F" - "6PBE" - "6S55" - "6SLA" - "6VJK" - "6WLZ" - "6WPF" - "6XDY" - "6ZKB" - "735Y" - "74H7" - "753E" - "7A4E" - "7BMT" - "7BUB" - "7FWL" - "7LBH" - "7MNF" - "7T8X" - "7TMG" - "7W2P" - "7Z25" - "7ZZ5" - "82AN" - "87E4" - "8CWC" - "8G76" - "8KB6" - "8MK2" - "8QBE" - "8UDB" - "8XDJ" - "8XYN" - "93JH" - "93WF" - "96L6" - "96NN/00" - "96NN/01" - "98YD" - "9BXH" - "9C9N" - "9CWY" - "9DXL" - "9FMG" - "9HCY" - "9J7A" - "9JBA" - "9KAX" - "9KBC" - "9MAG" - "9MMA" - "9MMW" - "9MQT/00" - "9MQT/01" - "9SA2" - "9SHH" - "9TFX" - "9U5K" - "9WXW" - "9YRD" - "A2M4" - "A6F9" - "A984" - "AB8U" - "AVM7" - "AZ63" - "AZW3" - "B3HG" - "B63P" - "BD7L" - "BEC7" - "BF9H" - "BS4K" - "BU8L" - "C2DT" - "C2SP" - "C4HZ" - "CC74" - "CFD4" - "CML9" - "CN3R" - "CPZ3" - "CQ3W" - "CT4Q" - "CTN5" - "CUP7" - "CVW2" - "CXX2" - "D49Q" - "D83L" - "D88J" - "D9TU" - "DBG4" - "DC7X" - "DE56/00" - "DE56/01" - "DE56/02" - "DE56/03" - "DE56/04" - "DE56/05" - "DFF7" - "DHP8" - "DK3J" - "DK4H" - "DK95/00" - "DK95/01" - "DK95/02" - "DK95/03" - "DK95/04" - "DK95/05" - "DK95/06" - "DK95/07" - "DK95/08" - "DMG6" - "DWX9" - "E76Z" - "EB22" - "EHF6" - "EW3V" - "EX5H" - "EXG3" - "F2C7" - "F3CP" - "F6MC" - "F8F9" - "FBC9" - "FH7J" - "FP8R" - "FQ7F" - "FRK4" - "FTA2" - "FUP4" - "G4RS" - "G5U8" - "G7JE" - "G992" - "G9HC" - "GDY7" - "GH63" - "GT5M" - "H2RW" - "H3Z8" - "H7J7" - "H7TQ" - "HM87/00" - "HM87/01" - "HMK4" - "HMQ5" - "HRE5" - "HS5T" - "HU3P" - "HWV9" - "J3BT" - "J5UC" - "J7PZ" - "J7VC" - "J9HZ" - "JEF9/00" - "JEF9/01" - "JEF9/02" - "JHB9" - "JKF3" - "JQ4R" - "JR7V" - "JS2J" - "JTV5" - "JY7Z" - "K3WX" - "K4SU" - "K527" - "K54U" - "K858" - "KH5V/00" - "KH5V/01" - "KH5V/02" - "KK5P" - "KMK3" - "KS4U" - "KSS4" - "L24T/00" - "L24T/01" - "L383" - "L94M" - "L9U5" - "LE5A" - "LHL4" - "LP6E" - "LQZ7" - "LX3P" - "M29M" - "M2N8/00" - "M2N8/01" - "M5C3" - "M5DY" - "M6YH" - "M7A3" - "M7NX" - "M9B4" - "MJS9" - "MUS6/00" - "MUS6/01" - "MUS6/02" - "MUS6/03" - "MUS6/04" - "MUS6/05" - "MUS6/06" - "MXS3" - "MYW6" - "MZX3" - "N4JP" - "N782" - "NAT4" - "NB6Z" - "NHX8" - "NJ66" - "NKF9" - "NP9H" - "P2AD" - "P2EQ" - "P76L" - "P94K" - "PBJ2" - "PRH3" - "PUW8" - "PW8X" - "Q4CL" - "Q5MG" - "Q88A" - "Q8AD" - "Q9WF" - "QB6E" - "QF4Y" - "QLJ7" - "QT73" - "R4YG" - "R52L" - "RHX7" - "RLU9" - "RR7F" - "RTP8" - "RXY3" - "RZP5" - "RZT7" - "S3PD" - "S4GJ" - "S4JQ" - "S4T7" - "S7BG" - "S98Z" - "S9E8" - "SBG9" - "SF5V" - "SKE5" - "SM9W/00" - "SM9W/01" - "SR86" - "SSW6" - "SU5Z" - "SU74" - "SY6V" - "SYW4" - "T26H" - "T4YY" - "T5N4" - "T833" - "TD5N" - "TE2A" - "TL85" - "TS54" - "U3C3" - "U3XV" - "U44R" - "U99R" - "U9NS" - "UDM2" - "UDR7" - "UGM3" - "UKK6/00" - "UKK6/01" - "UKK6/02" - "UT92" - "UV7Q" - "V55R" - "V9D5" - "VJP3/00" - "VJP3/01" - "W42U" - "W4TN" - "W5VH" - "W9L4" - "WZ62" - "X38W" - "X4QW" - "X8DW" - "XLQ9" - "XV9V" - "XW4D" - "Y2GN" - "Y79Y/000" - "Y79Y/001" - "Y79Y/002" - "Y79Y/003" - "Y79Y/004" - "Y79Y/005" - "Y79Y/006" - "Y79Y/007" - "Y79Y/008" - "Y79Y/009" - "Y79Y/010" - "YD5X" - "YJV2" - "Z67P" - "Z9M4" - "ZCZ6" - "ZF4X" - "ZH7C" - "ZK9H" - "ZL4Z" - "ZVH3" - "ZWK4" - "ZXT5" -) diff --git a/doc/Makefile.am b/doc/Makefile.am index 9902908..170aef7 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -43,9 +43,11 @@ maintainer-clean-local: @rm -rf Makefile.in EXTRA_DIST = \ + canned-man/fy-tool.1 \ conf.py \ + include \ index.rst \ intro.rst \ libfyaml.rst \ man/fy-tool.rst \ - canned-man/fy-tool.1 + windows-support.txt diff --git a/doc/windows-support.txt b/doc/windows-support.txt new file mode 100644 index 0000000..8820c98 --- /dev/null +++ b/doc/windows-support.txt @@ -0,0 +1,46 @@ +You need to download msvc and the winsdk +I used git@github.com:mstorsjo/msvc-wine.git + +Assume that you've installed at "${MSVC}" + +== Build with MSVC using wine + +$ mkdir build-msvc +$ cd build-msvc +$ export PATH="${MSVC}/bin/x64:/usr/bin:$PATH" +$ CC=cl CXX=cl cmake -S .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_MAKE_PROGRAM=/usr/bin/make + +=== Build with clang-cl without wine + +$ sudo apt-get install clang-cl llvm # I used clang-cl-18 +$ mkdir build-clang-cl +$ cd build-clang-cl +$ export INCLUDE="${MSVC}/vc/tools/msvc/14.44.35207/include;${MSVC}/kits/10/include/10.0.26100.0/shared;${MSVC}/kits/10/include/10.0.26100.0/ucrt;${MSVC}/kits/10/include/10.0.26100.0/um" +$ export LIB="${MSVC}/vc/tools/msvc/14.44.35207/lib/x64;${MSVC}/kits/10/lib/10.0.26100.0/ucrt/x64;${MSVC}/kits/10/lib/10.0.26100.0/um/x64" +$ cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_C_COMPILER=clang-cl-18 \ + -DCMAKE_CXX_COMPILER=clang-cl-18 \ + -DCMAKE_LINKER=lld-link-18 \ + -DCMAKE_MT=llvm-mt-18 \ + -DCMAKE_RC_COMPILER=llvm-rc-18 + +=== Build with native clang cross compiling for windows +$ export INCLUDE="${MSVC}/vc/tools/msvc/14.44.35207/include;${MSVC}/kits/10/include/10.0.26100.0/shared;${MSVC}/kits/10/include/10.0.26100.0/ucrt;${MSVC}/kits/10/include/10.0.26100.0/um" +$ export LIB="${MSVC}/vc/tools/msvc/14.44.35207/lib/x64;${MSVC}/kits/10/lib/10.0.26100.0/ucrt/x64;${MSVC}/kits/10/lib/10.0.26100.0/um/x64" +$ cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/clang-windows-toolchain.cmake + + +--- new method + +=== MSVC using wine +$ cmake -DMSVC_WINE_DIR="${MSVC}" -DMSVC_WINE_COMPILER=cl .. + +=== clang-cl +$ cmake -DMSVC_WINE_DIR="${MSVC}" -DMSVC_WINE_COMPILER=clang-cl .. + +=== clang cross +$ cmake -DMSVC_WINE_DIR="${MSVC}" -DMSVC_WINE_COMPILER=clang .. diff --git a/include/libfyaml.h b/include/libfyaml.h index 9a0acee..cc3ff1a 100644 --- a/include/libfyaml.h +++ b/include/libfyaml.h @@ -42,9 +42,28 @@ extern "C" { #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include +#include +#elif defined(_WIN32) +/* Windows compatibility definitions */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef int ssize_t; #endif - +#endif +/* Windows doesn't have sys/uio.h, provide iovec definition */ +#ifndef _FY_IOVEC_DEFINED +#define _FY_IOVEC_DEFINED +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif +#else #include +#endif /* opaque types for the user */ struct fy_token; @@ -74,10 +93,18 @@ struct fy_document_builder; /* NULL terminated string length specifier */ #define FY_NT ((size_t)-1) +/* DLL export/import macros + * Note: On Windows, a .def file (src/lib/fyaml.def) is used to export + * public API symbols. The FY_EXPORT macro is kept for GCC visibility. + */ #if defined(__GNUC__) && __GNUC__ >= 4 #define FY_EXPORT __attribute__ ((visibility ("default"))) #define FY_DEPRECATED __attribute__ ((deprecated)) #define FY_FORMAT(_t, _x, _y) __attribute__ ((format(_t, _x, _y))) +#elif defined(_MSC_VER) +#define FY_EXPORT /* nothing - exports handled via .def file */ +#define FY_DEPRECATED /* nothing - MSVC __declspec must be prefix */ +#define FY_FORMAT(_t, _x, _y) /* nothing */ #else #define FY_EXPORT /* nothing */ #define FY_DEPRECATED /* nothing */ @@ -90,6 +117,36 @@ struct fy_document_builder; * If the _str pointer is NULL, then NULL will be returned */ #ifndef FY_ALLOCA_COPY_FREE +#ifdef _MSC_VER +/* + * MSVC doesn't support GCC statement expressions, so we provide + * function-based alternatives using thread-local storage. + */ +#define FY_ALLOCA_COPY_FREE_BUFSZ 8192 +static __declspec(thread) char fy_alloca_copy_free_buf[FY_ALLOCA_COPY_FREE_BUFSZ]; + +static __inline const char *fy_alloca_copy_free_impl(char *str, size_t len) +{ + size_t actual_len; + if (!str) + return NULL; + actual_len = (len == FY_NT) ? strlen(str) : len; + if (actual_len >= FY_ALLOCA_COPY_FREE_BUFSZ) + actual_len = FY_ALLOCA_COPY_FREE_BUFSZ - 1; + memcpy(fy_alloca_copy_free_buf, str, actual_len); + fy_alloca_copy_free_buf[actual_len] = '\0'; + free(str); + return fy_alloca_copy_free_buf; +} + +#define FY_ALLOCA_COPY_FREE(_str, _len) \ + fy_alloca_copy_free_impl((_str), (size_t)(_len)) + +#define FY_ALLOCA_COPY_FREE_NO_NULL(_str, _len) \ + (fy_alloca_copy_free_impl((_str), (size_t)(_len)) ? fy_alloca_copy_free_buf : "") + +#else +/* GCC/Clang version with statement expressions */ #define FY_ALLOCA_COPY_FREE(_str, _len) \ ({ \ char *__str = (_str), *__stra = NULL; \ @@ -105,10 +162,8 @@ struct fy_document_builder; } \ (const char *)__stra; \ }) -#endif /* same as above but when _str == NULL return "" */ -#ifndef FY_ALLOCA_COPY_FREE_NO_NULL #define FY_ALLOCA_COPY_FREE_NO_NULL(_str, _len) \ ({ \ const char *__strb; \ @@ -119,6 +174,7 @@ struct fy_document_builder; __strb; \ }) #endif +#endif /** * DOC: libfyaml public API @@ -1357,22 +1413,6 @@ size_t fy_token_get_text_length(struct fy_token *fyt) FY_EXPORT; -/** - * fy_token_get_utf8_length() - Get length of the text of a token - * - * This method will return the length of the text representation - * of a token as a utf8 string. - * - * @fyt: The token - * - * Returns: - * The size of the utf8 text representation of a token, -1 in case of an error. - * Note that the NULL token will return a length of zero. - */ -size_t -fy_token_get_utf8_length(struct fy_token *fyt) - FY_EXPORT; - /** * enum fy_comment_placement - Comment placement relative to token * @@ -1390,9 +1430,10 @@ enum fy_comment_placement { /** * fy_token_get_comment() - Get zero terminated comment of a token * + * Get the comment that is attached at a token having the given + * placement. + * * @fyt: The token out of which the comment text will be returned. - * @buf: The buffer to be filled with the contents of the token - * @maxsz: The maximum size of the comment buffer * @which: The comment placement * * Returns: @@ -1400,8 +1441,86 @@ enum fy_comment_placement { * NULL in case of an error or if the token has no comment. */ const char * -fy_token_get_comment(struct fy_token *fyt, char *buf, size_t maxsz, - enum fy_comment_placement which) +fy_token_get_comment(struct fy_token *fyt, enum fy_comment_placement which) + FY_EXPORT; + +/** + * fy_token_get_comments() - Get all comments of a token + * + * Get all the comments that are attached on a token + * + * @fyt: The token + * + * Returns: + * A pointer to a zero terminated text representation of all the comments. + * The comments of all the placements are concatenated and returned as one. + * NULL in case of an error or if the event has no comments. + */ +const char * +fy_token_get_comments(struct fy_token *fyt) + FY_EXPORT; + +/** + * fy_token_set_comment() - Attach a comment to a token + * + * Attach the given comment on the token at the specified placement. + * If another comment is already present it will be overriden. + * If text is NULL the comment is removed. + * + * @fyt: The token out of which the comment text will be attached + * @which: The placement + * @text: The text of the comment + * @len: The length of the comment (FY_NT means the length of text) + * + * Returns: + * 0 on success, -1 on failure. + */ +int +fy_token_set_comment(struct fy_token *fyt, enum fy_comment_placement which, + const char *text, size_t len) + FY_EXPORT; + +/** + * fy_event_get_comments() - Get all comments of an event + * + * @fye: The event + * + * Returns: + * A pointer to a zero terminated text representation of all the comments. + * The comments of all the placements are concatenated and returned as one. + * NULL in case of an error or if the event has no comments. + * The pointer must be free'ed if it's not NULL via a call to free() + */ +const char * +fy_event_get_comments(struct fy_event *fye) + FY_EXPORT; + +/** + * fy_node_get_comment() - Get comment of a node + * + * @fyn: The node + * @placement: The placement of the comment + * + * Returns: + * A pointer to a zero terminated text representation of the comment. + * NULL in case of an error or if the event has no such comments. + */ +const char * +fy_node_get_comment(struct fy_node *fyn, enum fy_comment_placement which) + FY_EXPORT; + +/** + * fy_node_get_comments() - Get all comments of a node + * + * @fyn: The node + * + * Returns: + * A pointer to a zero terminated text representation of all the comments. + * The comments of all the placements are concatenated and returned as one. + * NULL in case of an error or if the event has no comments. + */ +const char * +fy_node_get_comments(struct fy_node *fyn) FY_EXPORT; /** @@ -2660,55 +2779,6 @@ char * fy_emit_to_string_collect(struct fy_emitter *emit, size_t *sizep) FY_EXPORT; -/** - * fy_emitter_vlog() - Log using the emitters diagnostics printf style (va_arg) - * - * Output a log on the emitter diagnostic output - * - * @emit: The emitter - * @type: The error type - * @fmt: The printf format string - * @ap: The argument list - */ -void -fy_emitter_vlog(struct fy_emitter *emit, enum fy_error_type type, const char *fmt, va_list ap) - FY_EXPORT; - -/** - * fy_emitter_log() - Log using the emitters diagnostics printf style - * - * Output a report on the emitter's diagnostics - * - * @emit: The emitter - * @type: The error type - * @fmt: The printf format string - * @...: The extra arguments - */ -void -fy_emitter_log(struct fy_emitter *emit, enum fy_error_type type, const char *fmt, ...) - FY_FORMAT(printf, 3, 4) - FY_EXPORT; - -#ifndef NDEBUG - -#define fy_emitter_debug(_emit, _fmt, ...) \ - fy_emitter_log((_emit), FYET_DEBUG, (_fmt) , ## __VA_ARGS__) -#else - -#define fy_emitter_debug(_emit, _fmt, ...) \ - do { } while(0) - -#endif - -#define fy_emitter_info(_emit, _fmt, ...) \ - fy_emitter_log((_emit), FYET_INFO, (_fmt) , ## __VA_ARGS__) -#define fy_emitter_notice(_emit, _fmt, ...) \ - fy_emitter_log((_emit), FYET_NOTICE, (_fmt) , ## __VA_ARGS__) -#define fy_emitter_warning(_emit, _fmt, ...) \ - fy_emitter_log((_emit), FYET_WARNING, (_fmt) , ## __VA_ARGS__) -#define fy_emitter_error(_emit, _fmt, ...) \ - fy_emitter_log((_emit), FYET_ERROR, (_fmt) , ## __VA_ARGS__) - /** * fy_node_copy() - Copy a node, associating the new node with the given document * @@ -3388,6 +3458,24 @@ enum fy_node_style fy_node_get_style(struct fy_node *fyn) FY_EXPORT; +/** + * fy_node_set_style() - Set the node style + * + * Try to set the node rendering style of a node. + * If the style of node is illegal (for example + * you can't set an arbitrary scalar that contains + * zeroes to plain) then the actual style that was + * set will be returned. + * + * @fyn: The node + * + * Returns: + * The final node style + */ +enum fy_node_style +fy_node_set_style(struct fy_node *fyn, enum fy_node_style style) + FY_EXPORT; + /** * fy_node_get_start_token() - Get the node start token * @@ -5850,6 +5938,20 @@ void fy_diag_set_collect_errors(struct fy_diag *diag, bool collect_errors) FY_EXPORT; +/** + * fy_diag_get_collect_errors() - Get collect errors state + * + * Get the collect errors mode. + * + * @diag: The diagnostic object + * + * Returns: + * true collecting errors, false otherwise + */ +bool +fy_diag_get_collect_errors(struct fy_diag *diag) + FY_EXPORT; + /** * fy_diag_cfg_default() - Fill in the configuration structure * with defaults @@ -5989,6 +6091,20 @@ fy_diagf(struct fy_diag *diag, const struct fy_diag_ctx *fydc, FY_FORMAT(printf, 3, 4) FY_EXPORT; +#ifdef _MSC_VER +/* MSVC doesn't support GCC statement expressions, use do-while(0) instead */ +#define fy_diag_diag(_diag, _level, _fmt, ...) \ + do { \ + struct fy_diag_ctx _ctx; \ + memset(&_ctx, 0, sizeof(_ctx)); \ + _ctx.level = (_level); \ + _ctx.module = FYEM_UNKNOWN; \ + _ctx.source_func = __func__; \ + _ctx.source_file = __FILE__; \ + _ctx.source_line = __LINE__; \ + fy_diagf((_diag), &_ctx, (_fmt) , ## __VA_ARGS__); \ + } while(0) +#else #define fy_diag_diag(_diag, _level, _fmt, ...) \ ({ \ struct fy_diag_ctx _ctx = { \ @@ -6003,6 +6119,7 @@ fy_diagf(struct fy_diag *diag, const struct fy_diag_ctx *fydc, }; \ fy_diagf((_diag), &_ctx, (_fmt) , ## __VA_ARGS__); \ }) +#endif #ifndef NDEBUG @@ -6253,63 +6370,6 @@ fy_diag_event_report(struct fy_diag *diag, struct fy_event *fye, FY_FORMAT(printf, 5, 6) FY_EXPORT; -/** - * fy_diag_event_override_vreport() - Report about a token vprintf style, - * overriding file, line and column info using - * the given diagnostic object - * - * Output a report about the given event part via the specific - * error type. This method will use the overrides provided in order - * to massage the reporting information. - * If @file is NULL, no file location will be reported. - * If either @line or @column is negative no location will be reported. - * - * @diag: The diag object - * @fye: The event - * @fyep: The event part - * @type: The error type - * @file: The file override - * @line: The line override - * @column: The column override - * @fmt: The printf format string - * @ap: The argument list - */ -void -fy_diag_event_override_vreport(struct fy_diag *diag, struct fy_event *fye, - enum fy_event_part fyep, enum fy_error_type type, - const char *file, int line, int column, - const char *fmt, va_list ap) - FY_EXPORT; - -/** - * fy_diag_event_override_report() - Report about a token printf style, - * overriding file, line and column info using - * the given diagnostic object - * - * Output a report about the given event part via the specific - * error type. This method will use the overrides provided in order - * to massage the reporting information. - * If @file is NULL, no file location will be reported. - * If either @line or @column is negative no location will be reported. - * - * @diag: The diag object - * @fyt: The event - * @fyep: The event part - * @type: The error type - * @file: The file override - * @line: The line override - * @column: The column override - * @fmt: The printf format string - * @...: The extra arguments. - */ -void -fy_diag_event_override_report(struct fy_diag *diag, struct fy_token *fyt, - enum fy_event_part fyep, enum fy_error_type type, - const char *file, int line, int column, - const char *fmt, ...) - FY_FORMAT(printf, 8, 9) - FY_EXPORT; - /** * fy_diag_errors_iterate() - Iterate over the errors of a diagnostic object * diff --git a/src/Makefile.am b/src/Makefile.am index 3d79cd5..7debd9f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -244,3 +244,9 @@ install-exec-hook: uninstall-hook: (cd "$(DESTDIR)$(bindir)" && rm -f fy-dump fy-filter fy-testsuite fy-join fy-ypath fy-compose) + +EXTRA_DIST = \ + getopt/getopt.c \ + getopt/getopt.h \ + util/fy-win32.h + diff --git a/src/allocator/fy-allocator-dedup.c b/src/allocator/fy-allocator-dedup.c index e964fcf..4b56f3d 100644 --- a/src/allocator/fy-allocator-dedup.c +++ b/src/allocator/fy-allocator-dedup.c @@ -22,11 +22,14 @@ /* for container_of */ #include "fy-list.h" #include "fy-utils.h" +#include "fy-win32.h" #include "fy-allocator-dedup.h" // #define DEBUG_GROWS +#ifdef DEBUG_GROWS + #undef BEFORE #define BEFORE() \ do { \ @@ -34,11 +37,22 @@ } while(0) #undef AFTER +#ifdef _MSC_VER +static __inline int64_t fy_dedup_after_calc(const struct timespec *before, const struct timespec *after) +{ + return (int64_t)(after->tv_sec - before->tv_sec) * (int64_t)1000000000UL + (int64_t)(after->tv_nsec - before->tv_nsec); +} +#define AFTER() \ + (clock_gettime(CLOCK_MONOTONIC, &after), fy_dedup_after_calc(&before, &after)) +#else #define AFTER() \ ({ \ clock_gettime(CLOCK_MONOTONIC, &after); \ (int64_t)(after.tv_sec - before.tv_sec) * (int64_t)1000000000UL + (int64_t)(after.tv_nsec - before.tv_nsec); \ }) +#endif + +#endif static const unsigned int bit_to_chain_length_map[] = { [0] = 1, /* 1 */ @@ -88,9 +102,9 @@ static void fy_dedup_tag_data_cleanup(struct fy_dedup_tag_data *dtd) { struct fy_dedup_allocator *da = dtd->cfg.da; - fy_parent_allocator_free(&da->a, dtd->buckets); - fy_parent_allocator_free(&da->a, dtd->bloom_id); - fy_parent_allocator_free(&da->a, dtd->buckets_in_use); + fy_parent_allocator_free(&da->a, (void *)dtd->buckets); + fy_parent_allocator_free(&da->a, (void *)dtd->bloom_id); + fy_parent_allocator_free(&da->a, (void *)dtd->buckets_in_use); } static int fy_dedup_tag_data_setup(struct fy_dedup_tag_data *dtd, @@ -129,10 +143,10 @@ static int fy_dedup_tag_data_setup(struct fy_dedup_tag_data *dtd, dtd->chain_length_grow_trigger++; } - dtd->bloom_filter_mask = (1U << dtd->bloom_filter_bits) - 1; - dtd->bucket_count_mask = (1U << dtd->bucket_count_bits) - 1; + dtd->bloom_filter_mask = ((unsigned int)1 << dtd->bloom_filter_bits) - 1; + dtd->bucket_count_mask = ((unsigned int)1 << dtd->bucket_count_bits) - 1; - dtd->bloom_id_count = ((1U << dtd->bloom_filter_bits) + FY_ID_BITS_BITS - 1) / FY_ID_BITS_BITS; + dtd->bloom_id_count = (((size_t)1 << dtd->bloom_filter_bits) + FY_ID_BITS_BITS - 1) / FY_ID_BITS_BITS; assert(dtd->bloom_id_count > 0); tmpsz = dtd->bloom_id_count * sizeof(*dtd->bloom_id); dtd->bloom_id = fy_parent_allocator_alloc(&da->a, tmpsz, _Alignof(fy_id_bits)); @@ -140,7 +154,7 @@ static int fy_dedup_tag_data_setup(struct fy_dedup_tag_data *dtd, goto err_out; fy_id_reset(dtd->bloom_id, dtd->bloom_id_count); - dtd->bucket_count = 1U << dtd->bucket_count_bits; + dtd->bucket_count = (size_t)1 << dtd->bucket_count_bits; assert(dtd->bucket_count); tmpsz = sizeof(*dtd->buckets) * dtd->bucket_count; @@ -150,7 +164,7 @@ static int fy_dedup_tag_data_setup(struct fy_dedup_tag_data *dtd, for (i = 0; i < dtd->bucket_count; i++) atomic_store(&dtd->buckets[i], NULL); - dtd->bucket_id_count = ((1U << dtd->bucket_count_bits) + FY_ID_BITS_BITS - 1) / FY_ID_BITS_BITS; + dtd->bucket_id_count = (((size_t)1 << dtd->bucket_count_bits) + FY_ID_BITS_BITS - 1) / FY_ID_BITS_BITS; assert(dtd->bucket_id_count > 0); tmpsz = dtd->bucket_id_count * sizeof(*dtd->buckets_in_use); dtd->buckets_in_use = fy_parent_allocator_alloc(&da->a, tmpsz, _Alignof(fy_id_bits)); @@ -333,9 +347,9 @@ fy_dedup_tag_adjust(struct fy_dedup_allocator *da, struct fy_dedup_tag *dt, return -1; } - bucket_count = 1U << dtd->bucket_count_bits; + bucket_count = (size_t)1 << dtd->bucket_count_bits; bucket_used = fy_id_count_used(dtd->buckets_in_use, dtd->bucket_id_count); - occupancy_ratio = (double)bucket_used/(double)bucket_count; + occupancy_ratio = (float)((double)bucket_used/(double)bucket_count); /* do not grow until we're over 60% full */ if (occupancy_ratio < da->cfg.minimum_bucket_occupancy) { @@ -472,7 +486,7 @@ static void fy_dedup_cleanup(struct fy_allocator *a) fy_dedup_tag_cleanup(da, dt); } - fy_parent_allocator_free(&da->a, da->ids); + fy_parent_allocator_free(&da->a, (void *)da->ids); fy_parent_allocator_free(&da->a, da->tags); } @@ -502,7 +516,7 @@ static int fy_dedup_setup(struct fy_allocator *a, struct fy_allocator *parent, i bucket_count_bits = cfg->bucket_count_bits; if (!bucket_count_bits && has_estimate) { bucket_count_bits = 1; - while ((1LU << bucket_count_bits) < (cfg->estimated_content_size / BUCKET_ESTIMATE_DIV)) + while (((size_t)1 << bucket_count_bits) < (cfg->estimated_content_size / BUCKET_ESTIMATE_DIV)) bucket_count_bits++; #ifdef DEBUG_GROWS fprintf(stderr, "bucket_count_bits %u\n", bucket_count_bits); @@ -518,7 +532,7 @@ static int fy_dedup_setup(struct fy_allocator *a, struct fy_allocator *parent, i bloom_filter_bits = cfg->bloom_filter_bits; if (!bloom_filter_bits && has_estimate) { bloom_filter_bits = 1; - while ((1LU << bloom_filter_bits) < (cfg->estimated_content_size / BLOOM_ESTIMATE_DIV)) + while (((size_t)1 << bloom_filter_bits) < (cfg->estimated_content_size / BLOOM_ESTIMATE_DIV)) bloom_filter_bits++; #ifdef DEBUG_GROWS fprintf(stderr, "bloom_filter_bits %u\n", bloom_filter_bits); @@ -862,7 +876,7 @@ again: /* verify it's aligned correctly */ assert(((uintptr_t)mem & (align - 1)) == 0); - de = mem + de_offset; + de = (void *)((char *)mem + de_offset); de->hash = hash; de->size = total_size; @@ -1077,7 +1091,7 @@ static int fy_dedup_set_tag_count(struct fy_allocator *a, unsigned int count) if (da->ids != ids) { da->ids = ids; - fy_parent_allocator_free(&da->a, da->ids); + fy_parent_allocator_free(&da->a, (void *)da->ids); } da->tag_count = tag_count; @@ -1087,7 +1101,7 @@ static int fy_dedup_set_tag_count(struct fy_allocator *a, unsigned int count) err_out: fy_parent_allocator_free(&da->a, alloc_tags); - fy_parent_allocator_free(&da->a, alloc_ids); + fy_parent_allocator_free(&da->a, (void *)alloc_ids); return -1; } diff --git a/src/allocator/fy-allocator-linear.c b/src/allocator/fy-allocator-linear.c index f516f9e..7d84a9e 100644 --- a/src/allocator/fy-allocator-linear.c +++ b/src/allocator/fy-allocator-linear.c @@ -106,18 +106,18 @@ struct fy_allocator *fy_linear_create(struct fy_allocator *parent, int parent_ta buf = cfg->buf; s = buf; - e = s + cfg->size; + e = (char *)s + cfg->size; s = fy_ptr_align(s, _Alignof(struct fy_linear_allocator)); - if ((size_t)(e - s) < sizeof(*la)) + if ((size_t)((char *)e - (char *)s) < sizeof(*la)) goto err_out; la = s; - s += sizeof(*la); + s = (char *)s + sizeof(*la); memset(&newcfg, 0, sizeof(newcfg)); newcfg.buf = s; - newcfg.size = (size_t)(e - s); + newcfg.size = (size_t)((char *)e - (char *)s); rc = fy_linear_setup(&la->a, parent, parent_tag, &newcfg); if (rc) @@ -177,8 +177,8 @@ static void *fy_linear_alloc(struct fy_allocator *a, int tag, size_t size, size_ /* atomically update the pointer */ do { next = fy_atomic_load(&la->next); - s = fy_ptr_align(la->start + next, align); - new_next = (s + size) - la->start; + s = fy_ptr_align((char *)la->start + next, align); + new_next = (size_t)(((char *)s + size) - (char *)la->start); /* handle both overflow and underflow */ if (new_next > la->cfg.size) goto err_out; @@ -239,7 +239,6 @@ static const void *fy_linear_storev(struct fy_allocator *a, int tag, if (a->flags & FYAF_KEEP_STATS) { la = container_of(a, struct fy_linear_allocator, a); - fy_atomic_fetch_add(&la->stores, 1); fy_atomic_fetch_add(&la->stores, size); } @@ -368,7 +367,8 @@ static bool fy_linear_contains(struct fy_allocator *a, int tag, const void *ptr) struct fy_linear_allocator *la; la = container_of(a, struct fy_linear_allocator, a); - return ptr >= la->start && ptr < (la->start + la->cfg.size); + return (char *)ptr >= (char *)la->start && + (char *)ptr < ((char *)la->start + la->cfg.size); } const struct fy_allocator_ops fy_linear_allocator_ops = { diff --git a/src/allocator/fy-allocator-malloc.c b/src/allocator/fy-allocator-malloc.c index 7c06185..1b59f42 100644 --- a/src/allocator/fy-allocator-malloc.c +++ b/src/allocator/fy-allocator-malloc.c @@ -14,12 +14,13 @@ #include #include #include - #include /* for container_of */ #include "fy-list.h" #include "fy-utils.h" +#include "fy-align.h" +#include "fy-win32.h" #include "fy-allocator-malloc.h" @@ -69,7 +70,7 @@ static void fy_malloc_tag_cleanup(struct fy_malloc_allocator *ma, struct fy_mall /* cleanup should happen from a single thread */ while ((me = fy_malloc_entry_list_pop(&mt->entries)) != NULL) - free(me->mem); + fy_align_free(me->mem); } static void fy_malloc_cleanup(struct fy_allocator *a) @@ -91,8 +92,8 @@ static void fy_malloc_cleanup(struct fy_allocator *a) fy_malloc_tag_cleanup(ma, mt); } - fy_parent_allocator_free(&ma->a, ma->ids); - fy_parent_allocator_free(&ma->a, ma->tags); + fy_parent_allocator_free(&ma->a, (void *)ma->ids); + fy_parent_allocator_free(&ma->a, (void *)ma->tags); } static int fy_malloc_setup(struct fy_allocator *a, struct fy_allocator *parent, int parent_tag, const void *data) @@ -214,17 +215,16 @@ static void *fy_malloc_tag_alloc(struct fy_malloc_allocator *ma, struct fy_mallo { struct fy_malloc_entry *me; size_t me_offset, max_align; - int r; void *mem; me_offset = fy_size_t_align(size, _Alignof(struct fy_malloc_entry)); max_align = align > _Alignof(struct fy_malloc_entry) ? align : _Alignof(struct fy_malloc_entry); - r = posix_memalign(&mem, max_align, me_offset + sizeof(*me)); - if (r) + mem = fy_align_alloc(max_align, me_offset + sizeof(*me)); + if (!mem) return NULL; - me = mem + me_offset; + me = (void *)((char *)mem + me_offset); me->size = size; fy_malloc_tag_list_lock(mt); @@ -245,7 +245,7 @@ static void fy_malloc_tag_free(struct fy_malloc_allocator *ma, struct fy_malloc_ fy_malloc_entry_list_del(&mt->entries, me); fy_malloc_tag_list_unlock(mt); - free(me->mem); + fy_align_free(me->mem); } static void *fy_malloc_alloc(struct fy_allocator *a, int tag, size_t size, size_t align) @@ -486,7 +486,7 @@ static int fy_malloc_set_tag_count(struct fy_allocator *a, unsigned int count) } if (ma->ids != ids) { ma->ids = ids; - fy_parent_allocator_free(&ma->a, ma->ids); + fy_parent_allocator_free(&ma->a, (void *)ma->ids); } ma->tag_count = tag_count; ma->tag_id_count = tag_id_count; @@ -494,7 +494,7 @@ static int fy_malloc_set_tag_count(struct fy_allocator *a, unsigned int count) err_out: fy_parent_allocator_free(&ma->a, tags); - fy_parent_allocator_free(&ma->a, ids); + fy_parent_allocator_free(&ma->a, (void *)ids); return -1; } @@ -517,7 +517,7 @@ static void fy_malloc_reset_tag(struct fy_allocator *a, int tag) /* no lock, reset is single thread only */ while ((me = fy_malloc_entry_list_pop(&mt->entries)) != NULL) - free(me->mem); + fy_align_free(me); } static struct fy_allocator_info * @@ -682,7 +682,8 @@ static bool fy_malloc_contains(struct fy_allocator *a, int tag, const void *ptr) for (me = fy_malloc_entry_list_head(&mt->entries); me; me = fy_malloc_entry_next(&mt->entries, me)) { - if (ptr >= (void *)me->mem && ptr < (void *)me->mem + me->size) { + if (ptr >= (void *)me->mem && + ptr < (void *)((char *)me->mem + me->size)) { fy_malloc_tag_list_unlock(mt); return true; } diff --git a/src/allocator/fy-allocator-mremap.c b/src/allocator/fy-allocator-mremap.c index d943221..c03f332 100644 --- a/src/allocator/fy-allocator-mremap.c +++ b/src/allocator/fy-allocator-mremap.c @@ -15,24 +15,27 @@ #include #include #include +#include + +#ifndef _WIN32 #include +#include +#endif #ifdef HAVE_ALLOCA_H #include #endif - -#include - /* for container_of */ #include "fy-list.h" #include "fy-utils.h" +#include "fy-win32.h" #include "fy-allocator-mremap.h" // #define DEBUG_ARENA -#if defined(__NetBSD__) +#if defined(__NetBSD__) || defined(_WIN32) #define DISABLE_MREMAP #endif @@ -100,7 +103,8 @@ fy_mremap_arena_create(struct fy_mremap_allocator *mra, struct fy_mremap_tag *mr if (size < mra->minimum_arena_size) size = mra->minimum_arena_size; - size_page_align = fy_mremap_useable_arena_size(mra, size); + /* need to be aligned to page boundary */ + size_page_align = fy_size_t_align(fy_mremap_useable_arena_size(mra, size), mra->pagesz); switch (mra->arena_type) { case FYMRAT_MALLOC: mran = malloc(size_page_align); @@ -124,7 +128,7 @@ fy_mremap_arena_create(struct fy_mremap_allocator *mra, struct fy_mremap_tag *mr /* either it fails, or it moves we handle it */ #else /* we don't shrink, we just unmap over the limit */ - rc = munmap(mem + size_page_align, balloon_size - size_page_align); + rc = munmap((char *)mem + size_page_align, balloon_size - size_page_align); if (rc) { #ifdef DEBUG_ARENA fprintf(stderr, "%s: failed to unmap for shink\n", __func__); @@ -227,8 +231,8 @@ static int fy_mremap_arena_grow(struct fy_mremap_allocator *mra, struct fy_mrema assert(mem == mran); #else /* do a mmap right after the one we have, if it succeeds we have grown */ - mem = mmap((void *)mran + mran->size, mran->size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (mem != (void *)mran + mran->size) { + mem = mmap((char *)mran + mran->size, mran->size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (mem != (char *)mran + mran->size) { if (mem != MAP_FAILED) munmap(mem, mran->size); break; @@ -283,7 +287,7 @@ static int fy_mremap_arena_trim(struct fy_mremap_allocator *mra, struct fy_mrema return -1; #else /* we don't shrink, we just unmap over the limit */ - rc = munmap((void *)mran + new_size, mran->size - new_size); + rc = munmap((char *)mran + new_size, mran->size - new_size); if (rc) return -1; #endif @@ -532,7 +536,7 @@ do_alloc: #endif goto again; } - ptr = (void *)mran + data_pos; + ptr = (void *)((char *)mran + data_pos); } while (!fy_atomic_compare_exchange_strong(&mran->next, &old_next, new_next)); /* malloc arenas need to zero out */ @@ -575,7 +579,7 @@ static void fy_mremap_cleanup(struct fy_allocator *a) fy_mremap_tag_cleanup(mra, mrt); } - fy_parent_allocator_free(&mra->a, mra->ids); + fy_parent_allocator_free(&mra->a, (void *)mra->ids); fy_parent_allocator_free(&mra->a, mra->tags); } @@ -950,7 +954,7 @@ static int fy_mremap_set_tag_count(struct fy_allocator *a, unsigned int count) } if (mra->ids != ids) { mra->ids = ids; - fy_parent_allocator_free(&mra->a, mra->ids); + fy_parent_allocator_free(&mra->a, (void *)mra->ids); } mra->tag_count = tag_count; mra->tag_id_count = tag_id_count; @@ -958,7 +962,7 @@ static int fy_mremap_set_tag_count(struct fy_allocator *a, unsigned int count) err_out: fy_parent_allocator_free(&mra->a, tags); - fy_parent_allocator_free(&mra->a, ids); + fy_parent_allocator_free(&mra->a, (void *)ids); return -1; } @@ -1080,7 +1084,7 @@ fy_mremap_get_info(struct fy_allocator *a, int tag) arena_info->free = arena_free; arena_info->used = arena_used; arena_info->total = arena_total; - arena_info->data = (void *)mran + FY_MREMAP_ARENA_OVERHEAD; + arena_info->data = (char *)mran + FY_MREMAP_ARENA_OVERHEAD; arena_info->size = arena_info->used; arena_info++; tag_info->num_arena_infos++; @@ -1124,7 +1128,7 @@ static bool mremap_tag_contains(struct fy_mremap_tag *mrt, const void *p) for (mra = fy_atomic_load(&mrt->arenas); mra; mra = mra->next_arena) { if (p >= (const void *)mra->mem && - p < (const void *)mra->mem + mra->size) + p < (const void *)((const char *)mra->mem + mra->size)) return true; } diff --git a/src/allocator/fy-allocator.c b/src/allocator/fy-allocator.c index 84fc059..c8179f9 100644 --- a/src/allocator/fy-allocator.c +++ b/src/allocator/fy-allocator.c @@ -18,15 +18,16 @@ #include +#ifndef _WIN32 #include - -#ifndef FY_NON_LOCKING_REGISTRY #include #endif /* for container_of */ #include "fy-list.h" #include "fy-utils.h" +#include "fy-align.h" +#include "fy-win32.h" #include "fy-allocator.h" #include "fy-allocator-linear.h" @@ -40,10 +41,13 @@ static bool allocator_registry_initialized = false; static bool allocator_registry_locked = false; #ifndef FY_NON_LOCKING_REGISTRY + +#if !defined(_WIN32) + static pthread_mutex_t allocator_registry_mutex = PTHREAD_MUTEX_INITIALIZER; static inline void allocator_registry_lock(void) { - int rc FY_UNUSED; + int rc FY_DEBUG_UNUSED; rc = pthread_mutex_lock(&allocator_registry_mutex); assert(!rc); @@ -54,7 +58,7 @@ static inline void allocator_registry_lock(void) static inline void allocator_registry_unlock(void) { - int rc FY_UNUSED; + int rc FY_DEBUG_UNUSED; assert(allocator_registry_locked); allocator_registry_locked = false; @@ -62,13 +66,39 @@ static inline void allocator_registry_unlock(void) rc = pthread_mutex_unlock(&allocator_registry_mutex); assert(!rc); } -#else + +#else // _WIN32 + +static CRITICAL_SECTION allocator_registry_mutex; +static LONG allocator_registry_mutex_initialized = 0; +static inline void allocator_registry_lock(void) +{ + /* Initialize on first use using interlocked operation */ + if (InterlockedCompareExchange(&allocator_registry_mutex_initialized, 1, 0) == 0) + InitializeCriticalSection(&allocator_registry_mutex); + EnterCriticalSection(&allocator_registry_mutex); + assert(!allocator_registry_locked); + allocator_registry_locked = true; +} + +static inline void allocator_registry_unlock(void) +{ + assert(allocator_registry_locked); + allocator_registry_locked = false; + LeaveCriticalSection(&allocator_registry_mutex); +} + +#endif + +#else // FY_NON_LOCKING_REGISTRY + static inline void allocator_registry_lock(void) { /* nothing */ } static inline void allocator_registry_unlock(void) { + /* nothing */ } #endif @@ -570,7 +600,7 @@ void fy_allocator_free(struct fy_allocator *a, int tag, void *ptr) { if (!a || !ptr) return; - return fy_allocator_free_nocheck(a, tag, ptr); + fy_allocator_free_nocheck(a, tag, ptr); } const void *fy_allocator_storev_hash(struct fy_allocator *a, int tag, const struct iovec *iov, int iovcnt, size_t align, uint64_t hash) @@ -707,9 +737,6 @@ fy_allocator_contains(struct fy_allocator *a, int tag, const void *ptr) /* respects the parent allocator (or uses posix_memalign if NULL) */ void *fy_early_parent_allocator_alloc(struct fy_allocator *parent, int parent_tag, size_t size, size_t align) { - void *ptr; - int r; - /* yeah, not gonna work */ if (parent == FY_PARENT_ALLOCATOR_INPLACE) return NULL; @@ -720,11 +747,7 @@ void *fy_early_parent_allocator_alloc(struct fy_allocator *parent, int parent_ta if (parent) return fy_allocator_alloc(parent, parent_tag, size, align); - r = posix_memalign(&ptr, align, size); - if (r) - return NULL; - - return ptr; + return fy_align_alloc(align, size); } void fy_early_parent_allocator_free(struct fy_allocator *parent, int parent_tag, void *ptr) @@ -735,7 +758,7 @@ void fy_early_parent_allocator_free(struct fy_allocator *parent, int parent_tag, if (parent) fy_allocator_free(parent, parent_tag, ptr); else - free(ptr); + fy_align_free(ptr); } /* respects the parent allocator (or uses posix_memalign if NULL) */ @@ -752,5 +775,5 @@ void fy_parent_allocator_free(struct fy_allocator *a, void *ptr) if (!a || !ptr) return; - return fy_early_parent_allocator_free(a->parent, a->parent_tag, ptr); + fy_early_parent_allocator_free(a->parent, a->parent_tag, ptr); } diff --git a/src/allocator/fy-allocator.h b/src/allocator/fy-allocator.h index b53fea0..84cbb5e 100644 --- a/src/allocator/fy-allocator.h +++ b/src/allocator/fy-allocator.h @@ -17,10 +17,13 @@ #include #include +#ifndef _WIN32 #include +#endif #include "fy-typelist.h" #include "fy-utils.h" +#include "fy-win32.h" #include @@ -49,7 +52,7 @@ struct fy_allocator_ops { void (*trim_tag)(struct fy_allocator *a, int tag); void (*reset_tag)(struct fy_allocator *a, int tag); struct fy_allocator_info *(*get_info)(struct fy_allocator *a, int tag); - unsigned int (*get_caps)(struct fy_allocator *a); /* Get capability flags (FYACF_*) */ + enum fy_allocator_cap_flags (*get_caps)(struct fy_allocator *a); /* Get capability flags (FYACF_*) */ bool (*contains)(struct fy_allocator *a, int tag, const void *ptr); }; diff --git a/src/blake3/blake3.c b/src/blake3/blake3.c index 65983a2..f10de40 100644 --- a/src/blake3/blake3.c +++ b/src/blake3/blake3.c @@ -11,13 +11,15 @@ #include #endif +#ifndef _WIN32 #include -#include +#endif #include "blake3.h" #include "blake3_impl.h" #include "blake3_internal.h" +#include "fy-win32.h" #include "fy-thread.h" // GCC gets confused sometimes diff --git a/src/blake3/blake3.h b/src/blake3/blake3.h index e13d114..02579cf 100644 --- a/src/blake3/blake3.h +++ b/src/blake3/blake3.h @@ -7,7 +7,6 @@ #include #include -#include #include #define BLAKE3_VERSION_STRING "1.4.1" diff --git a/src/blake3/blake3_backend.c b/src/blake3/blake3_backend.c index 4f00dd6..faef985 100644 --- a/src/blake3/blake3_backend.c +++ b/src/blake3/blake3_backend.c @@ -7,12 +7,7 @@ #include #include -#ifdef HAVE_ALLOCA_H -#include -#endif - #include -#include #include "fy-bit64.h" diff --git a/src/blake3/blake3_be_cpusimd.c b/src/blake3/blake3_be_cpusimd.c index 8a00951..b504bc8 100644 --- a/src/blake3/blake3_be_cpusimd.c +++ b/src/blake3/blake3_be_cpusimd.c @@ -12,13 +12,13 @@ #endif #include -#include #include "blake3.h" #include "blake3_impl.h" #include "blake3_internal.h" #include "fy-thread.h" +#include "fy-win32.h" struct cpusimd_data { unsigned int num_cpus; @@ -157,7 +157,7 @@ int blake3_backend_cpusimd_setup(unsigned int num_cpus, unsigned int mult_fact) if (num_cpus <= 1) return 0; - num_simd_cpus = round_down_to_power_of_2(num_cpus); + num_simd_cpus = (unsigned int)round_down_to_power_of_2(num_cpus); if (!mult_fact) mult_fact = 1; diff --git a/src/blake3/blake3_host_state.c b/src/blake3/blake3_host_state.c index 4699fa3..7ab02b9 100644 --- a/src/blake3/blake3_host_state.c +++ b/src/blake3/blake3_host_state.c @@ -6,19 +6,21 @@ #include #include #include +#include +#include #ifdef HAVE_ALLOCA_H #include #endif +#ifndef _WIN32 #include -#include #include #include -#include #include #include #include +#endif #include "fy-bit64.h" @@ -27,6 +29,7 @@ #include "blake3_internal.h" #include "fy-thread.h" +#include "fy-win32.h" /* 256K threshold for using alloca */ #define BLAKE3_ALLOCA_BUFFER_SIZE (256U << 10) @@ -458,7 +461,7 @@ int blake3_hash_file(blake3_hasher *hasher, const char *filename, while (left > 0) { chunk = left > max_chunk ? max_chunk : left; blake3_hasher_update(hasher, p, chunk); - p += chunk; + p = (char *)p + chunk; left -= chunk; } } else { diff --git a/src/blake3/blake3_impl.h b/src/blake3/blake3_impl.h index 230dc49..a66e98f 100644 --- a/src/blake3/blake3_impl.h +++ b/src/blake3/blake3_impl.h @@ -12,15 +12,10 @@ #include #include #include - -#ifndef _WIN32 #include -#if defined(__linux__) -#include -#endif -#endif #if defined(__linux__) +#include #include #endif @@ -69,9 +64,16 @@ #if defined(IS_X86) #if defined(_MSC_VER) +#if defined(__clang__) +/* clang-cl: intrin.h pulls in mmintrin.h which has vector type bugs + * when cross-compiling. Declare only the intrinsics we actually need. */ +unsigned long long _xgetbv(unsigned int); +void __cpuid(int[4], int); +#else #include #endif #endif +#endif /* Find index of the highest set bit */ /* x is assumed to be nonzero. */ diff --git a/src/blake3/blake3_internal.h b/src/blake3/blake3_internal.h index 2bfa42e..39bbf4a 100644 --- a/src/blake3/blake3_internal.h +++ b/src/blake3/blake3_internal.h @@ -7,8 +7,6 @@ #include #include -#include -#include #include "blake3.h" diff --git a/src/blake3/fy-blake3.c b/src/blake3/fy-blake3.c index e14234d..2214265 100644 --- a/src/blake3/fy-blake3.c +++ b/src/blake3/fy-blake3.c @@ -6,10 +6,6 @@ #include #include -#ifdef HAVE_ALLOCA_H -#include -#endif - #include #include diff --git a/src/getopt/LICENSE.getopt b/src/getopt/LICENSE.getopt new file mode 100644 index 0000000..22dd2f3 --- /dev/null +++ b/src/getopt/LICENSE.getopt @@ -0,0 +1,24 @@ +Copyright (c) 2012-2023, Kim Grasman +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 Kim Grasman 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 KIM GRASMAN BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/getopt/getopt.c b/src/getopt/getopt.c new file mode 100644 index 0000000..583ae61 --- /dev/null +++ b/src/getopt/getopt.c @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2012-2023, Kim Grasman + * 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 Kim Grasman 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 KIM GRASMAN 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. + * + ******************************************************************************/ + +#include +#include + +#include "getopt.h" + +char* optarg; +int optopt; +/* The variable optind [...] shall be initialized to 1 by the system. */ +int optind = 1; +int opterr; + +static char* optcursor = NULL; + +/* Implemented based on [1] and [2] for optional arguments. + optopt is handled FreeBSD-style, per [3]. + Other GNU and FreeBSD extensions are purely accidental. + +[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html +[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html +[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE +*/ +int getopt(int argc, char* const argv[], const char* optstring) { + int optchar = -1; + const char* optdecl = NULL; + + optarg = NULL; + opterr = 0; + optopt = 0; + + /* Unspecified, but we need it to avoid overrunning the argv bounds. */ + if (optind >= argc) + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] is a null pointer, getopt() + shall return -1 without changing optind. */ + if (argv[optind] == NULL) + goto no_more_optchars; + + /* If, when getopt() is called *argv[optind] is not the character '-', + getopt() shall return -1 without changing optind. */ + if (*argv[optind] != '-') + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] points to the string "-", + getopt() shall return -1 without changing optind. */ + if (strcmp(argv[optind], "-") == 0) + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] points to the string "--", + getopt() shall return -1 after incrementing optind. */ + if (strcmp(argv[optind], "--") == 0) { + ++optind; + goto no_more_optchars; + } + + if (optcursor == NULL || *optcursor == '\0') + optcursor = argv[optind] + 1; + + optchar = *optcursor; + + /* FreeBSD: The variable optopt saves the last known option character + returned by getopt(). */ + optopt = optchar; + + /* The getopt() function shall return the next option character (if one is + found) from argv that matches a character in optstring, if there is + one that matches. */ + optdecl = strchr(optstring, optchar); + if (optdecl) { + /* [I]f a character is followed by a colon, the option takes an + argument. */ + if (optdecl[1] == ':') { + optarg = ++optcursor; + if (*optarg == '\0') { + /* GNU extension: Two colons mean an option takes an + optional arg; if there is text in the current argv-element + (i.e., in the same word as the option name itself, for example, + "-oarg"), then it is returned in optarg, otherwise optarg is set + to zero. */ + if (optdecl[2] != ':') { + /* If the option was the last character in the string pointed to by + an element of argv, then optarg shall contain the next element + of argv, and optind shall be incremented by 2. If the resulting + value of optind is greater than argc, this indicates a missing + option-argument, and getopt() shall return an error indication. + + Otherwise, optarg shall point to the string following the + option character in that element of argv, and optind shall be + incremented by 1. + */ + if (++optind < argc) { + optarg = argv[optind]; + } else { + /* If it detects a missing option-argument, it shall return the + colon character ( ':' ) if the first character of optstring + was a colon, or a question-mark character ( '?' ) otherwise. + */ + optarg = NULL; + optchar = (optstring[0] == ':') ? ':' : '?'; + } + } else { + optarg = NULL; + } + } + + optcursor = NULL; + } + } else { + /* If getopt() encounters an option character that is not contained in + optstring, it shall return the question-mark ( '?' ) character. */ + optchar = '?'; + } + + if (optcursor == NULL || *++optcursor == '\0') + ++optind; + + return optchar; + +no_more_optchars: + optcursor = NULL; + return -1; +} + +/* Implementation based on [1]. + +[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html +*/ +int getopt_long(int argc, char* const argv[], const char* optstring, + const struct option* longopts, int* longindex) { + const struct option* o = longopts; + const struct option* match = NULL; + int num_matches = 0; + size_t argument_name_length = 0; + size_t option_length = 0; + const char* current_argument = NULL; + int retval = -1; + + optarg = NULL; + optopt = 0; + + if (optind >= argc) + return -1; + + if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0) + return getopt(argc, argv, optstring); + + /* It's an option; starts with -- and is longer than two chars. */ + current_argument = argv[optind] + 2; + argument_name_length = strcspn(current_argument, "="); + for (; o->name; ++o) { + /* Check for exact match first. */ + option_length = strlen(o->name); + if (option_length == argument_name_length && + strncmp(o->name, current_argument, option_length) == 0) { + match = o; + num_matches = 1; + break; + } + + /* If not exact, count the number of abbreviated matches. */ + if (strncmp(o->name, current_argument, argument_name_length) == 0) { + match = o; + ++num_matches; + } + } + + if (num_matches == 1) { + /* If longindex is not NULL, it points to a variable which is set to the + index of the long option relative to longopts. */ + if (longindex) + *longindex = (int)(match - longopts); + + /* If flag is NULL, then getopt_long() shall return val. + Otherwise, getopt_long() returns 0, and flag shall point to a variable + which shall be set to val if the option is found, but left unchanged if + the option is not found. */ + if (match->flag) + *(match->flag) = match->val; + + retval = match->flag ? 0 : match->val; + + if (match->has_arg != no_argument) { + optarg = strchr(argv[optind], '='); + if (optarg != NULL) + ++optarg; + + if (match->has_arg == required_argument) { + /* Only scan the next argv for required arguments. Behavior is not + specified, but has been observed with Ubuntu and Mac OSX. */ + if (optarg == NULL && ++optind < argc) { + optarg = argv[optind]; + } + + if (optarg == NULL) + retval = ':'; + } + } else if (strchr(argv[optind], '=')) { + /* An argument was provided to a non-argument option. + I haven't seen this specified explicitly, but both GNU and BSD-based + implementations show this behavior. + */ + retval = '?'; + } + } else { + /* Unknown option or ambiguous match. */ + retval = '?'; + } + + ++optind; + return retval; +} diff --git a/src/getopt/getopt.h b/src/getopt/getopt.h new file mode 100644 index 0000000..166ab26 --- /dev/null +++ b/src/getopt/getopt.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2012-2023, Kim Grasman + * 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 Kim Grasman 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 KIM GRASMAN 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. + * + ******************************************************************************/ + +#ifndef INCLUDED_GETOPT_PORT_H +#define INCLUDED_GETOPT_PORT_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define no_argument 1 +#define required_argument 2 +#define optional_argument 3 + +extern char* optarg; +extern int optind, opterr, optopt; + +struct option { + const char* name; + int has_arg; + int* flag; + int val; +}; + +int getopt(int argc, char* const argv[], const char* optstring); + +int getopt_long(int argc, char* const argv[], + const char* optstring, const struct option* longopts, int* longindex); + +#if defined(__cplusplus) +} +#endif + +#endif // INCLUDED_GETOPT_PORT_H diff --git a/src/internal/fy-allocators.c b/src/internal/fy-allocators.c index 79e83e2..7102357 100644 --- a/src/internal/fy-allocators.c +++ b/src/internal/fy-allocators.c @@ -14,25 +14,25 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 #include #include -#include #include #include #include - -#ifdef HAVE_ALLOCA_H -#include +#else +#include "fy-win32.h" #endif -#include #include -#include -#include -#include -#include -#include -#include #include diff --git a/src/internal/fy-b3sum.c b/src/internal/fy-b3sum.c index 9e81e42..e88fd6c 100644 --- a/src/internal/fy-b3sum.c +++ b/src/internal/fy-b3sum.c @@ -14,23 +14,26 @@ #include #include #include +#include +#include +#include + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#ifndef _WIN32 #include #include -#include #include #include #include - -#ifdef HAVE_ALLOCA_H -#include +#else +#include "fy-win32.h" #endif -#include #include -#include -#include -#include #include #define OPT_NO_MMAP 128 @@ -240,7 +243,7 @@ static int do_check_file(struct blake3_hasher *hasher, const char *check_filenam while (isxdigit((unsigned char)*s)) s++; - length = s - linebuf; + length = (unsigned int)(s - linebuf); if (length == 0 || length > (BLAKE3_OUT_LEN * 2) || (length % 1) || !isspace((unsigned char)*s)) { fprintf(stderr, "Bad line found at file \"%s\" line #%d\n", check_filename, line); diff --git a/src/internal/fy-thread.c b/src/internal/fy-thread.c index ac21563..2913dd4 100644 --- a/src/internal/fy-thread.c +++ b/src/internal/fy-thread.c @@ -14,32 +14,42 @@ #include #include #include -#include -#include -#include -#include -#include -#include #ifdef HAVE_ALLOCA_H #include #endif #include -#include #include #include #include #include #include -#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#else +#include "fy-win32.h" +#endif + +#include #include #include "fy-atomics.h" #include "fy-thread.h" +/* Helper to get number of online processors */ +static inline unsigned int get_num_processors(void) +{ + long scval = sysconf(_SC_NPROCESSORS_ONLN); + return scval > 0 ? (unsigned int)scval : 1; +} + static void test_worker_thread_fn(void *arg) { FY_ATOMIC(int) *p = arg; @@ -61,18 +71,12 @@ void test_worker_threads(unsigned int num_threads) struct fy_thread_pool *tp; struct fy_thread **threads, *t; struct fy_thread_work *works; - long scval; unsigned int i, count, num_cpus; int rc, test_count; (void)rc; - if (num_threads == 0) { - scval = sysconf(_SC_NPROCESSORS_ONLN); - assert(scval > 0); - num_cpus = (unsigned int)scval; - } else - num_cpus = num_threads; + num_cpus = (num_threads == 0) ? get_num_processors() : num_threads; memset(&tp_cfg, 0, sizeof(tp_cfg)); tp_cfg.flags = 0; @@ -133,18 +137,12 @@ void test_thread_join(unsigned int num_threads) struct fy_thread_pool_cfg tp_cfg; struct fy_thread_pool *tp; void **args; - long scval; unsigned int count, num_cpus; int rc, test_count; (void)rc; - if (num_threads == 0) { - scval = sysconf(_SC_NPROCESSORS_ONLN); - assert(scval > 0); - num_cpus = (unsigned int)scval; - } else - num_cpus = num_threads; + num_cpus = (num_threads == 0) ? get_num_processors() : num_threads; memset(&tp_cfg, 0, sizeof(tp_cfg)); tp_cfg.flags = 0; @@ -201,19 +199,13 @@ void test_thread_latency(unsigned int num_threads) struct fy_thread_pool *tp; struct fy_thread **threads, *t; struct fy_thread_work *works; - long scval; unsigned int i, count, num_cpus; int rc; struct thread_latency_state *states, *s; (void)rc; - if (num_threads == 0) { - scval = sysconf(_SC_NPROCESSORS_ONLN); - assert(scval > 0); - num_cpus = (unsigned int)scval; - } else - num_cpus = num_threads; + num_cpus = (num_threads == 0) ? get_num_processors() : num_threads; memset(&tp_cfg, 0, sizeof(tp_cfg)); tp_cfg.flags = 0; @@ -316,19 +308,12 @@ void test_thread_join_steal(unsigned int num_threads) struct fy_thread_pool_cfg tp_cfg; struct fy_thread_pool *tp; void **args; - long scval; unsigned int count, num_cpus; int rc, test_count; (void)rc; - if (num_threads == 0) { - scval = sysconf(_SC_NPROCESSORS_ONLN); - assert(scval > 0); - num_cpus = (unsigned int)scval; - } else - num_cpus = num_threads; - + num_cpus = (num_threads == 0) ? get_num_processors() : num_threads; tp_cfg.flags = FYTPCF_STEAL_MODE; tp_cfg.num_threads = num_cpus; @@ -382,7 +367,7 @@ static void test_worker_thread_sum_fn(void *arg) uint64_t sum; unsigned int pos; - pos = s->values - s->values_start; + pos = (unsigned int)(s->values - s->values_start); assert(pos <= s->count_start); assert(pos + s->count <= s->count_start); @@ -421,14 +406,16 @@ void test_thread_join_sum(unsigned int num_threads, unsigned int count, bool ste struct fy_thread_pool *tp; struct timespec before, after; unsigned int i, num_cpus; - long scval; uint8_t *values; int rc; uint64_t sum_single, sum_multi; struct sum_args args[2]; - long long table_multi[times]; + long long *table_multi; long long ns; + table_multi = (long long *)malloc(times * sizeof(long long)); + assert(table_multi); + (void)rc; fprintf(stderr, "**********************************************************************\n"); @@ -450,12 +437,7 @@ void test_thread_join_sum(unsigned int num_threads, unsigned int count, bool ste ns = delta_ns(before, after); fprintf(stderr, "%s: calculated sum=%"PRIu64" (single threaded) done in %lldus\n", __func__, sum_single, ns / 1000); - if (num_threads == 0) { - scval = sysconf(_SC_NPROCESSORS_ONLN); - assert(scval > 0); - num_cpus = (unsigned int)scval; - } else - num_cpus = num_threads; + num_cpus = (num_threads == 0) ? get_num_processors() : num_threads; memset(&tp_cfg, 0, sizeof(tp_cfg)); tp_cfg.flags = steal_mode ? FYTPCF_STEAL_MODE : 0; @@ -508,6 +490,7 @@ void test_thread_join_sum(unsigned int num_threads, unsigned int count, bool ste fy_thread_pool_destroy(tp); + free(table_multi); free(values); } diff --git a/src/internal/libfyaml-parser.c b/src/internal/libfyaml-parser.c index 8d0fb8d..5373da8 100644 --- a/src/internal/libfyaml-parser.c +++ b/src/internal/libfyaml-parser.c @@ -21,12 +21,18 @@ #include #include -#include +#include + +#ifndef _WIN32 +#include "fy-win32.h" +#endif #if defined(HAVE_LIBYAML) && HAVE_LIBYAML #include #endif +#include + #include "fy-parse.h" #include "fy-walk.h" #include "fy-blob.h" @@ -1030,7 +1036,7 @@ int do_comment(struct fy_parser *fyp) struct fy_atom *handle; enum fy_comment_placement placement; static const char *placement_txt[] = { "top", "right", "bottom" }; - char buf[1024]; + const char *comment; fydi = fy_document_iterator_create(); assert(fydi); @@ -1057,10 +1063,11 @@ int do_comment(struct fy_parser *fyp) if (!handle || !fy_atom_is_set(handle)) continue; - if (!fy_token_get_comment(fyt, buf, sizeof(buf), placement)) + comment = fy_token_get_comment(fyt, placement); + if (!comment) continue; - fprintf(stderr, "%s: %s\n", placement_txt[placement], buf); + fprintf(stderr, "%s: %s\n", placement_txt[placement], comment); } free(path); @@ -1662,7 +1669,7 @@ static void do_accel_kv(const struct fy_parse_cfg *cfg, int argc, char *argv[]) key = fy_kv_store_key_by_index(&kvs, idx); assert(key); - printf("removing #%d - %s\n", idx, key); + printf("removing #%d - %s\n", idx, key ? key : ""); rc = fy_kv_store_remove(&kvs, key); assert(!rc); @@ -2763,8 +2770,10 @@ int do_build(const struct fy_parse_cfg *cfg, int argc, char *argv[]) FILE *fp; char *mbuf = NULL; size_t msize; + struct fy_memstream *fyms = NULL; - fp = open_memstream(&mbuf, &msize); + fyms = fy_memstream_open(&fp); + assert(fyms); assert(fp); fy_diag_cfg_default(&dcfg); @@ -2785,8 +2794,7 @@ int do_build(const struct fy_parse_cfg *cfg, int argc, char *argv[]) fy_diag_destroy(diag); - fclose(fp); - + mbuf = fy_memstream_close(fyms, &msize); assert(mbuf); printf("checking diagnostic\n"); @@ -2802,9 +2810,10 @@ int do_build(const struct fy_parse_cfg *cfg, int argc, char *argv[]) FILE *fp; char *mbuf = NULL; size_t msize; + struct fy_memstream *fyms = NULL; - fp = open_memstream(&mbuf, &msize); - assert(fp); + fyms = fy_memstream_open(&fp); + assert(fyms); fy_diag_cfg_default(&dcfg); dcfg.fp = NULL; @@ -2826,8 +2835,7 @@ int do_build(const struct fy_parse_cfg *cfg, int argc, char *argv[]) fy_diag_destroy(diag); - fclose(fp); - + mbuf = fy_memstream_close(fyms, &msize); assert(mbuf); printf("checking diagnostic\n"); @@ -3745,7 +3753,7 @@ int do_walk(struct fy_parser *fyp, const char *walkpath, const char *walkstart, assert(fwr); fwr->type = fwrt_node_ref; fwr->fyn = fyn; - result = fy_path_expr_execute(fypx, 0, expr, fwr, fpet_none); + result = fy_path_expr_execute(fypx, 0, expr, fwr, fpet_none, NULL); printf("\n"); if (!result) { diff --git a/src/lib/fy-accel.c b/src/lib/fy-accel.c index 87a9b23..de5b981 100644 --- a/src/lib/fy-accel.c +++ b/src/lib/fy-accel.c @@ -137,14 +137,17 @@ int fy_accel_resize(struct fy_accel *xl, unsigned int min_buckets) int fy_accel_grow(struct fy_accel *xl) { + unsigned int idx; + if (!xl) return -1; /* should not grow indefinetely */ - if (xl->next_exp2 >= sizeof(prime_lt_pow2)/sizeof(prime_lt_pow2[0])) + idx = xl->next_exp2 + 1; + if (idx >= ARRAY_SIZE(prime_lt_pow2)) return -1; - return fy_accel_resize(xl, prime_lt_pow2[xl->next_exp2 + 1]); + return fy_accel_resize(xl, prime_lt_pow2[idx]); } int fy_accel_shrink(struct fy_accel *xl) diff --git a/src/lib/fy-atom.c b/src/lib/fy-atom.c index 4d554fe..22515dc 100644 --- a/src/lib/fy-atom.c +++ b/src/lib/fy-atom.c @@ -32,7 +32,7 @@ const char *fy_atom_data(const struct fy_atom *atom) if (!atom) return NULL; - return fy_input_start(atom->fyi) + atom->start_mark.input_pos; + return (const char *)fy_input_start(atom->fyi) + atom->start_mark.input_pos; } @@ -52,21 +52,20 @@ struct fy_atom *fy_reader_fill_atom(struct fy_reader *fyr, int advance, struct f int fy_reader_advance_mark(struct fy_reader *fyr, int advance, struct fy_mark *m) { - int i, c, tabsize; + int c, tabsize, w; bool is_line_break; tabsize = fy_reader_tabsize(fyr); - i = 0; while (advance-- > 0) { - c = fy_reader_peek_at(fyr, i++); + c = fy_reader_peek_at_offset_width(fyr, m->input_pos, &w); if (c == -1) return -1; m->input_pos += fy_utf8_width(c); /* first check for CR/LF */ - if (c == '\r' && fy_reader_peek_at(fyr, i) == '\n') { + if (c == '\r' && + fy_reader_peek_at_offset(fyr, m->input_pos + (size_t)w) == '\n') { m->input_pos++; - i++; is_line_break = true; } else if (fy_reader_is_lb(fyr, c)) is_line_break = true; @@ -232,25 +231,6 @@ _fy_atom_iter_add_chunk_copy(struct fy_atom_iter *iter, const char *str, size_t return 0; } -/* keep it around without a warning even though it's unused */ -static int -_fy_atom_iter_add_utf8(struct fy_atom_iter *iter, int c) - __attribute__((__unused__)); - -static int -_fy_atom_iter_add_utf8(struct fy_atom_iter *iter, int c) -{ - char buf[FY_UTF8_FORMAT_BUFMIN]; - char *e; - - /* only fails if invalid utf8 */ - e = fy_utf8_put(buf, sizeof(buf), c); - if (!e) - return -1; - - return _fy_atom_iter_add_chunk_copy(iter, buf, e - buf); -} - /* optimized linebreaks */ static int _fy_atom_iter_add_lb(struct fy_atom_iter *iter, int c) @@ -276,7 +256,6 @@ _fy_atom_iter_add_lb(struct fy_atom_iter *iter, int c) #ifndef DEBUG_CHUNK #define fy_atom_iter_add_chunk _fy_atom_iter_add_chunk #define fy_atom_iter_add_chunk_copy _fy_atom_iter_add_chunk_copy -#define fy_atom_iter_add_utf8 _fy_atom_iter_add_utf8 #define fy_atom_iter_add_lb _fy_atom_iter_add_lb #else #define fy_atom_iter_add_chunk(_iter, _str, _len) \ @@ -313,12 +292,6 @@ _fy_atom_iter_add_lb(struct fy_atom_iter *iter, int c) } \ __ret; \ }) -#define fy_atom_iter_add_utf8(_iter, _c) \ - ({ \ - int __c = (_c); \ - fprintf(stderr, "%s:%d utf8 %d\n", __func__, __LINE__, __c); \ - _fy_atom_iter_add_utf8((_iter), (_c)); \ - }) #define fy_atom_iter_add_lb(_iter, _c) \ ({ \ int __c = (_c); \ @@ -333,7 +306,7 @@ fy_atom_iter_line_analyze(struct fy_atom_iter *iter, struct fy_atom_iter_line_in { const struct fy_atom *atom = iter->atom; const char *s, *e, *ss; - int col, c, w, ts, cws, advws; + int col, c, cn, w, wn, ts, cws, advws; bool last_was_ws, is_block; int lastc; @@ -384,7 +357,7 @@ fy_atom_iter_line_analyze(struct fy_atom_iter *iter, struct fy_atom_iter_line_in last_was_ws = false; - ts = atom->tabsize ? : 8; /* pick it up from the atom (if there is) */ + ts = atom->tabsize ? atom->tabsize : 8; /* pick it up from the atom (if there is) */ /* consecutive whitespace */ cws = 0; @@ -446,6 +419,17 @@ fy_atom_iter_line_analyze(struct fy_atom_iter *iter, struct fy_atom_iter_line_in last_was_ws = true; } +#if defined(DEBUG_CHUNK) + /* special CR/LF handling */ + if (c == '\r') { + cn = ss < e ? fy_utf8_get(ss + w, (e - ss - w), &wn) : FYUG_EOF; + if (cn == '\n') { + ss += w; + w = wn; + } + } +#endif + } else if (fy_is_space(c)) { col++; @@ -605,6 +589,17 @@ do_nws: li->trailing_breaks_ws = true; if (fy_is_lb_m(c, atom->lb_mode)) { + /* special CRLF handling */ + if (c == '\r') { + cn = ss < e ? fy_utf8_get(ss + w, (e - ss - w), &wn) : FYUG_EOF; + if (cn == '\n') { +#if defined(DEBUG_CHUNK) + fprintf(stderr, "%s:%d trailing CRLF\n", __FILE__, __LINE__); +#endif + ss += w; + w = wn; + } + } li->trailing_breaks++; col = 0; } else { @@ -654,7 +649,7 @@ void fy_atom_iter_start(const struct fy_atom *atom, struct fy_atom_iter *iter) iter->chomp = atom->increment; /* default tab size is 8 */ - iter->tabsize = atom->tabsize ? : 8; + iter->tabsize = atom->tabsize ? atom->tabsize : 8; memset(iter->li, 0, sizeof(iter->li)); li = &iter->li[1]; @@ -829,13 +824,13 @@ fy_atom_iter_format(struct fy_atom_iter *iter) const struct fy_atom *atom = iter->atom; const struct fy_atom_iter_line_info *li; const char *s, *e, *t; - int value, code_length, rlen, ret; + int value, code_length, ret; uint8_t code[4], *tt; int j, pending_nl; int *pending_lb = NULL, *pending_lb_new = NULL; int pending_lb_size = 0; enum fy_utf8_escape esc_mode; - size_t i; + size_t rlen, i; /* done? */ li = fy_atom_iter_line(iter); @@ -1174,7 +1169,7 @@ fy_atom_iter_chunk_next(struct fy_atom_iter *iter, const struct fy_iter_chunk *c return ic; } -int fy_atom_format_text_length(struct fy_atom *atom) +ssize_t fy_atom_format_text_length(struct fy_atom *atom) { struct fy_atom_iter iter; const struct fy_iter_chunk *ic; @@ -1185,7 +1180,7 @@ int fy_atom_format_text_length(struct fy_atom *atom) return -1; if (atom->storage_hint_valid) - return atom->storage_hint; + return (ssize_t)atom->storage_hint; len = 0; fy_atom_iter_start(atom, &iter); @@ -1195,7 +1190,7 @@ int fy_atom_format_text_length(struct fy_atom *atom) fy_atom_iter_finish(&iter); /* something funky going on here */ - if ((int)len < 0) + if ((ssize_t)len < 0) return -1; if (ret != 0) @@ -1203,7 +1198,7 @@ int fy_atom_format_text_length(struct fy_atom *atom) atom->storage_hint = (size_t)len; atom->storage_hint_valid = true; - return (int)len; + return (ssize_t)len; } const char *fy_atom_format_text(struct fy_atom *atom, char *buf, size_t maxsz) @@ -1241,8 +1236,8 @@ int fy_atom_format_utf8_length(struct fy_atom *atom) struct fy_atom_iter iter; const struct fy_iter_chunk *ic; const char *s, *e; - size_t len; - int ret, rem, run, w; + size_t len, run, rem; + int ret, w; if (!atom) return -1; @@ -1256,7 +1251,7 @@ int fy_atom_format_utf8_length(struct fy_atom *atom) e = s + ic->len; /* add the remainder */ - run = (e - s) > rem ? rem : (e - s); + run = (size_t)(e - s) > rem ? rem : (size_t)(e - s); s += run; /* count utf8 characters */ @@ -1753,6 +1748,11 @@ fy_atom_raw_line_iter_next(struct fy_atom_raw_line_iter *iter) if (l->lineno > 0 && iter->rs >= iter->ae) return NULL; + /* this may happen on illegal utf8 */ + if (!iter->is) + return NULL; + + c = -1; while (s > iter->is) { c = fy_utf8_get_right(iter->is, (size_t)(s - iter->is), &w); if (c <= 0 || fy_is_lb_m(c, iter->atom->lb_mode)) @@ -1849,6 +1849,7 @@ fy_atom_raw_line_iter_next(struct fy_atom_raw_line_iter *iter) l->line_count = count; if (fy_is_lb_m(c, iter->atom->lb_mode)) { + s += w; /* special case for MSDOS */ if (c == '\r' && (s < iter->ie && s[1] == '\n')) @@ -1895,3 +1896,39 @@ void fy_atom_raw_line_iter_finish(struct fy_atom_raw_line_iter *iter) { /* nothing */ } + +/* returns the line of the atom, marking exactly where the atom is */ +const char *fy_atom_lines_containing(struct fy_atom *atom, size_t *lenp) +{ + const struct fy_raw_line *l, *ln; + struct fy_atom_raw_line_iter iter; + bool first_line, last_line; + const char *start, *end; + + if (!atom || !lenp) { + if (lenp) + *lenp = 0; + return NULL; + } + + start = end = NULL; + + fy_atom_raw_line_iter_start(atom, &iter); + l = fy_atom_raw_line_iter_next(&iter); + for (; l != NULL; l = ln) { + /* get the next too */ + ln = fy_atom_raw_line_iter_next(&iter); + + first_line = l->lineno <= 1; + last_line = ln == NULL; + + if (first_line) + start = l->line_start; + if (last_line) + end = l->line_start + l->line_count; + } + fy_atom_raw_line_iter_finish(&iter); + + *lenp = end - start; + return start; +} diff --git a/src/lib/fy-atom.h b/src/lib/fy-atom.h index 3f32275..e7fc76e 100644 --- a/src/lib/fy-atom.h +++ b/src/lib/fy-atom.h @@ -144,7 +144,7 @@ static inline bool fy_atom_is_flow_ws(struct fy_atom *handle, int c) return fy_is_flow_ws_m(c, fy_atom_flow_ws_mode(handle)); } -int fy_atom_format_text_length(struct fy_atom *atom); +ssize_t fy_atom_format_text_length(struct fy_atom *atom); const char *fy_atom_format_text(struct fy_atom *atom, char *buf, size_t maxsz); int fy_atom_format_utf8_length(struct fy_atom *atom); @@ -297,4 +297,7 @@ void fy_atom_raw_line_iter_finish(struct fy_atom_raw_line_iter *iter); const struct fy_raw_line * fy_atom_raw_line_iter_next(struct fy_atom_raw_line_iter *iter); +const char * +fy_atom_lines_containing(struct fy_atom *atom, size_t *lenp); + #endif diff --git a/src/lib/fy-composer-diag.c b/src/lib/fy-composer-diag.c index bc75b24..7bdb5a5 100644 --- a/src/lib/fy-composer-diag.c +++ b/src/lib/fy-composer-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include @@ -36,7 +33,7 @@ int fy_composer_vdiag(struct fy_composer *fyc, unsigned int flags, return -1; /* perform the enable tests early to avoid the overhead */ - if (((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < fyc->cfg.diag->cfg.level) + if ((int)((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < (int)fyc->cfg.diag->cfg.level) return 0; /* fill in fy_diag_ctx */ diff --git a/src/lib/fy-diag.c b/src/lib/fy-diag.c index 58a6dad..1f66f03 100644 --- a/src/lib/fy-diag.c +++ b/src/lib/fy-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include @@ -27,7 +24,6 @@ #include "fy-parse.h" #include "fy-input.h" - static const char *error_type_txt[] = { [FYET_DEBUG] = "debug", [FYET_INFO] = "info", @@ -254,7 +250,7 @@ void fy_diag_destroy(struct fy_diag *diag) while ((errp = fy_diag_errorp_list_pop(&diag->errors)) != NULL) fy_diag_errorp_free(errp); - return fy_diag_unref(diag); + fy_diag_unref(diag); } bool fy_diag_got_error(struct fy_diag *diag) @@ -297,6 +293,14 @@ void fy_diag_set_collect_errors(struct fy_diag *diag, bool collect_errors) } } +bool fy_diag_get_collect_errors(struct fy_diag *diag) +{ + if (!diag || diag->destroyed) + return false; + + return diag->collect_errors; +} + struct fy_diag_error *fy_diag_errors_iterate(struct fy_diag *diag, void **prevp) { struct fy_diag_errorp *errp; @@ -521,13 +525,13 @@ int fy_vdiag(struct fy_diag *diag, const struct fy_diag_ctx *fydc, } rc = fy_diag_printf(diag, "%s" "%*s" "%*s" "%*s" "%*s" "%s" "%s\n", - color_start ? : "", - source ? diag->cfg.source_width : 0, source ? : "", - position ? diag->cfg.position_width : 0, position ? : "", - typestr ? diag->cfg.type_width : 0, typestr ? : "", - modulestr ? diag->cfg.module_width : 0, modulestr ? : "", + color_start ? color_start : "", + source ? diag->cfg.source_width : 0, source ? source : "", + position ? diag->cfg.position_width : 0, position ? position : "", + typestr ? diag->cfg.type_width : 0, typestr ? typestr : "", + modulestr ? diag->cfg.module_width : 0, modulestr ? modulestr : "", msg, - color_end ? : ""); + color_end ? color_end : ""); if (rc > 0) rc++; @@ -792,14 +796,14 @@ void fy_diag_error_atom_display(struct fy_diag *diag, enum fy_error_type type, s } } display = rowbuf; - display_len = rbs - rowbuf; + display_len = (int)(rbs - rowbuf); tilde_start = content_start_col - line_shift; tilde_width = content_width; if (tilde_start + tilde_width > cols) tilde_width = cols - tilde_start; if ((size_t)tilde_width >= rowbufsz) - tilde_width = rowbufsz - 1; /* guard */ + tilde_width = (int)(rowbufsz - 1); /* guard */ tilde_width_m1 = tilde_width > 0 ? (tilde_width - 1) : 0; /* output the line */ @@ -869,7 +873,7 @@ void fy_diag_vreport(struct fy_diag *diag, if (!diag->collect_errors) { fy_diag_printf(diag, "%s" "%s%s: %s" "%s\n", - name_str ? : "", + name_str ? name_str : "", color_start, fy_error_type_to_string(fydrc->type), color_end, msg_str); diff --git a/src/lib/fy-doc-diag.c b/src/lib/fy-doc-diag.c index 04247e3..53c9bf1 100644 --- a/src/lib/fy-doc-diag.c +++ b/src/lib/fy-doc-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include @@ -36,7 +33,7 @@ int fy_document_vdiag(struct fy_document *fyd, unsigned int flags, return -1; /* perform the enable tests early to avoid the overhead */ - if (((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < fyd->diag->cfg.level) + if ((int)((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < (int)fyd->diag->cfg.level) return 0; /* fill in fy_diag_ctx */ diff --git a/src/lib/fy-doc.c b/src/lib/fy-doc.c index c3b10ff..9c05bfb 100644 --- a/src/lib/fy-doc.c +++ b/src/lib/fy-doc.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -223,7 +222,7 @@ static int fy_document_set_anchor_internal(struct fy_document *fyd, struct fy_no fyam->multiple = true; fya->multiple = true; - fyd_notice(fyd, "register anchor %.*s is multiple", (int)len, text); + fyd_debug(fyd, "register anchor %.*s is multiple", (int)len, text); } xle = fy_accel_entry_insert(fyd->axl, fya->anchor, fya); @@ -912,8 +911,8 @@ struct fy_token *fy_node_non_synthesized_token(struct fy_node *fyn) if (!fyt_start || !fyt_end) return NULL; - s = fy_input_start(fyi) + fyt_start->handle.start_mark.input_pos; - e = fy_input_start(fyi) + fyt_end->handle.end_mark.input_pos; + s = (const char *)fy_input_start(fyi) + fyt_start->handle.start_mark.input_pos; + e = (const char *)fy_input_start(fyi) + fyt_end->handle.end_mark.input_pos; size = (size_t)(e - s); if (size > 0) @@ -1111,8 +1110,8 @@ int fy_document_register_anchor(struct fy_document *fyd, { struct fy_anchor *fya, *fyam; struct fy_accel_entry *xle; - const char *text; - size_t text_len; + const char *text FY_DEBUG_UNUSED; + size_t text_len FY_DEBUG_UNUSED; int rc; fya = fy_anchor_create(fyd, fyn, anchor); @@ -1130,7 +1129,9 @@ int fy_document_register_anchor(struct fy_document *fyd, fya->multiple = true; text = fy_anchor_get_text(fya, &text_len); - fyd_notice(fyd, "register anchor %.*s is multiple", (int)text_len, text); + assert(text); + assert(text_len > 0); + fyd_debug(fyd, "register anchor %.*s is multiple", (int)text_len, text); } xle = fy_accel_entry_insert(fyd->axl, fya->anchor, fya); @@ -1943,11 +1944,11 @@ struct fy_document *fy_parse_load_document(struct fy_parser *fyp) } struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *fyn_from, - struct fy_node *fyn_parent) + struct fy_node *fyn_parent, int depth) { struct fy_document *fyd_from; - struct fy_node *fyn, *fyni, *fynit; - struct fy_node_pair *fynp, *fynpt; + struct fy_node *fyn = NULL, *fyni = NULL, *fynit = NULL; + struct fy_node_pair *fynp = NULL, *fynpt = NULL; struct fy_anchor *fya, *fya_from; const char *anchor; size_t anchor_len; @@ -1956,6 +1957,11 @@ struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *f if (!fyd || !fyn_from || !fyn_from->fyd) return NULL; + FYD_NODE_ERROR_CHECK(fyd, fyn_from, FYEM_DOC, + depth < FY_NODE_PATH_WALK_DEPTH_DEFAULT, err_out, + "maximum node copy depth exceeded %d (max %d)", + depth, FY_NODE_PATH_WALK_DEPTH_DEFAULT); + fyd_from = fyn_from->fyd; fyn = fy_node_alloc(fyd, fyn_from->type); @@ -1975,12 +1981,13 @@ struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *f for (fyni = fy_node_list_head(&fyn_from->sequence); fyni; fyni = fy_node_next(&fyn_from->sequence, fyni)) { - fynit = fy_node_copy_internal(fyd, fyni, fyn); + fynit = fy_node_copy_internal(fyd, fyni, fyn, depth + 1); fyd_error_check(fyd, fynit, err_out, "fy_node_copy_internal() failed"); fy_node_list_add_tail(&fyn->sequence, fynit); fynit->attached = true; + fynit = NULL; } break; @@ -1992,22 +1999,32 @@ struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *f fyd_error_check(fyd, fynpt, err_out, "fy_node_pair_alloc() failed"); - fynpt->key = fy_node_copy_internal(fyd, fynp->key, fyn); - fynpt->value = fy_node_copy_internal(fyd, fynp->value, fyn); + if (fynp->key) { + fynpt->key = fy_node_copy_internal(fyd, fynp->key, fyn, depth + 1); + fyd_error_check(fyd, fynpt->key, err_out, + "fy_node_copy_internal() key failed"); + } + if (fynp->value) { + fynpt->value = fy_node_copy_internal(fyd, fynp->value, fyn, depth + 1); + fyd_error_check(fyd, fynpt->value, err_out, + "fy_node_copy_internal() key failed"); + } fynp->parent = fyn; - fy_node_pair_list_add_tail(&fyn->mapping, fynpt); - if (fyn->xl) { - rc = fy_accel_insert(fyn->xl, fynpt->key, fynpt); - fyd_error_check(fyd, !rc, err_out, - "fy_accel_insert() failed"); - } if (fynpt->key) { fynpt->key->attached = true; fynpt->key->key_root = true; } if (fynpt->value) fynpt->value->attached = true; + + fy_node_pair_list_add_tail(&fyn->mapping, fynpt); + if (fyn->xl) { + rc = fy_accel_insert(fyn->xl, fynpt->key, fynpt); + fyd_error_check(fyd, !rc, err_out, + "fy_accel_insert() failed"); + } + fynpt = NULL; } break; } @@ -2041,6 +2058,9 @@ struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *f return fyn; err_out: + fy_node_free(fynit); + fy_node_pair_free(fynpt); + fy_node_free(fyn); return NULL; } @@ -2051,7 +2071,7 @@ struct fy_node *fy_node_copy(struct fy_document *fyd, struct fy_node *fyn_from) if (!fyd) return NULL; - fyn = fy_node_copy_internal(fyd, fyn_from, NULL); + fyn = fy_node_copy_internal(fyd, fyn_from, NULL, 0); if (!fyn) { fyd->diag->on_error = false; return NULL; @@ -2238,17 +2258,17 @@ int fy_node_insert(struct fy_node *fyn_to, struct fy_node *fyn_from) } } - /* verify no funkiness on root */ - assert(fyn_parent || fyn_to == fyd->root); - /* deleting target */ if (!fyn_from) { fyn_to->parent = NULL; if (!fyn_parent) { - fyd_doc_debug(fyd, "Deleting root node"); + fyd_doc_debug(fyd, "Deleting %s node", fyd->root == fyn_to ? "root" : "unattached"); fy_node_detach_and_free(fyn_to); - fyd->root = NULL; + + /* if destination was the root, remove it */ + if (fyn_to == fyd->root) + fyd->root = NULL; } else if (fyn_parent->type == FYNT_SEQUENCE) { fyd_doc_debug(fyd, "Deleting sequence node"); fy_node_list_del(&fyn_parent->sequence, fyn_to); @@ -2452,6 +2472,8 @@ err_out_rc: int fy_node_delete(struct fy_node *fyn) { + if (!fyn) + return 0; return fy_node_insert(fyn, NULL); } @@ -3024,7 +3046,7 @@ int fy_document_resolve(struct fy_document *fyd) /* for resolution to work, no reference loops should exist */ ret = fy_check_ref_loop(fyd, fyd->root, - FYNWF_MAXDEPTH_DEFAULT | FYNWF_FOLLOW, NULL); + FYNWF_MAXDEPTH_DEFAULT | FYNWF_FOLLOW, NULL, 0); fy_node_clear_system_marks(fyd->root); @@ -3290,13 +3312,14 @@ static struct fy_document *fy_document_build_internal(const struct fy_parse_cfg fyp->stream_error = false; /* if we collect diagnostics, we can continue */ - fyp_error_check(fyp, fyd || (fyp->cfg.flags & FYPCF_COLLECT_DIAG), err_out, - "fy_parse_load_document() failed"); + if (!fyd && !(fyp->cfg.flags & FYPCF_COLLECT_DIAG)) + goto err_out; /* no document, but we're collecting diagnostics */ if (!fyd) { - fyp_error(fyp, "fy_parse_load_document() failed"); + if (fyp->cfg.flags & FYPCF_COLLECT_DIAG) + fyp_error(fyp, "fy_parse_load_document() failed"); fyp->stream_error = false; fyd = fy_parse_document_create(fyp, NULL); @@ -3387,6 +3410,81 @@ enum fy_node_style fy_node_get_style(struct fy_node *fyn) return fyn ? fyn->style : FYNS_PLAIN; } +enum fy_node_style fy_node_set_style(struct fy_node *fyn, enum fy_node_style style) +{ + const struct fy_token_analysis *ta; + + /* a NULL node is always plain */ + if (!fyn) + return FYNS_PLAIN; + + /* same style */ + if (style == fyn->style) + return style; + + /* can't change an alias style */ + if (fyn->style == FYNS_ALIAS) + return fyn->style; + + /* any is easy... */ + if (style == FYNS_ANY) { + fyn->style = style; + return style; + } + + /* for a collection the choices are limited */ + if (fyn->type == FYNT_MAPPING || + fyn->type == FYNT_SEQUENCE) { + + /* only flow/block style is allowed */ + if (style == FYNS_FLOW || style == FYNS_BLOCK) + fyn->style = style; + + return fyn->style; + } + + /* a scalar, analyze it */ + ta = fy_token_text_analyze(fyn->scalar); + switch (style) { + case FYNS_PLAIN: + if (ta->flags & FYTTAF_CAN_BE_PLAIN) + fyn->style = style; + break; + case FYNS_SINGLE_QUOTED: + if (ta->flags & FYTTAF_CAN_BE_SINGLE_QUOTED) + fyn->style = style; + break; + + case FYNS_DOUBLE_QUOTED: + if (ta->flags & FYTTAF_CAN_BE_DOUBLE_QUOTED) + fyn->style = style; + break; + + case FYNS_LITERAL: + if (ta->flags & FYTTAF_CAN_BE_LITERAL) + fyn->style = style; + break; + + case FYNS_FOLDED: + /* we never support changing style to folded + * it is never emitted and frankly not worth the effort. + */ + break; + + default: + /* anything else, doesn't take */ + break; + + } + + /* NOTE even if the style has 'taken' the emitter is free to change it + * since the style might not be possible at a given state. For example + * setting a literal style when the emitter is in flow mode obviously + * will not work. + */ + return fyn->style; +} + struct fy_token *fy_node_get_start_token(struct fy_node *fyn) { if (!fyn) @@ -3438,7 +3536,14 @@ bool fy_node_is_null(struct fy_node *fyn) bool fy_node_is_attached(struct fy_node *fyn) { - return fyn ? fyn->attached : false; + if (!fyn) + return false; + + if (fyn->attached) + return true; + + /* finally root is attached (although the property is not set) */ + return fyn->fyd && fyn->fyd->root == fyn; } struct fy_node *fy_node_get_parent(struct fy_node *fyn) @@ -3964,6 +4069,22 @@ bool fy_node_is_empty(struct fy_node *fyn) return true; } +#ifdef _MSC_VER +/* MSVC version - use _alloca with helper function */ +static __inline struct fy_node_walk_ctx * +fy_node_walk_ctx_init_impl(struct fy_node_walk_ctx *ctx, unsigned int max_depth, unsigned int mark) +{ + ctx->max_depth = max_depth; + ctx->next_slot = 0; + ctx->mark = mark; + return ctx; +} +#define fy_node_walk_ctx_create_a(_max_depth, _mark) \ + fy_node_walk_ctx_init_impl( \ + (struct fy_node_walk_ctx *)_alloca(sizeof(struct fy_node_walk_ctx) + sizeof(struct fy_node *) * (_max_depth)), \ + (_max_depth), \ + (_mark)) +#else #define fy_node_walk_ctx_create_a(_max_depth, _mark) \ ({ \ unsigned int __max_depth = (_max_depth); \ @@ -3975,6 +4096,7 @@ bool fy_node_is_empty(struct fy_node *fyn) _ctx->mark = (_mark); \ _ctx; \ }) +#endif static inline void fy_node_walk_mark_start(struct fy_node_walk_ctx *ctx) { @@ -4115,8 +4237,8 @@ fy_node_by_path_internal(struct fy_node *fyn, const char *s, *e, *ss, *ee; char *end_idx, *json_key, *t, *p, *uri_path; char c; - int idx, rlen; - size_t len, json_key_len, uri_path_len; + int idx; + size_t len, rlen, json_key_len, uri_path_len; bool has_json_key_esc; uint8_t code[4]; int code_length; @@ -4385,7 +4507,7 @@ fy_node_by_path_internal(struct fy_node *fyn, while (ss < ee) { /* copy run until '%' or end */ p = memchr(ss, '%', ee - ss); - rlen = (p ? p : ee) - ss; + rlen = (size_t)((p ? p : ee) - ss); memcpy(t, ss, rlen); ss += rlen; t += rlen; @@ -4712,9 +4834,11 @@ struct fy_node *fy_node_create_relative_reference(struct fy_node *fyn_base, stru return fyn_ref; } -bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, - enum fy_node_walk_flags flags, - struct fy_node_walk_ctx *ctx) +bool +fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, + enum fy_node_walk_flags flags, + struct fy_node_walk_ctx *ctx, + unsigned int depth) { struct fy_node *fyni; struct fy_node_pair *fynp, *fynpi; @@ -4724,6 +4848,10 @@ bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, if (!fyn) return false; + /* over depth? */ + if (depth > fy_node_walk_max_depth_from_flags(flags)) + return true; + /* visited? no need to check */ if (fyn->marks & FY_BIT(FYNWF_VISIT_MARKER)) return false; @@ -4756,7 +4884,7 @@ bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, fyni = fy_node_follow_alias(fyn, flags); - ret = fy_check_ref_loop(fyd, fyni, flags, ctxn); + ret = fy_check_ref_loop(fyd, fyni, flags, ctxn, depth + 1); if (!ctx) fy_node_walk_mark_end(ctxn); @@ -4770,7 +4898,7 @@ bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, for (fyni = fy_node_list_head(&fyn->sequence); fyni; fyni = fy_node_next(&fyn->sequence, fyni)) { - ret = fy_check_ref_loop(fyd, fyni, flags, ctx); + ret = fy_check_ref_loop(fyd, fyni, flags, ctx, depth + 1); if (ret) break; } @@ -4781,11 +4909,11 @@ bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, fynpi = fy_node_pair_next(&fyn->mapping, fynp); - ret = fy_check_ref_loop(fyd, fynp->key, flags, ctx); + ret = fy_check_ref_loop(fyd, fynp->key, flags, ctx, depth + 1); if (ret) break; - ret = fy_check_ref_loop(fyd, fynp->value, flags, ctx); + ret = fy_check_ref_loop(fyd, fynp->value, flags, ctx, depth + 1); if (ret) break; } @@ -5246,11 +5374,12 @@ int fy_document_set_root(struct fy_document *fyd, struct fy_node *fyn) fy_node_detach_and_free(fyd->root); fyd->root = NULL; - fyn->parent = NULL; fyd->root = fyn; - if (fyn) + if (fyn) { + fyn->parent = NULL; fyn->attached = true; + } return 0; } @@ -5887,7 +6016,7 @@ void fy_node_mapping_perform_sort(struct fy_node *fyn_map, def_arg.cmp_fn = NULL; def_arg.arg = NULL; } - ctx.key_cmp = key_cmp ? : fy_node_mapping_sort_cmp_default; + ctx.key_cmp = key_cmp ? key_cmp : fy_node_mapping_sort_cmp_default; ctx.arg = key_cmp ? arg : &def_arg; ctx.fynpp = fynpp; ctx.count = count; @@ -7647,3 +7776,61 @@ fy_document_iterator_generate_next(struct fy_document_iterator *fydi) fydi->generator_state |= FYDIGF_GENERATED_NULL; return NULL; } + +const char * +fy_node_get_comment(struct fy_node *fyn, enum fy_comment_placement placement) +{ + struct fy_token *fyt; + + if (!fyn) + return NULL; + + switch (fyn->type) { + case FYNT_SCALAR: + fyt = fyn->scalar; + break; + case FYNT_SEQUENCE: + fyt = fyn->sequence_start; + break; + case FYNT_MAPPING: + fyt = fyn->mapping_start; + break; + default: + fyt = NULL; + break; + } + + if (!fyt) + return NULL; + + return fy_token_get_comment(fyt, placement); +} + +const char * +fy_node_get_comments(struct fy_node *fyn) +{ + struct fy_token *fyt; + + if (!fyn) + return NULL; + + switch (fyn->type) { + case FYNT_SCALAR: + fyt = fyn->scalar; + break; + case FYNT_SEQUENCE: + fyt = fyn->sequence_start; + break; + case FYNT_MAPPING: + fyt = fyn->mapping_start; + break; + default: + fyt = NULL; + break; + } + + if (!fyt) + return NULL; + + return fy_token_get_comments(fyt); +} diff --git a/src/lib/fy-doc.h b/src/lib/fy-doc.h index c9c46c9..e24be5b 100644 --- a/src/lib/fy-doc.h +++ b/src/lib/fy-doc.h @@ -157,7 +157,8 @@ bool fy_node_is_empty(struct fy_node *fyn); bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn, enum fy_node_walk_flags flags, - struct fy_node_walk_ctx *ctx); + struct fy_node_walk_ctx *ctx, + unsigned int depth); #define FYNWF_VISIT_MARKER (FYNWF_MAX_USER_MARKER + 1) #define FYNWF_REF_MARKER (FYNWF_MAX_USER_MARKER + 2) diff --git a/src/lib/fy-docbuilder-diag.c b/src/lib/fy-docbuilder-diag.c index 841469f..ef25257 100644 --- a/src/lib/fy-docbuilder-diag.c +++ b/src/lib/fy-docbuilder-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include @@ -36,7 +33,7 @@ int fy_document_builder_vdiag(struct fy_document_builder *fydb, unsigned int fla return -1; /* perform the enable tests early to avoid the overhead */ - if (((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < fydb->cfg.diag->cfg.level) + if ((int)((flags & FYDF_LEVEL_MASK) >> FYDF_LEVEL_SHIFT) < (int)fydb->cfg.diag->cfg.level) return 0; /* fill in fy_diag_ctx */ diff --git a/src/lib/fy-docbuilder.c b/src/lib/fy-docbuilder.c index f4a8d09..8ebadfa 100644 --- a/src/lib/fy-docbuilder.c +++ b/src/lib/fy-docbuilder.c @@ -15,7 +15,6 @@ #include #include #include -#include #include diff --git a/src/lib/fy-docstate.c b/src/lib/fy-docstate.c index 6c530bb..4071e57 100644 --- a/src/lib/fy-docstate.c +++ b/src/lib/fy-docstate.c @@ -32,6 +32,7 @@ struct fy_document_state *fy_document_state_alloc(void) return NULL; memset(fyds, 0, sizeof(*fyds)); + fyds->fyt_ds = NULL; fyds->fyt_vd = NULL; fy_token_list_init(&fyds->fyt_td); @@ -47,6 +48,7 @@ void fy_document_state_free(struct fy_document_state *fyds) assert(fyds->refs == 1); + fy_token_unref(fyds->fyt_ds); fy_token_unref(fyds->fyt_vd); fy_token_list_unref_all(&fyds->fyt_td); @@ -156,6 +158,7 @@ struct fy_document_state *fy_document_state_default( memset(&fyds->start_mark, 0, sizeof(fyds->start_mark)); memset(&fyds->end_mark, 0, sizeof(fyds->end_mark)); + fyds->fyt_ds = NULL; fyds->fyt_vd = NULL; fy_token_list_init(&fyds->fyt_td); @@ -191,6 +194,20 @@ struct fy_document_state *fy_document_state_copy(struct fy_document_state *fyds) fyds_new->start_mark = fyds->start_mark; fyds_new->end_mark = fyds->end_mark; + if (fyds->fyt_ds) { + fyt = fy_token_alloc(); + if (!fyt) + goto err_out; + + fyt->type = FYTT_DOCUMENT_START; + fyt->handle = fyds->fyt_ds->handle; + + /* take reference */ + fy_input_ref(fyt->handle.fyi); + + fyds_new->fyt_ds = fyt; + } + if (fyds->fyt_vd) { fyt = fy_token_alloc(); if (!fyt) diff --git a/src/lib/fy-docstate.h b/src/lib/fy-docstate.h index eb8df53..98cc196 100644 --- a/src/lib/fy-docstate.h +++ b/src/lib/fy-docstate.h @@ -37,6 +37,7 @@ struct fy_document_state { bool json_mode : 1; struct fy_mark start_mark; struct fy_mark end_mark; + struct fy_token *fyt_ds; /* the document start token (if any) */ struct fy_token *fyt_vd; /* version directive */ struct fy_token_list fyt_td; /* tag directives */ }; diff --git a/src/lib/fy-dump.c b/src/lib/fy-dump.c index 17a7543..84f9702 100644 --- a/src/lib/fy-dump.c +++ b/src/lib/fy-dump.c @@ -11,11 +11,6 @@ #include #include -#include -#include -#include -#include -#include #include #include #include diff --git a/src/lib/fy-emit-accum.h b/src/lib/fy-emit-accum.h index fd9bb67..e46ffa5 100644 --- a/src/lib/fy-emit-accum.h +++ b/src/lib/fy-emit-accum.h @@ -338,7 +338,7 @@ fy_emit_accum_empty(struct fy_emit_accum *ea) return ea->next == 0; } -static inline int +static inline size_t fy_emit_accum_size(struct fy_emit_accum *ea) { return ea->next; diff --git a/src/lib/fy-emit.c b/src/lib/fy-emit.c index cf1ef91..3b37d7c 100644 --- a/src/lib/fy-emit.c +++ b/src/lib/fy-emit.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -45,7 +44,7 @@ static inline void fy_emit_token_unref(struct fy_emitter *emit, struct fy_parser } /* fwd decl */ -void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len); +void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, size_t len); void fy_emit_printf(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *fmt, ...) __attribute__((format(printf, 3, 4))); @@ -141,7 +140,9 @@ static inline int fy_emit_width(struct fy_emitter *emit) static inline bool fy_emit_output_comments(struct fy_emitter *emit) { - return !!(emit->xcfg.cfg.flags & FYECF_OUTPUT_COMMENTS); + return !!(emit->xcfg.cfg.flags & FYECF_OUTPUT_COMMENTS) && + !fy_emit_is_oneline_or_compact(emit) && + !fy_emit_is_json_mode(emit); } static int fy_emit_node_check_json(struct fy_emitter *emit, struct fy_node *fyn) @@ -212,23 +213,23 @@ void fy_emit_scalar(struct fy_emitter *emit, struct fy_node *fyn, int flags, int void fy_emit_sequence(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent); void fy_emit_mapping(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent); -void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len); +void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, size_t len); void fy_emit_puts(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str); void fy_emit_putc(struct fy_emitter *emit, enum fy_emitter_write_type type, int c); /* simple write, just ascii, advance column */ -void fy_emit_write_simple(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len) +void fy_emit_write_simple(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, size_t len) { int outlen; if (!len) return; - outlen = emit->xcfg.cfg.output(emit, type, str, len, emit->xcfg.cfg.userdata); - if (outlen != len) + outlen = emit->xcfg.cfg.output(emit, type, str, (int)len, emit->xcfg.cfg.userdata); + if (outlen != (int)len) emit->output_error = true; - emit->column += len; + emit->column += (int)len; } /* simple puts as above */ @@ -248,17 +249,17 @@ void fy_emit_putc_simple(struct fy_emitter *emit, enum fy_emitter_write_type typ } } -void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len) +void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, size_t len) { int c, w; const char *m, *e; int outlen; - if (!len) + if (!len || len > INT_MAX) return; - outlen = emit->xcfg.cfg.output(emit, type, str, len, emit->xcfg.cfg.userdata); - if (outlen != len) + outlen = emit->xcfg.cfg.output(emit, type, str, (int)len, emit->xcfg.cfg.userdata); + if (outlen != (int)len) emit->output_error = true; e = str + len; @@ -596,6 +597,8 @@ struct fy_token *fy_node_value_token(struct fy_node *fyn) bool fy_emit_token_has_comment(struct fy_emitter *emit, struct fy_token *fyt, enum fy_comment_placement placement) { + if (!fy_emit_output_comments(emit)) + return false; return fy_emit_token_comment_handle(emit, fyt, placement) ? true : false; } @@ -610,7 +613,7 @@ void fy_emit_token_comment(struct fy_emitter *emit, struct fy_token *fyt, int fl struct fy_atom *handle; char *text; const char *t; - int len; + size_t len; handle = fy_emit_token_comment_handle(emit, fyt, placement); if (!handle) @@ -639,21 +642,6 @@ void fy_emit_token_comment(struct fy_emitter *emit, struct fy_token *fyt, int fl } } -void fy_emit_node_comment(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, - enum fy_comment_placement placement) -{ - struct fy_token *fyt; - - if (!fy_emit_output_comments(emit) || (unsigned int)placement >= fycp_max) - return; - - fyt = fy_node_value_token(fyn); - if (!fyt) - return; - - fy_emit_token_comment(emit, fyt, flags, indent, placement); -} - void fy_emit_common_node_preamble(struct fy_emitter *emit, struct fy_token *fyt_value, struct fy_token *fyt_anchor, @@ -669,13 +657,11 @@ void fy_emit_common_node_preamble(struct fy_emitter *emit, bool json_mode = false; struct fy_emit_save_ctx *sc = &emit->s_sc; + (void)sc; + json_mode = fy_emit_is_json_mode(emit); if (!json_mode) { - - if (fy_emit_token_has_comment(emit, fyt_value, fycp_top)) - fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_top); - if (!(emit->xcfg.cfg.flags & FYECF_STRIP_LABELS)) { if (fyt_anchor) anchor = fy_token_get_text(fyt_anchor, &anchor_len); @@ -1104,7 +1090,7 @@ void fy_emit_token_write_quoted(struct fy_emitter *emit, struct fy_token *fyt, i case '\v': fy_emit_accum_utf8_put(&emit->ea, 'v'); break; - case '\e': + case '\x1b': // \e fy_emit_accum_utf8_put(&emit->ea, 'e'); break; case 0x85: @@ -1429,7 +1415,7 @@ fy_emit_token_scalar_style(struct fy_emitter *emit, struct fy_token *fyt, } /* anything not empty is double quoted here */ - style = !(ta->flags & FYTTAF_EMPTY) ? FYNS_PLAIN : FYNS_DOUBLE_QUOTED; + style = (ta->flags & FYTTAF_EMPTY) ? FYNS_PLAIN : FYNS_DOUBLE_QUOTED; } /* try to pretify */ @@ -1481,8 +1467,17 @@ out: void fy_emit_token_scalar(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent, enum fy_node_style style, struct fy_token *fyt_tag) { + struct fy_emit_save_ctx *sc = &emit->s_sc; + assert(style != FYNS_FLOW && style != FYNS_BLOCK); + if (sc->flags & DDNF_HANGING_INDENT) + fy_emit_write_indent(emit, sc->indent); + + /* root scalar with a comment needs special handling */ + if ((flags & DDNF_ROOT) && fy_emit_token_has_comment(emit, fyt, fycp_top)) + fy_emit_token_comment(emit, fyt, flags, indent, fycp_top); + indent = fy_emit_increase_indent(emit, flags, indent); if (!fy_emit_whitespace(emit)) @@ -1512,6 +1507,12 @@ void fy_emit_token_scalar(struct fy_emitter *emit, struct fy_token *fyt, int fla default: break; } + + /* root scalar with a comment needs special handling */ + if ((flags & DDNF_ROOT) && fy_emit_token_has_comment(emit, fyt, fycp_right)) { + fy_emit_token_comment(emit, fyt, flags, indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; + } } void fy_emit_scalar(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, bool is_key) @@ -1531,11 +1532,17 @@ void fy_emit_scalar(struct fy_emitter *emit, struct fy_node *fyn, int flags, int style, fyn->tag); } -static void fy_emit_sequence_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc) +static void fy_emit_sequence_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, + struct fy_token *fyt) { bool json = fy_emit_is_json_mode(emit); bool was_flow = sc->flow; + if (fy_emit_token_has_comment(emit, fyt, fycp_top)) { + fy_emit_token_comment(emit, fyt, sc->flags, sc->indent, fycp_top); + sc->flags |= DDNF_HANGING_INDENT; + } + sc->old_indent = sc->indent; if (!json) { if (fy_emit_is_manual(emit)) @@ -1584,24 +1591,23 @@ static void fy_emit_sequence_epilog(struct fy_emitter *emit, struct fy_emit_save static void fy_emit_sequence_item_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, struct fy_token *fyt_value) { - int tmp_indent; + bool has_comment; sc->flags |= DDNF_SEQ; - if (fy_emit_token_has_comment(emit, fyt_value, fycp_top) || - !fy_emit_is_oneline_or_compact(emit) || + has_comment = fy_emit_token_has_comment(emit, fyt_value, fycp_top); + + if (!fy_emit_is_oneline_or_compact(emit) || ((fy_emit_is_compact(emit) || sc->flow) && emit->column >= fy_emit_width(emit))) fy_emit_write_indent(emit, sc->indent); + if (has_comment) { + fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_top); + sc->flags |= DDNF_HANGING_INDENT; + } + if (!sc->flow && !fy_emit_is_json_mode(emit)) fy_emit_write_indicator(emit, di_dash, sc->flags, sc->indent, fyewt_indicator); - - tmp_indent = sc->indent; - if (fy_emit_token_has_comment(emit, fyt_value, fycp_top)) { - if (!sc->flow && !fy_emit_is_json_mode(emit)) - tmp_indent = fy_emit_increase_indent(emit, sc->flags, sc->indent); - fy_emit_token_comment(emit, fyt_value, sc->flags, tmp_indent, fycp_top); - } } static void fy_emit_sequence_item_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, @@ -1612,7 +1618,10 @@ static void fy_emit_sequence_item_epilog(struct fy_emitter *emit, struct fy_emit if ((sc->flow || fy_emit_is_json_mode(emit)) && !last) fy_emit_write_indicator(emit, di_comma, sc->flags, sc->indent, fyewt_indicator); - fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right); + if (fy_emit_token_has_comment(emit, fyt_value, fycp_right)) { + fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; + } needs_hanging_indent = sc->flow && !fy_emit_is_oneline_or_compact(emit) && !sc->empty; @@ -1641,7 +1650,7 @@ void fy_emit_sequence(struct fy_emitter *emit, struct fy_node *fyn, int flags, i sc->xstyle = fyn->style; sc->old_indent = sc->indent; - fy_emit_sequence_prolog(emit, sc); + fy_emit_sequence_prolog(emit, sc, fyn->sequence_start); for (fyni = fy_node_list_head(&fyn->sequence); fyni; fyni = fynin) { @@ -1657,11 +1666,17 @@ void fy_emit_sequence(struct fy_emitter *emit, struct fy_node *fyn, int flags, i fy_emit_sequence_epilog(emit, sc); } -static void fy_emit_mapping_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc) +static void fy_emit_mapping_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, + struct fy_token *fyt) { bool json = fy_emit_is_json_mode(emit); bool was_flow = sc->flow; + if (fy_emit_token_has_comment(emit, fyt, fycp_top)) { + fy_emit_token_comment(emit, fyt, sc->flags, sc->indent, fycp_top); + sc->flags |= DDNF_HANGING_INDENT; + } + sc->old_indent = sc->indent; if (!json) { if (fy_emit_is_manual(emit)) @@ -1708,7 +1723,7 @@ static void fy_emit_mapping_key_prolog(struct fy_emitter *emit, struct fy_emit_s struct fy_token *fyt_key, bool simple_key) { const struct fy_token_analysis *ta; - bool do_indent, key_over; + bool do_indent, key_over, has_comment; sc->flags = DDNF_MAP | (sc->flags & DDNF_FLOW); @@ -1716,7 +1731,9 @@ static void fy_emit_mapping_key_prolog(struct fy_emitter *emit, struct fy_emit_s ((fy_emit_is_compact(emit) || sc->flow) && emit->column >= fy_emit_width(emit))) fy_emit_write_indent(emit, sc->indent); - if (simple_key) { + /* if we have a right comment then it's by a definition not a simple key */ + has_comment = fy_emit_token_has_comment(emit, fyt_key, fycp_right); + if (simple_key /* && !has_comment */ ) { sc->flags |= DDNF_SIMPLE; if (fyt_key && fyt_key->type == FYTT_SCALAR) sc->flags |= DDNF_SIMPLE_SCALAR_KEY; @@ -1727,7 +1744,7 @@ static void fy_emit_mapping_key_prolog(struct fy_emitter *emit, struct fy_emit_s } do_indent = false; - if (!fy_emit_is_oneline_or_compact(emit)) { + if (!has_comment && !fy_emit_is_oneline_or_compact(emit)) { if (fyt_key && fyt_key->type != FYTT_SCALAR) /* always indent on non scalar keys */ key_over = true; @@ -1747,15 +1764,14 @@ static void fy_emit_mapping_key_prolog(struct fy_emitter *emit, struct fy_emit_s fy_emit_write_indent(emit, sc->indent); /* complex? */ - if (!(sc->flags & DDNF_SIMPLE)) + if (!(sc->flags & DDNF_SIMPLE)) { fy_emit_write_indicator(emit, di_question_mark, sc->flags, sc->indent, fyewt_indicator); + } } static void fy_emit_mapping_key_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, struct fy_token *fyt_key) { - int tmp_indent; - /* if the key is an alias, always output an extra whitespace */ if (fyt_key && fyt_key->type == FYTT_ALIAS) fy_emit_write_ws(emit); @@ -1763,24 +1779,28 @@ static void fy_emit_mapping_key_epilog(struct fy_emitter *emit, struct fy_emit_s sc->flags &= ~DDNF_MAP; if (!(sc->flags & DDNF_SIMPLE)) { + + if (fy_emit_token_has_comment(emit, fyt_key, fycp_right)) { + fy_emit_token_comment(emit, fyt_key, sc->flags, sc->indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; + } + if (!emit->flow_level && !fy_emit_is_oneline_or_compact(emit)) fy_emit_write_indent(emit, sc->indent); if (!fy_emit_whitespace(emit)) fy_emit_write_ws(emit); + } else { } - fy_emit_write_indicator(emit, di_colon, sc->flags, sc->indent, fyewt_indicator); - - tmp_indent = sc->indent; - if (fy_emit_token_has_comment(emit, fyt_key, fycp_right)) { - if (!sc->flow && !fy_emit_is_json_mode(emit)) - tmp_indent = fy_emit_increase_indent(emit, sc->flags, sc->indent); + fy_emit_write_indicator(emit, di_colon, sc->flags, sc->indent, fyewt_indicator); - fy_emit_token_comment(emit, fyt_key, sc->flags, tmp_indent, fycp_right); - fy_emit_write_indent(emit, tmp_indent); + sc->flags &= ~DDNF_HANGING_INDENT; + if ((sc->flags & DDNF_SIMPLE) && fy_emit_token_has_comment(emit, fyt_key, fycp_right)) { + fy_emit_token_comment(emit, fyt_key, sc->flags, sc->indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; } - sc->flags = DDNF_MAP | (sc->flags & DDNF_FLOW); + sc->flags = DDNF_MAP | (sc->flags & (DDNF_FLOW | DDNF_HANGING_INDENT)); } static void fy_emit_mapping_value_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc, @@ -1789,6 +1809,9 @@ static void fy_emit_mapping_value_prolog(struct fy_emitter *emit, struct fy_emit const struct fy_token_analysis *ta; bool value_over; + if (sc->flags & DDNF_HANGING_INDENT) + fy_emit_write_indent(emit, sc->indent); + /* don't do anything for those cases */ if (!sc->flow || fy_emit_is_oneline_or_compact(emit) || !fyt_value || fyt_value->type != FYTT_SCALAR) return; @@ -1810,7 +1833,10 @@ static void fy_emit_mapping_value_epilog(struct fy_emitter *emit, struct fy_emit if ((sc->flow || fy_emit_is_json_mode(emit)) && !last) fy_emit_write_indicator(emit, di_comma, sc->flags, sc->indent, fyewt_indicator); - fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right); + if (fy_emit_token_has_comment(emit, fyt_value, fycp_right)) { + fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; + } needs_hanging_indent = sc->flow && !fy_emit_is_oneline_or_compact(emit) && !sc->empty; @@ -1841,7 +1867,7 @@ void fy_emit_mapping(struct fy_emitter *emit, struct fy_node *fyn, int flags, in sc->xstyle = fyn->style; sc->old_indent = sc->indent; - fy_emit_mapping_prolog(emit, sc); + fy_emit_mapping_prolog(emit, sc, fyn->mapping_start); if (!(emit->xcfg.cfg.flags & (FYECF_SORT_KEYS | FYECF_STRIP_EMPTY_KV))) { fynp = fy_node_pair_list_head(&fyn->mapping); @@ -1948,6 +1974,7 @@ int fy_emit_common_document_start(struct fy_emitter *emit, struct fy_document_state *fyds, bool root_tag_or_anchor) { + struct fy_emit_save_ctx *sc = &emit->s_sc; struct fy_token *fyt_chk; const char *td_handle, *td_prefix; size_t td_handle_size, td_prefix_size; @@ -1976,7 +2003,9 @@ int fy_emit_common_document_start(struct fy_emitter *emit, /* if either a version or directive tags exist, and no previous * explicit document end existed, output one now */ - if (!fy_emit_is_json_mode(emit) && (vd || td) && !(emit->flags & FYEF_HAD_DOCUMENT_END)) { + if (!fy_emit_is_json_mode(emit) && (vd || td) && + (emit->flags & (FYEF_HAD_DOCUMENT_END | FYEF_HAD_DOCUMENT_OUTPUT)) == + (FYEF_HAD_DOCUMENT_END | FYEF_HAD_DOCUMENT_OUTPUT)) { if (emit->column) fy_emit_putc_simple(emit, fyewt_linebreak, '\n'); if (!strip_doc) { @@ -1986,6 +2015,20 @@ int fy_emit_common_document_start(struct fy_emitter *emit, } } + /* top comment */ + if (fy_emit_token_has_comment(emit, fyds->fyt_ds, fycp_top)) { + if (emit->column) + fy_emit_putc_simple(emit, fyewt_linebreak, '\n'); + fy_emit_token_comment(emit, fyds->fyt_ds, sc->flags, sc->indent, fycp_top); + } + /* right comment */ + if (fy_emit_token_has_comment(emit, fyds->fyt_ds, fycp_right)) { + if (emit->column) + fy_emit_putc_simple(emit, fyewt_linebreak, '\n'); + fy_emit_token_comment(emit, fyds->fyt_ds, sc->flags, sc->indent, fycp_right); + sc->flags |= DDNF_HANGING_INDENT; + } + if (!fy_emit_is_json_mode(emit) && vd) { if (emit->column) fy_emit_putc_simple(emit, fyewt_linebreak, '\n'); @@ -2060,7 +2103,7 @@ int fy_emit_document_start(struct fy_emitter *emit, struct fy_document *fyd, if (!emit || !fyd || !fyd->fyds) return -1; - root = fyn_root ? : fy_document_root(fyd); + root = fyn_root ? fyn_root : fy_document_root(fyd); root_tag_or_anchor = root && (root->tag || fy_document_lookup_anchor_by_node(fyd, root)); @@ -2375,17 +2418,8 @@ int fy_emit_root_node_no_check(struct fy_emitter *emit, struct fy_node *fyn) if (!emit || !fyn) return -1; - /* top comment first */ - fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_top); - fy_emit_node_internal(emit, fyn, DDNF_ROOT, -1, false); - /* right comment next */ - fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_right); - - /* bottom comment last */ - fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_bottom); - return 0; } @@ -2428,6 +2462,8 @@ int fy_emit_document_no_check(struct fy_emitter *emit, struct fy_document *fyd) rc = fy_emit_document_end(emit); + fy_emit_reset(emit, false); + return rc; } @@ -2542,11 +2578,14 @@ static int do_buffer_output(struct fy_emitter *emit, enum fy_emitter_write_type size_t left, pagesize, size, len; char *bufnew; + if (leni < 0) + return -1; + /* convert to unsigned and use that */ len = (size_t)leni; /* no funky business */ - if (len < 0) + if ((ssize_t)len < 0) return -1; state->need += len; @@ -2574,7 +2613,7 @@ static int do_buffer_output(struct fy_emitter *emit, enum fy_emitter_write_type memcpy(state->buf + state->pos, str, len); state->pos += len; - return len; + return (int)len; } static void @@ -2600,6 +2639,9 @@ fy_emitter_create_str_internal(enum fy_emitter_cfg_flags flags, char **bufp, siz struct fy_emitter_cfg emit_cfg; struct fy_emit_buffer_state *state; + if (flags & FYECF_EXTENDED_CFG) + return NULL; + state = malloc(sizeof(*state)); if (!state) return NULL; @@ -2743,7 +2785,9 @@ int fy_emit_document_to_buffer(struct fy_document *fyd, enum fy_emitter_cfg_flag rc = fy_emit_str_internal(fyd, flags, NULL, &buf, &size, false); if (rc != 0) return -1; - return size; + if ((int)size < 0) + return -1; + return (int)size; } char *fy_emit_document_to_string(struct fy_document *fyd, enum fy_emitter_cfg_flags flags) @@ -2812,15 +2856,15 @@ fy_emit_to_string_collect(struct fy_emitter *emit, size_t *sizep) static int do_file_output(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int leni, void *userdata) { FILE *fp = userdata; - size_t len; - - len = (size_t)leni; + size_t len, wrn; /* no funky stuff */ - if (len < 0) + if (leni < 0) return -1; - return fwrite(str, 1, len, fp); + len = (size_t)leni; + wrn = fwrite(str, 1, len, fp); + return (int)wrn; } int fy_emit_document_to_fp(struct fy_document *fyd, enum fy_emitter_cfg_flags flags, @@ -2830,7 +2874,8 @@ int fy_emit_document_to_fp(struct fy_document *fyd, enum fy_emitter_cfg_flags fl struct fy_emitter_cfg emit_cfg; int rc; - if (!fp) + /* we don't allow an extended configuration */ + if (!fp || (flags & FYECF_EXTENDED_CFG)) return -1; memset(&emit_cfg, 0, sizeof(emit_cfg)); @@ -2905,7 +2950,7 @@ static int do_fd_output(struct fy_emitter *emit, enum fy_emitter_write_type type len -= wrn; str += wrn; - total += wrn; + total += (int)wrn; } return total; @@ -2917,7 +2962,7 @@ int fy_emit_document_to_fd(struct fy_document *fyd, enum fy_emitter_cfg_flags fl struct fy_emitter_cfg emit_cfg; int rc; - if (fd < 0) + if (fd < 0 || (flags & FYECF_EXTENDED_CFG)) return -1; memset(&emit_cfg, 0, sizeof(emit_cfg)); @@ -2946,7 +2991,7 @@ int fy_emit_node_to_buffer(struct fy_node *fyn, enum fy_emitter_cfg_flags flags, rc = fy_emit_str_internal(NULL, flags, fyn, &buf, &size, false); if (rc != 0) return -1; - return size; + return (int)size; } char *fy_emit_node_to_string(struct fy_node *fyn, enum fy_emitter_cfg_flags flags) @@ -3141,7 +3186,9 @@ static int fy_emit_streaming_node(struct fy_emitter *emit, struct fy_parser *fyp enum fy_node_style style, xstyle; int ret, s_flags, s_indent; - if (fye->type != FYET_ALIAS && fye->type != FYET_SCALAR && + /* force collections after --- to start at a new line */ + if (!fy_emit_is_oneline(emit) && + fye->type != FYET_ALIAS && fye->type != FYET_SCALAR && (emit->s_flags & DDNF_ROOT) && emit->column != 0) { fy_emit_putc_simple(emit, fyewt_linebreak, '\n'); emit->flags |= FYEF_WHITESPACE | FYEF_INDENTATION; @@ -3202,7 +3249,7 @@ static int fy_emit_streaming_node(struct fy_emitter *emit, struct fy_parser *fyp sc->s_flags = s_flags; sc->s_indent = s_indent; - fy_emit_sequence_prolog(emit, sc); + fy_emit_sequence_prolog(emit, sc, fye->sequence_start.sequence_start); sc->flags &= ~DDNF_MAP; sc->flags |= DDNF_SEQ; @@ -3243,7 +3290,7 @@ static int fy_emit_streaming_node(struct fy_emitter *emit, struct fy_parser *fyp sc->s_flags = s_flags; sc->s_indent = s_indent; - fy_emit_mapping_prolog(emit, sc); + fy_emit_mapping_prolog(emit, sc, fye->mapping_start.mapping_start); sc->flags &= ~DDNF_SEQ; sc->flags |= DDNF_MAP; @@ -3353,13 +3400,13 @@ static int fy_emit_handle_sequence_item(struct fy_emitter *emit, struct fy_parse struct fy_token *fyt_item = NULL; int ret; - fy_emit_token_unref(emit, fyp, sc->fyt_last_value); - sc->fyt_last_value = NULL; - switch (fye->type) { case FYET_SEQUENCE_END: fy_emit_sequence_item_epilog(emit, sc, true, sc->fyt_last_value); + fy_emit_token_unref(emit, fyp, sc->fyt_last_value); + sc->fyt_last_value = NULL; + /* emit epilog */ fy_emit_sequence_epilog(emit, sc); @@ -3392,6 +3439,11 @@ static int fy_emit_handle_sequence_item(struct fy_emitter *emit, struct fy_parse return -1; } + if (!first) + fy_emit_sequence_item_epilog(emit, sc, false, sc->fyt_last_value); + fy_emit_token_unref(emit, fyp, sc->fyt_last_value); + sc->fyt_last_value = NULL; + ret = fy_emit_push_state(emit, FYES_SEQUENCE_ITEM); if (ret) return ret; @@ -3400,9 +3452,6 @@ static int fy_emit_handle_sequence_item(struct fy_emitter *emit, struct fy_parse emit->s_indent = sc->indent; emit->s_flags = sc->flags; - if (!first) - fy_emit_sequence_item_epilog(emit, sc, false, sc->fyt_last_value); - sc->fyt_last_value = fyt_item; fy_emit_sequence_item_prolog(emit, sc, fyt_item); @@ -3438,11 +3487,6 @@ static int fy_emit_handle_mapping_key(struct fy_emitter *emit, struct fy_parser int ret; bool simple_key; - fy_emit_token_unref(emit, fyp, sc->fyt_last_key); - sc->fyt_last_key = NULL; - fy_emit_token_unref(emit, fyp, sc->fyt_last_value); - sc->fyt_last_value = NULL; - simple_key = false; switch (fye->type) { @@ -3452,6 +3496,11 @@ static int fy_emit_handle_mapping_key(struct fy_emitter *emit, struct fy_parser /* emit epilog */ fy_emit_mapping_epilog(emit, sc); + fy_emit_token_unref(emit, fyp, sc->fyt_last_key); + sc->fyt_last_key = NULL; + fy_emit_token_unref(emit, fyp, sc->fyt_last_value); + sc->fyt_last_value = NULL; + /* restore indent and flags */ emit->s_indent = sc->s_indent; emit->s_flags = sc->s_flags; @@ -3497,6 +3546,11 @@ static int fy_emit_handle_mapping_key(struct fy_emitter *emit, struct fy_parser if (!first) fy_emit_mapping_value_epilog(emit, sc, false, sc->fyt_last_value); + fy_emit_token_unref(emit, fyp, sc->fyt_last_key); + sc->fyt_last_key = NULL; + fy_emit_token_unref(emit, fyp, sc->fyt_last_value); + sc->fyt_last_value = NULL; + sc->fyt_last_key = fyt_key; fy_emit_mapping_key_prolog(emit, sc, fyt_key, simple_key); @@ -3904,7 +3958,7 @@ int fy_emitter_default_output(struct fy_emitter *fye, enum fy_emitter_write_type if (color) raw_default_output(fp, fd, color, strlen(color)); - ret = raw_default_output(fp, fd, str, len); + ret = (int)raw_default_output(fp, fd, str, len); if (color) raw_default_output(fp, fd, A_RESET, strlen(A_RESET)); diff --git a/src/lib/fy-emit.h b/src/lib/fy-emit.h index 9269c62..be64fc8 100644 --- a/src/lib/fy-emit.h +++ b/src/lib/fy-emit.h @@ -127,7 +127,7 @@ struct fy_emitter { int fy_emit_setup(struct fy_emitter *emit, const struct fy_emitter_cfg *cfg); void fy_emit_cleanup(struct fy_emitter *emit); -void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len); +void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, size_t len); static inline bool fy_emit_whitespace(struct fy_emitter *emit) { diff --git a/src/lib/fy-event.c b/src/lib/fy-event.c index f042713..fc8723a 100644 --- a/src/lib/fy-event.c +++ b/src/lib/fy-event.c @@ -169,8 +169,10 @@ void fy_parser_event_free(struct fy_parser *fyp, struct fy_event *fye) if (!fyp || !fye) return; - if (fy_reader_generates_events(fyp->reader)) - return fy_reader_event_free(fyp->reader, fye); + if (fy_reader_generates_events(fyp->reader)) { + fy_reader_event_free(fyp->reader, fye); + return; + } fyep = container_of(fye, struct fy_eventp, e); @@ -340,6 +342,7 @@ fy_eventp_vcreate_internal(struct fy_eventp_list *recycled_list, struct fy_diag case FYET_ALIAS: if (type == FYET_SCALAR) { + fye->scalar.value = fye->scalar.anchor = fye->scalar.tag = NULL; sstyle = va_arg(ap, enum fy_scalar_style); value = va_arg(ap, const char *); len = va_arg(ap, size_t); @@ -350,6 +353,7 @@ fy_eventp_vcreate_internal(struct fy_eventp_list *recycled_list, struct fy_diag if (len == FY_NT) len = strlen(value); } else { + fye->alias.anchor = NULL; sstyle = FYSS_PLAIN; value = va_arg(ap, const char *); if (!value) { @@ -1015,6 +1019,229 @@ fy_document_start_event_version(struct fy_event *fye) return fy_document_state_version(fye->document_start.document_state); } +char *fy_event_to_string(struct fy_event *fye) +{ + struct fy_memstream *fyms = NULL; + FILE *fp = NULL; + char *mbuf = NULL; + const char *anchor = NULL; + const char *tag = NULL; + const char *text = NULL; + const char *alias = NULL; + size_t msize, anchor_len = 0, tag_len = 0, text_len = 0, alias_len = 0; + enum fy_scalar_style style; + const uint8_t *p; + int i, c, w; + + if (!fye) + return NULL; + + fyms = fy_memstream_open(&fp); + if (!fyms) + return NULL; + + /* event type */ + switch (fye->type) { + case FYET_NONE: + fprintf(fp, "???"); + break; + case FYET_STREAM_START: + fprintf(fp, "+STR"); + break; + case FYET_STREAM_END: + fprintf(fp, "-STR"); + break; + case FYET_DOCUMENT_START: + fprintf(fp, "+DOC"); + break; + case FYET_DOCUMENT_END: + fprintf(fp, "-DOC"); + break; + case FYET_MAPPING_START: + fprintf(fp, "+MAP"); + if (fye->mapping_start.anchor) + anchor = fy_token_get_text(fye->mapping_start.anchor, &anchor_len); + if (fye->mapping_start.tag) + tag = fy_token_get_text(fye->mapping_start.tag, &tag_len); + if (fy_event_get_node_style(fye) == FYNS_FLOW) + fprintf(fp, " {}"); + break; + case FYET_MAPPING_END: + fprintf(fp, "-MAP"); + break; + case FYET_SEQUENCE_START: + fprintf(fp, "+SEQ"); + if (fye->sequence_start.anchor) + anchor = fy_token_get_text(fye->sequence_start.anchor, &anchor_len); + if (fye->sequence_start.tag) + tag = fy_token_get_text(fye->sequence_start.tag, &tag_len); + if (fy_event_get_node_style(fye) == FYNS_FLOW) + fprintf(fp, " []"); + break; + case FYET_SEQUENCE_END: + fprintf(fp, "-SEQ"); + break; + case FYET_SCALAR: + fprintf(fp, "=VAL"); + if (fye->scalar.anchor) + anchor = fy_token_get_text(fye->scalar.anchor, &anchor_len); + if (fye->scalar.tag) + tag = fy_token_get_text(fye->scalar.tag, &tag_len); + break; + case FYET_ALIAS: + fprintf(fp, "=ALI"); + break; + default: + break; + } + + /* (position) anchor and tag */ + if (anchor) + fprintf(fp, " &%.*s", (int)anchor_len, anchor); + if (tag) + fprintf(fp, " <%.*s>", (int)tag_len, tag); + + /* style hint */ + switch (fye->type) { + default: + break; + case FYET_DOCUMENT_START: + if (!fy_document_event_is_implicit(fye)) + fprintf(fp, " ---"); + break; + case FYET_DOCUMENT_END: + if (!fy_document_event_is_implicit(fye)) + fprintf(fp, " ..."); + break; + case FYET_MAPPING_START: + break; + case FYET_SEQUENCE_START: + break; + case FYET_SCALAR: + style = fy_token_scalar_style(fye->scalar.value); + switch (style) { + case FYSS_ANY: + fprintf(fp, " !"); + break; + case FYSS_PLAIN: + fprintf(fp, " :"); + break; + case FYSS_SINGLE_QUOTED: + fprintf(fp, " '"); + break; + case FYSS_DOUBLE_QUOTED: + fprintf(fp, " \""); + break; + case FYSS_LITERAL: + fprintf(fp, " |"); + break; + case FYSS_FOLDED: + fprintf(fp, " >"); + break; + default: + break; + } + break; + case FYET_ALIAS: + break; + } + + /* content */ + switch (fye->type) { + default: + break; + case FYET_SCALAR: + text = fy_token_get_text(fye->scalar.value, &text_len); + for (p = (const uint8_t *)text; text_len > 0; p += w, text_len -= (size_t)w) { + + /* get width from the first octet */ + w = (p[0] & 0x80) == 0x00 ? 1 : + (p[0] & 0xe0) == 0xc0 ? 2 : + (p[0] & 0xf0) == 0xe0 ? 3 : + (p[0] & 0xf8) == 0xf0 ? 4 : 0; + + /* error, clip it */ + if ((size_t)w > text_len) + break; + + /* initial value */ + c = p[0] & (0xff >> w); + for (i = 1; i < w; i++) { + if ((p[i] & 0xc0) != 0x80) + break; + c = (c << 6) | (p[i] & 0x3f); + } + + /* check for validity */ + if ((w == 4 && c < 0x10000) || + (w == 3 && c < 0x800) || + (w == 2 && c < 0x80) || + (c >= 0xd800 && c <= 0xdfff) || c >= 0x110000) + break; + + switch (c) { + case '\\': + fprintf(fp, "\\\\"); + break; + case '\0': + fprintf(fp, "\\0"); + break; + case '\b': + fprintf(fp, "\\b"); + break; + case '\f': + fprintf(fp, "\\f"); + break; + case '\n': + fprintf(fp, "\\n"); + break; + case '\r': + fprintf(fp, "\\r"); + break; + case '\t': + fprintf(fp, "\\t"); + break; + case '\a': + fprintf(fp, "\\a"); + break; + case '\v': + fprintf(fp, "\\v"); + break; + case '\x1b': + fprintf(fp, "\\e"); + break; + case 0x85: + fprintf(fp, "\\N"); + break; + case 0xa0: + fprintf(fp, "\\_"); + break; + case 0x2028: + fprintf(fp, "\\L"); + break; + case 0x2029: + fprintf(fp, "\\P"); + break; + default: + if ((c >= 0x01 && c <= 0x1f) || c == 0x7f || /* C0 */ + (c >= 0x80 && c <= 0x9f)) /* C1 */ + fprintf(fp, "\\x%02x", c); + else + fprintf(fp, "%.*s", w, p); + break; + } + } + break; + case FYET_ALIAS: + alias = fy_token_get_text(fye->alias.anchor, &alias_len); + fprintf(fp, " %s%.*s", "*", (int)alias_len, alias); + break; + } + + mbuf = fy_memstream_close(fyms, &msize); + return mbuf; +} + struct fy_eventp * fy_document_iterator_eventp_alloc(struct fy_document_iterator *fydi) { @@ -1099,3 +1326,18 @@ void fy_document_iterator_event_free(struct fy_document_iterator *fydi, struct f fy_document_iterator_eventp_recycle(fydi, fyep); } + +const char * +fy_event_get_comments(struct fy_event *fye) +{ + struct fy_token *fyt; + + if (!fye) + return NULL; + + fyt = fy_event_get_token(fye); + if (!fyt) + return NULL; + + return fy_token_get_comments(fyt); +} diff --git a/src/lib/fy-event.h b/src/lib/fy-event.h index 90f8241..e5ff7cc 100644 --- a/src/lib/fy-event.h +++ b/src/lib/fy-event.h @@ -43,6 +43,17 @@ struct fy_eventp *fy_parse_eventp_clone(struct fy_parser *fyp, struct fy_eventp struct fy_token *fy_event_get_and_clear_anchor_token(struct fy_event *fye); const char *fy_event_get_anchor(struct fy_event *fye, size_t *anchor_lenp); +struct fy_eventp * +fy_eventp_vcreate_internal(struct fy_eventp_list *recycled_list, struct fy_diag *diag, + struct fy_document_state *fyds, enum fy_event_type type, va_list ap); + +struct fy_token_list; + +void fy_eventp_clean_rl(struct fy_token_list *fytl, struct fy_eventp *fyep); + +/* convert to event dump string, string is malloc'ed */ +char *fy_event_to_string(struct fy_event *fye); + struct fy_document_iterator; struct fy_eventp *fy_document_iterator_eventp_alloc(struct fy_document_iterator *fydi); diff --git a/src/lib/fy-input-diag.c b/src/lib/fy-input-diag.c index 46baf03..d5270d6 100644 --- a/src/lib/fy-input-diag.c +++ b/src/lib/fy-input-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include diff --git a/src/lib/fy-input.c b/src/lib/fy-input.c index da7665d..39bd060 100644 --- a/src/lib/fy-input.c +++ b/src/lib/fy-input.c @@ -14,17 +14,21 @@ #include #include #include +#include +#include #include -#include -#include #include #include + +#ifndef _WIN32 +#include +#include #include -#include -#include +#endif #include +#include "fy-win32.h" #include "fy-parse.h" #include "fy-ctype.h" @@ -332,13 +336,15 @@ struct fy_diag *fy_reader_get_diag(struct fy_reader *fyr) int fy_reader_file_open(struct fy_reader *fyr, const char *filename) { + int flags = O_RDONLY; + if (!fyr || !filename) return -1; if (fyr->ops && fyr->ops->file_open) return fyr->ops->file_open(fyr, filename); - return open(filename, O_RDONLY); + return open(filename, flags); } void fy_reader_reset(struct fy_reader *fyr) @@ -490,8 +496,8 @@ int fy_reader_input_open(struct fy_reader *fyr, struct fy_input *fyi, const stru break; /* if we're not ignoring stdio, open a FILE* using the fd */ - if (!fyi->cfg.ignore_stdio) { - fyi->fp = fdopen(fyi->fd, "r"); + if (sb.st_size > 0 && !fyi->cfg.ignore_stdio) { + fyi->fp = fdopen(fyi->fd, "rb"); fyr_error_check(fyr, rc != -1, err_out, "failed to fdopen %s", fyi->name); } else fyi->fp = NULL; @@ -593,9 +599,15 @@ int fy_reader_input_done(struct fy_reader *fyr) case fyit_stream: case fyit_callback: /* chop extra buffer */ - buf = realloc(fyi->buffer, current_input_pos); - fyr_error_check(fyr, buf || !current_input_pos, err_out, - "realloc() failed"); + if (current_input_pos > 0) { + buf = realloc(fyi->buffer, current_input_pos); + fyr_error_check(fyr, buf || !current_input_pos, err_out, + "realloc() failed"); + } else { + /* for empty stream, just free everything */ + free(fyi->buffer); + buf = NULL; + } /* increate input generation; required for direct input to work */ if (fyi->buffer != buf) { @@ -666,7 +678,7 @@ int fy_reader_input_scan_token_mark_slow_path(struct fy_reader *fyr) assert(fyi->read >= current_input_pos); fyi_new->read = fyi->read - current_input_pos; if (fyi_new->read > 0) - memcpy(fyi_new->buffer, fyi->buffer + current_input_pos, fyi_new->read); + memcpy(fyi_new->buffer, (const char *)fyi->buffer + current_input_pos, fyi_new->read); fyr->this_input_start += current_input_pos; @@ -674,7 +686,7 @@ int fy_reader_input_scan_token_mark_slow_path(struct fy_reader *fyr) fyr->current_input = fyi_new; fyr->current_ptr = fyi_new->buffer; fyr->current_ptr_start = fyi_new->buffer; - fyr->current_ptr_end = fyr->current_ptr_start + fyi_new->read; + fyr->current_ptr_end = (const char *)fyr->current_ptr_start + fyi_new->read; fyr_debug(fyr, "chop at this_input_start=%zu chop=%zu\n", fyr->this_input_start, fyi->chop); @@ -712,14 +724,14 @@ const void *fy_reader_ptr_slow_path(struct fy_reader *fyr, size_t *leftp) left = size - current_input_pos; assert(left <= size); - p = start + current_input_pos; + p = (const char *)start + current_input_pos; if (leftp) *leftp = left; if (!fyr->current_ptr_start) { fyr->current_ptr_start = start; - fyr->current_ptr_end = start + size; + fyr->current_ptr_end = (const char *)start + size; } fyr->current_ptr = p; @@ -818,7 +830,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "file input exhausted"); break; } - p = fyi->addr + pos; + p = (char *)fyi->addr + pos; break; } @@ -830,7 +842,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi assert(fyi->read >= pos); left = fyi->read - pos; - p = fyi->buffer + pos; + p = (char *)fyi->buffer + pos; /* enough to satisfy directly */ if (left >= pull) @@ -877,7 +889,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyi->allocated = size; space = fyi->allocated - pos; - p = fyi->buffer + pos; + p = (char *)fyi->buffer + pos; } /* always try to read up to the allocated space */ @@ -889,7 +901,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "performing callback request of %zu", nreadreq); - nread = fyi->cfg.callback.input(fyi->cfg.userdata, fyi->buffer + fyi->read, nreadreq); + nread = fyi->cfg.callback.input(fyi->cfg.userdata, (char *)fyi->buffer + fyi->read, nreadreq); fyr_debug(fyr, "callback returned %zu", nread); @@ -908,7 +920,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "performing fread request of %zu", nreadreq); - nread = fread(fyi->buffer + fyi->read, 1, nreadreq, fyi->fp); + nread = fread((char *)fyi->buffer + fyi->read, 1, nreadreq, fyi->fp); fyr_debug(fyr, "fread returned %zu", nread); @@ -932,9 +944,8 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "performing read request of %zu", nreadreq); do { - snread = read(fyi->fd, fyi->buffer + fyi->read, nreadreq); + snread = read(fyi->fd, (char *)fyi->buffer + fyi->read, nreadreq); } while (snread == -1 && errno == EAGAIN); - fyr_debug(fyr, "read returned %zd", snread); if (snread == -1) { @@ -980,7 +991,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "memory input exhausted"); break; } - p = fyi->cfg.memory.data + pos; + p = (const char *)fyi->cfg.memory.data + pos; break; case fyit_alloc: @@ -991,7 +1002,7 @@ const void *fy_reader_input_try_pull(struct fy_reader *fyr, struct fy_input *fyi fyr_debug(fyr, "alloc input exhausted"); break; } - p = fyi->cfg.alloc.data + pos; + p = (const char *)fyi->cfg.alloc.data + pos; break; case fyit_dociter: @@ -1074,13 +1085,13 @@ struct fy_input *fy_input_create(const struct fy_input_cfg *fyic) break; case fyit_memory: ret = asprintf(&fyi->name, "", - fyic->memory.data, fyic->memory.data + fyic->memory.size - 1); + fyic->memory.data, (const char *)fyic->memory.data + fyic->memory.size - 1); if (ret == -1) fyi->name = NULL; break; case fyit_alloc: ret = asprintf(&fyi->name, "", - fyic->memory.data, fyic->memory.data + fyic->memory.size - 1); + fyic->memory.data, (const char *)fyic->memory.data + fyic->memory.size - 1); if (ret == -1) fyi->name = NULL; break; @@ -1170,7 +1181,7 @@ const void *fy_reader_ensure_lookahead_slow_path(struct fy_reader *fyr, size_t s fyr->current_ptr_start = new_start; } - fyr->current_ptr_end = fyr->current_ptr_start + new_size; + fyr->current_ptr_end = (const char *)fyr->current_ptr_start + new_size; } fyr->current_ptr = p; diff --git a/src/lib/fy-input.h b/src/lib/fy-input.h index a401af2..8bcf998 100644 --- a/src/lib/fy-input.h +++ b/src/lib/fy-input.h @@ -154,6 +154,21 @@ fy_input_start_size(const struct fy_input *fyi, size_t *sizep) break; } + /* if we've parsed need to clamp */ + if (fyi->state == FYIS_PARSED) { + switch (fyi->cfg.type) { + case fyit_file: + case fyit_fd: + case fyit_stream: + case fyit_callback: + if (fyi->allocated < size) + size = fyi->allocated; + break; + default: + break; + } + } + *sizep = size; return start; } @@ -274,13 +289,13 @@ int fy_reader_input_scan_token_mark_slow_path(struct fy_reader *fyr); static FY_ALWAYS_INLINE inline size_t fy_reader_current_input_pos(const struct fy_reader *fyr) { - return (size_t)(fyr->current_ptr - fyr->current_ptr_start); + return (size_t)((const char *)fyr->current_ptr - (const char *)fyr->current_ptr_start); } static FY_ALWAYS_INLINE inline size_t fy_reader_current_left(const struct fy_reader *fyr) { - return (size_t)(fyr->current_ptr_end - fyr->current_ptr); + return (size_t)((const char *)fyr->current_ptr_end - (const char *)fyr->current_ptr); } static inline bool @@ -564,7 +579,7 @@ fy_reader_peek_at_offset_width(struct fy_reader *fyr, size_t offset, int *wp) assert(fyr); if ((ssize_t)(fy_reader_current_left(fyr) - offset) >= 4) - return fy_utf8_get(fyr->current_ptr + offset, 4, wp); + return fy_utf8_get((const char *)fyr->current_ptr + offset, 4, wp); return fy_reader_peek_at_offset_width_slow_path(fyr, offset, wp); } @@ -577,7 +592,7 @@ fy_reader_peek_at_offset_width_64(struct fy_reader *fyr, size_t offset) assert(fyr); if ((ssize_t)(fy_reader_current_left(fyr) - offset) >= 4) - return fy_utf8_get_64(fyr->current_ptr + offset, 4); + return fy_utf8_get_64((const char *)fyr->current_ptr + offset, 4); return fy_reader_peek_at_offset_width_slow_path_64(fyr, offset); } @@ -695,7 +710,7 @@ fy_reader_peek_block(struct fy_reader *fyr, size_t *lenp) static FY_ALWAYS_INLINE inline void fy_reader_advance_octets(struct fy_reader *fyr, size_t advance) { - fyr->current_ptr += advance; + fyr->current_ptr = (const char *)fyr->current_ptr + advance; } void fy_reader_advance_slow_path(struct fy_reader *fyr, int c); @@ -725,7 +740,14 @@ fy_reader_advance_lb_mode(struct fy_reader *fyr, const int c, const enum fy_lb_m { assert(fy_utf8_is_valid(c)); fy_reader_advance_octets(fyr, fy_utf8_width(c)); - fy_reader_update_state_lb_mode(fyr, c, lb_mode); + /* Handle CRLF as single line break */ + if (c == '\r' && fy_reader_peek(fyr) == '\n') { + fy_reader_advance_octets(fyr, 1); + fyr->column = 0; + fyr->line++; + } else { + fy_reader_update_state_lb_mode(fyr, c, lb_mode); + } } static FY_ALWAYS_INLINE inline void @@ -980,4 +1002,9 @@ void fy_reader_diag_report(struct fy_reader *fyr, #define FYR_NODE_WARNING(_fyr, _fyn, _type, _module, _fmt, ...) \ FYR_NODE_DIAG(_fyr, _fyn, FYET_WARNING, _module, _fmt, ## __VA_ARGS__) +struct fy_input *fy_input_from_data(const char *data, size_t size, + struct fy_atom *handle, bool simple); +struct fy_input *fy_input_from_malloc_data(char *data, size_t size, + struct fy_atom *handle, bool simple); + #endif diff --git a/src/lib/fy-parse-diag.c b/src/lib/fy-parse-diag.c index a108312..1007a83 100644 --- a/src/lib/fy-parse-diag.c +++ b/src/lib/fy-parse-diag.c @@ -11,13 +11,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include diff --git a/src/lib/fy-parse.c b/src/lib/fy-parse.c index 830e11b..ae09bdc 100644 --- a/src/lib/fy-parse.c +++ b/src/lib/fy-parse.c @@ -11,20 +11,16 @@ #include #include -#include -#include -#include -#include -#include -#include #include #include #include #include #include +#include #include +#include "fy-win32.h" #include "fy-parse.h" #include "fy-utils.h" @@ -34,6 +30,12 @@ #define ATOM_SIZE_CHECK #endif +#ifdef ATOM_SIZE_CHECK +#define FORCE_ATOM_SIZE_CHECK_DEFAULT true +#else +#define FORCE_ATOM_SIZE_CHECK_DEFAULT false +#endif + const char *fy_library_version(void) { #ifndef VERSION @@ -354,7 +356,7 @@ const struct fy_version *fy_version_supported_iterate(void **prevp) versp++; - idx = versp - fy_map_option_to_version; + idx = (unsigned int)(versp - fy_map_option_to_version); if (idx >= sizeof(fy_map_option_to_version)/sizeof(fy_map_option_to_version[0])) return NULL; @@ -823,6 +825,7 @@ int fy_parse_setup(struct fy_parser *fyp, const struct fy_parse_cfg *cfg) fy_eventp_list_init(&fyp->mks.args); fy_atom_reset(&fyp->last_comment); + fy_atom_reset(&fyp->override_comment); fyp->suppress_recycling = !!(fyp->cfg.flags & FYPCF_DISABLE_RECYCLING) || (getenv("FY_VALGRIND") && @@ -863,6 +866,9 @@ void fy_parse_cleanup(struct fy_parser *fyp) fy_input_unref(fyp->last_comment.fyi); fy_atom_reset(&fyp->last_comment); + fy_input_unref(fyp->override_comment.fyi); + fy_atom_reset(&fyp->override_comment); + fy_input_unref(fyp->last_event_handle.fyi); fy_atom_reset(&fyp->last_event_handle); @@ -1042,15 +1048,15 @@ fy_comment_atoms_seperated_by_ws(struct fy_parser *fyp, struct fy_atom *a, struc bool ws_or_lb; if (!a || !b) - return NULL; + return false; /* must be on same input */ if (a->fyi != b->fyi) - return NULL; + return false; /* must be comments */ if (a->style != FYAS_COMMENT || b->style != FYAS_COMMENT) - return NULL; + return false; /* a must be before */ if (a->end_mark.input_pos > b->start_mark.input_pos) { @@ -1084,11 +1090,25 @@ fy_reset_last_comment(struct fy_parser *fyp) return true; } +static inline bool +fy_reset_override_comment(struct fy_parser *fyp) +{ + if (!(fyp->cfg.flags & FYPCF_PARSE_COMMENTS)) + return false; + + if (!fy_atom_is_set(&fyp->override_comment)) + return false; + + fy_atom_reset(&fyp->override_comment); + return true; +} + +/* -1 error, 0, no comment attached, 1 comment attached */ int fy_attach_comments_if_any(struct fy_parser *fyp, struct fy_token *fyt) { struct fy_atom *handle; struct fy_mark fym; - int c, rc; + int c, rc, count; if (!fyp || !fyt) return -1; @@ -1096,11 +1116,36 @@ int fy_attach_comments_if_any(struct fy_parser *fyp, struct fy_token *fyt) if (!(fyp->cfg.flags & FYPCF_PARSE_COMMENTS)) return 0; + count = 0; + + /* special handling for document start */ + if (fyt->type == FYTT_DOCUMENT_START && fy_atom_is_set(&fyp->override_comment)) { + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() top failed\n"); + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + *handle = fyp->override_comment; + count++; + + fy_reset_override_comment(fyp); + } + /* if a last comment exists and is valid */ - if (fy_atom_is_set(&fyp->last_comment) && - (handle = fy_token_comment_handle(fyt, fycp_top, true)) != NULL) { + if (fy_atom_is_set(&fyp->last_comment)) { + + handle = fy_token_comment_handle(fyt, count == 0 ? fycp_top : fycp_right, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() top failed\n"); + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); *handle = fyp->last_comment; + count++; fy_reset_last_comment(fyp); } @@ -1111,23 +1156,124 @@ int fy_attach_comments_if_any(struct fy_parser *fyp, struct fy_token *fyt) while (fy_is_ws(c = fy_parse_peek(fyp))) fy_advance(fyp, c); - if (c == '#') { - fy_get_mark(fyp, &fym); + if (c != '#') + return 0; - /* it's a right comment only if it's on the same line */ - if (fym.line == fyt->handle.end_mark.line) - handle = fy_token_comment_handle(fyt, fycp_right, true); - else - handle = &fyp->last_comment; /* otherwise, last comment */ + fy_get_mark(fyp, &fym); + + /* only the one that in the same line */ + if (fym.line != fyt->handle.end_mark.line) + return 0; + + /* it's a right comment only if it's on the same line */ + handle = fy_token_comment_handle(fyt, fycp_right, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() right failed\n"); + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + rc = fy_scan_comment(fyp, handle, false); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_scan_comment() failed"); + + if (fy_atom_is_set(handle)) + count++; + return count; + +err_out_rc: + return rc; +err_out: + rc = -1; + goto err_out; +} + +int fy_fetch_flow_collection_entry(struct fy_parser *fyp, int c); + +int fy_parse_handle_comma(struct fy_parser *fyp, int c) +{ + struct fy_mark m; + int rc; + + fyp->indent_line = fyp_line(fyp); + + fyp_error_check(fyp, c == ',', err_out, + "illegal comma"); + + fy_get_mark(fyp, &m); + + fyp_scan_debug(fyp, "fy_fetch_flow_collection_entry(%c)", c); + rc = fy_fetch_flow_collection_entry(fyp, c); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_fetch_flow_collection_entry() failed"); + + fyp->last_was_comma = true; + fyp->last_comma_mark = m; - rc = fy_scan_comment(fyp, handle, false); - fyp_error_check(fyp, !rc, err_out_rc, - "fy_scan_comment() failed"); - } return 0; err_out_rc: return rc; +err_out: + rc = -1; + goto err_out_rc; +} + +int fy_parse_handle_right_comment_after_comma(struct fy_parser *fyp, struct fy_token *fyt) +{ + int c, rc, start_line; + + /* not collecting comment or now in flow mode */ + if (!(fyp->cfg.flags & FYPCF_PARSE_COMMENTS) || !fyp->flow_level) + return 0; + + /* we can only attach right to these tokens */ + if (fyt->type != FYTT_SCALAR && + fyt->type != FYTT_FLOW_SEQUENCE_END && + fyt->type != FYTT_FLOW_MAPPING_END) + return 0; + + /* skip white space while we're on the same line */ + start_line = fyp_line(fyp); + while (fy_is_ws(c = fy_parse_peek(fyp))) + fy_advance(fyp, c); + + /* is it a comma at the same line? */ + if (c != ',' || start_line != fyp_line(fyp)) + return 0; + + /* immediately parse the comma because grab the scalar to attach the comment */ + rc = fy_parse_handle_comma(fyp, ','); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_parse_handle_comma() failed"); + + return 0; +err_out_rc: + return rc; +} + +int fy_parse_handle_comments_after_token(struct fy_parser *fyp, struct fy_token *fyt) +{ + int rc; + + if (!(fyp->cfg.flags & FYPCF_PARSE_COMMENTS)) + return 0; + + rc = fy_attach_comments_if_any(fyp, fyt); + fyp_error_check(fyp, rc >= 0, err_out_rc, + "fy_attach_right_hand_comment() failed"); + + rc = fy_parse_handle_right_comment_after_comma(fyp, fyt); + fyp_error_check(fyp, rc >= 0, err_out_rc, + "fy_parse_handle_right_comment_after_comma() failed"); + + return 0; + +err_out_rc: + return rc; +err_out: + rc = -1; + goto err_out; } int fy_scan_to_next_token(struct fy_parser *fyp) @@ -1231,8 +1377,6 @@ int fy_scan_to_next_token(struct fy_parser *fyp) rc = fy_scan_comment(fyp, NULL, false); fyp_error_check(fyp, !rc, err_out_rc, "fy_scan_comment() failed"); - - } else { memset(&this_comment, 0, sizeof(this_comment)); @@ -1243,17 +1387,22 @@ int fy_scan_to_next_token(struct fy_parser *fyp) if (fy_atom_is_set(&fyp->last_comment)) { - /* merge consecutive comments */ - if (fy_comment_atoms_seperated_by_ws(fyp, &fyp->last_comment, &this_comment)) { - fyp->last_comment.end_mark = this_comment.end_mark; - fy_input_unref(this_comment.fyi); - fy_atom_reset(&this_comment); + /* merge consecutive override comments */ + if (fy_atom_is_set(&fyp->override_comment) && + fy_comment_atoms_seperated_by_ws(fyp, &fyp->override_comment, &fyp->last_comment)) { + fyp->override_comment.end_mark = fyp->last_comment.end_mark; } else { - fy_input_unref(fyp->last_comment.fyi); - fy_atom_reset(&fyp->last_comment); - fyp->last_comment = this_comment; + /* override comment keeps the last comment */ + fy_input_unref(fyp->override_comment.fyi); + fy_atom_reset(&fyp->override_comment); + fyp->override_comment.fyi = fy_input_ref(fyp->last_comment.fyi); + fyp->override_comment = fyp->last_comment; } + fy_input_unref(fyp->last_comment.fyi); + fy_atom_reset(&fyp->last_comment); + fyp->last_comment = this_comment; + } else fyp->last_comment = this_comment; } @@ -2295,6 +2444,10 @@ int fy_fetch_document_indicator(struct fy_parser *fyp, enum fy_token_type type) while (fy_is_ws(c = fy_parse_peek(fyp))) fy_advance(fyp, c); + rc = fy_attach_comments_if_any(fyp, fyt); + fyp_error_check(fyp, rc >= 0, err_out_rc, + "fy_attach_right_hand_comment() failed"); + return 0; err_out: @@ -2352,6 +2505,28 @@ int fy_fetch_flow_collection_mark_start(struct fy_parser *fyp, int c) "fy_save_simple_key_mark() failed"); } + /* if a last comment exists and is valid */ + if ((fyp->cfg.flags & FYPCF_PARSE_COMMENTS) && + (fy_atom_is_set(&fyp->override_comment) || fy_atom_is_set(&fyp->last_comment))) { + + struct fy_atom *handle; + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + if (fy_atom_is_set(&fyp->override_comment)) { + *handle = fyp->override_comment; + fy_atom_reset(&fyp->override_comment); + } else if (fy_atom_is_set(&fyp->last_comment)) { + *handle = fyp->last_comment; + fy_atom_reset(&fyp->last_comment); + } + } + /* increase flow level */ fyp->flow_level++; fyp_error_check(fyp, fyp->flow_level, err_out, @@ -2482,6 +2657,11 @@ int fy_fetch_flow_collection_mark_end(struct fy_parser *fyp, int c) } } + rc = fy_parse_handle_comments_after_token(fyp, fyt); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_parse_handle_comments_after_token() failed"); + + return 0; err_out: @@ -2522,7 +2702,10 @@ int fy_fetch_flow_collection_entry(struct fy_parser *fyp, int c) fyp->tab_used_for_ws = false; fyp_scan_debug(fyp, "simple_key_allowed -> %s\n", fyp->simple_key_allowed ? "true" : "false"); - fyt_last = fy_token_list_tail(&fyp->queued_tokens); + if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) + fyt_last = fy_token_list_tail(&fyp->queued_tokens); + else + fyt_last = NULL; fyt = fy_token_queue_simple(fyp, &fyp->queued_tokens, type, 1); fyp_error_check(fyp, fyt, err_out, @@ -2536,20 +2719,21 @@ int fy_fetch_flow_collection_entry(struct fy_parser *fyp, int c) "invalid comment after comma"); /* skip white space */ - while (fy_is_ws(c = fy_parse_peek(fyp))) - fy_advance(fyp, c); - - if (c == '#') { - if (fyt_last) - fyt = fyt_last; + if (fyt_last) { + while (fy_is_ws(c = fy_parse_peek(fyp))) + fy_advance(fyp, c); - handle = NULL; - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) - handle = fy_token_comment_handle(fyt, fycp_right, true); + if (c == '#') { + handle = fy_token_comment_handle(fyt_last, fycp_right, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + fy_input_unref(handle->fyi); + fy_atom_reset(handle); - rc = fy_scan_comment(fyp, handle, true); - fyp_error_check(fyp, !rc, err_out_rc, - "fy_scan_comment() failed"); + rc = fy_scan_comment(fyp, handle, true); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_scan_comment() failed"); + } } return 0; @@ -2565,6 +2749,7 @@ int fy_fetch_block_entry(struct fy_parser *fyp, int c) struct fy_mark mark; struct fy_simple_key *fysk; struct fy_token *fyt; + struct fy_atom *handle; fyp_error_check(fyp, c == '-', err_out, "illegal block entry"); @@ -2601,8 +2786,30 @@ int fy_fetch_block_entry(struct fy_parser *fyp, int c) fyt = fy_token_queue_simple_internal(fyp, &fyp->queued_tokens, FYTT_BLOCK_SEQUENCE_START, 0); fyp_error_check(fyp, fyt, err_out, "fy_token_queue_simple_internal() failed"); + + /* if a last comment exists and is valid */ + if ((fyp->cfg.flags & FYPCF_PARSE_COMMENTS) && + (fy_atom_is_set(&fyp->override_comment) || fy_atom_is_set(&fyp->last_comment))) { + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + if (fy_atom_is_set(&fyp->override_comment)) { + *handle = fyp->override_comment; + fy_atom_reset(&fyp->override_comment); + } else if (fy_atom_is_set(&fyp->last_comment)) { + *handle = fyp->last_comment; + fy_atom_reset(&fyp->last_comment); + } + } } + /* always reset the override comment */ + fy_reset_override_comment(fyp); + if (c == '-' && fyp->flow_level) { /* this is an error, but we let the parser catch it */ ; @@ -2687,6 +2894,26 @@ int fy_fetch_key(struct fy_parser *fyp, int c) fyt = fy_token_queue_simple_internal(fyp, &fyp->queued_tokens, FYTT_BLOCK_MAPPING_START, 0); fyp_error_check(fyp, fyt, err_out, "fy_token_queue_simple_internal() failed"); + + /* if a last comment exists and is valid */ + if ((fyp->cfg.flags & FYPCF_PARSE_COMMENTS) && + (fy_atom_is_set(&fyp->override_comment) || fy_atom_is_set(&fyp->last_comment))) { + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + if (fy_atom_is_set(&fyp->override_comment)) { + *handle = fyp->override_comment; + fy_atom_reset(&fyp->override_comment); + } else if (fy_atom_is_set(&fyp->last_comment)) { + *handle = fyp->last_comment; + fy_atom_reset(&fyp->last_comment); + } + } } rc = fy_remove_simple_key(fyp, FYTT_KEY); @@ -2720,12 +2947,15 @@ int fy_fetch_key(struct fy_parser *fyp, int c) } else fyp->tab_used_for_ws = false; // XXX - /* comment? */ - if (c == '#') { + /* comment which we care about? */ + if ((fyp->cfg.flags & FYPCF_PARSE_COMMENTS) && c == '#') { + + handle = fy_token_comment_handle(fyt, fycp_right, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); - handle = NULL; - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) - handle = fy_token_comment_handle(fyt, fycp_right, true); + fy_input_unref(handle->fyi); + fy_atom_reset(handle); rc = fy_scan_comment(fyp, handle, false); fyp_error_check(fyp, !rc, err_out_rc, @@ -2884,6 +3114,40 @@ int fy_fetch_value(struct fy_parser *fyp, int c) fyp_error_check(fyp, fyt, err_out, "fy_token_queue_simple_internal() failed"); + /* if a last comment exists and is valid */ + if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) { + + struct fy_atom *key_handle, *handle; + + if (fysk && fysk->token) { + key_handle = fy_token_comment_handle(fysk->token, fycp_top, true); + fyp_error_check(fyp, key_handle, err_out, + "fy_token_comment_handle() failed"); + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + + /* move the comment to the block mapping start */ + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + *handle = *key_handle; + fy_atom_reset(key_handle); + } + + handle = fy_token_comment_handle(fyt, fycp_top, true); + fyp_error_check(fyp, handle, err_out, + "fy_token_comment_handle() failed"); + + if (fy_atom_is_set(&fyp->override_comment)) { + *handle = fyp->override_comment; + fy_atom_reset(&fyp->override_comment); + } else if (fy_atom_is_set(&fyp->last_comment)) { + *handle = fyp->last_comment; + fy_atom_reset(&fyp->last_comment); + } + } + /* update with this mark */ fyt->handle.start_mark = fyt->handle.end_mark = mark_insert; } @@ -2952,22 +3216,24 @@ int fy_fetch_value(struct fy_parser *fyp, int c) fyp->pending_complex_key_column); } - if (fyt_insert) { + if ((fyp->cfg.flags & FYPCF_PARSE_COMMENTS) && fyt_insert) { /* eat whitespace */ while (fy_is_blank(c = fy_parse_peek(fyp))) fy_advance(fyp, c); /* comment? */ if (c == '#') { - chandle = NULL; - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) - chandle = fy_token_comment_handle(fyt_insert, fycp_right, true); + chandle = fy_token_comment_handle(fyt_insert, fycp_right, true); + fyp_error_check(fyp, chandle, err_out, + "fy_token_comment_handle() failed"); + + fy_input_unref(chandle->fyi); + fy_atom_reset(chandle); rc = fy_scan_comment(fyp, chandle, false); fyp_error_check(fyp, !rc, err_out_rc, "fy_scan_comment() failed"); } - } return 0; @@ -3268,7 +3534,7 @@ int fy_scan_block_scalar_indent(struct fy_parser *fyp, int indent, int *breaks, fy_advance(fyp, c); - break_length = fy_utf8_width(c); + break_length = (int)fy_utf8_width(c); (*breaks)++; (*breaks_length) += 1; @@ -3301,7 +3567,7 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) int lastc, rc, increment = 0, current_indent, new_indent, indent = 0; int breaks, breaks_length, presentation_breaks_length, first_break_length; bool doc_start_end_detected, empty, empty_line, prev_empty_line, indented, prev_indented, first; - bool has_ws, has_lb, starts_with_ws, starts_with_lb, ends_with_ws, ends_with_lb, trailing_lb; + bool has_ws, has_lb, has_cr, starts_with_ws, starts_with_lb, ends_with_ws, ends_with_lb, trailing_lb; bool pending_nl, ends_with_eof, starts_with_eof, content_is_eof; struct fy_token *fyt; size_t length, line_length, trailing_ws, trailing_breaks_length; @@ -3311,9 +3577,7 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) int actual_lb_length, pending_lb_length; struct fy_mark indicator_mark; bool generated_indent, final_lb; -#ifdef ATOM_SIZE_CHECK size_t tlength; -#endif fyp_error_check(fyp, c == '|' || c == '>', err_out, "bad start of block scalar ('%s')", @@ -3423,6 +3687,7 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) empty = true; has_ws = false; has_lb = false; + has_cr = false; starts_with_ws = false; starts_with_lb = false; ends_with_ws = false; @@ -3508,7 +3773,7 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) if (!fy_is_z(c)) { /* eat line break */ - actual_lb_length = fy_utf8_width(c); + actual_lb_length = (int)fy_utf8_width(c); fy_advance(fyp, c); has_lb = true; @@ -3517,6 +3782,9 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) "fy_scan_block_scalar_indent() failed"); if (fy_is_lb_LS_PS(c)) presentation_breaks_length += actual_lb_length; + + /* very simple heuristic, if we hit a '\r' then force size check */ + has_cr |= c == '\r'; } else { has_lb = false; new_indent = indent; @@ -3693,15 +3961,16 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) handle.ends_with_eof = ends_with_eof; handle.is_merge_key = false; -#ifdef ATOM_SIZE_CHECK - tlength = fy_atom_format_text_length(&handle); - if (tlength != length) { - fyp_warning(fyp, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, - tlength, length, - fy_utf8_format_text_a(fy_atom_data(&handle), fy_atom_size(&handle), fyue_doublequote)); - length = tlength; + /* force calculation of length if we had a CR */ + if (has_cr || FORCE_ATOM_SIZE_CHECK_DEFAULT) { + tlength = fy_atom_format_text_length(&handle); + if (!has_cr && tlength != length) { + fyp_warning(fyp, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, + tlength, length, + fy_utf8_format_text_a(fy_atom_data(&handle), fy_atom_size(&handle), fyue_doublequote)); + length = tlength; + } } -#endif handle.storage_hint = length; handle.storage_hint_valid = true; @@ -3710,11 +3979,9 @@ int fy_fetch_block_scalar(struct fy_parser *fyp, bool is_literal, int c) fyp_error_check(fyp, fyt, err_out_rc, "fy_token_queue() failed"); - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) { - rc = fy_attach_comments_if_any(fyp, fyt); - fyp_error_check(fyp, !rc, err_out_rc, - "fy_attach_right_hand_comment() failed"); - } + rc = fy_attach_comments_if_any(fyp, fyt); + fyp_error_check(fyp, rc >= 0, err_out_rc, + "fy_attach_right_hand_comment() failed"); if (generated_indent) { rc = fy_pop_indent(fyp); @@ -3740,7 +4007,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, int breaks_found, blanks_found, break_run, total_code_length; int breaks_found_length, first_break_length, value; uint32_t hi_surrogate, lo_surrogate; - bool is_single, is_multiline, esc_lb, ws_lb_only, has_ws, has_lb, has_esc; + bool is_single, is_multiline, esc_lb, ws_lb_only, has_ws, has_lb, has_cr, has_esc; bool first, starts_with_ws, starts_with_lb, ends_with_ws, ends_with_lb, trailing_lb = false; bool unicode_esc, is_json_unesc, has_json_esc; int last_esc_lb, break_length, presentation_breaks_length; @@ -3749,9 +4016,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, size_t escbuf_len; enum fy_utf8_escape esc_mode; const char *ep; -#ifdef ATOM_SIZE_CHECK size_t tlength; -#endif (void)last_esc_lb; @@ -3780,6 +4045,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, ws_lb_only = true; has_ws = false; has_lb = false; + has_cr = false; starts_with_ws = false; starts_with_lb = false; ends_with_ws = false; @@ -3869,7 +4135,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, size_t len, consumed; const char *p, *s, *e; int8_t cc; - int run; + size_t run; run = 0; while ((p = fy_reader_ensure_lookahead(fyr, 1, &len)) != NULL) { @@ -3883,7 +4149,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, consumed = s - p; if (consumed) { fy_reader_advance_octets(fyr, consumed); - fyr->column += consumed; + fyr->column += (int)consumed; lastc = (int)cc; } run += consumed; @@ -4078,7 +4344,7 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, if (!fy_is_lb_LS_PS(c)) { break_length = 1; } else { - break_length = fy_utf8_width(c); + break_length = (int)fy_utf8_width(c); presentation_breaks_length += break_length; } @@ -4089,6 +4355,8 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, breaks_found_length += break_length; blanks_found = 0; esc_lb = false; + + has_cr |= c == '\r'; } else { has_ws = true; if (!esc_lb) @@ -4135,15 +4403,15 @@ int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, /* skip over block scalar end */ fy_reader_advance_by(fyr, 1); -#ifdef ATOM_SIZE_CHECK - tlength = fy_atom_format_text_length(handle); - if (tlength != length) { - fyr_warning(fyr, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, - tlength, length, - fy_utf8_format_text_a(fy_atom_data(handle), fy_atom_size(handle), fyue_doublequote)); - length = tlength; + if (has_cr || FORCE_ATOM_SIZE_CHECK_DEFAULT) { + tlength = fy_atom_format_text_length(handle); + if (!has_cr && tlength != length) { + fyr_warning(fyr, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, + tlength, length, + fy_utf8_format_text_a(fy_atom_data(handle), fy_atom_size(handle), fyue_doublequote)); + length = tlength; + } } -#endif handle->storage_hint = length; handle->storage_hint_valid = true; @@ -4169,6 +4437,7 @@ struct fy_fetch_plain_state { bool has_high_ascii; bool simple_key_allowed; bool last_was_lb; + bool has_cr; }; static FY_ALWAYS_INLINE inline int @@ -4187,10 +4456,12 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, struct { bool run_has_lb : 1; bool run_has_ws : 1; + bool run_has_cr : 1; bool has_json_esc : 1; bool has_high_ascii : 1; bool has_lb : 1; bool has_ws : 1; + bool has_cr : 1; bool had_run : 1; bool flow_level_gt_0 : 1; bool flow_level_le_0_indent_ge_0 : 1; @@ -4235,11 +4506,11 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, while (run < len && fy_utf8_is_simple_scalar_no_check((int8_t)s[run])) run++; - fyr->column += run; + fyr->column += (int)run; nextc = -1; } else { - width = fy_utf8_width(c); + width = (int)fy_utf8_width(c); if (c == ':') { @@ -4281,6 +4552,7 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, u.has_lb |= u.run_has_lb; u.has_ws |= u.run_has_ws; + u.has_cr |= u.run_has_cr; fy_reader_get_mark(fyr, &state->last_mark); } @@ -4293,9 +4565,9 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, breaks_found_length = 0; first_break_length = 0; blanks_found_length = 0; - u.run_has_ws = u.run_has_lb = false; + u.run_has_ws = u.run_has_lb = u.run_has_cr = false; - width = fy_utf8_width(c); + width = (int)fy_utf8_width(c); do { fy_reader_advance_lb_mode(fyr, c, lb_mode); @@ -4317,6 +4589,7 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, first_break_length = width; breaks_found_length += width; u.run_has_lb = true; + u.run_has_cr |= c == '\r'; } c = fy_reader_peek_width(fyr, &width); @@ -4340,6 +4613,7 @@ fy_reader_fetch_plain_scalar_handle_inline(struct fy_reader *fyr, int c, state->has_json_esc = u.has_json_esc; state->has_high_ascii = u.has_high_ascii; state->last_was_lb = u.last_was_lb; + state->has_cr = u.has_cr; return 0; @@ -4417,6 +4691,7 @@ int fy_reader_fetch_plain_scalar_handle(struct fy_reader *fyr, int c, const enum fy_lb_mode lb_mode = fy_reader_lb_mode(fyr); bool is_merge_key, ends_with_eof, is_multiline; int nextc, rc; + size_t tlength; nextc = fy_reader_peek_at(fyr, 1); @@ -4496,16 +4771,15 @@ int fy_reader_fetch_plain_scalar_handle(struct fy_reader *fyr, int c, handle->simple_key_allowed = state->simple_key_allowed; handle->high_ascii = state->has_high_ascii; -#ifdef ATOM_SIZE_CHECK - size_t tlength; - tlength = fy_atom_format_text_length(handle); - if (tlength != state->length) { - fyr_warning(fyr, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, - tlength, state->length, - fy_utf8_format_text_a(fy_atom_data(handle), fy_atom_size(handle), fyue_doublequote)); - state->length = tlength; + if (state->has_cr || FORCE_ATOM_SIZE_CHECK_DEFAULT) { + tlength = fy_atom_format_text_length(handle); + if (!state->has_cr && tlength != state->length) { + fyr_warning(fyr, "%s: storage hint calculation failed real %zu != hint %zu - \"%s\"", __func__, + tlength, state->length, + fy_utf8_format_text_a(fy_atom_data(handle), fy_atom_size(handle), fyue_doublequote)); + state->length = tlength; + } } -#endif handle->storage_hint = state->length; handle->storage_hint_valid = true; @@ -4615,11 +4889,9 @@ int fy_fetch_flow_scalar(struct fy_parser *fyp, int c) "invalid comment without whitespace after %s scalar", is_single ? "single-quoted" : "double-quoted"); - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) { - rc = fy_attach_comments_if_any(fyp, fyt); - fyp_error_check(fyp, !rc, err_out_rc, - "fy_attach_right_hand_comment() failed"); - } + rc = fy_parse_handle_comments_after_token(fyp, fyt); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_parse_handle_comments_after_token() failed"); return 0; @@ -4759,7 +5031,7 @@ void fy_reader_skip_space(struct fy_reader *fyr) consumed = s - p; if (consumed) { fy_reader_advance_octets(fyr, consumed); - fyr->column += consumed; + fyr->column += (int)consumed; } if (s < e) @@ -4968,11 +5240,9 @@ int fy_fetch_plain_scalar(struct fy_parser *fyp, int c) fyp->simple_key_allowed = handle.simple_key_allowed; fyp_scan_debug(fyp, "simple_key_allowed -> %s\n", fyp->simple_key_allowed ? "true" : "false"); - if (fyp->cfg.flags & FYPCF_PARSE_COMMENTS) { - rc = fy_attach_comments_if_any(fyp, fyt); - fyp_error_check(fyp, !rc, err_out_rc, - "fy_attach_right_hand_comment() failed"); - } + rc = fy_parse_handle_comments_after_token(fyp, fyt); + fyp_error_check(fyp, !rc, err_out_rc, + "fy_parse_handle_comments_after_token() failed"); return 0; @@ -4984,7 +5254,6 @@ err_out_rc: int fy_fetch_tokens(struct fy_parser *fyp) { - struct fy_mark m; bool was_double_colon; int c, rc; @@ -5121,17 +5390,9 @@ int fy_fetch_tokens(struct fy_parser *fyp) if (c == ',') { - fyp->indent_line = fyp_line(fyp); - - fy_get_mark(fyp, &m); - - fyp_scan_debug(fyp, "fy_fetch_flow_collection_entry(%c)", c); - rc = fy_fetch_flow_collection_entry(fyp, c); + rc = fy_parse_handle_comma(fyp, ','); fyp_error_check(fyp, !rc, err_out_rc, - "fy_fetch_flow_collection_entry() failed"); - - fyp->last_was_comma = true; - fyp->last_comma_mark = m; + "fy_parse_handle_comma() failed"); goto out; } @@ -5467,6 +5728,7 @@ fy_parse_node(struct fy_parser *fyp, struct fy_token *fyt, bool is_block) struct fy_token *fyt_td; struct fy_token *fytn; struct fy_atom *ev_handle = NULL; + int rc; fyds = fyp->current_document_state; assert(fyds); @@ -5544,6 +5806,10 @@ fy_parse_node(struct fy_parser *fyp, struct fy_token *fyt, bool is_block) fye->sequence_start.sequence_start = fytn; + rc = fy_attach_comments_if_any(fyp, fye->sequence_start.sequence_start); + fyp_error_check(fyp, rc >= 0, err_out, + "fy_attach_comments_if_any() failed"); + fy_parse_state_set(fyp, FYPS_INDENTLESS_SEQUENCE_ENTRY); ev_handle = &fye->sequence_start.sequence_start->handle; @@ -5778,6 +6044,7 @@ static struct fy_eventp *fy_parse_internal(struct fy_parser *fyp) char tbuf[16] __FY_DEBUG_UNUSED__; int rc; + (void)tbuf; version_directive = NULL; fy_token_list_init(&tag_directives); @@ -6051,6 +6318,11 @@ static struct fy_eventp *fy_parse_internal(struct fy_parser *fyp) fye->document_start.document_state = fy_document_state_ref(fyds); fye->document_start.implicit = fyds->start_implicit; + /* update the handle of the document start token */ + fytn = fyds->fyt_ds; + fyds->fyt_ds = fy_token_ref(fye->document_start.document_start); + fy_token_unref(fytn); + ev_handle = &fye->document_start.document_start->handle; break; @@ -6752,229 +7024,19 @@ const char *fy_event_type_get_text(enum fy_event_type type) static void fy_parse_dump_eventp(struct fy_parser *fyp, struct fy_eventp *fyep, const char *pfx) { - struct fy_event *fye = NULL; - FILE *fp; char *mbuf = NULL; - const char *anchor = NULL; - const char *tag = NULL; - const char *text = NULL; - const char *alias = NULL; - size_t msize, anchor_len = 0, tag_len = 0, text_len = 0, alias_len = 0; - enum fy_scalar_style style; - const uint8_t *p; - int i, c, w; + + if (!fyep) + return; /* perform the enable tests early to avoid the overhead */ if ((FYET_DEBUG >> FYDF_LEVEL_SHIFT) < fyp->diag->cfg.level) return; - fp = open_memstream(&mbuf, &msize); - if (!fp) + mbuf = fy_event_to_string(&fyep->e); + if (!mbuf) return; - if (!fyep) { - fprintf(fp, ""); - goto out; - } - fye = &fyep->e; - - /* event type */ - switch (fye->type) { - case FYET_NONE: - fprintf(fp, "???"); - break; - case FYET_STREAM_START: - fprintf(fp, "+STR"); - break; - case FYET_STREAM_END: - fprintf(fp, "-STR"); - break; - case FYET_DOCUMENT_START: - fprintf(fp, "+DOC"); - break; - case FYET_DOCUMENT_END: - fprintf(fp, "-DOC"); - break; - case FYET_MAPPING_START: - fprintf(fp, "+MAP"); - if (fye->mapping_start.anchor) - anchor = fy_token_get_text(fye->mapping_start.anchor, &anchor_len); - if (fye->mapping_start.tag) - tag = fy_token_get_text(fye->mapping_start.tag, &tag_len); - if (fy_event_get_node_style(fye) == FYNS_FLOW) - fprintf(fp, " {}"); - break; - case FYET_MAPPING_END: - fprintf(fp, "-MAP"); - break; - case FYET_SEQUENCE_START: - fprintf(fp, "+SEQ"); - if (fye->sequence_start.anchor) - anchor = fy_token_get_text(fye->sequence_start.anchor, &anchor_len); - if (fye->sequence_start.tag) - tag = fy_token_get_text(fye->sequence_start.tag, &tag_len); - if (fy_event_get_node_style(fye) == FYNS_FLOW) - fprintf(fp, " []"); - break; - case FYET_SEQUENCE_END: - fprintf(fp, "-SEQ"); - break; - case FYET_SCALAR: - fprintf(fp, "=VAL"); - if (fye->scalar.anchor) - anchor = fy_token_get_text(fye->scalar.anchor, &anchor_len); - if (fye->scalar.tag) - tag = fy_token_get_text(fye->scalar.tag, &tag_len); - break; - case FYET_ALIAS: - fprintf(fp, "=ALI"); - break; - default: - break; - } - - /* (position) anchor and tag */ - if (anchor) - fprintf(fp, " &%.*s", (int)anchor_len, anchor); - if (tag) - fprintf(fp, " <%.*s>", (int)tag_len, tag); - - /* style hint */ - switch (fye->type) { - default: - break; - case FYET_DOCUMENT_START: - if (!fy_document_event_is_implicit(fye)) - fprintf(fp, " ---"); - break; - case FYET_DOCUMENT_END: - if (!fy_document_event_is_implicit(fye)) - fprintf(fp, " ..."); - break; - case FYET_MAPPING_START: - break; - case FYET_SEQUENCE_START: - break; - case FYET_SCALAR: - style = fy_token_scalar_style(fye->scalar.value); - switch (style) { - case FYSS_PLAIN: - fprintf(fp, " :"); - break; - case FYSS_SINGLE_QUOTED: - fprintf(fp, " '"); - break; - case FYSS_DOUBLE_QUOTED: - fprintf(fp, "\""); - break; - case FYSS_LITERAL: - fprintf(fp, " |"); - break; - case FYSS_FOLDED: - fprintf(fp, " >"); - break; - default: - break; - } - break; - case FYET_ALIAS: - break; - } - - /* content */ - switch (fye->type) { - default: - break; - case FYET_SCALAR: - text = fy_token_get_text(fye->scalar.value, &text_len); - for (p = (const uint8_t *)text; text_len > 0; p += w, text_len -= (size_t)w) { - - /* get width from the first octet */ - w = (p[0] & 0x80) == 0x00 ? 1 : - (p[0] & 0xe0) == 0xc0 ? 2 : - (p[0] & 0xf0) == 0xe0 ? 3 : - (p[0] & 0xf8) == 0xf0 ? 4 : 0; - - /* error, clip it */ - if ((size_t)w > text_len) - break; - - /* initial value */ - c = p[0] & (0xff >> w); - for (i = 1; i < w; i++) { - if ((p[i] & 0xc0) != 0x80) - break; - c = (c << 6) | (p[i] & 0x3f); - } - - /* check for validity */ - if ((w == 4 && c < 0x10000) || - (w == 3 && c < 0x800) || - (w == 2 && c < 0x80) || - (c >= 0xd800 && c <= 0xdfff) || c >= 0x110000) - break; - - switch (c) { - case '\\': - fprintf(fp, "\\\\"); - break; - case '\0': - fprintf(fp, "\\0"); - break; - case '\b': - fprintf(fp, "\\b"); - break; - case '\f': - fprintf(fp, "\\f"); - break; - case '\n': - fprintf(fp, "\\n"); - break; - case '\r': - fprintf(fp, "\\r"); - break; - case '\t': - fprintf(fp, "\\t"); - break; - case '\a': - fprintf(fp, "\\a"); - break; - case '\v': - fprintf(fp, "\\v"); - break; - case '\e': - fprintf(fp, "\\e"); - break; - case 0x85: - fprintf(fp, "\\N"); - break; - case 0xa0: - fprintf(fp, "\\_"); - break; - case 0x2028: - fprintf(fp, "\\L"); - break; - case 0x2029: - fprintf(fp, "\\P"); - break; - default: - if ((c >= 0x01 && c <= 0x1f) || c == 0x7f || /* C0 */ - (c >= 0x80 && c <= 0x9f)) /* C1 */ - fprintf(fp, "\\x%02x", c); - else - fprintf(fp, "%.*s", w, p); - break; - } - } - break; - case FYET_ALIAS: - alias = fy_token_get_text(fye->alias.anchor, &alias_len); - fprintf(fp, " %s%.*s", "*", (int)alias_len, alias); - break; - } - -out: - fclose(fp); fyp_parse_debug(fyp, "%s%s", pfx, mbuf); free(mbuf); } @@ -7200,7 +7262,7 @@ int fy_parser_set_input_fp(struct fy_parser *fyp, const char *name, FILE *fp) memset(&fyic, 0, sizeof(fyic)); fyic.type = fyit_stream; - fyic.stream.name = name ? : ""; + fyic.stream.name = name ? name : ""; fyic.stream.fp = fp; fyic.ignore_stdio = !!(fyp->cfg.flags & FYPCF_DISABLE_BUFFERING); @@ -8634,6 +8696,7 @@ struct fy_parser_checkpoint *fy_parser_checkpoint_create(struct fy_parser *fyp) fypc->stream_end_token = fy_token_ref(fyp->stream_end_token); fypc->last_comment.fyi = fy_input_ref(fyp->last_comment.fyi); + fypc->override_comment.fyi = fy_input_ref(fyp->override_comment.fyi); fypc->current_document_state = fy_document_state_ref(fyp->current_document_state); fypc->default_document_state = fy_document_state_ref(fyp->default_document_state); fypc->next_single_document = fyp->next_single_document; @@ -8884,6 +8947,10 @@ int fy_parser_rollback(struct fy_parser *fyp, struct fy_parser_checkpoint *fypch fyp->last_comment = fypc->last_comment; fyp->last_comment.fyi = fy_input_ref(fypc->last_comment.fyi); + fy_input_unref(fyp->override_comment.fyi); + fyp->override_comment = fypc->override_comment; + fyp->override_comment.fyi = fy_input_ref(fypc->override_comment.fyi); + /* indents */ while ((fyit = fy_indent_list_pop(&fyp->indent_stack)) != NULL) fy_parse_indent_recycle(fyp, fyit); diff --git a/src/lib/fy-parse.h b/src/lib/fy-parse.h index d269f58..8aca4c7 100644 --- a/src/lib/fy-parse.h +++ b/src/lib/fy-parse.h @@ -217,6 +217,8 @@ struct fy_parser { /* last comment */ struct fy_atom last_comment; + /* next comment */ + struct fy_atom override_comment; /* indent stack */ struct fy_indent_list indent_stack; @@ -418,7 +420,7 @@ static inline void fy_get_mark(struct fy_parser *fyp, struct fy_mark *fym) { assert(fyp); - return fy_reader_get_mark(fyp->reader, fym); + fy_reader_get_mark(fyp->reader, fym); } static inline const void * @@ -440,7 +442,7 @@ static inline void fy_advance_octets(struct fy_parser *fyp, size_t advance) { assert(fyp); - return fy_reader_advance_octets(fyp->reader, advance); + fy_reader_advance_octets(fyp->reader, advance); } /* compare string at the current point (n max) */ @@ -619,7 +621,7 @@ fy_parser_set_reader(struct fy_parser *fyp, struct fy_reader *fyr) { if (!fyp) return; - fyp->reader = fyr ? : &fyp->builtin_reader; + fyp->reader = fyr ? fyr : &fyp->builtin_reader; } static inline void diff --git a/src/lib/fy-token.c b/src/lib/fy-token.c index 58f8315..5014298 100644 --- a/src/lib/fy-token.c +++ b/src/lib/fy-token.c @@ -42,7 +42,7 @@ enum fy_token_type fy_token_get_type(struct fy_token *fyt) void fy_token_clean_rl(struct fy_token_list *fytl, struct fy_token *fyt) { - int i; + struct fy_token_comment *tk; if (!fyt) return; @@ -51,12 +51,19 @@ void fy_token_clean_rl(struct fy_token_list *fytl, struct fy_token *fyt) fy_input_unref(fyt->handle.fyi); fyt->handle.fyi = NULL; - /* release comment references */ - if (fyt->comment) { - for (i = 0; i < fycp_max; i++) - fy_input_unref(fyt->comment[i].fyi); - free(fyt->comment); - fyt->comment = NULL; + /* release the token comments */ + while ((tk = fyt->token_comment) != NULL) { + fyt->token_comment = tk->next; + fy_input_unref(tk->handle.fyi); + fy_atom_reset(&tk->handle); + if (tk->comment) + free(tk->comment); + free(tk); + } + + if (fyt->comments) { + free(fyt->comments); + fyt->comments = NULL; } switch (fyt->type) { @@ -144,13 +151,26 @@ static bool fy_token_text_needs_rebuild(struct fy_token *fyt) return fya->fyi_generation != fya->fyi->generation; } -static int fy_tag_token_format_internal(const struct fy_token *fyt, void *out, size_t *outszp) +#define O_CPY(_src, _len) \ + do { \ + size_t _l = (_len); \ + if (o && _l) { \ + size_t _cl = _l; \ + if (_cl > (size_t)(oe - o)) \ + _cl = (size_t)(oe - o); \ + memcpy(o, (_src), _cl); \ + o += _cl; \ + } \ + len += _l; \ + } while(0) + +static size_t fy_tag_token_format_internal(const struct fy_token *fyt, void *out, size_t *outszp) { char *o = NULL, *oe = NULL; size_t outsz; const char *handle, *suffix; - size_t handle_size, suffix_size; - int len, code_length, rlen; + size_t len, rlen, handle_size, suffix_size; + int code_length; uint8_t code[4]; const char *t, *s, *e; @@ -163,32 +183,19 @@ static int fy_tag_token_format_internal(const struct fy_token *fyt, void *out, s if (out) { outsz = *outszp; o = out; - oe = out + outsz; + oe = (char *)out + outsz; } if (!fyt->tag.fyt_td) - return -1; + return 0; handle = fy_tag_directive_token_prefix(fyt->tag.fyt_td, &handle_size); if (!handle) - return -1; + return 0; suffix = fy_atom_data(&fyt->handle) + fyt->tag.skip + fyt->tag.handle_length; suffix_size = fyt->tag.suffix_length; -#define O_CPY(_src, _len) \ - do { \ - int _l = (_len); \ - if (o && _l) { \ - int _cl = _l; \ - if (_cl > (oe - o)) \ - _cl = oe - o; \ - memcpy(o, (_src), _cl); \ - o += _cl; \ - } \ - len += _l; \ - } while(0) - len = 0; O_CPY(handle, handle_size); @@ -198,7 +205,7 @@ static int fy_tag_token_format_internal(const struct fy_token *fyt, void *out, s while (s < e) { /* find next escape */ t = memchr(s, '%', e - s); - rlen = (t ? t : e) - s; + rlen = (size_t)((t ? t : e) - s); O_CPY(s, rlen); /* end of string */ @@ -216,28 +223,28 @@ static int fy_tag_token_format_internal(const struct fy_token *fyt, void *out, s s = t; } -#undef O_CPY return len; - } -int fy_tag_token_format_text_length(const struct fy_token *fyt) +size_t fy_tag_token_format_text_length(const struct fy_token *fyt) { return fy_tag_token_format_internal(fyt, NULL, NULL); } const char *fy_tag_token_format_text(const struct fy_token *fyt, char *buf, size_t maxsz) { + if (maxsz > 0) + buf[0] = '\0'; fy_tag_token_format_internal(fyt, buf, &maxsz); return buf; } -static int fy_tag_directive_token_format_internal(const struct fy_token *fyt, +static size_t fy_tag_directive_token_format_internal(const struct fy_token *fyt, void *out, size_t *outszp) { char *o = NULL, *oe = NULL; size_t outsz; - int len; + size_t len; const char *handle, *prefix; size_t handle_size, prefix_size; @@ -250,22 +257,9 @@ static int fy_tag_directive_token_format_internal(const struct fy_token *fyt, if (out) { outsz = *outszp; o = out; - oe = out + outsz; + oe = (char *)out + outsz; } -#define O_CPY(_src, _len) \ - do { \ - int _l = (_len); \ - if (o && _l) { \ - int _cl = _l; \ - if (_cl > (oe - o)) \ - _cl = oe - o; \ - memcpy(o, (_src), _cl); \ - o += _cl; \ - } \ - len += _l; \ - } while(0) - len = 0; handle = fy_atom_data(&fyt->handle); @@ -283,12 +277,11 @@ static int fy_tag_directive_token_format_internal(const struct fy_token *fyt, if (!handle_size) O_CPY(">", 1); -#undef O_CPY return len; - } +#undef O_CPY -int fy_tag_directive_token_format_text_length(const struct fy_token *fyt) +size_t fy_tag_directive_token_format_text_length(const struct fy_token *fyt) { return fy_tag_directive_token_format_internal(fyt, NULL, NULL); } @@ -408,6 +401,7 @@ struct fy_token *fy_token_vcreate_rl(struct fy_token_list *fytl, enum fy_token_t if (!fyt) goto err_out; fyt->type = type; + fyt->handle.fyi = NULL; handle = va_arg(ap, struct fy_atom *); if (handle) @@ -424,13 +418,13 @@ struct fy_token *fy_token_vcreate_rl(struct fy_token_list *fytl, enum fy_token_t fyt->tag_directive.handle0 = NULL; break; case FYTT_SCALAR: - fyt->scalar.style = va_arg(ap, enum fy_scalar_style); - if (fyt->scalar.style != FYSS_ANY && (unsigned int)fyt->scalar.style >= FYSS_MAX) - goto err_out; fyt->scalar.path_key = NULL; fyt->scalar.path_key_len = 0; fyt->scalar.path_key_storage = NULL; fyt->scalar.is_null = false; /* by default the scalar is not NULL */ + fyt->scalar.style = va_arg(ap, enum fy_scalar_style); + if (fyt->scalar.style != FYSS_ANY && (unsigned int)fyt->scalar.style >= FYSS_MAX) + goto err_out; break; case FYTT_TAG: fyt->tag.skip = va_arg(ap, unsigned int); @@ -533,9 +527,9 @@ struct fy_token *fy_parse_token_create(struct fy_parser *fyp, enum fy_token_type return fyt; } -int fy_token_format_text_length(struct fy_token *fyt) +size_t fy_token_format_text_length(struct fy_token *fyt) { - int length; + size_t length; if (!fyt) return 0; @@ -553,7 +547,6 @@ int fy_token_format_text_length(struct fy_token *fyt) } length = fy_atom_format_text_length(&fyt->handle); - return length; } @@ -587,7 +580,7 @@ const char *fy_token_format_text(struct fy_token *fyt, char *buf, size_t maxsz) return str; } -int fy_token_format_utf8_length(struct fy_token *fyt) +size_t fy_token_format_utf8_length(struct fy_token *fyt) { const char *str; size_t len; @@ -611,7 +604,6 @@ int fy_token_format_utf8_length(struct fy_token *fyt) return fy_atom_format_utf8_length(&fyt->handle); } - struct fy_atom *fy_token_atom(struct fy_token *fyt) { return fyt ? &fyt->handle : NULL; @@ -688,7 +680,7 @@ fy_token_text_analyze(struct fy_token *fyt) flags |= FYTTAF_DIRECT_OUTPUT; - maxcol = fyt->handle.storage_hint; + maxcol = (int)fyt->handle.storage_hint; maxspan = maxcol - 1; flags |= FYTTAF_DIRECT_OUTPUT | @@ -1067,7 +1059,7 @@ const char *fy_tag_token_short(struct fy_token *fyt, size_t *lenp) text0[handle_len + suffix_len] = '\0'; fyt->tag.short0 = text0; - fyt->tag.short_length = handle_len + suffix_len; + fyt->tag.short_length = (unsigned int)(handle_len + suffix_len); *lenp = fyt->tag.short_length; @@ -1094,7 +1086,7 @@ const struct fy_version * fy_version_directive_token_version(struct fy_token *fy static void fy_token_prepare_text(struct fy_token *fyt) { - int ret; + size_t ret; assert(fyt); @@ -1102,7 +1094,7 @@ static void fy_token_prepare_text(struct fy_token *fyt) ret = fy_token_format_text_length(fyt); /* no text on this token? */ - if (ret == -1) { + if (ret == 0) { fyt->text_len = 0; fyt->text = fyt->text0 = strdup(""); return; @@ -1174,85 +1166,261 @@ enum comment_out_state { cos_lastnlhashspc, }; -const char *fy_token_get_comment(struct fy_token *fyt, char *buf, size_t maxsz, - enum fy_comment_placement which) +const char * +fy_token_get_comment(struct fy_token *fyt, enum fy_comment_placement which) { + struct fy_token_comment *tk; struct fy_atom *handle; struct fy_atom_iter iter; const struct fy_iter_chunk *ic; char *s, *e; const char *ss, *ee; - int c, w, ret; + int c, w, ret, pass; + size_t size; enum comment_out_state state; bool output; - if (!buf || maxsz == 0 || (unsigned int)which >= fycp_max) + if (!fyt || (unsigned int)which >= fycp_max) return NULL; - /* return empty? */ - handle = fy_token_comment_handle(fyt, which, false); - if (!handle || !fy_atom_is_set(handle)) + for (tk = fyt->token_comment; tk; tk = tk->next) { + if (tk->placement == which && fy_atom_is_set(&tk->handle)) + break; + } + + /* not found, we're done */ + if (!tk) return NULL; - /* start expecting # */ - state = cos_lastnl; + /* found and already generated */ + if (tk->comment) + return tk->comment; - s = buf; - e = s + maxsz; - fy_atom_iter_start(handle, &iter); - ic = NULL; - while ((ic = fy_atom_iter_chunk_next(&iter, ic, &ret)) != NULL) { - ss = ic->str; - ee = ss + ic->len; + handle = &tk->handle; - while ((c = fy_utf8_get(ss, ee - ss, &w)) > 0) { + s = e = NULL; + size = 0; + for (pass = 0; pass < 2; pass++) { - output = true; - switch (state) { - case cos_normal: - if (fy_is_lb_m(c, handle->lb_mode)) - state = cos_lastnl; - break; + if (pass) { + tk->comment = malloc(size + 1); + if (!tk->comment) + return NULL; + s = tk->comment; + e = s + size + 1; + } - case cos_lastnl: - if (c == '#') { - state = cos_lastnlhash; - output = false; + /* start expecting # */ + state = cos_lastnl; + fy_atom_iter_start(handle, &iter); + ic = NULL; + while ((ic = fy_atom_iter_chunk_next(&iter, ic, &ret)) != NULL) { + ss = ic->str; + ee = ss + ic->len; + + while ((c = fy_utf8_get(ss, ee - ss, &w)) > 0) { + + output = true; + switch (state) { + case cos_normal: + if (fy_is_lb_m(c, handle->lb_mode)) + state = cos_lastnl; + break; + + case cos_lastnl: + if (c == '#') { + state = cos_lastnlhash; + output = false; + break; + } + state = cos_normal; + break; + + case cos_lastnlhash: + if (c == ' ') { + state = cos_lastnlhashspc; + output = false; + break; + } + if (fy_is_lb_m(c, handle->lb_mode)) { + state = cos_lastnl; + output = true; + break; + } + state = cos_normal; break; - } - state = cos_normal; - break; - case cos_lastnlhash: - if (c == ' ') { - state = cos_lastnlhashspc; - output = false; + case cos_lastnlhashspc: + state = cos_normal; break; } - state = cos_normal; - break; - case cos_lastnlhashspc: - state = cos_normal; - break; - } + if (output) { + if (s) { + s = fy_utf8_put(s, (size_t)(e - s), c); + if (!s) + return NULL; + } + size += w; + } - if (output) { - s = fy_utf8_put(s, (size_t)(e - s), c); - if (!s) - return NULL; + ss += w; } - - ss += w; } + fy_atom_iter_finish(&iter); } - fy_atom_iter_finish(&iter); - if (ret != 0 || s >= e) - return NULL; + assert(s); + assert(s < e); *s = '\0'; - return buf; + return tk->comment; +} + +const char * +fy_token_get_comments(struct fy_token *fyt) +{ + enum fy_comment_placement placement; + const char *str; + char *accum, *new_accum; + size_t size, accum_size, new_accum_size; + bool has_nl; + + if (!fyt) + return NULL; + + /* quick exit if no comments */ + if (!fyt->token_comment) + return NULL; + + /* already have it generated? */ + if (fyt->comments) + return fyt->comments; + + str = NULL; + accum = NULL; + accum_size = 0; + for (placement = fycp_top; placement < fycp_max; placement++) { + + str = fy_token_get_comment(fyt, placement); + if (!str) + continue; + + size = strlen(str); + has_nl = size > 0 && str[size-1] == '\n'; + new_accum_size = accum_size + size + !has_nl + 1; + new_accum = realloc(accum, new_accum_size); + if (!new_accum) + goto err_out; + accum = new_accum; + memcpy(accum + accum_size, str, size); + accum_size += size; + if (!has_nl) + accum[accum_size++] = '\n'; + accum[accum_size] = '\0'; + } + return fyt->comments = accum; + +err_out: + if (accum) + free(accum); + return NULL; +} + +int +fy_token_set_comment(struct fy_token *fyt, enum fy_comment_placement which, + const char *text, size_t len) +{ + struct fy_token_comment *tk, *tk_prev; + struct fy_input *fyi = NULL; + char *data = NULL; + const char *s, *e; + char utf8buf[FY_UTF8_MAX_WIDTH]; + int c, lastc, w; + size_t sz; + struct fy_atom *handle; + struct fy_memstream *fyms = NULL; + FILE *fp; + + if (!fyt || (unsigned int)which >= fycp_max) + return -1; + + tk_prev = NULL; + for (tk = fyt->token_comment; tk; tk = tk->next) { + if (tk->placement == which) + break; + tk_prev = tk; + } + + /* removal of comment */ + if (!text && tk) { + if (!tk_prev) + fyt->token_comment = tk->next; + else + tk_prev = tk->next; + + fy_input_unref(tk->handle.fyi); + fy_atom_reset(&tk->handle); + if (tk->comment) + free(tk->comment); + free(tk); + return 0; + } + + if (len == FY_NT) + len = strlen(text); + + data = NULL; + + fyms = fy_memstream_open(&fp); + if (!fyms) + goto err_out; + + s = text; + e = text + len; + lastc = -1; + fputs("# ", fp); + while ((c = fy_utf8_get(s, e - s, &w)) > 0) { + s += w; + if (lastc == '\n') + fputs("\n# ", fp); + if (fy_token_is_lb(fyt, c)) { + c = '\n'; + } else { + assert((size_t)w <= sizeof(utf8buf)); + fy_utf8_put_unchecked(utf8buf, c); + fwrite(utf8buf, 1, w, fp); + } + lastc = c; + } + + data = fy_memstream_close(fyms, &sz); + fyms = NULL; + + len = sz; + + handle = tk ? &tk->handle : fy_token_comment_handle(fyt, which, true); + if (!handle) + goto err_out; + + fy_input_unref(handle->fyi); + fy_atom_reset(handle); + + fyi = fy_input_from_malloc_data(data, len, handle, true); + if (!fyi) + goto err_out; + + return 0; + +err_out: + if (fyms) { + data = fy_memstream_close(fyms, &sz); + fyms = NULL; + } + fy_input_unref(fyi); + if (data) + free(data); + return -1; } const char *fy_token_get_scalar_path_key(struct fy_token *fyt, size_t *lenp) @@ -1366,7 +1534,7 @@ const char *fy_token_get_scalar_path_key(struct fy_token *fyt, size_t *lenp) case '\v': fy_emit_accum_utf8_put(&ea, 'v'); break; - case '\e': + case '\x1b': fy_emit_accum_utf8_put(&ea, 'e'); break; case 0x85: @@ -1624,7 +1792,7 @@ char *fy_token_debug_text(struct fy_token *fyt) out: text = fy_token_get_text(fyt, &length); - wlen = length > 8 ? 8 : length; + wlen = length > 8 ? 8 : (int)length; rc = asprintf(&buf, "%s:%.*s%s", typetxt, wlen, text, wlen < (int)length ? "..." : ""); assert(rc != -1); @@ -2010,38 +2178,41 @@ fy_tag_directive_token_tag(struct fy_token *fyt) struct fy_atom *fy_token_comment_handle(struct fy_token *fyt, enum fy_comment_placement placement, bool alloc) { - struct fy_atom *handle; - size_t size; + struct fy_token_comment *tk; if (!fyt || (unsigned int)placement >= fycp_max) return NULL; - if (!fyt->comment) { - if (!alloc) - return NULL; - - size = sizeof(*fyt->comment) * fycp_max; - fyt->comment = malloc(size); - if (!fyt->comment) - return NULL; - memset(fyt->comment, 0, size); + for (tk = fyt->token_comment; tk; tk = tk->next) { + if (tk->placement == placement) + return &tk->handle; } - handle = &fyt->comment[placement]; - return handle; + if (!alloc) + return NULL; + + tk = malloc(sizeof(*tk)); + if (!tk) + return NULL; + memset(tk, 0, sizeof(*tk)); + tk->placement = placement; + + /* simple list add */ + tk->next = fyt->token_comment; + fyt->token_comment = tk; + + return &tk->handle; } bool fy_token_has_any_comment(struct fy_token *fyt) { - struct fy_atom *handle; - enum fy_comment_placement placement; + struct fy_token_comment *tk; - if (!fyt || !fyt->comment) + if (!fyt) return false; - for (placement = fycp_top; placement <= fycp_bottom; placement++) { - handle = &fyt->comment[placement]; - if (fy_atom_is_set(handle)) + for (tk = fyt->token_comment; tk; tk = tk->next) { + if (fy_atom_is_set(&tk->handle)) return true; } return false; diff --git a/src/lib/fy-token.h b/src/lib/fy-token.h index c0a76f5..0dfbc04 100644 --- a/src/lib/fy-token.h +++ b/src/lib/fy-token.h @@ -113,6 +113,13 @@ struct fy_token_analysis { int maxcol; }; +struct fy_token_comment { + struct fy_token_comment *next; + enum fy_comment_placement placement; + struct fy_atom handle; + char *comment; +}; + FY_TYPE_FWD_DECL_LIST(token); struct fy_token { struct list_head node; @@ -123,7 +130,8 @@ struct fy_token { const char *text; char *text0; /* this is allocated */ struct fy_atom handle; - struct fy_atom *comment; /* only when enabled */ + struct fy_token_comment *token_comment; + char *comments; /* this is allocated */ union { struct { unsigned int tag_length; /* from start */ @@ -207,7 +215,8 @@ fy_token_alloc_rl(struct fy_token_list *fytl) fyt->text = NULL; fyt->text0 = NULL; fyt->handle.fyi = NULL; - fyt->comment = NULL; + fyt->token_comment = NULL; + fyt->comments = NULL; return fyt; } @@ -247,13 +256,13 @@ fy_token_alloc(void) static inline FY_ALWAYS_INLINE void fy_token_clean(struct fy_token *fyt) { - return fy_token_clean_rl(NULL, fyt); + fy_token_clean_rl(NULL, fyt); } static inline FY_ALWAYS_INLINE void fy_token_free(struct fy_token *fyt) { - return fy_token_free_rl(NULL, fyt); + fy_token_free_rl(NULL, fyt); } static inline FY_ALWAYS_INLINE struct fy_token * @@ -271,13 +280,13 @@ fy_token_ref(struct fy_token *fyt) static inline FY_ALWAYS_INLINE void fy_token_unref(struct fy_token *fyt) { - return fy_token_unref_rl(NULL, fyt); + fy_token_unref_rl(NULL, fyt); } static inline void fy_token_list_unref_all(struct fy_token_list *fytl_tofree) { - return fy_token_list_unref_all_rl(NULL, fytl_tofree); + fy_token_list_unref_all_rl(NULL, fytl_tofree); } /* recycling aware */ @@ -312,11 +321,11 @@ fy_token_list_queue(struct fy_token_list *fytl, enum fy_token_type type, ...) return fyt; } -int fy_tag_token_format_text_length(const struct fy_token *fyt); +size_t fy_tag_token_format_text_length(const struct fy_token *fyt); const char *fy_tag_token_format_text(const struct fy_token *fyt, char *buf, size_t maxsz); -int fy_token_format_utf8_length(struct fy_token *fyt); +size_t fy_token_format_utf8_length(struct fy_token *fyt); -int fy_token_format_text_length(struct fy_token *fyt); +size_t fy_token_format_text_length(struct fy_token *fyt); const char *fy_token_format_text(struct fy_token *fyt, char *buf, size_t maxsz); /* non-parser token methods */ @@ -467,6 +476,21 @@ unsigned int fy_analyze_scalar_content(const char *data, size_t size, /* must be freed */ char *fy_token_debug_text(struct fy_token *fyt); +#ifdef _MSC_VER +/* MSVC version using thread-local buffer */ +#define FY_TOKEN_DEBUG_TEXT_A_BUFSZ 4096 +static __declspec(thread) char fy_token_debug_text_a_buf[FY_TOKEN_DEBUG_TEXT_A_BUFSZ]; +static __inline const char *fy_token_debug_text_a_impl(struct fy_token *fyt) +{ + char *buf = fy_token_debug_text(fyt); + if (!buf) return ""; + strncpy(fy_token_debug_text_a_buf, buf, FY_TOKEN_DEBUG_TEXT_A_BUFSZ - 1); + fy_token_debug_text_a_buf[FY_TOKEN_DEBUG_TEXT_A_BUFSZ - 1] = '\0'; + free(buf); + return fy_token_debug_text_a_buf; +} +#define fy_token_debug_text_a(_fyt) fy_token_debug_text_a_impl(_fyt) +#else #define fy_token_debug_text_a(_fyt) \ ({ \ struct fy_token *__fyt = (_fyt); \ @@ -481,6 +505,7 @@ char *fy_token_debug_text(struct fy_token *fyt); } \ _rbuf; \ }) +#endif int fy_token_memcmp(struct fy_token *fyt, const void *ptr, size_t len); int fy_token_strcmp(struct fy_token *fyt, const char *str); diff --git a/src/lib/fy-types.c b/src/lib/fy-types.c index 1649e04..9a064ed 100644 --- a/src/lib/fy-types.c +++ b/src/lib/fy-types.c @@ -11,11 +11,6 @@ #include #include -#include -#include -#include -#include -#include #include #include #include diff --git a/src/lib/fy-walk.c b/src/lib/fy-walk.c index 8c83d9c..b539e15 100644 --- a/src/lib/fy-walk.c +++ b/src/lib/fy-walk.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -30,6 +29,34 @@ #undef DEBUG_EXPR // #define DEBUG_EXPR +/* debugging for when expressions go crazy */ +#ifdef DEBUG_EXPR +struct fy_walk_result * +_fy_path_exec_walk_result_create(struct fy_path_exec *fypx, + const char *file, int line, const char *func, + enum fy_walk_result_type type, ...); +#define fy_path_exec_walk_result_create(_fypx, _type, ...) \ + _fy_path_exec_walk_result_create((_fypx), \ + __FILE__, __LINE__, __func__, (_type) __VA_OPT__(,) __VA_ARGS__) + +void +_fy_walk_result_free(struct fy_walk_result *fwr, const char *file, int line, const char *func); +#define fy_walk_result_free(_fwr) _fy_walk_result_free((_fwr), __FILE__, __LINE__, __func__) + +void +_fy_walk_result_clean(struct fy_walk_result *fwr, const char *file, int line, const char *func); +#define fy_walk_result_clean(_fwr) _fy_walk_result_clean((_fwr), __FILE__, __LINE__, __func__) + +struct fy_walk_result * +_fy_walk_result_clone(struct fy_walk_result *fwr, const char *file, int line, const char *func); +#define fy_walk_result_clone(_fwr) _fy_walk_result_clone((_fwr), __FILE__, __LINE__, __func__) + +struct fy_walk_result * +_fy_walk_result_clone_deep(struct fy_walk_result *fwr, const char *file, int line, const char *func); +#define fy_walk_result_clone_deep(_fwr) _fy_walk_result_clone((_fwr), __FILE__, __LINE__, __func__) + +#endif + const char *fy_walk_result_type_txt[FWRT_COUNT] = { [fwrt_none] = "none", [fwrt_node_ref] = "node-ref", @@ -39,6 +66,9 @@ const char *fy_walk_result_type_txt[FWRT_COUNT] = { [fwrt_refs] = "refs", }; + +static struct fy_node *fy_node_alias_resolve_by_ypath_internal(struct fy_node *fyn, bool *errorp); + void fy_walk_result_dump(struct fy_walk_result *fwr, struct fy_diag *diag, enum fy_error_type errlevel, int level, const char *fmt, ...); @@ -66,10 +96,11 @@ void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum if (fmt) { banner = NULL; rc = vasprintf(&banner, fmt, ap); - assert(rc != -1); - assert(banner); + if (rc == -1 || !banner) + goto err_out; fy_diag_diag(diag, errlevel, "%-*s%s", level*2, "", banner); free(banner); + banner = NULL; } if (!fwr) goto out; @@ -80,7 +111,8 @@ void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum break; case fwrt_node_ref: texta = fy_node_get_path(fwr->fyn); - assert(texta); + if (!texta) + goto err_out; text = texta; break; case fwrt_number: @@ -92,7 +124,8 @@ void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum break; case fwrt_doc: texta = fy_emit_document_to_string(fwr->fyd, FYECF_WIDTH_INF | FYECF_INDENT_DEFAULT | FYECF_MODE_FLOW_ONELINE); - assert(texta); + if (!texta) + goto err_out; text = texta; break; case fwrt_refs: @@ -110,6 +143,7 @@ void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum if (texta) free(texta); + texta = NULL; if (fwr->type == fwrt_refs) { for (fwr2 = fy_walk_result_list_head(&fwr->refs); fwr2; fwr2 = fy_walk_result_next(&fwr->refs, fwr2)) @@ -117,6 +151,10 @@ void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum } out: diag->on_error = save_on_error; + return; +err_out: + diag->on_error = true; + return; } void fy_walk_result_dump(struct fy_walk_result *fwr, struct fy_diag *diag, enum fy_error_type errlevel, int level, @@ -211,6 +249,7 @@ err_out: return NULL; } +#ifndef DEBUG_EXPR struct fy_walk_result *fy_walk_result_clone(struct fy_walk_result *fwr) { struct fy_walk_result_list *fwrl; @@ -233,6 +272,45 @@ struct fy_walk_result *fy_walk_result_clone_deep(struct fy_walk_result *fwr) return fy_walk_result_clone_rl(fwrl, fwr, true); } +#else + +struct fy_walk_result * +_fy_walk_result_clone(struct fy_walk_result *fwr, const char *file, int line, const char *func) +{ + struct fy_walk_result_list *fwrl; + struct fy_walk_result *fwrn; + + if (!fwr) + return NULL; + + fwrl = fy_path_exec_walk_result_rl(fwr->fypx); + fwrn = fy_walk_result_clone_rl(fwrl, fwr, false); + + fprintf(stderr, "%s:%4d @%-48s %-32s fwr=%p (fypx=%p refs=%u)\n", + file, line, func, __func__, fwrn, fwrn->fypx, fwrn->fypx ? fwrn->fypx->refs : 0); + + return fwrn; +} + +struct fy_walk_result * +_fy_walk_result_clone_deep(struct fy_walk_result *fwr, const char *file, int line, const char *func) +{ + struct fy_walk_result_list *fwrl; + struct fy_walk_result *fwrn; + + if (!fwr) + return NULL; + + fwrl = fy_path_exec_walk_result_rl(fwr->fypx); + fwrn = fy_walk_result_clone_rl(fwrl, fwr, true); + + fprintf(stderr, "%s:%4d @%-48s %-32s fwr=%p (fypx=%p refs=%u)\n", + file, line, func, __func__, fwrn, fwrn->fypx, fwrn->fypx ? fwrn->fypx->refs : 0); + + return fwrn; +} +#endif + void fy_walk_result_clean_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr) { struct fy_walk_result *fwrn; @@ -265,9 +343,11 @@ void fy_walk_result_clean_rl(struct fy_walk_result_list *fwrl, struct fy_walk_re } fwr->type = fwrt_none; + fwr->fypx = NULL; memset(&fwr->_clean, 0, sizeof(fwr->_clean)); } +#ifndef DEBUG_EXPR void fy_walk_result_clean(struct fy_walk_result *fwr) { struct fy_walk_result_list *fwrl; @@ -278,6 +358,23 @@ void fy_walk_result_clean(struct fy_walk_result *fwr) fwrl = fy_path_exec_walk_result_rl(fwr->fypx); fy_walk_result_clean_rl(fwrl, fwr); } +#else +void _fy_walk_result_clean(struct fy_walk_result *fwr, + const char *file, int line, const char *func) +{ + struct fy_walk_result_list *fwrl; + + if (!fwr) + return; + + fprintf(stderr, "%s:%4d @%-48s %-32s fwr=%p (fypx=%p refs=%u)\n", + file, line, func, __func__, fwr, fwr->fypx, fwr->fypx ? fwr->fypx->refs : 0); + + fwrl = fy_path_exec_walk_result_rl(fwr->fypx); + fy_walk_result_clean_rl(fwrl, fwr); + +} +#endif void fy_walk_result_free_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr) { @@ -290,14 +387,17 @@ void fy_walk_result_free_rl(struct fy_walk_result_list *fwrl, struct fy_walk_res fy_walk_result_clean_rl(fwrl, fwr); +#ifndef DEBUG_EXPR if (fwrl) fy_walk_result_list_push(fwrl, fwr); else +#endif free(fwr); fy_path_exec_unref(fypx); /* NULL is OK */ } +#ifndef DEBUG_EXPR void fy_walk_result_free(struct fy_walk_result *fwr) { struct fy_walk_result_list *fwrl; @@ -308,6 +408,21 @@ void fy_walk_result_free(struct fy_walk_result *fwr) fwrl = fy_path_exec_walk_result_rl(fwr->fypx); fy_walk_result_free_rl(fwrl, fwr); } +#else +void _fy_walk_result_free(struct fy_walk_result *fwr, const char *file, int line, const char *func) +{ + struct fy_walk_result_list *fwrl; + + if (!fwr) + return; + + fprintf(stderr, "%s:%4d @%-48s %-32s fwr=%p (fypx=%p refs=%u)\n", + file, line, func, __func__, fwr, fwr->fypx, fwr->fypx ? fwr->fypx->refs : 0); + + fwrl = fy_path_exec_walk_result_rl(fwr->fypx); + fy_walk_result_free_rl(fwrl, fwr); +} +#endif void fy_walk_result_list_free_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result_list *results) { @@ -421,11 +536,15 @@ fy_walk_result_flatten_rl(struct fy_walk_result_list *fwrl, struct fy_walk_resul return NULL; fwrf = fy_walk_result_create_rl(fwrl, fwrt_refs); - assert(fwrf); + if (!fwrf) + goto err_out; fy_walk_result_flatten_internal(fwr, fwrf); fy_walk_result_free_rl(fwrl, fwr); return fwrf; + +err_out: + return NULL; } struct fy_walk_result * @@ -557,6 +676,7 @@ struct fy_path_expr *fy_path_expr_alloc_recycle(struct fy_path_parser *fypp) { struct fy_path_expr *expr = NULL; +#ifndef DEBUG_EXPR if (!fypp || fypp->suppress_recycling) expr = fy_path_expr_alloc(); @@ -568,6 +688,9 @@ struct fy_path_expr *fy_path_expr_alloc_recycle(struct fy_path_parser *fypp) } else expr = fy_path_expr_alloc(); } +#else + expr = fy_path_expr_alloc(); +#endif if (!expr) return NULL; @@ -579,6 +702,7 @@ struct fy_path_expr *fy_path_expr_alloc_recycle(struct fy_path_parser *fypp) void fy_path_expr_free_recycle(struct fy_path_parser *fypp, struct fy_path_expr *expr) { +#ifndef DEBUG_EXPR struct fy_path_expr *exprn; if (!fypp || fypp->suppress_recycling) { @@ -594,6 +718,9 @@ void fy_path_expr_free_recycle(struct fy_path_parser *fypp, struct fy_path_expr expr->fyt = NULL; } fy_path_expr_list_add_tail(&fypp->expr_recycle, expr); +#else + fy_path_expr_free(expr); +#endif } void fy_expr_stack_setup(struct fy_expr_stack *stack) @@ -651,13 +778,8 @@ int fy_expr_stack_push(struct fy_expr_stack *stack, struct fy_path_expr *expr) unsigned int alloc; size_t size; - if (!stack || !expr) - return -1; - - assert(stack->items); - assert(stack->alloc > 0); - - assert(expr->fyt); + if (!stack || !expr || !stack->items || stack->alloc <= 0 || !expr->fyt) + goto err_out; /* grow the stack if required */ if (stack->top >= stack->alloc) { @@ -681,6 +803,9 @@ int fy_expr_stack_push(struct fy_expr_stack *stack, struct fy_path_expr *expr) stack->items[stack->top++] = expr; return 0; + +err_out: + return -1; } struct fy_path_expr *fy_expr_stack_peek_at(struct fy_expr_stack *stack, unsigned int pos) @@ -877,7 +1002,8 @@ int fy_path_fetch_seq_index_or_slice(struct fy_path_parser *fypp, int c) fyr = &fypp->reader; /* verify that the called context is correct */ - assert(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1)))); + if (!(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))))) + goto err_out; i = 0; indices[0] = indices[1] = -1; @@ -949,7 +1075,8 @@ int fy_path_fetch_plain_or_method(struct fy_path_parser *fypp, int c, fyr = &fypp->reader; - assert(fy_is_first_alpha(c)); + if (!fy_is_first_alpha(c)) + goto err_out; type = fytt_plain; @@ -985,10 +1112,13 @@ int fy_path_fetch_dot_method(struct fy_path_parser *fypp, int c, enum fy_token_t fyr = &fypp->reader; - assert(c == '.'); + if (c != '.') + goto err_out; + fy_reader_advance(fyr, c); c = fy_reader_peek(fyr); - assert(fy_is_first_alpha(c)); + if (!fy_is_first_alpha(c)) + goto err_out; /* verify that the called context is correct */ i = 1; @@ -1020,7 +1150,8 @@ int fy_path_fetch_flow_document(struct fy_path_parser *fypp, int c, enum fy_toke fyr = &fypp->reader; /* verify that the called context is correct */ - assert(fy_is_path_flow_key_start(c)); + if (!fy_is_path_flow_key_start(c)) + goto err_out; fy_reader_fill_atom_start(fyr, &handle); @@ -1072,7 +1203,8 @@ int fy_path_fetch_flow_scalar(struct fy_path_parser *fypp, int c) fyr = &fypp->reader; /* verify that the called context is correct */ - assert(fy_is_path_flow_scalar_start(c)); + if (!fy_is_path_flow_scalar_start(c)) + goto err_out; is_single = c == '\''; @@ -1102,7 +1234,8 @@ int fy_path_fetch_number(struct fy_path_parser *fypp, int c) fyr = &fypp->reader; /* verify that the called context is correct */ - assert(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1)))); + if (!(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))))) + goto err_out; i = 0; if (c == '-') @@ -1438,10 +1571,8 @@ do_token: if (fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1)))) return fy_path_fetch_number(fypp, c); -#if 0 - if (c == '.' && fy_is_first_alpha(fy_reader_peek_at(fyr, 1))) - return fy_path_fetch_dot_method(fypp, c, FYTT_SE_METHOD); -#endif + // if (c == '.' && fy_is_first_alpha(fy_reader_peek_at(fyr, 1))) + // return fy_path_fetch_dot_method(fypp, c, FYTT_SE_METHOD); break; } @@ -1600,7 +1731,7 @@ fy_path_expr_to_node_internal(struct fy_document *fyd, struct fy_path_expr *expr const char *style = ""; const char *text; size_t len; - struct fy_node *fyn = NULL, *fyn2, *fyn_seq = NULL; + struct fy_node *fyn = NULL, *fyn2 = NULL, *fyn_seq = NULL; int rc; text = fy_token_get_text(expr->fyt, &len); @@ -1660,6 +1791,7 @@ fy_path_expr_to_node_internal(struct fy_document *fyd, struct fy_path_expr *expr rc = fy_node_sequence_append(fyn_seq, fyn2); if (rc) goto err_out; + fyn2 = NULL; } if (expr->type != fpet_method) { @@ -1889,7 +2021,7 @@ int fy_path_expr_type_prec(enum fy_path_expr_type type) static inline FY_UNUSED void dump_operand_stack(struct fy_path_parser *fypp) { - return fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands); + fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands); } static inline int @@ -2029,9 +2161,11 @@ expr_to_token_mark(struct fy_path_expr *expr, struct fy_input *fyi) return NULL; ms = fy_path_expr_start_mark(expr); - assert(ms); + if (!ms) + goto err_out; me = fy_path_expr_end_mark(expr); - assert(me); + if (!me) + goto err_out; memset(&handle, 0, sizeof(handle)); handle.start_mark = *ms; @@ -2041,6 +2175,8 @@ expr_to_token_mark(struct fy_path_expr *expr, struct fy_input *fyi) handle.chomp = FYAC_CLIP; return fy_token_create(FYTT_INPUT_MARKER, &handle); +err_out: + return NULL; } struct fy_token * @@ -2053,9 +2189,11 @@ expr_lr_to_token_mark(struct fy_path_expr *exprl, struct fy_path_expr *exprr, st return NULL; ms = fy_path_expr_start_mark(exprl); - assert(ms); + if (!ms) + goto err_out; me = fy_path_expr_end_mark(exprr); - assert(me); + if (!me) + goto err_out; memset(&handle, 0, sizeof(handle)); handle.start_mark = *ms; @@ -2065,6 +2203,8 @@ expr_lr_to_token_mark(struct fy_path_expr *exprl, struct fy_path_expr *exprr, st handle.chomp = FYAC_CLIP; return fy_token_create(FYTT_INPUT_MARKER, &handle); +err_out: + return NULL; } int @@ -2103,7 +2243,8 @@ int push_operand_lr(struct fy_path_parser *fypp, int ret; optimize = false; - assert(exprl || exprr); + if (!exprl && !exprr) + goto err_out; fyr = &fypp->reader; @@ -2121,25 +2262,13 @@ int push_operand_lr(struct fy_path_parser *fypp, expr->type = type; expr->fyt = NULL; - if (exprl) { - assert(exprl->fyt); - ms = fy_token_start_mark(exprl->fyt); - assert(ms); - } else { - ms = fy_token_start_mark(exprr->fyt); - assert(ms); - } - - if (exprr) { - assert(exprr->fyt); - me = fy_token_end_mark(exprr->fyt); - assert(me); - } else { - me = fy_token_end_mark(exprl->fyt); - assert(me); - } + ms = fy_token_start_mark(exprl ? exprl->fyt : exprr->fyt); + if (!ms) + goto err_out; - assert(ms && me); + me = fy_token_end_mark(exprr ? exprr->fyt : exprl->fyt); + if (!me) + goto err_out; memset(&handle, 0, sizeof(handle)); handle.start_mark = *ms; @@ -2217,14 +2346,16 @@ common_builtin_ref_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs); + struct fy_walk_result **args, int nargs, + bool *errorp); static struct fy_walk_result * common_builtin_collection_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs); + struct fy_walk_result **args, int nargs, + bool *errorp); static struct fy_walk_result * @@ -2232,14 +2363,16 @@ test_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs); + struct fy_walk_result **args, int nargs, + bool *errorp); static struct fy_walk_result * sum_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs); + struct fy_walk_result **args, int nargs, + bool *errorp); static const struct fy_method fy_methods[] = { [fymi_test] = { @@ -2332,7 +2465,7 @@ static inline int fy_method_to_builtin_idx(const struct fy_method *fym) { if (!fym || fym < fy_methods || fym >= &fy_methods[ARRAY_SIZE(fy_methods)]) return -1; - return fym - fy_methods; + return (int)(fym - fy_methods); } static struct fy_walk_result * @@ -2340,45 +2473,51 @@ common_builtin_ref_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs) + struct fy_walk_result **args, int nargs, + bool *errorp) { enum fy_method_idx midx; struct fy_walk_result *output = NULL; struct fy_walk_result *fwr, *fwrn; struct fy_node *fyn, *fynt; + bool error; int i; + if (errorp) + *errorp = false; + if (!fypx || !input) - goto out; + goto err_out; i = fy_method_to_builtin_idx(fym); if (i < 0) - goto out; + goto err_out; midx = (enum fy_method_idx)i; switch (midx) { case fymi_key: case fymi_value: if (!args || nargs != 1 || !args[0] || args[0]->type != fwrt_string) - goto out; + goto err_out; break; case fymi_index: if (!args || nargs != 1 || !args[0] || args[0]->type != fwrt_number) - goto out; + goto err_out; break; case fymi_root: case fymi_parent: case fymi_this: case fymi_null: if (nargs != 0) - goto out; + goto err_out; break; default: - goto out; + goto err_out; } output = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(output); + if (!output) + goto err_out; for (fwr = fy_walk_result_iter_start(input); fwr; fwr = fy_walk_result_iter_next(input, fwr)) { @@ -2396,8 +2535,9 @@ common_builtin_ref_exec(const struct fy_method *fym, case fymi_null: /* dereference alias */ if (fy_node_is_alias(fynt)) { - // fprintf(stderr, "%s: %s calling fy_node_alias_resolve_by_ypath()\n", __func__, fy_node_get_path_alloca(fyn)); - fynt = fy_node_alias_resolve_by_ypath(fynt); + fynt = fy_node_alias_resolve_by_ypath_internal(fynt, &error); + if (error) + goto err_out; } break; default: @@ -2450,8 +2590,8 @@ common_builtin_ref_exec(const struct fy_method *fym, continue; fwrn = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn); - assert(fwrn); - + if (!fwrn) + goto err_out; fy_walk_result_list_add_tail(&output->refs, fwrn); } @@ -2462,7 +2602,8 @@ common_builtin_ref_exec(const struct fy_method *fym, output = NULL; } else if (fy_walk_result_list_is_singular(&output->refs)) { fwr = fy_walk_result_list_pop(&output->refs); - assert(fwr); + if (!fwr) + goto err_out; fy_walk_result_free(output); output = fwr; } @@ -2475,6 +2616,11 @@ out: fy_walk_result_free(args[i]); } return output; +err_out: + if (errorp) + *errorp = true; + fy_walk_result_free(output); + goto out; } static struct fy_walk_result * @@ -2482,7 +2628,8 @@ common_builtin_collection_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs) + struct fy_walk_result **args, int nargs, + bool *errorp) { enum fy_method_idx midx; struct fy_walk_result *output = NULL; @@ -2490,14 +2637,18 @@ common_builtin_collection_exec(const struct fy_method *fym, struct fy_path_expr *expr_arg; bool match, done; int input_count, match_count; + bool error; int i; + if (errorp) + *errorp = false; + if (!fypx || !input) - goto out; + goto err_out; i = fy_method_to_builtin_idx(fym); if (i < 0) - goto out; + goto err_out; midx = (enum fy_method_idx)i; switch (midx) { @@ -2505,25 +2656,27 @@ common_builtin_collection_exec(const struct fy_method *fym, case fymi_all: case fymi_select: if (!args || nargs != 1 || !args[0]) - goto out; + goto err_out; break; default: - goto out; + goto err_out; } expr_arg = fy_path_expr_list_head(&expr->children); - assert(expr_arg); + if (!expr_arg) + goto err_out; /* only handle inputs of node and refs */ if (input->type != fwrt_node_ref && input->type != fwrt_refs) - goto out; + goto err_out; output = NULL; switch (midx) { case fymi_select: output = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(output); + if (!output) + goto err_out; break; default: break; @@ -2537,9 +2690,13 @@ common_builtin_collection_exec(const struct fy_method *fym, input_count++; fwrt = fy_walk_result_clone(fwr); - assert(fwrt); + if (!fwrt) + goto err_out; - fwrn = fy_path_expr_execute(fypx, level + 1, expr_arg, fwrt, expr->type); + fwrn = fy_path_expr_execute(fypx, level + 1, expr_arg, fwrt, expr->type, &error); + fwrt = NULL; + if (error) + goto err_out; match = fwrn != NULL; if (match) @@ -2564,7 +2721,8 @@ common_builtin_collection_exec(const struct fy_method *fym, break; if (match) { fwrt = fy_walk_result_clone(fwr); - assert(fwrt); + if (!fwrt) + goto err_out; fy_walk_result_list_add_tail(&output->refs, fwrt); } break; @@ -2573,8 +2731,8 @@ common_builtin_collection_exec(const struct fy_method *fym, break; } - if (fwrn) - fy_walk_result_free(fwrn); + fy_walk_result_free(fwrn); + fwrn = NULL; } switch (midx) { @@ -2602,21 +2760,28 @@ common_builtin_collection_exec(const struct fy_method *fym, output = NULL; } else if (fy_walk_result_list_is_singular(&output->refs)) { fwr = fy_walk_result_list_pop(&output->refs); - assert(fwr); + if (!fwr) + goto err_out; fy_walk_result_free(output); output = fwr; } } out: - if (input) - fy_walk_result_free(input); + fy_walk_result_free(input); if (args) { for (i = 0; i < nargs; i++) fy_walk_result_free(args[i]); } return output; + +err_out: + if (errorp) + *errorp = true; + fy_walk_result_free(output); + output = NULL; + goto out; } static struct fy_walk_result * @@ -2624,7 +2789,8 @@ test_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs) + struct fy_walk_result **args, int nargs, + bool *errorp) { int i; struct fy_walk_result *output = NULL; @@ -2657,7 +2823,8 @@ sum_exec(const struct fy_method *fym, struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs) + struct fy_walk_result **args, int nargs, + bool *errorp) { int i; struct fy_walk_result *output = NULL; @@ -2724,7 +2891,8 @@ int evaluate_method(struct fy_path_parser *fypp, struct fy_path_expr *exprm, fy_path_expr_order(exprm, exprt) < 0) { exprt = fy_expr_stack_pop(&fypp->operands); - assert(exprt); + if (!exprt) + goto err_out; #ifdef DEBUG_EXPR FYR_TOKEN_DIAG(fyr, exprt->fyt, @@ -2795,11 +2963,9 @@ int evaluate_new(struct fy_path_parser *fypp) fyr = &fypp->reader; expr = fy_expr_stack_pop(&fypp->operators); - fyr_error_check(fyr, expr, err_out, + fyr_error_check(fyr, expr && expr->fyt, err_out, "pop_operator() failed to find token operator to evaluate\n"); - assert(expr->fyt); - #ifdef DEBUG_EXPR FYR_TOKEN_DIAG(fyr, expr->fyt, FYDF_NOTICE, FYEM_PARSE, "poped operator expression"); @@ -2870,7 +3036,8 @@ int evaluate_new(struct fy_path_parser *fypp) expr->fyt = NULL; } else { - assert(exprr && exprl); + if (!(exprr && exprl)) + goto err_out; // fyr_notice(fyr, "CHAIN operator\n"); } @@ -3055,7 +3222,8 @@ int evaluate_new(struct fy_path_parser *fypp) if (exprm && exprm->type == fpet_method) { exprm = fy_expr_stack_pop(&fypp->operators); - assert(exprm); + if (!exprm) + goto err_out; return evaluate_method(fypp, exprm, exprl, exprr); } @@ -3545,14 +3713,19 @@ err_out: } static struct fy_node * -fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *expr, struct fy_node *fyn) +fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *expr, struct fy_node *fyn, bool *errorp) { struct fy_token *fyt; struct fy_anchor *fya; const char *text; size_t len; + bool error; - assert(expr); + if (errorp) + *errorp = false; + + if (!expr) + goto err_out; switch (expr->type) { case fpet_root: @@ -3560,8 +3733,9 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_this: if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } return fyn; @@ -3571,12 +3745,12 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_alias: fyt = expr->fyt; - assert(fyt); - assert(fyt->type == FYTT_PE_ALIAS); + if (!fyt || fyt->type != FYTT_PE_ALIAS) + goto err_out; text = fy_token_get_text(fyt, &len); if (!text || len < 1) - break; + goto err_out; if (*text == '*') { text++; @@ -3584,17 +3758,19 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex } fya = fy_document_lookup_anchor(fyn->fyd, text, len); if (!fya) - break; + goto err_out; + return fya->fyn; case fpet_seq_index: fyt = expr->fyt; - assert(fyt); - assert(fyt->type == FYTT_PE_SEQ_INDEX); + if (!fyt || fyt->type != FYTT_PE_SEQ_INDEX) + goto err_out; if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } /* only on sequence */ @@ -3605,12 +3781,13 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_map_key: fyt = expr->fyt; - assert(fyt); - assert(fyt->type == FYTT_PE_MAP_KEY); + if (!fyt || fyt->type != FYTT_PE_MAP_KEY) + goto err_out; if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } if (!fy_node_is_mapping(fyn)) @@ -3634,8 +3811,9 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_filter_collection: if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } if (!(fy_node_is_mapping(fyn) || fy_node_is_sequence(fyn))) @@ -3645,8 +3823,9 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_filter_sequence: if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } if (!fy_node_is_sequence(fyn)) @@ -3656,8 +3835,9 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex case fpet_filter_mapping: if (fy_node_is_alias(fyn)) { - // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn)); - fyn = fy_node_alias_resolve_by_ypath(fyn); + fyn = fy_node_alias_resolve_by_ypath_internal(fyn, &error); + if (error) + goto err_out; } if (!fy_node_is_mapping(fyn)) @@ -3669,6 +3849,11 @@ fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *ex } return NULL; + +err_out: + if (errorp) + *errorp = true; + return NULL; } static double @@ -3831,10 +4016,6 @@ int fy_path_exec_reset(struct fy_path_exec *fypx) struct fy_walk_result *fy_walk_result_simplify(struct fy_walk_result *fwr) { struct fy_walk_result *fwr2; -#if 0 - struct fy_walk_result *fwrf; - bool recursive; -#endif /* no fwr */ if (!fwr) @@ -3853,45 +4034,21 @@ struct fy_walk_result *fy_walk_result_simplify(struct fy_walk_result *fwr) /* single element, switch it out */ if (fy_walk_result_list_is_singular(&fwr->refs)) { fwr2 = fy_walk_result_list_pop(&fwr->refs); - assert(fwr2); + if (!fwr2) + goto err_out; fy_walk_result_free(fwr); fwr = fwr2; } return fwr; -#if 0 - /* non recursive return immediately */ - if (fwr->type != fwrt_refs) - return fwr; - - /* flatten if recursive */ - recursive = false; - for (fwr2 = fy_walk_result_list_head(&fwr->refs); fwr2; - fwr2 = fy_walk_result_next(&fwr->refs, fwr2)) { - - /* refs, recursive */ - if (fwr2->type == fwrt_refs) { - recursive = true; - break; - } - } - - if (!recursive) - return fwr; - - fwrf = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(fwrf); - - fy_walk_result_flatten_internal(fwr, fwrf); - +err_out: fy_walk_result_free(fwr); - return fwrf; -#endif - + return NULL; } -int fy_walk_result_all_children_recursive_internal(struct fy_path_exec *fypx, struct fy_node *fyn, struct fy_walk_result *output) +int fy_walk_result_all_children_recursive_internal(struct fy_path_exec *fypx, struct fy_node *fyn, + struct fy_walk_result *output) { struct fy_node *fyni; struct fy_walk_result *fwr; @@ -3901,14 +4058,15 @@ int fy_walk_result_all_children_recursive_internal(struct fy_path_exec *fypx, st if (!fyn) return 0; - assert(output); - assert(output->type == fwrt_refs); + if (!output || output->type != fwrt_refs) + goto err_out; /* this node */ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn); if (!fwr) - return -1; + goto err_out; fy_walk_result_list_add_tail(&output->refs, fwr); + fwr = NULL; if (fy_node_is_scalar(fyn)) return 0; @@ -3917,20 +4075,26 @@ int fy_walk_result_all_children_recursive_internal(struct fy_path_exec *fypx, st while ((fyni = fy_node_collection_iterate(fyn, &prevp)) != NULL) { ret = fy_walk_result_all_children_recursive_internal(fypx, fyni, output); if (ret) - return ret; + goto err_out; } return 0; +err_out: + return -1; } bool fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type type, - struct fy_walk_result *fwrl, struct fy_walk_result *fwrr) + struct fy_walk_result *fwrl, struct fy_walk_result *fwrr, + bool *errorp) { struct fy_token *fyt; struct fy_walk_result *fwrt; const char *str; - bool match; + bool match, ret, error; + + if (errorp) + *errorp = false; /* both NULL */ if (!fwrl && !fwrr) { @@ -3957,14 +4121,15 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type /* both are non NULL */ /* none should be multiple */ - assert(fwrl->type != fwrt_refs && fwrr->type != fwrt_refs); + if (fwrl->type == fwrt_refs || fwrr->type == fwrt_refs) + goto err_out; /* both are the same type */ if (fwrl->type == fwrr->type) { switch (fwrl->type) { case fwrt_none: - FY_IMPOSSIBLE_ABORT(); + goto err_out; case fwrt_node_ref: switch (type) { @@ -3979,12 +4144,12 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type return true; return !fy_node_compare(fwrl->fyn, fwrr->fyn); default: - break; + goto err_out; } break; case fwrt_refs: - FY_IMPOSSIBLE_ABORT(); + goto err_out; case fwrt_doc: switch (type) { @@ -4001,7 +4166,7 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type match = !match; return match; default: - break; + goto err_out; } break; @@ -4020,7 +4185,7 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type case fpet_gte: return fwrl->number >= fwrr->number; default: - break; + goto err_out; } break; @@ -4039,7 +4204,7 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type case fpet_gte: return strcmp(fwrl->string, fwrr->string) >= 0; default: - break; + goto err_out; } break; } @@ -4062,11 +4227,14 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type type = fpet_lt; break; default: - break; + goto err_out; } /* swap left with right */ - return fy_walk_result_compare_simple(fypx, type, fwrr, fwrl); + ret = fy_walk_result_compare_simple(fypx, type, fwrr, fwrl, &error); + if (error) + goto err_out; + return ret; } switch (fwrl->type) { @@ -4079,10 +4247,12 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type } fyt = fy_node_get_scalar_token(fwrl->fyn); - assert(fyt); + if (!fyt) + goto err_out; str = fy_token_get_text0(fyt); - assert(str); + if (!str) + goto err_out; fwrt = NULL; /* node ref against */ @@ -4090,7 +4260,8 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type case fwrt_string: /* create a new temporary walk result */ fwrt = fy_path_exec_walk_result_create(fypx, fwrt_string, str); - assert(fwrt); + if (!fwrt) + goto err_out; break; @@ -4101,7 +4272,8 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type /* create a new temporary walk result */ fwrt = fy_path_exec_walk_result_create(fypx, fwrt_number, strtod(str, NULL)); - assert(fwrt); + if (!fwrt) + goto err_out; break; default: @@ -4111,10 +4283,13 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type if (!fwrt) return false; - match = fy_walk_result_compare_simple(fypx, type, fwrt, fwrr); + match = fy_walk_result_compare_simple(fypx, type, fwrt, fwrr, &error); + if (error) + goto err_out; /* free the temporary result */ fy_walk_result_free(fwrt); + fwrt = NULL; return match; @@ -4123,21 +4298,30 @@ fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type } return false; + +err_out: + if (errorp) + *errorp = true; + return false; } struct fy_walk_result * fy_walk_result_arithmetic_simple(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_path_expr *exprl, struct fy_walk_result *fwrl, - struct fy_path_expr *exprr, struct fy_walk_result *fwrr) + struct fy_path_expr *exprr, struct fy_walk_result *fwrr, + bool *errorp) { struct fy_diag *diag; struct fy_walk_result *output = NULL; char *str; size_t len, len1, len2; + if (errorp) + *errorp = false; + if (!fwrl || !fwrr) - goto out; + goto err_out; diag = fypx->cfg.diag; @@ -4158,7 +4342,8 @@ fy_walk_result_arithmetic_simple(struct fy_path_exec *fypx, len2 = strlen(fwrr->string); len = len1 + len2; str = malloc(len + 1); - assert(str); + if (!str) + goto err_out; memcpy(str, fwrl->string, len1); memcpy(str + len1, fwrr->string, len2); str[len] = '\0'; @@ -4204,41 +4389,67 @@ out: fy_walk_result_free(fwrl); fy_walk_result_free(fwrr); return output; + +err_out: + fy_walk_result_free(output); + output = NULL; + if (errorp) + *errorp = true; + goto out; } struct fy_walk_result * fy_walk_result_conditional_simple(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_path_expr *exprl, struct fy_walk_result *fwrl, - struct fy_path_expr *exprr, struct fy_walk_result *fwrr) + struct fy_path_expr *exprr, struct fy_walk_result *fwrr, + bool *errorp) { - bool match; + bool match, error; + + if (errorp) + *errorp = false; + + match = fy_walk_result_compare_simple(fypx, expr->type, fwrl, fwrr, &error); + if (error) + goto err_out; - match = fy_walk_result_compare_simple(fypx, expr->type, fwrl, fwrr); + /* path expr, return left hand side result if match */ + + fy_walk_result_free(fwrr); + fwrr = NULL; if (!match) { fy_walk_result_free(fwrl); - fy_walk_result_free(fwrr); - return NULL; + fwrl = NULL; } - /* path expr, return left hand side result */ - fy_walk_result_free(fwrr); return fwrl; + +err_out: + fy_walk_result_free(fwrl); + fy_walk_result_free(fwrr); + if (errorp) + *errorp = true; + return NULL; } struct fy_walk_result * fy_walk_result_lhs_rhs(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_path_expr *exprl, struct fy_walk_result *fwrl, - struct fy_path_expr *exprr, struct fy_walk_result *fwrr) + struct fy_path_expr *exprr, struct fy_walk_result *fwrr, + bool *errorp) { - struct fy_walk_result *output = NULL, *fwr, *fwrrt, *fwrlt, *fwrlc, *fwrrc; + struct fy_walk_result *output = NULL, *fwr = NULL, *fwrrt = NULL, *fwrlt = NULL, *fwrlc = NULL, *fwrrc = NULL; struct fy_walk_result *outputl = NULL, *outputr = NULL; + bool error; - assert(expr); - assert(exprl); - assert(exprr); + if (errorp) + *errorp = false; + + if (!expr || !exprl || !exprr) + goto err_out; /* only supports those */ if (!fy_path_expr_type_is_conditional(expr->type) && @@ -4259,7 +4470,8 @@ fy_walk_result_lhs_rhs(struct fy_path_exec *fypx, } output = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(output); + if (!output) + goto err_out; for (fwrlt = fy_walk_result_iter_start(fwrl); fwrlt; fwrlt = fy_walk_result_iter_next(fwrl, fwrlt)) { @@ -4268,15 +4480,24 @@ fy_walk_result_lhs_rhs(struct fy_path_exec *fypx, if (fwrlt->type == fwrt_refs) { fwrlc = fy_walk_result_clone(fwrlt); - assert(fwrlc); + if (!fwrlc) + goto err_out; fwrrc = fy_walk_result_clone(fwrr); - assert(fwrrc); + if (!fwrrc) + goto err_out; + + outputl = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc, &error); + fwrlc = NULL; + fwrrc = NULL; + if (error) + goto err_out; - outputl = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc); if (outputl) fy_walk_result_list_add_tail(&output->refs, outputl); - else + else { fy_walk_result_free(outputl); + outputl = NULL; + } continue; } @@ -4288,50 +4509,72 @@ fy_walk_result_lhs_rhs(struct fy_path_exec *fypx, if (fwrrt->type == fwrt_refs) { fwrlc = fy_walk_result_clone(fwrlt); - assert(fwrlc); + if (!fwrlc) + goto err_out; fwrrc = fy_walk_result_clone(fwrrt); - assert(fwrrc); + if (!fwrrc) + goto err_out; + + outputr = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc, &error); + fwrlc = NULL; + fwrrc = NULL; + if (error) + goto err_out; - outputr = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc); if (outputr) fy_walk_result_list_add_tail(&output->refs, outputr); - else + else { fy_walk_result_free(outputr); + outputr = NULL; + } continue; } fwrlc = fy_walk_result_clone(fwrlt); - assert(fwrlc); + if (!fwrlc) + goto err_out; fwrrc = fy_walk_result_clone(fwrrt); - assert(fwrrc); + if (!fwrrc) + goto err_out; fwr = NULL; - - if (fy_path_expr_type_is_conditional(expr->type)) - fwr = fy_walk_result_conditional_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc); - else if (fy_path_expr_type_is_arithmetic(expr->type)) - fwr = fy_walk_result_arithmetic_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc); - else + if (fy_path_expr_type_is_conditional(expr->type)) { + fwr = fy_walk_result_conditional_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc, &error); + fwrlc = NULL; + fwrrc = NULL; + if (error) + goto err_out; + } else if (fy_path_expr_type_is_arithmetic(expr->type)) { + fwr = fy_walk_result_arithmetic_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc, &error); + fwrlc = NULL; + fwrrc = NULL; + if (error) + goto err_out; + } else FY_IMPOSSIBLE_ABORT(); - fwrlc = NULL; - fwrrc = NULL; - if (fwr) fy_walk_result_list_add_tail(&output->refs, fwr); } } out: + fy_walk_result_free(fwrlc); + fy_walk_result_free(fwrrc); fy_walk_result_free(fwrl); fy_walk_result_free(fwrr); return fy_walk_result_simplify(output); +err_out: + fy_walk_result_free(output); + output = NULL; + goto out; } struct fy_path_expr * -fy_scalar_walk_result_to_expr(struct fy_path_exec *fypx, struct fy_walk_result *fwr, enum fy_path_expr_type ptype) +fy_scalar_walk_result_to_expr(struct fy_path_exec *fypx, struct fy_walk_result *fwr, + enum fy_path_expr_type ptype, bool *errorp) { struct fy_input *fyit = NULL; struct fy_path_expr *exprt = NULL; @@ -4340,53 +4583,63 @@ fy_scalar_walk_result_to_expr(struct fy_path_exec *fypx, struct fy_walk_result * char *buf; int rc __FY_DEBUG_UNUSED__; - exprt = NULL; + if (errorp) + *errorp = false; if (!fwr) - return NULL; + goto err_out; collection_addressing = ptype == fpet_chain || ptype == fpet_multi; switch (fwr->type) { case fwrt_string: fyit = fy_input_from_malloc_data(fwr->string, FY_NT, &handle, true); - assert(fyit); + if (!fyit) + goto err_out; fwr->string = NULL; fy_walk_result_free(fwr); fwr = NULL; exprt = fy_path_expr_alloc(); - assert(exprt); + if (!exprt) + goto err_out; if (collection_addressing) { exprt->type = fpet_map_key; exprt->fyt = fy_token_create(FYTT_PE_MAP_KEY, &handle, NULL); - assert(exprt->fyt); + if (!exprt->fyt) + goto err_out; } else { exprt->type = fpet_scalar; exprt->fyt = fy_token_create(FYTT_SCALAR, &handle, FYSS_PLAIN, NULL); - assert(exprt->fyt); + if (!exprt->fyt) + goto err_out; } break; case fwrt_number: rc = asprintf(&buf, "%d", (int)fwr->number); - assert(rc != -1); + if (rc == -1) + goto err_out; fyit = fy_input_from_malloc_data(buf, FY_NT, &handle, true); - assert(fyit); + if (!fyit) + goto err_out; exprt = fy_path_expr_alloc(); - assert(exprt); + if (!exprt) + goto err_out; if (collection_addressing) { exprt->type = fpet_seq_index; exprt->fyt = fy_token_create(FYTT_PE_SEQ_INDEX, &handle, (int)fwr->number); - assert(exprt->fyt); + if (!exprt->fyt) + goto err_out; } else { exprt->type = fpet_scalar; exprt->fyt = fy_token_create(FYTT_SCALAR, &handle, FYSS_PLAIN, NULL); - assert(exprt->fyt); + if (!exprt->fyt) + goto err_out; } break; @@ -4395,10 +4648,17 @@ fy_scalar_walk_result_to_expr(struct fy_path_exec *fypx, struct fy_walk_result * break; } +out: fy_walk_result_free(fwr); fy_input_unref(fyit); return exprt; +err_out: + fy_path_expr_free(exprt); + exprt = NULL; + if (errorp) + *errorp = true; + goto out; } enum fy_walk_result_set_op { @@ -4449,7 +4709,7 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * struct fy_walk_result *fwr, *fwrrm, *fwrin; struct fy_walk_result *output = NULL; char *relpath = NULL; - struct fy_node *fyn2, *fynt; + struct fy_node *fyn2 = NULL, *fynt; int rc; /* no input? return it */ @@ -4465,7 +4725,8 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * return NULL; } - assert(input->type == fwrt_node_ref || input->type == fwrt_refs); + if (input->type != fwrt_node_ref && input->type != fwrt_refs) + goto err_out; #ifdef DEBUG_EXPR fy_walk_result_dump(input, fypx->cfg.diag, FYET_WARNING, 0, "set-op: input\n"); @@ -4509,8 +4770,11 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * /* get the node at the relative path of the copy */ fyn2 = fy_node_by_path(fwr->fyn, relpath, FY_NT, FYNWF_DONT_FOLLOW); - if (!fyn2) + if (!fyn2) { + fy_walk_result_free(fwr); + fwr = NULL; goto err_out; + } free(relpath); relpath = NULL; @@ -4520,6 +4784,8 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * rc = fy_node_delete(fyn2); if (rc) goto err_out; + fyn2 = NULL; + fwr->fyn = NULL; } else { fynt = fyn2; @@ -4533,9 +4799,10 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * } } - if (output && fwr) { + if (output && fwr) fy_walk_result_list_add_tail(&output->refs, fwr); - } + else if (fwr) + fy_walk_result_free(fwr); fwr = NULL; } @@ -4565,25 +4832,27 @@ fy_walk_result_perform_set_op(struct fy_path_exec *fypx, struct fy_walk_result * } +out: /* get rid of input */ fy_walk_result_free(input); return output; err_out: - fy_walk_result_free(input); fy_walk_result_free(output); + output = NULL; if (relpath) free(relpath); - return NULL; + goto out; } struct fy_walk_result * fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, - struct fy_walk_result *input, enum fy_path_expr_type ptype) + struct fy_walk_result *input, enum fy_path_expr_type ptype, + bool *errorp) { struct fy_diag *diag; - struct fy_walk_result *fwr, *fwrn, *fwrt, *fwrtn; - struct fy_walk_result *output = NULL, *input1, *output1, *input2, *output2; + struct fy_walk_result *fwr = NULL, *fwrn = NULL, *fwrt = NULL, *fwrtn = NULL; + struct fy_walk_result *output = NULL, *input1 = NULL, *output1 = NULL, *input2 = NULL, *output2 = NULL; struct fy_path_expr *exprn, *exprl, *exprr; struct fy_node *fyn, *fynn, *fyni; struct fy_token *fyt; @@ -4593,6 +4862,7 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * unsigned int nargs; struct fy_walk_result **fwr_args; void *prevp; + bool error; int rc __FY_DEBUG_UNUSED__; /* error */ @@ -4610,11 +4880,15 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (input && input->type == fwrt_refs && !fy_path_expr_type_handles_refs(expr->type)) { output = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(output); + if (!output) + goto err_out; while ((fwr = fy_walk_result_list_pop(&input->refs)) != NULL) { - fwrn = fy_path_expr_execute(fypx, level + 1, expr, fwr, ptype); + fwrn = fy_path_expr_execute(fypx, level + 1, expr, fwr, ptype, &error); + fwr = NULL; + if (error) + goto err_out; if (fwrn) fy_walk_result_list_add_tail(&output->refs, fwrn); } @@ -4629,7 +4903,9 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (input && input->type == fwrt_node_ref) { - fynn = fy_path_expr_execute_single_result(diag, expr, input->fyn); + fynn = fy_path_expr_execute_single_result(diag, expr, input->fyn, &error); + if (error) + goto err_out; if (!fynn) goto out; @@ -4657,7 +4933,9 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * for (exprn = fy_path_expr_list_head(&expr->children); exprn; exprn = fy_path_expr_next(&expr->children, exprn)) { - output = fy_path_expr_execute(fypx, level + 1, exprn, output, expr->type); + output = fy_path_expr_execute(fypx, level + 1, exprn, output, expr->type, &error); + if (error) + goto err_out; if (!output) break; } @@ -4671,20 +4949,26 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * /* allocate a refs output result */ output = fy_path_exec_walk_result_create(fypx, fwrt_refs); - assert(output); + if (!output) + goto err_out; /* iterate over each multi item */ for (exprn = fy_path_expr_list_head(&expr->children); exprn; exprn = fy_path_expr_next(&expr->children, exprn)) { input2 = fy_walk_result_clone(input); - assert(input2); + if (!input2) + goto err_out; - output2 = fy_path_expr_execute(fypx, level + 1, exprn, input2, expr->type); + output2 = fy_path_expr_execute(fypx, level + 1, exprn, input2, expr->type, &error); + input2 = NULL; + if (error) + goto err_out; if (!output2) continue; fy_walk_result_list_add_tail(&output->refs, output2); + output2 = NULL; } fy_walk_result_free(input); input = NULL; @@ -4720,8 +5004,8 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * prevp = NULL; while ((fyni = fy_node_collection_iterate(fyn, &prevp)) != NULL) { fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyni); - assert(fwr); - + if (!fwr) + goto err_out; fy_walk_result_list_add_tail(&output->refs, fwr); } @@ -4747,8 +5031,8 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * fy_walk_result_list_init(&output->refs); rc = fy_walk_result_all_children_recursive_internal(fypx, fyn, output); - assert(!rc); - + if (rc) + goto err_out; break; case fpet_seq_slice: @@ -4757,14 +5041,13 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * goto out; /* only valid for node ref on a sequence */ - if (input->type != fwrt_node_ref || !fy_node_is_sequence(input->fyn)) { + if (input->type != fwrt_node_ref || !fy_node_is_sequence(input->fyn)) break; - } fyn = input->fyn; fyt = expr->fyt; - assert(fyt); - assert(fyt->type == FYTT_PE_SEQ_SLICE); + if (!fyt || fyt->type != FYTT_PE_SEQ_SLICE) + goto err_out; start = fyt->seq_slice.start_index; end = fyt->seq_slice.end_index; @@ -4792,7 +5075,8 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * continue; fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fynn); - assert(fwr); + if (!fwr) + goto err_out; fy_walk_result_list_add_tail(&output->refs, fwr); } @@ -4811,14 +5095,16 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * case fpet_div: exprl = fy_path_expr_lhs(expr); - assert(exprl); - + if (!exprl) + goto err_out; exprr = fy_path_expr_rhs(expr); - assert(exprr); + if (!exprr) + goto err_out; if (input) { input1 = fy_walk_result_clone(input); - assert(input1); + if (!input1) + goto err_out; input2 = input; input = NULL; @@ -4829,11 +5115,21 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * } /* execute LHS and RHS */ - output1 = fy_path_expr_execute(fypx, level + 1, exprl, input1, expr->type); - output2 = fy_path_expr_execute(fypx, level + 1, exprr, input2, expr->type); + output1 = fy_path_expr_execute(fypx, level + 1, exprl, input1, expr->type, &error); + input1 = NULL; + if (error) + goto err_out; - output = fy_walk_result_lhs_rhs(fypx, expr, exprl, output1, exprr, output2); + output2 = fy_path_expr_execute(fypx, level + 1, exprr, input2, expr->type, &error); + input2 = NULL; + if (error) + goto err_out; + output = fy_walk_result_lhs_rhs(fypx, expr, exprl, output1, exprr, output2, &error); + output1 = NULL; + output2 = NULL; + if (error) + goto err_out; break; case fpet_scalar: @@ -4841,10 +5137,12 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * /* duck typing! */ if (fy_token_is_number(expr->fyt)) { output = fy_path_exec_walk_result_create(fypx, fwrt_number, token_number(expr->fyt)); - assert(output); + if (!output) + goto err_out; } else { output = fy_path_exec_walk_result_create(fypx, fwrt_string, fy_token_get_text0(expr->fyt)); - assert(output); + if (!output) + goto err_out; } fy_walk_result_free(input); @@ -4860,12 +5158,16 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (input) { input1 = fy_walk_result_clone(input); - assert(input1); - } else { + if (!input1) + goto err_out; + } else input1 = NULL; - } - output = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type); + output = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type, &error); + input1 = NULL; + if (error) + goto err_out; + if (output) break; } @@ -4880,15 +5182,20 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (input) { input1 = fy_walk_result_clone(input); - assert(input1); - } else { + if (!input1) + goto err_out; + } else input1 = NULL; - } - output1 = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type); + output1 = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type, &error); + input1 = NULL; + if (error) + goto err_out; + if (output1) { fy_walk_result_free(output); output = output1; + output1 = NULL; } else break; } @@ -4930,9 +5237,12 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (fwrt->type == fwrt_refs) continue; - assert(fwrt != fwr); + if (fwrt == fwr) + goto err_out; - match = fy_walk_result_compare_simple(fypx, fpet_eq, fwr, fwrt); + match = fy_walk_result_compare_simple(fypx, fpet_eq, fwr, fwrt, &error); + if (error) + goto err_out; if (match) { fy_walk_result_list_del(&input->refs, fwrt); @@ -4948,31 +5258,33 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * case fpet_scalar_expr: exprl = fy_path_expr_list_head(&expr->children); - if (!exprl) { - fy_warning(diag, "%s:%d\n", __FILE__, __LINE__); + if (!exprl) goto out; - } - output = fy_path_expr_execute(fypx, level + 1, exprl, NULL, ptype); - if (!output) { - fy_warning(diag, "%s:%d\n", __FILE__, __LINE__); + output = fy_path_expr_execute(fypx, level + 1, exprl, NULL, ptype, &error); + if (error) + goto err_out; + + if (!output) goto out; - } - exprt = fy_scalar_walk_result_to_expr(fypx, output, ptype); + exprt = fy_scalar_walk_result_to_expr(fypx, output, ptype, &error); + if (error) + goto err_out; + + fy_walk_result_free(output); output = NULL; - if (!exprt) { - fy_warning(diag, "%s:%d\n", __FILE__, __LINE__); + + if (!exprt) break; - } - output = fy_path_expr_execute(fypx, level + 1, exprt, input, ptype); - if (!output) { - fy_warning(diag, "%s:%d\n", __FILE__, __LINE__); - } + output = fy_path_expr_execute(fypx, level + 1, exprt, input, ptype, &error); input = NULL; + if (error) + goto err_out; fy_path_expr_free(exprt); + exprt = NULL; break; case fpet_path_expr: @@ -4980,13 +5292,17 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (!exprl) goto out; - output = fy_path_expr_execute(fypx, level + 1, exprl, input, ptype); + output = fy_path_expr_execute(fypx, level + 1, exprl, input, ptype, &error); + input = NULL; + if (error) + goto err_out; input = NULL; break; case fpet_method: - assert(expr->fym); + if (!expr->fym) + goto err_out; /* execute the arguments */ nargs = expr->fym->nargs; @@ -4998,16 +5314,23 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * if (input) { input1 = fy_walk_result_clone(input); - assert(input1); + if (!input1) + goto err_out; } else input1 = NULL; - fwr_args[i] = fy_path_expr_execute(fypx, level + 1, exprt, input1, expr->type); + fwr_args[i] = fy_path_expr_execute(fypx, level + 1, exprt, input1, + expr->type, &error); + input1 = NULL; + if (error) + goto err_out; } } else fwr_args = NULL; - output = expr->fym->exec(expr->fym, fypx, level + 1, expr, input, fwr_args, nargs); + output = expr->fym->exec(expr->fym, fypx, level + 1, expr, input, fwr_args, nargs, &error); + if (error) + goto err_out; input = NULL; break; @@ -5016,26 +5339,26 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * case fpet_unselect: /* pop the top in either case */ - assert(input); if (!input) - goto out; + goto err_out; /* only handle inputs of node and refs */ if (input->type != fwrt_node_ref && input->type != fwrt_refs) - goto out; + goto err_out; /* execute the expression */ exprn = fy_path_expr_list_head(&expr->children); - assert(exprn); if (!exprn) - goto out; + goto err_out; fwrt = fy_walk_result_clone(input); - assert(fwrt); if (!fwrt) - goto out; + goto err_out; - fwrn = fy_path_expr_execute(fypx, level + 1, exprn, fwrt, expr->type); + fwrn = fy_path_expr_execute(fypx, level + 1, exprn, fwrt, expr->type, &error); + fwrt = NULL; + if (error) + goto err_out; output = fy_walk_result_perform_set_op(fypx, input, fwrn, expr->type == fpet_unselect ? FYWRSO_UNSELECT : FYWRSO_SELECT); input = NULL; @@ -5050,6 +5373,10 @@ fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr * } out: + fy_walk_result_free(output1); + fy_walk_result_free(output2); + fy_walk_result_free(input1); + fy_walk_result_free(input2); fy_walk_result_free(input); output = fy_walk_result_simplify(output); @@ -5058,12 +5385,20 @@ out: fy_walk_result_dump(output, diag, FYET_NOTICE, level, "output %s\n", fy_path_expr_type_txt[expr->type]); #endif return output; + +err_out: + if (errorp) + *errorp = true; + fy_walk_result_free(output); + output = NULL; + goto out; } static int fy_path_exec_execute_internal(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_node *fyn_start) { struct fy_walk_result *fwr; + bool error; if (!fypx || !expr || !fyn_start) return -1; @@ -5072,9 +5407,13 @@ static int fy_path_exec_execute_internal(struct fy_path_exec *fypx, fypx->result = NULL; fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn_start); - assert(fwr); + if (!fwr) + goto err_out; + + fwr = fy_path_expr_execute(fypx, 0, expr, fwr, fpet_none, &error); + if (error) + goto err_out; - fwr = fy_path_expr_execute(fypx, 0, expr, fwr, fpet_none); if (!fwr) return 0; @@ -5087,6 +5426,9 @@ static int fy_path_exec_execute_internal(struct fy_path_exec *fypx, fypx->result = fwr; return 0; +err_out: + fy_walk_result_free(fwr); + return -1; } int fy_path_exec_execute(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_node *fyn_start) @@ -5159,6 +5501,7 @@ fy_path_exec_walk_result_vcreate(struct fy_path_exec *fypx, enum fy_walk_result_ return fy_walk_result_vcreate_rl(fwrl, type, ap); } +#ifndef DEBUG_EXPR struct fy_walk_result * fy_path_exec_walk_result_create(struct fy_path_exec *fypx, enum fy_walk_result_type type, ...) { @@ -5182,6 +5525,36 @@ fy_path_exec_walk_result_create(struct fy_path_exec *fypx, enum fy_walk_result_t return fwr; } +#else +struct fy_walk_result * +_fy_path_exec_walk_result_create(struct fy_path_exec *fypx, + const char *file, int line, const char *func, + enum fy_walk_result_type type, ...) +{ + struct fy_walk_result_list *fwrl; + struct fy_walk_result *fwr; + va_list ap; + + if (!fypx) + return NULL; + + fwrl = fy_path_exec_walk_result_rl(fypx); + + va_start(ap, type); + fwr = fy_walk_result_vcreate_rl(fwrl, type, ap); + va_end(ap); + + if (!fwr) + return NULL; + + fwr->fypx = fy_path_exec_ref(fypx); + + fprintf(stderr, "%s:%4d @%-48s %-32s fwr=%p (fypx=%p refs=%u)\n", + file, line, func, __func__, fwr, fwr->fypx, fwr->fypx ? fwr->fypx->refs : 0); + + return fwr; +} +#endif void fy_path_exec_walk_result_free(struct fy_path_exec *fypx, struct fy_walk_result *fwr) @@ -5269,7 +5642,8 @@ int fy_node_setup_path_expr_data(struct fy_node *fyn) return rc; } pxdd = fyn->fyd->pxdd; - assert(pxdd); + if (!pxdd) + goto err_out; pxnd = malloc(sizeof(*pxnd)); if (!pxnd) @@ -5323,6 +5697,7 @@ err_no_input: err_no_text: free(pxnd); err_no_mem: +err_out: return -1; } @@ -5380,10 +5755,12 @@ fy_node_alias_resolve_by_ypath_result(struct fy_node *fyn) "fy_node_setup_path_expr_data() failed"); pxnd = fyn->pxnd; - assert(pxnd); + if (!pxnd) + goto err_out; pxdd = fyd->pxdd; - assert(pxdd); + if (!pxdd) + goto err_out; if (pxnd->traversals++ > 0) { FYD_NODE_ERROR(fyd, fyn, FYEM_DOC, @@ -5399,32 +5776,10 @@ fy_node_alias_resolve_by_ypath_result(struct fy_node *fyn) fy_path_exec_set_result_recycle_list(fypx, &pxdd->fwr_recycle); -#if 0 - { - struct fy_document *fyd_pe; - const char *text; - size_t len; - - text = fy_token_get_text(fyn->scalar, &len); - if (text) { - fyd_pe = fy_path_expr_to_document(pxnd->expr); - if (fyd_pe) { - fprintf(stderr, "%s: %.*s\n", __func__, (int)len, text); - fy_document_default_emit_to_fp(fyd_pe, stderr); - fy_document_destroy(fyd_pe); - } - } - } -#endif - - // fprintf(stderr, "%s: %s 2\n", __func__, fy_node_get_path_alloca(fyn)); - /* execute, starting at this */ rc = fy_path_exec_execute(fypx, pxnd->expr, fyn); - fyd_error_check(fyd, !rc, err_out, - "fy_path_exec_execute() failed"); - - // fprintf(stderr, "%s: %s 3\n", __func__, fy_node_get_path_alloca(fyn)); + if (rc) + goto err_out; fwr = fy_path_exec_take_results(fypx); @@ -5435,8 +5790,6 @@ fy_node_alias_resolve_by_ypath_result(struct fy_node *fyn) if (!fwr) return NULL; - // fprintf(stderr, "%s: %s 4\n", __func__, fy_node_get_path_alloca(fyn)); - return fwr; err_out: @@ -5446,30 +5799,46 @@ err_out: return NULL; } -struct fy_node *fy_node_alias_resolve_by_ypath(struct fy_node *fyn) +static struct fy_node * +fy_node_alias_resolve_by_ypath_internal(struct fy_node *fyn, bool *errorp) { struct fy_anchor *fya; struct fy_walk_result *fwr; void *iterp; if (!fyn || !fy_node_is_alias(fyn)) - return NULL; + goto err_out; /* simple and common enough to do it now */ fya = fy_document_lookup_anchor_by_token(fyn->fyd, fyn->scalar); - if (fya) + if (fya) { + if (errorp) + *errorp = false; return fya->fyn; + } fwr = fy_node_alias_resolve_by_ypath_result(fyn); if (!fwr) - return NULL; + goto err_out; iterp = NULL; fyn = fy_walk_result_node_iterate(fwr, &iterp); fy_walk_result_free(fwr); + if (errorp) + *errorp = false; + return fyn; +err_out: + if (errorp) + *errorp = true; + return NULL; +} + +struct fy_node *fy_node_alias_resolve_by_ypath(struct fy_node *fyn) +{ + return fy_node_alias_resolve_by_ypath_internal(fyn, NULL); } struct fy_walk_result * @@ -5509,7 +5878,8 @@ fy_node_by_ypath_result(struct fy_node *fyn, const char *path, size_t len) "fy_node_setup_path_expr_data() failed"); pxdd = fyd->pxdd; - assert(pxdd); + if (!pxdd) + goto err_out; fyi = fy_input_from_data(path, len, NULL, false); fyd_error_check(fyd, fyi, err_no_input, diff --git a/src/lib/fy-walk.h b/src/lib/fy-walk.h index 3697e9c..3eefef9 100644 --- a/src/lib/fy-walk.h +++ b/src/lib/fy-walk.h @@ -268,7 +268,8 @@ struct fy_method { struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, struct fy_walk_result *input, - struct fy_walk_result **args, int nargs); + struct fy_walk_result **args, int nargs, + bool *errorp); }; FY_TYPE_FWD_DECL_LIST(path_expr); @@ -397,7 +398,8 @@ fy_path_exec_unref(struct fy_path_exec *fypx) struct fy_walk_result * fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr *expr, - struct fy_walk_result *input, enum fy_path_expr_type ptype); + struct fy_walk_result *input, enum fy_path_expr_type ptype, + bool *errorp); static inline struct fy_walk_result_list * fy_path_exec_walk_result_rl(struct fy_path_exec *fypx) diff --git a/src/thread/fy-thread.c b/src/thread/fy-thread.c index 08b7771..03c3153 100644 --- a/src/thread/fy-thread.c +++ b/src/thread/fy-thread.c @@ -18,17 +18,21 @@ #include #endif -#include #include -#include #include #include +#ifndef _WIN32 +#include +#include +#endif + #if defined(__linux__) #include #include #endif +#include "fy-win32.h" #include "fy-diag.h" #include "fy-utils.h" #include "fy-thread.h" @@ -36,6 +40,13 @@ #undef WORK_SHUTDOWN #define WORK_SHUTDOWN ((struct fy_thread_work *)(void *)-1) +/* Platform-specific thread-local storage access */ +#ifdef _WIN32 +#define fy_thread_getspecific(key) TlsGetValue(key) +#else +#define fy_thread_getspecific(key) pthread_getspecific(key) +#endif + #ifdef FY_THREAD_DEBUG #define TDBG(_fmt, ...) \ do { fprintf(stderr, (_fmt) __VA_OPT__(,) __VA_ARGS__); } while(0) @@ -44,14 +55,106 @@ do { /* nothing */ } while(0) #endif +#ifdef _WIN32 +static DWORD WINAPI fy_worker_thread_standard(void *arg); +static DWORD WINAPI fy_worker_thread_steal(void *arg); +#else static void *fy_worker_thread_standard(void *arg); static void *fy_worker_thread_steal(void *arg); +#endif static void fy_thread_work_join_standard(struct fy_thread_pool *tp, struct fy_thread_work *works, size_t work_count, fy_work_check_fn check_fn); static void fy_thread_work_join_steal(struct fy_thread_pool *tp, struct fy_thread_work *works, size_t work_count, fy_work_check_fn check_fn); static void fy_thread_work_join_steal_2(struct fy_thread_pool *tp, struct fy_thread_work works[2], fy_work_check_fn check_fn); -#if defined(__linux__) && !defined(FY_THREAD_PORTABLE) +#if defined(_WIN32) + +/* Windows implementation using Events and Critical Sections */ + +static inline void fy_thread_init_sync(struct fy_thread *t) +{ + t->submit_event = CreateEvent(NULL, FALSE, FALSE, NULL); + t->done_event = CreateEvent(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&t->lock); + InitializeCriticalSection(&t->wait_lock); +} + +static inline struct fy_thread_work *fy_worker_wait_for_work(struct fy_thread *t) +{ + struct fy_thread_work *work; + + while ((work = fy_atomic_load(&t->work)) == NULL) + WaitForSingleObject(t->submit_event, INFINITE); + + return work; +} + +static inline void fy_worker_signal_work_done(struct fy_thread *t, struct fy_thread_work *work) +{ + struct fy_thread_work *exp_work; + + EnterCriticalSection(&t->wait_lock); + + exp_work = work; + if (!fy_atomic_compare_exchange_strong(&t->work, &exp_work, NULL)) + assert(exp_work == WORK_SHUTDOWN); + SetEvent(t->done_event); + LeaveCriticalSection(&t->wait_lock); +} + +static inline int fy_thread_submit_work_internal(struct fy_thread *t, struct fy_thread_work *work) +{ + struct fy_thread_work *exp_work; + int ret; + + assert(t); + assert(work); + + EnterCriticalSection(&t->lock); + exp_work = NULL; + if (!fy_atomic_compare_exchange_strong(&t->work, &exp_work, work)) { + assert(exp_work == WORK_SHUTDOWN); + ret = -1; + } else { + SetEvent(t->submit_event); + ret = 0; + } + LeaveCriticalSection(&t->lock); + + return ret; +} + +static inline int fy_thread_wait_work_internal(struct fy_thread *t) +{ + const struct fy_thread_work *work; + + EnterCriticalSection(&t->wait_lock); + while ((work = fy_atomic_load(&t->work)) != NULL) { + LeaveCriticalSection(&t->wait_lock); + WaitForSingleObject(t->done_event, INFINITE); + EnterCriticalSection(&t->wait_lock); + } + LeaveCriticalSection(&t->wait_lock); + + return 0; +} + +void fy_worker_thread_shutdown(struct fy_thread *t) +{ + EnterCriticalSection(&t->lock); + fy_atomic_store(&t->work, WORK_SHUTDOWN); + SetEvent(t->submit_event); + LeaveCriticalSection(&t->lock); + + WaitForSingleObject(t->tid, INFINITE); + CloseHandle(t->tid); + CloseHandle(t->submit_event); + CloseHandle(t->done_event); + DeleteCriticalSection(&t->lock); + DeleteCriticalSection(&t->wait_lock); +} + +#elif defined(__linux__) && !defined(FY_THREAD_PORTABLE) /* linux pedal to the metal implementation */ static inline int futex(FY_ATOMIC(uint32_t) *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) @@ -419,6 +522,11 @@ void fy_thread_pool_cleanup(struct fy_thread_pool *tp) fy_cacheline_free(tp->threads); } +#ifdef _WIN32 + if (tp->key != TLS_OUT_OF_INDEXES) + TlsFree(tp->key); +#endif + memset(tp, 0, sizeof(*tp)); } @@ -427,9 +535,13 @@ int fy_thread_pool_setup(struct fy_thread_pool *tp, const struct fy_thread_pool_ struct fy_thread *t; unsigned int i, num_threads, num_threads_words; size_t size, free_offset, loot_offset, thread_bitmask_size; +#ifdef _WIN32 + LPTHREAD_START_ROUTINE start_routine; +#else void *(*start_routine)(void *); - long scval; int rc __FY_DEBUG_UNUSED__; +#endif + long scval; assert(tp); @@ -471,11 +583,16 @@ int fy_thread_pool_setup(struct fy_thread_pool *tp, const struct fy_thread_pool_ memset(tp->threads, 0, size); +#ifdef _WIN32 + tp->key = TlsAlloc(); + assert(tp->key != TLS_OUT_OF_INDEXES); +#else rc = pthread_key_create(&tp->key, NULL); assert(!rc); +#endif - tp->freep = (void *)tp->threads + free_offset; - tp->lootp = (void *)tp->threads + loot_offset; + tp->freep = (_Atomic(uint64_t) *)((char *)tp->threads + free_offset); + tp->lootp = (_Atomic(uint64_t) *)((char *)tp->threads + loot_offset); /* prime the thread free */ for (i = 0; i < num_threads_words - 1; i++) @@ -497,9 +614,15 @@ int fy_thread_pool_setup(struct fy_thread_pool *tp, const struct fy_thread_pool_ fy_worker_thread_steal; for (i = 0, t = tp->threads; i < tp->num_threads; i++, t++) { +#ifdef _WIN32 + t->tid = CreateThread(NULL, 0, start_routine, t, 0, NULL); + if (t->tid == NULL) + goto err_out; +#else rc = pthread_create(&t->tid, NULL, start_routine, t); if (rc) goto err_out; +#endif } return 0; @@ -592,7 +715,7 @@ void fy_thread_arg_array_join(struct fy_thread_pool *tp, fy_work_exec_fn fn, fy_ for (i = 0; i < count; i++) { works[i].fn = fn; works[i].arg = args; - args += argsize; + args = (char *)args + argsize; } fy_thread_work_join(tp, works, count, check_fn); @@ -619,7 +742,11 @@ void fy_thread_arg_join(struct fy_thread_pool *tp, fy_work_exec_fn fn, fy_work_c /* * the standard (non-stealing implementation) */ +#ifdef _WIN32 +static DWORD WINAPI fy_worker_thread_standard(void *arg) +#else static void *fy_worker_thread_standard(void *arg) +#endif { struct fy_thread *t = arg; struct fy_thread_pool *tp; @@ -628,14 +755,22 @@ static void *fy_worker_thread_standard(void *arg) tp = t->tp; /* store per thread info */ +#ifdef _WIN32 + TlsSetValue(tp->key, t); +#else pthread_setspecific(tp->key, t); +#endif while ((work = fy_worker_wait_for_work(t)) != WORK_SHUTDOWN) { work->fn(work->arg); fy_worker_signal_work_done(t, work); } +#ifdef _WIN32 + return 0; +#else return NULL; +#endif } static void fy_thread_work_join_standard(struct fy_thread_pool *tp, struct fy_thread_work *works, size_t work_count, fy_work_check_fn check_fn) @@ -719,7 +854,7 @@ static void fy_thread_work_join_standard(struct fy_thread_pool *tp, struct fy_th */ static inline struct fy_work_pool *fy_work_pool_init(struct fy_work_pool *wp, size_t work_count) { -#if !(defined(__linux__) && !defined(FY_THREAD_PORTABLE)) +#if !(defined(__linux__) && !defined(FY_THREAD_PORTABLE)) && !defined(_WIN32) int rc __FY_DEBUG_UNUSED__; #endif @@ -733,6 +868,9 @@ static inline struct fy_work_pool *fy_work_pool_init(struct fy_work_pool *wp, si wp->sem = dispatch_semaphore_create(!work_count); assert(wp->sem != NULL); (void)rc; +#elif defined(_WIN32) + wp->sem = CreateSemaphore(NULL, !work_count, LONG_MAX, NULL); + assert(wp->sem != NULL); #else rc = sem_init(&wp->sem, 0, !work_count); assert(!rc); @@ -742,14 +880,16 @@ static inline struct fy_work_pool *fy_work_pool_init(struct fy_work_pool *wp, si static inline void fy_work_pool_cleanup(struct fy_work_pool *wp) { -#if !(defined(__linux__) && !defined(FY_THREAD_PORTABLE)) +#if !(defined(__linux__) && !defined(FY_THREAD_PORTABLE)) && !defined(_WIN32) int rc __FY_DEBUG_UNUSED__; #endif if (!wp) return; -#if defined(__linux__) && !defined(FY_THREAD_PORTABLE) +#if defined(_WIN32) + CloseHandle(wp->sem); +#elif defined(__linux__) && !defined(FY_THREAD_PORTABLE) /* nothing */ #elif defined(__APPLE__) /* nothing */ @@ -763,14 +903,18 @@ static inline void fy_work_pool_cleanup(struct fy_work_pool *wp) static inline bool fy_work_pool_signal(struct fy_work_pool *wp) { size_t prev_work_left; +#if !defined(_WIN32) int rc __FY_DEBUG_UNUSED__; +#endif if (!wp) return false; prev_work_left = fy_atomic_fetch_sub(&wp->work_left, 1); if (prev_work_left == 1) { -#if defined(__linux__) && !defined(FY_THREAD_PORTABLE) +#if defined(_WIN32) + ReleaseSemaphore(wp->sem, 1, NULL); +#elif defined(__linux__) && !defined(FY_THREAD_PORTABLE) rc = fpost(&wp->done); assert(!rc); #elif defined(__APPLE__) @@ -787,7 +931,11 @@ static inline bool fy_work_pool_signal(struct fy_work_pool *wp) static inline void fy_work_pool_wait(struct fy_work_pool *wp) { +#if !defined(_WIN32) int rc __FY_DEBUG_UNUSED__; +#else + DWORD rc __FY_DEBUG_UNUSED__; +#endif if (!wp) return; @@ -796,13 +944,17 @@ static inline void fy_work_pool_wait(struct fy_work_pool *wp) while (fy_atomic_load(&wp->work_left) > 0) { #if defined(__linux__) && !defined(FY_THREAD_PORTABLE) rc = fwait(&wp->done); + assert(!rc || (rc == -1 && errno == EAGAIN)); #elif defined(__APPLE__) dispatch_semaphore_wait(wp->sem, DISPATCH_TIME_FOREVER); rc = 0; +#elif defined(_WIN32) + rc = WaitForSingleObject(wp->sem, INFINITE); + assert(rc == WAIT_OBJECT_0); #else rc = sem_wait(&wp->sem); -#endif assert(!rc || (rc == -1 && errno == EAGAIN)); +#endif } } @@ -860,7 +1012,11 @@ static inline struct fy_thread_work *fy_worker_thread_steal_work(struct fy_threa return NULL; } +#ifdef _WIN32 +static DWORD WINAPI fy_worker_thread_steal(void *arg) +#else static void *fy_worker_thread_steal(void *arg) +#endif { struct fy_thread *t = arg; struct fy_thread_pool *tp; @@ -869,7 +1025,11 @@ static void *fy_worker_thread_steal(void *arg) tp = t->tp; /* store per thread info */ +#ifdef _WIN32 + TlsSetValue(tp->key, t); +#else pthread_setspecific(tp->key, t); +#endif TDBG("%s: T#%u in steal mode\n", __func__, t->id); @@ -906,7 +1066,11 @@ static void *fy_worker_thread_steal(void *arg) } TDBG("%s: T#%u leaving steal mode\n", __func__, t->id); +#ifdef _WIN32 + return 0; +#else return NULL; +#endif } static void fy_thread_work_join_steal(struct fy_thread_pool *tp, struct fy_thread_work *works, size_t work_count, fy_work_check_fn check_fn) @@ -922,9 +1086,14 @@ static void fy_thread_work_join_steal(struct fy_thread_pool *tp, struct fy_threa resolved_t = false; tid = -1; (void)tid; + (void)rc; #ifdef FY_THREAD_DEBUG - t = pthread_getspecific(tp->key); +#ifdef _WIN32 + t = TlsGetValue(tp->key); +#else + t = fy_thread_getspecific(tp->key); +#endif tid = t ? (int)t->id : -1; resolved_t = true; #else @@ -970,7 +1139,7 @@ static void fy_thread_work_join_steal(struct fy_thread_pool *tp, struct fy_threa if (work_count > 0) { if (!resolved_t) { - t = pthread_getspecific(tp->key); + t = fy_thread_getspecific(tp->key); tid = t ? (int)t->id : -1; resolved_t = true; } @@ -1059,7 +1228,11 @@ static void fy_thread_work_join_steal_2(struct fy_thread_pool *tp, struct fy_thr (void)tid; #ifdef FY_THREAD_DEBUG - t = pthread_getspecific(tp->key); +#ifdef _WIN32 + t = TlsGetValue(tp->key); +#else + t = fy_thread_getspecific(tp->key); +#endif tid = t ? (int)t->id : -1; resolved_t = true; #else @@ -1092,7 +1265,7 @@ static void fy_thread_work_join_steal_2(struct fy_thread_pool *tp, struct fy_thr } else { if (!resolved_t) { - t = pthread_getspecific(tp->key); + t = fy_thread_getspecific(tp->key); tid = t ? (int)t->id : -1; resolved_t = true; } diff --git a/src/thread/fy-thread.h b/src/thread/fy-thread.h index 8b18025..ed16c9b 100644 --- a/src/thread/fy-thread.h +++ b/src/thread/fy-thread.h @@ -15,15 +15,17 @@ #include #include #include -#include -#include +#ifndef _WIN32 +#include #ifndef __APPLE__ #include #else #include #endif +#endif +#include "fy-win32.h" #include "fy-bit64.h" #include "fy-align.h" #include "fy-atomics.h" @@ -39,6 +41,8 @@ struct fy_work_pool { FY_ATOMIC(uint32_t) done; #elif defined(__APPLE__) dispatch_semaphore_t sem; +#elif defined(_WIN32) + HANDLE sem; #else sem_t sem; #endif @@ -47,12 +51,21 @@ struct fy_work_pool { struct fy_thread { struct fy_thread_pool *tp; unsigned int id; - pthread_t tid; FY_ATOMIC(struct fy_thread_work *)work; FY_ATOMIC(struct fy_thread_work *)next_work; +#ifndef _WIN32 + pthread_t tid; +#else + HANDLE tid; +#endif #if defined(__linux__) && !defined(FY_THREAD_PORTABLE) FY_ATOMIC(uint32_t) submit; FY_ATOMIC(uint32_t) done; +#elif defined(_WIN32) + HANDLE submit_event; + HANDLE done_event; + CRITICAL_SECTION lock; + CRITICAL_SECTION wait_lock; #else pthread_mutex_t lock; pthread_cond_t cond; @@ -67,7 +80,11 @@ struct fy_thread_pool { struct fy_thread *threads; FY_ATOMIC(uint64_t) *freep; FY_ATOMIC(uint64_t) *lootp; +#if !defined(_WIN32) pthread_key_t key; +#else // _WIN32 + DWORD key; +#endif }; /* those are internal only */ diff --git a/src/tool/fy-tool-dump.c b/src/tool/fy-tool-dump.c index a66a4f4..a54adb8 100644 --- a/src/tool/fy-tool-dump.c +++ b/src/tool/fy-tool-dump.c @@ -14,18 +14,14 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include #include #include #include #include #include +#include +#include #include @@ -91,7 +87,7 @@ void print_escaped(const char *str, size_t length) case '\v': printf("\\v"); break; - case '\e': + case '\x1b': printf("\\e"); break; case 0x85: @@ -130,14 +126,13 @@ void dump_token_comments(struct fy_token *fyt, bool colorize, const char *banner [fycp_bottom] = "bottom", }; enum fy_comment_placement placement; - char buf[4096]; const char *str; if (!fyt) return; for (placement = fycp_top; placement < fycp_max; placement++) { - str = fy_token_get_comment(fyt, buf, sizeof(buf), placement); + str = fy_token_get_comment(fyt, placement); if (!str) continue; fputs("\n", stdout); diff --git a/src/tool/fy-tool-util.c b/src/tool/fy-tool-util.c index a3eac5d..d237434 100644 --- a/src/tool/fy-tool-util.c +++ b/src/tool/fy-tool-util.c @@ -14,13 +14,10 @@ #include #include #include -#include #include -#include #include #include #include -#include #include #include #include @@ -100,7 +97,7 @@ uintmax_t load_bitfield_le(const void *ptr, size_t bit_offset, size_t bit_width, v = 0; width = bit_width; - p = ptr + bit_offset / 8; + p = (const uint8_t *)ptr + bit_offset / 8; off = bit_offset & 7; if (off) { space = 8 - off; @@ -145,7 +142,7 @@ void store_bitfield_le(void *ptr, size_t bit_offset, size_t bit_width, uintmax_t uint8_t bmask; width = bit_width; - p = ptr + bit_offset / 8; + p = (uint8_t *)ptr + bit_offset / 8; off = bit_offset & 7; if (off) { space = 8 - off; diff --git a/src/tool/fy-tool-util.h b/src/tool/fy-tool-util.h index ca34287..1094e44 100644 --- a/src/tool/fy-tool-util.h +++ b/src/tool/fy-tool-util.h @@ -15,8 +15,13 @@ #include #include +#ifdef _WIN32 +#include "fy-win32.h" +#include +#else #include #include +#endif #include #include @@ -54,9 +59,13 @@ #define COMPILE_ERROR_ON_ZERO(_e) ((void)(sizeof(char[1 - 2*!!(_e)]))) /* true, if types are the same, false otherwise (depends on builtin_types_compatible_p) */ -#if defined(__has_builtin) && __has_builtin(__builtin_types_compatible_p) +#if defined(__has_builtin) +#if __has_builtin(__builtin_types_compatible_p) #define SAME_TYPE(_a, _b) __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b)) -#else +#endif +#endif + +#ifndef SAME_TYPE #define SAME_TYPE(_a, _b) true #endif @@ -65,8 +74,20 @@ COMPILE_ERROR_ON_ZERO(!SAME_TYPE(_a, _b)) /* type safe add overflow */ -#if defined(__has_builtin) && __has_builtin(__builtin_add_overflow) +#if defined(__has_builtin) +#if __has_builtin(__builtin_add_overflow) #define ADD_OVERFLOW __builtin_add_overflow +#endif +#endif +#ifndef ADD_OVERFLOW +#if defined(_MSC_VER) +/* MSVC-compatible version - simple overflow check for unsigned types */ +static inline bool fy_add_overflow_size_t(size_t a, size_t b, size_t *resp) +{ + *resp = a + b; + return *resp < a; +} +#define ADD_OVERFLOW(a, b, resp) fy_add_overflow_size_t((size_t)(a), (size_t)(b), (size_t*)(resp)) #else #define ADD_OVERFLOW(_a, _b, _resp) \ ({ \ @@ -83,10 +104,23 @@ __overflow; \ }) #endif +#endif /* type safe sub overflow */ -#if defined(__has_builtin) && __has_builtin(__builtin_sub_overflow) +#if defined(__has_builtin) +#if __has_builtin(__builtin_sub_overflow) #define SUB_OVERFLOW __builtin_sub_overflow +#endif +#endif +#ifndef SUB_OVERFLOW +#if defined(_MSC_VER) +/* MSVC-compatible version */ +static inline bool fy_sub_overflow_size_t(size_t a, size_t b, size_t *resp) +{ + *resp = a - b; + return a < b; +} +#define SUB_OVERFLOW(a, b, resp) fy_sub_overflow_size_t((size_t)(a), (size_t)(b), (size_t*)(resp)) #else #define SUB_OVERFLOW(_a, _b, _resp) \ ({ \ @@ -103,10 +137,27 @@ __overflow; \ }) #endif +#endif /* type safe multiply overflow */ -#if defined(__has_builtin) && __has_builtin(__builtin_mul_overflow) +#if defined(__has_builtin) +#if __has_builtin(__builtin_mul_overflow) #define MUL_OVERFLOW __builtin_mul_overflow +#endif +#endif +#ifndef MUL_OVERFLOW +#if defined(_MSC_VER) +/* MSVC-compatible version */ +static inline bool fy_mul_overflow_size_t(size_t a, size_t b, size_t *resp) +{ + if (a == 0 || b == 0) { + *resp = 0; + return false; + } + *resp = a * b; + return (*resp / a) != b; +} +#define MUL_OVERFLOW(a, b, resp) fy_mul_overflow_size_t((size_t)(a), (size_t)(b), (size_t*)(resp)) #else #define MUL_OVERFLOW(_a, _b, _resp) \ ({ \ @@ -128,6 +179,7 @@ __overflow; \ }) #endif +#endif /* ANSI colors and escapes */ #define A_RESET "\x1b[0m" @@ -207,21 +259,21 @@ void store_le(void *ptr, size_t width, uintmax_t v); uintmax_t load_bitfield_le(const void *ptr, size_t bit_offset, size_t bit_width, bool is_signed); void store_bitfield_le(void *ptr, size_t bit_offset, size_t bit_width, uintmax_t v); -static inline intmax_t signed_integer_max_from_bit_width(int bit_width) +static inline intmax_t signed_integer_max_from_bit_width(size_t bit_width) { - assert((size_t)bit_width <= sizeof(intmax_t) * 8); + assert(bit_width <= sizeof(intmax_t) * 8); return INTMAX_MAX >> (sizeof(intmax_t) * 8 - bit_width); } -static inline intmax_t signed_integer_min_from_bit_width(int bit_width) +static inline intmax_t signed_integer_min_from_bit_width(size_t bit_width) { - assert((size_t)bit_width <= sizeof(intmax_t) * 8); + assert(bit_width <= sizeof(intmax_t) * 8); return INTMAX_MIN >> (sizeof(intmax_t) * 8 - bit_width); } -static inline uintmax_t unsigned_integer_max_from_bit_width(int bit_width) +static inline uintmax_t unsigned_integer_max_from_bit_width(size_t bit_width) { - assert((size_t)bit_width <= sizeof(uintmax_t) * 8); + assert(bit_width <= sizeof(uintmax_t) * 8); return UINTMAX_MAX >> (sizeof(uintmax_t) * 8 - bit_width); } diff --git a/src/tool/fy-tool.c b/src/tool/fy-tool.c index 3529e4f..99fe401 100644 --- a/src/tool/fy-tool.c +++ b/src/tool/fy-tool.c @@ -14,13 +14,11 @@ #include #include #include -#include #include -#include + #include #include #include -#include #include #include #include @@ -30,9 +28,22 @@ #include +#ifndef _WIN32 +#include +#include +#else +#include "fy-win32.h" +#endif + +#include + #include "fy-valgrind.h" #include "fy-tool-util.h" +#ifdef _WIN32 +#undef SORT_DEFAULT +#endif + #define QUIET_DEFAULT false #define INCLUDE_DEFAULT "" #define DEBUG_LEVEL_DEFAULT 3 @@ -510,7 +521,8 @@ int apply_flags_option(const char *arg, unsigned int *flagsp, { const char *s, *e, *sn; char *targ; - int len, rc; + size_t len; + int rc; if (!arg || !flagsp || !modify_flags) return -1; @@ -522,7 +534,7 @@ int apply_flags_option(const char *arg, unsigned int *flagsp, if (!sn) sn = e; - len = sn - s; + len = (size_t)(sn - s); targ = alloca(len + 1); memcpy(targ, s, len); targ[len] = '\0'; @@ -841,7 +853,8 @@ static int do_b3sum_check_file(struct fy_blake3_hasher *hasher, const char *chec const uint8_t *computed_hash; char *s; char c; - unsigned int i, j, length; + unsigned int i, j; + size_t length; size_t linesz; int line, exit_code; @@ -878,7 +891,7 @@ static int do_b3sum_check_file(struct fy_blake3_hasher *hasher, const char *chec while (isxdigit((unsigned char)*s)) s++; - length = s - linebuf; + length = (size_t)(s - linebuf); if (length == 0 || length > (FY_BLAKE3_OUT_LEN * 2) || (length % 1) || !isspace((unsigned char)*s)) { fprintf(stderr, "Bad line found at file \"%s\" line #%d\n", check_filename, line); @@ -1122,13 +1135,60 @@ int main(int argc, char *argv[]) fy_valgrind_check(&argc, &argv); +#ifdef _WIN32 + /* On Windows, set stdin/stdout to binary mode to prevent CRLF conversion */ + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + + /* Enable ANSI escape sequence processing for colored output. + * Skip this on Wine - the host terminal handles ANSI natively and + * enabling VT processing causes Wine to interpret sequences internally. */ + if (!GetModuleHandleA("ntdll.dll") || + !GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_version")) { + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE); + DWORD dwMode = 0; + if (hOut != INVALID_HANDLE_VALUE && GetConsoleMode(hOut, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hOut, dwMode); + } + if (hErr != INVALID_HANDLE_VALUE && GetConsoleMode(hErr, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hErr, dwMode); + } + } +#endif + /* select the appropriate tool mode */ progname = strrchr(argv[0], '/'); +#ifdef _WIN32 + /* On Windows, also check for backslash */ + { + char *p = strrchr(argv[0], '\\'); + if (p && (!progname || p > progname)) + progname = p; + } +#endif if (!progname) progname = argv[0]; else progname++; +#ifdef _WIN32 + /* On Windows, strip .exe extension for comparison */ + { + static char progname_buf[256]; + size_t len = strlen(progname); + if (len > 4 && !_stricmp(progname + len - 4, ".exe")) { + if (len - 4 < sizeof(progname_buf)) { + memcpy(progname_buf, progname, len - 4); + progname_buf[len - 4] = '\0'; + progname = progname_buf; + } + } + } +#endif + /* default mode is dump */ if (!strcmp(progname, "fy-filter")) tool_mode = OPT_FILTER; diff --git a/src/util/fy-align.h b/src/util/fy-align.h index 1e7e180..71b012f 100644 --- a/src/util/fy-align.h +++ b/src/util/fy-align.h @@ -19,7 +19,7 @@ #if defined(__GNUC__) || defined(__clang__) #define FY_ALIGNED_TO(x) __attribute__ ((aligned(x))) #elif defined(_MSC_VER) -#define FY_ALIGNED_TO(x) __declspec(align(x)) +#define FY_ALIGNED_TO(x) /* nothing - MSVC doesn't support trailing alignment */ #else #define FY_ALIGNED_TO(x) /* nothing */ #endif @@ -31,55 +31,31 @@ #define FY_CACHELINE_SIZE_ALIGN(_x) FY_ALIGN(FY_CACHELINE_SIZE, _x) #define FY_CACHELINE_ALIGN FY_ALIGNED_TO(FY_CACHELINE_SIZE) -/* provide posix_memalign for platforms that don't have it */ -#ifdef _WIN32 -static inline int posix_memalign(void **ptr, size_t align, size_t size) -{ - void *p; - - /* must be a power of two */ - if ((size & (size -1)) != 0) { - *ptr = NULL; - return EINVAL; - } - - p = _aligned_malloc(size, align); - if (!p) { - *ptr = NULL; - return ENOMEM; - } - return p; -} - -static inline void posix_memalign_free(void *p) -{ - _aligned_free(p); -} - -#else -/* normal implementations just use free */ -static inline void posix_memalign_free(void *p) -{ - free(p); -} -#endif - static inline void *fy_align_alloc(size_t align, size_t size) { void *p; - int rc; size = FY_ALIGN(align, size); - rc = posix_memalign(&p, align, size); - if (rc) +#ifndef _WIN32 + if (posix_memalign(&p, align, size)) return NULL; +#else + p = _aligned_malloc(size, align); + if (!p) + return NULL; +#endif return p; } static inline void fy_align_free(void *p) { - if (p) - posix_memalign_free(p); + if (!p) + return; +#ifndef _WIN32 + free(p); +#else + _aligned_free(p); +#endif } static inline void *fy_cacheline_alloc(size_t size) diff --git a/src/util/fy-atomics.h b/src/util/fy-atomics.h index bfc177f..099bc48 100644 --- a/src/util/fy-atomics.h +++ b/src/util/fy-atomics.h @@ -191,12 +191,14 @@ static inline void fy_cpu_relax(void) #endif } -#define fy_atomic_get_and_clear_counter(_ptr) \ - ({ \ - __typeof__(_ptr) __ptr = (_ptr); \ - __typeof__(*__ptr + 0) __v = fy_atomic_load(__ptr); \ - fy_atomic_fetch_sub(__ptr, __v); \ - __v; \ - }) +static inline uint64_t +fy_atomic_get_and_clear_counter(FY_ATOMIC(uint64_t) *ptr) +{ + uint64_t v; + + v = fy_atomic_load(ptr); + fy_atomic_fetch_sub(ptr, v); + return v; +} #endif diff --git a/src/util/fy-bit64.h b/src/util/fy-bit64.h index 48e2c4a..dac17af 100644 --- a/src/util/fy-bit64.h +++ b/src/util/fy-bit64.h @@ -46,6 +46,101 @@ static inline unsigned int fy_bit64_ffs(uint64_t x) return FY_BIT64_FFS(x); } +#elif defined(_MSC_VER) /* MSVC implementation */ + +#include + +#if defined(_M_X64) || defined(_M_AMD64) || defined(_M_ARM64) +/* 64-bit MSVC - use native 64-bit intrinsics */ + +static inline unsigned int fy_bit64_lowest(uint64_t x) +{ + unsigned long index; + if (_BitScanForward64(&index, x)) + return (unsigned int)index; + return 64; +} + +static inline unsigned int fy_bit64_highest(uint64_t x) +{ + unsigned long index; + if (_BitScanReverse64(&index, x)) + return 63 - (unsigned int)index; + return 64; +} + +static inline unsigned int fy_bit64_popcnt(uint64_t x) +{ + return (unsigned int)__popcnt64(x); +} + +static inline unsigned int fy_bit64_ffs(uint64_t x) +{ + unsigned long index; + if (x == 0) + return 0; + _BitScanForward64(&index, x); + return (unsigned int)index + 1; +} + +#else +/* 32-bit MSVC - use 32-bit intrinsics on halves */ + +static inline unsigned int fy_bit64_lowest(uint64_t x) +{ + unsigned long index; + uint32_t lo = (uint32_t)x; + uint32_t hi = (uint32_t)(x >> 32); + + if (lo && _BitScanForward(&index, lo)) + return (unsigned int)index; + if (hi && _BitScanForward(&index, hi)) + return (unsigned int)index + 32; + return 64; +} + +static inline unsigned int fy_bit64_highest(uint64_t x) +{ + unsigned long index; + uint32_t lo = (uint32_t)x; + uint32_t hi = (uint32_t)(x >> 32); + + if (hi && _BitScanReverse(&index, hi)) + return 63 - ((unsigned int)index + 32); + if (lo && _BitScanReverse(&index, lo)) + return 63 - (unsigned int)index; + return 64; +} + +static inline unsigned int fy_bit64_popcnt(uint64_t x) +{ + uint32_t lo = (uint32_t)x; + uint32_t hi = (uint32_t)(x >> 32); + return (unsigned int)(__popcnt(lo) + __popcnt(hi)); +} + +static inline unsigned int fy_bit64_ffs(uint64_t x) +{ + unsigned long index; + uint32_t lo = (uint32_t)x; + uint32_t hi = (uint32_t)(x >> 32); + + if (x == 0) + return 0; + if (lo && _BitScanForward(&index, lo)) + return (unsigned int)index + 1; + if (hi && _BitScanForward(&index, hi)) + return (unsigned int)index + 33; + return 0; +} + +#endif /* 64-bit vs 32-bit MSVC */ + +#define FY_BIT64_LOWEST(_x) fy_bit64_lowest(_x) +#define FY_BIT64_HIGHEST(_x) fy_bit64_highest(_x) +#define FY_BIT64_POPCNT(_x) fy_bit64_popcnt(_x) +#define FY_BIT64_FFS(_x) fy_bit64_ffs(_x) + #else /* portable implementation */ static inline unsigned int fy_bit64_lowest(uint64_t x) @@ -89,7 +184,7 @@ static inline unsigned int fy_bit64_ffs(uint64_t x) } #define FY_BIT64_LOWEST(_x) fy_bit64_lowest(_x) -#define FY_BIT64_HIGEST(_x) fy_bit64_highest(_x) +#define FY_BIT64_HIGHEST(_x) fy_bit64_highest(_x) #define FY_BIT64_POPCNT(_x) fy_bit64_popcnt(_x) #define FY_BIT64_FFS(_x) fy_bit64_ffs(_x) diff --git a/src/util/fy-blob.c b/src/util/fy-blob.c index 9ed73a9..e8ec2b9 100644 --- a/src/util/fy-blob.c +++ b/src/util/fy-blob.c @@ -9,12 +9,16 @@ #include "config.h" #endif -#include -#include -#include #include #include +#include + +#ifndef _WIN32 +#include +#include +#endif +#include "fy-win32.h" #include "fy-blob.h" /* define optimized methods for probing */ diff --git a/src/util/fy-ctype.h b/src/util/fy-ctype.h index 7c20791..879b00d 100644 --- a/src/util/fy-ctype.h +++ b/src/util/fy-ctype.h @@ -262,24 +262,26 @@ static inline bool fy_is_flow_ws_m(const int c, const enum fy_flow_ws_mode fws_m static inline const void * \ fy_find_ ## _kind (const void *s, size_t len) \ { \ - const void *e = s + len; \ + const char *sc = (const char *)s; \ + const char *e = sc + len; \ int c, w; \ - for (; s < e && (c = fy_utf8_get(s, e - s, &w)) >= 0; s += w) { \ + for (; sc < e && (c = fy_utf8_get(sc, (size_t)(e - sc), &w)) >= 0; sc += w) { \ assert(w); \ if (fy_is_ ## _kind (c)) \ - return s; \ + return sc; \ } \ return NULL; \ } \ static inline const void * \ fy_find_non_ ## _kind (const void *s, size_t len) \ { \ - const void *e = s + len; \ + const char *sc = (const char *)s; \ + const char *e = sc + len; \ int c, w; \ - for (; s < e && (c = fy_utf8_get(s, e - s, &w)) >= 0; s += w) { \ + for (; sc < e && (c = fy_utf8_get(sc, (size_t)(e - sc), &w)) >= 0; sc += w) { \ assert(w); \ if (!(fy_is_ ## _kind (c))) \ - return s; \ + return sc; \ assert(w); \ } \ return NULL; \ @@ -334,7 +336,7 @@ static inline const void *fy_skip_lb(const void *ptr, size_t left) if (c == '\r' && left > (size_t)width && *(char *)ptr == '\n') width++; - return ptr + width; + return (const char *)ptr + width; } /* given a pointer to a chunk of memory, return pointer to first diff --git a/src/util/fy-endian.h b/src/util/fy-endian.h index 5778037..0b6fa03 100644 --- a/src/util/fy-endian.h +++ b/src/util/fy-endian.h @@ -8,18 +8,18 @@ #ifndef FY_ENDIAN_H #define FY_ENDIAN_H -#if defined(__linux__) || defined(__CYGWIN__) || defined(__OpenBSD__) || defined(__GNU__) +#if defined(__linux__) || defined(__CYGWIN__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__EMSCRIPTEN__) # include #elif defined(__APPLE__) # include # include #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) # include -#elif defined(_MSC_VER) -# include -# ifdef __GNUC__ -# include -# endif +#elif defined(_WIN32) || defined(_MSC_VER) +/* Windows is always little-endian on supported platforms */ +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __BYTE_ORDER __LITTLE_ENDIAN #else # error unsupported platform #endif diff --git a/src/util/fy-id.h b/src/util/fy-id.h index aae5b9f..f692aaf 100644 --- a/src/util/fy-id.h +++ b/src/util/fy-id.h @@ -9,10 +9,8 @@ #define FY_ID_H #include -#include #include #include - #include #include "fy-bit64.h" @@ -61,7 +59,6 @@ static inline int fy_id_alloc(fy_id_bits *bits, const size_t count) fy_id_bits_non_atomic v, new_v; for (i = 0; i < count; i++, bits++) { - for (;;) { v = fy_atomic_load(bits); pos = fy_id_ffs(~v); @@ -69,7 +66,7 @@ static inline int fy_id_alloc(fy_id_bits *bits, const size_t count) break; new_v = v | FY_ID_BIT_MASK(pos); if (fy_atomic_compare_exchange_strong(bits, &v, new_v)) - return (i * FY_ID_BITS_BITS) + pos; + return ((int)i * FY_ID_BITS_BITS) + pos; } } return -1; diff --git a/src/util/fy-utf8.c b/src/util/fy-utf8.c index 228879a..218509f 100644 --- a/src/util/fy-utf8.c +++ b/src/util/fy-utf8.c @@ -127,7 +127,7 @@ struct fy_utf8_fmt_esc_map { }; static const struct fy_utf8_fmt_esc_map esc_all = { - .ch = (const int []){ '\\', '\0', '\b', '\r', '\t', '\f', '\n', '\v', '\a', '\e', 0x85, 0xa0, 0x2028, 0x2029, -1 }, + .ch = (const int []){ '\\', '\0', '\b', '\r', '\t', '\f', '\n', '\v', '\a', '\x1b', 0x85, 0xa0, 0x2028, 0x2029, -1 }, .map = (const int []){ '\\', '0', 'b', 'r', 't', 'f', 'n', 'v', 'a', 'e', 'N', '_', 'L', 'P', 0 } }; @@ -251,13 +251,14 @@ char *fy_utf8_format_text_alloc(const char *buf, size_t len, enum fy_utf8_escape const void *fy_utf8_memchr_generic(const void *s, int c, size_t n) { int cc, w; - const void *e; + const char *sc, *e; - e = s + n; - while (s < e && (cc = fy_utf8_get(s, e - s, &w)) >= 0) { + sc = (const char *)s; + e = sc + n; + while (sc < e && (cc = fy_utf8_get(sc, e - sc, &w)) >= 0) { if (c == cc) - return s; - s += w; + return sc; + sc += w; } return NULL; @@ -350,7 +351,7 @@ int fy_utf8_parse_escape(const char **strp, size_t len, enum fy_utf8_escape esc) value = '\v'; break; case 'e': - value = '\e'; + value = '\x1b'; break; case ' ': value = ' '; @@ -978,11 +979,11 @@ void *fy_utf8_split_posix(const char *str, int *argcp, const char * const *argvp return NULL; tmpargv = mem; - tmparg = mem + (argv_count + 1) * sizeof(*tmpargv); + tmparg = (char *)mem + (argv_count + 1) * sizeof(*tmpargv); for (i = 0; i < argv_count; i++) { tmpargv[i] = tmparg; strcpy(tmparg, argv[i]); - tmparg += strlen(argv[i]) + 1; + tmparg = tmparg + strlen(argv[i]) + 1; } tmpargv[i] = NULL; @@ -1004,7 +1005,7 @@ int fy_utf8_get_generic_s(const void *ptr, const void *ptr_end, int *widthp) width = fy_utf8_width_by_first_octet(p[0]); if (!width) return FYUG_INV; - if (ptr + width > ptr_end) + if ((const char *)ptr + width > (const char *)ptr_end) return FYUG_PARTIAL; /* initial value */ diff --git a/src/util/fy-utf8.h b/src/util/fy-utf8.h index 9a40af7..5f9321a 100644 --- a/src/util/fy-utf8.h +++ b/src/util/fy-utf8.h @@ -20,7 +20,7 @@ #include #endif - +#include "fy-win32.h" #include "fy-utils.h" #define FY_UTF8_MIN_WIDTH 1 @@ -319,7 +319,7 @@ fy_utf8_get_no_width(const void *ptr, size_t left) static inline FY_ALWAYS_INLINE int fy_utf8_get_end(const void *ptr, const void *ptr_end, int *widthp) { - return fy_utf8_get(ptr, (size_t)(ptr_end - ptr), widthp); + return fy_utf8_get(ptr, (size_t)((const char *)ptr_end - (const char *)ptr), widthp); } static inline FY_ALWAYS_INLINE int @@ -327,14 +327,14 @@ fy_utf8_get_end_no_width(const void *ptr, const void *ptr_end) { int w; - return fy_utf8_get(ptr, (size_t)(ptr_end - ptr), &w); + return fy_utf8_get(ptr, (size_t)((const char *)ptr_end - (const char *)ptr), &w); } int fy_utf8_get_right_generic(const void *ptr, size_t left, int *widthp); static inline int fy_utf8_get_right(const void *ptr, size_t left, int *widthp) { - const uint8_t *p = ptr + left; + const uint8_t *p = (const uint8_t *)ptr + left; /* single byte (hot path) */ if (left > 0 && !(p[-1] & 0x80)) { @@ -395,11 +395,27 @@ static inline bool fy_utf8_escape_is_any_doublequote(const enum fy_utf8_escape e char *fy_utf8_format(int c, char *buf, const enum fy_utf8_escape esc); +#ifdef _MSC_VER +/* MSVC doesn't support GCC statement expressions - use static buffers */ +static __declspec(thread) char fy_utf8_format_a_buf[FY_UTF8_FORMAT_BUFMIN]; +static __declspec(thread) char fy_utf8_format_text_a_buf[4096]; + +#define fy_utf8_format_a(_c, _esc) \ + strcpy(alloca(FY_UTF8_FORMAT_BUFMIN + 1), \ + fy_utf8_format((_c), fy_utf8_format_a_buf, (_esc))) + +#define fy_utf8_format_text_a(_buf, _len, _esc) \ + strcpy(alloca(sizeof(fy_utf8_format_text_a_buf) + 1), \ + fy_utf8_format_text((_buf), (_len), \ + fy_utf8_format_text_a_buf, sizeof(fy_utf8_format_text_a_buf) - 1, (_esc))) +#else +/* GCC/Clang version with statement expressions */ #define fy_utf8_format_a(_c, _esc) \ ({ \ char *_buf = alloca(FY_UTF8_FORMAT_BUFMIN); \ fy_utf8_format((_c), _buf, _esc); \ }) +#endif size_t fy_utf8_format_text_length(const char *buf, size_t len, enum fy_utf8_escape esc); @@ -407,6 +423,7 @@ char *fy_utf8_format_text(const char *buf, size_t len, char *out, size_t maxsz, enum fy_utf8_escape esc); +#ifndef _MSC_VER #define fy_utf8_format_text_a(_buf, _len, _esc) \ ({ \ const char *__buf = (_buf); \ @@ -416,6 +433,7 @@ char *fy_utf8_format_text(const char *buf, size_t len, char *_out = alloca(_outsz + 1); \ fy_utf8_format_text(__buf, __len, _out, _outsz, __esc); \ }) +#endif char *fy_utf8_format_text_alloc(const char *buf, size_t len, const enum fy_utf8_escape esc); @@ -441,7 +459,7 @@ static inline const void *fy_utf8_strchr(const void *s, int c) static inline int fy_utf8_count(const void *ptr, size_t len) { - const uint8_t *s = ptr, *e = ptr + len; + const uint8_t *s = (const uint8_t *)ptr, *e = (const uint8_t *)ptr + len; int w, count; count = 0; diff --git a/src/util/fy-utils.c b/src/util/fy-utils.c index 40ce77b..46dcf17 100644 --- a/src/util/fy-utils.c +++ b/src/util/fy-utils.c @@ -15,13 +15,17 @@ #include #include #include +#include + +#ifndef _WIN32 #include #include #include #include #include -#include +#endif +#include "fy-win32.h" #include "fy-utf8.h" #include "fy-ctype.h" #include "fy-utils.h" @@ -298,14 +302,14 @@ int fy_tag_handle_length(const char *data, size_t len) c = fy_utf8_get(s, e - s, &w); if (c == -1) - return len; + return (int)len; if (fy_is_ws(c)) - return s - data; + return (int)(s - data); /* if first character is !, empty handle */ if (c == '!') { s += w; - return s - data; + return (int)(s - data); } if (!fy_is_first_alpha(c)) return -1; @@ -315,7 +319,7 @@ int fy_tag_handle_length(const char *data, size_t len) if (c == '!') s += w; - return s - data; + return (int)(s - data); } int fy_tag_uri_length(const char *data, size_t len) @@ -332,7 +336,7 @@ int fy_tag_uri_length(const char *data, size_t len) break; s += w; } - uri_length = s - data; + uri_length = (int)(s - data); if (!fy_tag_uri_is_valid(data, uri_length)) return -1; @@ -404,7 +408,10 @@ int fy_tag_scan(const char *data, size_t len, struct fy_tag_scan_info *info) } /* simple terminal methods; mainly for getting size of terminal */ -int fy_term_set_raw(int fd, struct termios *oldt) +/* These functions are not available on Windows */ +#ifndef _WIN32 +static int +fy_term_set_raw(int fd, struct termios *oldt) { struct termios newt, t; int ret; @@ -431,7 +438,8 @@ int fy_term_set_raw(int fd, struct termios *oldt) return 0; } -int fy_term_restore(int fd, const struct termios *oldt) +static int +fy_term_restore(int fd, const struct termios *oldt) { /* must be a terminal */ if (!isatty(fd)) @@ -440,7 +448,8 @@ int fy_term_restore(int fd, const struct termios *oldt) return tcsetattr(fd, TCSANOW, oldt); } -ssize_t fy_term_write(int fd, const void *data, size_t count) +static ssize_t +fy_term_write(int fd, const void *data, size_t count) { ssize_t wrn, r; @@ -463,7 +472,8 @@ ssize_t fy_term_write(int fd, const void *data, size_t count) return wrn > 0 ? wrn : r; } -int fy_term_safe_write(int fd, const void *data, size_t count) +static int +fy_term_safe_write(int fd, const void *data, size_t count) { if (!isatty(fd)) return -1; @@ -471,7 +481,8 @@ int fy_term_safe_write(int fd, const void *data, size_t count) return fy_term_write(fd, data, count) == (ssize_t)count ? 0 : -1; } -ssize_t fy_term_read(int fd, void *data, size_t count, int timeout_us) +static ssize_t +fy_term_read(int fd, void *data, size_t count, int timeout_us) { ssize_t rdn, r; struct timeval tv, tvto, *tvp; @@ -523,7 +534,8 @@ ssize_t fy_term_read(int fd, void *data, size_t count, int timeout_us) return rdn > 0 ? rdn : r; } -ssize_t fy_term_read_escape(int fd, void *buf, size_t count) +static ssize_t +fy_term_read_escape(int fd, void *buf, size_t count) { char *p; int r, rdn; @@ -572,7 +584,8 @@ ssize_t fy_term_read_escape(int fd, void *buf, size_t count) return rdn; } -int fy_term_query_size_raw(int fd, int *rows, int *cols) +static int +fy_term_query_size_raw(int fd, int *rows, int *cols) { char buf[32]; char *s, *e; @@ -639,6 +652,27 @@ int fy_term_query_size(int fd, int *rows, int *cols) return ret; } +#else /* _WIN32 */ + +int fy_term_query_size(int fd, int *rows, int *cols) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + HANDLE h; + + if (fd != _fileno(stdout) && fd != _fileno(stderr)) + return -1; + + h = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleScreenBufferInfo(h, &csbi)) + return -1; + + *cols = (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1); + *rows = (int)(csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + return 0; +} + +#endif /* !_WIN32 - end of terminal functions */ + int fy_comment_iter_begin(const char *comment, size_t size, struct fy_comment_iter *iter) { if (!comment || !iter) @@ -734,6 +768,7 @@ void fy_comment_iter_end(struct fy_comment_iter *iter) char *fy_get_cooked_comment(const char *raw_comment, size_t size) { + struct fy_memstream *fyms = NULL; struct fy_comment_iter iter; FILE *fp; char *buf; @@ -744,8 +779,8 @@ char *fy_get_cooked_comment(const char *raw_comment, size_t size) if (!raw_comment) return NULL; - fp = open_memstream(&buf, &len); - if (!fp) + fyms = fy_memstream_open(&fp); + if (!fyms) return NULL; ret = 0; @@ -757,8 +792,7 @@ char *fy_get_cooked_comment(const char *raw_comment, size_t size) } fy_comment_iter_end(&iter); - fclose(fp); - + buf = fy_memstream_close(fyms, &len); if (ret < 0) { if (buf) free(buf); @@ -898,3 +932,69 @@ uint64_t fy_iovec_xxhash64(const struct iovec *iov, int iovcnt) hash++; return hash; } + +struct fy_memstream { + FILE *fp; + char *buf; + size_t size; +}; + +struct fy_memstream *fy_memstream_open(FILE **fpp) +{ + struct fy_memstream *fyms; + + if (!fpp) + return NULL; + + *fpp = NULL; + + fyms = malloc(sizeof(*fyms)); + if (!fyms) + return NULL; + memset(fyms, 0, sizeof(*fyms)); +#if !defined(_WIN32) + fyms->fp = open_memstream(&fyms->buf, &fyms->size); +#else + fyms->fp = tmpfile(); +#endif + if (!fyms->fp) { + free(fyms); + return NULL; + } + *fpp = fyms->fp; + return fyms; +} + +char *fy_memstream_close(struct fy_memstream *fyms, size_t *sizep) +{ + char *buf; +#if defined(_WIN32) + long sz; +#endif + + if (!fyms) { + *sizep = 0; + return NULL; + } + +#if defined(_WIN32) + if (fyms->fp && fflush(fyms->fp) == 0 && + (sz = ftell(fyms->fp)) >= 0 && + (fyms->buf = malloc((size_t)sz + 1)) != NULL) { + rewind(fyms->fp); + fyms->size = fread(fyms->buf, 1, (size_t)sz, fyms->fp); + fyms->buf[fyms->size] = '\0'; + } +#endif + + if (fyms->fp) + fclose(fyms->fp); + + /* the buffer is updated only after close */ + buf = fyms->buf; + *sizep = buf ? fyms->size : 0; + + free(fyms); + + return buf; +} diff --git a/src/util/fy-utils.h b/src/util/fy-utils.h index 8e5fa4e..e7ac43c 100644 --- a/src/util/fy-utils.h +++ b/src/util/fy-utils.h @@ -16,13 +16,18 @@ #include #include #include -#include -#include #include #include #include #include + +#ifndef _WIN32 +#include +#include #include +#endif + +#include "fy-win32.h" /* * Define FY_CPP_SHORT_NAMES to use shortened macro names in the implementation. @@ -39,6 +44,13 @@ #define FY_BIT(x) (1U << (x)) #endif +/* MSVC doesn't support __attribute__, so disable it */ +#if defined(_MSC_VER) && !defined(__clang__) +#ifndef __attribute__ +#define __attribute__(x) +#endif +#endif + #if defined(__linux__) #include #endif @@ -61,37 +73,50 @@ struct fy_tag_scan_info { int fy_tag_scan(const char *data, size_t len, struct fy_tag_scan_info *info); +/* container_of - use a fallback for MSVC which doesn't support typeof */ #ifndef container_of +#if defined(_MSC_VER) +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) +#else #define container_of(ptr, type, member) \ ({ \ const __typeof__(((type *)0)->member) *__mptr = (ptr); \ (type *)((void *)__mptr - offsetof(type,member)); \ }) -#endif +#endif /* _MSC_VER */ +#endif /* container_of */ #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) ((sizeof(x)/sizeof((x)[0]))) #endif -#if defined(NDEBUG) && (defined(__GNUC__) && __GNUC__ >= 4) +/* even if we're compiling for windows with _MSC_VER defined, clang-cl has attributes */ +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define FY_HAS_GCC_ATTRIBUTES +#else +#undef FY_HAS_GCC_ATTRIBUTES +#endif + +#if defined(FY_HAS_GCC_ATTRIBUTES) && defined(NDEBUG) #define FY_ALWAYS_INLINE __attribute__((always_inline)) #else #define FY_ALWAYS_INLINE /* nothing */ #endif -#if defined(__GNUC__) && __GNUC__ >= 4 +#if defined(FY_HAS_GCC_ATTRIBUTES) #define FY_UNUSED __attribute__((unused)) #else #define FY_UNUSED /* nothing */ #endif -#if defined(NDEBUG) && defined(__GNUC__) && __GNUC__ >= 4 +#if defined(FY_HAS_GCC_ATTRIBUTES) && defined(NDEBUG) #define FY_DEBUG_UNUSED __attribute__((unused)) #else #define FY_DEBUG_UNUSED /* nothing */ #endif -#if defined(__GNUC__) && __GNUC__ >= 4 +#if defined(FY_HAS_GCC_ATTRIBUTES) #define FY_CONSTRUCTOR __attribute__((constructor)) #define FY_DESTRUCTOR __attribute__((destructor)) #define FY_HAS_CONSTRUCTOR @@ -122,17 +147,7 @@ static inline size_t fy_size_t_align(size_t size, size_t align) return (size + (align - 1)) & ~(align - 1); } -int fy_term_set_raw(int fd, struct termios *oldt); -int fy_term_restore(int fd, const struct termios *oldt); -ssize_t fy_term_write(int fd, const void *data, size_t count); -int fy_term_safe_write(int fd, const void *data, size_t count); -ssize_t fy_term_read(int fd, void *data, size_t count, int timeout_us); -ssize_t fy_term_read_escape(int fd, void *buf, size_t count); - -/* the raw methods require the terminal to be in raw mode */ -int fy_term_query_size_raw(int fd, int *rows, int *cols); - -/* the non raw methods will set the terminal to raw and then restore */ +/* this is expected to be supported */ int fy_term_query_size(int fd, int *rows, int *cols); struct fy_comment_iter { @@ -181,9 +196,12 @@ void fy_keyword_iter_end(struct fy_keyword_iter *iter); #endif /* true, if types are the same, false otherwise (depends on builtin_types_compatible_p) */ +#if !defined(_MSC_VER) && defined(__has_builtin) #if __has_builtin(__builtin_types_compatible_p) #define FY_SAME_TYPE(_a, _b) __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b)) -#else +#endif +#endif +#ifndef FY_SAME_TYPE #define FY_SAME_TYPE(_a, _b) true #endif @@ -192,9 +210,15 @@ void fy_keyword_iter_end(struct fy_keyword_iter *iter); FY_COMPILE_ERROR_ON_ZERO(!FY_SAME_TYPE(_a, _b)) /* type safe add overflow */ +#if !defined(_MSC_VER) && defined(__has_builtin) #if __has_builtin(__builtin_add_overflow) #define FY_ADD_OVERFLOW __builtin_add_overflow -#else +#endif +#endif +#ifdef _MSC_VER +/* MSVC: simple implementation, returns false (no overflow detection) */ +#define FY_ADD_OVERFLOW(_a, _b, _resp) ((void)((*(_resp) = (_a) + (_b))), false) +#elif !defined(FY_ADD_OVERFLOW) #define FY_ADD_OVERFLOW(_a, _b, _resp) \ ({ \ __typeof__(_a) __a = (_a), __res; \ @@ -212,9 +236,15 @@ void fy_keyword_iter_end(struct fy_keyword_iter *iter); #endif /* type safe sub overflow */ +#if !defined(_MSC_VER) && defined(__has_builtin) #if __has_builtin(__builtin_sub_overflow) #define FY_SUB_OVERFLOW __builtin_sub_overflow -#else +#endif +#endif +#ifdef _MSC_VER +/* MSVC: simple implementation, returns false (no overflow detection) */ +#define FY_SUB_OVERFLOW(_a, _b, _resp) ((void)((*(_resp) = (_a) - (_b))), false) +#elif !defined(FY_SUB_OVERFLOW) #define FY_SUB_OVERFLOW(_a, _b, _resp) \ ({ \ __typeof__(_a) __a = (_a), __res; \ @@ -232,9 +262,15 @@ void fy_keyword_iter_end(struct fy_keyword_iter *iter); #endif /* type safe multiply overflow */ +#if !defined(_MSC_VER) && defined(__has_builtin) #if __has_builtin(__builtin_mul_overflow) #define FY_MUL_OVERFLOW __builtin_mul_overflow -#else +#endif +#endif +#ifdef _MSC_VER +/* MSVC: simple implementation, returns false (no overflow detection) */ +#define FY_MUL_OVERFLOW(_a, _b, _resp) ((void)((*(_resp) = (_a) * (_b))), false) +#elif !defined(FY_MUL_OVERFLOW) #define FY_MUL_OVERFLOW(_a, _b, _resp) \ ({ \ __typeof__(_a) __a = (_a), __res; \ @@ -268,6 +304,48 @@ static inline void fy_strip_trailing_nl(char *str) } /* alloca formatted print methods */ +#ifdef _MSC_VER +/* + * MSVC doesn't support GCC statement expressions, so we provide + * function-based alternatives. These use a static thread-local buffer + * which is less flexible but works for typical use cases. + */ +#define FY_ALLOCA_SPRINTF_BUFSZ 4096 + +static __declspec(thread) char fy_alloca_sprintf_buf[FY_ALLOCA_SPRINTF_BUFSZ]; + +static inline char *fy_alloca_vsprintf_impl(const char *fmt, va_list ap) +{ + va_list ap_copy; + int len; + char *s; + + va_copy(ap_copy, ap); + len = vsnprintf(fy_alloca_sprintf_buf, FY_ALLOCA_SPRINTF_BUFSZ, fmt, ap_copy); + va_end(ap_copy); + if (len < 0) + return NULL; + /* strip trailing newlines */ + s = fy_alloca_sprintf_buf + strlen(fy_alloca_sprintf_buf); + while (s > fy_alloca_sprintf_buf && s[-1] == '\n') + *--s = '\0'; + return fy_alloca_sprintf_buf; +} + +/* For MSVC, we can't use variadic macros that return values easily, + * so fy_vsprintfa needs a different approach + * This is not very efficient and wastes memory but it's the best + * we can do with the potato compiler + */ +#define fy_vsprintfa(_fmt, _ap) \ + strcpy(alloca(FY_ALLOCA_SPRINTF_BUFSZ + 1), \ + fy_alloca_vsprintf_impl((_fmt), (_ap))) +#define fy_sprintfa(_fmt, ...) \ + strcpy(alloca(FY_ALLOCA_SPRINTF_BUFSZ + 1), \ + (snprintf(fy_alloca_sprintf_buf, FY_ALLOCA_SPRINTF_BUFSZ, (_fmt), __VA_ARGS__) < 0 ? \ + "" : fy_alloca_sprintf_buf)) +#else +/* GCC/Clang version with statement expressions */ #define fy_vsprintfa(_fmt, _ap) \ ({ \ const char *__fmt = (_fmt); \ @@ -294,6 +372,7 @@ static inline void fy_strip_trailing_nl(char *str) (void)snprintf(_buf, _size + 1, __fmt, __VA_ARGS__); \ _buf; \ }) +#endif #if !defined(NDEBUG) && defined(HAVE_DEVMODE) && HAVE_DEVMODE #define FY_DEVMODE @@ -301,15 +380,11 @@ static inline void fy_strip_trailing_nl(char *str) #undef FY_DEVMODE #endif -#ifdef FY_DEVMODE -#define __FY_DEBUG_UNUSED__ /* nothing */ -#else -#if defined(__GNUC__) && __GNUC__ >= 4 +#if defined(FY_HAS_GCC_ATTRIBUTES) && !defined(FY_DEVMODE) #define __FY_DEBUG_UNUSED__ __attribute__((__unused__)) #else #define __FY_DEBUG_UNUSED__ /* nothing */ #endif -#endif // C preprocessor magic follows (pilfered and adapted from h4x0r.org) @@ -548,7 +623,7 @@ fy_iovec_copy_from(const struct iovec *iov, int iovcnt, void *dst) size_t size; int i; - for (i = 0; i < iovcnt; i++, dst += size) { + for (i = 0; i < iovcnt; i++, dst = (void *)((char *)dst + size)) { size = iov[i].iov_len; memcpy(dst, iov[i].iov_base, size); } @@ -561,7 +636,7 @@ fy_iovec_copy_to(const struct iovec *iov, int iovcnt, const void *src) size_t size; int i; - for (i = 0; i < iovcnt; i++, src += size) { + for (i = 0; i < iovcnt; i++, src = (const void *)((const char *)src + size)) { size = iov[i].iov_len; memcpy(iov[i].iov_base, src, size); } @@ -575,12 +650,11 @@ fy_iovec_cmp(const struct iovec *iov, int iovcnt, const void *data) size_t size; int i, ret; - for (i = 0; i < iovcnt; i++) { + for (i = 0; i < iovcnt; i++, s = (const void *)((const char *)s + size)) { size = iov[i].iov_len; ret = memcmp(iov[i].iov_base, s, size); if (ret) return ret; - s += size; } return 0; } @@ -616,4 +690,10 @@ uint64_t fy_iovec_xxhash64(const struct iovec *iov, int iovcnt); #define FY_HAVE_NESTED_FUNC_LAMBDAS #endif +/* wrapper for memstream, on all platforms */ +struct fy_memstream; // opaque + +struct fy_memstream *fy_memstream_open(FILE **fpp); +char *fy_memstream_close(struct fy_memstream *fyms, size_t *sizep); + #endif diff --git a/src/util/fy-win32.h b/src/util/fy-win32.h new file mode 100644 index 0000000..f190c2f --- /dev/null +++ b/src/util/fy-win32.h @@ -0,0 +1,616 @@ +/* + * fy-win32.h - Windows compatibility layer + * + * Copyright (c) 2024-2026 libfyaml contributors + * + * SPDX-License-Identifier: MIT + * + * This header provides Unix-like APIs on Windows to allow libfyaml to + * build and run on Windows platforms. + */ +#ifndef FY_WIN32_H +#define FY_WIN32_H + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +/* Undefine Windows macros that conflict with common identifiers */ +#ifdef near +#undef near +#endif +#ifdef far +#undef far +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ssize_t is not defined on Windows */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef int ssize_t; +#endif +#endif + +/* Provide SSIZE_MAX if not defined */ +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX _I64_MAX +#else +#define SSIZE_MAX INT_MAX +#endif +#endif + +/* + * Memory mapping emulation using Windows Virtual Memory APIs + */ + +/* mmap protection flags */ +#ifndef PROT_NONE +#define PROT_NONE 0x0 +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 +#endif + +/* mmap flags */ +#ifndef MAP_SHARED +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *)-1) +#endif + +static inline void *fy_win32_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + DWORD flProtect = 0; + DWORD dwDesiredAccess = 0; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapping = NULL; + void *ptr = NULL; + + (void)addr; /* Hint address is ignored */ + + /* Anonymous mapping - use VirtualAlloc */ + if (flags & MAP_ANONYMOUS) { + DWORD flAllocationType = MEM_RESERVE | MEM_COMMIT; + DWORD flAllocProtect = 0; + + if (prot == PROT_NONE) + flAllocProtect = PAGE_NOACCESS; + else if (prot & PROT_EXEC) { + if (prot & PROT_WRITE) + flAllocProtect = PAGE_EXECUTE_READWRITE; + else + flAllocProtect = PAGE_EXECUTE_READ; + } else if (prot & PROT_WRITE) + flAllocProtect = PAGE_READWRITE; + else + flAllocProtect = PAGE_READONLY; + + ptr = VirtualAlloc(NULL, length, flAllocationType, flAllocProtect); + if (!ptr) + return MAP_FAILED; + return ptr; + } + + /* File mapping */ + if (fd < 0) { + errno = EBADF; + return MAP_FAILED; + } + + hFile = (HANDLE)_get_osfhandle(fd); + if (hFile == INVALID_HANDLE_VALUE) { + errno = EBADF; + return MAP_FAILED; + } + + /* Set protection and access flags */ + if (prot == PROT_NONE) { + flProtect = PAGE_NOACCESS; + dwDesiredAccess = 0; + } else if (prot & PROT_EXEC) { + if (prot & PROT_WRITE) { + flProtect = PAGE_EXECUTE_READWRITE; + dwDesiredAccess = FILE_MAP_WRITE | FILE_MAP_EXECUTE; + } else { + flProtect = PAGE_EXECUTE_READ; + dwDesiredAccess = FILE_MAP_READ | FILE_MAP_EXECUTE; + } + } else if (prot & PROT_WRITE) { + if (flags & MAP_PRIVATE) { + flProtect = PAGE_WRITECOPY; + dwDesiredAccess = FILE_MAP_COPY; + } else { + flProtect = PAGE_READWRITE; + dwDesiredAccess = FILE_MAP_WRITE; + } + } else { + flProtect = PAGE_READONLY; + dwDesiredAccess = FILE_MAP_READ; + } + + hMapping = CreateFileMapping(hFile, NULL, flProtect, 0, 0, NULL); + if (!hMapping) { + errno = ENOMEM; + return MAP_FAILED; + } + + ptr = MapViewOfFile(hMapping, dwDesiredAccess, + (DWORD)((uint64_t)offset >> 32), (DWORD)offset, length); + CloseHandle(hMapping); + + if (!ptr) { + errno = ENOMEM; + return MAP_FAILED; + } + + return ptr; +} + +static inline int fy_win32_munmap(void *addr, size_t length) +{ + (void)length; + + /* + * We try both VirtualFree (for anonymous mappings) and + * UnmapViewOfFile (for file mappings). One of them will succeed. + */ + if (UnmapViewOfFile(addr)) + return 0; + if (VirtualFree(addr, 0, MEM_RELEASE)) + return 0; + + errno = EINVAL; + return -1; +} + +/* Define mmap/munmap macros that use our implementations */ +#define mmap(addr, length, prot, flags, fd, offset) \ + fy_win32_mmap(addr, length, prot, flags, fd, offset) +#define munmap(addr, length) \ + fy_win32_munmap(addr, length) + +/* mremap is not supported on Windows - will never be defined */ + +/* + * File descriptor operations + */ + +/* open() flags that may be missing */ +#ifndef O_RDONLY +#define O_RDONLY _O_RDONLY +#endif +#ifndef O_WRONLY +#define O_WRONLY _O_WRONLY +#endif +#ifndef O_RDWR +#define O_RDWR _O_RDWR +#endif +#ifndef O_CREAT +#define O_CREAT _O_CREAT +#endif +#ifndef O_TRUNC +#define O_TRUNC _O_TRUNC +#endif +#ifndef O_APPEND +#define O_APPEND _O_APPEND +#endif +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif + +/* Standard file descriptor numbers */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +static inline int fy_win32_open(const char *pathname, int flags, ...) +{ + /* always use O_BINARY to avoid CRLF mangling */ + return _open(pathname, flags | O_BINARY); +} + +/* Use _open, _close on Windows */ +#ifndef open +#define open fy_win32_open +#endif +#ifndef close +#define close _close +#endif + +/* + * Windows _read/_write use unsigned int (32-bit) for count. + * Provide wrappers that handle size_t properly by chunking large I/O. + */ +static inline ssize_t fy_win32_read(int fd, void *buf, size_t count) +{ + unsigned char *p = (unsigned char *)buf; + size_t remaining = count; + ssize_t total = 0; + + while (remaining > 0) { + unsigned int chunk = (remaining > UINT_MAX) ? UINT_MAX : (unsigned int)remaining; + int ret = _read(fd, p, chunk); + if (ret < 0) + return total > 0 ? total : -1; + if (ret == 0) + break; + total += ret; + p += ret; + remaining -= ret; + if ((unsigned int)ret < chunk) + break; /* short read */ + } + return total; +} + +static inline ssize_t fy_win32_write(int fd, const void *buf, size_t count) +{ + const unsigned char *p = (const unsigned char *)buf; + size_t remaining = count; + ssize_t total = 0; + + while (remaining > 0) { + unsigned int chunk = (remaining > UINT_MAX) ? UINT_MAX : (unsigned int)remaining; + int ret = _write(fd, p, chunk); + if (ret < 0) + return total > 0 ? total : -1; + if (ret == 0) + break; + total += ret; + p += ret; + remaining -= ret; + if ((unsigned int)ret < chunk) + break; /* short write */ + } + return total; +} + +#ifndef read +#define read fy_win32_read +#endif +#ifndef write +#define write fy_win32_write +#endif +#ifndef lseek +#define lseek _lseek +#endif +#ifndef fileno +#define fileno _fileno +#endif +#ifndef fdopen +#define fdopen _fdopen +#endif +#ifndef dup +#define dup _dup +#endif +#ifndef dup2 +#define dup2 _dup2 +#endif +#ifndef access +#define access _access +#endif + +/* stat structure and functions */ +#ifndef stat +#define stat _stat64 +#endif +#ifndef fstat +#define fstat _fstat64 +#endif + +#ifndef isatty + +static inline int +fy_win32_isatty(int fd) +{ + HANDLE h; + DWORD mode; + + h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE || h == NULL) + return 0; + + return GetConsoleMode(h, &mode) != 0; +} + +#define isatty(fd) fy_win32_isatty(fd) +#endif + +/* S_ISREG and S_ISDIR macros */ +#ifndef S_ISREG +#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#endif +#ifndef S_IFREG +#define S_IFREG _S_IFREG +#endif +#ifndef S_IFMT +#define S_IFMT _S_IFMT +#endif + +/* Unix file permission macros - Windows doesn't have these */ +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IXUSR +#define S_IXUSR _S_IEXEC +#endif +/* Group/other permissions don't exist on Windows, define as 0 */ +#ifndef S_IRGRP +#define S_IRGRP 0 +#endif +#ifndef S_IWGRP +#define S_IWGRP 0 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0 +#endif +#ifndef S_IROTH +#define S_IROTH 0 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0 +#endif + +/* + * System configuration - sysconf() emulation + */ +#ifndef _SC_PAGESIZE +#define _SC_PAGESIZE 1 +#endif +#ifndef _SC_NPROCESSORS_ONLN +#define _SC_NPROCESSORS_ONLN 2 +#endif + +static inline long fy_win32_sysconf(int name) +{ + SYSTEM_INFO si; + + switch (name) { + case _SC_PAGESIZE: + GetSystemInfo(&si); + return (long)si.dwPageSize; + case _SC_NPROCESSORS_ONLN: + GetSystemInfo(&si); + return (long)si.dwNumberOfProcessors; + default: + errno = EINVAL; + return -1; + } +} + +#define sysconf(name) fy_win32_sysconf(name) + +/* + * asprintf() implementation for Windows + */ +#ifndef HAVE_ASPRINTF + +static inline int fy_win32_vasprintf(char **strp, const char *fmt, va_list ap) +{ + int len; + va_list ap_copy; + + va_copy(ap_copy, ap); + len = _vscprintf(fmt, ap_copy); + va_end(ap_copy); + + if (len < 0) { + *strp = NULL; + return -1; + } + + *strp = (char *)malloc(len + 1); + if (!*strp) + return -1; + + len = vsprintf(*strp, fmt, ap); + if (len < 0) { + free(*strp); + *strp = NULL; + return -1; + } + + return len; +} + +static inline int fy_win32_asprintf(char **strp, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = fy_win32_vasprintf(strp, fmt, ap); + va_end(ap); + + return ret; +} + +#define asprintf fy_win32_asprintf +#define vasprintf fy_win32_vasprintf + +#endif /* HAVE_ASPRINTF */ + +/* + * Directory functions + */ +#ifndef getcwd +#define getcwd _getcwd +#endif +#ifndef mkdir +#define mkdir(path, mode) _mkdir(path) +#endif +#ifndef rmdir +#define rmdir _rmdir +#endif +#ifndef chdir +#define chdir _chdir +#endif + +/* + * Process functions + */ +#ifndef getpid +#define getpid _getpid +#endif + +/* + * String functions + */ +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif +#ifndef strncasecmp +#define strncasecmp _strnicmp +#endif +#ifndef strdup +#define strdup _strdup +#endif + +/* + * Sleep functions + */ +static inline unsigned int fy_win32_sleep(unsigned int seconds) +{ + Sleep(seconds * 1000); + return 0; +} + +static inline int fy_win32_usleep(unsigned int usec) +{ + HANDLE timer; + LARGE_INTEGER ft; + + ft.QuadPart = -(10 * (LONGLONG)usec); + timer = CreateWaitableTimer(NULL, TRUE, NULL); + if (!timer) + return -1; + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); + return 0; +} + +#define sleep(s) fy_win32_sleep(s) +#define usleep(us) fy_win32_usleep(us) + +/* + * clock_gettime emulation for Windows + */ +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +/* + * struct timespec is defined in UCRT's time.h for MSVC 2015+ (VS2015+) + * time.h is included above to ensure it's available. + */ + +static inline int fy_win32_clock_gettime(int clock_id, struct timespec *tp) +{ + LARGE_INTEGER freq, count; + FILETIME ft; + ULARGE_INTEGER uli; + + if (clock_id == CLOCK_MONOTONIC) { + if (!QueryPerformanceFrequency(&freq)) + return -1; + if (!QueryPerformanceCounter(&count)) + return -1; + tp->tv_sec = (time_t)(count.QuadPart / freq.QuadPart); + tp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * 1000000000LL / freq.QuadPart); + } else { + /* CLOCK_REALTIME */ + GetSystemTimeAsFileTime(&ft); + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + /* FILETIME is 100-nanosecond intervals since January 1, 1601 */ + /* Convert to Unix epoch (seconds since January 1, 1970) */ + uli.QuadPart -= 116444736000000000ULL; + tp->tv_sec = (time_t)(uli.QuadPart / 10000000ULL); + tp->tv_nsec = (long)((uli.QuadPart % 10000000ULL) * 100); + } + return 0; +} + +#define clock_gettime(clock_id, tp) fy_win32_clock_gettime(clock_id, tp) + +/* + * EAGAIN may be different from EWOULDBLOCK on some systems + */ +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +/* + * alloca is available as _alloca on Windows + */ +#ifndef alloca +#define alloca _alloca +#endif + +/* + * iovec structure for scatter/gather I/O + * Defined here since sys/uio.h doesn't exist on Windows + */ +#ifndef _FY_IOVEC_DEFINED +#define _FY_IOVEC_DEFINED +struct iovec { + void *iov_base; /* Starting address */ + size_t iov_len; /* Number of bytes to transfer */ +}; +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +typedef union { + void* p; + double d; + __int64 i64; +} max_align_t; +#endif + +#endif /* _WIN32 */ + +#endif /* FY_WIN32_H */ diff --git a/src/valgrind/fy-valgrind.h b/src/valgrind/fy-valgrind.h index 36a64bd..09699ee 100644 --- a/src/valgrind/fy-valgrind.h +++ b/src/valgrind/fy-valgrind.h @@ -13,6 +13,17 @@ #include "config.h" #endif +/* Valgrind is not available on Windows */ +#ifdef _WIN32 + +static inline void fy_valgrind_check(int *argcp, char ***argvp) +{ + (void)argcp; + (void)argvp; +} + +#else /* !_WIN32 */ + #include #include #include @@ -163,4 +174,6 @@ do_valgrind_no_opt: fprintf(stderr, "warning: failed to start valgrind, continue without"); } -#endif +#endif /* !_WIN32 */ + +#endif /* FY_VALGRIND_H */ diff --git a/src/xxhash/xxhash.c b/src/xxhash/xxhash.c index 892c601..2a61c44 100644 --- a/src/xxhash/xxhash.c +++ b/src/xxhash/xxhash.c @@ -1623,9 +1623,9 @@ void XXH128_digest (const XXH128_state_t* state_in, void* out) XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH128_digest_endian(state_in, XXH_littleEndian, (unsigned long long*)out); + XXH128_digest_endian(state_in, XXH_littleEndian, (unsigned long long*)out); else - return XXH128_digest_endian(state_in, XXH_bigEndian, (unsigned long long*)out); + XXH128_digest_endian(state_in, XXH_bigEndian, (unsigned long long*)out); } @@ -1875,7 +1875,7 @@ void XXH256_digest (const XXH256_state_t* state_in, void* out) XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH256_digest_endian(state_in, XXH_littleEndian, (unsigned long long*)out); + XXH256_digest_endian(state_in, XXH_littleEndian, (unsigned long long*)out); else - return XXH256_digest_endian(state_in, XXH_bigEndian, (unsigned long long*)out); + XXH256_digest_endian(state_in, XXH_bigEndian, (unsigned long long*)out); } diff --git a/test/Makefile.am b/test/Makefile.am index 6da175b..31f1021 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -120,53 +120,21 @@ endif # !CROSS_COMPILING EXTRA_DIST = test-env $(TESTS) -# Add the error test files -EXTRA_DIST += \ - test-errors/0002/=== \ - test-errors/0002/in.yaml \ - test-errors/0002/test.error \ - test-errors/0003/=== \ - test-errors/0003/in.yaml \ - test-errors/0003/test.error \ - test-errors/0004/=== \ - test-errors/0004/in.yaml \ - test-errors/0004/test.error \ - test-errors/0005/=== \ - test-errors/0005/in.yaml \ - test-errors/0005/test.error \ - test-errors/0006/=== \ - test-errors/0006/in.yaml \ - test-errors/0006/test.error \ - test-errors/0007/=== \ - test-errors/0007/in.yaml \ - test-errors/0007/test.error \ - test-errors/0008/=== \ - test-errors/0008/in.yaml \ - test-errors/0008/test.error \ - test-errors/0009/=== \ - test-errors/0009/in.yaml \ - test-errors/0009/test.error \ - test-errors/0010/=== \ - test-errors/0010/in.yaml \ - test-errors/0010/test.error \ - test-errors/0011/=== \ - test-errors/0011/in.yaml \ - test-errors/0011/test.error - -# Add the emitter test files +# Add everyting EXTRA_DIST += \ emitter-examples/anchors-1.yaml \ emitter-examples/anchors-2.yaml \ emitter-examples/anchors-3.yaml \ emitter-examples/anchors-4.1.yaml \ emitter-examples/anchors-4.yaml \ + emitter-examples/anchors-on-empty-scalars.yaml \ emitter-examples/anchors-on-empty-scalars1.yaml \ emitter-examples/anchors-on-empty-scalars2.yaml \ emitter-examples/anchors-on-empty-scalars3.yaml \ emitter-examples/anchors-on-empty-scalars4.yaml \ - emitter-examples/anchors-on-empty-scalars.yaml \ emitter-examples/anchors.yaml \ emitter-examples/array.yaml \ + emitter-examples/block.yaml \ emitter-examples/block2.yaml \ emitter-examples/block3.yaml \ emitter-examples/block4.yaml \ @@ -174,12 +142,11 @@ EXTRA_DIST += \ emitter-examples/block7.yaml \ emitter-examples/blocked.yaml \ emitter-examples/blockind.yaml \ - emitter-examples/block.yaml \ + emitter-examples/c1.yaml \ emitter-examples/c10.yaml \ emitter-examples/c11.yaml \ emitter-examples/c12.yaml \ emitter-examples/c13.yaml \ - emitter-examples/c1.yaml \ emitter-examples/c2.yaml \ emitter-examples/c3.yaml \ emitter-examples/c4.yaml \ @@ -190,6 +157,7 @@ EXTRA_DIST += \ emitter-examples/c9.yaml \ emitter-examples/compact1.yaml \ emitter-examples/compactblockmap.yaml \ + emitter-examples/complexkey.yaml \ emitter-examples/complexkey2.yaml \ emitter-examples/complexkey3.yaml \ emitter-examples/complexkey4.yaml \ @@ -198,7 +166,6 @@ EXTRA_DIST += \ emitter-examples/complexkey7.yaml \ emitter-examples/complexkey8.yaml \ emitter-examples/complexkey9.yaml \ - emitter-examples/complexkey.yaml \ emitter-examples/docstartend.yaml \ emitter-examples/dqscalar.yaml \ emitter-examples/dqzero.yaml \ @@ -206,54 +173,57 @@ EXTRA_DIST += \ emitter-examples/emptydoc.yaml \ emitter-examples/emptykey.yaml \ emitter-examples/emptystream.yaml \ + emitter-examples/flow.yaml \ emitter-examples/flow1.yaml \ emitter-examples/flow2.yaml \ - emitter-examples/flow.yaml \ + emitter-examples/fold-tricky.yaml \ + emitter-examples/fold.yaml \ emitter-examples/fold2.yaml \ emitter-examples/fold3.yaml \ emitter-examples/fold4.yaml \ emitter-examples/fold5.yaml \ - emitter-examples/folded2.yaml \ + emitter-examples/folded-endbreaks.yaml \ emitter-examples/folded.yaml \ + emitter-examples/folded2.yaml \ emitter-examples/folding.yaml \ - emitter-examples/fold.yaml \ emitter-examples/global-tag.yaml \ emitter-examples/invoice.yaml \ emitter-examples/json.yaml \ emitter-examples/keyflow.yaml \ - emitter-examples/keykey2.yaml \ emitter-examples/keykey.yaml \ + emitter-examples/keykey2.yaml \ emitter-examples/line.yaml \ + emitter-examples/literal-endbreaks.yaml \ + emitter-examples/literal.yaml \ emitter-examples/literal1.yaml \ emitter-examples/literal2.yaml \ emitter-examples/literal3.yaml \ emitter-examples/literal4.yaml \ - emitter-examples/literal.yaml \ emitter-examples/mapping.yaml \ emitter-examples/mergekeyspec.yaml \ emitter-examples/multi-document.yaml \ emitter-examples/multiline-quoted-key.yaml \ emitter-examples/multiline-simple-key.yaml \ - emitter-examples/nodeprop2.yaml \ emitter-examples/nodeprop.yaml \ + emitter-examples/nodeprop2.yaml \ emitter-examples/numbers-flow.yaml \ emitter-examples/numbers.yaml \ - emitter-examples/plainlines.yaml \ emitter-examples/plain-scalars-with-commas.yaml \ + emitter-examples/plainlines.yaml \ emitter-examples/plainscalar.yaml \ - emitter-examples/quotedbackslash.yaml \ emitter-examples/quoted.yaml \ + emitter-examples/quotedbackslash.yaml \ emitter-examples/scalar-multiline.yaml \ - emitter-examples/scalars2.yaml \ emitter-examples/scalar-singlequoted.yaml \ - emitter-examples/scalar-space1.yaml \ emitter-examples/scalar-space.yaml \ + emitter-examples/scalar-space1.yaml \ emitter-examples/scalars.yaml \ + emitter-examples/scalars2.yaml \ + emitter-examples/scanner-c-1.yaml \ emitter-examples/scanner-c-10.yaml \ emitter-examples/scanner-c-11.yaml \ emitter-examples/scanner-c-12.yaml \ emitter-examples/scanner-c-13.yaml \ - emitter-examples/scanner-c-1.yaml \ emitter-examples/scanner-c-2.yaml \ emitter-examples/scanner-c-3.yaml \ emitter-examples/scanner-c-4.yaml \ @@ -263,37 +233,38 @@ EXTRA_DIST += \ emitter-examples/scanner-c-8-2.yaml \ emitter-examples/scanner-c-8.yaml \ emitter-examples/scanner-c-9.yaml \ + emitter-examples/seq.yaml \ emitter-examples/seq1.yaml \ emitter-examples/seq2.yaml \ emitter-examples/seq3.yaml \ emitter-examples/seq4.yaml \ emitter-examples/seq5.yaml \ emitter-examples/seq6.yaml \ - emitter-examples/seq.yaml \ emitter-examples/sets.yaml \ + emitter-examples/simple.yaml \ emitter-examples/simple1.yaml \ emitter-examples/simple2.yaml \ + emitter-examples/simpleanchor.yaml \ emitter-examples/simpleanchor1.yaml \ emitter-examples/simpleanchor2.yaml \ emitter-examples/simpleanchor3.yaml \ emitter-examples/simpleanchor4.yaml \ - emitter-examples/simpleanchor.yaml \ emitter-examples/simplefolded.yaml \ + emitter-examples/simplekey.yaml \ emitter-examples/simplekey1.yaml \ emitter-examples/simplekey2.yaml \ emitter-examples/simplekey3.yaml \ emitter-examples/simplekey4.yaml \ emitter-examples/simplekey5.yaml \ - emitter-examples/simplekey.yaml \ emitter-examples/simpleliteral.yaml \ - emitter-examples/simpleseq1.yaml \ emitter-examples/simpleseq.yaml \ - emitter-examples/simple.yaml \ - emitter-examples/singlepairimp2.yaml \ + emitter-examples/simpleseq1.yaml \ emitter-examples/singlepairimp.yaml \ - emitter-examples/sqscalarspace.yaml \ + emitter-examples/singlepairimp2.yaml \ emitter-examples/sqscalar.yaml \ + emitter-examples/sqscalarspace.yaml \ emitter-examples/strings.yaml \ + emitter-examples/t.yaml \ emitter-examples/t1.yaml \ emitter-examples/t2.yaml \ emitter-examples/t3.yaml \ @@ -304,26 +275,55 @@ EXTRA_DIST += \ emitter-examples/tagesc.yaml \ emitter-examples/tags-1.yaml \ emitter-examples/tags.yaml \ + emitter-examples/test.yaml \ emitter-examples/test1.yaml \ emitter-examples/test2.yaml \ - emitter-examples/test.yaml \ - emitter-examples/t.yaml \ + emitter-examples/u.yaml \ emitter-examples/u1.yaml \ emitter-examples/u2.yaml \ emitter-examples/u3.yaml \ emitter-examples/utf8-simple.yaml \ emitter-examples/utf8.yaml \ - emitter-examples/u.yaml \ + emitter-examples/v.yaml \ emitter-examples/v1.yaml \ emitter-examples/v2.yaml \ emitter-examples/version.yaml \ - emitter-examples/v.yaml \ emitter-examples/weirdplain.yaml \ emitter-examples/ws0.yaml \ emitter-examples/ws1.yaml \ emitter-examples/ws2.yaml \ emitter-examples/ws3.yaml \ - emitter-examples/yaml-version.yaml \ emitter-examples/y.yaml \ + emitter-examples/yaml-version.yaml \ emitter-examples/yy.yaml \ - emitter-examples/zeroexplicit.yaml + emitter-examples/zeroexplicit.yaml \ + test-errors/0002/=== \ + test-errors/0002/in.yaml \ + test-errors/0002/test.error \ + test-errors/0003/=== \ + test-errors/0003/in.yaml \ + test-errors/0003/test.error \ + test-errors/0004/=== \ + test-errors/0004/in.yaml \ + test-errors/0004/test.error \ + test-errors/0005/=== \ + test-errors/0005/in.yaml \ + test-errors/0005/test.error \ + test-errors/0006/=== \ + test-errors/0006/in.yaml \ + test-errors/0006/test.error \ + test-errors/0007/=== \ + test-errors/0007/in.yaml \ + test-errors/0007/test.error \ + test-errors/0008/=== \ + test-errors/0008/in.yaml \ + test-errors/0008/test.error \ + test-errors/0009/=== \ + test-errors/0009/in.yaml \ + test-errors/0009/test.error \ + test-errors/0010/=== \ + test-errors/0010/in.yaml \ + test-errors/0010/test.error \ + test-errors/0011/=== \ + test-errors/0011/in.yaml \ + test-errors/0011/test.error diff --git a/test/libfyaml-test-allocator.c b/test/libfyaml-test-allocator.c index b5435cb..3bec576 100644 --- a/test/libfyaml-test-allocator.c +++ b/test/libfyaml-test-allocator.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/libfyaml-test-core.c b/test/libfyaml-test-core.c index 47d8fac..f19e905 100644 --- a/test/libfyaml-test-core.c +++ b/test/libfyaml-test-core.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include @@ -1533,7 +1531,7 @@ START_TEST(manual_scalar_esc) { #undef MANUAL_SCALAR_ESC #undef MANUAL_SCALAR_ESC_TXT -#define MANUAL_SCALAR_ESC "\\\"\0\a\b\t\v\f\r\e\xc2\x85\xc2\xa0\xe2\x80\xa8\xe2\x80\xa9" +#define MANUAL_SCALAR_ESC "\\\"\0\a\b\t\v\f\r\x1b\xc2\x85\xc2\xa0\xe2\x80\xa8\xe2\x80\xa9" #define MANUAL_SCALAR_ESC_TXT "\"\\\\\\\"\\0\\a\\b\\t\\v\\f\\r\\e\\N\\_\\L\\P\"" const char *what = MANUAL_SCALAR_ESC; size_t what_sz = sizeof(MANUAL_SCALAR_ESC) - 1; @@ -2041,6 +2039,276 @@ START_TEST(token_test) } END_TEST +/* test style change on a plain */ +START_TEST(style_set_plain_scalar) +{ + struct fy_document *fyd; + struct fy_node *fyn; + enum fy_node_style style; + char *buf; + + fyd = fy_document_build_from_string(NULL, "foo\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + fprintf(stderr, "--------------\n"); + fprintf(stderr, "-- original --\n"); + fprintf(stderr, "--------------\n"); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + fprintf(stderr, "--------------\n"); + + fprintf(stderr, "-----------\n"); + fprintf(stderr, "-- plain --\n"); + fprintf(stderr, "-----------\n"); + style = fy_node_set_style(fyn, FYNS_PLAIN); + ck_assert(style == FYNS_PLAIN); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "foo\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- single quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED); + ck_assert(style == FYNS_SINGLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "'foo'\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- double quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED); + ck_assert(style == FYNS_DOUBLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "\"foo\"\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------\n"); + fprintf(stderr, "-- literal --\n"); + fprintf(stderr, "-------------\n"); + style = fy_node_set_style(fyn, FYNS_LITERAL); + ck_assert(style == FYNS_LITERAL); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "|-\n foo\n"); + fprintf(stderr, "%s", buf); + free(buf); + + /* NOTE we don't try folded, because we never support emitting one */ + + fy_document_destroy(fyd); +} +END_TEST + +/* test style change on a double quoted scalar which ends with colon */ +START_TEST(style_set_double_quoted_scalar_colon) +{ + struct fy_document *fyd; + struct fy_node *fyn; + enum fy_node_style style; + char *buf; + + /* simple double quoted scalar that can't be a plain */ + fyd = fy_document_build_from_string(NULL, "\"foo:\"\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + fprintf(stderr, "--------------\n"); + fprintf(stderr, "-- original --\n"); + fprintf(stderr, "--------------\n"); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + fprintf(stderr, "--------------\n"); + + /* we can't convert to a plain, it ends with : */ + fprintf(stderr, "-----------\n"); + fprintf(stderr, "-- plain --\n"); + fprintf(stderr, "-----------\n"); + style = fy_node_set_style(fyn, FYNS_PLAIN); + ck_assert(style != FYNS_PLAIN); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- single quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED); + ck_assert(style == FYNS_SINGLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "'foo:'\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- double quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED); + ck_assert(style == FYNS_DOUBLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "\"foo:\"\n"); + fprintf(stderr, "%s", buf); + free(buf); + + /* literal has no problem with it */ + fprintf(stderr, "-------------\n"); + fprintf(stderr, "-- literal --\n"); + fprintf(stderr, "-------------\n"); + style = fy_node_set_style(fyn, FYNS_LITERAL); + ck_assert(style == FYNS_LITERAL); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "|-\n foo:\n"); + fprintf(stderr, "%s", buf); + free(buf); + + /* NOTE we don't try folded, because we never support emitting one */ + + fy_document_destroy(fyd); +} +END_TEST + +/* test style change on a double quoted scalar which contains a \0 */ +START_TEST(style_set_double_quoted_scalar_0) +{ + struct fy_document *fyd; + struct fy_node *fyn; + enum fy_node_style style; + char *buf; + + /* simple double quoted scalar that can't be anything else */ + fyd = fy_document_build_from_string(NULL, "\"foo\\0\"\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + fprintf(stderr, "--------------\n"); + fprintf(stderr, "-- original --\n"); + fprintf(stderr, "--------------\n"); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + fprintf(stderr, "--------------\n"); + + /* we can't convert to a plain */ + fprintf(stderr, "-----------\n"); + fprintf(stderr, "-- plain --\n"); + fprintf(stderr, "-----------\n"); + style = fy_node_set_style(fyn, FYNS_PLAIN); + ck_assert(style != FYNS_PLAIN); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- single quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_SINGLE_QUOTED); + ck_assert(style != FYNS_SINGLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-------------------\n"); + fprintf(stderr, "-- double quoted --\n"); + fprintf(stderr, "-------------------\n"); + style = fy_node_set_style(fyn, FYNS_DOUBLE_QUOTED); + ck_assert(style == FYNS_DOUBLE_QUOTED); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "\"foo\\0\"\n"); + fprintf(stderr, "%s", buf); + free(buf); + + /* literal has no problem with it */ + fprintf(stderr, "-------------\n"); + fprintf(stderr, "-- literal --\n"); + fprintf(stderr, "-------------\n"); + style = fy_node_set_style(fyn, FYNS_LITERAL); + ck_assert(style != FYNS_LITERAL); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + + /* NOTE we don't try folded, because we never support emitting one */ + + fy_document_destroy(fyd); +} +END_TEST + +/* test style change on a collection */ +START_TEST(style_set_collection) +{ + struct fy_document *fyd; + struct fy_node *fyn; + enum fy_node_style style; + char *buf; + + fyd = fy_document_build_from_string(NULL, "- one\n- two\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + fprintf(stderr, "--------------\n"); + fprintf(stderr, "-- original --\n"); + fprintf(stderr, "--------------\n"); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + fprintf(stderr, "%s", buf); + free(buf); + fprintf(stderr, "--------------\n"); + + fprintf(stderr, "----------\n"); + fprintf(stderr, "-- flow --\n"); + fprintf(stderr, "----------\n"); + style = fy_node_set_style(fyn, FYNS_FLOW); + ck_assert(style == FYNS_FLOW); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "[\n one,\n two\n]\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fprintf(stderr, "-----------\n"); + fprintf(stderr, "-- block --\n"); + fprintf(stderr, "-----------\n"); + style = fy_node_set_style(fyn, FYNS_BLOCK); + ck_assert(style == FYNS_BLOCK); + buf = fy_emit_document_to_string(fyd, FYECF_DEFAULT); + ck_assert_ptr_ne(buf, NULL); + ck_assert_str_eq(buf, "- one\n- two\n"); + fprintf(stderr, "%s", buf); + free(buf); + + fy_document_destroy(fyd); +} +END_TEST + TCase *libfyaml_case_core(void) { TCase *tc; @@ -2113,5 +2381,10 @@ TCase *libfyaml_case_core(void) tcase_add_test(tc, token_test); + tcase_add_test(tc, style_set_plain_scalar); + tcase_add_test(tc, style_set_double_quoted_scalar_colon); + tcase_add_test(tc, style_set_double_quoted_scalar_0); + tcase_add_test(tc, style_set_collection); + return tc; } diff --git a/test/libfyaml-test-emit.c b/test/libfyaml-test-emit.c index 6b3e8f4..be0e16e 100644 --- a/test/libfyaml-test-emit.c +++ b/test/libfyaml-test-emit.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/libfyaml-test-meta.c b/test/libfyaml-test-meta.c index 7bf096b..d04b7cc 100644 --- a/test/libfyaml-test-meta.c +++ b/test/libfyaml-test-meta.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/libfyaml-test-parser.c b/test/libfyaml-test-parser.c index 13927bd..354cdb1 100644 --- a/test/libfyaml-test-parser.c +++ b/test/libfyaml-test-parser.c @@ -1017,8 +1017,8 @@ START_TEST(parser_comment_retrieval) struct fy_document_iterator *fydi; struct fy_node *fyn; struct fy_token *fyt; - char buf[256]; bool found_comment = false; + const char *comment; /* Build document with comments */ fyd = fy_document_build_from_string(&cfg, @@ -1043,9 +1043,10 @@ START_TEST(parser_comment_retrieval) /* Try to get comments at different placements */ for (int placement = fycp_top; placement < fycp_max; placement++) { - if (fy_token_get_comment(fyt, buf, sizeof(buf), placement)) { - ck_assert_ptr_ne(buf, NULL); - ck_assert_int_gt(strlen(buf), 0); + comment = fy_token_get_comment(fyt, placement); + if (comment) { + ck_assert_ptr_ne(comment, NULL); + ck_assert_int_gt(strlen(comment), 0); found_comment = true; } } @@ -2079,6 +2080,164 @@ START_TEST(parser_compose_callback) } END_TEST +START_TEST(parser_comments_0) +{ + struct fy_parse_cfg cfg; + struct fy_document *fyd; + struct fy_node *fyn; + const char *comment; + + memset(&cfg, 0, sizeof(cfg)); + cfg.flags = FYPCF_DEFAULT_DOC | FYPCF_PARSE_COMMENTS; + + /* root scalar right comment */ + fyd = fy_document_build_from_string(&cfg, "foo # right\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + /* no top */ + comment = fy_node_get_comment(fyn, fycp_top); + ck_assert_ptr_eq(comment, NULL); + + /* right */ + comment = fy_node_get_comment(fyn, fycp_right); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "right"), NULL); + + /* no bootm */ + comment = fy_node_get_comment(fyn, fycp_bottom); + ck_assert_ptr_eq(comment, NULL); + + /* all */ + comment = fy_node_get_comments(fyn); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "right"), NULL); + + fy_document_destroy(fyd); +} +END_TEST + +START_TEST(parser_comments_1) +{ + struct fy_parse_cfg cfg; + struct fy_document *fyd; + struct fy_node *fyn; + const char *comment; + + memset(&cfg, 0, sizeof(cfg)); + cfg.flags = FYPCF_DEFAULT_DOC | FYPCF_PARSE_COMMENTS; + + /* root scalar top & right comment */ + fyd = fy_document_build_from_string(&cfg, "# top\nfoo # right\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + /* top */ + comment = fy_node_get_comment(fyn, fycp_top); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top"), NULL); + ck_assert_ptr_eq(strstr(comment, "right"), NULL); + + /* right */ + comment = fy_node_get_comment(fyn, fycp_right); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_eq(strstr(comment, "top"), NULL); + ck_assert_ptr_ne(strstr(comment, "right"), NULL); + + /* no bootm */ + comment = fy_node_get_comment(fyn, fycp_bottom); + ck_assert_ptr_eq(comment, NULL); + + /* all */ + comment = fy_node_get_comments(fyn); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top"), NULL); + ck_assert_ptr_ne(strstr(comment, "right"), NULL); + + fy_document_destroy(fyd); +} +END_TEST + +START_TEST(parser_comments_2) +{ + struct fy_parse_cfg cfg; + struct fy_document *fyd; + struct fy_node *fyn; + const char *comment; + + memset(&cfg, 0, sizeof(cfg)); + cfg.flags = FYPCF_DEFAULT_DOC | FYPCF_PARSE_COMMENTS; + + /* root scalar right comment */ + fyd = fy_document_build_from_string(&cfg, "# top\n[]\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + /* top */ + comment = fy_node_get_comment(fyn, fycp_top); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top"), NULL); + + /* all */ + comment = fy_node_get_comments(fyn); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top"), NULL); + + fy_document_destroy(fyd); +} +END_TEST + +START_TEST(parser_comments_3) +{ + struct fy_parse_cfg cfg; + struct fy_document *fyd; + struct fy_node *fyn; + const char *comment; + + memset(&cfg, 0, sizeof(cfg)); + cfg.flags = FYPCF_DEFAULT_DOC | FYPCF_PARSE_COMMENTS; + + /* root scalar right comment */ + fyd = fy_document_build_from_string(&cfg, "# top seq\n- foo # right item\n", FY_NT); + ck_assert_ptr_ne(fyd, NULL); + + fyn = fy_document_root(fyd); + ck_assert_ptr_ne(fyn, NULL); + + /* top */ + comment = fy_node_get_comment(fyn, fycp_top); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top seq"), NULL); + + /* all */ + comment = fy_node_get_comments(fyn); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "top seq"), NULL); + + /* get the first item of the sequence */ + fyn = fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW); + ck_assert_ptr_ne(fyn, NULL); + + /* right */ + comment = fy_node_get_comment(fyn, fycp_right); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "right item"), NULL); + + /* all */ + comment = fy_node_get_comments(fyn); + ck_assert_ptr_ne(comment, NULL); + ck_assert_ptr_ne(strstr(comment, "right item"), NULL); + + fy_document_destroy(fyd); +} +END_TEST + TCase *libfyaml_case_parser(void) { TCase *tc; @@ -2161,5 +2320,11 @@ TCase *libfyaml_case_parser(void) /* Compose callback tests */ tcase_add_test(tc, parser_compose_callback); + /* Comment extraction tests */ + tcase_add_test(tc, parser_comments_0); + tcase_add_test(tc, parser_comments_1); + tcase_add_test(tc, parser_comments_2); + tcase_add_test(tc, parser_comments_3); + return tc; } diff --git a/test/libfyaml-test-private-id.c b/test/libfyaml-test-private-id.c index f943868..4d1f33b 100644 --- a/test/libfyaml-test-private-id.c +++ b/test/libfyaml-test-private-id.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #ifdef HAVE_ALLOCA_H diff --git a/test/libfyaml-test-private.c b/test/libfyaml-test-private.c index c428682..2cccd5f 100644 --- a/test/libfyaml-test-private.c +++ b/test/libfyaml-test-private.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/libfyaml-test-thread.c b/test/libfyaml-test-thread.c index 581b4df..dad405a 100644 --- a/test/libfyaml-test-thread.c +++ b/test/libfyaml-test-thread.c @@ -14,15 +14,13 @@ #include #include #include -#include -#include #include #include #include -#include +#include "fy-atomics.h" /* Test: Basic thread pool creation and destruction */ START_TEST(thread_pool_create_destroy) @@ -58,10 +56,10 @@ static void atomic_increment_worker(void *arg) int v, exp_v; /* Atomically increase the counter */ - v = atomic_load(p); + v = fy_atomic_load(p); for (;;) { exp_v = v; - if (atomic_compare_exchange_strong(p, &exp_v, v + 1)) + if (fy_atomic_compare_exchange_strong(p, &exp_v, v + 1)) return; v = exp_v; } @@ -104,7 +102,7 @@ START_TEST(thread_reserve_submit_wait) } /* Verify counter was incremented 4 times */ - ck_assert_int_eq(atomic_load(&counter), 4); + ck_assert_int_eq(fy_atomic_load(&counter), 4); /* Unreserve all threads */ for (i = 0; i < 4; i++) { @@ -132,10 +130,10 @@ START_TEST(thread_arg_join) ck_assert_ptr_ne(tp, NULL); /* Use arg_join to execute the same function multiple times */ - fy_thread_arg_join(tp, atomic_increment_worker, NULL, &counter, num_tasks); + fy_thread_arg_join(tp, atomic_increment_worker, NULL, (void *)&counter, num_tasks); /* Verify counter was incremented num_tasks times */ - ck_assert_int_eq(atomic_load(&counter), (int)num_tasks); + ck_assert_int_eq(fy_atomic_load(&counter), (int)num_tasks); fy_thread_pool_destroy(tp); } @@ -208,10 +206,10 @@ static void steal_mode_worker(void *arg) /* Increment counter 100 times */ for (i = 0; i < 100; i++) { - v = atomic_load(p); + v = fy_atomic_load(p); for (;;) { exp_v = v; - if (atomic_compare_exchange_strong(p, &exp_v, v + 1)) + if (fy_atomic_compare_exchange_strong(p, &exp_v, v + 1)) break; v = exp_v; } @@ -235,10 +233,10 @@ START_TEST(thread_steal_mode) ck_assert_ptr_ne(tp, NULL); /* Execute many tasks with work stealing enabled */ - fy_thread_arg_join(tp, steal_mode_worker, NULL, &counter, num_tasks); + fy_thread_arg_join(tp, steal_mode_worker, NULL, (void *)&counter, num_tasks); /* Verify all tasks completed: 16 tasks * 100 increments each */ - ck_assert_int_eq(atomic_load(&counter), (int)(num_tasks * 100)); + ck_assert_int_eq(fy_atomic_load(&counter), (int)(num_tasks * 100)); fy_thread_pool_destroy(tp); } diff --git a/test/libfyaml-test.c b/test/libfyaml-test.c index 0fbf83e..717fa22 100644 --- a/test/libfyaml-test.c +++ b/test/libfyaml-test.c @@ -11,8 +11,8 @@ #endif #include -#include #include +#include #include @@ -20,12 +20,6 @@ #define QUIET_DEFAULT false -static struct option lopts[] = { - {"quiet", no_argument, 0, 'q' }, - {"help", no_argument, 0, 'h' }, - {0, 0, 0, 0 }, -}; - static void display_usage(FILE *fp, char *progname) { fprintf(fp, "Usage: %s [options] [files]\n", progname); @@ -70,25 +64,24 @@ Suite *libfyaml_suite(void) int main(int argc, char *argv[]) { - int exitcode = EXIT_FAILURE, opt, lidx; + int exitcode = EXIT_FAILURE; bool quiet = QUIET_DEFAULT; - int number_failed; + int i, number_failed; Suite *s; SRunner *sr; fy_valgrind_check(&argc, &argv); - while ((opt = getopt_long(argc, argv, "qh", lopts, &lidx)) != -1) { - switch (opt) { - case 'q': + /* don't use getopt - it's not there in windows */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-q") || !strcmp(argv[1], "--quiet")) { quiet = true; - break; - case 'h' : - default: - if (opt != 'h') - fprintf(stderr, "Unknown option\n"); - display_usage(opt == 'h' ? stdout : stderr, argv[0]); + } else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + display_usage(stdout, argv[0]); return EXIT_SUCCESS; + } else { + display_usage(stderr, argv[0]); + return EXIT_FAILURE; } }