pax_global_header00006660000000000000000000000064151621637430014522gustar00rootroot0000000000000052 comment=bf2770b528ed339e8790e733fb9cd39f85e0f5e6 bstring-1.1.0/000077500000000000000000000000001516216374300131715ustar00rootroot00000000000000bstring-1.1.0/.clusterfuzzlite/000077500000000000000000000000001516216374300165255ustar00rootroot00000000000000bstring-1.1.0/.clusterfuzzlite/Dockerfile000066400000000000000000000007161516216374300205230ustar00rootroot00000000000000FROM gcr.io/oss-fuzz-base/base-builder:v1@sha256:9caf963eedfdcd5363da61e08943baace25ceb1e4ecd0fe8b395910e88d40573 RUN apt-get update && \ apt-get install -y --no-install-recommends \ meson \ ninja-build \ && rm -rf /var/lib/apt/lists/* COPY bstring/ $SRC/project/bstring/ COPY fuzz/ $SRC/project/fuzz/ COPY meson_options.txt $SRC/project/ COPY meson.build $SRC/project/ WORKDIR $SRC/project COPY .clusterfuzzlite/build.sh $SRC/build.sh bstring-1.1.0/.clusterfuzzlite/build.sh000077500000000000000000000025001516216374300201600ustar00rootroot00000000000000#!/bin/bash -eu # # ClusterFuzzLite build script for bstring. # # ClusterFuzzLite sets: # $CC / $CXX - clang # $CFLAGS - sanitizer + coverage flags (e.g. -fsanitize=address,fuzzer-no-link) # $LDFLAGS - sanitizer link flags # $LIB_FUZZING_ENGINE - fuzzer driver (e.g. -fsanitize=fuzzer) # $OUT - output directory for fuzz target binaries # # Meson picks up $CC/$CFLAGS/$LDFLAGS from the environment during setup. # Do not append $LIB_FUZZING_ENGINE to global LDFLAGS here: Meson's # compiler sanity check links a regular main() and fails when libFuzzer's # main() is injected globally. PROJECT_SRC="$SRC/project" if [[ ! -f "$PROJECT_SRC/meson.build" ]]; then PROJECT_SRC="$SRC/bstring" fi build_targets() { cd "$PROJECT_SRC" rm -rf build meson_args=( -Ddefault_library=static -Denable-docs=false -Denable-fuzzing=true -Denable-utf8=true -Denable-tests=false --buildtype=plain ) if [[ -n "${LIB_FUZZING_ENGINE:-}" ]]; then meson_args+=("-Dfuzz-link-arg=${LIB_FUZZING_ENGINE}") fi meson setup build "${meson_args[@]}" ninja -C build } package_outputs() { cd "$PROJECT_SRC" cp build/fuzz/fuzz_bstring "$OUT/" zip -j "$OUT/fuzz_bstring_seed_corpus.zip" fuzz/corpus/* } build_targets package_outputs bstring-1.1.0/.github/000077500000000000000000000000001516216374300145315ustar00rootroot00000000000000bstring-1.1.0/.github/CODEOWNERS000066400000000000000000000000251516216374300161210ustar00rootroot00000000000000* @msteinert @rdmark bstring-1.1.0/.github/dependabot.yml000066400000000000000000000003411516216374300173570ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "docker" directory: "/.clusterfuzzlite" schedule: interval: "weekly" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" bstring-1.1.0/.github/workflows/000077500000000000000000000000001516216374300165665ustar00rootroot00000000000000bstring-1.1.0/.github/workflows/build.yml000066400000000000000000000407741516216374300204240ustar00rootroot00000000000000name: Build and Test on: push: branches: - main pull_request: branches: - main types: - opened - synchronize - reopened jobs: build-alpine: name: Alpine Linux runs-on: ubuntu-latest permissions: contents: read container: image: alpine:3.22.1 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | apk add \ build-base \ check \ check-dev \ gcc \ meson \ ninja \ pkgconfig \ valgrind - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Run memory profiling run: meson test --wrapper='valgrind --leak-check=full --error-exitcode=1' -C build - name: Install run: meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-archlinux: name: Arch Linux runs-on: ubuntu-latest permissions: contents: read container: image: archlinux:base-devel steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | pacman -Sy --noconfirm \ check \ gcc \ meson \ ninja \ pkgconfig \ valgrind - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Install run: meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-debian: name: Debian Linux runs-on: ubuntu-latest permissions: contents: read container: image: debian:13.0 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | apt-get update apt-get install --assume-yes --no-install-recommends \ build-essential \ check \ meson \ ninja-build \ pkg-config \ valgrind - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Run memory profiling run: meson test --wrapper='valgrind --leak-check=full --error-exitcode=1' -C build - name: Install run: meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-fedora: name: Fedora Linux runs-on: ubuntu-latest permissions: contents: read container: image: fedora:42 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | dnf --setopt=install_weak_deps=False --assumeyes install \ check \ check-devel \ gcc \ meson \ ninja-build \ valgrind - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Run memory profiling run: meson test --wrapper='valgrind --leak-check=full --error-exitcode=1' -C build - name: Install run: sudo meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-ubuntu: name: Ubuntu Linux runs-on: ubuntu-latest permissions: contents: read container: image: ubuntu:25.10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | apt-get update apt-get install --assume-yes --no-install-recommends \ build-essential \ check \ cmake \ gcc \ meson \ ninja-build \ pkg-config \ valgrind - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Run memory profiling run: meson test --wrapper='valgrind --leak-check=full --error-exitcode=1' -C build - name: Install run: meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-macos: name: macOS permissions: contents: read runs-on: macos-latest env: HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_NO_AUTO_UPDATE: 1 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | brew update brew install \ check \ meson - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Install run: sudo meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-dflybsd: name: DragonflyBSD runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/dragonflybsd-vm@323497fa680c1856dd1ba5c4fd89182a9194f649 # v1.2.7 with: copyback: true prepare: | set -e pkg install -y \ check \ meson \ pkgconf run: | set -e meson setup build \ -Denable-tests=true meson compile -C build meson test -C build meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-freebsd: name: FreeBSD runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/freebsd-vm@4807432c7cab1c3f97688665332c0b932062d31f # v1.4.3 with: release: "14.3" copyback: true sync: sshfs prepare: | set -e pkg install -y \ check \ meson \ pkgconf \ valgrind run: | set -e meson setup build \ -Denable-tests=true meson compile -C build meson test -C build --no-rebuild meson test --wrapper='valgrind --leak-check=full --error-exitcode=1' -C build --no-rebuild meson install -C build --no-rebuild - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-netbsd: name: NetBSD runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/netbsd-vm@ca7ff0556959998c82761c34ea0c3c99fa084c48 # v1.3.7 with: copyback: true sync: sshfs prepare: | set -e export PKG_PATH="http://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f '1 2' -d.)/All/" pkg_add \ check \ meson \ pkg-config run: | set -e meson setup build \ -Denable-tests=true meson compile -C build meson test -C build --no-rebuild meson install -C build --no-rebuild - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-openbsd: name: OpenBSD runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/openbsd-vm@3fafb45f2e2e696249c583835939323fe1c3448c # v1.3.7 with: copyback: true sync: sshfs prepare: | set -e pkg_add -I \ check \ meson \ pkgconf run: | set -e meson setup build \ -Denable-tests=true meson compile -C build meson test -C build --no-rebuild meson install -C build --no-rebuild - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-omnios: name: OmniOS runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/omnios-vm@68da93c6d9812b29fc90c5b5141b093f84a590fb # v1.2.7 with: copyback: true prepare: | set -e pkg install \ build-essential \ pkg-config curl -O https://pkgsrc.smartos.org/packages/SmartOS/bootstrap/bootstrap-trunk-x86_64-20240116.tar.gz tar -zxpf bootstrap-trunk-x86_64-20240116.tar.gz -C / export PATH=/opt/local/sbin:/opt/local/bin:/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin:$PATH pkgin -y install \ check \ meson run: | set -e export PATH=/opt/local/sbin:/opt/local/bin:/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin:$PATH meson setup build \ -Denable-tests=true \ -Dpkg_config_path=/opt/local/lib/pkgconfig meson compile -C build meson test -C build meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-openindiana: name: OpenIndiana runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/openindiana-vm@0544593284f5c837f637097e278df86bf5bbab1e # v1.0.7 with: release: "202510-build" copyback: true prepare: | set -e pkg install \ developer/build/meson \ developer/build/ninja \ developer/build/pkg-config \ developer/check \ developer/gcc-14 run: | set -e export PATH=/usr/local/sbin:/usr/local/bin:$PATH meson setup build \ -Denable-bgets-workaround=true \ -Denable-tests=true \ -Dpkg_config_path=/usr/lib/amd64/pkgconfig meson compile -C build meson test -C build meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-solaris: name: Solaris runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build on VM uses: vmactions/solaris-vm@0a231b94365d1911cf62097ef342f6b30d95598f # v1.3.2 with: copyback: true sync-time: true sync: nfs prepare: | set -e pkg install \ check \ gcc \ meson \ ninja \ pkg-config run: | set -e export PATH=/usr/local/sbin:/usr/local/bin:$PATH meson setup build \ -Denable-bgets-workaround=true \ -Denable-tests=true \ -Dpkg_config_path=/usr/lib/amd64/pkgconfig meson compile -C build meson test -C build meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs build-windows: name: Windows (MSYS2+gcc) permissions: contents: read runs-on: windows-latest defaults: run: shell: msys2 {0} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup MSYS2 uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 with: msystem: MINGW64 update: true install: >- mingw-w64-x86_64-gcc mingw-w64-x86_64-meson mingw-w64-x86_64-ninja mingw-w64-x86_64-pkg-config mingw-w64-x86_64-check - name: Configure run: | meson setup build \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Install run: meson install -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs ci-msvc: name: Windows (MSVC) permissions: contents: read runs-on: windows-latest steps: - name: Fetch Sources uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.x' - name: Install Python Dependencies run: pip install meson ninja - name: Prepare MSVC uses: bus1/cabuild/action/msdevshell@06ea2833eef61e9b0d0ce0d728416e617e4fb1fe # v1 with: architecture: x64 - name: Configure run: meson setup build - name: Build run: meson compile -v -C build - name: Upload meson logs if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: meson-logs-${{ github.job }} path: build/meson-logs bstring-1.1.0/.github/workflows/fuzz.yml000066400000000000000000000047751516216374300203240ustar00rootroot00000000000000name: ClusterFuzzLite on: push: branches: - main pull_request: branches: - main types: - opened - synchronize - reopened schedule: - cron: '0 3 * * 0' # Weekly on Sunday at 03:00 UTC workflow_dispatch: permissions: actions: read contents: read jobs: # Run for a short window on every PR / push to catch regressions introduced # by the change under review. PR: name: Fuzzing (code-change) if: github.event_name == 'pull_request' || github.event_name == 'push' runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ github.ref }}-pr cancel-in-progress: true permissions: actions: read contents: read security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build fuzz targets uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: language: c sanitizer: address - name: Run fuzz targets uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 60 mode: code-change sanitizer: address output-sarif: true - name: Upload SARIF results if: always() && hashFiles('sarif-results/address.sarif') != '' uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: sarif-results/address.sarif # Run longer on a schedule to build up a persistent corpus and surface # crashes that require deeper exploration. batch: name: Fuzzing (batch) if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build fuzz targets uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: language: c sanitizer: address - name: Run fuzz targets uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 3600 mode: batch sanitizer: address bstring-1.1.0/.github/workflows/scorecard.yml000066400000000000000000000023071516216374300212600ustar00rootroot00000000000000name: Scorecard supply-chain security on: branch_protection_rule: schedule: - cron: '45 23 * * 0' push: branches: ["main"] jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' permissions: contents: read security-events: write id-token: write steps: - name: "Checkout code" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif publish_results: true - name: "Upload artifact" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: results.sarif bstring-1.1.0/.github/workflows/test.yml000066400000000000000000000033631516216374300202750ustar00rootroot00000000000000name: Unit Tests on: push: branches: - main pull_request: branches: - main types: - opened - synchronize - reopened jobs: test-ubuntu: name: Test and Coverage Reports runs-on: ubuntu-latest permissions: contents: read checks: write pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | sudo apt-get update sudo apt-get install --assume-yes --no-install-recommends \ check \ gcovr \ meson \ ninja-build - name: Configure run: | meson setup build \ -Db_coverage=true \ -Denable-tests=true - name: Build run: meson compile -C build - name: Run unit tests run: meson test -C build - name: Run code coverage run: ninja coverage -C build - name: Publish test report uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v6.3.1 if: success() || failure() with: report_paths: build/meson-logs/testlog.junit.xml include_passed: true - name: Report code coverage uses: threeal/gcovr-action@64cb417ed6f143cafe1c69611922672002f460bb # v1.2.0 with: excludes: 'tests/.*' github-token: ${{ secrets.GITHUB_TOKEN }} xml-out: build/meson-logs/coverage.xml - uses: 5monkeys/cobertura-action@ee5787cc56634acddedc51f21c7947985531e6eb # v14 with: path: build/meson-logs/coverage.xml fail_below_threshold: true minimum_coverage: 50 show_line: true show_branch: true bstring-1.1.0/COPYING000066400000000000000000000027561516216374300142360ustar00rootroot00000000000000Copyright (c) 2002-2015 Paul Hsieh All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of bstrlib nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. bstring-1.1.0/README.md000066400000000000000000000115021516216374300144470ustar00rootroot00000000000000# Better String Library [![SonarQube Security](https://sonarcloud.io/api/project_badges/measure?project=msteinert_bstring&metric=security_rating)](https://sonarcloud.io/project/overview?id=msteinert_bstring) [![SonarQube Reliability](https://sonarcloud.io/api/project_badges/measure?project=msteinert_bstring&metric=reliability_rating)](https://sonarcloud.io/project/overview?id=msteinert_bstring) [![SonarQube Maintainability](https://sonarcloud.io/api/project_badges/measure?project=msteinert_bstring&metric=sqale_rating)](https://sonarcloud.io/project/overview?id=msteinert_bstring) [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/msteinert/bstring?label=openssf+scorecard&style=flat)](https://scorecard.dev/viewer/?uri=github.com/msteinert/bstring) The Better String Library is an abstraction of a string data type which is superior to the C standard library char buffer string type. Among the features achieved are: - Substantial mitigation of buffer overflow/overrun problems and other failures that result from erroneous usage of the common C string library functions - Significantly simplified string manipulation - High performance interoperability with other source/libraries which expect '\0' terminated char buffers - Improved overall performance of common string operations - Functional equivalency with other more modern languages - Optional API for manipulating UTF-8 encoded strings ## bstring fork This is a fork of Paul Hsieh's [Better String Library][]. The following features (or mis-features, depending on your point of view) are included: 1. A build system ([Meson][]+[Ninja][]) 2. Improved test suite using the [Check][] library 3. Continuous integration via GitHub Actions, including memory profiling with [Valgrind][] 4. Remove C++ wrapper code, returning this to a pure C library 5. Documentation generation with [Doxygen][] Currently this fork should be binary-compatible with the original code. The only source incompatibility is the removal of the `const_bstring` type. Just use `const bstring` instead. [Better String Library]: http://bstring.sourceforge.net/ [Meson]: https://mesonbuild.com/ [Ninja]: https://ninja-build.org/ [Check]: https://github.com/libcheck/check [Valgrind]: http://valgrind.org/ [Doxygen]: https://www.doxygen.nl/ [libFuzzer]: https://llvm.org/docs/LibFuzzer.html ## Get bstring The bstring library is widely available as a binary package in various distributions. The shared library and development headers can be installed with their respective package manager. The current packaging status as reported by repology.org: [![Packaging status](https://repology.org/badge/vertical-allrepos/bstring.svg)](https://repology.org/project/bstring/versions) ## Building The bstring library can be built using the [Meson][]+[Ninja][] build system and a C compiler. See the [Porting Guide](https://mike.steinert.ca/bstring/doc/md_porting.html) for notes on compiler compatibility. Configure the `build` directory with Meson. meson setup build Then compile and install. meson compile -C build sudo meson install -C build ### Build options The following build options are available: - `enable-utf8` (default: `true`): Enable UTF-8 string manipulation functions - `enable-docs` (default: `false`): Generate documentation with [Doxygen][] - `enable-tests` (default: `false`): Build the test suite with the [Check][] library - `enable-fuzzing` (default: `false`): Build the fuzzing targets with [libFuzzer][] - `enable-bgets-workaround` (default: `false`): Avoid namespace conflict with the `bgets` function in the standard C library (notably: Solaris) - `enable-old-api` (default: `false`): Enable backward compatibility macros for pre-1.0 API ### Testing A test suite is available if the [Check][] library is is installed. meson setup build -Denable-docs=true meson test -C build If Valgrind is installed the test suite can be checked for memory leaks. meson test --wrapper='valgrind --leak-check=full' -C build ## Documentation The original documentation has been migrated into the header files and converted to the Doxygen format. The generated output can be found [online][]. The original documentation essays are available in the source distribution and are included in the Doxygen output. 1. [Introduction](doc/introduction.md) 2. [Comparisons](doc/comparisons.md) 3. [Porting](doc/porting.md) 4. [Security](doc/security.md) These essays have been reformatted for easier reading. Minor edits have been made to reflect changes to the codebase. [online]: http://mike.steinert.ca/bstring/doc/ ## License The Better String Library is released under the [BSD 3-Clause License][] or the [GNU General Public License v2][] at the option of the user. [BSD 3-Clause License]: http://opensource.org/licenses/BSD-3-Clause [GNU General Public License v2]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt bstring-1.1.0/SECURITY.md000066400000000000000000000021621516216374300147630ustar00rootroot00000000000000# Security Policy The bstring project is committed to shipping a resilient and memory safe library. We take any exploitable security vulnerabilities very seriously. ## Supported Versions The latest stable version of the bstring library is supported with security patches. If you are using an older version, we recommend upgrading as soon as possible so that you have the most recent security improvements and bug fixes. ## Reporting a Vulnerability If you find an exploitable security vulnerability in this software, we would love to hear from you. Please file a [private GitHub security vulnerability ticket](https://github.com/msteinert/bstring/security/advisories/new). When reporting a vulnerability, please include as much of the following information as possible: - Description: A clear description of the vulnerability - Impact: Potential impact and attack scenarios - Reproduction: Step-by-step instructions to reproduce the issue - Affected Versions: Which versions are affected - Environment: Operating system, software versions, configuration details - Proof of Concept: A trivial implementation, unit test, or similar bstring-1.1.0/bstring/000077500000000000000000000000001516216374300146415ustar00rootroot00000000000000bstring-1.1.0/bstring/bstraux.c000066400000000000000000000644001516216374300165010ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ #ifdef HAVE_CONFIG_H #include #endif #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include "bstraux.h" bstring bTail(bstring b, int n) { if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) { return NULL; } if (n >= b->slen) { return bstrcpy(b); } return bmidstr(b, b->slen - n, n); } bstring bHead(bstring b, int n) { if (b == NULL || n < 0 || (b->mlen < b->slen && b->mlen > 0)) { return NULL; } if (n >= b->slen) { return bstrcpy(b); } return bmidstr(b, 0, n); } int bFill(bstring b, char c, int len) { if (b == NULL || len < 0 || (b->mlen < b->slen && b->mlen > 0)) { return -__LINE__; } b->slen = 0; return bsetstr(b, len, NULL, c); } int bReplicate(bstring b, int n) { return bpattern (b, n * b->slen); } int bReverse(bstring b) { int i, n, m; unsigned char t; if (b == NULL || b->slen < 0 || b->mlen < b->slen) { return -__LINE__; } n = b->slen; if (2 <= n) { m = ((unsigned int)n) >> 1; n--; for (i = 0; i < m; i++) { t = b->data[n - i]; b->data[n - i] = b->data[i]; b->data[i] = t; } } return 0; } int bInsertChrs(bstring b, int pos, int len, unsigned char c, unsigned char fill) { if (b == NULL || b->slen < 0 || b->mlen < b->slen || pos < 0 || len <= 0) { return -__LINE__; } if (pos > b->slen && 0 > bsetstr(b, pos, NULL, fill)) { return -__LINE__; } if (0 > balloc(b, b->slen + len)) { return -__LINE__; } if (pos < b->slen) { memmove(b->data + pos + len, b->data + pos, b->slen - pos); } memset(b->data + pos, c, len); b->slen += len; b->data[b->slen] = (unsigned char)'\0'; return BSTR_OK; } int bJustifyLeft(bstring b, int space) { int j, i, s, t; unsigned char c = (unsigned char) space; if (b == NULL || b->slen < 0 || b->mlen < b->slen) { return -__LINE__; } if (space != (int) c) { return BSTR_OK; } for (s = j = i = 0; i < b->slen; i++) { t = s; s = c != (b->data[j] = b->data[i]); j += (t|s); } if (j > 0 && b->data[j-1] == c) { --j; } b->data[j] = (unsigned char)'\0'; b->slen = j; return BSTR_OK; } int bJustifyRight(bstring b, int width, int space) { int ret; if (width <= 0) { return -__LINE__; } if (0 > (ret = bJustifyLeft(b, space))) { return ret; } if (b->slen <= width) { return bInsertChrs(b, 0, width - b->slen, (unsigned char)space, (unsigned char)space); } return BSTR_OK; } int bJustifyCenter(bstring b, int width, int space) { int ret; if (width <= 0) { return -__LINE__; } if (0 > (ret = bJustifyLeft(b, space))) { return ret; } if (b->slen <= width) { return bInsertChrs(b, 0, (width - b->slen + 1) >> 1, (unsigned char)space, (unsigned char)space); } return BSTR_OK; } int bJustifyMargin(bstring b, int width, int space) { struct bstrList * sl; int i, l, c; if (b == NULL || b->slen < 0 || b->mlen == 0 || b->mlen < b->slen) { return -__LINE__; } if (NULL == (sl = bsplit (b, (unsigned char)space))) { return -__LINE__; } for (l = c = i = 0; i < sl->qty; i++) { if (sl->entry[i]->slen > 0) { c ++; l += sl->entry[i]->slen; } } if (l + c >= width || c < 2) { bstrListDestroy(sl); return bJustifyLeft(b, space); } b->slen = 0; for (i = 0; i < sl->qty; i++) { if (sl->entry[i]->slen > 0) { if (b->slen > 0) { int s = (width - l + (c / 2)) / c; bInsertChrs(b, b->slen, s, (unsigned char)space, (unsigned char)space); l += s; } bconcat(b, sl->entry[i]); c--; if (c <= 0) break; } } bstrListDestroy(sl); return BSTR_OK; } static size_t readNothing(BSTR_UNUSED void *buff, BSTR_UNUSED size_t elsize, BSTR_UNUSED size_t nelem, BSTR_UNUSED void *parm) { return 0; /* Immediately indicate EOF. */ } struct bStream * bsFromBstr(const bstring b) { struct bStream *s = bsopen((bNread) readNothing, NULL); bsunread(s, b); /* Push the bstring data into the empty bStream. */ return s; } static size_t readRef(void *buff, size_t elsize, size_t nelem, void *parm) { struct tagbstring * t = (struct tagbstring *)parm; size_t tsz = elsize * nelem; if (tsz > (size_t)t->slen) { tsz = (size_t)t->slen; } if (tsz > 0) { memcpy (buff, t->data, tsz); t->slen -= (int)tsz; t->data += tsz; return tsz / elsize; } return 0; } /** * The "by reference" version of the above function. * * This function puts a number of restrictions on the call site (the passed in * struct tagbstring *will* be modified by this function, and the source data * must remain alive and constant for the lifetime of the bStream). Hence it * is not presented as an extern. */ static struct bStream * bsFromBstrRef(struct tagbstring * t) { if (!t) { return NULL; } return bsopen((bNread) readRef, t); } char * bStr2NetStr(const bstring b) { char strnum[sizeof(b->slen) * 3 + 1]; bstring s; unsigned char * buff; if (b == NULL || b->data == NULL || b->slen < 0) { return NULL; } sprintf(strnum, "%d:", b->slen); if (NULL == (s = bfromcstr(strnum)) || bconcat(s, b) == BSTR_ERR || bconchar(s, (char) ',') == BSTR_ERR) { bdestroy(s); return NULL; } buff = s->data; bcstrfree((char *)s); return (char *)buff; } bstring bNetStr2Bstr(const char * buff) { int i, x; bstring b; if (buff == NULL) { return NULL; } x = 0; for (i = 0; i < 11 && buff[i] != ':'; ++i) { unsigned int v = buff[i] - '0'; if (v > 9 || x > ((INT_MAX - (signed int)v) / 10)) { return NULL; } x = (x * 10) + v; } if (buff[i] != ':') { return NULL; } /* Ensure the buffer spans at least i+1+x+1 bytes (digits, ':', data, ',') * without scanning past that bound. A null byte before the expected comma * position means the netstring is truncated. */ if (memchr(buff, '\0', (size_t)i + 2 + (size_t)x) != NULL) { return NULL; } /* This thing has to be properly terminated */ if (buff[i + 1 + x] != ',') { return NULL; } if (NULL == (b = bfromcstr(""))) { return NULL; } if (balloc(b, x + 1) != BSTR_OK) { bdestroy(b); return NULL; } memcpy(b->data, buff + i + 1, x); b->data[x] = (unsigned char)'\0'; b->slen = x; return b; } static char b64ETable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; bstring bBase64Encode(const bstring b) { int i, c0, c1, c2, c3; bstring out; if (b == NULL || b->slen < 0 || b->data == NULL) { return NULL; } out = bfromcstr(""); for (i = 0; i + 2 < b->slen; i += 3) { if (i && ((i % 57) == 0)) { if (bconchar(out, (char)'\015') < 0 || bconchar(out, (char)'\012') < 0) { bdestroy(out); return NULL; } } c0 = b->data[i] >> 2; c1 = ((b->data[i] << 4) | (b->data[i+1] >> 4)) & 0x3F; c2 = ((b->data[i+1] << 2) | (b->data[i+2] >> 6)) & 0x3F; c3 = b->data[i+2] & 0x3F; if (bconchar(out, b64ETable[c0]) < 0 || bconchar(out, b64ETable[c1]) < 0 || bconchar(out, b64ETable[c2]) < 0 || bconchar(out, b64ETable[c3]) < 0) { bdestroy(out); return NULL; } } if (i && ((i % 57) == 0)) { if (bconchar(out, (char)'\015') < 0 || bconchar(out, (char)'\012') < 0) { bdestroy(out); return NULL; } } switch (i + 2 - b->slen) { case 0: c0 = b->data[i] >> 2; c1 = ((b->data[i] << 4) | (b->data[i+1] >> 4)) & 0x3F; c2 = (b->data[i+1] << 2) & 0x3F; if (bconchar(out, b64ETable[c0]) < 0 || bconchar(out, b64ETable[c1]) < 0 || bconchar(out, b64ETable[c2]) < 0 || bconchar(out, (char)'=') < 0) { bdestroy(out); return NULL; } break; case 1: c0 = b->data[i] >> 2; c1 = (b->data[i] << 4) & 0x3F; if (bconchar(out, b64ETable[c0]) < 0 || bconchar(out, b64ETable[c1]) < 0 || bconchar(out, (char)'=') < 0 || bconchar(out, (char)'=') < 0) { bdestroy(out); return NULL; } break; case 2: break; } return out; } #define B64_PAD (-2) #define B64_ERR (-1) static int base64DecodeSymbol(unsigned char alpha) { if ((alpha >= 'A') && (alpha <= 'Z')) { return (int)(alpha - 'A'); } else if ((alpha >= 'a') && (alpha <= 'z')) { return 26 + (int)(alpha - 'a'); } else if ((alpha >= '0') && (alpha <= '9')) { return 52 + (int)(alpha - '0'); } else if (alpha == '+') { return 62; } else if (alpha == '/') { return 63; } else if (alpha == '=') { return B64_PAD; } else { return B64_ERR; } } bstring bBase64DecodeEx(const bstring b, int * boolTruncError) { int i, v; unsigned char c0, c1, c2; bstring out; if (b == NULL || b->slen < 0 || b->data == NULL) { return NULL; } if (boolTruncError) { *boolTruncError = 0; } out = bfromcstr(""); i = 0; while (1) { do { if (i >= b->slen) { return out; } if (b->data[i] == '=') { /* Bad "too early" truncation */ if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy(out); return NULL; } v = base64DecodeSymbol(b->data[i]); i++; } while (v < 0); c0 = (unsigned char)(v << 2); do { if (i >= b->slen || b->data[i] == '=') { /* Bad "too early" truncation */ if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy(out); return NULL; } v = base64DecodeSymbol(b->data[i]); i++; } while (v < 0); c0 |= (unsigned char)(v >> 4); c1 = (unsigned char)(v << 4); do { if (i >= b->slen) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy(out); return NULL; } if (b->data[i] == '=') { i++; if (i >= b->slen || b->data[i] != '=' || bconchar(out, c0) < 0) { if (boolTruncError) { *boolTruncError = 1; return out; } /* Missing "=" at the end. */ bdestroy(out); return NULL; } return out; } v = base64DecodeSymbol(b->data[i]); i++; } while (v < 0); c1 |= (unsigned char)(v >> 2); c2 = (unsigned char)(v << 6); do { if (i >= b->slen) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy(out); return NULL; } if (b->data[i] == '=') { if (bconchar(out, c0) < 0 || bconchar(out, c1) < 0) { if (boolTruncError) { *boolTruncError = 1; return out; } bdestroy(out); return NULL; } if (boolTruncError) { *boolTruncError = 0; } return out; } v = base64DecodeSymbol(b->data[i]); i++; } while (v < 0); c2 |= (unsigned char)(v); if (bconchar(out, c0) < 0 || bconchar(out, c1) < 0 || bconchar(out, c2) < 0) { if (boolTruncError) { *boolTruncError = -1; return out; } bdestroy(out); return NULL; } } } #define UU_DECODE_BYTE(b) \ (((b) == (signed int)'`') ? 0 : (b) - (signed int)' ') struct bUuInOut { bstring src, dst; int * badlines; }; #define UU_MAX_LINELEN 45 static int bUuDecLine(void *parm, int ofs, int len) { struct bUuInOut *io = (struct bUuInOut *)parm; bstring s = io->src; bstring t = io->dst; int i, llen, otlen, ret, c0, c1, c2, c3, d0, d1, d2, d3; if (len == 0) { return 0; } llen = UU_DECODE_BYTE (s->data[ofs]); ret = 0; otlen = t->slen; if (((unsigned)llen) > UU_MAX_LINELEN) { ret = -__LINE__; goto exit; } llen += t->slen; for (i = 1; i < s->slen && t->slen < llen; i += 4) { unsigned char outoctet[3]; int invalid_c0c1 = 0; int invalid_c2 = 0; int invalid_c3 = 0; c0 = UU_DECODE_BYTE(d0 = (int)bchare(s, i+ofs+0, ' ' - 1)); c1 = UU_DECODE_BYTE(d1 = (int)bchare(s, i+ofs+1, ' ' - 1)); c2 = UU_DECODE_BYTE(d2 = (int)bchare(s, i+ofs+2, ' ' - 1)); c3 = UU_DECODE_BYTE(d3 = (int)bchare(s, i+ofs+3, ' ' - 1)); if (((unsigned)(c0|c1) >= 0x40)) { invalid_c0c1 = (d0 > 0x60 || (d0 < (' ' - 1) && !isspace(d0))) || (d1 > 0x60 || (d1 < (' ' - 1) && !isspace(d1))); if (!ret) { ret = -__LINE__; } if (invalid_c0c1) { t->slen = otlen; goto exit; } c0 = c1 = 0; } outoctet[0] = (unsigned char)((c0 << 2) | ((unsigned)c1 >> 4)); if (t->slen+1 >= llen) { int result = bconchar(t, (char)outoctet[0]); if (result < 0) { return -__LINE__; } i = s->slen; continue; } if ((unsigned)c2 >= 0x40) { invalid_c2 = (d2 > 0x60 || (d2 < (' ' - 1) && !isspace(d2))); if (!ret) { ret = -__LINE__; } if (invalid_c2) { t->slen = otlen; goto exit; } c2 = 0; } outoctet[1] = (unsigned char) ((c1 << 4) | ((unsigned) c2 >> 2)); if (t->slen + 2 >= llen) { if (0 > bcatblk (t, outoctet, 2)) { return -__LINE__; } break; } if ((unsigned)c3 >= 0x40) { invalid_c3 = (d3 > 0x60 || (d3 < (' ' - 1) && !isspace(d3))); if (!ret) { ret = -__LINE__; } if (invalid_c3) { t->slen = otlen; goto exit; } c3 = 0; } outoctet[2] = (unsigned char)((c2 << 6) | ((unsigned)c3)); if (0 > bcatblk(t, outoctet, 3)) { return -__LINE__; } } if (t->slen < llen) { if (0 == ret) { ret = -__LINE__; } t->slen = otlen; } exit: if (ret && io->badlines) { (*io->badlines)++; return 0; } return ret; } struct bsUuCtx { struct bUuInOut io; struct bStream * sInp; }; bstring bUuDecodeEx(const bstring src, int *badlines) { struct bStream *s, *d; struct bsUuCtx *ctx; struct tagbstring t; bstring b; if (!src) { return NULL; } t = *src; /* Short lifetime alias to header of src */ s = bsFromBstrRef(&t); /* t is undefined after this */ if (!s) { return NULL; } d = bsUuDecode(s, badlines); b = bfromcstralloc(256, ""); if (NULL == b) { goto error; } if (src->slen > 0 && 0 > bsread(b, d, src->slen)) { goto error; } exit: /* * bsUuDecodePart frees ctx->io.dst and ctx->io.src when it signals * EOF (nulling both pointers), but leaves ctx itself for us to free. * If the stream was never read (e.g. empty input), all three are still * live. Either way, bsclose returns the ctx pointer and we own it. */ ctx = (struct bsUuCtx *)bsclose(d); if (ctx) { bdestroy(ctx->io.dst); bdestroy(ctx->io.src); free(ctx); } bsclose(s); return b; error: bdestroy(b); b = NULL; goto exit; } static size_t bsUuDecodePart(void *buff, size_t elsize, size_t nelem, void *parm) { static struct tagbstring eol = bsStatic ("\r\n"); struct bsUuCtx * ctx = (struct bsUuCtx *) parm; size_t tsz; int l, lret; if (NULL == buff || NULL == parm) { return 0; } tsz = elsize * nelem; check: /* If internal buffer has sufficient data, just output it */ if (((size_t)ctx->io.dst->slen) > tsz) { memcpy(buff, ctx->io.dst->data, tsz); bdelete(ctx->io.dst, 0, (int)tsz); return nelem; } decode: if (0 <= (l = binchr(ctx->io.src, 0, &eol))) { int ol = 0; struct tagbstring t; bstring s = ctx->io.src; ctx->io.src = &t; do { if (l > ol) { bmid2tbstr(t, s, ol, l - ol); lret = bUuDecLine(&ctx->io, 0, t.slen); if (0 > lret) { ctx->io.src = s; goto done; } } ol = l + 1; if (((size_t)ctx->io.dst->slen) > tsz) { break; } l = binchr(s, ol, &eol); } while (BSTR_ERR != l); bdelete(s, 0, ol); ctx->io.src = s; goto check; } if (BSTR_ERR != bsreada(ctx->io.src, ctx->sInp, bsbufflength(ctx->sInp, BSTR_BS_BUFF_LENGTH_GET))) { goto decode; } bUuDecLine(&ctx->io, 0, ctx->io.src->slen); done: /* Output any lingering data that has been translated */ if (((size_t)ctx->io.dst->slen) > 0) { if (((size_t)ctx->io.dst->slen) > tsz) { goto check; } memcpy(buff, ctx->io.dst->data, ctx->io.dst->slen); tsz = ctx->io.dst->slen / elsize; ctx->io.dst->slen = 0; if (tsz > 0) { return tsz; } } /* Release stream buffers on EOF; ctx itself is owned by bUuDecodeEx. */ bdestroy(ctx->io.dst); ctx->io.dst = NULL; bdestroy(ctx->io.src); ctx->io.src = NULL; return 0; } struct bStream * bsUuDecode(struct bStream *sInp, int *badlines) { struct bsUuCtx *ctx = (struct bsUuCtx *)malloc(sizeof(struct bsUuCtx)); struct bStream *sOut; if (NULL == ctx) { return NULL; } ctx->io.src = bfromcstr(""); ctx->io.dst = bfromcstr(""); if (NULL == ctx->io.dst || NULL == ctx->io.src) { goto error; } ctx->io.badlines = badlines; if (badlines) { *badlines = 0; } ctx->sInp = sInp; sOut = bsopen((bNread) bsUuDecodePart, ctx); if (NULL == sOut) { goto error; } return sOut; error: bdestroy(ctx->io.dst); bdestroy(ctx->io.src); free(ctx); return NULL; } #define UU_ENCODE_BYTE(b) \ ((char)(((b) == 0) ? '`' : ((b) + ' '))) bstring bUuEncode(const bstring src) { bstring out; int i, j, jm; unsigned int c0, c1, c2; if (src == NULL || src->slen < 0 || src->data == NULL) { return NULL; } if ((out = bfromcstr("")) == NULL) { return NULL; } for (i = 0; i < src->slen; i += UU_MAX_LINELEN) { if ((jm = i + UU_MAX_LINELEN) > src->slen) { jm = src->slen; } if (bconchar(out, UU_ENCODE_BYTE(jm - i)) < 0) { bstrFree(out); break; } for (j = i; j < jm; j += 3) { c0 = (unsigned int)bchar(src, j); c1 = (unsigned int)bchar(src, j + 1); c2 = (unsigned int)bchar(src, j + 2); if (bconchar(out, UU_ENCODE_BYTE((c0 & 0xFC) >> 2)) < 0 || bconchar(out, UU_ENCODE_BYTE(((c0 & 0x03) << 4) | ((c1 & 0xF0) >> 4))) < 0 || bconchar(out, UU_ENCODE_BYTE(((c1 & 0x0F) << 2) | ((c2 & 0xC0) >> 6))) < 0 || bconchar(out, UU_ENCODE_BYTE((c2 & 0x3F))) < 0) { bstrFree (out); goto exit; } } if (bconchar(out, (char)'\r') < 0 || bconchar(out, (char)'\n') < 0) { bstrFree(out); break; } } exit: return out; } bstring bYEncode(const bstring src) { int i; bstring out; unsigned char c; if (src == NULL || src->slen < 0 || src->data == NULL) { return NULL; } if ((out = bfromcstr("")) == NULL) { return NULL; } for (i = 0; i < src->slen; ++i) { c = (unsigned char)(src->data[i] + 42); if (c == '=' || c == '\0' || c == '\r' || c == '\n') { if (0 > bconchar (out, (char) '=')) { bdestroy (out); return NULL; } c += (unsigned char)64; } if (0 > bconchar(out, c)) { bdestroy(out); return NULL; } } return out; } #define MAX_OB_LEN (64) bstring bYDecode(const bstring src) { int i, obl; bstring out; unsigned char c, octetbuff[MAX_OB_LEN]; if (src == NULL || src->slen < 0 || src->data == NULL) { return NULL; } if ((out = bfromcstr ("")) == NULL) { return NULL; } obl = 0; for (i = 0; i < src->slen; i++) { if ('=' == (c = src->data[i])) { /* The = escape mode */ ++i; if (i >= src->slen) { bdestroy(out); return NULL; } c = (unsigned char)(src->data[i] - 64); } else { if ('\0' == c) { bdestroy(out); return NULL; } /* Extraneous CR/LFs are to be ignored. */ if (c == '\r' || c == '\n') { continue; } } octetbuff[obl] = (unsigned char)((int)c - 42); obl++; if (obl >= MAX_OB_LEN) { if (0 > bcatblk(out, octetbuff, obl)) { bdestroy(out); return NULL; } obl = 0; } } if (0 > bcatblk(out, octetbuff, obl)) { bdestroy(out); out = NULL; } return out; } /* int bSGMLEncode (bstring b) * * Change the string into a version that is quotable in SGML (HTML, XML). */ int bSGMLEncode(bstring b) { static struct tagbstring fr[4][2] = { { bsStatic("&"), bsStatic("&") }, { bsStatic("\""), bsStatic(""") }, { bsStatic("<"), bsStatic("<") }, { bsStatic(">"), bsStatic(">") } }; for (int i = 0; i < 4; i++) { int ret = bfindreplace(b, &fr[i][0], &fr[i][1], 0); if (0 > ret) return ret; } return 0; } bstring bStrfTime(const char * fmt, const struct tm * timeptr) { #if defined(__TURBOC__) && !defined(__BORLANDC__) static struct tagbstring ns = bsStatic ("bStrfTime Not supported"); fmt = fmt; timeptr = timeptr; return &ns; #else bstring buff; int n; size_t r; if (fmt == NULL) { return NULL; } /* Since the length is not determinable beforehand, a search is * performed using the truncating "strftime" call on increasing * potential sizes for the output result. */ if ((n = (int)(2 * strlen (fmt))) < 16) { n = 16; } buff = bfromcstralloc(n + 2, ""); while (1) { if (BSTR_OK != balloc(buff, n + 2)) { bdestroy (buff); return NULL; } r = strftime((char *)buff->data, n + 1, fmt, timeptr); if (r > 0) { buff->slen = (int) r; break; } n += n; } return buff; #endif } int bSetCstrChar(bstring b, int pos, char c) { if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) { return BSTR_ERR; } if (pos < 0 || pos > b->slen) { return BSTR_ERR; } if (pos == b->slen) { if ('\0' != c) return bconchar(b, c); return 0; } b->data[pos] = (unsigned char) c; if ('\0' == c) { b->slen = pos; } return 0; } int bSetChar(bstring b, int pos, char c) { if (NULL == b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen) { return BSTR_ERR; } if (pos < 0 || pos > b->slen) { return BSTR_ERR; } if (pos == b->slen) { return bconchar(b, c); } b->data[pos] = (unsigned char)c; return 0; } #define INIT_SECURE_INPUT_LENGTH (256) bstring bSecureInput(int maxlen, int termchar, bNgetc vgetchar, void *vgcCtx) { size_t i; size_t m; int c; int done; bstring b; bstring t; if (!vgetchar) { return NULL; } b = bfromcstralloc(INIT_SECURE_INPUT_LENGTH, ""); if (!b) { return NULL; } i = 0; done = 0; while (!done && (maxlen <= 0 || i < (size_t)maxlen)) { c = vgetchar(vgcCtx); if (EOF == c) { done = 1; continue; } if (i + 1 >= (size_t)b->mlen) { /* Double size, and deal with numeric overflows */ if (b->mlen <= INT_MAX / 2) { m = (size_t)b->mlen << 1; } else if (b->mlen <= INT_MAX - 1024) { m = (size_t)b->mlen + 1024; } else if (b->mlen <= INT_MAX - 16) { m = (size_t)b->mlen + 16; } else if (b->mlen <= INT_MAX - 1) { m = (size_t)b->mlen + 1; } else { bSecureDestroy(b); /* Cleanse partial buffer */ return NULL; } t = bfromcstrrangealloc(b->mlen + 1, (int)m, ""); if (t) { memcpy(t->data, b->data, i); } bSecureDestroy(b); /* Cleanse previous buffer */ b = t; if (!b) { return b; } } b->data[i++] = (unsigned char)c; done = (termchar == c); } b->slen = (int)i; b->data[i] = (unsigned char)'\0'; return b; } #define BWS_BUFF_SZ (BUFSIZ) struct bwriteStream { bstring buff; /* Buffer for underwrites */ void * parm; /* The stream handle for core stream */ bNwrite writeFn; /* fwrite work-a-like fnptr for core stream */ int isEOF; /* track stream's EOF state */ int minBuffSz; }; struct bwriteStream * bwsOpen(bNwrite writeFn, void *parm) { struct bwriteStream * ws; if (NULL == writeFn) { return NULL; } ws = (struct bwriteStream *)malloc(sizeof(struct bwriteStream)); if (ws) { if (NULL == (ws->buff = bfromcstr(""))) { free(ws); ws = NULL; } else { ws->parm = parm; ws->writeFn = writeFn; ws->isEOF = 0; ws->minBuffSz = BWS_BUFF_SZ; } } return ws; } #define internal_bwswriteout(ws,b) { \ if ((b)->slen > 0) { \ if (1 != (ws->writeFn ((b)->data, (b)->slen, 1, ws->parm))) { \ ws->isEOF = 1; \ return BSTR_ERR; \ } \ } \ } int bwsWriteFlush(struct bwriteStream *ws) { if (NULL == ws || ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn || NULL == ws->buff) { return BSTR_ERR; } internal_bwswriteout(ws, ws->buff); ws->buff->slen = 0; return 0; } int bwsWriteBstr(struct bwriteStream *ws, const bstring b) { struct tagbstring t; int l; if (NULL == ws || NULL == b || NULL == ws->buff || ws->isEOF || 0 >= ws->minBuffSz || NULL == ws->writeFn) { return BSTR_ERR; } /* Buffer prepacking optimization */ if (b->slen > 0 && ws->buff->mlen - ws->buff->slen > b->slen) { static struct tagbstring empty = bsStatic(""); if (0 > bconcat (ws->buff, b)) { return BSTR_ERR; } return bwsWriteBstr(ws, &empty); } if (0 > (l = ws->minBuffSz - ws->buff->slen)) { internal_bwswriteout(ws, ws->buff); ws->buff->slen = 0; l = ws->minBuffSz; } if (b->slen < l) { return bconcat(ws->buff, b); } if (0 > bcatblk (ws->buff, b->data, l)) { return BSTR_ERR; } internal_bwswriteout(ws, ws->buff); ws->buff->slen = 0; bmid2tbstr(t, (bstring)b, l, b->slen); if (t.slen >= ws->minBuffSz) { internal_bwswriteout(ws, &t); return 0; } return bassign(ws->buff, &t); } int bwsWriteBlk(struct bwriteStream *ws, void *blk, int len) { struct tagbstring t; if (NULL == blk || len < 0) { return BSTR_ERR; } blk2tbstr(t, blk, len); return bwsWriteBstr(ws, &t); } int bwsIsEOF(const struct bwriteStream * ws) { if (NULL == ws || NULL == ws->buff || 0 > ws->minBuffSz || NULL == ws->writeFn) { return BSTR_ERR; } return ws->isEOF; } int bwsBuffLength(struct bwriteStream *ws, int sz) { int oldSz; if (ws == NULL || sz < 0) { return BSTR_ERR; } oldSz = ws->minBuffSz; if (sz > 0) { ws->minBuffSz = sz; } return oldSz; } void * bwsClose(struct bwriteStream * ws) { void *parm = NULL; if (ws) { if (NULL == ws->buff || 0 >= ws->minBuffSz || NULL == ws->writeFn) { return NULL; } bwsWriteFlush(ws); parm = ws->parm; ws->parm = NULL; ws->minBuffSz = -1; ws->writeFn = NULL; bstrFree(ws->buff); free(ws); } return parm; } bstring-1.1.0/bstring/bstraux.h000066400000000000000000000266511516216374300165140ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /** * \file * \brief C example that implements trivial additional functions * * This file is not a necessary part of the core bstring library itself, but is * just an auxilliary module which includes miscellaneous or trivial functions. */ #ifndef BSTRAUX_H #define BSTRAUX_H #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef __cplusplus extern "C" { #endif /* Safety mechanisms */ #define bstrDeclare(b) \ bstring(b) = NULL #define bstrFree(b) \ do { \ if ((b) != NULL && (b)->slen >= 0 && (b)->mlen >= (b)->slen) { \ bdestroy(b); \ (b) = NULL; \ } \ } while (0) /* Backward compatibilty with previous versions of Bstrlib */ #if !defined(BSTRLIB_REDUCE_NAMESPACE_POLLUTION) #define bAssign(a, b) \ ((bassign)((a), (b))) #define bSubs(b, pos, len, a, c) \ ((breplace)((b),(pos),(len),(a),(unsigned char)(c))) #define bStrchr(b, c) \ ((bstrchr)((b), (c))) #define bStrchrFast(b, c) \ ((bstrchr)((b), (c))) #define bCatCstr(b, s) \ ((bcatcstr)((b), (s))) #define bCatBlk(b, s, len) \ ((bcatblk)((b), (s), (len))) #define bCatStatic(b, s) \ bcatStatic(b, s) #define bTrunc(b, n) \ ((btrunc)((b), (n))) #define bReplaceAll(b, find, repl, pos) \ ((bfindreplace)((b),(find),(repl),(pos))) #define bUppercase(b) \ ((btoupper)(b)) #define bLowercase(b) \ ((btolower)(b)) #define bCaselessCmp(a, b) \ ((bstricmp)((a), (b))) #define bCaselessNCmp(a, b, n) \ ((bstrnicmp)((a), (b), (n))) #define bBase64Decode(b) \ (bBase64DecodeEx((b), NULL)) #define bUuDecode(b) \ (bUuDecodeEx((b), NULL)) #endif /* Unusual functions */ /** * Create a bStream whose contents are a copy of the bstring passed in. * * This allows the use of all the bStream APIs with bstrings. */ BSTR_PUBLIC struct bStream * bsFromBstr(const bstring b); /** * Return with a string of the last n characters of b. */ BSTR_PUBLIC bstring bTail(bstring b, int n); /** * Return with a string of the first n characters of b. */ BSTR_PUBLIC bstring bHead(bstring b, int n); /** * Sets the character at position pos to the character c in the bstring a. * * If the character c is NUL ('\0') then the string is truncated at this point. * Note: this does not enable any other '\0' character in the bstring as * terminator indicator for the string. pos must be in the position between 0 * and b->slen inclusive, otherwise BSTR_ERR will be returned. */ BSTR_PUBLIC int bSetCstrChar(bstring a, int pos, char c); /** * Sets the character at position pos to the character c in the bstring a. * * The string is not truncated if the character c is NUL ('\0'). pos must be in * the position between 0 and b->slen inclusive, otherwise BSTR_ERR will be * returned. */ BSTR_PUBLIC int bSetChar(bstring b, int pos, char c); /** * Fill a given bstring with the character in parameter c, for a length n. */ BSTR_PUBLIC int bFill(bstring a, char c, int len); /** * Replicate the contents of b end to end n times and replace it in b. */ BSTR_PUBLIC int bReplicate(bstring b, int n); /** * Reverse the contents of b in place. */ BSTR_PUBLIC int bReverse(bstring b); /** * Insert a repeated sequence of a given character into the string at position * pos for a length len. */ BSTR_PUBLIC int bInsertChrs(bstring b, int pos, int len, unsigned char c, unsigned char fill); /** * Takes a format string that is compatible with strftime and a struct tm * pointer, formats the time according to the format string and outputs the * bstring as a result. * * Note that if there is an early generation of a '\0' character, the bstring * will be truncated to this end point. */ BSTR_PUBLIC bstring bStrfTime(const char * fmt, const struct tm * timeptr); #define bAscTime(t) (bStrfTime ("%c\n", (t))) #define bCTime(t) ((t) ? bAscTime (localtime (t)) : NULL) /* Spacing formatting */ /** * Left justify a string. */ BSTR_PUBLIC int bJustifyLeft(bstring b, int space); /** * Right justify a string to within a given width. */ BSTR_PUBLIC int bJustifyRight(bstring b, int width, int space); /** * Stretch a string to flush against left and right margins by evenly * distributing additional white space between words. * * If the line is too long to be margin justified, it is left justified. */ BSTR_PUBLIC int bJustifyMargin(bstring b, int width, int space); /** * Center a string's non-white space characters to within a given width by * inserting whitespaces at the beginning. */ BSTR_PUBLIC int bJustifyCenter(bstring b, int width, int space); /* Esoteric standards specific functions */ /** * Convert a bstring to a netstring. * * See http://cr.yp.to/proto/netstrings.txt for a description of netstrings. * * Note: 1) The value returned should be freed with a call to bcstrfree() at * the point when it will no longer be referenced to avoid a memory * leak. * 2) If the returned value is non-NULL, then it also '\0' terminated * in the character position one past the "," terminator. */ BSTR_PUBLIC char * bStr2NetStr(const bstring b); /** * Convert a netstring to a bstring. * * See http://cr.yp.to/proto/netstrings.txt for a description of netstrings. * * Note that the terminating "," *must* be present, however a following '\0' * is *not* required. */ BSTR_PUBLIC bstring bNetStr2Bstr(const char *buf); /** * Generate a base64 encoding. * * See: RFC1341 */ BSTR_PUBLIC bstring bBase64Encode(const bstring b); /** * Decode a base64 block of data. * * All MIME headers are assumed to have been removed. * * See: RFC1341 */ BSTR_PUBLIC bstring bBase64DecodeEx(const bstring b, int *boolTruncError); /** * Creates a bStream which performs the UUDecode of an an input stream. * * If there are errors in the decoding, they are counted up and returned in * "badlines", if badlines is not NULL. It is assumed that the "begin" and * "end" lines have already been stripped off. The potential security problem * of writing the filename in the begin line is something that is beyond the * scope of a portable library. */ BSTR_PUBLIC struct bStream * bsUuDecode(struct bStream *sInp, int *badlines); /** * Performs a UUDecode of a block of data. * * If there are errors in the decoding, they are counted up and returned in * "badlines", if badlines is not NULL. It is assumed that the "begin" and * "end" lines have already been stripped off. The potential security problem * of writing the filename in the begin line is something that is beyond the * scope of a portable library. */ BSTR_PUBLIC bstring bUuDecodeEx(const bstring src, int *badlines); /** * Performs a UUEncode of a block of data. * * The "begin" and "end" lines are not appended. */ BSTR_PUBLIC bstring bUuEncode(const bstring src); /** * Performs a YEncode of a block of data. * * No header or tail info is appended. * * See: https://yence.sourceforge.net/docs/protocol/version1_3_draft.html */ BSTR_PUBLIC bstring bYEncode(const bstring src); /** * Performs a YDecode of a block of data. * * See: https://yence.sourceforge.net/docs/protocol/version1_3_draft.html */ BSTR_PUBLIC bstring bYDecode(const bstring src); /** * Change the string into a version that is quotable in SGML (HTML, XML). * Replaces &, ", <, > with their SGML entity equivalents. * * @param b the bstring to encode in-place * @return BSTR_OK on success, BSTR_ERR on error */ BSTR_PUBLIC int bSGMLEncode(bstring b); /* Writable stream */ typedef int (*bNwrite)(const void *buf, size_t elsize, size_t nelem, void *parm); /** * Wrap a given open stream (described by a fwrite work-a-like function pointer * and stream handle) into an open bwriteStream suitable for write streaming * functions. */ BSTR_PUBLIC struct bwriteStream * bwsOpen(bNwrite writeFn, void *parm); /** * Send a bstring to a bwriteStream. * * If the stream is at EOF BSTR_ERR is returned. Note that there is no * deterministic way to determine the exact cut off point where the core stream * stopped accepting data. */ BSTR_PUBLIC int bwsWriteBstr(struct bwriteStream *stream, const bstring b); /** * Send a block of data a bwriteStream. * * If the stream is at EOF BSTR_ERR is returned. */ BSTR_PUBLIC int bwsWriteBlk(struct bwriteStream *stream, void *blk, int len); /** * Force any pending data to be written to the core stream. */ BSTR_PUBLIC int bwsWriteFlush(struct bwriteStream *stream); /** * Returns 0 if the stream is currently writable, 1 if the core stream has * responded by not accepting the previous attempted write. */ BSTR_PUBLIC int bwsIsEOF(const struct bwriteStream *stream); /** * Set the length of the buffer used by the bwsStream. * * If sz is zero, the length is not set. This function returns with the * previous length. */ BSTR_PUBLIC int bwsBuffLength(struct bwriteStream *stream, int sz); /** * Close the bwriteStream, and return the handle to the stream that was * originally used to open the given stream. * * Note that even if the stream is at EOF it still needs to be closed with a * call to bwsClose. */ BSTR_PUBLIC void * bwsClose(struct bwriteStream *stream); /* Security functions */ #define bSecureDestroy(b) \ do { \ if ((b) && (b)->mlen > 0 && (b)->data) { \ (void)memset((b)->data, 0, (size_t)(b)->mlen); \ } \ (void)bdestroy((b)); \ } while (0) #define bSecureWriteProtect(t) \ do { \ if ((t).mlen >= 0) { \ if ((t).mlen > (t).slen)) { \ (void)memset((t).data + (t).slen, 0, \ (size_t)(t).mlen - (t).slen); \ } \ (t).mlen = -1; \ } \ } while (0) /** * Read input from an abstracted input interface, for a length of at most * maxlen characters. * * If maxlen <= 0, then there is no length limit put on the input. The result * is terminated early if vgetchar() return EOF or the user specified value * termchar. * */ BSTR_PUBLIC bstring bSecureInput(int maxlen, int termchar, bNgetc vgetchar, void *vgcCtx); #ifdef __cplusplus } #endif #endif /* BSTRAUX_H */ bstring-1.1.0/bstring/bstrlib.c000066400000000000000000001723621516216374300164610ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /* * This file is the core module for implementing the bstring functions. */ #ifdef HAVE_CONFIG_H #include #endif #if defined (_MSC_VER) /* These warnings from MSVC++ are totally pointless. */ # define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include #include #include "bstrlib.h" /* Just a length safe wrapper for memmove. */ #define bBlockCopy(D, S, L) \ do { \ if ((L) > 0) { \ memmove((D), (S), (L)); \ } \ } while (0) /** * Compute the snapped size for a given requested size. * * By snapping to powers of 2 like this, repeated reallocations are avoided. * */ static int snapUpSize(int i) { if (i < 8) { i = 8; } else { unsigned int j; j = (unsigned int)i; j |= (j >> 1); j |= (j >> 2); j |= (j >> 4); j |= (j >> 8); /* Ok, since int >= 16 bits */ #if (UINT_MAX != 0xffff) j |= (j >> 16); /* For 32 bit int systems */ #if (UINT_MAX > 0xffffffffUL) j |= (j >> 32); /* For 64 bit int systems */ #endif #endif /* Least power of two greater than i */ j++; if ((int)j >= i) { i = (int)j; } } return i; } int balloc(bstring b, int olen) { int len; if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen || olen <= 0) { return BSTR_ERR; } if (olen >= b->mlen) { unsigned char *x; if ((len = snapUpSize(olen)) <= b->mlen) { return BSTR_OK; } /* Assume probability of a non-moving realloc is 0.125 */ if (7 * b->mlen < 8 * b->slen) { /* If slen is close to mlen in size then use realloc * to reduce the memory defragmentation */ retry: x = realloc(b->data, len); if (x == NULL) { /* Since we failed, try mallocating the tightest * possible mallocation */ len = olen; x = realloc(b->data, len); if (!x) { return BSTR_ERR; } } } else { /* If slen is not close to mlen then avoid the penalty * of copying the extra bytes that are mallocated, but * not considered part of the string */ x = malloc(len); if (!x) { /* Perhaps there is no available memory for the * two mallocations to be in memory at once */ goto retry; } else { if (b->slen) { memcpy(x, b->data, b->slen); } free(b->data); } } b->data = x; b->mlen = len; b->data[b->slen] = (unsigned char)'\0'; #if defined (BSTRLIB_TEST_CANARY) if (len > b->slen + 1) { memchr (b->data + b->slen + 1, 'X', len - (b->slen + 1)); } #endif } return BSTR_OK; } int ballocmin(bstring b, int len) { unsigned char *s; if (b == NULL || b->data == NULL) { return BSTR_ERR; } if (b->slen >= INT_MAX || b->slen < 0) { return BSTR_ERR; } if (b->mlen <= 0 || b->mlen < b->slen || len <= 0) { return BSTR_ERR; } if (len < b->slen + 1) { len = b->slen + 1; } if (len != b->mlen) { s = realloc(b->data, (size_t)len); if (NULL == s) { return BSTR_ERR; } s[b->slen] = (unsigned char)'\0'; b->data = s; b->mlen = len; } return BSTR_OK; } bstring bfromcstr(const char *str) { bstring b; int i; size_t j; if (str == NULL) { return NULL; } j = strlen(str); i = snapUpSize((int)(j + (2 - (j != 0)))); if (i <= (int)j) { return NULL; } b = malloc(sizeof(struct tagbstring)); if (!b) { return NULL; } b->slen = (int)j; b->mlen = i; b->data = malloc(b->mlen); if (!b->data) { free (b); return NULL; } memcpy(b->data, str, j + 1); return b; } bstring bfromcstrrangealloc(int minl, int maxl, const char *str) { bstring b; int i; size_t j; /* Bad parameters? */ if (str == NULL) { return NULL; } if (maxl < minl || minl < 0) { return NULL; } /* Adjust lengths */ j = strlen(str); if ((size_t)minl < (j + 1)) { minl = (int)(j + 1); } if (maxl < minl) { maxl = minl; } i = maxl; b = malloc(sizeof(struct tagbstring)); if (b == NULL) { return NULL; } b->slen = (int)j; while (1) { b->data = malloc(i); if (b->data != NULL) { b->mlen = i; break; } int k = (i >> 1) + (minl >> 1); if (i == k || i < minl) { free(b); return NULL; } i = k; } bBlockCopy(b->data, str, j + 1); return b; } bstring bfromcstralloc(int mlen, const char *str) { return bfromcstrrangealloc(mlen, mlen, str); } bstring blk2bstr(const void *blk, int len) { bstring b; int i; if (blk == NULL || len < 0) { return NULL; } b = malloc(sizeof(struct tagbstring)); if (b == NULL) { return NULL; } b->slen = len; i = len + (2 - (len != 0)); i = snapUpSize(i); b->mlen = i; b->data = malloc(b->mlen); if (!b->data) { free(b); return NULL; } if (len > 0) { memcpy(b->data, blk, len); } if (b->mlen > len) { b->data[len] = (unsigned char)'\0'; } else { free(b); return NULL; } return b; } char * bstr2cstr(const bstring b, char z) { int i, l; char *r; if (!b || b->slen < 0 || !b->data) { return NULL; } l = b->slen; r = malloc((size_t)(l + 1)); if (r == NULL) { return r; } for (i = 0; i < l; i ++) { r[i] = (char)((b->data[i] == '\0') ? z : (char)(b->data[i])); } r[l] = (unsigned char)'\0'; return r; } int bcstrfree(char *s) { free(s); return BSTR_OK; } int bconcat(bstring b0, const bstring b1) { int len, d; bstring aux = b1; if (!b0 || !b1 || !b0->data || !b1->data) { return BSTR_ERR; } d = b0->slen; len = b1->slen; if ((d | (b0->mlen - d) | len | (d + len)) < 0) { return BSTR_ERR; } if (b0->mlen <= d + len + 1) { ptrdiff_t pd = b1->data - b0->data; if (0 <= pd && pd < b0->mlen) { aux = bstrcpy(b1); if (!aux) { return BSTR_ERR; } } if (balloc(b0, d + len + 1) != BSTR_OK) { if (aux != b1) { bdestroy(aux); } return BSTR_ERR; } } bBlockCopy(&b0->data[d], &aux->data[0], len); b0->data[d + len] = (unsigned char)'\0'; b0->slen = d + len; if (aux != b1) { bdestroy(aux); } return BSTR_OK; } int bconchar(bstring b, char c) { int d; if (!b) { return BSTR_ERR; } d = b->slen; if ((d | (b->mlen - d)) < 0 || balloc(b, d + 2) != BSTR_OK) { return BSTR_ERR; } b->data[d] = (unsigned char)c; b->data[d + 1] = (unsigned char)'\0'; b->slen++; return BSTR_OK; } int bcatcstr(bstring b, const char *s) { char *d; int i, l; if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen || b->mlen <= 0 || s == NULL) { return BSTR_ERR; } /* Optimistically concatenate directly */ l = b->mlen - b->slen; d = (char *)&b->data[b->slen]; for (i = 0; i < l; ++i) { if ((*d++ = *s++) == '\0') { b->slen += i; return BSTR_OK; } } b->slen += i; /* Need to explicitely resize and concatenate tail */ return bcatblk(b, s, (int)strlen(s)); } int bcatblk(bstring b, const void *s, int len) { int nl; if (!b || !b->data || b->slen < 0 || b->mlen < b->slen || b->mlen <= 0 || !s || len < 0) { return BSTR_ERR; } if (0 > (nl = b->slen + len)) { /* Overflow? */ return BSTR_ERR; } if (b->mlen <= nl && 0 > balloc(b, nl + 1)) { return BSTR_ERR; } bBlockCopy(&b->data[b->slen], s, len); b->slen = nl; b->data[nl] = (unsigned char)'\0'; return BSTR_OK; } bstring bstrcpy(const bstring b) { bstring b0; int i, j; /* Attempted to copy an invalid string? */ if (!b || b->slen < 0 || !b->data) { return NULL; } b0 = malloc(sizeof(struct tagbstring)); if (!b0) { /* Unable to mallocate memory for string header */ return NULL; } i = b->slen; j = snapUpSize(i + 1); b0->data = malloc(j); if (b0->data == NULL) { j = i + 1; b0->data = (unsigned char *)malloc(j); if (b0->data == NULL) { /* Unable to mallocate memory for string data */ free(b0); return NULL; } } b0->mlen = j; b0->slen = i; if (i) { memcpy(b0->data, b->data, i); } b0->data[b0->slen] = (unsigned char)'\0'; return b0; } int bassign(bstring a, const bstring b) { if (!b || !b->data || b->slen < 0) { return BSTR_ERR; } if (b->slen != 0) { if (balloc(a, b->slen) != BSTR_OK) { return BSTR_ERR; } memmove(a->data, b->data, b->slen); } else { if (!a || !a->data || a->mlen < a->slen || a->slen < 0 || a->mlen == 0) { return BSTR_ERR; } } a->data[b->slen] = (unsigned char)'\0'; a->slen = b->slen; return BSTR_OK; } int bassignmidstr(bstring a, const bstring b, int left, int len) { if (!b || !b->data || b->slen < 0) { return BSTR_ERR; } if (left < 0) { len += left; left = 0; } if (len > b->slen - left) { len = b->slen - left; } if (!a || !a->data || a->mlen < a->slen || a->slen < 0 || a->mlen == 0) { return BSTR_ERR; } if (len > 0) { if (balloc(a, len) != BSTR_OK) { return BSTR_ERR; } memmove(a->data, b->data + left, len); a->slen = len; } else { a->slen = 0; } a->data[a->slen] = (unsigned char)'\0'; return BSTR_OK; } int bassigncstr(bstring a, const char *str) { int i; size_t len; if (!a || !a->data || a->mlen < a->slen || a->slen < 0 || a->mlen == 0 || !str) { return BSTR_ERR; } for (i = 0; i < a->mlen; ++i) { if ('\0' == (a->data[i] = str[i])) { a->slen = i; return BSTR_OK; } } a->slen = i; len = strlen(str + i); if (len + 1 > (size_t)INT_MAX - i || 0 > balloc(a, (int)(i + len + 1))) { return BSTR_ERR; } bBlockCopy(a->data + i, str + i, (size_t)len + 1); a->slen += (int)len; return BSTR_OK; } int bassignblk(bstring a, const void *s, int len) { if (!a || !a->data || a->mlen < a->slen || a->slen < 0 || a->mlen == 0 || !s || len < 0 || len >= INT_MAX) { return BSTR_ERR; } if (len + 1 > a->mlen && 0 > balloc(a, len + 1)) { return BSTR_ERR; } bBlockCopy(a->data, s, len); a->data[len] = (unsigned char)'\0'; a->slen = len; return BSTR_OK; } int btrunc(bstring b, int n) { if (n < 0 || !b || !b->data || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } if (b->slen > n) { b->slen = n; b->data[n] = (unsigned char)'\0'; } return BSTR_OK; } #define upcase(c) \ (toupper((unsigned char)c)) #define downcase(c) \ (tolower((unsigned char)c)) #define wspace(c) \ (isspace((unsigned char)c)) int btoupper(bstring b) { int i, len; if (b == NULL || b->data == NULL || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } for (i = 0, len = b->slen; i < len; i++) { b->data[i] = (unsigned char)upcase(b->data[i]); } return BSTR_OK; } int btolower(bstring b) { int i, len; if (b == NULL || b->data == NULL || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } for (i = 0, len = b->slen; i < len; i++) { b->data[i] = (unsigned char)downcase(b->data[i]); } return BSTR_OK; } int bstricmp(const bstring b0, const bstring b1) { int i, v, n; if (bdata (b0) == NULL || b0->slen < 0 || bdata (b1) == NULL || b1->slen < 0) { return SHRT_MIN; } if ((n = b0->slen) > b1->slen) { n = b1->slen; } else if (b0->slen == b1->slen && b0->data == b1->data) { return BSTR_OK; } for (i = 0; i < n; i ++) { v = (char)downcase(b0->data[i]) - (char)downcase(b1->data[i]); if (0 != v) { return v; } } if (b0->slen > n) { v = (char)downcase(b0->data[n]); if (v) { return v; } return UCHAR_MAX + 1; } if (b1->slen > n) { v = - (char)downcase(b1->data[n]); if (v) { return v; } return -(int)(UCHAR_MAX + 1); } return BSTR_OK; } int bstrnicmp(const bstring b0, const bstring b1, int n) { int i, v, m; if (bdata (b0) == NULL || b0->slen < 0 || bdata (b1) == NULL || b1->slen < 0 || n < 0) { return SHRT_MIN; } m = n; if (m > b0->slen) { m = b0->slen; } if (m > b1->slen) { m = b1->slen; } if (b0->data != b1->data) { for (i = 0; i < m; i ++) { v = (char)downcase(b0->data[i]); v -= (char)downcase(b1->data[i]); if (v != 0) { return b0->data[i] - b1->data[i]; } } } if (n == m || b0->slen == b1->slen) { return BSTR_OK; } if (b0->slen > m) { v = (char)downcase(b0->data[m]); if (v) { return v; } return UCHAR_MAX + 1; } v = - (char)downcase(b1->data[m]); if (v) { return v; } return -(int)(UCHAR_MAX + 1); } int biseqcaselessblk(const bstring b, const void *blk, int len) { int i; if (bdata(b) == NULL || b->slen < 0 || blk == NULL || len < 0) { return BSTR_ERR; } if (b->slen != len) { return 0; } if (len == 0 || b->data == blk) { return 1; } for (i = 0; i < len; i++) { if (b->data[i] != ((const unsigned char *)blk)[i]) { unsigned char c = (unsigned char)downcase(b->data[i]); if (c != (unsigned char)downcase(((unsigned char *)blk)[i])) { return 0; } } } return 1; } int biseqcaseless(const bstring b0, const bstring b1) { if (NULL == b1) { return BSTR_ERR; } return biseqcaselessblk(b0, b1->data, b1->slen); } int bisstemeqcaselessblk(const bstring b0, const void *blk, int len) { int i; if (bdata(b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) { return BSTR_ERR; } if (b0->slen < len) { return BSTR_OK; } if (b0->data == (const unsigned char *)blk || len == 0) { return 1; } for (i = 0; i < len; i++) { if (b0->data[i] != ((const unsigned char *)blk)[i]) { if (downcase(b0->data[i]) != downcase(((const unsigned char *)blk)[i])) { return 0; } } } return 1; } int bltrimws(bstring b) { int i, len; if (!b || !b->data || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } for (len = b->slen, i = 0; i < len; i++) { if (!wspace(b->data[i])) { return bdelete(b, 0, i); } } b->data[0] = (unsigned char) '\0'; b->slen = 0; return BSTR_OK; } int brtrimws(bstring b) { int i; if (b == NULL || b->data == NULL || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } for (i = b->slen - 1; i >= 0; i--) { if (!wspace(b->data[i])) { if (b->mlen > i) { b->data[i + 1] = (unsigned char)'\0'; } b->slen = i + 1; return BSTR_OK; } } b->data[0] = (unsigned char)'\0'; b->slen = 0; return BSTR_OK; } int btrimws(bstring b) { int i, j; if (b == NULL || b->data == NULL || b->mlen < b->slen || b->slen < 0 || b->mlen <= 0) { return BSTR_ERR; } for (i = b->slen - 1; i >= 0; i--) { if (!wspace(b->data[i])) { if (b->mlen > i) { b->data[i + 1] = (unsigned char)'\0'; } b->slen = i + 1; for (j = 0; wspace (b->data[j]); j++) ; return bdelete(b, 0, j); } } b->data[0] = (unsigned char)'\0'; b->slen = 0; return BSTR_OK; } int biseqblk(const bstring b, const void *blk, int len) { if (len < 0 || b == NULL || blk == NULL || b->data == NULL || b->slen < 0) { return BSTR_ERR; } if (b->slen != len) { return 0; } if (len == 0 || b->data == blk) { return 1; } return !memcmp(b->data, blk, len); } int biseq(const bstring b0, const bstring b1) { if (!b0 || !b1 || !b0->data || !b1->data || b0->slen < 0 || b1->slen < 0) { return BSTR_ERR; } if (b0->slen != b1->slen) { return BSTR_OK; } if (b0->data == b1->data || b0->slen == 0) { return 1; } return !memcmp(b0->data, b1->data, b0->slen); } int bisstemeqblk(const bstring b0, const void *blk, int len) { if (!bdata(b0) || b0->slen < 0 || !blk || len < 0) { return BSTR_ERR; } if (b0->slen < len) { return BSTR_OK; } if (b0->data == (const unsigned char *)blk || len == 0) { return 1; } for (int i = 0; i < len; i ++) { if (b0->data[i] != ((const unsigned char *)blk)[i]) { return BSTR_OK; } } return 1; } int biseqcstr(const bstring b, const char *s) { int i; if (!b || !s || !b->data || b->slen < 0) { return BSTR_ERR; } for (i = 0; i < b->slen; i++) { if (s[i] == '\0' || b->data[i] != (unsigned char)s[i]) { return BSTR_OK; } } return s[i] == '\0'; } int biseqcstrcaseless(const bstring b, const char *s) { int i; if (!b || !s || !b->data || b->slen < 0) { return BSTR_ERR; } for (i = 0; i < b->slen; i++) { if (s[i] == '\0' || (b->data[i] != (unsigned char)s[i] && downcase(b->data[i]) != (unsigned char)downcase(s[i]))) { return BSTR_OK; } } return s[i] == '\0'; } int bstrcmp(const bstring b0, const bstring b1) { int i, v, n; if (!b0 || !b1 || !b0->data || !b1->data || b0->slen < 0 || b1->slen < 0) { return SHRT_MIN; } n = b0->slen; if (n > b1->slen) { n = b1->slen; } if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0)) { return BSTR_OK; } for (i = 0; i < n; i ++) { v = ((char)b0->data[i]) - ((char)b1->data[i]); if (v != 0) { return v; } if (b0->data[i] == (unsigned char)'\0') { return BSTR_OK; } } if (b0->slen > n) { return 1; } if (b1->slen > n) { return -1; } return BSTR_OK; } int bstrncmp(const bstring b0, const bstring b1, int n) { int i, v, m; if (!b0 || !b1 || !b0->data || !b1->data || b0->slen < 0 || b1->slen < 0) { return SHRT_MIN; } m = n; if (m > b0->slen) { m = b0->slen; } if (m > b1->slen) { m = b1->slen; } if (b0->data != b1->data) { for (i = 0; i < m; i++) { v = ((char)b0->data[i]) - ((char)b1->data[i]); if (v != 0) { return v; } if (b0->data[i] == (unsigned char)'\0') { return BSTR_OK; } } } if (n == m || b0->slen == b1->slen) { return BSTR_OK; } if (b0->slen > m) { return 1; } return -1; } bstring bmidstr(const bstring b, int left, int len) { if (b == NULL || b->slen < 0 || b->data == NULL) { return NULL; } if (left < 0) { len += left; left = 0; } if (len > b->slen - left) { len = b->slen - left; } if (len <= 0) { return bfromcstr(""); } return blk2bstr(b->data + left, len); } int bdelete(bstring b, int pos, int len) { /* Clamp to left side of bstring */ if (pos < 0) { len += pos; pos = 0; } if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen || b->mlen <= 0) { return BSTR_ERR; } if (len > 0 && pos < b->slen) { if (pos + len >= b->slen) { b->slen = pos; } else { bBlockCopy((char *)(b->data + pos), (char *)(b->data + pos + len), b->slen - (pos+len)); b->slen -= len; } b->data[b->slen] = (unsigned char)'\0'; } return BSTR_OK; } int bdestroy(bstring b) { if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen || b->data == NULL) { return BSTR_ERR; } if (b->data != NULL) { free(b->data); } /* In case there is any stale usage, there is one more chance to * notice this error. */ b->slen = -1; b->mlen = -__LINE__; b->data = NULL; free(b); return BSTR_OK; } int binstr(const bstring b1, int pos, const bstring b2) { int j, ii, ll, lf; unsigned char *d0; unsigned char c0; register unsigned char *d1; register unsigned char c1; register int i; if (b1 == NULL || b1->data == NULL || b1->slen < 0 || b2 == NULL || b2->data == NULL || b2->slen < 0) { return BSTR_ERR; } if (b1->slen == pos) { return (b2->slen == 0) ? pos : BSTR_ERR; } if (b1->slen < pos || pos < 0) { return BSTR_ERR; } if (b2->slen == 0) { return pos; } /* No space to find such a string? */ if ((lf = b1->slen - b2->slen + 1) <= pos) { return BSTR_ERR; } /* An obvious alias case */ if (b1->data == b2->data && pos == 0) { return 0; } i = pos; d0 = b2->data; d1 = b1->data; ll = b2->slen; /* Peel off the b2->slen == 1 case */ c0 = d0[0]; if (1 == ll) { for (; i < lf; i++) { if (c0 == d1[i]) { return i; } } return BSTR_ERR; } c1 = c0; j = 0; lf = b1->slen - 1; ii = -1; if (i < lf) { do { /* Unrolled current character test */ if (c1 != d1[i]) { if (c1 != d1[1+i]) { i += 2; continue; } i++; } /* Take note if this is the start of a potential * match */ if (0 == j) { ii = i; } /* Shift the test character down by one */ j++; i++; /* If this isn't past the last character continue */ if (j < ll) { c1 = d0[j]; continue; } N0: /* If no characters mismatched, then we matched */ if (i == ii + j) { return ii; } /* Shift back to the beginning */ i -= j; j = 0; c1 = c0; } while (i < lf); } /* Deal with last case if unrolling caused a misalignment */ if (i == lf && ll == j + 1 && c1 == d1[i]) { goto N0; } return BSTR_ERR; } int binstrr(const bstring b1, int pos, const bstring b2) { int j, i, l; unsigned char *d0, *d1; if (b1 == NULL || b1->data == NULL || b1->slen < 0 || b2 == NULL || b2->data == NULL || b2->slen < 0) { return BSTR_ERR; } if (b1->slen == pos && b2->slen == 0) { return pos; } if (b1->slen < pos || pos < 0) { return BSTR_ERR; } if (b2->slen == 0) { return pos; } /* Obvious alias case */ if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) { return 0; } i = pos; if ((l = b1->slen - b2->slen) < 0) { return BSTR_ERR; } /* If no space to find such a string then snap back */ if (l + 1 <= i) { i = l; } j = 0; d0 = b2->data; d1 = b1->data; l = b2->slen; while (1) { if (d0[j] == d1[i + j]) { j++; if (j >= l) { return i; } } else { i--; if (i < 0) { break; } j = 0; } } return BSTR_ERR; } int binstrcaseless(const bstring b1, int pos, const bstring b2) { int j, i, l, ll; unsigned char *d0, *d1; if (b1 == NULL || b1->data == NULL || b1->slen < 0 || b2 == NULL || b2->data == NULL || b2->slen < 0) { return BSTR_ERR; } if (b1->slen == pos) { return (b2->slen == 0) ? pos : BSTR_ERR; } if (b1->slen < pos || pos < 0) { return BSTR_ERR; } if (b2->slen == 0) { return pos; } l = b1->slen - b2->slen + 1; /* No space to find such a string? */ if (l <= pos) { return BSTR_ERR; } /* An obvious alias case */ if (b1->data == b2->data && pos == 0) { return BSTR_OK; } i = pos; j = 0; d0 = b2->data; d1 = b1->data; ll = b2->slen; while (1) { if (d0[j] == d1[i + j] || downcase(d0[j]) == downcase (d1[i + j])) { j++; if (j >= ll) { return i; } } else { i ++; if (i >= l) { break; } j = 0; } } return BSTR_ERR; } int binstrrcaseless(const bstring b1, int pos, const bstring b2) { int j, i, l; unsigned char *d0, *d1; if (b1 == NULL || b1->data == NULL || b1->slen < 0 || b2 == NULL || b2->data == NULL || b2->slen < 0) { return BSTR_ERR; } if (b1->slen == pos && b2->slen == 0) { return pos; } if (b1->slen < pos || pos < 0) { return BSTR_ERR; } if (b2->slen == 0) { return pos; } /* Obvious alias case */ if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) { return BSTR_OK; } i = pos; if ((l = b1->slen - b2->slen) < 0) { return BSTR_ERR; } /* If no space to find such a string then snap back */ if (l + 1 <= i) { i = l; } j = 0; d0 = b2->data; d1 = b1->data; l = b2->slen; while (1) { if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase(d1[i + j])){ j++; if (j >= l) { return i; } } else { i--; if (i < 0) { break; } j = 0; } } return BSTR_ERR; } int bstrchrp(const bstring b, int c, int pos) { unsigned char *p; if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) { return BSTR_ERR; } p = (unsigned char *)memchr((b->data + pos), (unsigned char)c, (b->slen - pos)); if (p) { return (int)(p - b->data); } return BSTR_ERR; } int bstrrchrp(const bstring b, int c, int pos) { int i; if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) { return BSTR_ERR; } for (i = pos; i >= 0; i--) { if (b->data[i] == (unsigned char)c) { return i; } } return BSTR_ERR; } #ifndef BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF #define LONG_LOG_BITS_QTY (3) #define LONG_BITS_QTY \ (1 << LONG_LOG_BITS_QTY) #define LONG_TYPE unsigned char #define CFCLEN \ ((1 << CHAR_BIT) / LONG_BITS_QTY) struct charField { LONG_TYPE content[CFCLEN]; }; #define testInCharField(cf, c) \ ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1)))) #define setInCharField(cf, idx) \ do { \ unsigned int c = (unsigned int)(idx); \ (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE)(1ul << (c & (LONG_BITS_QTY-1))); \ } while (0) #else /* BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF */ #define CFCLEN \ (1 << CHAR_BIT) struct charField { unsigned char content[CFCLEN]; }; #define testInCharField(cf, c) \ ((cf)->content[(unsigned char) (c)]) #define setInCharField(cf, idx) \ (cf)->content[(unsigned int) (idx)] = ~0 #endif /* BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF */ /* Convert a bstring to charField */ static int buildCharField(struct charField *cf, const bstring b) { int i; if (b == NULL || b->data == NULL || b->slen <= 0) { return BSTR_ERR; } memset((void *)cf->content, 0, sizeof(struct charField)); for (i = 0; i < b->slen; i++) { setInCharField(cf, b->data[i]); } return BSTR_OK; } static void invertCharField (struct charField *cf) { int i; for (i = 0; i < CFCLEN; i++) { cf->content[i] = ~cf->content[i]; } } /* Inner engine for binchr */ static int binchrCF(const unsigned char *data, int len, int pos, const struct charField *cf) { int i; for (i = pos; i < len; i++) { unsigned char c = (unsigned char)data[i]; if (testInCharField(cf, c)) { return i; } } return BSTR_ERR; } int binchr(const bstring b0, int pos, const bstring b1) { struct charField chrs; if (pos < 0 || b0 == NULL || b0->data == NULL || b0->slen <= pos) { return BSTR_ERR; } if (1 == b1->slen) { return bstrchrp(b0, b1->data[0], pos); } if (0 > buildCharField (&chrs, b1)) { return BSTR_ERR; } return binchrCF(b0->data, b0->slen, pos, &chrs); } /* Inner engine for binchrr */ static int binchrrCF(const unsigned char *data, int pos, const struct charField *cf) { int i; for (i = pos; i >= 0; i--) { unsigned int c = (unsigned int)data[i]; if (testInCharField(cf, c)) { return i; } } return BSTR_ERR; } int binchrr(const bstring b0, int pos, const bstring b1) { struct charField chrs; if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL || b0->slen < pos) { return BSTR_ERR; } if (pos == b0->slen) { pos--; } if (1 == b1->slen) { return bstrrchrp(b0, b1->data[0], pos); } if (0 > buildCharField(&chrs, b1)) { return BSTR_ERR; } return binchrrCF(b0->data, pos, &chrs); } int bninchr(const bstring b0, int pos, const bstring b1) { struct charField chrs; if (pos < 0 || b0 == NULL || b0->data == NULL || b0->slen <= pos) { return BSTR_ERR; } if (buildCharField(&chrs, b1) < 0) { return BSTR_ERR; } invertCharField(&chrs); return binchrCF(b0->data, b0->slen, pos, &chrs); } int bninchrr(const bstring b0, int pos, const bstring b1) { struct charField chrs; if (pos < 0 || b0 == NULL || b0->data == NULL || b0->slen < pos) { return BSTR_ERR; } if (pos == b0->slen) { pos--; } if (buildCharField(&chrs, b1) < 0) { return BSTR_ERR; } invertCharField(&chrs); return binchrrCF(b0->data, pos, &chrs); } int bsetstr(bstring b0, int pos, const bstring b1, unsigned char fill) { int d, newlen; ptrdiff_t pd; bstring aux = (bstring) b1; if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || b0->mlen < b0->slen || b0->mlen <= 0) { return BSTR_ERR; } if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) { return BSTR_ERR; } d = pos; /* Aliasing case */ if (NULL != aux) { if ((pd = (ptrdiff_t)(b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) { if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; } d += aux->slen; } /* Increase memory size if necessary */ if (balloc(b0, d + 1) != BSTR_OK) { if (aux != b1) { bdestroy (aux); } return BSTR_ERR; } newlen = b0->slen; /* Fill in "fill" character as necessary */ if (pos > newlen) { memset(b0->data + b0->slen, (int)fill, (size_t)(pos - b0->slen)); newlen = pos; } /* Copy b1 to position pos in b0. */ if (aux != NULL) { bBlockCopy((char *)(b0->data + pos), (char *)aux->data, aux->slen); if (aux != b1) { bdestroy(aux); } } /* Indicate the potentially increased size of b0 */ if (d > newlen) { newlen = d; } b0->slen = newlen; b0->data[newlen] = (unsigned char)'\0'; return BSTR_OK; } int binsertblk(bstring b, int pos, const void *blk, int len, unsigned char fill) { int d, l; unsigned char *aux = (unsigned char *)blk; if (b == NULL || blk == NULL || pos < 0 || len < 0 || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen) { return BSTR_ERR; } /* Compute the two possible end pointers */ d = b->slen + len; l = pos + len; if ((d|l) < 0) { return BSTR_ERR; /* Integer wrap around. */ } /* Aliasing case */ if (((size_t)((const unsigned char *)blk + len)) >= ((size_t)b->data) && ((size_t)blk) < ((size_t)(b->data + b->mlen))) { if (NULL == (aux = (unsigned char *)malloc(len))) { return BSTR_ERR; } memcpy(aux, blk, len); } if (l > d) { /* Inserting past the end of the string */ if (balloc(b, l + 1) != BSTR_OK) { if (aux != (const unsigned char *)blk) { free(aux); } return BSTR_ERR; } memset(b->data + b->slen, (int)fill, (size_t)(pos - b->slen)); b->slen = l; } else { /* Inserting in the middle of the string */ if (balloc(b, d + 1) != BSTR_OK) { if (aux != (const unsigned char *)blk) { free(aux); } return BSTR_ERR; } bBlockCopy(b->data + l, b->data + pos, d - l); b->slen = d; } bBlockCopy(b->data + pos, aux, len); b->data[b->slen] = (unsigned char)'\0'; if (aux != (const unsigned char *)blk) { free(aux); } return BSTR_OK; } int binsert(bstring b1, int pos, const bstring b2, unsigned char fill) { if (NULL == b2 || (b2->mlen > 0 && b2->slen > b2->mlen)) { return BSTR_ERR; } return binsertblk(b1, pos, b2->data, b2->slen, fill); } int breplace(bstring b1, int pos, int len, const bstring b2, unsigned char fill) { int pl, ret; ptrdiff_t pd; bstring aux = (bstring) b2; if (pos < 0 || len < 0) { return BSTR_ERR; } if (pos > INT_MAX - len) { /* Overflow */ return BSTR_ERR; } pl = pos + len; if (b1 == NULL || b2 == NULL || b1->data == NULL || b2->data == NULL || b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) { return BSTR_ERR; } /* Straddles the end? */ if (pl >= b1->slen) { if ((ret = bsetstr (b1, pos, b2, fill)) < 0) { return ret; } if (pos + b2->slen < b1->slen) { b1->slen = pos + b2->slen; b1->data[b1->slen] = (unsigned char) '\0'; } return ret; } /* Aliasing case */ pd = (ptrdiff_t)(b2->data - b1->data); if (pd >= 0 && pd < (ptrdiff_t)b1->slen) { aux = bstrcpy(b2); if (!aux) { return BSTR_ERR; } } if (aux->slen > len) { if (balloc(b1, b1->slen + aux->slen - len) != BSTR_OK) { if (aux != b2) { bdestroy(aux); } return BSTR_ERR; } } if (aux->slen != len) { memmove(b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len)); } memcpy(b1->data + pos, aux->data, aux->slen); b1->slen += aux->slen - len; b1->data[b1->slen] = (unsigned char)'\0'; if (aux != b2) { bdestroy(aux); } return BSTR_OK; } typedef int (*instr_fnptr)(const bstring s1, int pos, const bstring s2); #define INITIAL_STATIC_FIND_INDEX_COUNT 32 /* * findreplaceengine is used to implement bfindreplace and * bfindreplacecaseless. It works by breaking the three cases of * expansion, reduction and replacement, and solving each of these * in the most efficient way possible. */ static int findreplaceengine(bstring b, const bstring find, const bstring repl, int pos, instr_fnptr instr) { int i, ret, slen, mlen, delta, acc; int *d; /* This +1 is unnecessary, but it shuts up LINT. */ int static_d[INITIAL_STATIC_FIND_INDEX_COUNT + 1]; ptrdiff_t pd; bstring auxf = (bstring) find; bstring auxr = (bstring) repl; if (!b || !b->data || !find || !find->data || !repl || !repl->data || pos < 0 || find->slen <= 0 || b->slen > b->mlen || b->mlen <= 0 || b->slen < 0 || repl->slen < 0) { return BSTR_ERR; } if (pos > b->slen - find->slen) { return BSTR_OK; } /* Alias with find string */ pd = (ptrdiff_t)(find->data - b->data); if ((ptrdiff_t)(pos - find->slen) < pd && pd < (ptrdiff_t)b->slen) { auxf = bstrcpy(find); if (!auxf) { return BSTR_ERR; } } /* Alias with repl string */ pd = (ptrdiff_t)(repl->data - b->data); if ((ptrdiff_t)(pos - repl->slen) < pd && pd < (ptrdiff_t)b->slen) { auxr = bstrcpy (repl); if (!auxr) { if (auxf != find) { bdestroy(auxf); } return BSTR_ERR; } } delta = auxf->slen - auxr->slen; /* in-place replacement since find and replace strings are of equal * length */ if (delta == 0) { while ((pos = instr(b, pos, auxf)) >= 0) { memcpy(b->data + pos, auxr->data, auxr->slen); pos += auxf->slen; } if (auxf != find) { bdestroy (auxf); } if (auxr != repl) { bdestroy (auxr); } return BSTR_OK; } /* shrinking replacement since auxf->slen > auxr->slen */ if (delta > 0) { acc = 0; while ((i = instr (b, pos, auxf)) >= 0) { if (acc && i > pos) { memmove(b->data + pos - acc, b->data + pos, i - pos); } if (auxr->slen) { memcpy(b->data + i - acc, auxr->data, auxr->slen); } acc += delta; pos = i + auxf->slen; } if (acc) { i = b->slen; if (i > pos) { memmove(b->data + pos - acc, b->data + pos, i - pos); } b->slen -= acc; b->data[b->slen] = (unsigned char) '\0'; } if (auxf != find) { bdestroy (auxf); } if (auxr != repl) { bdestroy (auxr); } return BSTR_OK; } /* expanding replacement since find->slen < repl->slen. Its a lot * more complicated. This works by first finding all the matches and * storing them to a growable array, then doing at most one resize of * the destination bstring and then performing the direct memory * transfers of the string segment pieces to form the final result. The * growable array of matches uses a deferred doubling reallocing * strategy. What this means is that it starts as a reasonably fixed * sized auto array in the hopes that many if not most cases will never * need to grow this array. But it switches as soon as the bounds of * the array will be exceeded. An extra find result is always appended * to this array that corresponds to the end of the destination string, * so slen is checked against mlen - 1 rather than mlen before * resizing. */ mlen = INITIAL_STATIC_FIND_INDEX_COUNT; d = (int *) static_d; /* Avoid malloc for trivial/initial cases */ acc = slen = 0; while ((pos = instr(b, pos, auxf)) >= 0) { if (slen >= mlen - 1) { int sl, *t; /* Overflow */ if (mlen > (int)(INT_MAX / sizeof(int *)) / 2) { ret = BSTR_ERR; goto done; } mlen += mlen; sl = sizeof(int *) * mlen; if (static_d == d) { /* static_d cannot be realloced */ d = NULL; } if (NULL == (t = (int *) realloc(d, sl))) { ret = BSTR_ERR; goto done; } if (NULL == d) { memcpy(t, static_d, sizeof (static_d)); } d = t; } d[slen] = pos; slen++; acc -= delta; pos += auxf->slen; if (pos < 0 || acc < 0) { ret = BSTR_ERR; goto done; } } /* slen <= INITIAL_STATIC_INDEX_COUNT-1 or mlen-1 here. */ d[slen] = b->slen; ret = balloc (b, b->slen + acc + 1); if (BSTR_OK == ret) { b->slen += acc; for (i = slen-1; i >= 0; i--) { int s, l; s = d[i] + auxf->slen; l = d[i+1] - s; /* d[slen] may be accessed here. */ if (l) { memmove(b->data + s + acc, b->data + s, l); } if (auxr->slen) { memmove(b->data + s + acc - auxr->slen, auxr->data, auxr->slen); } acc += delta; } b->data[b->slen] = (unsigned char)'\0'; } done: if (static_d == d) { d = NULL; } free(d); if (auxf != find) { bdestroy(auxf); } if (auxr != repl) { bdestroy(auxr); } return ret; } int bfindreplace(bstring b, const bstring find, const bstring repl, int pos) { return findreplaceengine(b, find, repl, pos, binstr); } int bfindreplacecaseless(bstring b, const bstring find, const bstring repl, int pos) { return findreplaceengine(b, find, repl, pos, binstrcaseless); } int binsertch(bstring b, int pos, int len, unsigned char fill) { int d, l, i; if (pos < 0 || !b || b->slen < 0 || b->mlen < b->slen || b->mlen <= 0 || len < 0) { return BSTR_ERR; } /* Compute the two possible end pointers */ d = b->slen + len; l = pos + len; if ((d|l) < 0) { return BSTR_ERR; } if (l > d) { /* Inserting past the end of the string */ if (balloc(b, l + 1) != BSTR_OK) { return BSTR_ERR; } pos = b->slen; b->slen = l; } else { /* Inserting in the middle of the string */ if (balloc(b, d + 1) != BSTR_OK) { return BSTR_ERR; } for (i = d - 1; i >= l; i--) { b->data[i] = b->data[i - len]; } b->slen = d; } for (i = pos; i < l; i++) { b->data[i] = fill; } b->data[b->slen] = (unsigned char)'\0'; return BSTR_OK; } int bpattern(bstring b, int len) { int i, d; d = blength(b); if (d <= 0 || len < 0 || balloc(b, len + 1) != BSTR_OK) { return BSTR_ERR; } if (len > 0) { if (d == 1) { return bsetstr(b, len, NULL, b->data[0]); } for (i = d; i < len; i++) { b->data[i] = b->data[i - d]; } } b->data[len] = (unsigned char)'\0'; b->slen = len; return BSTR_OK; } #define BS_BUFF_SZ (1024) int breada(bstring b, bNread readPtr, void *parm) { int i, l, n; if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || readPtr == NULL) { return BSTR_ERR; } i = b->slen; n = i + 16; while (1) { if (BSTR_OK != balloc(b, n + 1)) { return BSTR_ERR; } l = (int)readPtr((void *)(b->data + i), 1, n - i, parm); i += l; b->slen = i; if (i < n) { goto done; } n += (n < BS_BUFF_SZ) ? n : BS_BUFF_SZ; } done: b->data[i] = (unsigned char)'\0'; return BSTR_OK; } bstring bread(bNread readPtr, void *parm) { bstring buff; if (0 > breada(buff = bfromcstr (""), readPtr, parm)) { bdestroy(buff); return NULL; } return buff; } int bassigngets(bstring b, bNgetc getcPtr, void *parm, char terminator) { int c, d, e; if (!b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || getcPtr == NULL) { return BSTR_ERR; } d = 0; e = b->mlen - 2; while ((c = getcPtr(parm)) >= 0) { if (d > e) { b->slen = d; if (balloc (b, d + 2) != BSTR_OK) { return BSTR_ERR; } e = b->mlen - 2; } b->data[d] = (unsigned char)c; d++; if (c == terminator) { break; } } b->data[d] = (unsigned char)'\0'; b->slen = d; return d == 0 && c < 0; } int bgetsa(bstring b, bNgetc getcPtr, void *parm, char terminator) { int c, d, e; if (!b || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || !getcPtr) { return BSTR_ERR; } d = b->slen; e = b->mlen - 2; while ((c = getcPtr(parm)) >= 0) { if (d > e) { b->slen = d; if (balloc(b, d + 2) != BSTR_OK) { return BSTR_ERR; } e = b->mlen - 2; } b->data[d] = (unsigned char) c; d++; if (c == terminator) { break; } } b->data[d] = (unsigned char)'\0'; b->slen = d; return d == 0 && c < 0; } bstring #if defined(HAVE_BGETS) bgetstream(bNgetc getcPtr, void *parm, char terminator) #else bgets(bNgetc getcPtr, void *parm, char terminator) #endif { bstring buff; if (0 > bgetsa(buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) { bdestroy(buff); buff = NULL; } return buff; } struct bStream { bstring buff; /* Buffer for over-reads */ void *parm; /* The stream handle for core stream */ bNread readFnPtr; /* fread compatible fnptr for core stream */ int isEOF; /* track file's EOF state */ int maxBuffSz; }; struct bStream * bsopen (bNread readPtr, void *parm) { struct bStream *s; if (readPtr == NULL) { return NULL; } s = malloc(sizeof (struct bStream)); if (!s) { return NULL; } s->parm = parm; s->buff = bfromcstr (""); s->readFnPtr = readPtr; s->maxBuffSz = BS_BUFF_SZ; s->isEOF = 0; return s; } int bsbufflength(struct bStream *s, int sz) { int oldSz; if (!s || sz < 0) { return BSTR_ERR; } oldSz = s->maxBuffSz; if (sz > 0) { s->maxBuffSz = sz; } return oldSz; } int bseof(const struct bStream *s) { if (!s || !s->readFnPtr) { return BSTR_ERR; } return s->isEOF && (s->buff->slen == 0); } void * bsclose(struct bStream *s) { void *parm; if (s == NULL) { return NULL; } s->readFnPtr = NULL; if (s->buff) { bdestroy(s->buff); } s->buff = NULL; parm = s->parm; s->parm = NULL; s->isEOF = 1; free(s); return parm; } int bsreadlna(bstring r, struct bStream *s, char terminator) { int i, l, ret, rlo; char *b; struct tagbstring x; if (!s || !s->buff || !r || r->mlen <= 0 || r->slen < 0 || r->mlen < r->slen) { return BSTR_ERR; } l = s->buff->slen; if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } b = (char *)s->buff->data; x.data = (unsigned char *)b; /* First check if the current buffer holds the terminator */ b[l] = terminator; /* Set sentinel */ for (i=0; b[i] != terminator; i++) ; if (i < l) { x.slen = i + 1; ret = bconcat(r, &x); s->buff->slen = l; if (BSTR_OK == ret) { bdelete(s->buff, 0, i + 1); } return BSTR_OK; } rlo = r->slen; /* If not then just concatenate the entire buffer to the output */ x.slen = l; if (BSTR_OK != bconcat(r, &x)) { return BSTR_ERR; } /* Perform direct in-place reads into the destination to allow for * the minimum of data-copies */ while (1) { if (BSTR_OK != balloc(r, r->slen + s->maxBuffSz + 1)) { return BSTR_ERR; } b = (char *) (r->data + r->slen); l = (int) s->readFnPtr(b, 1, s->maxBuffSz, s->parm); if (l <= 0) { r->data[r->slen] = (unsigned char)'\0'; s->buff->slen = 0; s->isEOF = 1; /* If nothing was read return with an error message */ return BSTR_ERR & -(r->slen == rlo); } b[l] = terminator; /* Set sentinel */ for (i=0; b[i] != terminator; i++) ; if (i < l) { break; } r->slen += l; } /* Terminator found, push over-read back to buffer */ i++; r->slen += i; s->buff->slen = l - i; memcpy(s->buff->data, b + i, l - i); r->data[r->slen] = (unsigned char)'\0'; return BSTR_OK; } int bsreadlnsa(bstring r, struct bStream *s, const bstring term) { int i, l, ret, rlo; unsigned char *b; struct tagbstring x; struct charField cf; if (!s || !s->buff || !r || !term || !term->data || r->mlen <= 0 || r->slen < 0 || r->mlen < r->slen) { return BSTR_ERR; } if (term->slen == 1) { return bsreadlna(r, s, term->data[0]); } if (term->slen < 1 || buildCharField(&cf, term)) { return BSTR_ERR; } l = s->buff->slen; if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } b = (unsigned char *)s->buff->data; x.data = b; /* First check if the current buffer holds the terminator */ b[l] = term->data[0]; /* Set sentinel */ for (i = 0; !testInCharField(&cf, b[i]); i++) ; if (i < l) { x.slen = i + 1; ret = bconcat(r, &x); s->buff->slen = l; if (BSTR_OK == ret) { bdelete(s->buff, 0, i + 1); } return BSTR_OK; } rlo = r->slen; /* If not then just concatenate the entire buffer to the output */ x.slen = l; if (BSTR_OK != bconcat(r, &x)) { return BSTR_ERR; } /* Perform direct in-place reads into the destination to allow for * the minimum of data-copies */ while (1) { if (BSTR_OK != balloc(r, r->slen + s->maxBuffSz + 1)) { return BSTR_ERR; } b = (unsigned char *)(r->data + r->slen); l = (int) s->readFnPtr(b, 1, s->maxBuffSz, s->parm); if (l <= 0) { r->data[r->slen] = (unsigned char)'\0'; s->buff->slen = 0; s->isEOF = 1; /* If nothing was read return with an error message */ return BSTR_ERR & -(r->slen == rlo); } b[l] = term->data[0]; /* Set sentinel */ for (i = 0; !testInCharField(&cf, b[i]); i++) ; if (i < l) { break; } r->slen += l; } /* Terminator found, push over-read back to buffer */ i++; r->slen += i; s->buff->slen = l - i; memcpy(s->buff->data, b + i, l - i); r->data[r->slen] = (unsigned char)'\0'; return BSTR_OK; } int bsreada(bstring r, struct bStream *s, int n) { int l, ret, orslen; char *b; struct tagbstring x; if (!s || !s->buff || !r || r->mlen <= 0 || r->slen < 0 || r->mlen < r->slen || n <= 0) { return BSTR_ERR; } if (n > INT_MAX - r->slen) { return BSTR_ERR; } n += r->slen; l = s->buff->slen; orslen = r->slen; if (0 == l) { if (s->isEOF) { return BSTR_ERR; } if (r->mlen > n) { l = (int)s->readFnPtr(r->data + r->slen, 1, n - r->slen, s->parm); if (0 >= l || l > n - r->slen) { s->isEOF = 1; return BSTR_ERR; } r->slen += l; r->data[r->slen] = (unsigned char)'\0'; return 0; } } if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } b = (char *) s->buff->data; x.data = (unsigned char *)b; do { if (l + r->slen >= n) { x.slen = n - r->slen; ret = bconcat(r, &x); s->buff->slen = l; if (BSTR_OK == ret) { bdelete(s->buff, 0, x.slen); } return BSTR_ERR & -(r->slen == orslen); } x.slen = l; if (BSTR_OK != bconcat (r, &x)) { break; } l = n - r->slen; if (l > s->maxBuffSz) { l = s->maxBuffSz; } l = (int)s->readFnPtr(b, 1, l, s->parm); } while (l > 0); if (l < 0) { l = 0; } if (l == 0) { s->isEOF = 1; } s->buff->slen = l; return BSTR_ERR & -(r->slen == orslen); } int bsreadln(bstring r, struct bStream *s, char terminator) { if (!s || !s->buff || !r || r->mlen <= 0) { return BSTR_ERR; } if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } r->slen = 0; return bsreadlna(r, s, terminator); } int bsreadlns(bstring r, struct bStream *s, const bstring term) { if (!s || !s->buff || !r || !term || !term->data || r->mlen <= 0) { return BSTR_ERR; } if (term->slen == 1) { return bsreadln (r, s, term->data[0]); } if (term->slen < 1) { return BSTR_ERR; } if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } r->slen = 0; return bsreadlnsa(r, s, term); } int bsread(bstring r, struct bStream *s, int n) { if (!s || !s->buff || !r || r->mlen <= 0 || n <= 0) { return BSTR_ERR; } if (BSTR_OK != balloc(s->buff, s->maxBuffSz + 1)) { return BSTR_ERR; } r->slen = 0; return bsreada(r, s, n); } int bsunread(struct bStream *s, const bstring b) { if (!s || !s->buff) { return BSTR_ERR; } return binsert(s->buff, 0, b, (unsigned char)'?'); } int bspeek(bstring r, const struct bStream *s) { if (!s || !s->buff) { return BSTR_ERR; } return bassign(r, s->buff); } bstring bjoinblk(const struct bstrList *bl, const void *blk, int len) { bstring b; unsigned char *p; int i, c, v; if (bl == NULL || bl->qty < 0) { return NULL; } if (len < 0) { return NULL; } if (len > 0 && blk == NULL) { return NULL; } if (bl->qty < 1) { return bfromcstr(""); } for (i = 0, c = 1; i < bl->qty; i++) { v = bl->entry[i]->slen; if (v < 0) { return NULL; /* Invalid input */ } c += v; if (c < 0) { return NULL; /* Wrap around ?? */ } } b = (bstring)malloc(sizeof(struct tagbstring)); if (len == 0) { p = b->data = (unsigned char *)malloc(c); if (p == NULL) { free(b); return NULL; } for (i = 0; i < bl->qty; i++) { v = bl->entry[i]->slen; memcpy(p, bl->entry[i]->data, v); p += v; } } else { v = (bl->qty - 1) * len; if ((bl->qty > 512 || len > 127) && v / len != bl->qty - 1) { return NULL; /* Wrap around ?? */ } c += v; if (c < v) { return NULL; /* Wrap around ?? */ } p = b->data = (unsigned char *)malloc(c); if (p == NULL) { free(b); return NULL; } v = bl->entry[0]->slen; memcpy(p, bl->entry[0]->data, v); p += v; for (i = 1; i < bl->qty; i++) { memcpy(p, blk, len); p += len; v = bl->entry[i]->slen; if (v) { memcpy(p, bl->entry[i]->data, v); p += v; } } } b->mlen = c; b->slen = c - 1; b->data[c - 1] = (unsigned char)'\0'; return b; } bstring bjoin(const struct bstrList *bl, const bstring sep) { if (sep == NULL) { return bjoinblk(bl, NULL, 0); } if (sep->slen < 0 || (sep->slen > 0 && sep->data == NULL)) { return NULL; } return bjoinblk(bl, sep->data, sep->slen); } #define BSSSC_BUFF_LEN (256) int bssplitscb(struct bStream *s, const bstring splitStr, int (*cb)(void *parm, int ofs, const bstring entry), void *parm) { struct charField chrs; bstring buff; int i, p, ret; if (!cb || !s || !s->readFnPtr || !splitStr || splitStr->slen < 0) { return BSTR_ERR; } buff = bfromcstr (""); if (!buff) { return BSTR_ERR; } if (splitStr->slen == 0) { while (bsreada(buff, s, BSSSC_BUFF_LEN) >= 0) ; if ((ret = cb(parm, 0, buff)) > 0) { ret = 0; } } else { buildCharField(&chrs, splitStr); ret = p = i = 0; while (1) { if (i >= buff->slen) { bsreada(buff, s, BSSSC_BUFF_LEN); if (i >= buff->slen) { if (0 < (ret = cb (parm, p, buff))) { ret = 0; } break; } } if (testInCharField(&chrs, buff->data[i])) { struct tagbstring t; unsigned char c; blk2tbstr(t, buff->data + i + 1, buff->slen - (i + 1)); if ((ret = bsunread(s, &t)) < 0) { break; } buff->slen = i; c = buff->data[i]; buff->data[i] = (unsigned char)'\0'; if ((ret = cb(parm, p, buff)) < 0) { break; } buff->data[i] = c; buff->slen = 0; p += i + 1; i = -1; } i++; } } bdestroy(buff); return ret; } int bssplitstrcb(struct bStream *s, const bstring splitStr, int (*cb)(void *parm, int ofs, const bstring entry), void *parm) { bstring buff; int i, p, ret; if (!cb || !s || !s->readFnPtr || !splitStr || splitStr->slen < 0) { return BSTR_ERR; } if (splitStr->slen == 1) { return bssplitscb(s, splitStr, cb, parm); } buff = bfromcstr(""); if (!buff) { return BSTR_ERR; } if (splitStr->slen == 0) { for (i = 0; bsreada(buff, s, BSSSC_BUFF_LEN) >= 0; i++) { if ((ret = cb(parm, 0, buff)) < 0) { bdestroy(buff); return ret; } buff->slen = 0; } bdestroy(buff); return BSTR_OK; } else { ret = p = i = 0; for (i = p = 0; ;) { ret = binstr(buff, 0, splitStr); if (ret >= 0) { struct tagbstring t; blk2tbstr(t, buff->data, ret); i = ret + splitStr->slen; ret = cb (parm, p, &t); if (ret < 0) { break; } p += i; bdelete(buff, 0, i); } else { bsreada(buff, s, BSSSC_BUFF_LEN); if (bseof(s)) { ret = cb (parm, p, buff); if (ret > 0) { ret = 0; } break; } } } } bdestroy(buff); return ret; } struct bstrList * bstrListCreate(void) { struct bstrList *sl = malloc(sizeof(struct bstrList)); if (sl) { sl->entry = (bstring *)malloc(1 * sizeof(bstring)); if (!sl->entry) { free(sl); sl = NULL; } else { sl->qty = 0; sl->mlen = 1; } } return sl; } int bstrListDestroy(struct bstrList *sl) { int i; if (!sl || sl->qty < 0) { return BSTR_ERR; } for (i = 0; i < sl->qty; i++) { if (sl->entry[i]) { bdestroy(sl->entry[i]); sl->entry[i] = NULL; } } sl->qty = -1; sl->mlen = -1; free(sl->entry); sl->entry = NULL; free(sl); return BSTR_OK; } int bstrListAlloc(struct bstrList *sl, int msz) { bstring *l; int smsz; size_t nsz; if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) { return BSTR_ERR; } if (sl->mlen >= msz) { return BSTR_OK; } smsz = snapUpSize(msz); nsz = ((size_t)smsz) * sizeof(bstring); if (nsz < (size_t) smsz) { return BSTR_ERR; } l = realloc(sl->entry, nsz); if (!l) { smsz = msz; nsz = ((size_t)smsz) * sizeof(bstring); l = realloc(sl->entry, nsz); if (!l) { return BSTR_ERR; } } sl->mlen = smsz; sl->entry = l; return BSTR_OK; } int bstrListAllocMin(struct bstrList *sl, int msz) { bstring *l; size_t nsz; if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) { return BSTR_ERR; } if (msz < sl->qty) { msz = sl->qty; } if (sl->mlen == msz) { return BSTR_OK; } nsz = ((size_t)msz) * sizeof(bstring); if (nsz < (size_t)msz) { return BSTR_ERR; } l = realloc(sl->entry, nsz); if (!l) { return BSTR_ERR; } sl->mlen = msz; sl->entry = l; return BSTR_OK; } int bsplitcb(const bstring str, unsigned char splitChar, int pos, int (*cb) (void *parm, int ofs, int len), void *parm) { int i, p, ret; if (!cb || !str || pos < 0 || pos > str->slen) { return BSTR_ERR; } p = pos; do { for (i = p; i < str->slen; i++) { if (str->data[i] == splitChar) { break; } } if ((ret = cb(parm, p, i - p)) < 0) { return ret; } p = i + 1; } while (p <= str->slen); return BSTR_OK; } int bsplitscb(const bstring str, const bstring splitStr, int pos, int (*cb)(void *parm, int ofs, int len), void *parm) { struct charField chrs; int i, p, ret; if (!cb || !str || pos < 0 || pos > str->slen || !splitStr || splitStr->slen < 0) { return BSTR_ERR; } if (splitStr->slen == 0) { if ((ret = cb (parm, 0, str->slen)) > 0) { ret = 0; } return ret; } if (splitStr->slen == 1) { return bsplitcb (str, splitStr->data[0], pos, cb, parm); } buildCharField(&chrs, splitStr); p = pos; do { for (i = p; i < str->slen; i++) { if (testInCharField(&chrs, str->data[i])) { break; } } if ((ret = cb(parm, p, i - p)) < 0) { return ret; } p = i + 1; } while (p <= str->slen); return BSTR_OK; } int bsplitstrcb(const bstring str, const bstring splitStr, int pos, int (*cb)(void *parm, int ofs, int len), void *parm) { int i, p, ret; if (!cb || !str || pos < 0 || pos > str->slen || !splitStr || splitStr->slen < 0) { return BSTR_ERR; } if (0 == splitStr->slen) { for (i = pos; i < str->slen; i++) { ret = cb (parm, i, 1); if (ret < 0) { return ret; } } return BSTR_OK; } if (splitStr->slen == 1) { return bsplitcb(str, splitStr->data[0], pos, cb, parm); } i = p = pos; while (i <= str->slen - splitStr->slen) { ret = memcmp(splitStr->data, str->data + i, splitStr->slen); if (0 == ret) { ret = cb (parm, p, i - p); if (ret < 0) { return ret; } i += splitStr->slen; p = i; } else { i++; } } ret = cb (parm, p, str->slen - p); if (ret < 0) { return ret; } return BSTR_OK; } struct genBstrList { bstring b; struct bstrList *bl; }; static int bscb(void *parm, int ofs, int len) { struct genBstrList *g = (struct genBstrList *)parm; if (g->bl->qty >= g->bl->mlen) { int mlen = g->bl->mlen * 2; bstring *tbl; while (g->bl->qty >= mlen) { if (mlen < g->bl->mlen) { return BSTR_ERR; } mlen += mlen; } tbl = (bstring *)realloc(g->bl->entry, sizeof(bstring) * mlen); if (tbl == NULL) { return BSTR_ERR; } g->bl->entry = tbl; g->bl->mlen = mlen; } g->bl->entry[g->bl->qty] = bmidstr(g->b, ofs, len); g->bl->qty++; return BSTR_OK; } struct bstrList * bsplit(const bstring str, unsigned char splitChar) { struct genBstrList g; if (!str || !str->data || str->slen < 0) { return NULL; } g.bl = malloc(sizeof(struct bstrList)); if (!g.bl) { return NULL; } g.bl->mlen = 4; g.bl->entry = malloc(g.bl->mlen * sizeof(bstring)); if (!g.bl->entry) { free(g.bl); return NULL; } g.b = (bstring)str; g.bl->qty = 0; if (bsplitcb(str, splitChar, 0, bscb, &g) < 0) { bstrListDestroy(g.bl); return NULL; } return g.bl; } struct bstrList * bsplitstr(const bstring str, const bstring splitStr) { struct genBstrList g; if (!str || !str->data || str->slen < 0) { return NULL; } g.bl = malloc(sizeof(struct bstrList)); if (!g.bl) { return NULL; } g.bl->mlen = 4; g.bl->entry = malloc(g.bl->mlen * sizeof (bstring)); if (!g.bl->entry) { free(g.bl); return NULL; } g.b = (bstring)str; g.bl->qty = 0; if (bsplitstrcb(str, splitStr, 0, bscb, &g) < 0) { bstrListDestroy(g.bl); return NULL; } return g.bl; } struct bstrList * bsplits(const bstring str, const bstring splitStr) { struct genBstrList g; if (!str || str->slen < 0 || !str->data || !splitStr || splitStr->slen < 0 || !splitStr->data) { return NULL; } g.bl = malloc(sizeof(struct bstrList)); if (!g.bl) { return NULL; } g.bl->mlen = 4; g.bl->entry = malloc (g.bl->mlen * sizeof(bstring)); if (!g.bl->entry) { free(g.bl); return NULL; } g.b = (bstring)str; g.bl->qty = 0; if (bsplitscb(str, splitStr, 0, bscb, &g) < 0) { bstrListDestroy(g.bl); return NULL; } return g.bl; } #define exvsnprintf(r, b, n, f, a) \ { \ r = vsnprintf(b, n, f, a); \ } #define START_VSNBUFF (16) /* On IRIX vsnprintf returns n-1 when the operation would overflow the target * buffer, WATCOM and MSVC both return -1, while C99 requires that the returned * value be exactly what the length would be if the buffer would be large * enough. This leads to the idea that if the return value is larger than n, * then changing n to the return value will reduce the number of iterations * required. */ int bformata(bstring b, const char *fmt, ...) { va_list arglist; bstring buff; int n, r; if (!b || !fmt || !b->data || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) { return BSTR_ERR; } /* Since the length is not determinable beforehand, a search is * performed using the truncating "vsnprintf" call (to avoid buffer * overflows) on increasing potential sizes for the output result. */ n = (int)(2 * strlen(fmt)); if (n < START_VSNBUFF) { n = START_VSNBUFF; } buff = bfromcstralloc(n + 2, ""); if (!buff) { n = 1; buff = bfromcstralloc(n + 2, ""); if (!buff) { return BSTR_ERR; } } while (1) { if (n < 0 || n > INT_MAX - 2) { bdestroy(buff); return BSTR_ERR; } va_start(arglist, fmt); exvsnprintf(r, (char *) buff->data, n + 1, fmt, arglist); va_end(arglist); buff->data[n] = (unsigned char) '\0'; buff->slen = (int) (strlen) ((char *) buff->data); if (buff->slen < n) { break; } if (r > n) { n = r; } else { n += n; } if (BSTR_OK != balloc(buff, n + 2)) { bdestroy(buff); return BSTR_ERR; } } r = bconcat(b, buff); bdestroy(buff); return r; } int bassignformat(bstring b, const char *fmt, ...) { va_list arglist; bstring buff; int n, r; if (!b || !fmt || !b->data || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) { return BSTR_ERR; } /* Since the length is not determinable beforehand, a search is * performed using the truncating "vsnprintf" call (to avoid buffer * overflows) on increasing potential sizes for the output result. */ n = (int)(2 * strlen(fmt)); if (n < START_VSNBUFF) { n = START_VSNBUFF; } buff = bfromcstralloc (n + 2, ""); if (!buff) { n = 1; buff = bfromcstralloc (n + 2, ""); if (!buff) { return BSTR_ERR; } } while (1) { if (n < 0 || n > INT_MAX - 2) { bdestroy(buff); return BSTR_ERR; } va_start(arglist, fmt); exvsnprintf(r, (char *)buff->data, n + 1, fmt, arglist); va_end(arglist); buff->data[n] = (unsigned char)'\0'; buff->slen = (int)strlen((char *)buff->data); if (buff->slen < n) { break; } if (r > n) { n = r; } else { n += n; } if (BSTR_OK != balloc(buff, n + 2)) { bdestroy(buff); return BSTR_ERR; } } r = bassign(b, buff); bdestroy(buff); return r; } bstring bformat(const char *fmt, ...) { va_list arglist; bstring buff; int n, r; if (!fmt) { return NULL; } /* Since the length is not determinable beforehand, a search is * performed using the truncating "vsnprintf" call (to avoid buffer * overflows) on increasing potential sizes for the output result. */ n = (int)(2 * strlen(fmt)); if (n < START_VSNBUFF) { n = START_VSNBUFF; } buff = bfromcstralloc(n + 2, ""); if (!buff) { n = 1; buff = bfromcstralloc(n + 2, ""); if (!buff) { return NULL; } } while (1) { if (n < 0 || n > INT_MAX - 2) { bdestroy(buff); return NULL; } va_start(arglist, fmt); exvsnprintf(r, (char *)buff->data, n + 1, fmt, arglist); va_end(arglist); buff->data[n] = (unsigned char)'\0'; buff->slen = (int)strlen((char *)buff->data); if (buff->slen < n) { break; } if (r > n) { n = r; } else { n += n; } if (BSTR_OK != balloc(buff, n + 2)) { bdestroy(buff); return NULL; } } return buff; } int bvcformata(bstring b, int count, const char *fmt, va_list arg) { int n, r, l; if (!b || !fmt || count <= 0 || !b->data || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) { return BSTR_ERR; } if (count > (n = b->slen + count) + 2) { return BSTR_ERR; } if (BSTR_OK != balloc(b, n + 2)) { return BSTR_ERR; } exvsnprintf(r, (char *)b->data + b->slen, count + 2, fmt, arg); b->data[b->slen + count + 2] = '\0'; /* Did the operation complete successfully within bounds? */ for (l = b->slen; l <= n; l++) { if ('\0' == b->data[l]) { b->slen = l; return BSTR_OK; } } /* Abort, since the buffer was not large enough. The return value * tries to help set what the retry length should be. */ b->data[b->slen] = '\0'; if (r > count + 1) { n = r; } else { if (count > INT_MAX / 2) { n = INT_MAX; } else { n = count + count; } } n = -n; if (n > BSTR_ERR - 1) { n = BSTR_ERR - 1; } return n; } bstring-1.1.0/bstring/bstrlib.h000066400000000000000000001754231516216374300164670ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /** * \file * \brief C implementaion of bstring functions * * This file is the header file for the core module for implementing the * bstring functions. */ #ifndef BSTRLIB_H #define BSTRLIB_H #if __GNUC__ >= 4 #define BSTR_PUBLIC \ __attribute__ ((visibility ("default"))) #define BSTR_PRIVATE \ __attribute__ ((visibility ("hidden"))) #else #define BSTR_PUBLIC #define BSTR_PRIVATE #endif #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #define BSTR_PRINTF(format, argument) \ __attribute__ ((__format__ (__printf__, format, argument))) #define BSTR_UNUSED \ __attribute__ ((__unused__)) #elif defined(_MSC_VER) #define BSTR_PRINTF(format, argument) #define BSTR_UNUSED \ __pragma(warning(suppress:4100)) #else #define BSTR_PRINTF(format, argument) #define BSTR_UNUSED #endif #ifdef __cplusplus extern "C" { #endif #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #define BSTR_ERR (-1) #define BSTR_OK (0) #define BSTR_BS_BUFF_LENGTH_GET (0) typedef struct tagbstring *bstring; struct tagbstring { int mlen; int slen; unsigned char *data; }; /* Copy functions */ #define cstr2bstr bfromcstr /** * Take a standard C library style '\0' terminated char buffer and generate * a bstring with the same contents as the char buffer. * * If an error occurs NULL is returned. * * \code * bstring b = bfromcstr("Hello"); * if(!b) { * fprintf(stderr, "Out of memory"); * } else { * puts((char *)b->data); * } * \endcode */ BSTR_PUBLIC bstring bfromcstr(const char *str); /** * Create a bstring which contains the contents of the '\0' terminated * char *buffer str. * * The memory buffer backing the bstring is at least mlen characters in * length. If an error occurs NULL is returned. * * \code * bstring b = bfromcstralloc(64, someCstr); * if(b) { * b->data[63] = 'x'; * } * \endcode * * The idea is that this will set the 64th character of b to 'x' if it is at * least 64 characters long otherwise do nothing. And we know this is well * defined so long as b was successfully created, since it will have been * allocated with at least 64 characters. */ BSTR_PUBLIC bstring bfromcstralloc(int mlen, const char *str); /** * Create a bstring which contains the contents of the '\\0' terminated * char* buffer str. The memory buffer backing the string is at least * minl characters in length, but an attempt is made to allocate up to * maxl characters. */ BSTR_PUBLIC bstring bfromcstrrangealloc(int minl, int maxl, const char *str); /** * Create a bstring whose contents are described by the contiguous buffer * pointing to by blk with a length of len bytes. * * Note that this function creates a copy of the data in blk, rather than * simply referencing it. Compare with the blk2tbstr macro. If an error * occurs NULL is returned. */ BSTR_PUBLIC bstring blk2bstr(const void *blk, int len); /** * Create a '\0' terminated char buffer which contains the contents of the * bstring s, except that any contained '\0' characters are converted to the * character in z. * * This returned value should be freed with bcstrfree(), by the caller. If * an error occurs NULL is returned. */ BSTR_PUBLIC char * bstr2cstr(const bstring s, char z); /** * Frees a C-string generated by bstr2cstr(). * * This is normally unnecessary since it just wraps a call to free(), * however, if malloc() and free() have been redefined as a macros within * the bstrlib module (via macros in the memdbg.h backdoor) with some * difference in behaviour from the std library functions, then this allows * a correct way of freeing the memory that allows higher level code to be * independent from these macro redefinitions. */ BSTR_PUBLIC int bcstrfree(char *s); /** * Make a copy of the passed in bstring. * * The copied bstring is returned if there is no error, otherwise NULL is * returned. */ BSTR_PUBLIC bstring bstrcpy(const bstring b1); /** * Overwrite the bstring a with the contents of bstring b. * * Note that the bstring a must be a well defined and writable bstring. If * an error occurs BSTR_ERR is returned and a is not overwritten. */ BSTR_PUBLIC int bassign(bstring a, const bstring b); /** * Overwrite the bstring a with the middle of contents of bstring b * starting from position left and running for a length len. * * left and len are clamped to the ends of b as with the function bmidstr. * Note that the bstring a must be a well defined and writable bstring. If * an error occurs BSTR_ERR is returned and a is not overwritten. */ BSTR_PUBLIC int bassignmidstr(bstring a, const bstring b, int left, int len); /** * Overwrite the string a with the contents of char * string str. * * Note that the bstring a must be a well defined and writable bstring. If * an error occurs BSTR_ERR is returned and a may be partially overwritten. */ BSTR_PUBLIC int bassigncstr(bstring a, const char *str); /** * Overwrite the bstring a with the middle of contents of bstring b * starting from position left and running for a length len. * * left and len are clamped to the ends of b as with the function bmidstr. * Note that the bstring a must be a well defined and writable bstring. If * an error occurs BSTR_ERR is returned and a is not overwritten. */ BSTR_PUBLIC int bassignblk(bstring a, const void *s, int len); /* Destroy function */ /** * Deallocate the bstring passed. * * Passing NULL in as a parameter will have no effect. Note that both the * header and the data portion of the bstring will be freed. No other * bstring function which modifies one of its parameters will free or * reallocate the header. Because of this, in general, bdestroy cannot be * called on any declared struct tagbstring even if it is not write * protected. A bstring which is write protected cannot be destroyed via the * bdestroy call. Any attempt to do so will result in no action taken, and * BSTR_ERR will be returned. */ BSTR_PUBLIC int bdestroy(bstring b); /* Space allocation hinting functions */ /** * Increase the allocated memory backing the data buffer for the bstring b * to a length of at least length. * * If the memory backing the bstring b is already large enough, not action is * performed. This has no effect on the bstring b that is visible to the * bstring API. Usually this function will only be used when a minimum buffer * size is required coupled with a direct access to the ->data member of the * bstring structure. * * Be warned that like any other bstring function, the bstring must be well * defined upon entry to this function, i.e., doing something like: * * \code * b->slen *= 2; * balloc(b, b->slen); * \endcode * * is invalid, and should be implemented as: * * \code * int t; * if (BSTR_OK == balloc (b, t = (b->slen * 2))) { * b->slen = t; * } * \endcode * * This function will return with BSTR_ERR if b is not detected as a valid * bstring or length is not greater than 0, otherwise BSTR_OK is returned. */ BSTR_PUBLIC int balloc(bstring s, int len); /** * Change the amount of memory backing the bstring b to at least length. * * This operation will never truncate the bstring data including the * extra terminating '\0' and thus will not decrease the length to less than * b->slen + 1. Note that repeated use of this function may cause * performance problems (realloc may be called on the bstring more than * the O(log(INT_MAX)) times). This function will return with BSTR_ERR if b * is not detected as a valid bstring or length is not greater than 0, * otherwise BSTR_OK is returned. * * So for example: * * \code * if (BSTR_OK == ballocmin (b, 64)) { * b->data[63] = 'x'; * } * \endcode * * The idea is that this will set the 64th character of b to 'x' if it is at * least 64 characters long otherwise do nothing. And we know this is well * defined so long as the ballocmin call was successfully, since it will * ensure that b has been allocated with at least 64 characters. */ BSTR_PUBLIC int ballocmin(bstring b, int len); /* Substring extraction */ /** * Create a bstring which is the substring of b starting from position left * and running for a length len (clamped by the end of the bstring b). * * If there was no error, the value of this constructed bstring is returned * otherwise NULL is returned. */ BSTR_PUBLIC bstring bmidstr(const bstring b, int left, int len); /*Various standard manipulations */ /** * Concatenate the bstring b1 to the end of bstring b0. * * The value BSTR_OK is returned if the operation is successful, otherwise * BSTR_ERR is returned. */ BSTR_PUBLIC int bconcat(bstring b0, const bstring b1); /** * Concatenate the character c to the end of bstring b. * * The value BSTR_OK is returned if the operation is successful, otherwise * BSTR_ERR is returned. */ BSTR_PUBLIC int bconchar(bstring b0, char c); /** * Concatenate the char * string s to the end of bstring b. * * The value BSTR_OK is returned if the operation is successful, otherwise * BSTR_ERR is returned. */ BSTR_PUBLIC int bcatcstr(bstring b, const char *s); /** * Concatenate a fixed length buffer (s, len) to the end of bstring b. * * The value BSTR_OK is returned if the operation is successful, otherwise * BSTR_ERR is returned. */ BSTR_PUBLIC int bcatblk(bstring b, const void *s, int len); /** * Inserts the bstring s2 into s1 at position pos. * * If the position pos is past the end of s1, then the character "fill" is * appended as necessary to make up the gap between the end of s1 and pos. The * value BSTR_OK is returned if the operation is successful, otherwise BSTR_ERR * is returned. */ BSTR_PUBLIC int binsert(bstring s1, int pos, const bstring s2, unsigned char fill); /** * Inserts the block of characters at blk with length len into b at position * pos. * * If the position pos is past the end of b, then the character "fill" is * appended as necessary to make up the gap between the end of b and pos. * Unlike bsetstr, binsertblk does not allow blk to be NULL. */ BSTR_PUBLIC int binsertblk(bstring b, int pos, const void *blk, int len, unsigned char fill); /** * Inserts the character fill repeatedly into s1 at position pos for a * length len. * * If the position pos is past the end of s1, then the character "fill" is * appended as necessary to make up the gap between the end of s1 and the * position pos + len (exclusive). The value BSTR_OK is returned if the * operation is successful, otherwise BSTR_ERR is returned. */ BSTR_PUBLIC int binsertch(bstring s1, int pos, int len, unsigned char fill); /** * Replace a section of a bstring from pos for a length len with the bstring * b2. * * If the position pos is past the end of b1 then the character "fill" is * appended as necessary to make up the gap between the end of b1 and pos. */ BSTR_PUBLIC int breplace(bstring b1, int pos, int len, const bstring b2, unsigned char fill); /** * Removes characters from pos to pos+len-1 and shifts the tail of the * bstring starting from pos+len to pos. * * len must be positive for this call to have any effect. The section of the * bstring described by (pos, len) is clamped to boundaries of the bstring b. * The value BSTR_OK is returned if the operation is successful, otherwise * BSTR_ERR is returned. */ BSTR_PUBLIC int bdelete(bstring s1, int pos, int len); /** * Overwrite the bstring b0 starting at position pos with the bstring b1. * * If the position pos is past the end of b0, then the character "fill" is * appended as necessary to make up the gap between the end of b0 and pos. If * b1 is NULL, it behaves as if it were a 0-length bstring. The value BSTR_OK * is returned if the operation is successful, otherwise BSTR_ERR is returned. */ BSTR_PUBLIC int bsetstr(bstring b0, int pos, const bstring b1, unsigned char fill); /** * Truncate the bstring to at most n characters. * * This function will return with BSTR_ERR if b is not detected as a valid * bstring or n is less than 0, otherwise BSTR_OK is returned. */ BSTR_PUBLIC int btrunc(bstring b, int n); /*Scan/search functions */ /** * Compare two bstrings without differentiating between case. * * The return value is the difference of the values of the characters where the * two bstrings first differ, otherwise 0 is returned indicating that the * bstrings are equal. If the lengths are different, then a difference from 0 * is given, but if the first extra character is '\0', then it is taken to be * the value UCHAR_MAX + 1. */ BSTR_PUBLIC int bstricmp(const bstring b0, const bstring b1); /** * Compare two bstrings without differentiating between case for at most n * characters. * * If the position where the two bstrings first differ is before the nth * position, the return value is the difference of the values of the * characters, otherwise 0 is returned. If the lengths are different and less * than n characters, then a difference from 0 is given, but if the first extra * character is '\0', then it is taken to be the value UCHAR_MAX + 1. */ BSTR_PUBLIC int bstrnicmp(const bstring b0, const bstring b1, int n); /** * Compare two bstrings for equality without differentiating between case. * * If the bstrings differ other than in case, 0 is returned, if the bstrings * are the same, 1 is returned, if there is an error, -1 is returned. If * the length of the bstrings are different, this function is O(1). '\0' * termination characters are not treated in any special way. */ BSTR_PUBLIC int biseqcaseless(const bstring b0, const bstring b1); /** * Compare content of b and the array of bytes in blk for length len for * equality without differentiating between character case. * * If the content differs other than in case, 0 is returned, if, ignoring * case, the content is the same, 1 is returned, if there is an error, -1 is * returned. If the length of the strings are different, this function is * O(1). '\0' characters are not treated in any special way. */ BSTR_PUBLIC int biseqcaselessblk(const bstring b, const void *blk, int len); /** * Compare beginning of bstring b0 with a block of memory of length len * without differentiating between case for equality. * * If the beginning of b0 differs from the memory block other than in case (or * if b0 is too short), 0 is returned, if the bstrings are the same, 1 is * returned, if there is an error, -1 is returned. */ BSTR_PUBLIC int bisstemeqcaselessblk(const bstring b0, const void *blk, int len); /** * Compare the bstring b0 and b1 for equality. * * If the bstrings differ, 0 is returned, if the bstrings are the same, 1 is * returned, if there is an error, -1 is returned. If the length of the * bstrings are different, this function has O(1) complexity. Contained '\0' * characters are not treated as a termination character. * * Note that the semantics of biseq are not completely compatible with * bstrcmp because of its different treatment of the '\0' character. */ BSTR_PUBLIC int biseq(const bstring b0, const bstring b1); /** * Compare the bstring b with the character block blk of length len. * * If the content differs, 0 is returned, if the content is the same, 1 is * returned, if there is an error, -1 is returned. If the length of the * strings are different, this function is O(1). '\0' characters are not * treated in any special way. */ BSTR_PUBLIC int biseqblk(const bstring b, const void *blk, int len); /** * Compare beginning of bstring b0 with a block of memory of length len for * equality. * * If the beginning of b0 differs from the memory block (or if b0 is too * short), 0 is returned, if the bstrings are the same, 1 is returned, if there * is an error, -1 is returned. */ BSTR_PUBLIC int bisstemeqblk(const bstring b0, const void *blk, int len); /** * Compare the bstring b and char * string s. * * The C string s must be '\0' terminated at exactly the length of the bstring * b, and the contents between the two must be identical with the bstring b * with no '\0' characters for the two contents to be considered equal. This is * equivalent to the condition that their current contents will be always be * equal when comparing them in the same format after converting one or the * other. If they are equal 1 is returned, if they are unequal 0 is returned * and if there is a detectable error BSTR_ERR is returned. */ BSTR_PUBLIC int biseqcstr(const bstring b, const char *s); /** * Compare the bstring b and char * string s. * * The C string s must be '\0' terminated at exactly the length of the bstring * b, and the contents between the two must be identical except for case with * the bstring b with no '\0' characters for the two contents to be considered * equal. This is equivalent to the condition that their current contents will * be always be equal ignoring case when comparing them in the same format * after converting one or the other. If they are equal, except for case, 1 is * returned, if they are unequal regardless of case 0 is returned and if there * is a detectable error BSTR_ERR is returned. */ BSTR_PUBLIC int biseqcstrcaseless(const bstring b, const char *s); /** * Compare the bstrings b0 and b1 for ordering. * * If there is an error, SHRT_MIN is returned, otherwise a value less than or * greater than zero, indicating that the bstring pointed to by b0 is * lexicographically less than or greater than the bstring pointed to by b1 is * returned. If the bstring lengths are unequal but the characters up until the * length of the shorter are equal then a value less than, or greater than * zero, indicating that the bstring pointed to by b0 is shorter or longer than * the bstring pointed to by b1 is returned. 0 is returned if and only if the * two bstrings are the same. If the length of the bstrings are different, this * function is O(n). Like its standard C library counter part, the comparison * does not proceed past any '\0' termination characters encountered. * * The seemingly odd error return value, merely provides slightly more * granularity than the undefined situation given in the C library function * strcmp. The function otherwise behaves very much like strcmp(). * * Note that the semantics of bstrcmp are not completely compatible with * biseq because of its different treatment of the '\0' termination * character. */ BSTR_PUBLIC int bstrcmp(const bstring b0, const bstring b1); /** * Compare the bstrings b0 and b1 for ordering for at most n characters. * * If there is an error, SHRT_MIN is returned, otherwise a value is returned as * if b0 and b1 were first truncated to at most n characters then bstrcmp was * called with these new bstrings are paremeters. If the length of the bstrings * are different, this function is O(n). Like its standard C library counter * part, the comparison does not proceed past any '\0' termination characters * encountered. * * The seemingly odd error return value, merely provides slightly more * granularity than the undefined situation given in the C library function * strncmp. The function otherwise behaves very much like strncmp(). */ BSTR_PUBLIC int bstrncmp(const bstring b0, const bstring b1, int n); /** * Search for the bstring s2 in s1 starting at position pos and looking in a * forward (increasing) direction. * * If it is found then it returns with the first position after pos where it is * found, otherwise it returns BSTR_ERR. The algorithm used is brute force; * O(m*n). */ BSTR_PUBLIC int binstr(const bstring s1, int pos, const bstring s2); /** * Search for the bstring s2 in s1 starting at position pos and looking in a * backward (decreasing) direction. * * If it is found then it returns with the first position after pos where it is * found, otherwise return BSTR_ERR. Note that the current position at pos is * tested as well -- so to be disjoint from a previous forward search it is * recommended that the position be backed up (decremented) by one position. * The algorithm used is brute force; O(m*n). */ BSTR_PUBLIC int binstrr(const bstring s1, int pos, const bstring s2); /** * Search for the bstring s2 in s1 starting at position pos and looking in a * forward (increasing) direction but without regard to case. * * If it is found then it returns with the first position after pos where it is * found, otherwise it returns BSTR_ERR. The algorithm used is brute force; * O(m*n). */ BSTR_PUBLIC int binstrcaseless(const bstring s1, int pos, const bstring s2); /** * Search for the bstring s2 in s1 starting at position pos and looking in a * backward (decreasing) direction but without regard to case. * * If it is found then it returns with the first position after pos where it is * found, otherwise return BSTR_ERR. Note that the current position at pos is * tested as well -- so to be disjoint from a previous forward search it is * recommended that the position be backed up (decremented) by one position. * The algorithm used is brute force; O(m*n). */ BSTR_PUBLIC int binstrrcaseless(const bstring s1, int pos, const bstring s2); /** * Search for the character c in b forwards from the position pos * (inclusive). * * Returns the position of the found character or BSTR_ERR if it is not found. */ BSTR_PUBLIC int bstrchrp(const bstring b, int c, int pos); /** * Search for the character c in b backwards from the position pos in bstring * (inclusive). * * Returns the position of the found character or BSTR_ERR if it is not found. */ BSTR_PUBLIC int bstrrchrp(const bstring b, int c, int pos); /** * Search for the character c in the bstring b forwards from the start of * the bstring. * * Returns the position of the found character or BSTR_ERR if it is not found. */ #define bstrchr(b, c) \ bstrchrp((b), (c), 0) /** * Search for the character c in the bstring b backwards from the end of the * bstring. * * Returns the position of the found character or BSTR_ERR if it is not found. */ #define bstrrchr(b, c) \ bstrrchrp((b), (c), blength(b) - 1) /** * Search for the first position in b0 starting from pos or after, in which * one of the characters in b1 is found. * * This function has an execution time of O(b0->slen + b1->slen). If such a * position does not exist in b0, then BSTR_ERR is returned. */ BSTR_PUBLIC int binchr(const bstring b0, int pos, const bstring b1); /** * Search for the last position in b0 no greater than pos, in which one of * the characters in b1 is found. * * This function has an execution time of O(b0->slen + b1->slen). If such a * position does not exist in b0, then BSTR_ERR is returned. */ BSTR_PUBLIC int binchrr(const bstring b0, int pos, const bstring b1); /** * Search for the first position in b0 starting from pos or after, in which * none of the characters in b1 is found and return it. * * This function has an execution time of O(b0->slen + b1->slen). If such a * position does not exist in b0, then BSTR_ERR is returned. */ BSTR_PUBLIC int bninchr(const bstring b0, int pos, const bstring b1); /** * Search for the last position in b0 no greater than pos, in which none of * the characters in b1 is found and return it. * * This function has an execution time of O(b0->slen + b1->slen). If such a * position does not exist in b0, then BSTR_ERR is returned. */ BSTR_PUBLIC int bninchrr(const bstring b0, int pos, const bstring b1); /** * Replace all occurrences of the find substring with a replace bstring * after a given position in the bstring b. * * The find bstring must have a length > 0 otherwise BSTR_ERR is returned. This * function does not perform recursive per character replacement; that is to * say successive searches resume at the position after the last replace. * * So for example: * * \code * bfindreplace(a0 = bfromcstr("aabaAb"), a1 = bfromcstr("a"), * a2 = bfromcstr("aa"), 0); * \endcode * * Should result in changing a0 to "aaaabaaAb". * * This function performs exactly (b->slen - position) bstring comparisons, * and data movement is bounded above by character volume equivalent to size * of the output bstring. */ BSTR_PUBLIC int bfindreplace(bstring b, const bstring find, const bstring repl, int pos); /** * Replace all occurrences of the find substring, ignoring case, with a * replace bstring after a given position in the bstring b. * * The find bstring must have a length > 0 otherwise BSTR_ERR is returned. This * function does not perform recursive per character replacement; that is to * say successive searches resume at the position after the last replace. * * So for example: * * \code * bfindreplacecaseless(a0 = bfromcstr("AAbaAb"), a1 = bfromcstr("a"), * a2 = bfromcstr("aa"), 0); * \endcode * * Should result in changing a0 to "aaaabaaaab". * * This function performs exactly (b->slen - position) bstring comparisons, * and data movement is bounded above by character volume equivalent to size * of the output bstring. */ BSTR_PUBLIC int bfindreplacecaseless(bstring b, const bstring find, const bstring repl, int pos); /* List of string container functions */ struct bstrList { int qty, mlen; bstring *entry; }; /** * Create an empty struct bstrList. * * The struct bstrList output structure is declared as follows: * * \code * struct bstrList { * int qty, mlen; * bstring *entry; * }; * \endcode * * The entry field actually is an array with qty number entries. The mlen * record counts the maximum number of bstring's for which there is memory * in the entry record. * * The Bstrlib API does *NOT* include a comprehensive set of functions for * full management of struct bstrList in an abstracted way. The reason for * this is because aliasing semantics of the list are best left to the user * of this function, and performance varies wildly depending on the * assumptions made. */ BSTR_PUBLIC struct bstrList * bstrListCreate(void); /** * Destroy a struct bstrList structure that was returned by the bsplit * function. Note that this will destroy each bstring in the ->entry array * as well. See bstrListCreate() above for structure of struct bstrList. */ BSTR_PUBLIC int bstrListDestroy(struct bstrList *sl); /** * Ensure that there is memory for at least msz number of entries for the * list. */ BSTR_PUBLIC int bstrListAlloc(struct bstrList *sl, int msz); /** * Try to allocate the minimum amount of memory for the list to include at * least msz entries or sl->qty whichever is greater. */ BSTR_PUBLIC int bstrListAllocMin(struct bstrList *sl, int msz); /* String split and join functions */ /** * Create an array of sequential substrings from str divided by the * character splitChar. * * Successive occurrences of the splitChar will be divided by empty bstring * entries, following the semantics from the Python programming language. To * reclaim the memory from this output structure, bstrListDestroy() should be * called. See bstrListCreate() above for structure of struct bstrList. */ BSTR_PUBLIC struct bstrList * bsplit(const bstring str, unsigned char splitChar); /** * Create an array of sequential substrings from str divided by any * character contained in splitStr. * * An empty splitStr causes a single entry bstrList containing a copy of str to * be returned. See bstrListCreate() above for structure of struct bstrList. */ BSTR_PUBLIC struct bstrList * bsplits(const bstring str, const bstring splitStr); /** * Create an array of sequential substrings from str divided by the entire * substring splitStr. * * An empty splitStr causes a single entry bstrList containing a copy of str to * be returned. See bstrListCreate() above for structure of struct bstrList. */ BSTR_PUBLIC struct bstrList * bsplitstr(const bstring str, const bstring splitStr); /** * Join the entries of a bstrList into one bstring by sequentially * concatenating them with the sep bstring in between. * * If sep is NULL, it is treated as if it were the empty bstring. Note that: * * \code * bjoin (l = bsplit (b, s->data[0]), s); * \endcode * * should result in a copy of b, if s->slen is 1. If there is an error NULL * is returned, otherwise a bstring with the correct result is returned. * See bstrListCreate() above for structure of struct bstrList. */ BSTR_PUBLIC bstring bjoin(const struct bstrList *bl, const bstring sep); /** * Join the entries of a bstrList into one bstring by sequentially * concatenating them with the content from blk for length len in between. * * If there is an error NULL is returned, otherwise a bstring with the * correct result is returned. */ BSTR_PUBLIC bstring bjoinblk(const struct bstrList *bl, const void *blk, int len); /** * Iterate the set of disjoint sequential substrings over str starting at * position pos divided by the character splitChar. * * The parm passed to bsplitcb is passed on to cb. If the function cb returns a * value < 0, then further iterating is halted and this value is returned by * bsplitcb. * * Note: Non-destructive modification of str from within the cb function * while performing this split is not undefined. bsplitcb behaves in * sequential lock step with calls to cb. I.e., after returning from a cb * that return a non-negative integer, bsplitcb continues from the position * 1 character after the last detected split character and it will halt * immediately if the length of str falls below this point. However, if the * cb function destroys str, then it *must* return with a negative value, * otherwise bsplitcb will continue in an undefined manner. * * This function is provided as an incremental alternative to bsplit that is * abortable and which does not impose additional memory allocation. */ BSTR_PUBLIC int bsplitcb(const bstring str, unsigned char splitChar, int pos, int(*cb)(void *parm, int ofs, int len), void *parm); /** * Iterate the set of disjoint sequential substrings over str starting at * position pos divided by any of the characters in splitStr. * * An empty splitStr causes the whole str to be iterated once. The parm passed * to bsplitcb is passed on to cb. If the function cb returns a value < 0, then * further iterating is halted and this value is returned by bsplitcb. * * Note: Non-destructive modification of str from within the cb function * while performing this split is not undefined. bsplitscb behaves in * sequential lock step with calls to cb. I.e., after returning from a cb * that return a non-negative integer, bsplitscb continues from the position * 1 character after the last detected split character and it will halt * immediately if the length of str falls below this point. However, if the * cb function destroys str, then it *must* return with a negative value, * otherwise bsplitscb will continue in an undefined manner. * * This function is provided as an incremental alternative to bsplits that * is abortable and which does not impose additional memory allocation. */ BSTR_PUBLIC int bsplitscb(const bstring str, const bstring splitStr, int pos, int(*cb)(void *parm, int ofs, int len), void *parm); /** * Iterate the set of disjoint sequential substrings over str starting at * position pos divided by the entire substring splitStr. * * An empty splitStr causes each character of str to be iterated. The parm * passed to bsplitcb is passed on to cb. If the function cb returns a value < * 0, then further iterating is halted and this value is returned by bsplitcb. * * Note: Non-destructive modification of str from within the cb function * while performing this split is not undefined. bsplitstrcb behaves in * sequential lock step with calls to cb. I.e., after returning from a cb * that return a non-negative integer, bsplitstrcb continues from the position * 1 character after the last detected split character and it will halt * immediately if the length of str falls below this point. However, if the * cb function destroys str, then it *must* return with a negative value, * otherwise bsplitscb will continue in an undefined manner. * * This function is provided as an incremental alternative to bsplitstr that * is abortable and which does not impose additional memory allocation. */ BSTR_PUBLIC int bsplitstrcb(const bstring str, const bstring splitStr, int pos, int(*cb)(void *parm, int ofs, int len), void *parm); /* Miscellaneous functions */ /** * Replicate the starting bstring, b, end to end repeatedly until it * surpasses len characters, then chop the result to exactly len characters. * * This function operates in-place. This function will return with BSTR_ERR * if b is NULL or of length 0, otherwise BSTR_OK is returned. */ BSTR_PUBLIC int bpattern(bstring b, int len); /** * Convert contents of bstring to upper case. * * This function will return with BSTR_ERR if b is NULL or of length 0, * otherwise BSTR_OK is returned. */ BSTR_PUBLIC int btoupper(bstring b); /** * Convert contents of bstring to lower case. * * This function will return with BSTR_ERR if b is NULL or of length 0, * otherwise BSTR_OK is returned. */ BSTR_PUBLIC int btolower(bstring b); /** * Delete whitespace contiguous from the left end of the bstring. * * This function will return with BSTR_ERR if b is NULL or of length 0, * otherwise BSTR_OK is returned. */ BSTR_PUBLIC int bltrimws(bstring b); /** * Delete whitespace contiguous from the right end of the bstring. * * This function will return with BSTR_ERR if b is NULL or of length 0, * otherwise BSTR_OK is returned. */ BSTR_PUBLIC int brtrimws(bstring b); /** * Delete whitespace contiguous from both ends of the bstring. * * This function will return with BSTR_ERR if b is NULL or of length 0, * otherwise BSTR_OK is returned. */ BSTR_PUBLIC int btrimws(bstring b); /* *printf format functions */ /** * Takes the same parameters as printf(), but rather than outputting * results to stdio, it forms a bstring which contains what would have been * output. * * Note that if there is an early generation of a '\0' character, the bstring * will be truncated to this end point. * * Note that %s format tokens correspond to '\0' terminated char * buffers, * not bstrings. To print a bstring, first dereference data element of the * the bstring: * * b1->data needs to be '\0' terminated, so tagbstrings generated by * blk2tbstr() might not be suitable. * * \code * b0 = bformat ("Hello, %s", b1->data); * \endcode */ BSTR_PUBLIC bstring bformat(const char *fmt, ...); /** * In addition to the initial output buffer b, bformata takes the same * parameters as printf (), but rather than outputting results to stdio, it * appends the results to the initial bstring parameter. * * Note that if there is an early generation of a '\0' character, the bstring * will be truncated to this end point. * * Note that %s format tokens correspond to '\0' terminated char * buffers, * not bstrings. To print a bstring, first dereference data element of the * the bstring: * * b1->data needs to be '\0' terminated, so tagbstrings generated by * blk2tbstr() might not be suitable. * * \code * bformata (b0 = bfromcstr ("Hello"), ", %s", b1->data); * \endcode */ BSTR_PUBLIC int bformata(bstring b, const char *fmt, ...); /** * After the first parameter, it takes the same parameters as printf(), but * rather than outputting results to stdio, it outputs the results to * the bstring parameter b. * * Note that if there is an early generation of a '\0' character, the bstring * will be truncated to this end point. * * Note that %s format tokens correspond to '\0' terminated char * buffers, * not bstrings. To print a bstring, first dereference data element of the * the bstring: * * b1->data needs to be '\0' terminated, so tagbstrings generated by * blk2tbstr() might not be suitable. * * \code * bassignformat (b0 = bfromcstr ("Hello"), ", %s", b1->data); * \endcode */ BSTR_PUBLIC int bassignformat(bstring b, const char *fmt, ...); /** * The bvcformata function formats data under control of the format control * string fmt and attempts to append the result to b. * * The fmt parameter is the same as that of the printf function. The variable * argument list is replaced with arglist, which has been initialized by the * va_start macro. The size of the output is upper bounded by count. If the * required output exceeds count, the string b is not augmented with any * contents and a value below BSTR_ERR is returned. If a value below -count is * returned then it is recommended that the negative of this value be used as * an update to the count in a subsequent pass. On other errors, such as * running out of memory, parameter errors or numeric wrap around BSTR_ERR is * returned. BSTR_OK is returned when the output is successfully generated and * appended to b. * * Note: There is no sanity checking of arglist, and this function is * destructive of the contents of b from the b->slen point onward. If there * is an early generation of a '\0' character, the bstring will be truncated * to this end point. * * Although this function is part of the external API for Bstrlib, the * interface and semantics (length limitations, and unusual return codes) * are fairly atypical. The real purpose for this function is to provide an * engine for the bvformata macro. */ BSTR_PUBLIC int bvcformata(bstring b, int count, const char *fmt, va_list arglist); /** * Append the bstring b with printf like formatting with the format control * string, and the arguments taken from the list of arguments after * lastarg passed to the containing function. * * If the containing function does not have extra parameters or lastarg is not * the last named parameter before the extra parameters then the results are * undefined. If successful, the results are appended to b and BSTR_OK is * assigned to ret. Otherwise BSTR_ERR is assigned to ret. * * Example: * * \code * void dbgerror (FILE *fp, const char *fmt, ...) * { * int ret; * bstring b; * bvformata(ret, b = bfromcstr ("DBG: "), fmt, fmt); * if (BSTR_OK == ret) { * fputs ((char *) bdata (b), fp); * } * bdestroy (b); * } * \endcode */ #define bvformata(ret, b, fmt, lastarg) \ do { \ bstring bstrtmp_b =(b); \ const char *bstrtmp_fmt = (fmt); \ int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \ for (;;) { \ va_list bstrtmp_arglist; \ va_start(bstrtmp_arglist, lastarg); \ bstrtmp_r = bvcformata(bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, \ bstrtmp_arglist); \ va_end(bstrtmp_arglist); \ if(bstrtmp_r >= 0) { \ /* Everything went ok */ \ bstrtmp_r = BSTR_OK; \ break; \ } else if(-bstrtmp_r <= bstrtmp_sz) { \ /* A real error? */ \ bstrtmp_r = BSTR_ERR; \ break; \ } \ /* Doubled or target size */ \ bstrtmp_sz = -bstrtmp_r; \ } \ ret = bstrtmp_r; \ } while (0); typedef int (*bNgetc)(void *parm); typedef size_t (*bNread)(void *buff, size_t elsize, size_t nelem, void *parm); /* Input functions */ /** * Read a bstring from a stream. * * As many bytes as is necessary are read until the terminator is consumed or * no more characters are available from the stream. If read from the stream, * the terminator character will be appended to the end of the returned * bstring. The getcPtr function must have the same semantics as the fgetc C * library function (i.e., returning an integer whose value is negative when * there are no more characters available, otherwise the value of the next * available unsigned character from the stream.) The intention is that parm * would contain the stream data context/state required (similar to the role of * the FILE* I/O stream parameter of fgets.) If no characters are read, or * there is some other detectable error, NULL is returned. * * bgets will never call the getcPtr function more often than necessary to * construct its output (including a single call, if required, to determine * that the stream contains no more characters.) * * Abstracting the character stream function and terminator character allows * for different stream devices and string formats other than '\n' * terminated lines in a file if desired (consider \032 terminated email * messages, in a UNIX mailbox for example.) * * For files, this function can be used analogously as fgets as follows: * * \code * fp = fopen( ... ); * if (fp) b = bgets((bNgetc) fgetc, fp, '\n'); * \endcode * * (Note that only one terminator character can be used, and that '\0' is * not assumed to terminate the stream in addition to the terminator * character. This is consistent with the semantics of fgets.) */ BSTR_PUBLIC bstring #if defined(HAVE_BGETS) bgetstream(bNgetc getcPtr, void *parm, char terminator); #else bgets(bNgetc getcPtr, void *parm, char terminator); #endif /** * Read an entire stream into a bstring, verbatum. * * The readPtr function pointer is compatible with fread sematics, except that * it need not obtain the stream data from a file. The intention is that parm * would contain the stream data context/state required (similar to the role of * the FILE* I/O stream parameter of fread.) * * Abstracting the block read function allows for block devices other than * file streams to be read if desired. Note that there is an ANSI * compatibility issue if "fread" is used directly; see the ANSI issues * section below. */ BSTR_PUBLIC bstring bread(bNread readPtr, void *parm); /** * Read from a stream and concatenate to a bstring. * * Behaves like bgets, except that it appends it results to the bstring b. The * value 1 is returned if no characters are read before a negative result is * returned from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is * returned in other normal cases. */ BSTR_PUBLIC int bgetsa(bstring b, bNgetc getcPtr, void *parm, char terminator); /** * Read from a stream and concatenate to a bstring. * * Behaves like bgets, except that it assigns the results to the bstring b. The * value 1 is returned if no characters are read before a negative result is * returned from getcPtr. Otherwise BSTR_ERR is returned on error, and 0 is * returned in other normal cases. */ BSTR_PUBLIC int bassigngets(bstring b, bNgetc getcPtr, void *parm, char terminator); /** * Read an entire stream and append it to a bstring, verbatim. * * Behaves like bread, except that it appends it results to the bstring b. * BSTR_ERR is returned on error, otherwise 0 is returned. */ BSTR_PUBLIC int breada(bstring b, bNread readPtr, void *parm); /* Stream functions */ /** * Wrap a given open stream (described by a fread compatible function * pointer and stream handle) into an open bStream suitable for the bstring * library streaming functions. */ BSTR_PUBLIC struct bStream * bsopen(bNread readPtr, void *parm); /** * Close the bStream, and return the handle to the stream that was * originally used to open the given stream. * * If s is NULL or detectably invalid, NULL will be returned. */ BSTR_PUBLIC void * bsclose(struct bStream *s); /** * Set the length of the buffer used by the bStream. * * If sz is the macro BSTR_BS_BUFF_LENGTH_GET (which is 0), the length is not * set. If s is NULL or sz is negative, the function will return with BSTR_ERR, * otherwise this function returns with the previous length. */ BSTR_PUBLIC int bsbufflength(struct bStream *s, int sz); /** * Read a bstring terminated by the terminator character or the end of the * stream from the bStream (s) and return it into the parameter r. * * The matched terminator, if found, appears at the end of the line read. If * the stream has been exhausted of all available data, before any can be read, * BSTR_ERR is returned. This function may read additional characters into the * stream buffer from the core stream that are not returned, but will be * retained for subsequent read operations. When reading from high speed * streams, this function can perform significantly faster than bgets. */ BSTR_PUBLIC int bsreadln(bstring b, struct bStream *s, char terminator); /** * Read a bstring terminated by any character in the terminators bstring or * the end of the stream from the bStream (s) and return it into the * parameter r. * * This function may read additional characters from the core stream that are * not returned, but will be retained for subsequent read operations. */ BSTR_PUBLIC int bsreadlns(bstring r, struct bStream *s, const bstring term); /** * Read a bstring of length n (or, if it is fewer, as many bytes as is * remaining) from the bStream. * * This function will read the minimum required number of additional characters * from the core stream. When the stream is at the end of the file BSTR_ERR is * returned, otherwise BSTR_OK is returned. */ BSTR_PUBLIC int bsread(bstring b, struct bStream *s, int n); /** * Read a bstring terminated by the terminator character or the end of the * stream from the bStream (s) and concatenate it to the parameter r. * * The matched terminator, if found, appears at the end of the line read. If * the stream has been exhausted of all available data, before any can be read, * BSTR_ERR is returned. This function may read additional characters into the * stream buffer from the core stream that are not returned, but will be * retained for subsequent read operations. When reading from high speed * streams, this function can perform significantly faster than bgets. */ BSTR_PUBLIC int bsreadlna(bstring b, struct bStream *s, char terminator); /** * Read a bstring terminated by any character in the terminators bstring or * the end of the stream from the bStream (s) and concatenate it to the * parameter r. * * If the stream has been exhausted of all available data, before any can be * read, BSTR_ERR is returned. This function may read additional characters * from the core stream that are not returned, but will be retained for * subsequent read operations. */ BSTR_PUBLIC int bsreadlnsa(bstring r, struct bStream *s, const bstring term); /** * Read a bstring of length n (or, if it is fewer, as many bytes as is * remaining) from the bStream and concatenate it to the parameter r. * * This function will read the minimum required number of additional characters * from the core stream. When the stream is at the end of the file BSTR_ERR is * returned, otherwise BSTR_OK is returned. */ BSTR_PUBLIC int bsreada(bstring b, struct bStream *s, int n); /** * Insert a bstring into the bStream at the current position. * * These characters will be read prior to those that actually come from the * core stream. */ BSTR_PUBLIC int bsunread(struct bStream *s, const bstring b); /** * Return the number of currently buffered characters from the bStream that * will be read prior to reads from the core stream, and append it to the * the parameter r. */ BSTR_PUBLIC int bspeek(bstring r, const struct bStream *s); /** * Iterate the set of disjoint sequential substrings over the stream s * divided by any character from the bstring splitStr. * * The parm passed to bssplitscb is passed on to cb. If the function cb returns * a value < 0, then further iterating is halted and this return value is * returned by bssplitscb. * * Note: At the point of calling the cb function, the bStream pointer is * pointed exactly at the position right after having read the split * character. The cb function can act on the stream by causing the bStream * pointer to move, and bssplitscb will continue by starting the next split * at the position of the pointer after the return from cb. * * However, if the cb causes the bStream s to be destroyed then the cb must * return with a negative value, otherwise bssplitscb will continue in an * undefined manner. * * This function is provided as way to incrementally parse through a file * or other generic stream that in total size may otherwise exceed the * practical or desired memory available. As with the other split callback * based functions this is abortable and does not impose additional memory * allocation. */ BSTR_PUBLIC int bssplitscb(struct bStream *s, const bstring splitStr, int(*cb)(void *parm, int ofs, const bstring entry), void *parm); /** * Iterate the set of disjoint sequential substrings over the stream s * divided by the entire substring splitStr. * * The parm passed to bssplitstrcb is passed on to cb. If the function cb * returns a value < 0, then further iterating is halted and this return value * is returned by bssplitstrcb. * * Note: At the point of calling the cb function, the bStream pointer is * pointed exactly at the position right after having read the split * character. The cb function can act on the stream by causing the bStream * pointer to move, and bssplitstrcb will continue by starting the next * split at the position of the pointer after the return from cb. * * However, if the cb causes the bStream s to be destroyed then the cb must * return with a negative value, otherwise bssplitscb will continue in an * undefined manner. * * This function is provided as way to incrementally parse through a file * or other generic stream that in total size may otherwise exceed the * practical or desired memory available. As with the other split callback * based functions this is abortable and does not impose additional memory * allocation. */ BSTR_PUBLIC int bssplitstrcb(struct bStream *s, const bstring splitStr, int(*cb)(void *parm, int ofs, const bstring entry), void *parm); /** * Return the defacto "EOF" (end of file) state of a stream (1 if the * bStream is in an EOF state, 0 if not, and BSTR_ERR if stream is closed or * detectably erroneous). * * When the readPtr callback returns a value <= 0 the stream reaches its "EOF" * state. Note that bunread with non-empty content will essentially turn off * this state, and the stream will not be in its "EOF" state so long as its * possible to read more data out of it. * * Also note that the semantics of bseof() are slightly different from * something like feof(), i.e., reaching the end of the stream does not * necessarily guarantee that bseof() will return with a value indicating * that this has happened. bseof() will only return indicating that it has * reached the "EOF" and an attempt has been made to read past the end of * the bStream. */ BSTR_PUBLIC int bseof(const struct bStream *s); /* Accessor macros */ /** * Returns the length of the bstring. * * If the bstring is NULL err is returned. */ #define blengthe(b, e) \ (((b) == (void *)0 || (b)->slen < 0) \ ? (int)(e) \ : ((b)->slen)) /** * Returns the length of the bstring. * * If the bstring is NULL, the length returned is 0. */ #define blength(b) \ (blengthe((b), 0)) /** * Returns the char * data portion of the bstring b offset by ofs. * * If b is NULL, err is returned. */ #define bdataofse(b, o, e) \ (((b) == (void *)0 || (b)->data == (void *)0) \ ? (char *)(e) \ : ((char *)(b)->data) + (o)) /** * Returns the char * data portion of the bstring b offset by ofs. * * If b is NULL, NULL is returned. */ #define bdataofs(b, o) \ (bdataofse((b),(o),(void *)0)) /** * Returns the char * data portion of the bstring b. * * If b is NULL, err is returned. */ #define bdatae(b, e) \ (bdataofse(b, 0, e)) /** * Returns the char * data portion of the bstring b. * * If b is NULL, NULL is returned. */ #define bdata(b) \ (bdataofs(b, 0)) /** * Returns the p'th character of the bstring b. * * If the position p refers to a position that does not exist in the bstring or * the bstring is NULL, then c is returned. */ #define bchare(b, p, e) \ ((((unsigned)(p)) < (unsigned)blength(b)) \ ? ((b)->data[(p)]) \ : (e)) /** * Returns the p'th character of the bstring b. * * If the position p refers to a position that does not exist in the bstring or * the bstring is NULL, then '\0' is returned. */ #define bchar(b, p) \ bchare((b), (p), '\0') /* Static constant string initialization macro */ /** */ #define bsStaticMlen(q, m) { (m), (int)sizeof(q) - 1, (unsigned char *)("" q "") } #if defined (_MSC_VER) /* There are many versions of MSVC which emit __LINE__ as a non-constant. */ /** * The bsStatic macro allows for static declarations of literal string * constants as struct tagbstring structures. * * The resulting tagbstring does not need to be freed or destroyed. Note that * this macro is only well defined for string literal arguments. For more * general string pointers, use the btfromcstr macro. * * The resulting struct tagbstring is permanently write protected. Attempts * to write to this struct tagbstring from any bstrlib function will lead to * BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct * tagbstring has no effect. */ #define bsStatic(q) bsStaticMlen(q, -32) #endif /* defined (_MSC_VER) */ #ifndef bsStatic /** */ #define bsStatic(q) bsStaticMlen(q, -__LINE__) #endif /* bsStatic */ /* Static constant block parameter pair */ /** * The bsStaticBlkParms macro emits a pair of comma seperated parameters * corresponding to the block parameters for the block functions in Bstrlib * (i.e., blk2bstr, bcatblk, blk2tbstr, bisstemeqblk, bisstemeqcaselessblk). * * Note that this macro is only well defined for string literal arguments. * * Examples: * * \code * bstring b = blk2bstr(bsStaticBlkParms("Fast init.")); * bcatblk(b, bsStaticBlkParms("No frills fast concatenation.")); * \endcode * * These are faster than using bfromcstr() and bcatcstr() respectively * because the length of the inline string is known as a compile time * constant. Also note that seperate struct tagbstring declarations for */ #define bsStaticBlkParms(q) \ ((void *)("" q "")), ((int)sizeof(q) -1) /* Static convenience macros for blk functions with string literals */ #define bcatStatic(b, s) ((bcatblk)((b), bsStaticBlkParms(s))) #define bfromStatic(s) ((blk2bstr)(bsStaticBlkParms(s))) #define bassignStatic(b, s) ((bassignblk)((b), bsStaticBlkParms(s))) #define binsertStatic(b, p, s, f) ((binsertblk)((b), (p), bsStaticBlkParms(s), (f))) #define bjoinStatic(b, s) ((bjoinblk)((b), bsStaticBlkParms(s))) #define biseqStatic(b, s) ((biseqblk)((b), bsStaticBlkParms(s))) #define bisstemeqStatic(b, s) ((bisstemeqblk)((b), bsStaticBlkParms(s))) #define biseqcaselessStatic(b, s) ((biseqcaselessblk)((b), bsStaticBlkParms(s))) #define bisstemeqcaselessStatic(b, s) ((bisstemeqcaselessblk)((b), bsStaticBlkParms(s))) /* Reference building macros */ /** */ #define cstr2tbstr btfromcstr /** * Fill in the tagbstring t with the '\0' terminated char buffer s. * * This action is purely reference oriented; no memory management is done. The * data member is just assigned s, and slen is assigned the strlen of s. The s * parameter is accessed exactly once in this macro. * * The resulting struct tagbstring is initially write protected. Attempts * to write to this struct tagbstring in a write protected state from any * bstrlib function will lead to BSTR_ERR being returned. Invoke the * bwriteallow on this struct tagbstring to make it writeable (though this * requires that s be obtained from a function compatible with malloc.) */ #define btfromcstr(t, s) \ do { \ (t).data = (unsigned char *)(s); \ (t).slen = ((t).data) \ ? ((int)(strlen)((char *)(t).data)) \ : 0; \ (t).mlen = -1; \ } while (0) /** */ #define blk2tbstr(t, s, l) \ do { \ (t).data = (unsigned char *)(s); \ (t).slen = l; \ (t).mlen = -1; \ } while (0) /** * Fill in the tagbstring t with the data buffer s with length len. * * This action is purely reference oriented; no memory management is done. The * data member of t is just assigned s, and slen is assigned len. Note that the * buffer is not appended with a '\0' character. The s and len parameters are * accessed exactly once each in this macro. * * The resulting struct tagbstring is initially write protected. Attempts * to write to this struct tagbstring in a write protected state from any * bstrlib function will lead to BSTR_ERR being returned. Invoke the * bwriteallow on this struct tagbstring to make it writeable (though this * requires that s be obtained from a function compatible with malloc.) */ #define btfromblk(t, s, l) blk2tbstr(t, s, l) /** * Fill the tagbstring t with the substring from b, starting from position * pos with a length len. * * The segment is clamped by the boundaries of the bstring b. This action is * purely reference oriented; no memory management is done. Note that the * buffer is not appended with a '\0' character. Note that the t parameter to * this macro may be accessed multiple times. Note that the contents of t will * become undefined if the contents of b change or are destroyed. * * The resulting struct tagbstring is permanently write protected. Attempts * to write to this struct tagbstring in a write protected state from any * bstrlib function will lead to BSTR_ERR being returned. Invoking the * bwriteallow macro on this struct tagbstring will have no effect. */ #define bmid2tbstr(t, b, p, l) \ do { \ const bstring bstrtmp_s =(b); \ if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) { \ int bstrtmp_left = (p); \ int bstrtmp_len = (l); \ if (bstrtmp_left < 0) { \ bstrtmp_len += bstrtmp_left; \ bstrtmp_left = 0; \ } \ if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left) { \ bstrtmp_len = bstrtmp_s->slen - bstrtmp_left; \ } \ if(bstrtmp_len <= 0) { \ (t).data =(unsigned char *)""; \ (t).slen = 0; \ } else { \ (t).data = bstrtmp_s->data + bstrtmp_left; \ (t).slen = bstrtmp_len; \ } \ } else { \ (t).data = (unsigned char *)""; \ (t).slen = 0; \ } \ (t).mlen = -__LINE__; \ } while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it * has been left trimmed. * * This action is purely reference oriented; no memory management is done. The * data member of t is just assigned to a pointer inside the buffer s. Note * that the buffer is not appended with a '\0' character. The s and len * parameters are accessed exactly once each in this macro. * * The resulting struct tagbstring is permanently write protected. Attempts * to write to this struct tagbstring from any bstrlib function will lead to * BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct * tagbstring has no effect. */ #define btfromblkltrimws(t, s, l) \ do { \ int bstrtmp_idx = 0, bstrtmp_len =(l); \ unsigned char *bstrtmp_s = (s); \ if (bstrtmp_s && bstrtmp_len >= 0) { \ for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) { \ if (!isspace(bstrtmp_s[bstrtmp_idx])) { \ break; \ } \ } \ } \ (t).data = bstrtmp_s + bstrtmp_idx; \ (t).slen = bstrtmp_len - bstrtmp_idx; \ (t).mlen = -__LINE__; \ } while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it * has been right trimmed. * * This action is purely reference oriented; no memory management is done. The * data member of t is just assigned to a pointer inside the buffer s. Note * that the buffer is not appended with a '\0' character. The s and len * parameters are accessed exactly once each in this macro. * * The resulting struct tagbstring is permanently write protected. Attempts * to write to this struct tagbstring from any bstrlib function will lead to * BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct * tagbstring has no effect. */ #define btfromblkrtrimws(t, s, l) \ do { \ int bstrtmp_len = (l) - 1; \ unsigned char *bstrtmp_s = (s); \ if (bstrtmp_s && bstrtmp_len >= 0) { \ for (; bstrtmp_len >= 0; bstrtmp_len--) { \ if (!isspace(bstrtmp_s[bstrtmp_len])) { \ break; \ } \ } \ } \ (t).data = bstrtmp_s; \ (t).slen = bstrtmp_len + 1; \ (t).mlen = -__LINE__; \ } while (0) /** * Fill in the tagbstring t with the data buffer s with length len after it * has been left and right trimmed. * * This action is purely reference oriented; no memory management is done. The * data member of t is just assigned to a pointer inside the buffer s. Note * that the buffer is not appended with a '\0' character. The s and len * parameters are accessed exactly once each in this macro. * * The resulting struct tagbstring is permanently write protected. Attempts * to write to this struct tagbstring from any bstrlib function will lead to * BSTR_ERR being returned. Invoking the bwriteallow macro onto this struct * tagbstring has no effect. */ #define btfromblktrimws(t, s, l) \ do { \ int bstrtmp_idx = 0, bstrtmp_len = (l) - 1; \ unsigned char *bstrtmp_s = (s); \ if (bstrtmp_s && bstrtmp_len >= 0) { \ for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) { \ if(!isspace(bstrtmp_s[bstrtmp_idx])) { \ break; \ } \ } \ for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) { \ if (!isspace(bstrtmp_s[bstrtmp_len])) { \ break; \ } \ } \ } \ (t).data = bstrtmp_s + bstrtmp_idx; \ (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \ (t).mlen = -__LINE__; \ } while (0) /* Write protection macros */ /** * Disallow bstring from being written to via the bstrlib API. * * Attempts to write to the resulting tagbstring from any bstrlib function will * lead to BSTR_ERR being returned. * * Note: bstrings which are write protected cannot be destroyed via bdestroy. */ #define bwriteprotect(t) \ do { \ if ((t).mlen >= 0) { \ (t).mlen = -1; \ } \ } while (0) /** * Allow bstring to be written to via the bstrlib API. * * Note that such an action makes the bstring both writable and destroyable. If * the bstring is not legitimately writable (as is the case for struct * tagbstrings initialized with a bsStatic value), the results of this are * undefined. * * Note that invoking the bwriteallow macro may increase the number of * reallocs by one more than necessary for every call to bwriteallow * interleaved with any bstring API which writes to this bstring. */ #define bwriteallow(t) \ do { \ if ((t).mlen == -1) { \ (t).mlen = (t).slen + ((t).slen == 0); \ } \ } while (0) /** * Returns 1 if the bstring is write protected, otherwise 0 is returned. */ #define biswriteprotected(t) \ ((t).mlen <= 0) #ifdef __cplusplus } #endif #endif /* BSTRLIB_H */ bstring-1.1.0/bstring/buniutil.c000066400000000000000000000230211516216374300166360ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /* * buniutil.c * * This file is not necessarily part of the core bstring library itself, but * is an implementation of basic UTF-8 processing for bstrings. This module * depends on bstrlib.c and utf8util.c. */ #include "bstrlib.h" #include "buniutil.h" #define UNICODE__CODE_POINT__REPLACEMENT_CHARACTER (0xFFFDL) /* int buIsUTF8Content (const bstring bu) * * Scan string and return 1 if its entire contents is entirely UTF-8 code * points. Otherwise return 0. */ int buIsUTF8Content(const bstring bu) { struct utf8Iterator iter; if (NULL == bdata(bu)) return 0; for (utf8IteratorInit(&iter, bu->data, bu->slen); iter.next < iter.slen;) { if (0 >= utf8IteratorGetNextCodePoint(&iter, -1)) return 0; } return 1; } /* int buGetBlkUTF16 (cpUcs2 *ucs2, int len, cpUcs4 errCh, * const bstring bu, int pos) * * Convert a string of UTF-8 code points (bu) skipping the first pos code * points, into a sequence of UTF-16 encoded code points. Returns the * number of UCS-2 16-bit words written to the output. No more than len * words are written to the target array ucs2. If any code point in bu is * unparsable, it will be translated to errCh. */ int buGetBlkUTF16(/* @out */ cpUcs2 *ucs2, int len, cpUcs4 errCh, const bstring bu, int pos) { struct tagbstring t; struct utf8Iterator iter; cpUcs4 ucs4; int i; int j; if (!isLegalUnicodeCodePoint(errCh)) errCh = UNICODE__CODE_POINT__REPLACEMENT_CHARACTER; if (NULL == ucs2 || 0 >= len || NULL == bdata(bu) || 0 > pos) return BSTR_ERR; for (j=0, i=0; j < bu->slen; j++) { if (0x80 != (0xC0 & bu->data[j])) { if (i >= pos) break; i++; } } t.mlen = -1; t.data = bu->data + j; t.slen = bu->slen - j; utf8IteratorInit(&iter, t.data, t.slen); ucs4 = BSTR_ERR; for (i=0; 0 < len && iter.next < iter.slen; i++) { ucs4 = utf8IteratorGetNextCodePoint(&iter, errCh); if (0 > ucs4) break; if (ucs4 < 0x10000) { *ucs2++ = (cpUcs2) ucs4; len--; } else { if (len < 2) { *ucs2++ = UNICODE__CODE_POINT__REPLACEMENT_CHARACTER; len--; } else { long y = ucs4 - 0x10000; ucs2[0] = (cpUcs2) (0xD800 | (y >> 10)); ucs2[1] = (cpUcs2) (0xDC00 | (y & 0x03FF)); len -= 2; ucs2 += 2; i++; } } } while (0 < len) { *ucs2++ = 0; len--; } utf8IteratorUninit(&iter); if (0 > ucs4) return BSTR_ERR; return i; } /* Unicode UTF-8 ------- ----- U-00000000 - U-0000007F: 0xxxxxxx U-00000080 - U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx UTF-32: U-000000 - U-10FFFF */ /* int buAppendBlkUcs4 (bstring b, const cpUcs4 *bu, int len, cpUcs4 errCh) * * Convert an array of UCS-4 code points (bu) to UTF-8 code points and * append to b. Any invalid code point is replaced by errCh. If errCh is * itself not a valid code point, then this translation will halt upon the * first error and return BSTR_ERR. Otherwise BSTR_OK is returned. */ int buAppendBlkUcs4(bstring b, const cpUcs4 *bu, int len, cpUcs4 errCh) { int oldSlen; if (NULL == bu || NULL == b || 0 > len) return BSTR_ERR; oldSlen = blengthe(b, -1); if (0 > oldSlen) return BSTR_ERR; if (!isLegalUnicodeCodePoint(errCh)) errCh = ~0; for (int i=0; i < len; i++) { unsigned char c[6]; cpUcs4 v = bu[i]; if (!isLegalUnicodeCodePoint(v)) { if (~0 == errCh) { b->slen = oldSlen; return BSTR_ERR; } v = errCh; } if (v < 0x80) { if (BSTR_OK != bconchar(b, (char) v)) { b->slen = oldSlen; return BSTR_ERR; } } else if (v < 0x800) { c[0] = (unsigned char) ( (v >> 6) + 0xc0); c[1] = (unsigned char) (( v & 0x3f) + 0x80); if (BSTR_OK != bcatblk(b, c, 2)) { b->slen = oldSlen; return BSTR_ERR; } } else if (v < 0x10000) { c[0] = (unsigned char) ( (v >> 12) + 0xe0); c[1] = (unsigned char) (((v >> 6) & 0x3f) + 0x80); c[2] = (unsigned char) (( v & 0x3f) + 0x80); if (BSTR_OK != bcatblk(b, c, 3)) { b->slen = oldSlen; return BSTR_ERR; } } else #if 0 if (v < 0x200000) #endif { c[0] = (unsigned char) ( (v >> 18) + 0xf0); c[1] = (unsigned char) (((v >> 12) & 0x3f) + 0x80); c[2] = (unsigned char) (((v >> 6) & 0x3f) + 0x80); c[3] = (unsigned char) (( v & 0x3f) + 0x80); if (BSTR_OK != bcatblk(b, c, 4)) { b->slen = oldSlen; return BSTR_ERR; } } #if 0 else if (v < 0x4000000) { c[0] = (unsigned char) ( (v >> 24) + 0xf8); c[1] = (unsigned char) (((v >> 18) & 0x3f) + 0x80); c[2] = (unsigned char) (((v >> 12) & 0x3f) + 0x80); c[3] = (unsigned char) (((v >> 6) & 0x3f) + 0x80); c[4] = (unsigned char) (( v & 0x3f) + 0x80); if (BSTR_OK != bcatblk(b, c, 5)) { b->slen = oldSlen; return BSTR_ERR; } } else { c[0] = (unsigned char) ( (v >> 30) + 0xfc); c[1] = (unsigned char) (((v >> 24) & 0x3f) + 0x80); c[2] = (unsigned char) (((v >> 18) & 0x3f) + 0x80); c[3] = (unsigned char) (((v >> 12) & 0x3f) + 0x80); c[4] = (unsigned char) (((v >> 6) & 0x3f) + 0x80); c[5] = (unsigned char) (( v & 0x3f) + 0x80); if (BSTR_OK != bcatblk(b, c, 6)) { b->slen = oldSlen; return BSTR_ERR; } } #endif } return BSTR_OK; } #define endSwap(cs, mode) \ ((mode) ? ((((cs) & 0xFF) << 8) | (((cs) >> 8) & 0xFF)) : (cs)) #define TEMP_UCS4_BUFFER_SIZE (64) /* int buAppendBlkUTF16 (bstring bu, const cpUcs2 *utf16, int len, * cpUcs2 *bom, cpUcs4 errCh) * * Append an array of UCS-2 code units (utf16) as UTF-8 to bstring bu. * Any invalid code point is replaced by errCh. If errCh is itself not a * valid code point, then this translation will halt upon the first error * and return BSTR_ERR. Otherwise BSTR_OK is returned. If a byte order * mark has been previously read, it may be passed in as bom, otherwise if * *bom is set to 0, it will be filled in with the BOM as read from the * first character if it is a BOM. */ int buAppendBlkUTF16(bstring bu, const cpUcs2 *utf16, int len, cpUcs2 *bom, cpUcs4 errCh) { cpUcs4 buff[TEMP_UCS4_BUFFER_SIZE]; int cc; int i; int sm; int oldSlen; if (NULL == bdata(bu) || NULL == utf16 || len < 0) return BSTR_ERR; if (!isLegalUnicodeCodePoint(errCh)) errCh = ~0; if (len == 0) return BSTR_OK; oldSlen = bu->slen; i = 0; /* Check for BOM character and select endianness. Also remove the BOM from the stream, since there is no need for it in UTF-8. */ if (bom && (cpUcs2) 0xFFFE == *bom) { sm = 8; } else if (bom && (cpUcs2) 0xFEFF == *bom) { sm = 0; } else if (utf16[i] == (cpUcs2) 0xFFFE) { if (bom) *bom = utf16[i]; sm = 8; i++; } else if (utf16[i] == (cpUcs2) 0xFEFF) { if (bom) *bom = utf16[i]; sm = 0; i++; } else { sm = 0; /* Assume local endianness. */ } cc = 0; while (i < len) { cpUcs4 v; int invalid = 0; v = endSwap(utf16[i], sm); i++; if ((v | 0x7FF) == 0xDFFF) { /* Deal with surrogate pairs */ if (v >= 0xDC00) { invalid = 1; /* Isolated low surrogate */ } else if (i >= len) { invalid = 1; /* Unterminated high surrogate */ } else { cpUcs4 c = endSwap(utf16[i], sm); if (c < 0xDC00 || c > 0xDFFF) { invalid = 1; } else { i++; v = ((v - 0xD800) << 10) + (c - 0xDC00) + 0x10000; } } } if (invalid) { if (~0 == errCh) { bu->slen = oldSlen; return BSTR_ERR; } v = errCh; } buff[cc] = v; cc++; if (cc >= TEMP_UCS4_BUFFER_SIZE) { if (0 > buAppendBlkUcs4(bu, buff, cc, errCh)) { bu->slen = oldSlen; return BSTR_ERR; } cc = 0; } } if (cc > 0 && 0 > buAppendBlkUcs4(bu, buff, cc, errCh)) { bu->slen = oldSlen; return BSTR_ERR; } return BSTR_OK; } bstring-1.1.0/bstring/buniutil.h000066400000000000000000000071501516216374300166500ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /** * \file * \brief Interface for basic Unicode utility functions for bstrings. * * Depends on bstrlib.h and utf8util.h. */ #ifndef BSTRLIB_UNICODE_UTILITIES #define BSTRLIB_UNICODE_UTILITIES #include "bstrlib.h" #include "utf8util.h" #ifdef __cplusplus extern "C" { #endif /** * Scan a bstring and return 1 if its entire content consists of valid UTF-8 * encoded code points, otherwise return 0. */ BSTR_PUBLIC int buIsUTF8Content(const bstring bu); /** * Convert an array of UCS-4 code points (bu, len elements) to UTF-8 and * append the result to the bstring b. * * Any invalid code point is replaced by errCh. If errCh is itself not a * valid code point, translation halts on the first error and BSTR_ERR is * returned. Otherwise BSTR_OK is returned. */ BSTR_PUBLIC int buAppendBlkUcs4(bstring b, const cpUcs4 *bu, int len, cpUcs4 errCh); /* For those unfortunate enough to be stuck supporting UTF-16. */ /** * Convert the UTF-8 bstring bu (starting at code-point offset pos) to a * sequence of UTF-16 encoded code units written to ucs2 (at most len units). * * Returns the number of UCS-2 16-bit words written. Any unparsable code * point is translated to errCh. */ BSTR_PUBLIC int buGetBlkUTF16(/* @out */ cpUcs2 *ucs2, int len, cpUcs4 errCh, const bstring bu, int pos); /** * Append an array of UTF-16 code units (utf16, len elements) to the UTF-8 * bstring bu. * * Any invalid code point is replaced by errCh. If errCh is itself not a * valid code point, translation halts on the first error and BSTR_ERR is * returned. Otherwise BSTR_OK is returned. If a byte order mark has been * previously read it may be passed in via bom; if *bom is 0 it will be * filled in from the first character if it is a BOM. */ BSTR_PUBLIC int buAppendBlkUTF16(bstring bu, const cpUcs2 *utf16, int len, cpUcs2 *bom, cpUcs4 errCh); #ifdef __cplusplus } #endif #endif /* BSTRLIB_UNICODE_UTILITIES */ bstring-1.1.0/bstring/meson.build000066400000000000000000000020441516216374300170030ustar00rootroot00000000000000bstring_sources = ['bstraux.c', 'bstrlib.c'] bstring_headers = ['bstraux.h', 'bstrlib.h'] if get_option('enable-utf8') bstring_sources += ['buniutil.c', 'utf8util.c'] bstring_headers += ['buniutil.h', 'utf8util.h'] endif install_headers(bstring_headers) # When fuzzing, the library must be static so that coverage-instrumented object # files are linked directly into the fuzz binary, where the sanitizer runtime # can resolve the __sanitizer_cov_* symbols. A shared library would leave # those references dangling at library link time. if get_option('enable-fuzzing') libbstring = static_library( meson.project_name(), bstring_sources, include_directories: bstring_inc, install: false, ) else libbstring = library( meson.project_name(), bstring_sources, version: meson.project_version(), soversion: '1', include_directories: bstring_inc, install: true, ) endif bstring_dep = declare_dependency(include_directories: bstring_inc, link_with: libbstring) bstring-1.1.0/bstring/utf8util.c000066400000000000000000000224531516216374300165770ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /* * utf8util.c * * This file is not necessarily part of the core bstring library itself, but * is a generic module for implementing UTF-8 utility functions. */ #include "utf8util.h" #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif /* Surrogate range is wrong, there is a maximum, the BOM alias is illegal and 0xFFFF is illegal */ #define isLegalUnicodeCodePoint(v) \ ((((v) < 0xD800L) || ((v) > 0xDFFFL)) && \ (((unsigned long)(v)) <= 0x0010FFFFL) && \ (((v)|0x1F0001) != 0x1FFFFFL)) void utf8IteratorInit(struct utf8Iterator *iter, unsigned char *data, int slen) { if (iter) { iter->data = data; iter->slen = (iter->data && slen >= 0) ? slen : -1; iter->start = -1; iter->next = (iter->slen >= 0) ? 0 : -1; iter->error = (iter->slen >= 0) ? 0 : 1; } } void utf8IteratorUninit(struct utf8Iterator *iter) { if (iter) { iter->data = NULL; iter->slen = -1; iter->start = iter->next = -1; } } int utf8ScanBackwardsForCodePoint(const unsigned char *msg, int len, int pos, cpUcs4 *out) { cpUcs4 v1; cpUcs4 v2; cpUcs4 v3; cpUcs4 v4; cpUcs4 x; int ret; if (NULL == msg || len < 0 || (unsigned) pos >= (unsigned) len) { return -__LINE__; } if (!out) out = &x; ret = 0; if (msg[pos] < 0x80) { *out = msg[pos]; return 0; } else if (msg[pos] < 0xC0) { if (0 == pos) return -__LINE__; if (msg[pos-1] >= 0xC1 && msg[pos-1] < 0xF8) { pos--; ret = 1; } else { if (1 == pos) return -__LINE__; if ((msg[pos-1] | 0x3F) != 0xBF) return -__LINE__; if (msg[pos-2] >= 0xE0 && msg[pos-2] < 0xF8) { pos -= 2; ret = 2; } else { if (2 == pos) return -__LINE__; if ((msg[pos-2] | 0x3F) != 0xBF) return -__LINE__; if ((msg[pos-3]|0x07) == 0xF7) { pos -= 3; ret = 3; } else return -__LINE__; } } } if (msg[pos] < 0xE0) { if (pos + 1 >= len) return -__LINE__; if ((msg[pos+1] & 0xC0) != 0x80) return -__LINE__; v1 = msg[pos] & ~0xE0; v2 = msg[pos+1] & ~0xC0; v1 = (v1 << 6) + v2; if (v1 < 0x80) return -__LINE__; *out = v1; return ret; } if (msg[pos] < 0xF0) { if (pos + 2 >= len) return -__LINE__; if ((msg[pos+1] & 0xC0) != 0x80) return -__LINE__; if ((msg[pos+2] & 0xC0) != 0x80) return -__LINE__; v1 = msg[pos] & ~0xF0; v2 = msg[pos+1] & ~0xC0; v3 = msg[pos+2] & ~0xC0; v1 = (v1 << 12) + (v2 << 6) + v3; if (v1 < 0x800) return -__LINE__; if (!isLegalUnicodeCodePoint(v1)) return -__LINE__; *out = v1; return ret; } if (msg[pos] >= 0xF8) return -__LINE__; if (pos + 3 >= len) return -__LINE__; if ((msg[pos+1] & 0xC0) != 0x80) return -__LINE__; if ((msg[pos+2] & 0xC0) != 0x80) return -__LINE__; if ((msg[pos+3] & 0xC0) != 0x80) return -__LINE__; v1 = msg[pos] & ~0xF8; v2 = msg[pos+1] & ~0xC0; v3 = msg[pos+2] & ~0xC0; v4 = msg[pos+3] & ~0xC0; v1 = (v1 << 18) + (v2 << 12) + (v3 << 6) + v4; if (v1 < 0x10000) return -__LINE__; if (!isLegalUnicodeCodePoint(v1)) return -__LINE__; *out = v1; return ret; } /* Code point UTF-8 ---------- ----- U-00000000 - U-0000007F: 0xxxxxxx U-00000080 - U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ /* * Returns next read code point for iterator. * * iter->data + iter->start points at the characters just read. * * iter->data + iter->next points at the characters that will be read next. * * iter->error is boolean indicating whether or not last read contained * an error. */ cpUcs4 utf8IteratorGetNextCodePoint(struct utf8Iterator *iter, cpUcs4 errCh) { const unsigned char *chrs; unsigned char c; unsigned char d; unsigned char e; long v; int i; int ofs; int invalid; if (NULL == iter || iter->next < 0) return errCh; if (iter->next >= iter->slen) { iter->start = iter->slen; return errCh; } if (NULL == iter->data || iter->next < 0 || utf8IteratorNoMore(iter)) return errCh; chrs = iter->data + iter->next; iter->error = 0; c = chrs[0]; ofs = 0; invalid = 0; if (c < 0xC0 || c > 0xFD) { if (c >= 0x80) { invalid = 1; } else { v = c; ofs = 1; } } else if (c < 0xE0) { if (iter->next + 1 >= iter->slen) { invalid = 1; } else { v = (c << 6u) - (0x0C0 << 6u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); v += c; if (c >= 0x40 || v < 0x80) { invalid = 1; } else { ofs = 2; } } } else if (c < 0xF0) { if (iter->next + 2 >= iter->slen) { invalid = 1; } else { v = (c << 12) - (0x0E0 << 12u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); d = (unsigned char) ((unsigned) chrs[2] - 0x080); v += (c << 6u) + d; if ((c|d) >= 0x40 || v < 0x800 || !isLegalUnicodeCodePoint(v)) { invalid = 1; } else { ofs = 3; } } } else if (c < 0xF8) { if (iter->next + 3 >= iter->slen) { invalid = 1; } else { v = (c << 18) - (0x0F0 << 18u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); d = (unsigned char) ((unsigned) chrs[2] - 0x080); e = (unsigned char) ((unsigned) chrs[3] - 0x080); v += (c << 12u) + (d << 6u) + e; if ((c|d|e) >= 0x40 || v < 0x10000 || !isLegalUnicodeCodePoint(v)) { invalid = 1; } else { ofs = 4; } } } else { /* 5 and 6 byte encodings are invalid */ invalid = 1; } if (invalid) { iter->error = 1; v = errCh; for (i = iter->next+1; i < iter->slen; i++) { if ((iter->data[i] & 0xC0) != 0x80) break; } ofs = i - iter->next; } iter->start = iter->next; iter->next += ofs; return v; } /* * Returns current code point for iterator without advancing. * * iter->data + iter->start points at the characters to be read. * * iter->data + iter->next points at the characters that will be read next. * * iter->error is boolean indicating whether or not last read contained * an error. */ cpUcs4 utf8IteratorGetCurrCodePoint(struct utf8Iterator *iter, cpUcs4 errCh) { const unsigned char *chrs; unsigned char c; unsigned char d; unsigned char e; long v; int invalid; if (NULL == iter || iter->next < 0) return errCh; if (iter->next >= iter->slen) { iter->start = iter->slen; return errCh; } if (NULL == iter->data || iter->next < 0 || utf8IteratorNoMore(iter)) return errCh; chrs = iter->data + iter->next; iter->error = 0; c = chrs[0]; invalid = 0; if (c < 0xC0 || c > 0xFD) { if (c >= 0x80) { invalid = 1; } else { v = c; } } else if (c < 0xE0) { if (iter->next + 1 >= iter->slen) { invalid = 1; } else { v = (c << 6u) - (0x0C0 << 6u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); v += c; if (c >= 0x40 || v < 0x80) invalid = 1; } } else if (c < 0xF0) { if (iter->next + 2 >= iter->slen) { invalid = 1; } else { v = (c << 12UL) - (0x0E0 << 12u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); d = (unsigned char) ((unsigned) chrs[2] - 0x080); v += (c << 6u) + d; if ((c|d) >= 0x40 || v < 0x800 || !isLegalUnicodeCodePoint(v)) invalid = 1; } } else if (c < 0xF8) { if (iter->next + 3 >= iter->slen) { invalid = 1; } else { v = (c << 18UL) - (0x0F0 << 18u); c = (unsigned char) ((unsigned) chrs[1] - 0x080); d = (unsigned char) ((unsigned) chrs[2] - 0x080); e = (unsigned char) ((unsigned) chrs[3] - 0x080); v += (c << 12UL) + (d << 6u) + e; if ((c|d|e) >= 0x40 || v < 0x10000 || !isLegalUnicodeCodePoint(v)) invalid = 1; } } else { /* 5 and 6 byte encodings are invalid */ invalid = 1; } if (invalid) { iter->error = 1; v = errCh; } return v; } bstring-1.1.0/bstring/utf8util.h000066400000000000000000000070631516216374300166040ustar00rootroot00000000000000/* Copyright 2002-2015 Paul Hsieh * This file is part of Bstrlib. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of bstrlib nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * GNU General Public License Version 2 (the "GPL"). */ /** * \file * \brief Interface for low-level UTF-8 utility functions. * * This module is standalone and does not depend on bstrlib. */ #ifndef UTF8_UNICODE_UTILITIES #define UTF8_UNICODE_UTILITIES #include /* If bstrlib.h has not been included, define the visibility attribute here. The #ifndef guard ensures we don't conflict if bstrlib.h came first. */ #ifndef BSTR_PUBLIC # if __GNUC__ >= 4 # define BSTR_PUBLIC __attribute__ ((visibility ("default"))) # else # define BSTR_PUBLIC # endif #endif #ifdef __cplusplus extern "C" { #endif #if INT_MAX >= 0x7fffffffUL typedef int cpUcs4; #elif LONG_MAX >= 0x7fffffffUL typedef long cpUcs4; #else #error This compiler is not supported #endif #if UINT_MAX == 0xFFFF typedef unsigned int cpUcs2; #elif USHRT_MAX == 0xFFFF typedef unsigned short cpUcs2; #elif UCHAR_MAX == 0xFFFF typedef unsigned char cpUcs2; #else #error This compiler is not supported #endif #define isLegalUnicodeCodePoint(v) \ ((((v) < 0xD800L) || ((v) > 0xDFFFL)) && \ (((unsigned long)(v)) <= 0x0010FFFFL) && \ (((v)|0x1F0001) != 0x1FFFFFL)) struct utf8Iterator { unsigned char *data; int slen; int start; int next; int error; }; #define utf8IteratorNoMore(it) (!(it) || (it)->next >= (it)->slen) BSTR_PUBLIC void utf8IteratorInit(struct utf8Iterator *iter, unsigned char *data, int slen); BSTR_PUBLIC void utf8IteratorUninit(struct utf8Iterator *iter); BSTR_PUBLIC cpUcs4 utf8IteratorGetNextCodePoint(struct utf8Iterator *iter, cpUcs4 errCh); BSTR_PUBLIC cpUcs4 utf8IteratorGetCurrCodePoint(struct utf8Iterator *iter, cpUcs4 errCh); BSTR_PUBLIC int utf8ScanBackwardsForCodePoint(const unsigned char *msg, int len, int pos, cpUcs4 *out); #ifdef __cplusplus } #endif #endif /* UTF8_UNICODE_UTILITIES */ bstring-1.1.0/doc/000077500000000000000000000000001516216374300137365ustar00rootroot00000000000000bstring-1.1.0/doc/Doxyfile.in000066400000000000000000003761601516216374300160660ustar00rootroot00000000000000# Doxyfile 1.14.0 # This file describes the settings to be used by the documentation system # Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use Doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use Doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = @PACKAGE@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # With the PROJECT_ICON tag one can specify an icon that is included in the tabs # when the HTML document is shown. Doxygen will copy the logo to the output # directory. PROJECT_ICON = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @OUTPUT_DIR@ # If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding Doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise cause # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # number of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but # less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the # first line (until the first dot, question mark or exclamation mark) of a # Javadoc-style comment as the brief description. If set to NO, the Javadoc- # style will behave just like regular Qt-style comments (thus requiring an # explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first # line (until the first dot, question mark or exclamation mark) of a Qt-style # comment as the brief description. If set to NO, the Qt-style will behave just # like regular Qt-style comments (thus requiring an explicit \brief command for # a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and Doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # Doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as Doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make Doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by Doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by Doxygen, so you can # mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 6 # The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to # generate identifiers for the Markdown headings. Note: Every identifier is # unique. # Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a # sequence number starting at 0 and GITHUB use the lower case version of title # with any whitespace replaced by '-' and punctuation characters removed. # The default value is: DOXYGEN. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. MARKDOWN_ID_STYLE = DOXYGEN # When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. Words listed in the # AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES # This tag specifies a list of words that, when matching the start of a word in # the documentation, will suppress auto links generation, if it is enabled via # AUTOLINK_SUPPORT. This list does not affect links explicitly created using \# # or the \link or commands. # This tag requires that the tag AUTOLINK_SUPPORT is set to YES. AUTOLINK_IGNORE_WORDS = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also makes the inheritance and # collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software) sources only. Doxygen will parse # them like normal C++ but will assume all classes use public instead of private # inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small # Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use # during processing. When set to 0 Doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 # If the TIMESTAMP tag is set different from NO then each generated page will # contain the date or date and time when the page was generated. Setting this to # NO can help when comparing the output of multiple runs. # Possible values are: YES, NO, DATETIME and DATE. # The default value is: NO. TIMESTAMP = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # will also hide undocumented C++ concepts if enabled. This option has no effect # if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all # undocumented namespaces that are normally visible in the namespace hierarchy. # If set to NO, these namespaces will be included in the various overviews. This # option has no effect if EXTRACT_ALL is enabled. # The default value is: YES. HIDE_UNDOC_NAMESPACES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES Doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and macOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run Doxygen from a directory containing a file called # DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = # The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH # environment variable) so that external tools such as latex and gs can be # found. # Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the # path already specified by the PATH variable, and are added in the order # specified. # Note: This option is particularly useful for macOS version 14 (Sonoma) and # higher, when running Doxygen from Doxywizard, because in this case any user- # defined changes to the PATH are ignored. A typical example on macOS is to set # EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin # together with the standard path, the full search path used by doxygen when # launching external tools will then become # PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin EXTERNAL_TOOL_PATH = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete # function parameter documentation. If set to NO, Doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, Doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about # undocumented enumeration values. If set to NO, Doxygen will accept # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: NO. WARN_IF_UNDOC_ENUM_VAL = NO # If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found # while parsing the user defined layout file, such as missing or wrong elements. # See also LAYOUT_FILE for details. If set to NO, problems with the layout file # will be suppressed. # The default value is: YES. WARN_LAYOUT_FILE = YES # If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the Doxygen process Doxygen will return with a non-zero status. # If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves # like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not # write the warning messages in between other messages but write them at the end # of a run, in case a WARN_LOGFILE is defined the warning messages will be # besides being in the defined file also be shown at the end of a run, unless # the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case # the behavior will remain as with the setting FAIL_ON_WARNINGS. # Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of Doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @ABS_TOP_SRCDIR@/doc \ @ABS_TOP_SRCDIR@/bstring # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). # See also: INPUT_ENCODING for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by Doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, # *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, # *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, # *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be # provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.md \ *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that Doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by Doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the Doxygen output. USE_MDFILE_AS_MAINPAGE = # If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- # directories of the project's root, is used as the documentation for that sub- # directory, except when the README.md starts with a \dir, \page or \mainpage # command. If set to NO, the README.md file needs to start with an explicit \dir # command in order to be used as directory documentation. # The default value is: YES. IMPLICIT_DIR_DOCS = YES # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # multi-line macros, enums or list initialized variables directly into the # documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) # that should be ignored while generating the index headers. The IGNORE_PREFIX # tag works for classes, function and member names. The entity will be placed in # the alphabetical list under the first letter of the entity name that remains # after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). # Note: Since the styling of scrollbars can currently not be overruled in # Webkit/Chromium, the styling will be left out of the default doxygen.css if # one or more extra stylesheets have been specified. So if scrollbar # customization is desired it has to be added explicitly. For an example see the # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. # Possible values are: LIGHT always generates light mode output, DARK always # generates dark mode output, AUTO_LIGHT automatically sets the mode according # to the user preference, uses light mode if no preference is set (the default), # AUTO_DARK automatically sets the mode according to the user preference, uses # dark mode if no preference is set and TOGGLE allows a user to switch between # light and dark mode via a button. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be # dynamically folded and expanded in the generated HTML source code. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_CODE_FOLDING = YES # If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in # the top right corner of code and text fragments that allows the user to copy # its content to the clipboard. Note this only works if supported by the browser # and the web page is served via a secure context (see: # https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: # protocol. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COPY_CLIPBOARD = YES # Doxygen stores a couple of settings persistently in the browser (via e.g. # cookies). By default these settings apply to all HTML pages generated by # Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store # the settings under a project specific key, such that the user preferences will # be stored separately. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_PROJECT_COOKIE = # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, Doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline (the HTML help workshop was already many # years in maintenance mode). You can download the HTML help workshop from the # web archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # The SITEMAP_URL tag is used to specify the full URL of the place where the # generated documentation will be placed on the server by the user during the # deployment of the documentation. The generated sitemap is called sitemap.xml # and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL # is specified no sitemap is generated. For information about the sitemap # protocol see https://www.sitemaps.org # This tag requires that the tag GENERATE_HTML is set to YES. SITEMAP_URL = # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by Doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has more details information than the tab index, you # could consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # When GENERATE_TREEVIEW is set to YES, the PAGE_OUTLINE_PANEL option determines # if an additional navigation panel is shown at the right hand side of the # screen, displaying an outline of the contents of the main page, similar to # e.g. https://developer.android.com/reference If GENERATE_TREEVIEW is set to # NO, this option has no effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. PAGE_OUTLINE_PANEL = YES # When GENERATE_TREEVIEW is set to YES, the FULL_SIDEBAR option determines if # the side bar is limited to only the treeview area (value NO) or if it should # extend to the full height of the window (value YES). Setting this to YES gives # a layout similar to e.g. https://docs.readthedocs.io with more room for # contents, but less room for the project logo, title, and description. If # GENERATE_TREEVIEW is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # When the SHOW_ENUM_VALUES tag is set doxygen will show the specified # enumeration values besides the enumeration mnemonics. # The default value is: NO. SHOW_ENUM_VALUES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled Doxygen will generate a search box for # the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /