]> dgit.raspbian.org Git - libfyaml.git/commitdiff
New upstream version 0.9.4
authorTimo Röhling <roehling@debian.org>
Thu, 12 Feb 2026 16:40:11 +0000 (17:40 +0100)
committerTimo Röhling <roehling@debian.org>
Thu, 12 Feb 2026 16:40:11 +0000 (17:40 +0100)
99 files changed:
.github/workflows/cmake.yaml [new file with mode: 0644]
.libtool-version
.tarball-version
CHANGELOG.md
CMakeLists.txt
Makefile.am
README.md
cmake/MSVCCrossCompile.cmake [new file with mode: 0644]
cmake/add-tap-subtests.cmake
cmake/clang-windows-toolchain.cmake [new file with mode: 0644]
cmake/generate-def.cmake [new file with mode: 0644]
cmake/jsontestsuite-tests.cmake [deleted file]
cmake/run-single-tap-test.sh
cmake/setup-yaml-test-suite.cmake [deleted file]
cmake/testsuite-tests.cmake [deleted file]
doc/Makefile.am
doc/windows-support.txt [new file with mode: 0644]
include/libfyaml.h
src/Makefile.am
src/allocator/fy-allocator-dedup.c
src/allocator/fy-allocator-linear.c
src/allocator/fy-allocator-malloc.c
src/allocator/fy-allocator-mremap.c
src/allocator/fy-allocator.c
src/allocator/fy-allocator.h
src/blake3/blake3.c
src/blake3/blake3.h
src/blake3/blake3_backend.c
src/blake3/blake3_be_cpusimd.c
src/blake3/blake3_host_state.c
src/blake3/blake3_impl.h
src/blake3/blake3_internal.h
src/blake3/fy-blake3.c
src/getopt/LICENSE.getopt [new file with mode: 0644]
src/getopt/getopt.c [new file with mode: 0644]
src/getopt/getopt.h [new file with mode: 0644]
src/internal/fy-allocators.c
src/internal/fy-b3sum.c
src/internal/fy-thread.c
src/internal/libfyaml-parser.c
src/lib/fy-accel.c
src/lib/fy-atom.c
src/lib/fy-atom.h
src/lib/fy-composer-diag.c
src/lib/fy-diag.c
src/lib/fy-doc-diag.c
src/lib/fy-doc.c
src/lib/fy-doc.h
src/lib/fy-docbuilder-diag.c
src/lib/fy-docbuilder.c
src/lib/fy-docstate.c
src/lib/fy-docstate.h
src/lib/fy-dump.c
src/lib/fy-emit-accum.h
src/lib/fy-emit.c
src/lib/fy-emit.h
src/lib/fy-event.c
src/lib/fy-event.h
src/lib/fy-input-diag.c
src/lib/fy-input.c
src/lib/fy-input.h
src/lib/fy-parse-diag.c
src/lib/fy-parse.c
src/lib/fy-parse.h
src/lib/fy-token.c
src/lib/fy-token.h
src/lib/fy-types.c
src/lib/fy-walk.c
src/lib/fy-walk.h
src/thread/fy-thread.c
src/thread/fy-thread.h
src/tool/fy-tool-dump.c
src/tool/fy-tool-util.c
src/tool/fy-tool-util.h
src/tool/fy-tool.c
src/util/fy-align.h
src/util/fy-atomics.h
src/util/fy-bit64.h
src/util/fy-blob.c
src/util/fy-ctype.h
src/util/fy-endian.h
src/util/fy-id.h
src/util/fy-utf8.c
src/util/fy-utf8.h
src/util/fy-utils.c
src/util/fy-utils.h
src/util/fy-win32.h [new file with mode: 0644]
src/valgrind/fy-valgrind.h
src/xxhash/xxhash.c
test/Makefile.am
test/libfyaml-test-allocator.c
test/libfyaml-test-core.c
test/libfyaml-test-emit.c
test/libfyaml-test-meta.c
test/libfyaml-test-parser.c
test/libfyaml-test-private-id.c
test/libfyaml-test-private.c
test/libfyaml-test-thread.c
test/libfyaml-test.c

diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml
new file mode 100644 (file)
index 0000000..8cd5a6f
--- /dev/null
@@ -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
index 7e093fb232f19996a6c97fba267581542428e24a..3e95e1df6d40eb2cf600884348aad6ad0e8b9f1b 100644 (file)
@@ -1 +1 @@
-2:0:0
+3:0:1
index 965065db5b84c28a60e0e932153c4f59ac44c4da..a602fc9e283389306d606c164ce73ac338999373 100644 (file)
@@ -1 +1 @@
-0.9.3
+0.9.4
index ae5157ba54df21417acb99f6dfd7ea8bfa6a6b6c..473f22073dcbec01c138468c7b6be4882d4360ff 100644 (file)
@@ -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
index 135e3d23dcb7ba6a02f45f4a1487da700ad48672..dbcca1d03e20d2eb70cf3749f586769f7d381773 100644 (file)
@@ -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 <emmintrin.h>
         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 $<TARGET_OBJECTS:${blake3_target}>)
 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 $<TARGET_FILE:libfyaml-test>
             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()
index e593feae44b949d30c55c2b4d7f73ed98e1299bc..f79c6591f2225c4d2a2ff2fa85770c746db6b8ca 100644 (file)
@@ -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 \
index bdd7af2373ec82a302499b53c9c11524ac2259b7..c115d476b1593e795a504293ffbe2357541defd7 100644 (file)
--- 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 (file)
index 0000000..f60a5a1
--- /dev/null
@@ -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 "<CMAKE_LINKER> <LINK_FLAGS> <OBJECTS> -out:<TARGET> <LINK_LIBRARIES>" CACHE STRING "" FORCE)
+    set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <LINK_FLAGS> <OBJECTS> -out:<TARGET> <LINK_LIBRARIES>" CACHE STRING "" FORCE)
+    set(CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_LINKER> -dll <LINK_FLAGS> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> <LINK_LIBRARIES>" CACHE STRING "" FORCE)
+    set(CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_LINKER> -dll <LINK_FLAGS> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> <LINK_LIBRARIES>" 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")
index 638ee2c58b01041bcb250d07c6d4069d69ef8cc5..58cf8166da207b2acd6010e34161570468dd5f0f 100644 (file)
 # 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"
+            "$<TARGET_FILE:fy-tool>"
+            "${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 (file)
index 0000000..f243548
--- /dev/null
@@ -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 "<CMAKE_LINKER> <LINK_FLAGS> <OBJECTS> -out:<TARGET> <LINK_LIBRARIES>")
+set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <LINK_FLAGS> <OBJECTS> -out:<TARGET> <LINK_LIBRARIES>")
+
+set(CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_LINKER> -dll <LINK_FLAGS> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> <LINK_LIBRARIES>")
+set(CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_LINKER> -dll <LINK_FLAGS> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> <LINK_LIBRARIES>")
+
+# 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 (file)
index 0000000..a83911d
--- /dev/null
@@ -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 (file)
index 3737900..0000000
+++ /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"
-)
index 5382f5647ff812ee42edcfea52a718f0561ceb19..211b3d79477370bbb1b95fe597291cccff47c341 100755 (executable)
@@ -1,26 +1,67 @@
 #!/usr/bin/env bash
 
 # Wrapper script to run a single TAP subtest
-# Usage: run-single-tap-test.sh <test_suite_name> <test_id>
-
-test_suite="$1"
-test_id="$2"
+# Usage: run-single-tap-test.sh <fy_tool_path> <test_suite_name> <test_id>
+
+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 (file)
index c8c7f2b..0000000
+++ /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 (file)
index 284aead..0000000
+++ /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"
-)
index 9902908abca5b8fe218ab338a8570242b7e1e7df..170aef7c6d8a48163325f20a70666fe43e1cf314 100644 (file)
@@ -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 (file)
index 0000000..8820c98
--- /dev/null
@@ -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 ..
index 9a0acee1f965a27606d61eb043679362158ced0c..cc3ff1a071a4e0556f9ed5f1dfce0a53ca690bc1 100644 (file)
@@ -42,9 +42,28 @@ extern "C" {
 
 #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
 #include <unistd.h>
+#include <sys/uio.h>
+#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 <sys/uio.h>
+#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
  *
index 3d79cd5cdc0c9fd6d94c83fb7811dd9cb3bb8b2a..7debd9f49f0e081889af7ee3f68b6886c293e16e 100644 (file)
@@ -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
+
index e964fcfa2e0cfc8fb044fc7c47667d819fa919a7..4b56f3d6a93a714f0d5ae20b736ba284361688cc 100644 (file)
 /* 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 { \
        } 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;
 }
 
index f516f9e9454459b99446ef4483ac885b60f03bdd..7d84a9e6c5b0905bd8bf009d26207251b48cfa07 100644 (file)
@@ -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 = {
index 7c06185d969baf4f31a222661ca966a2f2d151b3..1b59f424f9a877d4a0468ea6d78da2a2af2216ab 100644 (file)
 #include <stdalign.h>
 #include <time.h>
 #include <inttypes.h>
-
 #include <stdio.h>
 
 /* 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;
                        }
index d94322105b9655a1838ccb4e422b431e87d2833f..c03f3328fd7a2a86510825fe7815a75f4581859f 100644 (file)
 #include <time.h>
 #include <inttypes.h>
 #include <math.h>
+#include <stdio.h>
+
+#ifndef _WIN32
 #include <sys/mman.h>
+#include <unistd.h>
+#endif
 
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
 #endif
 
-
-#include <stdio.h>
-
 /* 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;
        }
 
index 84fc0595a66c60196b6809918a28d9e51a79fce5..c8179f97d6ebf33320337482705a4cb54d96210e 100644 (file)
 
 #include <stdio.h>
 
+#ifndef _WIN32
 #include <sys/mman.h>
-
-#ifndef FY_NON_LOCKING_REGISTRY
 #include <pthread.h>
 #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);
 }
index b53fea0f2d2bc654f88150e9671e25bf9614dc14..84cbb5e69149407bee8ff27450dec7b99aeebd38 100644 (file)
 #include <stdbool.h>
 #include <assert.h>
 
+#ifndef _WIN32
 #include <sys/uio.h>
+#endif
 
 #include "fy-typelist.h"
 #include "fy-utils.h"
+#include "fy-win32.h"
 
 #include <libfyaml.h>
 
@@ -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);
 };
 
index 65983a23ed932831a58aaa3aa9ebaf9ca006543d..f10de40c27cc2da64cc4719266915cd9c84967c4 100644 (file)
 #include <alloca.h>
 #endif
 
+#ifndef _WIN32
 #include <pthread.h>
-#include <stdlib.h>
+#endif
 
 #include "blake3.h"
 #include "blake3_impl.h"
 #include "blake3_internal.h"
 
+#include "fy-win32.h"
 #include "fy-thread.h"
 
 // GCC gets confused sometimes
index e13d1141b3e6d12cb1a71b4ab92078910738c407..02579cf04b5158fa74c0531bda5c9c198d487174 100644 (file)
@@ -7,7 +7,6 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <pthread.h>
 #include <stdalign.h>
 
 #define BLAKE3_VERSION_STRING "1.4.1"
index 4f00dd66c8656abcc6fec92be9bd8fe2b30e55fb..faef985bdf70685f3607a6439bf34ff651672a7e 100644 (file)
@@ -7,12 +7,7 @@
 #include <string.h>
 #include <stdio.h>
 
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
 #include <stdlib.h>
-#include <unistd.h>
 
 #include "fy-bit64.h"
 
index 8a00951079234dca664678ac810b3e74c26dc7d5..b504bc8f9a27970dc11fdefc041b48423c140990 100644 (file)
 #endif
 
 #include <stdlib.h>
-#include <unistd.h>
 
 #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;
index 4699fa370ffe6550d3c33b97e9c7407cb4d02757..7ab02b9b5a02b77cba14cc220829f9c96efddabb 100644 (file)
@@ -6,19 +6,21 @@
 #include <stdbool.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
 
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
 #endif
 
+#ifndef _WIN32
 #include <pthread.h>
-#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#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 {
index 230dc49dd615112ee79a69d0b46904df182cbcca..a66e98f9c0fcb1c23ae3e7863ba4c918e30d3ef1 100644 (file)
 #include <string.h>
 #include <stdatomic.h>
 #include <stdalign.h>
-
-#ifndef _WIN32
 #include <stdlib.h>
-#if defined(__linux__)
-#include <malloc.h>
-#endif
-#endif
 
 #if defined(__linux__)
+#include <malloc.h>
 #include <sys/sysmacros.h>
 #endif
 
 
 #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 <intrin.h>
 #endif
 #endif
+#endif
 
 /* Find index of the highest set bit */
 /* x is assumed to be nonzero.       */
index 2bfa42e8ac9a5d417b7d3fd2f8607c106cb376f4..39bbf4a09ab606a6290164e4fe5088087e2319a9 100644 (file)
@@ -7,8 +7,6 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <pthread.h>
-#include <stdatomic.h>
 
 #include "blake3.h"
 
index e14234d092f961e0e69a6fc6dd5587d9b94391b8..22142658a09a03e8f870cc7056b697194a0d8288 100644 (file)
@@ -6,10 +6,6 @@
 #include <stdbool.h>
 #include <stdint.h>
 
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
 #include <stdlib.h>
 #include <errno.h>
 
diff --git a/src/getopt/LICENSE.getopt b/src/getopt/LICENSE.getopt
new file mode 100644 (file)
index 0000000..22dd2f3
--- /dev/null
@@ -0,0 +1,24 @@
+Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
+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 (file)
index 0000000..583ae61
--- /dev/null
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
+ * 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 <stddef.h>
+#include <string.h>
+
+#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 (file)
index 0000000..166ab26
--- /dev/null
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
+ * 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
index 79e83e2e1925e0e2ef2aa59bcce68998853d5e5c..710235743c258d66ee53d2872bba490d6de5fb54 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <assert.h>
+#include <time.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdalign.h>
+
+#ifndef _WIN32
 #include <unistd.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
+#else
+#include "fy-win32.h"
 #endif
 
-#include <stdbool.h>
 #include <getopt.h>
-#include <ctype.h>
-#include <assert.h>
-#include <time.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdalign.h>
 
 #include <libfyaml.h>
 
index 9e81e42bf099581dd5e0cf6336150fda471ac2f5..e88fd6c726407ae978dd1a45f3c63b303b8fc764 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <assert.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#ifndef _WIN32
 #include <unistd.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
+#else
+#include "fy-win32.h"
 #endif
 
-#include <stdbool.h>
 #include <getopt.h>
-#include <ctype.h>
-#include <assert.h>
 
-#include <stdatomic.h>
 #include <blake3.h>
 
 #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);
index ac21563170a0c2c7bde64eea0cd075bcb1b6fe0c..2913dd4fba6781895cc8ac174315b03c5a5e56ab 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
 #endif
 
 #include <stdbool.h>
-#include <getopt.h>
 #include <ctype.h>
 #include <assert.h>
 #include <time.h>
 #include <stdint.h>
 #include <inttypes.h>
 
-#include <stdatomic.h>
+#ifndef _WIN32
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#else
+#include "fy-win32.h"
+#endif
+
+#include <getopt.h>
 
 #include <libfyaml.h>
 
 #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);
 }
 
index 8d0fb8df821d711761c5a1c9cd27c988fc4f064f..5373da82e2a7611993df10a11d7923145aeed3fa 100644 (file)
 #include <time.h>
 #include <inttypes.h>
 
-#include <libfyaml.h>
+#include <getopt.h>
+
+#ifndef _WIN32
+#include "fy-win32.h"
+#endif
 
 #if defined(HAVE_LIBYAML) && HAVE_LIBYAML
 #include <yaml.h>
 #endif
 
+#include <libfyaml.h>
+
 #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 : "<NULL>");
 
                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) {
index 87a9b238aca7c8f11b2d7538cbb4b95aa5be73fd..de5b981cc4b3b5d8f2d81848d12d2b57096ea98d 100644 (file)
@@ -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)
index 4d554fedd71dbce0222c247b7841b59bcd30b00c..22515dc46645eaee8a2c894e508db63615527662 100644 (file)
@@ -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;
+}
index 3f32275c936a0801c7f389ce9d074cd2ff2c26d9..e7fc76eff8fb1c9e80903a1dd05204938691a583 100644 (file)
@@ -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
index bc75b240bf13be66177e8d164ccfb172e1b12cf5..7bdb5a55a112f50d8b6a02d39da4159b2583b576 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
@@ -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 */
index 58a6dadd5e0c97474a2ecf4e4182034c24bad1cb..1f66f0358bb1648cbcdf9aa679d01584e4249d2e 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
@@ -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);
 
index 04247e389cfe87f28adba3e54eb2843eb735f880..53c9bf10ef720c3eb6bb7b21484e3555f83582d8 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
@@ -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 */
index c3b10ff1a2494eaf5e6cb572ada875513f10acfb..9c05bfba1c991a21717d6822768aaf5642407ca7 100644 (file)
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <libfyaml.h>
@@ -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);
+}
index c9c46c910773cb5523f6a3a1e84f6163a0dd84e2..e24be5be2fcd2c0565a757c95ad58c93df414a06 100644 (file)
@@ -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)
index 841469f9f27df6b5ee0e529f4a12461603e3072e..ef25257b1f092b1cc2ca95cacc1bc4d0d0b79132 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
@@ -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 */
index f4a8d09ea3e552fba1b0c2954fcab3bdf4ce3e68..8ebadfa90b8868941b98d6afc561323be9b236b0 100644 (file)
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
-#include <unistd.h>
 
 #include <libfyaml.h>
 
index 6c530bb99a7bea54e767797af868db0cb81f70b2..4071e5765d504d54a1075edcd7c42b176fe54ef3 100644 (file)
@@ -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)
index eb8df53b721bd1b409a823348f907b20ed8f54be..98cc1960a2b75a20d3a16b3445dcba93cf000e87 100644 (file)
@@ -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 */
 };
index 17a75437436770094bf9fff9e7bc7165ca7e7d1c..84f9702dec4d2dad47247e0ad4bd818e44e6f741 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
index fd9bb679fd1ed9739d12841f48fc22314bbb431b..e46ffa5ca39fa1d9f50bc1e298e5929290a53ceb 100644 (file)
@@ -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;
index cf1ef9176cbdc1c86ca4a7a9b9dab72a628f95cb..3b37d7c06175c89cc1a37c2a719a042249b564e0 100644 (file)
@@ -14,7 +14,6 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <limits.h>
-#include <unistd.h>
 #include <ctype.h>
 #include <errno.h>
 
@@ -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));
index 9269c6232b8508921dd31870a913ac51695ab59f..be64fc844bd72a6ac40c94f40870d40bff03a575 100644 (file)
@@ -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)
 {
index f0427138b37cba6391058ac65bb29b4ab9e8f921..fc8723ad20ecdda7deee48cac9228368d4f0c50b 100644 (file)
@@ -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);
+}
index 90f8241525709a70302b2cbc2dfb0adc390a8d58..e5ff7cca732ad10d0b80f85473f529f9cec71ccb 100644 (file)
@@ -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);
index 46baf03e07adae2782982ffe91157b0aaf7b98b7..d5270d6ed2ca5b29b61cfbc20fbd35a6422446c4 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
index da7665d66f52ae25bfcea4fa3d76b32f90c7020a..39bd06051b3c0e09db52f9286682fd0d5bdf392b 100644 (file)
 #include <assert.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <unistd.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/mman.h>
 #include <sys/ioctl.h>
-#include <limits.h>
-#include <errno.h>
+#endif
 
 #include <libfyaml.h>
 
+#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, "<memory-@%p-%p>",
-                       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, "<alloc-@%p-%p>",
-                       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;
index a401af2255df54ab7038013dddc161bfaa2d3985..8bcf998c41de5332e352d38a3b492562f565c4df 100644 (file)
@@ -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
index a108312a815850b73ea11c28e248b72a19e1b235..1007a8331aa725559f40fe0652a7a31dd2030667 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <ctype.h>
 
 #include <libfyaml.h>
index 830e11bf10e4d7533e7c9b561052973709345c5d..ae09bdc1a11f60364a8544db81d138cd4124786d 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <limits.h>
+#include <fcntl.h>
 
 #include <libfyaml.h>
 
+#include "fy-win32.h"
 #include "fy-parse.h"
 
 #include "fy-utils.h"
 #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, "<NULL>");
-               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 ? : "<stream>";
+       fyic.stream.name = name ? name : "<stream>";
        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);
index d269f58a2e9fb02e9ae4f83add5850c07c09b194..8aca4c7ca1ecc4d359b664da9f0e375cd2b01f6a 100644 (file)
@@ -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
index 58f831578bc465a8b6dc99923ca4d1410b78215e..50142981f9a8eda39898fdb21af4c7ee97f2d722 100644 (file)
@@ -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;
index c0a76f5d7a5f5d2f63a62add18a6bad49f0e747b..0dfbc044e7be8e8eed374da35f26e4a5a1c414c2 100644 (file)
@@ -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);
index 1649e04c15b9cb02d7c1469c264da0b2bdbae103..9a064ed75ef81e23a03bf88516eecbcc534890be 100644 (file)
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <errno.h>
index 8c83d9c6163b9b24cacefb9fcba2bd852fece0df..b539e151fdd0b437f0ee2cb96466e72c34dcf5fe 100644 (file)
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
-#include <unistd.h>
 #include <math.h>
 #include <limits.h>
 
 #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,
index 3697e9c8367974eb41fd74dd2299027323813f8d..3eefef9958d3807e76cdb2660695dc2aa25e108b 100644 (file)
@@ -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)
index 08b7771fc1d0e7c4a73774aa327895efa730c9b1..03c3153943a8e1832e91675ada105a9fef9214da 100644 (file)
 #include <alloca.h>
 #endif
 
-#include <pthread.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <errno.h>
 #include <limits.h>
 
+#ifndef _WIN32
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
 #if defined(__linux__)
 #include <sys/syscall.h>
 #include <linux/futex.h>
 #endif
 
+#include "fy-win32.h"
 #include "fy-diag.h"
 #include "fy-utils.h"
 #include "fy-thread.h"
 #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)
        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;
                        }
index 8b18025b2ea6b893702a93ad91b7be690601d314..ed16c9b30324536cb8dc33406b7782ce75ba0cda 100644 (file)
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
-#include <stdatomic.h>
-#include <pthread.h>
 
+#ifndef _WIN32
+#include <pthread.h>
 #ifndef __APPLE__
 #include <semaphore.h>
 #else
 #include <dispatch/dispatch.h>
 #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 */
index a66a4f49d23312ef12417d4b0245ef013ac3d7d2..a54adb8cb742be6adf85aa6c299c51a82c365f27 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
 #include <ctype.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <regex.h>
 #include <stdalign.h>
 #include <inttypes.h>
 #include <float.h>
 #include <limits.h>
 #include <math.h>
+#include <errno.h>
+#include <fcntl.h>
 
 #include <libfyaml.h>
 
@@ -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);
index a3eac5d51a753ece90ea1d06a3a543075417d7dd..d237434543ef3b533ab58548bcdd5eba0f24fffa 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
 #include <ctype.h>
-#include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <regex.h>
 #include <stdalign.h>
 #include <inttypes.h>
 #include <float.h>
@@ -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;
index ca3428797987c53ad2416eec1504e45be1b85835..1094e446bca7c80776bdd4c51b052902d65da962 100644 (file)
 
 #include <stdio.h>
 #include <stdbool.h>
+#ifdef _WIN32
+#include "fy-win32.h"
+#include <io.h>
+#else
 #include <unistd.h>
 #include <termios.h>
+#endif
 #include <stdint.h>
 #include <string.h>
 
 #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
 
     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) \
 ({ \
        __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) \
 ({ \
        __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) \
 ({ \
        __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);
 }
 
index 3529e4f32718e8c689e78f5ec5e0ff3c987887b4..99fe401c924d1e09624dda3afa6931c403ab59be 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
 #include <ctype.h>
-#include <unistd.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <regex.h>
 #include <stdalign.h>
 #include <inttypes.h>
 #include <float.h>
 
 #include <libfyaml.h>
 
+#ifndef _WIN32
+#include <unistd.h>
+#include <regex.h>
+#else
+#include "fy-win32.h"
+#endif
+
+#include <getopt.h>
+
 #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;
index 1e7e180b5c8144dc7e930e6ada5f50a7fb309735..71b012f9206b53f3f0e3bf4fd66e4f54782d6820 100644 (file)
@@ -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
 #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)
index bfc177f24f83234b4c370daa2431fc1e5786ac51..099bc48f3c902a1ffa82657740e480ccf0b9df66 100644 (file)
@@ -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
index 48e2c4ae5362322d7be96469f7cfff36b2051125..dac17af8847a5b46bdf24e77a925ae7425f289fa 100644 (file)
@@ -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 <intrin.h>
+
+#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)
 
index 9ed73a99c3f69cecbb2c817914f952776b3da685..e8ec2b9cbf3660db40b4822422075cd344d420c0 100644 (file)
@@ -9,12 +9,16 @@
 #include "config.h"
 #endif
 
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <fcntl.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
 
+#include "fy-win32.h"
 #include "fy-blob.h"
 
 /* define optimized methods for probing */
index 7c207913aef477dada638020ad19be568930d55e..879b00d5017d12947387195589e4bbe55f5e8794 100644 (file)
@@ -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
index 5778037f49fff8917f36725e7b27831345854e0f..0b6fa03822f3fa590447622a5e4ca5af1f2ce376 100644 (file)
@@ -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 <endian.h>
 #elif defined(__APPLE__)
 # include <libkern/OSByteOrder.h>
 # include <machine/endian.h>
 #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
 # include <sys/endian.h>
-#elif defined(_MSC_VER)
-# include <winsock2.h>
-# ifdef __GNUC__
-#  include <sys/param.h>
-# 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
index aae5b9fb1e583663324292d22a7c8901d58f24f9..f692aaf69319b3c9038aba04f0e23fb49c0249b8 100644 (file)
@@ -9,10 +9,8 @@
 #define FY_ID_H
 
 #include <stdlib.h>
-#include <strings.h>
 #include <string.h>
 #include <assert.h>
-
 #include <stdint.h>
 
 #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;
index 228879a73c16eefbf4125c9ecb3aa7ac7784c6cc..218509f08336ddd84c16507be3a2ca63fd37ea5c 100644 (file)
@@ -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 */
index 9a40af7eaa7ac4d24ab94daa4c927cc46ae16da3..5f9321ace1c4ada76298d5c591fdcd8f4f7be697 100644 (file)
@@ -20,7 +20,7 @@
 #include <alloca.h>
 #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;
index 40ce77b33f6c69957ed8564fc66eb37531fe1d4f..46dcf177fcfed02e24d78635d4879267bb109211 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <ctype.h>
+
+#ifndef _WIN32
 #include <termios.h>
 #include <unistd.h>
 #include <sys/select.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <ctype.h>
+#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;
+}
index 8e5fa4e9fcdbcbb2d5f291b21505d26aae50c505..e7ac43c2c1505e1c49eb754a73c3296c77ac7429 100644 (file)
 #include <stdio.h>
 #include <stdbool.h>
 #include <stddef.h>
-#include <unistd.h>
-#include <termios.h>
 #include <stdint.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <termios.h>
 #include <sys/uio.h>
+#endif
+
+#include "fy-win32.h"
 
 /*
  * Define FY_CPP_SHORT_NAMES to use shortened macro names in the implementation.
 #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 <sys/sysmacros.h>
 #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 (file)
index 0000000..f190c2f
--- /dev/null
@@ -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 <windows.h>
+/* Undefine Windows macros that conflict with common identifiers */
+#ifdef near
+#undef near
+#endif
+#ifdef far
+#undef far
+#endif
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <direct.h>
+#include <process.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+#include <malloc.h>
+
+/* 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 */
index 36a64bd75dc04a9779edf175a642c7a073977a41..09699eeb83894204667e5efe591980453faf1ced 100644 (file)
 #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 <stdlib.h>
 #include <string.h>
 #include <assert.h>
@@ -163,4 +174,6 @@ do_valgrind_no_opt:
        fprintf(stderr, "warning: failed to start valgrind, continue without");
 }
 
-#endif
+#endif /* !_WIN32 */
+
+#endif /* FY_VALGRIND_H */
index 892c6015b7c53c017c13a187bbe69c3dd755f6c9..2a61c445fb82884b3c876b003430c6ba0a7cca76 100644 (file)
@@ -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);
 }
index 6da175b7be275bee02c5da32d436d8982ba6fbe4..31f1021702a9e6a6b4678d19ee5f0184ce7ff941 100644 (file)
@@ -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
index b5435cb490eeaa2e25b7bb6749e849f8c362d0c9..3bec576bbe6ceb88b5603f365d4da0089f69e7e3 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 #include <stdint.h>
 
index 47d8fac9b88c35577a8fd221bcf8b8c3c5115f5a..f19e905e6d77c43d50cf902e47b31272f4755a52 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <check.h>
@@ -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;
 }
index 6b3e8f449484b04d1c9fe53082f6ff66c0919cb1..be0e16e20f03e3b2308bfd09ab5abca06eadf7ae 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <check.h>
index 7bf096b301ca2a930d972681ad1016b56296bcd5..d04b7cc2dd2ad99f70eae609a08dd2c97b9452d1 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <check.h>
index 13927bdb916db83966e2c721e55cf61e0e8b3b69..354cdb1d16c8d1de957c97d0779e6f76f06ddaeb 100644 (file)
@@ -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;
 }
index f9438682af5896a656429012e5a1d58c9b71ad05..4d1f33b0b7619c8701c3128f95719edc9a640dc9 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #ifdef HAVE_ALLOCA_H
index c42868282984531e88e6c66cf23019c7ea6c9cd1..2cccd5f2a3150b97f4b4a5da3b3ada9ad82d1ef2 100644 (file)
@@ -14,8 +14,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <check.h>
index 581b4dfcafea7decc6d28ee8544b202a35345ba3..dad405a298d9ba3621626baa18c63e5a6f1551df 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
-#include <getopt.h>
-#include <unistd.h>
 #include <limits.h>
 
 #include <check.h>
 
 #include <libfyaml.h>
 
-#include <stdatomic.h>
+#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);
 }
index 0fbf83e0599be07b4afa1468ce0524acaa8c14b2..717fa2256c31de76d23c32fe5284e7d795fa46ac 100644 (file)
@@ -11,8 +11,8 @@
 #endif
 
 #include <stdio.h>
-#include <getopt.h>
 #include <stdbool.h>
+#include <stdlib.h>
 
 #include <check.h>
 
 
 #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;
                }
        }