pax_global_header00006660000000000000000000000064152036220400014504gustar00rootroot0000000000000052 comment=496079de9134f721eae6bb148ef1772d0df6494c libecoli-0.11.6/000077500000000000000000000000001520362204000133535ustar00rootroot00000000000000libecoli-0.11.6/.clang-format000066400000000000000000000121401520362204000157240ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry --- AccessModifierOffset: -4 AlignAfterOpenBracket: BlockIndent AlignArrayOfStructures: None AlignConsecutiveAssignments: None AlignConsecutiveBitFields: None AlignConsecutiveDeclarations: None AlignConsecutiveMacros: None AlignEscapedNewlines: Right AlignOperands: DontAlign AlignTrailingComments: false AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: None AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine AttributeMacros: - __capability BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: Both BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: Never AfterEnum: false AfterExternBlock: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: false BeforeWhile: false IndentBraces: false SplitEmptyFunction: true SplitEmptyNamespace: true SplitEmptyRecord: true BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom BreakBeforeConceptDeclarations: false BreakBeforeTernaryOperators: false BreakConstructorInitializers: BeforeComma BreakConstructorInitializersBeforeComma: false BreakInheritanceList: BeforeColon BreakStringLiterals: true ColumnLimit: 100 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false ForEachMacros: - LIST_FOREACH - LIST_FOREACH_SAFE - SLIST_FOREACH - SLIST_FOREACH_SAFE - STAILQ_FOREACH - STAILQ_FOREACH_SAFE - TAILQ_FOREACH - TAILQ_FOREACH_SAFE - EC_PNODE_FOREACH_CHILD - EC_COMP_FOREACH IfMacros: IncludeBlocks: Regroup IncludeCategories: - Regex: '^".*' Priority: 10000 - Regex: '^- ${{ (github.event_name == 'issue_comment' && github.event.issue.pull_request && (contains(github.event.comment.body, 'Acked-by:') || contains(github.event.comment.body, 'Tested-by:'))) || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') }} runs-on: ubuntu-latest steps: - name: Get PR info and trailer id: info uses: actions/github-script@v9 with: github-token: ${{ secrets.TRAILER_BOT_TOKEN }} script: | let pr, trailer; if (context.eventName === 'issue_comment') { const body = context.payload.comment.body; const regexp = /(acked|tested)-by:\s+(\w[^<]+\w)(?:\s+<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>)?/i; const match = body.match(regexp); if (!match) { throw new Error(`No valid trailer found in comment: ${body}`) } const { data: prData } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.issue.number }); pr = prData; trailer = `${match[1]}-by: ${match[2]} <${match[3] || 'UNKNOWN_EMAIL'}>`; } else if (context.eventName === 'workflow_run') { const headBranch = context.payload.workflow_run.head_branch; const { data: allPRs } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', }); const matchingPRs = allPRs.filter(p => p.head.ref === headBranch); if (matchingPRs.length === 0) { throw new Error(`No open PR found for ${headBranch}`) } const { data: prData } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: matchingPRs[0].number }); pr = prData; const actor = context.payload.workflow_run.triggering_actor.login; const { data: user } = await github.rest.users.getByUsername({ username: actor }); if (!user.name) { throw new Error(`User ${actor} does not expose their full name`); } trailer = `Reviewed-by: ${user.name} <${user.email || 'UNKNOWN_EMAIL'}>`; } return { commits: pr.commits, head_repo: pr.head.repo.full_name, head_ref: pr.head.ref, trailer: trailer, }; - uses: actions/checkout@v6 with: token: ${{ secrets.TRAILER_BOT_TOKEN }} repository: ${{ fromJSON(steps.info.outputs.result).head_repo }} ref: ${{ fromJSON(steps.info.outputs.result).head_ref }} fetch-depth: 0 persist-credentials: true - name: Amend all pull request commits with the trailer env: GIT_TRAILER_DEBUG: 1 COMMITS: ${{ fromJSON(steps.info.outputs.result).commits }} HEAD_REF: ${{ fromJSON(steps.info.outputs.result).head_ref }} TRAILER: ${{ fromJSON(steps.info.outputs.result).trailer }} run: | set -xe if echo "$TRAILER" | grep -qw 'UNKNOWN_EMAIL'; then NAME=$(echo "$TRAILER" | sed -En 's/.+-by:[[:blank:]]+([^<]+)[[:blank:]]<.+/\1/p') EMAIL=$(git log --pretty=%aE --author="$NAME" | head -n1) if [ -z "$EMAIL" ]; then EMAIL=$(git shortlog -se -w0 --group=author --group=committer \ --group=trailer:acked-by --group=trailer:reviewed-by \ --group=trailer:reported-by --group=trailer:signed-off-by \ --group=trailer:tested-by HEAD | \ sed -En "s/^[[:space:]]+[0-9]+[[:space:]]+$NAME <([^@]+@[^>]+)>$/\\1/p" | head -n1) fi if [ -z "$EMAIL" ]; then echo "Error: Could not find email for $NAME" exit 1 fi TRAILER=$(echo "$TRAILER" | sed "s/\\/$EMAIL/") fi GIT_COMMITTER_NAME=$(git log -1 --pretty=%cN) GIT_COMMITTER_EMAIL=$(git log -1 --pretty=%cE) git config set user.name "$GIT_COMMITTER_NAME" git config set user.email "$GIT_COMMITTER_EMAIL" export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL rm -f .git/hooks/commit-msg ln -s ../../devtools/commit-msg .git/hooks/commit-msg git rebase "HEAD~$COMMITS" \ --exec "git commit -C HEAD --no-edit --amend --trailer='$TRAILER'" git push --force origin "$HEAD_REF" libecoli-0.11.6/.github/workflows/check.yml000066400000000000000000000103651520362204000205550ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2024 Robin Jarry --- name: Check permissions: contents: read pull-requests: read on: push: branches: - master pull_request: workflow_dispatch: inputs: tmate: type: boolean description: Run the build with tmate debugging enabled required: false default: false jobs: build: if: ${{ github.actor != 'grout-bot' }} runs-on: ubuntu-24.04 env: DEBIAN_FRONTEND: noninteractive NEEDRESTART_MODE: l COVERAGE: true SANITIZE: address MESON_EXTRA_OPTS: --auto-features=enabled steps: - name: install system dependencies run: | set -xe sudo apt-get update -qy sudo apt-get install -qy --no-install-recommends \ clang \ curl \ doxygen \ doxygen2man \ gcc \ gcovr \ graphviz \ libasan8 \ libc-dev \ libedit-dev \ libreadline-dev \ libxml2-utils \ libyaml-dev \ meson \ ninja-build \ pkg-config \ tmate \ python3 curl -L https://git.sr.ht/~rjarry/dotfiles/blob/main/tmux.conf > ~/.tmate.conf - uses: actions/checkout@v6 with: persist-credentials: false fetch-depth: 0 # force fetch all history ref: ${{ github.event.pull_request.head.sha || github.ref }} - run: git config --global --add safe.directory $PWD - run: git rebase -x "git --no-pager log --oneline -1 && make tests check-symbols" "HEAD~${{ github.event.pull_request.commits }}" if: ${{ github.event.pull_request.commits }} - run: make tests check-symbols if: ${{ ! github.event.pull_request.commits }} - run: make coverage - uses: mxschmitt/action-tmate@v3 with: install-dependencies: false limit-access-to-actor: true if: ${{ github.event_name == 'workflow_dispatch' && inputs.tmate && !cancelled() }} docs: if: ${{ github.actor != 'grout-bot' && github.event.pull_request.commits }} runs-on: ubuntu-24.04 env: MESON_EXTRA_OPTS: --auto-features=disabled -Ddoc=enabled steps: - name: install system dependencies run: | set -xe sudo apt-get update -qy sudo apt-get install -qy --no-install-recommends \ doxygen \ doxygen2man \ gcc \ graphviz \ libc-dev \ libxml2-utils \ meson \ ninja-build \ pkg-config - uses: actions/checkout@v6 with: persist-credentials: false fetch-depth: 0 # force fetch all history ref: ${{ github.event.pull_request.head.sha || github.ref }} - run: git config --global --add safe.directory $PWD - run: git rebase -x "git --no-pager log --oneline -1 && make" "HEAD~${{ github.event.pull_request.commits }}" lint: if: ${{ github.actor != 'grout-bot' }} runs-on: ubuntu-latest container: fedora:latest steps: - run: dnf install -y gawk make clang-tools-extra git codespell - uses: actions/checkout@v6 with: persist-credentials: false fetch-depth: 0 # force fetch all history ref: ${{ github.event.pull_request.head.sha || github.ref }} - run: git config --global --add safe.directory $PWD - run: git rebase -x "git --no-pager log --oneline -1 && make lint" "HEAD~${{ github.event.pull_request.commits }}" if: ${{ github.event.pull_request.commits }} - run: make lint if: ${{ ! github.event.pull_request.commits }} commits: runs-on: ubuntu-24.04 if: ${{ github.event.pull_request.commits }} container: fedora:latest env: REVISION_RANGE: "HEAD~${{ github.event.pull_request.commits }}.." steps: - run: dnf install -y make git jq curl codespell - uses: actions/checkout@v6 with: persist-credentials: false fetch-depth: 0 # force fetch all history ref: ${{ github.event.pull_request.head.sha || github.ref }} - run: git config --global --add safe.directory $PWD - run: make check-commits libecoli-0.11.6/.github/workflows/publish.yml000066400000000000000000000026151520362204000211450ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry --- name: Publish on: push: branches: - master workflow_dispatch: # Allow only one concurrent deployment, skipping runs queued between the run # in-progress and latest queued. However, do NOT cancel in-progress runs as we # want to allow these production deployments to complete. concurrency: group: Publish cancel-in-progress: false jobs: build: runs-on: ubuntu-24.04 container: fedora:latest env: MESON_EXTRA_OPTS: --auto-features=disabled -Ddoc=enabled permissions: contents: read steps: - name: install system dependencies run: | dnf install -y --nodocs --setopt=install_weak_deps=0 \ doxygen \ doxygen2man \ gcc \ graphviz \ libxml2 \ make \ meson \ ninja-build \ pkgconf - uses: actions/checkout@v6 - uses: actions/configure-pages@v6 - run: make - uses: actions/upload-pages-artifact@v5 id: deployment with: path: build/doc/html/ deploy: runs-on: ubuntu-24.04 needs: build permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deploy.outputs.page_url }} steps: - uses: actions/deploy-pages@v5 id: deployment libecoli-0.11.6/.github/workflows/review.yml000066400000000000000000000005741520362204000210020ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry --- name: Review on: pull_request_review: types: [submitted] permissions: contents: read jobs: approved: if: github.event.review.state == 'approved' runs-on: ubuntu-latest steps: - name: Review approved run: echo "PR approved by ${{ github.event.review.user.login }}" libecoli-0.11.6/.mailmap000066400000000000000000000000611520362204000147710ustar00rootroot00000000000000Robin Jarry libecoli-0.11.6/CONTRIBUTING.md000066400000000000000000000230771520362204000156150ustar00rootroot00000000000000# Contribution Guidelines This document contains guidelines for contributing code to libecoli. It has to be followed in order for your patches to be approved and applied. ## Patch the code Anyone can contribute to libecoli. First you need to clone the repository and build the project: ```sh git clone https://github.com/rjarry/libecoli cd libecoli make ``` Patch the code. Write some tests. Ensure that your code is properly formatted with `clang-format`. Ensure that everything builds and works as expected. Ensure that you did not break anything. - If applicable, update unit tests. - If adding a new feature, please consider adding new tests. - Do not forget to update the docs, if applicable. - Run the linters using `make lint`. - Run unit tests using `make tests`. Once you are happy with your work, you can create a commit (or several commits). Follow these general rules: - Limit the first line (title) of the commit message to 60 characters. - Use a short prefix for the commit title for readability with `git log --oneline`. Do not use the `fix:` nor `feature:` prefixes. See recent commits for inspiration. - Only use lower case letters for the commit title except when quoting symbols or known acronyms. - Use the body of the commit message to actually explain what your patch does and why it is useful. Even if your patch is a one line fix, the description is not limited in length and may span over multiple paragraphs. Use proper English syntax, grammar and punctuation. - Address only one issue/topic per commit. - Describe your changes in the imperative mood, e.g. *"make xyzzy do frotz"* instead of *"[This patch] makes xyzzy do frotz"* or *"[I] changed xyzzy to do frotz"*, as if you are giving orders to the codebase to change its behaviour. - If you are fixing a GitHub issue, add an appropriate `Closes: ` trailer. - If you are fixing a regression introduced by another commit, add a `Fixes: ("")` trailer. - When in doubt, follow the format and layout of the recent existing commits. - The following trailers are accepted in commits. If you are using multiple trailers in a commit, it's preferred to also order them according to this list. Note, that the `commit-msg` hook (see below for installing) will automatically sort them for you when committing. * `Closes: <URL>` closes the referenced issue. * `Fixes: <sha> ("<title>")` reference the commit that introduced a regression. * `Link:` * `Cc:` * `Suggested-by:` * `Requested-by:` * `Reported-by:` * `Co-authored-by:` * `Assisted-by:` used to acknowledge AI assistants or automated tools that helped with the implementation. * `Signed-off-by:` compulsory! * `Tested-by:` used in review _after_ submission to the mailing list. If minimal changes occur between respins, feel free to include that into your respin to keep track of previous reviews. * `Reviewed-by:` used in review _after_ submission. If minimal changes occur between respins, feel free to include that into your respin to keep track of previous reviews. * `Acked-by:` used in review _after_ submission. There is a great reference for commit messages in the [Linux kernel documentation](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes). IMPORTANT: you must sign-off your work using `git commit --signoff`. Follow the [Linux kernel developer's certificate of origin][linux-signoff] for more details. All contributions are made under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) license. Please use your real name and not a pseudonym. Here is an example: Signed-off-by: Robin Jarry <rjarry@redhat.com> Once you are happy with your commits, you can verify that they are correct with the following command: ```console $ make check-commits ok [commit 1/2] 'worker: allow starting threads outside of current process affinity' ok [commit 2/2] 'main: allow creating ports when only a single cpu is available' 2/2 valid patches ``` ## Create a pull request [Fork the project](https://github.com/rjarry/libecoli/fork) if you haven't already done so. Configure your clone to point at your fork and keep a reference on the upstream repository. You can also take the opportunity to configure git to use SSH for pushing and https:// for pulling. ```console $ git remote remove origin $ git remote add upstream https://github.com/rjarry/libecoli $ git remote add origin https://github.com/foobar/libecoli $ git fetch --all Fetching origin From https://github.com/foobar/libecoli * [new branch] main -> origin/main Fetching upstream From https://github.com/rjarry/libecoli * [new branch] main -> upstream/main $ git config url.git@github.com:.pushinsteadof https://github.com/ ``` Create a local branch named after the topic of your patch(es) and push it on your fork. ```console $ git checkout -b completion $ git push origin completion Enumerating objects: 11, done. Counting objects: 100% (11/11), done. Delta compression using up to 8 threads Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 771 bytes | 771.00 KiB/s, done. Total 6 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0) remote: Resolving deltas: 100% (5/5), completed with 5 local objects. remote: remote: Create a pull request for 'completion' on GitHub by visiting: remote: https://github.com/foobar/libecoli/pull/new/completion remote: To github.com:foobar/libecoli * [new branch] completion -> completion ``` Before your pull request can be applied, it needs to be reviewed and approved by others. They will indicate their approval by replying to your patch with a [Tested-by, Reviewed-by or Acked-by][linux-review] (see also: [the git wiki][git-trailers]) trailer. For example: Acked-by: Robin Jarry <rjarry@redhat.com> There is no "chain of command" in libecoli. Anyone that feels comfortable enough to "ack" or "review" a patch should express their opinion freely with an official `Acked-by` or `Reviewed-by` trailer. If you only tested that a patch works as expected but did not conduct a proper code review, you can indicate it with a `Tested-by` trailer. You can follow the review process on GitHub [web UI](https://github.com/rjarry/libecoli/pulls). Wait for feedback. Address comments and amend your original commit(s). Then you should push to refresh your branch which will update the pull request: ```console $ git commit --amend $ git rebase -i upstream/main $ git push --force origin completion Enumerating objects: 23, done. Counting objects: 100% (23/23), done. Delta compression using up to 8 threads Compressing objects: 100% (9/9), done. Writing objects: 100% (12/12), 1.04 KiB | 1.04 MiB/s, done. Total 12 (delta 7), reused 0 (delta 0), pack-reused 0 (from 0) remote: Resolving deltas: 100% (7/7), completed with 7 local objects. To github.com:foobar/libecoli + 0bde1bea3491...e66d7e8ef74e completion -> completion (forced update) ``` Be polite, patient and address *all* of the reviewers' remarks. If you disagree with something, feel free to discuss it. Once your patch has been reviewed and approved (and if the maintainer is OK with it), the pull request will be applied. ## Code Style Please refer only to the quoted sections when guidelines are sourced from outside documents as some rules of the source material may conflict with other rules set out in this document. When updating an existing file, respect the existing coding style unless there is a good reason not to do so. ### C code All C code follows the rules specified in the [`.clang-format`](/.clang-format) file. The code can be automatically formatted by running `make format`. If `make lint` accepts your code, it is most likely properly formatted. ### Commenting Use standard C block comments: ```c /* * This is a block comment. * Please use this format instead of C99 line comments. */ int foo(void); ``` Do NOT use C99 line comments: ```c // This is a C99 line comment. // Please use block comments instead. int foo(void); ``` ### Editor modelines > Some editors can interpret configuration information embedded in source > files, indicated with special markers. For example, emacs interprets lines > marked like this: > > -*- mode: c -*- > > Or like this: > > /* > Local Variables: > compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" > End: > */ > > Vim interprets markers that look like this: > > /* vim:set sw=8 noet */ > > Do not include any of these in source files. People have their own personal > editor configurations, and your source files should not override them. This > includes markers for indentation and mode configuration. People may use > their own custom mode, or may have some other magic method for making > indentation work correctly. > — [Linux kernel coding style][linux-coding-style] In the same way, files specific to only your workflow (for example the `.vscode` directory) are not desired. If a script might be useful to other contributors, it can be sent as a separate patch that adds it to the `devtools` directory. Since it is not editor-specific, an [`.editorconfig`](/.editorconfig) file is available in the repository. [git-send-email-tutorial]: https://git-send-email.io/ [git-trailers]: https://git.wiki.kernel.org/index.php/CommitMessageConventions [linux-coding-style]: https://www.kernel.org/doc/html/v5.19-rc8/process/coding-style.html [linux-review]: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#using-reported-by-tested-by-reviewed-by-suggested-by-and-fixes [linux-signoff]: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/LICENSE�����������������������������������������������������������������������������0000664�0000000�0000000�00000002656�15203622040�0014371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������The 3-Clause BSD License 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 the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ����������������������������������������������������������������������������������libecoli-0.11.6/Makefile����������������������������������������������������������������������������0000664�0000000�0000000�00000011766�15203622040�0015026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry BUILDDIR ?= build BUILDTYPE ?= debugoptimized SANITIZE ?= none COVERAGE ?= false DESTDIR ?= V ?= 0 ifeq ($V,1) ninja_opts = --verbose Q = else Q = @ endif CC ?= gcc .PHONY: build build: $(BUILDDIR)/build.ninja $Q ninja -C $(BUILDDIR) $(ninja_opts) .PHONY: debug debug: BUILDTYPE = debug debug: SANITIZE = address debug: COVERAGE = true debug: build .PHONY: tests tests: $(BUILDDIR)/build.ninja $Q meson test -C $(BUILDDIR) --print-errorlogs $(if $(filter 1,$V),--verbose) .PHONY: install install: build $Q meson install -C $(BUILDDIR) $(ninja_opts) \ $(if $(DESTDIR),--destdir $(DESTDIR)) .PHONY: check-symbols check-symbols: build $Q set -xa && \ root=$(abspath $(BUILDDIR))/install && \ rm -rf "$$root" && \ meson install -C $(BUILDDIR) --quiet --destdir "$$root" && \ PKG_CONFIG_SYSROOT_DIR=$$root && \ PKG_CONFIG_PATH=`find $$root -type f -name libecoli.pc -printf '%h\n'` && \ deps=`meson introspect $(BUILDDIR) --targets | jq -r '.[] | select(.name == "ecoli") | .dependencies | .[]'` && \ cflags=`pkgconf --env-only --cflags libecoli` && \ ldflags=`pkgconf --env-only --libs libecoli` && \ includedir=`pkgconf --env-only --variable=includedir libecoli` && \ set -- && \ if ! echo "$$deps" | grep -q yaml; then set -- "$$@" --exclude yaml.h; fi && \ if ! echo "$$deps" | grep -q edit; then set -- "$$@" --exclude editline.h; fi && \ devtools/gen_sym_check.py "$$@" "$$includedir" | \ $(CC) $$cflags -Wno-deprecated-declarations -x c - $$ldflags -o /dev/null .PHONY: coverage coverage: tests $Q mkdir -p $(BUILDDIR)/coverage gcovr --html-details $(BUILDDIR)/coverage/index.html --txt \ -e 'test/*' -e 'examples/*' --gcov-ignore-parse-errors \ --gcov-executable `$(CC) -print-prog-name=gcov` \ --object-directory $(BUILDDIR) \ --sort uncovered-percent \ -r . $(BUILDDIR) @echo Coverage data is present in $(BUILDDIR)/coverage/index.html .PHONY: clean clean: $Q ninja -C $(BUILDDIR) clean $(ninja_opts) meson_opts = --buildtype=$(BUILDTYPE) --werror --warnlevel=2 meson_opts += -Db_sanitize=$(SANITIZE) -Db_coverage=$(COVERAGE) meson_opts += $(MESON_EXTRA_OPTS) $(BUILDDIR)/build.ninja: meson setup $(BUILDDIR) $(meson_opts) CLANG_FORMAT ?= clang-format c_src = git ls-files '*.[ch]' all_files = git ls-files ':!:subprojects' licensed_files = git ls-files ':!:*.svg' ':!:LICENSE' ':!:*.md' ':!:todo.txt' ':!:.*' ':!:*.asc' .PHONY: lint lint: @echo '[clang-format]' $Q $(c_src) | $(CLANG_FORMAT) --files=/proc/self/fd/0 --dry-run --Werror @echo '[license-check]' $Q ! $(licensed_files) | while read -r f; do \ if ! grep -qF 'SPDX-License-Identifier: BSD-3-Clause' $$f; then \ echo $$f; \ fi; \ if ! grep -q 'Copyright .*\<[0-9]\{4\}\>' $$f; then \ echo $$f; \ fi; \ done | LC_ALL=C sort -u | grep --color . || { \ echo 'error: files are missing license and/or copyright notice'; \ exit 1; \ } @echo '[white-space]' $Q $(all_files) | xargs devtools/check-whitespace @echo '[comments]' $Q $(c_src) | xargs devtools/check-comments @echo '[codespell]' $Q $(all_files) | xargs codespell .PHONY: format format: @echo '[clang-format]' $Q $(c_src) | $(CLANG_FORMAT) --files=/proc/self/fd/0 -i --verbose REVISION_RANGE ?= @{u}.. .PHONY: check-commits check-commits: $Q devtools/check-commits $(REVISION_RANGE) .PHONY: git-config git-config: @mkdir -p .git/hooks @rm -f .git/hooks/commit-msg* ln -s ../../devtools/commit-msg .git/hooks/commit-msg .PHONY: tag-release tag-release: @cur_version=`sed -En "s/^[[:space:]]+version[[:space:]]*:[[:space:]]*'([0-9\\.]+)',$$/\\1/p" meson.build` && \ next_version=`echo $$cur_version | awk -F. -v OFS=. '{$$(NF) += 1; print}'` && \ read -rp "next version ($$next_version)? " n && \ if [ -n "$$n" ]; then next_version="$$n"; fi && \ set -xe && \ sed -i "s/\<$$cur_version\>/$$next_version/" meson.build && \ git commit -sm "release: version $$next_version" -m "`devtools/git-stats v$$cur_version..`" meson.build && \ git tag -sm "`devtools/git-stats v$$cur_version..HEAD^`" "v$$next_version" .PHONY: help help: $Q echo 'Available targets:' $Q echo $Q echo ' build Configure and build with default options (default target)' $Q echo ' install Install with default options' $Q echo ' debug Configure and build with -O0 and -fsantize=address' $Q echo ' clean Clean build directory' $Q echo ' tag-release Create a release commit and signed tag' $Q echo ' tests Run unit tests' $Q echo ' coverage Run unit tests and generate test coverage report' $Q echo $Q echo 'Environment variables:' $Q echo $Q echo ' BUILDDIR Build directory (default: $(BUILDDIR))' $Q echo ' BUILDTYPE Optimization level (default: $(BUILDTYPE))' $Q echo ' DESTDIR The installation destination directory' $Q echo ' COVERAGE Enable coverage data in binaries (default: $(COVERAGE))' $Q echo ' SANITIZE Value of -fsanitize (default: $(SANITIZE))' $Q echo ' MESON_EXTRA_OPTS Additional options for meson setup' ����������libecoli-0.11.6/README.md���������������������������������������������������������������������������0000664�0000000�0000000�00000006742�15203622040�0014643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# libecoli ![logo.svg](/doc/logo.svg) [![Doxygen](https://img.shields.io/badge/doxygen-2C4AA8)](https://rjarry.github.io/libecoli/) Libecoli is an **E**xtensible **CO**mmand **LI**ne library written in C that provides a modular, composable framework for building interactive command line interfaces with dynamic completion, contextual help, and parsing capabilities. ## Use Cases * Complex interactive command line interfaces (e.g., router or network appliance CLIs). * Application arguments parsing with native bash completion support. * Generic grammar-based parsers. ## Features * **Grammar-based parsing**: Define command syntax using composable grammar nodes that form a directed graph. * **Dynamic completion**: Automatic TAB completion based on the grammar, with support for runtime-generated suggestions. * **Contextual help**: Display relevant help messages based on current input position. * **Shell-like tokenization**: Built-in lexers handle quoting, escaping, and variable expansion. * **Interactive editing**: Integration with libedit for line editing, history, and key bindings. * **YAML configuration**: Define grammars in YAML files instead of C code. * **Extensible**: Write custom node types to provide application-specific features. ## Quick Example ```c #include <ecoli.h> static bool done; static int hello_cb(const struct ec_pnode *p) { const char *name = ec_strvec_val( ec_pnode_get_strvec(ec_pnode_find(p, "NAME")), 0); printf("Hello, %s!\n", name); return 0; } static int exit_cb(const struct ec_pnode *p) { (void)p; done = true; return 0; } static bool check_exit_cb(void *priv) { (void)priv; return done; } int main(void) { struct ec_node *cmd, *root; struct ec_editline *edit; ec_init(); // Build grammar: "hello <name>" or "exit" root = ec_node("or", EC_NO_ID); cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "hello"), ec_node_any("NAME", NULL)); ec_interact_set_callback(cmd, hello_cb); ec_interact_set_help(cmd, "Say hello to someone."); ec_node_or_add(root, cmd); cmd = ec_node_str(EC_NO_ID, "exit"); ec_interact_set_callback(cmd, exit_cb); ec_interact_set_help(cmd, "Exit the program."); ec_node_or_add(root, cmd); // Add shell lexer for quote/escape handling root = ec_node_sh_lex(EC_NO_ID, root); // Create interactive session edit = ec_editline("example", stdin, stdout, stderr, 0); ec_editline_set_prompt(edit, "> "); ec_editline_set_node(edit, root); ec_editline_interact(edit, check_exit_cb, NULL); ec_editline_free(edit); ec_node_free(root); ec_exit(); return 0; } ``` ## Documentation See the [API documentation](https://rjarry.github.io/libecoli/) for details on grammar nodes, parsing, completion, and the interactive editing API. ## License libecoli uses the Open Source BSD-3-Clause license. The full license text can be found in LICENSE. libecoli makes use of Unique License Identifiers, as defined by the SPDX project (https://spdx.org/): - it avoids including large license headers in all files - it ensures licence consistency between all files - it improves automated detection of licences The SPDX tag should be placed in the first line of the file when possible, or on the second line (e.g.: shell scripts). ## Contributing Anyone can contribute to libecoli. See [`CONTRIBUTING.md`](/CONTRIBUTING.md). ## Projects that use libecoli * [6WIND Virtual Service Router](https://doc.6wind.com/new/vsr-3/latest/vsr-guide/user-guide/cli/index.html) * [grout # a graph router based on DPDK](https://github.com/DPDK/grout) ������������������������������libecoli-0.11.6/devtools/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0015212�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/devtools/ai-instructions.md���������������������������������������������������������0000664�0000000�0000000�00000005150�15203622040�0020670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# libecoli - AI Instructions ## Code Style & Conventions See @.editorconfig for indentation and line length limits. See @CONTRIBUTING.md for C coding style. ## Git Commit Guidelines See @CONTRIBUTING.md for expected commit message format and git trailers. - **Title:** Use component-based prefix (e.g., `ip:`, `cli:`, `infra:`) followed by all lowercase title except for acronyms/symbols. - **Use imperative mood:** "add feature" not "added feature" - **Hard wrap body to 72 columns** - **Sign-off required:** Use `git commit --signoff` - **One issue per commit** - **Emojis are forbidden:** Use regular UTF-8 printable characters. - **Avoid bullet lists unless necessary:** Prefer regular prose - **Do not paraphrase the diff:** No need to repeat what files were added, modified or changed. Only mention added functions or symbols when this is relevant in the context of the commit. - **Avoid shallow/empty statements:** We don't need AI slop. We need actual information that is not present in the diff already. - **Commit body is NOT optional** Here is a pathological example: ``` 🚀 Restructure core components for better scalability The following modifications were performed to ensure improved scalability and maintainability: - Refactored code to follow best practices - Reorganized files to make the structure cleaner - Simplified logic in some areas for easier understanding - Prepared the codebase for future features and improvements This refactor sets the stage for a more robust architecture. The following files were modified: - modules/infra/control/gr_port.h - modules/infra/control/iface.c - modules/infra/control/nexthop.c - modules/infra/control/port.c Assisted-by: Lame AI v2 Signed-off-by: Foo Bar <foo@bar.com> ``` Here is a good example: ``` rx,tx: use one node per port queue Instead of dealing with a list of queues to poll packets from, instantiate one port_rx node clone for each configured port RX queue. Do the same thing for port_tx and each configured port TX queue. Depending on worker RX queue mapping, include the associated port_rx-p*q* clones and initialize their context data accordingly. Always include *all* port_tx-p*q* clones in all graphs, regardless of worker TX queue mappings. Instead, configure the port_output node context to steer packets to the proper port_tx-p*q* clone. All of this requires delicate juggling with multiple node names lists to ensure that nodes are cloned when needed before they are affected to a graph. Secondly, we need to call rte_node_free() on those which are not used in any graph after all of them have been reloaded. Signed-off-by: Foo Bar <foo@bar.com> ``` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/devtools/check-comments�������������������������������������������������������������0000775�0000000�0000000�00000001430�15203622040�0020036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/awk -f # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry BEGIN { isatty = system("test -t 1") == "0" retcode = 0 } function color(code, s) { if (isatty) { return "\033[" code "m" s "\033[0m" } return s } function red(s) { return color("31", s) } function green(s) { return color("32", s) } function yellow(s) { return color("33", s) } function magenta(s) { return color("35", s) } function cyan(s) { return color("36", s) } function hl(s, pattern) { gsub(pattern, yellow("&"), s) # convert tab characters to 8 spaces to allow coloring gsub(/\t/, " ", s) return s } /[\t ]*\/\// { retcode = 1 print magenta(FILENAME) cyan(":") green(FNR) cyan(":") \ hl($0, "[\\t ]*\\/\\/.*") red("<-- C99 line comment used") } END { exit retcode } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/devtools/check-commits��������������������������������������������������������������0000775�0000000�0000000�00000006767�15203622040�0017706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry set -eu revision_range="${1?revision range}" valid=0 revisions=$(git rev-list --reverse "$revision_range") total=$(echo $revisions | wc -w) if [ "$total" -eq 0 ]; then exit 0 fi tmp=$(mktemp) trap "rm -f $tmp" EXIT allowed_trailers=" Fixes Closes Link Cc Suggested-by Requested-by Reported-by Assisted-by Signed-off-by Co-authored-by Tested-by Reviewed-by Acked-by " n=0 title= fail=false repo=rjarry/libecoli repo_url=https://github.com/$repo api_url=https://api.github.com/repos/$repo err() { echo "error [commit $n/$total] '$title' $*" >&2 fail=true } check_issue() { json=$(curl -f -X GET -L --no-progress-meter \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "$api_url/issues/${1##*/}") || return 1 test "$(printf '%s\n' "$json" | jq -r .state)" = open } for rev in $revisions; do n=$((n + 1)) title=$(git log --format='%s' -1 "$rev") fail=false if ! echo "$title" | grep -qE '^Revert ".+"$'; then if [ "$(echo "$title" | wc -m)" -gt 72 ]; then err "title is longer than 72 characters, please make it shorter" fi if ! echo "$title" | grep -qE '^[a-z0-9,{}/_-]+: '; then err "title lacks a lowercase topic prefix (e.g. 'ipv6:')" fi if echo "$title" | grep -qE '^[a-z0-9,{}/_-]+: [A-Z][a-z]'; then err "title starts with an capital letter, please use lower case" fi if ! echo "$title" | grep -qE '[[:alnum:])]$'; then err "title ends with punctuation, please remove it" fi fi author=$(git log --format='%an <%ae>' -1 "$rev") if ! git log --format="%(trailers:key=Signed-off-by,only,valueonly,unfold)" -1 "$rev" | grep -qFx "$author"; then err "'Signed-off-by: $author' trailer is missing" fi for trailer in $(git log --format="%(trailers:only,keyonly)" -1 "$rev"); do if ! echo "$allowed_trailers" | grep -qFx "$trailer"; then err "trailer '$trailer' is misspelled or not in the sanctioned list" fi done git log --format="%(trailers:key=Closes,only,valueonly,unfold)" -1 "$rev" > $tmp while read -r value; do if [ -z "$value" ]; then continue fi case "$value" in $repo_url/*/[0-9]*) if ! check_issue "$value"; then err "'$value' does not reference a valid open issue" fi ;; \#[0-9]*) value=${value#\#} err "please use the full issue URL: 'Closes: $repo_url/issues/$value'" ;; *) err "invalid trailer value '$value'. The 'Closes:' trailer must only be used to reference issue URLs" ;; esac done < "$tmp" git log --format="%(trailers:key=Fixes,only,valueonly,unfold)" -1 "$rev" > $tmp while read -r value; do if [ -z "$value" ]; then continue fi fixes_rev=$(echo "$value" | sed -En 's/([A-Fa-f0-9]{7,})[[:space:]]\(".*"\)/\1/p') if ! git cat-file commit "$fixes_rev" >/dev/null; then err "trailer 'Fixes: $value' does not refer to a known commit" fi done < "$tmp" body=$(git log --format='%b' -1 "$rev") body=${body%$(git log --format='%(trailers)' -1 "$rev")} if [ "$(echo "$body" | wc -w)" -lt 3 ]; then err "body has less than three words, please describe your changes" fi if ! git log --format='%s%n%b' -1 "$rev" | codespell -; then err "spelling errors in title and/or body" fi if ! [ "$(git log --format='%P' -1 "$rev" | wc -w)" = 1 ]; then err "merge commit found, please rebase your code" fi if [ "$fail" = true ]; then continue fi echo "ok [commit $n/$total] '$title'" valid=$((valid + 1)) done echo "$valid/$total valid commits" if [ "$valid" -ne "$total" ]; then exit 1 fi ���������libecoli-0.11.6/devtools/check-whitespace�����������������������������������������������������������0000775�0000000�0000000�00000002304�15203622040�0020346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/awk -f # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2024 Robin Jarry BEGIN { isatty = system("test -t 1") == "0" retcode = 0 } function color(code, s) { if (isatty) { return "\033[" code "m" s "\033[0m" } return s } function red(s) { return color("31", s) } function green(s) { return color("32", s) } function magenta(s) { return color("35", s) } function cyan(s) { return color("36", s) } function bg_red(s) { return color("41", s) } function hl_ws(s, pattern) { gsub(pattern, bg_red("&"), s) # convert tab characters to 8 spaces to allow coloring gsub(/\t/, " ", s) return s } / +\t+/ { retcode = 1 print magenta(FILENAME) cyan(":") green(FNR) cyan(":") \ hl_ws($0, " +\\t+") red("<-- space(s) followed by tab(s)") } /[ \t]+$/ { retcode = 1 print magenta(FILENAME) cyan(":") green(FNR) cyan(":") \ hl_ws($0, "[ \\t]+$") red("<-- trailing whitespace") } ENDFILE { # will only match on GNU awk, ignored on non-GNU versions if ($0 ~ /^[ \t]*$/) { retcode = 1 print magenta(FILENAME) cyan(": ") red("trailing new line(s)") } else if (RT != "\n") { retcode = 1 print magenta(FILENAME) cyan(": ") red("no new line at end of file") } } END { exit retcode } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/devtools/commit-msg�����������������������������������������������������������������0000775�0000000�0000000�00000004072�15203622040�0017217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry # This is a git hook that should be installed in ./.git/hooks/commit-msg and # should be executable. It will be invoked with the message file as argument by # git when creating commits. # # It will ensure that git trailers (see git-interpret-trailers(1)) are in # a consistent order. set -e debug() { if [ "$GIT_TRAILER_DEBUG" = 1 ]; then "$@" >&2 fi } trailer_order=" Fixes: Closes: Link: Cc: Suggested-by: Requested-by: Reported-by: Assisted-by: Signed-off-by: Co-authored-by: Tested-by: Reviewed-by: Acked-by: " file=${1?file} tmp=$(mktemp) trap "rm -f $tmp" EXIT # Read unfolded trailers and normalize case. git interpret-trailers --parse --trim-empty "$file" | while read -r key value; do # Force title case on trailer key. first_letter=$(echo "$key" | sed 's/^\(.\).*/\1/' | tr '[:lower:]' '[:upper:]') other_letters=$(echo "$key" | sed 's/^.\(.*\)/\1/' | tr '[:upper:]' '[:lower:]') key="$first_letter$other_letters" # Find sort order of this key. order=$(echo "$trailer_order" | grep -Fxn "$key" | sed -nE 's/^([0-9]+):.*/\1/p') if [ -z "$order" ]; then echo "warning: unknown trailer '$key'" >&2 # Unknown trailers are always first. order="0" fi echo "$order $key $value" done | # Sort trailers according to their numeric order, trim the numeric order. LC_ALL=C sort -n | sed -E 's/^[0-9]+ //' > "$tmp" debug echo ==== sanitized trailers ==== debug cat "$tmp" # Unfortunately, reordering trailers is not possible at the moment. Delete all # trailers first. The only way to do it is to force replace existing trailers # with empty values and trim empty trailers one by one. while read -r key value; do git interpret-trailers --in-place --if-exists=replace \ --trailer="$key " "$file" git interpret-trailers --in-place --trim-empty "$file" done < "$tmp" set -- while read -r trailer; do set -- "$@" --trailer="$trailer" done < "$tmp" # Remove duplicate "key: value" trailers (e.g. duplicate signed-off-by). git interpret-trailers --in-place --if-exists=addIfDifferent "$@" "$file" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/devtools/gen_sym_check.py�����������������������������������������������������������0000775�0000000�0000000�00000006672�15203622040�0020400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2026 Robin Jarry """ Generate a C file that references all function and variable declarations from project headers. This ensures the linker will complain if any declared symbol is missing a definition. """ import argparse import json import subprocess def is_project_header(path: str, excludes: list[str]) -> bool: if not path: return False if path.startswith("/usr"): return False if path.endswith(".c"): return False for exc in excludes: if path.endswith(exc): return False return True def get_effective_file(node: dict, current_file: str) -> str: loc = node.get("loc", {}) range_file = node.get("range", {}).get("begin", {}).get("file", "") if range_file: return range_file f = loc.get("file", "") if f: return f f = loc.get("expansionLoc", {}).get("file", "") if f: return f return current_file def update_current_file(node: dict, current_file: str) -> str: loc = node.get("loc", {}) f = loc.get("file", "") if f: return f f = node.get("range", {}).get("begin", {}).get("file", "") if f: return f return current_file def extract_symbols(ast: dict, excludes: list[str]) -> tuple[list[str], list[str]]: funcs = [] variables = [] current_file = "" for node in ast.get("inner", []): effective_file = get_effective_file(node, current_file) current_file = update_current_file(node, current_file) kind = node.get("kind") name = node.get("name", "") if not is_project_header(effective_file, excludes): continue if name.startswith("_"): continue if kind == "FunctionDecl": if node.get("storageClass", "") == "static": continue funcs.append(name) elif kind == "VarDecl": if node.get("storageClass", "") == "extern": variables.append(name) return sorted(set(funcs)), sorted(set(variables)) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("include_dir") parser.add_argument("--exclude", action="append", default=[]) args = parser.parse_args() # Dump the AST in JSON format. cmd = ( "clang", "-Xclang", "-ast-dump=json", "-fsyntax-only", "-I" + args.include_dir, "-include", "ecoli.h", "-x", "c", "/dev/null", ) result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: parser.error(result.stderr) # Extract all ecoli symbols from the AST ast = json.loads(result.stdout) funcs, variables = extract_symbols(ast, args.exclude) # Generate a dummy C source file that references all declared symbols in # ecoli headers. print("/* Auto-generated - do not edit. */") print() print("#include <ecoli.h>") print("#include <stdio.h>") print() print("static void *syms[] = {") for f in funcs: print(f"\t(void *){f},") for v in variables: print(f"\t(void *)&{v},") print("};") print() print("int main(void)") print("{") print("\tfor (unsigned i = 0; i < sizeof(syms) / sizeof(syms[0]); i++)") print('\t\tprintf("%p\\n", syms[i]);') print("\treturn 0;") print("}") if __name__ == "__main__": main() ����������������������������������������������������������������������libecoli-0.11.6/devtools/git-stats������������������������������������������������������������������0000775�0000000�0000000�00000001734�15203622040�0017064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2024 Robin Jarry set -e set -o pipefail columns="Author,Commits,Changed Files,Insertions,Deletions" git shortlog -sn "$@" | while read -r commits author; do git log --author="$author" --pretty=tformat: --numstat "$@" | { adds=0 subs=0 files=0 while read -r a s f; do if [ "$a" = "-" ]; then a=1 fi if [ "$s" = "-" ]; then s=1 fi adds=$((adds + a)) subs=$((subs + s)) files=$((files + 1)) done printf '%s;%d;%d;%+d;%+d;\n' \ "$author" "$commits" "$files" "$adds" "-$subs" } done | column -t -s ';' -N "$columns" -R "${columns#*,}" | sed -E 's/[[:space:]]+$//' echo columns="Reviewer/Tester,Commits" git shortlog -sn \ --group=trailer:acked-by \ --group=trailer:tested-by \ --group=trailer:reviewed-by "$@" | while read -r commits author; do printf '%s;%s\n' "$author" "$commits" done | column -t -s ';' -N "$columns" -R "${columns#*,}" | sed -E 's/[[:space:]]+$//' ������������������������������������libecoli-0.11.6/devtools/gpg-signing-key.asc��������������������������������������������������������0000664�0000000�0000000�00000005264�15203622040�0020710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP PUBLIC KEY BLOCK----- mDMEYaOAcBYJKwYBBAHaRw8BAQdAv4gyJJyJ6Pa352i9dkChWv9InSp3Lcb0hliK oI3AMKC0HFJvYmluIEphcnJ5IDxyb2JpbkBqYXJyeS5jYz6IkAQTFggAOBYhBNwH GOMi4sdgXr3IMUaVfsCP0P6QBQJho4BwAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B AheAAAoJEEaVfsCP0P6Qj8oA/iH+GbIH6Eab3cqsASKA/3CQb93XmxqsI9hjsuqn ba1gAP91eGGfZoNlmc1+vgNgV/20euYhia0phEE9KnEergcLA4kCMwQQAQoAHRYh BMY9u+8hQnziSdvZawYSEpRGR6QRBQJjCLQTAAoJEAYSEpRGR6QRK/AP/ApUhbIh dm+ExNnb+kPYvazq3O6nesPWcyOTXGoB/WUunGEom3zPfYcs/+7vmqjMvx+gdBIv 1zo8wvc5Q0eEAGt2dzCTHsycprCd9eyLWUwujfUzw9hzuE/9kyZY5lQkfVpwm0j6 Ju6t/nRCNhrAB3H+EtHvu9QWCvucuNbbza00tMQKJw1cGVR40tA6IIdoHcmNgG9j NoY7trts0dRJRvBCIG5uGpL8WC9aizxM+9hKOlpvdBS/+Ys19wRKw634iKyr+3R5 LdqlU73HWSPS/0SLZmomffwRJnHDbIKrUtBRrEqpP/or+9pWndqBEjgJbUXZd9Eg lNXoI7M3V2ZJFF0Xesd0u1BDrw9fQYs+uLBhOIrbf4gRb5qD/VZvuw+/M9HJleF+ 9d2Vj8GRYgIWEBSYIyHKlRI81Rzf6pCbbkWV+pBOg7a+yMZuporAKp7u3mTzJkwX 1m/AXNRjZ6x7MybuGpqvYzbiydgJerYS7M0Iy7jxX9GRO0ScLasUx/MZGYq9JrfN hG31a5eu3uFjdbyjEIJ/mZLYA99nFT40k//0ubcajP1zg/BiOMGaoSfG0rPjBYCc 56gAPL+SzVXqyQQis/mjpfjIxV4xYWuEtjizQzzSvU85T6GPTPxFnJPhfcud9xTY h7+yIc41jgSBQ0RTFGQ8Vnaw1antI5xfmvKUuDgEYaOAcBIKKwYBBAGXVQEFAQEH QJKMaL7qmDBd6lb+6KCPmfO8hIYWxct8kdo8iceWftIZAwEIB4h+BBgWCAAmAhsM FiEE3AcY4yLix2BevcgxRpV+wI/Q/pAFAmGjgbMFCQPCaEMACgkQRpV+wI/Q/pDD owEA+3ZS2AzhG4Hk80oOUCcn7hv0NPHqaYa6vX2c/8l1QxUA/jkCAUuKGZSpvSbf jmHlsElCPYOJIx+Ov92vyjP/eIoCiH4EGBYKACYCGwwWIQTcBxjjIuLHYF69yDFG lX7Aj9D+kAUCaUa1kwUJC2WcIwAKCRBGlX7Aj9D+kIWpAQDuOZ52vn16LKLWOjK4 fhfzK3mr0HUDcnjg9vGwBl24awEArtanD+yLvaYORWwGUYg+EzY5Ma733cQFOczp B0z8Dwm4MwRho4FCFgkrBgEEAdpHDwEBB0BBwrifCsMD3W97/+Q9JM2VthuZ7tOA OiIhluxCpE0r0Ih+BBgWCAAmFiEE3AcY4yLix2BevcgxRpV+wI/Q/pAFAmGjgUIC GyAFCQPCZwAACgkQRpV+wI/Q/pAnhgEA4h0xjyK5dH/G+4OP1xdW2az8E9/Vnm1B E2A5LQ6lz7EA/RX9XnK5hZgaDEStRnmthCZ2MpIbc430ox+SIBlLoFUOiH4EGBYK ACYCGyAWIQTcBxjjIuLHYF69yDFGlX7Aj9D+kAUCaUa1oAUJC2WbUQAKCRBGlX7A j9D+kFlVAQDRnxDQJ2vR3abdj7DySamV1W9wuhq+/eJVggHyz4FviAD+KuQjoKAa +l1TZvFfpFUgfAgRtiu9mbJADYAcEXNTuQm4MwRho4FcFgkrBgEEAdpHDwEBB0AY FtKTC0QyGYBFChL78bax1FZ7YlKw52BgWEBCzAcalYj1BBgWCAAmFiEE3AcY4yLi x2BevcgxRpV+wI/Q/pAFAmGjgVwCGwIFCQPCZwAAgQkQRpV+wI/Q/pB2IAQZFggA HRYhBNi2qZB8OrcgQotgaGJxjg1mb8M1BQJho4FcAAoJEGJxjg1mb8M1JNwBAIkO dCpYHyeU+Y/4WM0vu2Z+d50ShvcTjiBq9i1cIiF9AP938RY0dgvilD7rEAvWOn6t BKif/vLFrv9OVMLqntNODTXSAP9oMeclstLRWPzBCYKGU7OGg9jTpdyFQVh0Qj0+ 1YAUbwD8Cm/L+fGHdblS0LYGWqJ3/LbmuT770uQlAL+6oON0SgKI9QQYFgoAJgIb AhYhBNwHGOMi4sdgXr3IMUaVfsCP0P6QBQJpRrWgBQkLZZs3AIF2IAQZFggAHRYh BNi2qZB8OrcgQotgaGJxjg1mb8M1BQJho4FcAAoJEGJxjg1mb8M1JNwBAIkOdCpY HyeU+Y/4WM0vu2Z+d50ShvcTjiBq9i1cIiF9AP938RY0dgvilD7rEAvWOn6tBKif /vLFrv9OVMLqntNODQkQRpV+wI/Q/pAVIgD/SwOAFUCjd1DpgkgOmh0LHi3TJ9vd +Uw+y/QL1quPp8wBANI8YGnA5YgJucVgErgy/RgFfhbiA7sgHxQCFaAN2BAO =xDQk -----END PGP PUBLIC KEY BLOCK----- ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0014120�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/Doxyfile.in���������������������������������������������������������������������0000664�0000000�0000000�00000003363�15203622040�0016240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> PROJECT_NAME = "Libecoli" PROJECT_BRIEF = "Extensible COmmand LIne library" PROJECT_NUMBER = @VERSION@ INPUT = @TOPDIR@/include @TOPDIR@/include/ecoli @EXAMPLES_INPUT@ @TOPDIR@/doc @OUTPUT@ OUTPUT_DIRECTORY = @OUTPUT@ FILE_PATTERNS = *.h *.c *.md RECURSIVE = YES IMAGE_PATH = @TOPDIR@/doc EXCLUDE_SYMBOLS = __* TAILQ_* free* init* realloc malloc boolean complete desc dict u64 test type size string subschema parse node name list key i64 help set_config schema priority exit get_child get_children_count eval_* @1* @3* lstat opendir readdir closedir dirfd fstatat ip_pool* JAVADOC_AUTOBRIEF = YES OPTIMIZE_OUTPUT_FOR_C = YES ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES EXTRACT_STATIC = YES DISTRIBUTE_GROUP_DOC = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_SCOPE_NAMES = NO GENERATE_DEPRECATEDLIST = YES INTERNAL_DOCS = NO VERBATIM_HEADERS = NO ALPHABETICAL_INDEX = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = @TOPDIR@/include STRIP_FROM_INC_PATH = @TOPDIR@/include WARN_AS_ERROR = NO MARKDOWN_SUPPORT = YES EXAMPLE_PATH = @TOPDIR@/examples EXAMPLE_PATTERNS = *.c EXAMPLE_RECURSIVE = YES HTML_OUTPUT = html HTML_DYNAMIC_SECTIONS = YES SEARCHENGINE = YES SORT_MEMBER_DOCS = NO SOURCE_BROWSER = YES GENERATE_HTML = @GENERATE_HTML@ GENERATE_XML = @GENERATE_XML@ GENERATE_LATEX = NO GENERATE_MAN = NO XML_OUTPUT = xml �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/architecture.md�����������������������������������������������������������������0000664�0000000�0000000�00000012663�15203622040�0017134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@page architecture Architecture Libecoli is written in C and provides an API for building interactive command line interfaces. The library consists of several components: - **Core**: The main API for parsing and completing input strings. - **Nodes**: The modular component of libecoli. Each node type implements specific parsing behavior (integers, strings, regular expressions, sequences, etc.). - **YAML parser**: Loads grammar trees from YAML configuration files. - **Editline**: Integration helpers for the editline library. - **Utilities**: Logging, string manipulation, string vectors, hash tables, and other support functions. ## The Grammar Graph Nodes are organized into a directed graph that defines the grammar. Although this structure is typically a tree, loops are permitted in certain cases. The grammar graph describes how input is parsed and completed. Consider the following example: ![Simple grammar tree](simple-tree.svg) The same structure can be represented textually as: ``` sh_lex( seq( str(foo), option( str(bar) ) ) ) ``` This grammar matches: - `"foo"` - `"foo bar"` It does not match: - `"bar"` - `"foobar"` - `""` (empty input) ## Parsing When libecoli parses input, it traverses the grammar graph using depth-first search and constructs a parse tree. The following example illustrates the process when @ref ec_parse_strvec() is called: 1. The input is a string vector containing `["foo bar"]`. 2. The `sh_lex` node tokenizes the input using shell lexing rules, producing `["foo", "bar"]`, and passes this to its child. 3. The `seq` node forwards the input to its first child. 4. The `str` node checks whether the first token equals `foo`. Since it does, the node returns 1 (the number of consumed tokens) to its parent. 5. The `seq` node passes the remaining tokens `["bar"]` to its next child. 6. The `option` node forwards the input to its child, which matches and returns 1. 7. The `seq` node has processed all children and returns 2 (total consumed tokens). 8. The `sh_lex` node compares the return value against its token count and returns 1 (success) to the caller. When a node fails to match, it returns @ref EC_PARSE_NOMATCH to its parent. This value propagates up the tree until a node handles the failure. For example, the `or` node tries each child in sequence until one matches. ## The Parse Tree Consider another example grammar: ![Grammar with or node](simple-tree2.svg) Without a lexer node, the input must already be tokenized. This grammar matches: - `["foo"]` - `["bar", "1"]` - `["bar", "100"]` It does not match: - `["bar 1"]` (not tokenized) - `["bar"]` (missing required argument) - `[]` (empty input) During parsing, a parse tree is constructed. When parsing succeeds, the tree describes which grammar nodes matched and what input each node consumed. Parsing `["bar", "1"]` produces the following parse tree: ![Parse tree structure](parse-tree.svg) Each parse tree node references the grammar node that matched and stores the corresponding input tokens: ![Parse tree with token references](parse-tree2.svg) Not all grammar nodes appear in the parse tree. In this example, `str("foo")` was not matched and therefore does not appear. Conversely, a single grammar node may appear multiple times in the parse tree. Consider this grammar that matches zero or more occurrences of `foo`: ![Grammar with many node](simple-tree3.svg) Parsing `[foo, foo, foo]` produces: ![Parse tree with repeated matches](parse-tree3.svg) ## Node Identifiers Each grammar node may have an optional string identifier. This identifier enables locating specific nodes in the parse tree after parsing completes. For example, a node created with `ec_node_int("COUNT", 0, 100, 10)` can be found using `ec_pnode_find(parse_tree, "COUNT")`. Identifiers need not be unique within the grammar graph. When multiple nodes share the same identifier, @ref ec_pnode_find() returns the first match. Use @ref ec_pnode_find_next() to iterate through additional matches. ## Completion The completion mechanism operates similarly to parsing but collects possible continuations instead of validating input. When the user requests completion, libecoli traverses the grammar graph and queries each node for tokens that could follow the current partial input. Completions are grouped by the grammar node that produced them. Each completion item has one of three types: - @ref EC_COMP_FULL - A complete token (e.g., a command keyword). - @ref EC_COMP_PARTIAL - A partial token requiring further input (e.g., a directory path). - @ref EC_COMP_UNKNOWN - Valid input that cannot be completed (e.g., an arbitrary string matching a regular expression). ## Node Attributes Grammar nodes support arbitrary key-value attributes. The interactive layer uses these attributes to store: - Help text displayed during completion or on errors - Callbacks invoked when parsing succeeds - Short descriptions for contextual help Use @ref ec_node_attrs() to access the attribute dictionary and manipulate it with @ref ec_dict_set() and @ref ec_dict_get(). The @ref ecoli_interact API provides convenience functions such as @ref ec_interact_set_help() and @ref ec_interact_set_callback(). ## Node Configuration Nodes support a generic configuration system for setting parameters after creation. Each node type defines a schema describing its configuration options. The YAML parser uses this system to instantiate nodes from configuration files. See @ref ecoli_config for details. �����������������������������������������������������������������������������libecoli-0.11.6/doc/gen-man-pages.sh����������������������������������������������������������������0000775�0000000�0000000�00000003362�15203622040�0017102�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2026 Robin Jarry # # Generate man pages from doxygen XML using doxygen2man. # Usage: gen-man-pages.sh <xml-dir> <include-dir> <output-dir> <stamp-file> set -e xml_dir="$1" include_dir="$2" output_dir="$3" stamp="$4" if [ -z "$xml_dir" ] || [ -z "$include_dir" ] || \ [ -z "$output_dir" ] || [ -z "$stamp" ]; then echo "usage: $0 <xml-dir> <include-dir> <output-dir> <stamp-file>" >&2 exit 1 fi mkdir -p "$output_dir" # Extract group XML filenames from the doxygen index. groups=$(xmllint --xpath '//compound[@kind="group"]/@refid' \ "$xml_dir/index.xml" | sed 's/ *refid="\([^"]*\)"/\1.xml/g') if [ -z "$groups" ]; then echo "error: no groups found in $xml_dir/index.xml" >&2 exit 1 fi for xml in $groups; do doxygen2man -m -p libecoli -s 3 \ -H "Libecoli Programmer's Manual" \ -C "Olivier Matz, Robin Jarry" \ -S "2010" \ -d "$xml_dir" \ -o "$output_dir" \ "$xml" || [ "$?" -gt 128 ] done # Fix include paths in generated man pages. xpath='string(//memberdef[@kind="function"][name="%s"]/location/@file)' for manpage in "$output_dir"/*.3; do if ! [ -s "$manpage" ]; then rm -f "$manpage" continue fi func_name=$(basename -s .3 "$manpage") inc=$(xmllint --xpath "$(printf "$xpath" "$func_name")" \ "$xml_dir"/group__*.xml 2>/dev/null | xargs echo) if [ -n "$inc" ]; then sed -i "s|^\.B #include <.*>|.B #include <${inc}>|" "$manpage" fi done # Strip the systematic ", Inc. All rights reserved." suffix to all copyrights sed -Ei 's/, Inc\. All rights reserved//' "$output_dir"/*.3 # Strip empty DESCRIPTION sections sed -Ezi 's/\.SH DESCRIPTION\n\.(SH|RE)/.\1/' "$output_dir"/*.3 # Trim trailing whitespace sed -Ei 's/[[:space:]]+$//' "$output_dir"/*.3 touch "$stamp" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/gen-node-schema.c���������������������������������������������������������������0000664�0000000�0000000�00000002727�15203622040�0017226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> /* Map node type names to doxygen group names when they differ. */ static const char *type_to_group(const char *name) { if (strcmp(name, "uint") == 0) return "int"; return name; } static void dump_schema(FILE *f, const struct ec_node_type *type) { fprintf(f, "@addtogroup ecoli_node_%s\n", type_to_group(type->name)); fprintf(f, "@{\n\n"); fprintf(f, "<b>Configuration Schema</b>\n\n"); if (type->schema == NULL) { fprintf(f, "No configuration schema.\n"); } else { fprintf(f, "```c\n"); ec_config_schema_dump(f, type->schema, type->name); fprintf(f, "```\n"); } fprintf(f, "\n@}\n"); } int main(int argc, char **argv) { struct ec_node_type *type; const char *dir, *stamp; char fname[PATH_MAX]; FILE *f; if (argc != 3) errx(EXIT_FAILURE, "invalid arguments. usage: %s DIR STAMP_FILE", argv[0]); dir = argv[1]; stamp = argv[2]; TAILQ_FOREACH (type, &node_type_list, next) { snprintf(fname, sizeof(fname), "%s/node-%s-schema.md", dir, type->name); printf("generating %s ...\n", fname); f = fopen(fname, "w"); if (f == NULL) errx(EXIT_FAILURE, "failed to create file: %s", fname); dump_schema(f, type); fclose(f); } f = fopen(stamp, "w"); if (f == NULL) errx(EXIT_FAILURE, "failed to created stamp file: %s", stamp); fclose(f); return 0; } �����������������������������������������libecoli-0.11.6/doc/install-man-pages.sh������������������������������������������������������������0000775�0000000�0000000�00000000664�15203622040�0020001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2026 Robin Jarry # # Install generated man pages. Called by meson.add_install_script(). set -e man_dir="$1" mandir="$2" # mandir may be relative (e.g. "share/man"), resolve against install prefix. case "$mandir" in /*) dest="${DESTDIR}${mandir}/man3" ;; *) dest="${MESON_INSTALL_DESTDIR_PREFIX}/${mandir}/man3" ;; esac install -m 644 -Dvt "$dest" "$man_dir"/*.3 ����������������������������������������������������������������������������libecoli-0.11.6/doc/logo.svg������������������������������������������������������������������������0000664�0000000�0000000�00000006710�15203622040�0015605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="50.595764mm" height="9.8371639mm" viewBox="0 0 50.595764 9.837164" version="1.1" id="svg8" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="logo.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs2" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="3.959798" inkscape:cx="118.94546" inkscape:cy="-25.632621" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-31.293259,-93.101089)"> <rect style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect865" width="49.845764" height="9.0871639" x="31.668259" y="93.476089" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46667px;line-height:6.61458px;font-family:Cantarell;-inkscape-font-specification:'Cantarell Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="58.799297" y="92.806084" id="text836" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.46667px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="56.504822" y="100.59354" id="text840"><tspan sodipodi:role="line" id="tspan838" x="56.504822" y="100.59354" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Nimbus Mono L';-inkscape-font-specification:'Nimbus Mono L Bold';stroke-width:0.264583px">lib<tspan style="fill:#0000ff" id="tspan842">ecoli</tspan>></tspan></text> </g> </svg> ��������������������������������������������������������libecoli-0.11.6/doc/main.md�������������������������������������������������������������������������0000664�0000000�0000000�00000015336�15203622040�0015376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@mainpage About Libecoli Libecoli is an <b>E</b>xtensible <b>CO</b>mmand <b>LI</b>ne library written in C that provides a modular, composable framework for building interactive command line interfaces with dynamic completion, contextual help, and parsing capabilities. Project page: https://github.com/rjarry/libecoli ## Key Features - **Grammar-based parsing**: Define command syntax using composable grammar nodes that form a directed graph. - **Dynamic completion**: Automatic TAB completion based on the grammar, with support for runtime-generated suggestions. - **Contextual help**: Display relevant help messages based on current input position. - **Shell-like tokenization**: Built-in lexers handle quoting, escaping, and variable expansion. - **Interactive editing**: Integration with libedit for line editing, history, and key bindings. - **YAML configuration**: Define grammars in YAML files instead of C code. - **Expression evaluation**: Parse and evaluate arithmetic or custom expressions with user-defined operators. ## Core Concepts ### Grammar Graph A grammar is built by composing @ref ecoli_nodes into a directed graph. Each node type matches specific input patterns: - **Terminal nodes** match actual input tokens: @ref ec_node_str() matches a literal string, @ref ec_node_int() matches integers, @ref ec_node_re() matches regular expressions, the file node matches filesystem paths. - **Composite nodes** combine other nodes: @ref ec_node_seq() matches children in sequence, @ref ec_node_or() matches any one child, @ref ec_node_many() matches a child repeatedly, @ref ec_node_option() makes a child optional. - **Lexer nodes** tokenize raw input before passing to children: @ref ec_node_sh_lex() provides shell-like tokenization with quote handling. ### Parsing The @ref ecoli_parse API validates input against a grammar and produces a parse tree. Each node in the tree references the grammar node that matched it and the tokens it consumed. Use @ref ec_pnode_find() to locate specific nodes by their identifier and extract matched values. ### Completion The @ref ecoli_complete API generates completion suggestions for partial input. Completions are grouped by the grammar node that produced them and can be filtered by type (full match, partial match, or unknown). ### Interactive Mode The @ref ecoli_editline API provides a complete interactive command line with: - Line editing with libedit - TAB and `?` key completion - Command history with file persistence - Callback-based command execution - Contextual help display on errors ## Quick Example ```c #include <ecoli.h> static bool done; static int hello_cb(const struct ec_pnode *p) { const char *name = ec_strvec_val( ec_pnode_get_strvec(ec_pnode_find(p, "NAME")), 0); printf("Hello, %s!\n", name); return 0; } static int exit_cb(const struct ec_pnode *p) { (void)p; done = true; return 0; } static bool check_exit_cb(void *priv) { (void)priv; return done; } int main(void) { struct ec_node *cmd, *root; struct ec_editline *edit; ec_init(); // Build grammar: "hello <name>" or "exit" root = ec_node("or", EC_NO_ID); cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "hello"), ec_node_any("NAME", NULL)); ec_interact_set_callback(cmd, hello_cb); ec_interact_set_help(cmd, "Say hello to someone."); ec_node_or_add(root, cmd); cmd = ec_node_str(EC_NO_ID, "exit"); ec_interact_set_callback(cmd, exit_cb); ec_interact_set_help(cmd, "Exit the program."); ec_node_or_add(root, cmd); // Add shell lexer for quote/escape handling root = ec_node_sh_lex(EC_NO_ID, root); // Create interactive session edit = ec_editline("example", stdin, stdout, stderr, 0); ec_editline_set_prompt(edit, "> "); ec_editline_set_node(edit, root); ec_editline_interact(edit, check_exit_cb, NULL); ec_editline_free(edit); ec_node_free(root); ec_exit(); return 0; } ``` See the [Examples](examples.html) page for complete working examples demonstrating grammar construction, completion, readline integration, custom node types, and dynamic completion from runtime data. ## Available Node Types ### Terminal Nodes | Node Type | Description | |-----------|-------------| | @ref ecoli_node_str | Matches a specific literal string | | @ref ecoli_node_int | Matches signed integers with range/base constraints | | @ref ecoli_node_re | Matches input against a POSIX extended regex | | @ref ecoli_node_file | Matches and completes filesystem paths | | @ref ecoli_node_any | Matches any single token | | @ref ecoli_node_empty | Matches zero tokens (always succeeds) | | @ref ecoli_node_none | Matches nothing (always fails) | | @ref ecoli_node_space | Matches whitespace | ### Composite Nodes | Node Type | Description | |-----------|-------------| | @ref ecoli_node_seq | Matches children in strict order | | @ref ecoli_node_or | Matches any one of its children | | @ref ecoli_node_many | Matches a child repeatedly (with min/max) | | @ref ecoli_node_option | Makes a child optional | | @ref ecoli_node_subset | Matches any subset of children in any order | ### Advanced Nodes | Node Type | Description | |-----------|-------------| | @ref ecoli_node_cmd | Parses commands using a format string syntax | | @ref ecoli_node_expr | Parses expressions with operators and precedence | | @ref ecoli_node_dynamic | Builds grammar dynamically at parse time | | @ref ecoli_node_dynlist | Matches names from a runtime-generated list | | @ref ecoli_node_cond | Conditionally matches based on an expression | | @ref ecoli_node_once | Prevents a child from matching more than once | | @ref ecoli_node_bypass | Pass-through node for building graph loops | ### Lexer Nodes | Node Type | Description | |-----------|-------------| | @ref ecoli_node_sh_lex | Shell-like tokenization with quotes and escapes | | @ref ecoli_node_re_lex | Regex-based tokenization | ## API Reference - @ref architecture - How libecoli works internally - @ref ecoli_init - Library initialization - @ref ecoli_nodes - Grammar node types and operations - @ref ecoli_parse - Parsing API - @ref ecoli_complete - Completion API - @ref ecoli_editline - Interactive editing with libedit - @ref ecoli_interact - Command callbacks and help system - @ref ecoli_yaml - YAML grammar import/export - @ref ecoli_config - Node configuration system - @ref ecoli_log - Logging facilities ## Real-World Usage Libecoli powers the CLI of several production systems including the [grout](https://github.com/DPDK/grout) graph router and 6WIND's Virtual Service Router. These projects demonstrate patterns such as: - Modular command registration with constructor functions - Dynamic completion callbacks that query live system state - Hierarchical command contexts (e.g., `ip route add ...`) - Typed argument extraction helpers - JSON export of command trees for documentation generation ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/meson.build���������������������������������������������������������������������0000664�0000000�0000000�00000004625�15203622040�0016271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> doxygen = find_program('doxygen', required: get_option('doc')) doxygen2man = find_program('doxygen2man', required: get_option('doc')) xmllint = find_program('xmllint', required: get_option('doc')) if not doxygen.found() subdir_done() endif cdata = configuration_data() cdata.set('VERSION', meson.project_version()) cdata.set('OUTPUT', meson.current_build_dir()) cdata.set('TOPDIR', meson.project_source_root()) ecoli_gen_node_schema = executable( 'gen-node-schema', files('gen-node-schema.c'), include_directories : inc, link_with: libecoli, ) node_schemas = custom_target( 'node-schemas', output: 'node-schemas', command: [ecoli_gen_node_schema, meson.current_build_dir(), '@OUTPUT@'], ) if doxygen2man.found() and xmllint.found() xml_cdata = configuration_data() xml_cdata.merge_from(cdata) xml_cdata.set('GENERATE_HTML', 'NO') xml_cdata.set('GENERATE_XML', 'YES') xml_cdata.set('EXAMPLES_INPUT', '') doxygen_xml_conf = configure_file( input: 'Doxyfile.in', output: 'Doxyfile.xml', configuration: xml_cdata, install: false) doxygen_xml = custom_target( 'doxygen-xml', input: doxygen_xml_conf, output: 'xml', command: [doxygen, '@INPUT@'], build_by_default: true, depends: node_schemas, depend_files: doc_sources, install: false) custom_target( 'man-pages', output: 'man-pages.stamp', command: [ files('gen-man-pages.sh'), meson.current_build_dir() / 'xml', meson.project_source_root() / 'include', meson.current_build_dir() / 'man', '@OUTPUT@', ], depends: doxygen_xml, depend_files: doc_sources, build_by_default: true, install: false) meson.add_install_script( files('install-man-pages.sh'), meson.current_build_dir() / 'man', get_option('mandir')) endif html_cdata = configuration_data() html_cdata.merge_from(cdata) html_cdata.set('GENERATE_HTML', 'YES') html_cdata.set('GENERATE_XML', 'NO') html_cdata.set('EXAMPLES_INPUT', meson.project_source_root() / 'examples') doxygen_html_conf = configure_file( input: 'Doxyfile.in', output: 'Doxyfile.html', configuration: html_cdata, install: false) custom_target( 'doxygen-html', input: doxygen_html_conf, output: 'html', command: [doxygen, '@INPUT@'], build_by_default: true, depends: node_schemas, depend_files: doc_sources, install: true, install_dir: join_paths(get_option('datadir'), 'doc', 'libecoli')) �����������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/parse-tree.svg������������������������������������������������������������������0000664�0000000�0000000�00000031417�15203622040�0016716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="97.485992mm" height="109.30539mm" viewBox="0 0 97.48599 109.30539" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="parse-tree.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="197.85714" inkscape:cy="154.64286" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-76.002527,-121.82488)"> <rect style="opacity:1;fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="95.816948" height="107.7232" x="76.947472" y="122.9588" rx="5" ry="5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="124.74552" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6" cx="124.74552" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2" cx="124.74553" cy="211.3884" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458302px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017"><tspan sodipodi:role="line" id="tspan6015" x="63.877975" y="112.79115" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" /></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.64906" y="142.50148" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="124.64906" y="142.50148" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">or</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.62737" y="213.08734" id="text6021-3-0-6"><tspan sodipodi:role="line" x="124.62737" y="213.08734" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-3">str</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.91586" y="177.63344" id="text6021-3-0-0"><tspan sodipodi:role="line" x="124.91586" y="177.63344" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7">seq</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.74552,150.10694 0,17.21044" id="path6098" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-6" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.74552,185.22443 1e-5,17.21044" id="path6100" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2-6" cx="152.82364" cy="211.80174" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="152.67412" y="213.67795" id="text6021-3-5"><tspan sodipodi:role="line" id="tspan6019-6-24" x="152.67412" y="213.67795" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">int</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 130.57567,183.64854 16.41782,20.77557" id="path6173" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2-6" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.74638" y="146.47508" id="text6021-3-0-68"><tspan sodipodi:role="line" id="tspan6019-6-0-5" x="124.74638" y="146.47508" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar, 1]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.83473" y="181.62686" id="text6021-3-0-4"><tspan sodipodi:role="line" id="tspan6019-6-0-9" x="124.83473" y="181.62686" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar, 1]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.7464" y="216.92993" id="text6021-3-0-46"><tspan sodipodi:role="line" id="tspan6019-6-0-8" x="124.7464" y="216.92993" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;line-height:6.61458349px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="152.82449" y="217.45903" id="text6021-3-0-3"><tspan sodipodi:role="line" id="tspan6019-6-0-2" x="152.82449" y="217.45903" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52777767px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[1]</tspan></text> </g> </svg> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/parse-tree2.svg�����������������������������������������������������������������0000664�0000000�0000000�00000062031�15203622040�0016774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="151.7977mm" height="131.62782mm" viewBox="0 0 151.7977 131.62782" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="parse-tree2.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400"> <marker inkscape:isstock="true" style="overflow:visible" id="marker2644" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2642" inkscape:connector-curvature="0" /> </marker> <marker inkscape:isstock="true" style="overflow:visible" id="marker2616" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2614" inkscape:connector-curvature="0" /> </marker> <marker inkscape:isstock="true" style="overflow:visible" id="marker2594" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2592" inkscape:connector-curvature="0" /> </marker> <marker inkscape:stockid="Arrow1Lend" orient="auto" refY="0" refX="0" id="Arrow1Lend" style="overflow:visible" inkscape:isstock="true" inkscape:collect="always"> <path id="path2312" d="M 0,0 5,-5 -12.5,0 5,5 Z" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" transform="matrix(-0.8,0,0,-0.8,-10,0)" inkscape:connector-curvature="0" /> </marker> </defs> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.98994949" inkscape:cx="432.34529" inkscape:cy="281.32748" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-21.690821,-99.502441)"> <rect style="fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="149.53812" height="130.04019" x="23.089148" y="100.50591" rx="5" ry="5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="124.74552" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6" cx="124.74552" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2" cx="124.74553" cy="211.3884" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.64906" y="142.50148" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="124.64906" y="142.50148" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">or</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.62737" y="213.08734" id="text6021-3-0-6"><tspan sodipodi:role="line" x="124.62737" y="213.08734" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-3">str</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.91586" y="177.63344" id="text6021-3-0-0"><tspan sodipodi:role="line" x="124.91586" y="177.63344" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7">seq</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.74552,150.10694 0,17.21044" id="path6098" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-6" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.74552,185.22443 1e-5,17.21044" id="path6100" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2-6" cx="152.82364" cy="211.80174" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="152.67412" y="213.67795" id="text6021-3-5"><tspan sodipodi:role="line" id="tspan6019-6-24" x="152.67412" y="213.67795" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">int</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 130.57567,183.64854 16.41782,20.77557" id="path6173" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2-6" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.74638" y="146.47508" id="text6021-3-0-68"><tspan sodipodi:role="line" id="tspan6019-6-0-5" x="124.74638" y="146.47508" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar, 1]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.83473" y="181.62686" id="text6021-3-0-4"><tspan sodipodi:role="line" id="tspan6019-6-0-9" x="124.83473" y="181.62686" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar, 1]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.7464" y="216.92993" id="text6021-3-0-46"><tspan sodipodi:role="line" id="tspan6019-6-0-8" x="124.7464" y="216.92993" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[bar]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="152.82449" y="217.45903" id="text6021-3-0-3"><tspan sodipodi:role="line" id="tspan6019-6-0-2" x="152.82449" y="217.45903" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[1]</tspan></text> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2-0" cx="56.435303" cy="118.83097" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-21" cx="42.355698" cy="153.94846" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-6" cx="70.433815" cy="153.94846" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2-5" cx="70.433823" cy="189.06595" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="-23.040615" y="80.265793" id="text6017-0" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="56.338837" y="120.17905" id="text6021-3-1"><tspan sodipodi:role="line" id="tspan6019-6-22" x="56.338837" y="120.17905" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">or</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="42.31591" y="152.32455" id="text6021-3-0"><tspan sodipodi:role="line" id="tspan6019-6-2" x="42.31591" y="152.32455" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="42.31591" y="158.97026" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#de8787;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059">foo</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="70.29306" y="187.44205" id="text6021-3-0-6-6"><tspan sodipodi:role="line" id="tspan6019-6-2-9" x="70.29306" y="187.44205" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="70.29306" y="194.08775" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#de8787;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-3-9">bar</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="70.604156" y="155.31099" id="text6021-3-0-0-1"><tspan sodipodi:role="line" x="70.604156" y="155.31099" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7-0">seq</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 53.045965,127.28469 -7.300929,18.21005" id="path6096" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-0" inkscape:connection-end="#path5951-21" /> <path style="fill:none;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 59.807238,127.29 7.254642,18.19942" id="path6098-4" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-0" inkscape:connection-end="#path5951-6-6" /> <path style="fill:none;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 70.433817,162.90199 4e-6,17.21043" id="path6100-2" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-6" inkscape:connection-end="#path5951-6-2-5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2-6-7" cx="98.511932" cy="189.47929" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="98.362411" y="191.3555" id="text6021-3-5-4"><tspan sodipodi:role="line" id="tspan6019-6-24-0" x="98.362411" y="191.3555" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke:none;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">int</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 76.263966,161.32609 16.417815,20.77557" id="path6173-3" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-6" inkscape:connection-end="#path5951-6-2-6-7" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2644)" d="M 115.11271,138.0056 66.068116,121.97879" id="path2301" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-2-0" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)" d="M 115.4414,172.44685 79.737934,157.77251" id="path2303" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-6" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2616)" d="M 115.44141,207.56435 79.737942,192.89" id="path2305" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-2" inkscape:connection-end="#path5951-6-2-5" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2594)" d="M 143.51952,207.97769 107.81605,193.30334" id="path2307" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-2-6" inkscape:connection-end="#path5951-6-2-6-7" /> </g> </svg> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/parse-tree3.svg�����������������������������������������������������������������0000664�0000000�0000000�00000047115�15203622040�0017003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="107.22144mm" height="99.133545mm" viewBox="0 0 107.22143 99.133546" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="parse-tree3.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400"> <marker inkscape:isstock="true" style="overflow:visible" id="marker2947" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2945" inkscape:connector-curvature="0" /> </marker> <marker inkscape:isstock="true" style="overflow:visible" id="marker2919" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2917" inkscape:connector-curvature="0" /> </marker> <marker inkscape:isstock="true" style="overflow:visible" id="marker2897" refX="0" refY="0" orient="auto" inkscape:stockid="Arrow1Lend"> <path transform="matrix(-0.8,0,0,-0.8,-10,0)" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" d="M 0,0 5,-5 -12.5,0 5,5 Z" id="path2895" inkscape:connector-curvature="0" /> </marker> <marker inkscape:stockid="Arrow1Lend" orient="auto" refY="0" refX="0" id="Arrow1Lend" style="overflow:visible" inkscape:isstock="true" inkscape:collect="always"> <path id="path2312" d="M 0,0 5,-5 -12.5,0 5,5 Z" style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:1.00000003pt;stroke-opacity:1" transform="matrix(-0.8,0,0,-0.8,-10,0)" inkscape:connector-curvature="0" /> </marker> </defs> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="17.857143" inkscape:cy="116.07143" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-90.082132,-121.82488)"> <rect style="fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="105.45534" height="97.139854" x="91.125877" y="122.75523" rx="5" ry="5" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)" d="M 141.55564,196.27523 118.99373,181.62558" id="path2871" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-21" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2897)" d="m 167.22825,198.00821 -47.0708,-18.11562" id="path2873" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-4" inkscape:connection-end="#path5951-21" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="110.74701" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-21" cx="110.74701" cy="176.2709" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.65054" y="142.50148" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="110.65054" y="142.50148" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">many</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.70722" y="174.647" id="text6021-3-0"><tspan sodipodi:role="line" id="tspan6019-6-2" x="110.70722" y="174.647" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#b3b3b3;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="110.70722" y="181.29271" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#de8787;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059">foo</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 110.74701,150.10694 0,17.21044" id="path6096" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-21" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2-2" cx="149.80237" cy="166.51241" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="149.70592" y="165.74384" id="text6021-3-8"><tspan sodipodi:role="line" id="tspan6019-6-0" x="149.70592" y="165.74384" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">many</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="149.6842" y="202.99243" id="text6021-3-0-0"><tspan sodipodi:role="line" x="149.6842" y="202.99243" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7">str</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 149.80237,175.46593 v 17.21044" id="path6098" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-2" inkscape:connection-end="#path5951-6" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:3px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="149.80324" y="169.71744" id="text6021-3-0-68"><tspan sodipodi:role="line" id="tspan6019-6-0-5" x="149.80324" y="169.71744" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;line-height:3px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[foo, foo,</tspan><tspan sodipodi:role="line" x="149.80324" y="173.05287" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;line-height:3px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan2812">foo]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="149.80322" y="206.62503" id="text6021-3-0-4"><tspan sodipodi:role="line" id="tspan6019-6-0-9" x="149.80322" y="206.62503" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[foo]</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="122.84789" y="202.99243" id="text6021-3-0-0-7"><tspan sodipodi:role="line" x="122.84789" y="202.99243" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7-0">str</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="122.96692" y="206.62503" id="text6021-3-0-4-0"><tspan sodipodi:role="line" id="tspan6019-6-0-9-3" x="122.96692" y="206.62503" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[foo]</tspan></text> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6" cx="149.80237" cy="201.6299" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-3" cx="122.96606" cy="201.6299" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-4" cx="176.63869" cy="201.6299" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="176.52052" y="202.99243" id="text6021-3-0-0-9"><tspan sodipodi:role="line" x="176.52052" y="202.99243" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7-2">str</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="176.63954" y="206.62503" id="text6021-3-0-4-7"><tspan sodipodi:role="line" id="tspan6019-6-0-9-4" x="176.63954" y="206.62503" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#0000ff;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">[foo]</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 144.10487,173.96804 -15.44132,20.20622" id="path2863" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-2" inkscape:connection-end="#path5951-6-3" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 155.49986,173.96804 15.44133,20.20622" id="path2865" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-2" inkscape:connection-end="#path5951-6-4" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2919)" d="M 141.55564,161.15773 118.99373,146.50808" id="path2867" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2-2" inkscape:connection-end="#path5951-2" /> <path style="fill:#999999;fill-rule:evenodd;stroke:#999999;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker2947)" d="m 118.98747,193.37288 -4.26188,-8.84496" id="path2869" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6-3" inkscape:connection-end="#path5951-21" /> </g> </svg> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/doc/simple-tree.svg�����������������������������������������������������������������0000664�0000000�0000000�00000025701�15203622040�0017074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="69.488983mm" height="144.00954mm" viewBox="0 0 69.488983 144.00954" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="simple-tree.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="74.285714" inkscape:cy="285.35714" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-76.002527,-86.70739)"> <rect style="fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="67.84671" height="142.30801" x="76.932884" y="87.637749" rx="5" ry="5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951" cx="110.74702" cy="106.03592" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="110.74702" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-21" cx="96.667404" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6" cx="124.82664" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2" cx="124.82664" cy="211.3884" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.65538" y="107.32973" id="text6021"><tspan sodipodi:role="line" id="tspan6019" x="110.65538" y="107.32973" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">sh_lex</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.82901" y="142.02278" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="110.82901" y="142.02278" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">seq</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="96.627617" y="174.647" id="text6021-3-0"><tspan sodipodi:role="line" id="tspan6019-6-2" x="96.627617" y="174.647" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="96.627617" y="181.29271" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#800000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059">foo</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.60477" y="209.7645" id="text6021-3-0-6"><tspan sodipodi:role="line" id="tspan6019-6-2-9" x="124.60477" y="209.7645" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="124.60477" y="216.4102" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#800000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-3">bar</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.91586" y="177.63344" id="text6021-3-0-0"><tspan sodipodi:role="line" x="124.91586" y="177.63344" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7">option</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 110.74702,114.98945 v 17.21043" id="path6094" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951" inkscape:connection-end="#path5951-2" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 107.35768,149.60713 -7.30094,18.21006" id="path6096" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-21" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 114.13636,149.60713 7.30094,18.21006" id="path6098" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-6" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.82664,185.22443 0,17.21044" id="path6100" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2" /> </g> </svg> ���������������������������������������������������������������libecoli-0.11.6/doc/simple-tree2.svg����������������������������������������������������������������0000664�0000000�0000000�00000025732�15203622040�0017162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="97.485992mm" height="109.30539mm" viewBox="0 0 97.48599 109.30539" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="simple-tree2.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="197.85714" inkscape:cy="154.64286" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-76.002527,-121.82488)"> <rect style="fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="95.816948" height="107.7232" x="76.743896" y="122.67964" rx="5" ry="5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="110.74701" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-21" cx="96.667404" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6" cx="124.74552" cy="176.2709" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2" cx="124.74553" cy="211.3884" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.65054" y="142.50148" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="110.65054" y="142.50148" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">or</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="96.627617" y="174.647" id="text6021-3-0"><tspan sodipodi:role="line" id="tspan6019-6-2" x="96.627617" y="174.647" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="96.627617" y="181.29271" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#800000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059">foo</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.60477" y="209.7645" id="text6021-3-0-6"><tspan sodipodi:role="line" id="tspan6019-6-2-9" x="124.60477" y="209.7645" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="124.60477" y="216.4102" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#800000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-3">bar</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="124.91586" y="177.63344" id="text6021-3-0-0"><tspan sodipodi:role="line" x="124.91586" y="177.63344" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059-7">seq</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 107.35767,149.60713 -7.30093,18.21006" id="path6096" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-21" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 114.11894,149.61244 7.25465,18.19943" id="path6098" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-6" /> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 124.74552,185.22443 1e-5,17.21044" id="path6100" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-6-2-6" cx="152.82364" cy="211.80174" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="152.67412" y="213.67795" id="text6021-3-5"><tspan sodipodi:role="line" id="tspan6019-6-24" x="152.67412" y="213.67795" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">int</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 130.57567,183.64854 16.41782,20.77557" id="path6173" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-6" inkscape:connection-end="#path5951-6-2-6" /> </g> </svg> ��������������������������������������libecoli-0.11.6/doc/simple-tree3.svg����������������������������������������������������������������0000664�0000000�0000000�00000013373�15203622040�0017161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg width="41.329754mm" height="73.774551mm" viewBox="0 0 41.329753 73.774552" version="1.1" id="svg5406" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="simple-tree3.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs5400" /> <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="169.28571" inkscape:cy="198.21429" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="1920" inkscape:window-height="1008" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" /> <metadata id="metadata5403"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-90.082132,-121.82488)"> <rect style="fill:#ffffff;stroke:#808080;stroke-width:0.499999;stroke-dasharray:none" id="rect1" width="40.372559" height="72.760391" x="90.570381" y="122.37117" rx="5" ry="5" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-2" cx="110.74701" cy="141.15341" rx="10.289877" ry="8.9535294" /> <ellipse style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path5951-21" cx="110.74701" cy="176.2709" rx="10.289877" ry="8.9535294" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="63.877975" y="108.2009" id="text6017" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.65054" y="142.50148" id="text6021-3"><tspan sodipodi:role="line" id="tspan6019-6" x="110.65054" y="142.50148" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">many</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;line-height:6.61458px;font-family:MrsEavesPetiteCaps;-inkscape-font-specification:'MrsEavesPetiteCaps Bold';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" x="110.70722" y="174.647" id="text6021-3-0"><tspan sodipodi:role="line" id="tspan6019-6-2" x="110.70722" y="174.647" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none">str</tspan><tspan sodipodi:role="line" x="110.70722" y="181.29271" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#800000;stroke-width:0.75;stroke-miterlimit:4;stroke-dasharray:none" id="tspan6059">foo</tspan></text> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 110.74701,150.10694 0,17.21044" id="path6096" inkscape:connector-type="polyline" inkscape:connector-curvature="0" inkscape:connection-start="#path5951-2" inkscape:connection-end="#path5951-21" /> </g> </svg> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0015171�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/extension-editline/��������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0021000�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/extension-editline/README.md�����������������������������������������������0000664�0000000�0000000�00000000372�15203622040�0022261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This example shows how to extend libecoli by creating a custom grammar node. We introduce "node_bool_tuple", which aims to parse and complete a tuple of booleans, like this: - () - (false) - (true) - (false,true) - (false,true,false,false,true) ... ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/extension-editline/main.c��������������������������������������������������0000664�0000000�0000000�00000011014�15203622040�0022065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /** * @example extension-editline/main.c * Define custom node types with specialized parsing and completion. * * Demonstrates creating a custom grammar node type (bool_tuple) with its * own parsing and completion logic. The bool_tuple node parses and completes * tuples of booleans like "(true,false,true)" and converts them to an integer * where each boolean becomes a bit (e.g. "(true,false,true)" = 101 binary = 5). * * The custom node (node_bool_tuple.c) implements: * - Parsing: validates input matches the exact grammar (bool,bool,...) * - Completion: provides context-aware suggestions at each parsing state * (opening paren, true/false keywords, comma, closing paren) * * The main program registers two commands: * - "convert <bool_tuple>" - parses the tuple and prints its integer value * - "exit" - quits the program * * Example session: * @code * extension> convert (true,false,true) * Integer value for (true,false,true) is 5 * extension> convert (false,true) * Integer value for (false,true) is 1 * extension> exit * Exit ! * @endcode * * See also node_bool_tuple.c for the node type implementation. */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> #define ID_BOOL_TUPLE "id_bool_tuple" /* stop program when true */ static bool done; static int convert_cb(const struct ec_pnode *parse) { const char *bool_tuple; const char *next_false; const char *next_true; unsigned int val = 0; const char *t; bool_tuple = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_BOOL_TUPLE)), 0); t = bool_tuple; /* convert bool tuple into an integer */ while (1) { next_true = strstr(t, "true"); next_false = strstr(t, "false"); if (next_true && (next_false == NULL || next_false > next_true)) { val = (val << 1) | 1; t = next_true + 1; } else if (next_false && (next_true == NULL || next_true > next_false)) { val = val << 1; t = next_false + 1; } else { break; } } printf("Integer value for %s is %u\n", bool_tuple, val); return 0; } static int exit_cb(const struct ec_pnode *parse) { (void)parse; printf("Exit !\n"); done = true; return 0; } static int check_exit(void *opaque) { (void)opaque; return done; } static struct ec_node *create_commands(void) { struct ec_node *cmdlist = NULL, *cmd = NULL; int ret; /* the top node containing the list of commands */ cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; /* the convert command */ cmd = EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "convert"), ec_node("bool_tuple", ID_BOOL_TUPLE) ); if (cmd == NULL) goto fail; if (ec_interact_set_callback(cmd, convert_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "Convert a tuple of boolean into its integer representation") < 0) goto fail; if (ec_interact_set_help( ec_node_find(cmd, ID_BOOL_TUPLE), "A tuple of booleans. Example: \"(true,false,true)\"" ) < 0) goto fail; ret = ec_node_or_add(cmdlist, cmd); cmd = NULL; /* already freed, even on error */ if (ret < 0) goto fail; /* the exit command */ cmd = ec_node_str(EC_NO_ID, "exit"); if (ec_interact_set_callback(cmd, exit_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "exit program") < 0) goto fail; ret = ec_node_or_add(cmdlist, cmd); cmd = NULL; /* already freed, even on error */ if (ret < 0) goto fail; /* the lexer, added above the command list */ cmdlist = ec_node_sh_lex(EC_NO_ID, cmdlist); if (cmdlist == NULL) goto fail; return cmdlist; fail: fprintf(stderr, "cannot initialize nodes\n"); ec_node_free(cmdlist); ec_node_free(cmd); return NULL; } int main(void) { struct ec_editline *editline = NULL; struct ec_node *node = NULL; if (ec_init() < 0) { fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); return 1; } node = create_commands(); if (node == NULL) { fprintf(stderr, "failed to create commands: %s\n", strerror(errno)); goto fail; } editline = ec_editline("extension-editline", stdin, stdout, stderr, 0); if (editline == NULL) { fprintf(stderr, "Failed to initialize editline\n"); goto fail; } if (ec_editline_set_prompt(editline, "extension> ") < 0) { fprintf(stderr, "Failed to set prompt\n"); goto fail; } ec_editline_set_node(editline, node); if (ec_editline_interact(editline, check_exit, NULL) < 0) goto fail; ec_editline_free(editline); ec_node_free(node); return 0; fail: ec_editline_free(editline); ec_node_free(node); return 1; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/extension-editline/meson.build���������������������������������������������0000664�0000000�0000000�00000001005�15203622040�0023136�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2025, Olivier MATZ <zer0@droids-corp.org> if not get_option('examples').require(edit_dep.found(), error_message: 'examples require libedit').allowed() subdir_done() endif extension_editline_sources = files( 'main.c', 'node_bool_tuple.c', ) doc_sources += extension_editline_sources ecoli_extension_editline = executable( 'ecoli-extension-editline', extension_editline_sources, include_directories : inc, link_with : libecoli, dependencies: [edit_dep]) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/extension-editline/node_bool_tuple.c���������������������������������������0000664�0000000�0000000�00000012073�15203622040�0024320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> EC_LOG_TYPE_REGISTER(node_bool_tuple); enum bool_tuple_state { BT_EMPTY, BT_OPEN, BT_BOOL, BT_COMMA, BT_END, BT_FAIL, }; /* Check if input matches a bool tuple like this: "(true,false,true)". On success, *state is set to * BT_END. If parsing is incomplete or fails, set the *state to something else, and return the * pointer to the incomplete or failing token. */ static const char *parse_bool_tuple(const char *input, enum bool_tuple_state *state) { *state = BT_EMPTY; if (*input == '\0') return input; if (*input != '(') { *state = BT_FAIL; return input; } input++; *state = BT_OPEN; if (*input == ')') { *state = BT_END; input++; if (*input != '\0') { *state = BT_FAIL; return input; } return input; } while (1) { if (ec_str_startswith(input, "true")) { input += 4; } else if (ec_str_startswith(input, "false")) { input += 5; } else { if (!ec_str_startswith("true", input) && !ec_str_startswith("false", input)) *state = BT_FAIL; return input; } *state = BT_BOOL; if (*input == '\0') return input; if (*input == ')') { *state = BT_END; input++; if (*input != '\0') { *state = BT_FAIL; return input; } return input; } if (*input != ',') { *state = BT_FAIL; return input; } input++; *state = BT_COMMA; } } static int ec_node_bool_tuple_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { enum bool_tuple_state state; const char *input; (void)node; (void)pstate; if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; input = ec_strvec_val(strvec, 0); parse_bool_tuple(input, &state); if (state != BT_END) return EC_PARSE_NOMATCH; return 1; } static int ec_node_bool_tuple_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_comp_item *item = NULL; const char *false_str = "false"; const char *true_str = "true"; enum bool_tuple_state state; char *comp_str = NULL; char *disp_str = NULL; const char *incomplete; const char *input; int ret; if (ec_strvec_len(strvec) != 1) return 0; input = ec_strvec_val(strvec, 0); incomplete = parse_bool_tuple(input, &state); switch (state) { case BT_EMPTY: if (asprintf(&comp_str, "%s(", input) < 0) goto fail; if (asprintf(&disp_str, "(") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_PARTIAL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; break; case BT_OPEN: if (incomplete[0] == '\0') { if (asprintf(&comp_str, "%s)", input) < 0) goto fail; if (asprintf(&disp_str, ")") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_FULL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; free(comp_str); comp_str = NULL; free(disp_str); disp_str = NULL; } /* fallthrough */ case BT_COMMA: if (incomplete[0] == 't' || incomplete[0] == '\0') { if (asprintf(&comp_str, "%s%s", input, &true_str[strlen(incomplete)]) < 0) goto fail; if (asprintf(&disp_str, "true") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_PARTIAL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; free(comp_str); comp_str = NULL; free(disp_str); disp_str = NULL; } if (incomplete[0] == 'f' || incomplete[0] == '\0') { if (asprintf(&comp_str, "%s%s", input, &false_str[strlen(incomplete)]) < 0) goto fail; if (asprintf(&disp_str, "false") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_PARTIAL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; free(comp_str); comp_str = NULL; free(disp_str); disp_str = NULL; } break; case BT_BOOL: if (asprintf(&comp_str, "%s,", input) < 0) goto fail; if (asprintf(&disp_str, ",") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_PARTIAL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; if (asprintf(&comp_str, "%s)", input) < 0) goto fail; if (asprintf(&disp_str, ")") < 0) goto fail; item = ec_comp_add_item(comp, node, EC_COMP_PARTIAL, input, comp_str); if (item == NULL) goto fail; if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; break; case BT_END: item = ec_comp_add_item(comp, node, EC_COMP_FULL, input, input); if (item == NULL) goto fail; break; default: break; } ret = 0; out: free(comp_str); free(disp_str); return ret; fail: ret = -1; goto out; } static struct ec_node_type ec_node_bool_tuple_type = { .name = "bool_tuple", .parse = ec_node_bool_tuple_parse, .complete = ec_node_bool_tuple_complete, }; EC_NODE_TYPE_REGISTER(ec_node_bool_tuple_type); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/meson.build����������������������������������������������������������������0000664�0000000�0000000�00000000326�15203622040�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> subdir('simple-editline') subdir('pool-editline') subdir('extension-editline') subdir('readline') subdir('parse-yaml') ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/����������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0017243�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/cond.yaml�������������������������������������������������������0000664�0000000�0000000�00000001354�15203622040�0021055�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> --- type: re_lex patterns: - pattern: "[_a-zA-Z][._a-zA-Z0-9]*" keep: true attr: a_identifier - pattern: "\\(" keep: true attr: a_open - pattern: "\\)" keep: true attr: a_close - pattern: "," keep: true - pattern: "[ \t]+" keep: false child: &term id: term type: seq children: - type: any id: function_name attr: a_identifier - type: any attr: a_open - type: option id: term_list child: type: seq children: - *term - type: many child: type: seq children: - type: str string: "," - *term - type: any attr: a_close ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/cross.yaml������������������������������������������������������0000664�0000000�0000000�00000000473�15203622040�0021264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> --- # matches "x", "x + x", "x + x + x", ... &term id: term type: or children: - type: seq children: - &value id: value type: str string: x - id: operator type: str string: + - *term - *value �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/ecoli-parse-yaml.sh���������������������������������������������0000664�0000000�0000000�00000004306�15203622040�0022745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> set -e parse_yaml=ecoli-parse-yaml libecoli=/usr/share/libecoli/ecoli.sh usage() { cat <<- END_OF_HELP This example demonstrates how to build an interactive command line in a shell script, using a description in yaml format. Usage: $0 [options...] [tests...] -h show this help -p <path> path to ecoli-parse-yaml binary (default is to search in PATH). -l <path> path to libecoli.sh library (default is $libecoli) END_OF_HELP } ARG=0 while getopts "hp:l:" opt; do case "$opt" in "h") usage exit 0 ;; "p") parse_yaml="$OPTARG" ;; "l") libecoli="$OPTARG" ;; esac done if ! command -v $parse_yaml > /dev/null 2> /dev/null && \ ! readlink -e $parse_yaml > /dev/null 2> /dev/null; then echo "Cannot find ecoli-parse-yaml ($parse_yaml)" exit 1 fi if ! readlink -e $libecoli > /dev/null 2> /dev/null; then echo "Cannot find libecoli.sh ($libecoli)" exit 1 fi . $libecoli yaml=$(mktemp) cat << EOF > $yaml type: or children: - type: seq id: hello help: Say hello to someone children: - type: str string: hello - type: or id: name help: Name of the person to greet children: - type: str string: john - type: str string: mike - type: seq id: goodbye help: Say good bye to someone children: - type: str string: good - type: str string: bye - type: or id: name help: Name of the person to greet children: - type: str string: mary - type: str string: jessica EOF output=$(mktemp) match=1 $parse_yaml -i $yaml -o $output || match=0 if [ "$match" = "1" ]; then cat $output . $output name=$(ec_pnode_get_str $(ec_pnode_find_first ec_node1 name) 0) hello=$(ec_pnode_get_str $(ec_pnode_find_first ec_node1 hello) 0) if [ "$hello" != "" ]; then echo "$name says hello to you!" else echo "$name says good bye to you!" fi else echo "no match" fi rm $output rm $yaml ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/meson.build�����������������������������������������������������0000664�0000000�0000000�00000001104�15203622040�0021401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> if not get_option('examples').require(yaml_dep.found() and edit_dep.found(), error_message: 'examples require libyaml and libedit').allowed() subdir_done() endif parse_yaml_sources = files( 'parse-yaml.c', ) doc_sources += parse_yaml_sources ecoli_parse_yaml = executable( 'ecoli-parse-yaml', parse_yaml_sources, include_directories : inc, link_with : libecoli, dependencies: [yaml_dep, edit_dep]) install_data( 'ecoli-parse-yaml.sh', install_dir : get_option('bindir')) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/parse-yaml.c����������������������������������������������������0000664�0000000�0000000�00000014552�15203622040�0021470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @example parse-yaml/parse-yaml.c * Load grammar from YAML and parse or complete input. * * Demonstrates loading a grammar definition from a YAML file using * ec_yaml_import() and using it to parse or complete command line input. * * Example session using test.yaml grammar: * @code * $ ./parse-yaml -i test.yaml -o /dev/stdout * parse-yaml> hello john * ec_node1_id='sh_lex' * ec_node1_type='sh_lex' * ec_node1_strvec_len=1 * ec_node1_str0='hello john' * ec_node1_first_child='ec_node2' * ec_node2_id='hello' * ... * $ ./parse-yaml -i test.yaml -o /dev/null -c hello "" * john * mike * @endcode */ #include <errno.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> static char *input_file; static char *output_file; static bool complete; /* clang-format off */ static const char short_options[] = "h" /* help */ "i:" /* input-file */ "o:" /* output-file */ "c" /* complete */ ; /* clang-format on */ #define OPT_HELP "help" #define OPT_INPUT_FILE "input-file" #define OPT_OUTPUT_FILE "output-file" #define OPT_COMPLETE "complete" static const struct option long_options[] = { {OPT_HELP, 0, NULL, 'h'}, {OPT_INPUT_FILE, 1, NULL, 'i'}, {OPT_OUTPUT_FILE, 1, NULL, 'o'}, {OPT_COMPLETE, 0, NULL, 'c'}, {NULL, 0, NULL, 0} }; static void usage(const char *prgname) { fprintf(stderr, "%s -o <file.sh> -i <file.yaml>\n" " -h\n" " --" OPT_HELP "\n" " Show this help.\n" " -i <input-file>\n" " --" OPT_INPUT_FILE "=<file>\n" " Set the yaml input file describing the grammar.\n" " -o <output-file>\n" " --" OPT_OUTPUT_FILE "=<file>\n" " Set the output file.\n" " -c\n" " --" OPT_COMPLETE "\n" " Output the completion list.\n", prgname); } static int parse_args(int argc, char **argv) { int ret, opt; while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF) { switch (opt) { case 'h': /* help */ usage(argv[0]); exit(0); case 'i': /* input-file */ input_file = strdup(optarg); break; case 'o': /* output-file */ output_file = strdup(optarg); break; case 'c': /* complete */ complete = 1; break; default: usage(argv[0]); return -1; } } if (input_file == NULL) { fprintf(stderr, "No input file\n"); usage(argv[0]); return -1; } if (output_file == NULL) { fprintf(stderr, "No output file\n"); usage(argv[0]); return -1; } ret = optind - 1; optind = 1; return ret; } static int __dump_as_shell(FILE *f, const struct ec_pnode *parse, size_t *seq) { const struct ec_node *node = ec_pnode_get_node(parse); struct ec_pnode *child; size_t cur_seq, i, len; char *quoted; (*seq)++; cur_seq = *seq; quoted = ec_str_quote(ec_node_id(node), '\'', true); fprintf(f, "ec_node%zu_id=%s\n", cur_seq, quoted); free(quoted); quoted = ec_str_quote(ec_node_type_name(ec_node_type(node)), '\'', true); fprintf(f, "ec_node%zu_type=%s\n", cur_seq, quoted); free(quoted); len = ec_strvec_len(ec_pnode_get_strvec(parse)); fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len); for (i = 0; i < len; i++) { quoted = ec_str_quote(ec_strvec_val(ec_pnode_get_strvec(parse), i), '\'', true); fprintf(f, "ec_node%zu_str%zu=%s\n", cur_seq, i, quoted); free(quoted); } if (ec_pnode_get_first_child(parse) != NULL) { fprintf(f, "ec_node%zu_first_child='ec_node%zu'\n", cur_seq, cur_seq + 1); } EC_PNODE_FOREACH_CHILD (child, parse) { fprintf(f, "ec_node%zu_parent='ec_node%zu'\n", *seq + 1, cur_seq); __dump_as_shell(f, child, seq); } if (ec_pnode_next(parse) != NULL) { fprintf(f, "ec_node%zu_next='ec_node%zu'\n", cur_seq, *seq + 1); } return 0; } static int dump_as_shell(const struct ec_pnode *parse) { FILE *f; size_t seq = 0; int ret; f = fopen(output_file, "w"); if (f == NULL) return -1; ret = __dump_as_shell(f, parse, &seq); fclose(f); return ret; } static int interact(struct ec_node *node) { struct ec_editline *editline = NULL; struct ec_pnode *parse = NULL; struct ec_node *shlex = NULL; shlex = ec_node_sh_lex(EC_NO_ID, ec_node_clone(node)); if (shlex == NULL) { fprintf(stderr, "Failed to add lexer node\n"); goto fail; } editline = ec_editline("parse-yaml", stdin, stdout, stderr, 0); if (editline == NULL) { fprintf(stderr, "Failed to initialize editline\n"); goto fail; } if (ec_editline_set_node(editline, shlex) < 0) { fprintf(stderr, "Failed to set editline ec_node\n"); goto fail; } parse = ec_editline_parse(editline); if (parse == NULL) goto fail; if (!ec_pnode_matches(parse)) goto fail; if (dump_as_shell(parse) < 0) { fprintf(stderr, "Failed to dump the parsed result\n"); goto fail; } ec_pnode_free(parse); ec_editline_free(editline); ec_node_free(shlex); return 0; fail: ec_pnode_free(parse); ec_editline_free(editline); ec_node_free(shlex); return -1; } static int complete_words(const struct ec_node *node, int argc, char *argv[]) { struct ec_comp *comp = NULL; struct ec_strvec *strvec = NULL; struct ec_comp_item *item = NULL; size_t count; if (argc <= 1) goto fail; strvec = ec_strvec_from_array((const char *const *)&argv[1], argc - 1); if (strvec == NULL) goto fail; comp = ec_complete_strvec(node, strvec); if (comp == NULL) goto fail; count = ec_comp_count(comp, EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); EC_COMP_FOREACH (item, comp, EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL) { /* only one match, display it fully */ if (count == 1) { printf("%s\n", ec_comp_item_get_str(item)); break; } /* else show the 'display' part only */ printf("%s\n", ec_comp_item_get_display(item)); } ec_comp_free(comp); ec_strvec_free(strvec); return 0; fail: ec_comp_free(comp); ec_strvec_free(strvec); return -1; } int main(int argc, char *argv[]) { struct ec_node *node = NULL; int ret; ret = parse_args(argc, argv); if (ret < 0) goto fail; argc -= ret; argv += ret; if (ec_init() < 0) { fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); return 1; } node = ec_yaml_import(input_file); if (node == NULL) { fprintf(stderr, "Failed to parse file\n"); goto fail; } if (complete) { if (complete_words(node, argc, argv) < 0) goto fail; } else { if (interact(node) < 0) goto fail; } ec_node_free(node); return 0; fail: ec_node_free(node); return 1; } ������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/test-completion.sh����������������������������������������������0000664�0000000�0000000�00000000704�15203622040�0022726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> # complete -F _parser_options dummy_command _parser_options() { local curr_arg local i curr_arg=${COMP_WORDS[COMP_CWORD]} # remove the command name, only keep args words=( "${COMP_WORDS[@]}" ) unset words[0] IFS=$'\n' read -d '' -r -a COMPREPLY <<- EOF $(./build/parse-yaml --complete -i examples/yaml/test.yaml -o pipo -- "${words[@]}") EOF } ������������������������������������������������������������libecoli-0.11.6/examples/parse-yaml/test.yaml�������������������������������������������������������0000664�0000000�0000000�00000001256�15203622040�0021112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> --- type: or attrs: key1: val1 key2: val2 key3: children: - type: seq id: hello help: Say hello to someone children: - type: str string: hello - type: or id: name help: Name of the person to greet children: - type: str string: john - type: str string: mike - type: seq id: goodbye help: Say good bye to someone children: - type: str string: good - type: str string: bye - type: or id: name help: Name of the person to greet children: - type: str string: mary - type: str string: jessica ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/�������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0017735�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/README.md����������������������������������������������������0000664�0000000�0000000�00000000311�15203622040�0021207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This example shows a simple usage of ec_node_dynlist, which can be used to dynamically build a list of strings depending on a context. In this case, the cli is used to populate IP pools interactively. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/ip_pool.c����������������������������������������������������0000664�0000000�0000000�00000005052�15203622040�0021544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdint.h> #include <stdlib.h> #include "ip_pool.h" #include <ecoli.h> struct ip_pool { struct ec_dict *addrs; }; static struct ec_dict *pools = NULL; int ip_pool_init(void) { pools = ec_dict(); if (pools == NULL) return -1; return 0; } void ip_pool_exit(void) { ec_dict_free(pools); } static void __ip_pool_free(void *_pool) { struct ip_pool *pool = _pool; if (pool == NULL) return; ec_dict_free(pool->addrs); free(pool); } /* create an ip pool */ struct ip_pool *ip_pool(const char *name) { struct ip_pool *pool = NULL; if (ec_dict_has_key(pools, name)) { errno = EEXIST; goto fail; } pool = calloc(1, sizeof(*pool)); if (pool == NULL) goto fail; pool->addrs = ec_dict(); if (pool->addrs == NULL) goto fail; if (ec_dict_set(pools, name, pool, __ip_pool_free) < 0) goto fail; return pool; fail: __ip_pool_free(pool); return NULL; } /* lookup for an ip pool */ struct ip_pool *ip_pool_lookup(const char *name) { return ec_dict_get(pools, name); } /* list ip pool names */ struct ec_strvec *ip_pool_list(void) { struct ec_dict_elt_ref *iter = NULL; struct ec_strvec *names = NULL; const char *key; names = ec_strvec(); if (names == NULL) goto fail; for (iter = ec_dict_iter(pools); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); if (ec_strvec_add(names, key) < 0) goto fail; } return names; fail: ec_strvec_free(names); return NULL; } /* destroy the ip pool */ void ip_pool_free(const char *name) { struct ip_pool *pool; pool = ec_dict_get(pools, name); if (pool == NULL) return; ec_dict_del(pools, name); } /* add an IP */ int ip_pool_addr_add(struct ip_pool *pool, const char *addr) { if (ec_dict_has_key(pool->addrs, addr)) { errno = EEXIST; return -1; } if (ec_dict_set(pool->addrs, addr, NULL, NULL) < 0) return -1; return 0; } /* del an IP */ int ip_pool_addr_del(struct ip_pool *pool, const char *addr) { return ec_dict_del(pool->addrs, addr); } /* list pool IP addresses */ struct ec_strvec *ip_pool_addr_list(const struct ip_pool *pool) { struct ec_dict_elt_ref *iter = NULL; struct ec_strvec *addrs = NULL; const char *key; addrs = ec_strvec(); if (addrs == NULL) goto fail; for (iter = ec_dict_iter(pool->addrs); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); if (ec_strvec_add(addrs, key) < 0) goto fail; } return addrs; fail: ec_strvec_free(addrs); return NULL; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/ip_pool.h����������������������������������������������������0000664�0000000�0000000�00000001515�15203622040�0021551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /* A very simple IP pool. */ #include <stdint.h> struct ec_strvec; struct ip_pool; struct ip_address; /* create htable of ip pools */ int ip_pool_init(void); /* free ip pools */ void ip_pool_exit(void); /* create an ip pool */ struct ip_pool *ip_pool(const char *name); /* lookup for an ip pool, by name */ struct ip_pool *ip_pool_lookup(const char *name); /* list ip pool names */ struct ec_strvec *ip_pool_list(void); /* destroy the ip pool */ void ip_pool_free(const char *name); /* add an IP */ int ip_pool_addr_add(struct ip_pool *pool, const char *addr); /* del an IP */ int ip_pool_addr_del(struct ip_pool *pool, const char *addr); /* list pool IP addresses */ struct ec_strvec *ip_pool_addr_list(const struct ip_pool *pool); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/main.c�������������������������������������������������������0000664�0000000�0000000�00000022637�15203622040�0021037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /** * @example pool-editline/main.c * Dynamic completion from runtime data structures. * * Demonstrates using ec_node_dynlist() to generate completions from * runtime data. The example manages IP address pools and provides * completion for pool names and addresses. * * Example session: * @code * pool> pool list * No pool * pool> pool add servers * pool> pool add clients * pool> pool list * servers * clients * pool> addr pool servers add 192.168.1.1 * pool> addr pool servers add 192.168.1.2 * pool> addr pool servers list * 192.168.1.1 * 192.168.1.2 * pool> addr pool servers del 192.168.1.1 * pool> pool del clients * pool> exit * Exit ! * @endcode */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> #include "ip_pool.h" #define POOL_REGEXP "[A-Za-z][-_a-zA-Z0-9]+" #define IP_REGEXP \ "((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-" \ "9]|[1-9][0-9]|[0-9])" #define ID_POOL_NAME "id_pool_name" #define ID_ADDR "id_addr" /* stop program when true */ static bool done; static struct ec_node *with_help(struct ec_node *node, const char *help) { if (node == NULL) return NULL; if (ec_interact_set_help(node, help) < 0) { ec_node_free(node); return NULL; } return node; } static struct ec_node *with_cb(struct ec_node *node, ec_interact_command_cb_t cb) { if (node == NULL) return NULL; if (ec_interact_set_callback(node, cb) < 0) { ec_node_free(node); return NULL; } return node; } static struct ec_node *with_desc(struct ec_node *node, const char *desc) { if (node == NULL) return NULL; if (ec_interact_set_desc(node, desc) < 0) { ec_node_free(node); return NULL; } return node; } static int pool_list_cb(const struct ec_pnode *parse) { struct ec_strvec *names = NULL; size_t len; size_t i; (void)parse; names = ip_pool_list(); if (names == NULL) { fprintf(stderr, "Failed to list pools\n"); return -1; } len = ec_strvec_len(names); if (len == 0) { printf("No pool\n"); } else { for (i = 0; i < len; i++) printf("%s\n", ec_strvec_val(names, i)); } ec_strvec_free(names); return 0; } static int pool_add_cb(const struct ec_pnode *parse) { const char *pool_name; pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0); if (ip_pool(pool_name) == NULL) { fprintf(stderr, "Failed to add pool\n"); return -1; } return 0; } static int pool_del_cb(const struct ec_pnode *parse) { const char *pool_name; pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0); ip_pool_free(pool_name); return 0; } static int addr_list_cb(const struct ec_pnode *parse) { struct ec_strvec *addrs = NULL; const struct ip_pool *pool; const char *pool_name; size_t len; size_t i; pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0); pool = ip_pool_lookup(pool_name); addrs = ip_pool_addr_list(pool); if (addrs == NULL) { fprintf(stderr, "Failed to list pool addresses\n"); return -1; } len = ec_strvec_len(addrs); if (len == 0) { printf("No address\n"); } else { for (i = 0; i < len; i++) printf("%s\n", ec_strvec_val(addrs, i)); } ec_strvec_free(addrs); return 0; } static int addr_add_cb(const struct ec_pnode *parse) { const char *pool_name; struct ip_pool *pool; const char *addr; pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0); pool = ip_pool_lookup(pool_name); addr = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_ADDR)), 0); if (ip_pool_addr_add(pool, addr) < 0) { fprintf(stderr, "Failed to add address to pool\n"); return -1; } return 0; } static int addr_del_cb(const struct ec_pnode *parse) { const char *pool_name; struct ip_pool *pool; const char *addr; pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0); pool = ip_pool_lookup(pool_name); addr = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_ADDR)), 0); if (ip_pool_addr_del(pool, addr) < 0) { fprintf(stderr, "Failed to delete address from pool\n"); return -1; } return 0; } static int exit_cb(const struct ec_pnode *parse) { (void)parse; printf("Exit !\n"); done = true; return 0; } static int check_exit(void *opaque) { (void)opaque; return done; } static struct ec_strvec *list_pools(struct ec_pnode *pstate, void *opaque) { (void)pstate; (void)opaque; return ip_pool_list(); } static struct ec_strvec *list_addrs(struct ec_pnode *pstate, void *opaque) { const char *pool_name; struct ip_pool *pool; (void)opaque; pool_name = ec_strvec_val( ec_pnode_get_strvec(ec_pnode_find(ec_pnode_get_root(pstate), ID_POOL_NAME)), 0 ); if (pool_name == NULL) return ec_strvec(); pool = ip_pool_lookup(pool_name); if (pool == NULL) return ec_strvec(); return ip_pool_addr_list(pool); } static struct ec_node *create_pool_commands(void) { struct ec_node *cmdlist = NULL; /* the list of pool subcommands */ cmdlist = EC_NODE_OR( EC_NO_ID, with_cb(with_help(ec_node_str(EC_NO_ID, "list"), "Display the list of IP pools"), pool_list_cb), with_cb(EC_NODE_SEQ( EC_NO_ID, with_help(ec_node_str(EC_NO_ID, "add"), "Create an IP pool"), with_help( with_desc( ec_node_dynlist( ID_POOL_NAME, list_pools, NULL, POOL_REGEXP, DYNLIST_MATCH_REGEXP | DYNLIST_EXCLUDE_LIST ), "<pool-name>" ), "The name of the pool to create" ) ), pool_add_cb), with_cb(EC_NODE_SEQ( EC_NO_ID, with_help(ec_node_str(EC_NO_ID, "del"), "Delete an IP pool"), with_help( with_desc( ec_node_dynlist( ID_POOL_NAME, list_pools, NULL, POOL_REGEXP, DYNLIST_MATCH_LIST ), "<pool-name>" ), "The name of the pool to delete" ) ), pool_del_cb) ); /* the pool command */ return EC_NODE_SEQ( EC_NO_ID, with_help(ec_node_str(EC_NO_ID, "pool"), "Add, delete, or list pools"), cmdlist ); } static struct ec_node *create_addr_commands(void) { struct ec_node *cmdlist = NULL; /* the list of addr subcommands */ cmdlist = EC_NODE_OR( EC_NO_ID, with_cb(with_help( EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "list")), "Display the list of IP addresses in a pool" ), addr_list_cb), EC_NODE_SEQ( EC_NO_ID, with_cb(with_help( ec_node_str(EC_NO_ID, "add"), "Add an IP address into a pool" ), addr_add_cb), with_help( with_desc( ec_node_dynlist( ID_ADDR, list_addrs, NULL, IP_REGEXP, DYNLIST_MATCH_REGEXP | DYNLIST_EXCLUDE_LIST ), "<a.b.c.d>" ), "The IP to add" ) ), EC_NODE_SEQ( EC_NO_ID, with_cb(with_help( ec_node_str(EC_NO_ID, "del"), "Delete an IP address from a pool" ), addr_del_cb), with_help( with_desc( ec_node_dynlist( ID_ADDR, list_addrs, NULL, IP_REGEXP, DYNLIST_MATCH_LIST ), "<a.b.c.d>" ), "The existing IP to delete" ) ) ); return EC_NODE_SEQ( EC_NO_ID, with_help(ec_node_str(EC_NO_ID, "addr"), "Add, delete, list addresses in pool"), with_help(ec_node_str(EC_NO_ID, "pool"), "Specify the pool for this operation"), with_help( with_desc( ec_node_dynlist( ID_POOL_NAME, list_pools, NULL, POOL_REGEXP, DYNLIST_MATCH_LIST ), "<pool-name>" ), "The name of the pool (must exist)" ), cmdlist ); } static struct ec_node *create_commands(void) { struct ec_node *cmdlist = NULL; struct ec_node *cmd = NULL; /* the top node containing the list of commands */ cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; /* the exit command */ cmd = ec_node_str(EC_NO_ID, "exit"); if (ec_interact_set_callback(cmd, exit_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "Exit program") < 0) goto fail; if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; /* the pool commands */ if (ec_node_or_add(cmdlist, create_pool_commands()) < 0) goto fail; /* the addr commands */ if (ec_node_or_add(cmdlist, create_addr_commands()) < 0) goto fail; /* the lexer, added above the command list */ cmdlist = ec_node_sh_lex(EC_NO_ID, cmdlist); if (cmdlist == NULL) goto fail; return cmdlist; fail: fprintf(stderr, "cannot initialize nodes\n"); ec_node_free(cmdlist); return NULL; } int main(void) { struct ec_editline *editline = NULL; struct ec_node *node = NULL; if (ip_pool_init() < 0) { fprintf(stderr, "cannot init IP pools: %s\n", strerror(errno)); return 1; } if (ec_init() < 0) { fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); return 1; } node = create_commands(); if (node == NULL) { fprintf(stderr, "failed to create commands: %s\n", strerror(errno)); goto fail; } editline = ec_editline("pool-editline", stdin, stdout, stderr, 0); if (editline == NULL) { fprintf(stderr, "Failed to initialize editline\n"); goto fail; } if (ec_editline_set_prompt(editline, "pool> ") < 0) { fprintf(stderr, "Failed to set prompt\n"); goto fail; } ec_editline_set_node(editline, node); if (ec_editline_interact(editline, check_exit, NULL) < 0) goto fail; ec_editline_free(editline); ec_node_free(node); ip_pool_exit(); return 0; fail: ec_editline_free(editline); ec_node_free(node); ip_pool_exit(); return 1; } �������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/pool-editline/meson.build��������������������������������������������������0000664�0000000�0000000�00000000771�15203622040�0022104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2025, Olivier MATZ <zer0@droids-corp.org> if not get_option('examples').require(edit_dep.found(), error_message: 'examples require libedit').allowed() subdir_done() endif pool_editline_sources = files( 'ip_pool.c', 'main.c', ) doc_sources += pool_editline_sources + files('ip_pool.h') ecoli_pool_editline = executable( 'ecoli-pool-editline', pool_editline_sources, include_directories : inc, link_with : libecoli, dependencies: [edit_dep]) �������libecoli-0.11.6/examples/readline/������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0016754�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/readline/main.c������������������������������������������������������������0000664�0000000�0000000�00000020241�15203622040�0020043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @example readline/main.c * Integration with GNU readline instead of libedit. * * Demonstrates how to use libecoli's completion API with GNU readline * by implementing custom completion functions. * * Example session: * @code * > hello john 5 * parse(match=yes, len=3) * seq(match=yes, len=3) * str(hello)(match=yes, len=1) "hello" * or(name)(match=yes, len=1) * str(john)(match=yes, len=1) "john" * int(int)(match=yes, len=1) "5" * > good morning bob * parse(match=yes, len=3) * cmd(match=yes, len=3) * cmd(name)(match=yes, len=1) "bob" * > buy potatoes * parse(match=yes, len=2) * cmd(match=yes, len=2) * > bye * parse(match=yes, len=1) * seq(match=yes, len=1) * str(bye)(match=yes, len=1) "bye" * @endcode */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <readline/history.h> #include <readline/readline.h> #include <ecoli.h> static struct ec_node *commands; static char *my_completion_entry(const char *s, int state) { static struct ec_comp *c; static struct ec_comp_item *item; enum ec_comp_type item_type; const char *item_str, *item_display; (void)s; /* Don't append a quote. Note: there are still some bugs when * completing a quoted token. */ rl_completion_suppress_quote = 1; rl_completer_quote_characters = "\"'"; if (state == 0) { char *line; ec_comp_free(c); line = strdup(rl_line_buffer); if (line == NULL) return NULL; line[rl_point] = '\0'; c = ec_complete(commands, line); free(line); if (c == NULL) return NULL; free(item); item = ec_comp_iter_first(c, EC_COMP_FULL | EC_COMP_PARTIAL); if (item == NULL) return NULL; } else { item = ec_comp_iter_next(item, EC_COMP_FULL | EC_COMP_PARTIAL); if (item == NULL) return NULL; } item_str = ec_comp_item_get_str(item); if (ec_comp_count(c, EC_COMP_FULL | EC_COMP_PARTIAL) == 1) { /* don't add the trailing space for partial completions */ if (state == 0) { item_type = ec_comp_item_get_type(item); if (item_type == EC_COMP_FULL) rl_completion_suppress_append = 0; else rl_completion_suppress_append = 1; } return strdup(item_str); } else if (rl_completion_type == '?') { /* on second try only show the display part */ item_display = ec_comp_item_get_display(item); return strdup(item_display); } return strdup(item_str); } static char **my_attempted_completion(const char *text, int start, int end) { (void)start; (void)end; /* remove default file completion */ rl_attempted_completion_over = 1; return rl_completion_matches(text, my_completion_entry); } /* this function builds the help string */ static char *get_node_help(const struct ec_comp_item *item) { const struct ec_comp_group *grp; const struct ec_pnode *pstate; const struct ec_node *node; char *help = NULL; const char *node_help = NULL; char *node_desc = NULL; grp = ec_comp_item_get_grp(item); for (pstate = ec_comp_group_get_pstate(grp); pstate != NULL; pstate = ec_pnode_get_parent(pstate)) { node = ec_pnode_get_node(pstate); if (node_help == NULL) node_help = ec_dict_get(ec_node_attrs(node), "help"); if (node_desc == NULL) node_desc = ec_node_desc(node); } if (node_help == NULL) node_help = "-"; if (node_desc == NULL) return NULL; if (asprintf(&help, "%-20s %s", node_desc, node_help) < 0) return NULL; free(node_desc); return help; } static int show_help(int ignore, int invoking_key) { const struct ec_comp_group *grp, *prev_grp = NULL; struct ec_comp_item *item = NULL; struct ec_comp *c = NULL; struct ec_pnode *p = NULL; char *line = NULL; size_t count = 0; char **helps = NULL; int match = 0; int ret = 1; int cols; (void)ignore; (void)invoking_key; line = strdup(rl_line_buffer); if (line == NULL) goto fail; /* check if the current line matches */ p = ec_parse(commands, line); if (ec_pnode_matches(p)) match = 1; ec_pnode_free(p); p = NULL; /* complete at current cursor position */ line[rl_point] = '\0'; c = ec_complete(commands, line); free(line); line = NULL; if (c == NULL) goto fail; /* strangely, rl_display_match_list() expects first index at 1 */ helps = calloc(match + 1, sizeof(char *)); if (helps == NULL) goto fail; if (match) helps[1] = "<return>"; /* let's display one contextual help per node */ EC_COMP_FOREACH (item, c, EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL) { char **tmp; /* keep one help per group, skip other items */ grp = ec_comp_item_get_grp(item); if (grp == prev_grp) continue; prev_grp = grp; tmp = realloc(helps, (count + match + 2) * sizeof(char *)); if (tmp == NULL) goto fail; helps = tmp; helps[count + match + 1] = get_node_help(item); count++; } /* ensure not more than 1 entry per line */ rl_get_screen_size(NULL, &cols); rl_display_match_list(helps, count + match, cols); rl_forced_update_display(); ret = 0; fail: free(item); ec_pnode_free(p); free(line); ec_comp_free(c); if (helps != NULL) { while (count--) free(helps[count + match + 1]); } free(helps); return ret; } static int create_commands(void) { struct ec_node *cmdlist = NULL, *cmd = NULL; cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; cmd = EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "hello"), EC_NODE_OR( "name", ec_node_str("john", "john"), ec_node_str(EC_NO_ID, "johnny"), ec_node_str(EC_NO_ID, "mike") ), ec_node_option(EC_NO_ID, ec_node_int("int", 0, 10, 10)) ); if (cmd == NULL) goto fail; ec_dict_set(ec_node_attrs(cmd), "help", "say hello to someone several times", NULL); ec_dict_set( ec_node_attrs(ec_node_find(cmd, "john")), "help", "specific help for john", NULL ); ec_dict_set( ec_node_attrs(ec_node_find(cmd, "name")), "help", "the name of the person", NULL ); ec_dict_set(ec_node_attrs(ec_node_find(cmd, "int")), "help", "an integer (0-10)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; cmd = EC_NODE_CMD( EC_NO_ID, "good morning name [count]", EC_NODE_CMD("name", "bob|bobby|michael"), ec_node_int("count", 0, 10, 10) ); if (cmd == NULL) goto fail; ec_dict_set(ec_node_attrs(cmd), "help", "say good morning to someone several times", NULL); ec_dict_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the person to greet", NULL); ec_dict_set( ec_node_attrs(ec_node_find(cmd, "count")), "help", "how many times to greet (0-10)", NULL ); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; cmd = EC_NODE_CMD(EC_NO_ID, "buy potatoes,carrots,pumpkins"); if (cmd == NULL) goto fail; ec_dict_set(ec_node_attrs(cmd), "help", "buy some vegetables", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; cmd = EC_NODE_CMD( EC_NO_ID, "eat vegetables", ec_node_many( "vegetables", EC_NODE_OR( EC_NO_ID, ec_node_str(EC_NO_ID, "potatoes"), ec_node_once(EC_NO_ID, ec_node_str(EC_NO_ID, "carrots")), ec_node_once(EC_NO_ID, ec_node_str(EC_NO_ID, "pumpkins")) ), 1, 0 ) ); if (cmd == NULL) goto fail; ec_dict_set(ec_node_attrs(cmd), "help", "eat vegetables (take some more potatoes)", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "bye")); ec_dict_set(ec_node_attrs(cmd), "help", "say bye", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "load"), ec_node("file", EC_NO_ID)); ec_dict_set(ec_node_attrs(cmd), "help", "load a file", NULL); if (ec_node_or_add(cmdlist, cmd) < 0) goto fail; commands = ec_node_sh_lex(EC_NO_ID, cmdlist); if (commands == NULL) goto fail; return 0; fail: fprintf(stderr, "cannot initialize nodes\n"); ec_node_free(cmdlist); return -1; } int main(void) { struct ec_pnode *p; char *line; if (ec_init() < 0) { fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); return 1; } if (create_commands() < 0) return 1; rl_bind_key('?', show_help); rl_attempted_completion_function = my_attempted_completion; while (1) { line = readline("> "); if (line == NULL) break; p = ec_parse(commands, line); ec_pnode_dump(stdout, p); add_history(line); ec_pnode_free(p); } ec_node_free(commands); return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/readline/meson.build�������������������������������������������������������0000664�0000000�0000000�00000000664�15203622040�0021124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> readline_dep = dependency('readline', required: get_option('examples')) if not readline_dep.found() subdir_done() endif readline_sources = files( 'main.c', ) doc_sources += readline_sources ecoli_readline = executable( 'ecoli-readline', readline_sources, include_directories : inc, link_with : libecoli, dependencies: readline_dep) ����������������������������������������������������������������������������libecoli-0.11.6/examples/simple-editline/�����������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0020255�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/simple-editline/README.md��������������������������������������������������0000664�0000000�0000000�00000000723�15203622040�0021536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This is the most simple example that demonstrates a common use-case for libecoli. It shows how to build a custom CLI based on libedit, with completion and contextual help. The top-level helpers of `ec_editline` API are used (`ec_interact()`), making the code as compact as possible. The grammar is defined in one function `create_commands()`. The callbacks are registered using `ec_editline_set_callback()`, and the contextual helps using `ec_editline_set_help()`. ���������������������������������������������libecoli-0.11.6/examples/simple-editline/main.c�����������������������������������������������������0000664�0000000�0000000�00000011052�15203622040�0021344�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /** * @example simple-editline/main.c * Basic interactive CLI with commands, help, and completion. * * Demonstrates building a grammar using sequence, or, and option nodes, * setting help text and callbacks on commands, and using the editline * integration for interactive input with TAB completion. * * Example session: * @code * simple> hello john * you say hello to john * simple> hello mike 3 * you say hello to mike 3 times * simple> bye johnny * you say bye to johnny * simple> exit * Exit ! * @endcode */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli.h> #define ID_NAME "id_name" #define ID_JOHN "id_john" #define ID_COUNT "id_count" /* stop program when true */ static bool done; static int hello_cb(const struct ec_pnode *parse) { const char *count; const char *name; name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_NAME)), 0); count = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_COUNT)), 0); printf("you say hello to %s", name); if (count) printf(" %s times", count); printf("\n"); return 0; } static int bye_cb(const struct ec_pnode *parse) { const char *name; name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_NAME)), 0); printf("you say bye to %s\n", name); return 0; } static int exit_cb(const struct ec_pnode *parse) { (void)parse; printf("Exit !\n"); done = true; return 0; } static int check_exit(void *opaque) { (void)opaque; return done; } static struct ec_node *create_commands(void) { struct ec_node *cmdlist = NULL, *cmd = NULL; struct ec_node *names = NULL; int ret; /* the top node containing the list of commands */ cmdlist = ec_node("or", EC_NO_ID); if (cmdlist == NULL) goto fail; /* a common subtree containing a list of names */ names = EC_NODE_OR( ID_NAME, ec_node_str(ID_JOHN, "john"), ec_node_str(EC_NO_ID, "johnny"), ec_node_str(EC_NO_ID, "mike") ); if (names == NULL) goto fail; /* the hello command */ cmd = EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "hello"), ec_node_clone(names), ec_node_option(EC_NO_ID, ec_node_int(ID_COUNT, 0, 10, 10)) ); if (cmd == NULL) goto fail; if (ec_interact_set_callback(cmd, hello_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "say hello to someone several times") < 0) goto fail; if (ec_interact_set_help(ec_node_find(cmd, ID_JOHN), "specific help for john") < 0) goto fail; if (ec_interact_set_help(ec_node_find(cmd, ID_NAME), "the name of the person") < 0) goto fail; if (ec_interact_set_help(ec_node_find(cmd, ID_COUNT), "an integer (0-10)") < 0) goto fail; ret = ec_node_or_add(cmdlist, cmd); cmd = NULL; /* already freed, even on error */ if (ret < 0) goto fail; /* the bye command */ cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "bye"), ec_node_clone(names)); if (ec_interact_set_callback(cmd, bye_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "say bye") < 0) goto fail; ret = ec_node_or_add(cmdlist, cmd); cmd = NULL; /* already freed, even on error */ if (ret < 0) goto fail; /* the exit command */ cmd = ec_node_str(EC_NO_ID, "exit"); if (ec_interact_set_callback(cmd, exit_cb) < 0) goto fail; if (ec_interact_set_help(cmd, "exit program") < 0) goto fail; ret = ec_node_or_add(cmdlist, cmd); cmd = NULL; /* already freed, even on error */ if (ret < 0) goto fail; /* the lexer, added above the command list */ cmdlist = ec_node_sh_lex(EC_NO_ID, cmdlist); if (cmdlist == NULL) goto fail; ec_node_free(names); return cmdlist; fail: fprintf(stderr, "cannot initialize nodes\n"); ec_node_free(names); ec_node_free(cmdlist); ec_node_free(cmd); return NULL; } int main(void) { struct ec_editline *editline = NULL; struct ec_node *node = NULL; if (ec_init() < 0) { fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); return 1; } node = create_commands(); if (node == NULL) { fprintf(stderr, "failed to create commands: %s\n", strerror(errno)); goto fail; } editline = ec_editline("simple-editline", stdin, stdout, stderr, 0); if (editline == NULL) { fprintf(stderr, "Failed to initialize editline\n"); goto fail; } if (ec_editline_set_prompt(editline, "simple> ") < 0) { fprintf(stderr, "Failed to set prompt\n"); goto fail; } ec_editline_set_node(editline, node); if (ec_editline_interact(editline, check_exit, NULL) < 0) goto fail; ec_editline_free(editline); ec_node_free(node); return 0; fail: ec_editline_free(editline); ec_node_free(node); return 1; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/examples/simple-editline/meson.build������������������������������������������������0000664�0000000�0000000�00000000740�15203622040�0022420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2025, Olivier MATZ <zer0@droids-corp.org> if not get_option('examples').require(edit_dep.found(), error_message: 'examples require libedit').allowed() subdir_done() endif simple_editline_sources = files( 'main.c', ) doc_sources += simple_editline_sources ecoli_simple_editline = executable( 'ecoli-simple-editline', simple_editline_sources, include_directories : inc, link_with : libecoli, dependencies: [edit_dep]) ��������������������������������libecoli-0.11.6/include/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0014776�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli.h���������������������������������������������������������������������0000664�0000000�0000000�00000002315�15203622040�0016243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #pragma once #include <ecoli/assert.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/editline.h> #include <ecoli/htable.h> #include <ecoli/init.h> #include <ecoli/interact.h> #include <ecoli/log.h> #include <ecoli/murmurhash.h> #include <ecoli/node.h> #include <ecoli/node_any.h> #include <ecoli/node_bypass.h> #include <ecoli/node_cmd.h> #include <ecoli/node_cond.h> #include <ecoli/node_dynamic.h> #include <ecoli/node_dynlist.h> #include <ecoli/node_empty.h> #include <ecoli/node_expr.h> #include <ecoli/node_file.h> #include <ecoli/node_helper.h> #include <ecoli/node_int.h> #include <ecoli/node_many.h> #include <ecoli/node_none.h> #include <ecoli/node_once.h> #include <ecoli/node_option.h> #include <ecoli/node_or.h> #include <ecoli/node_re.h> #include <ecoli/node_re_lex.h> #include <ecoli/node_seq.h> #include <ecoli/node_sh_lex.h> #include <ecoli/node_space.h> #include <ecoli/node_str.h> #include <ecoli/node_subset.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> #include <ecoli/utils.h> #include <ecoli/vec.h> #include <ecoli/yaml.h> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0016071�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/assert.h��������������������������������������������������������������0000664�0000000�0000000�00000003331�15203622040�0017543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_assert Assert * @{ * * @brief Assertion helpers * * Helpers to check at runtime if a condition is true, or otherwise * either abort (exit program) or return an error. * * @internal */ #pragma once #include <stdbool.h> /** * Abort if the condition is false. * * If expression is false, this macro will print an error message to * standard error and terminates the program by calling abort(3). * * @param expr * The expression to be checked. * @param args * The format string, optionally followed by other arguments. * * @internal */ #define ec_assert_print(expr, args...) __ec_assert_print(expr, #expr, args) /** * Actual function invoked by ec_assert_print() * * @internal */ void __ec_assert_print(bool expr, const char *expr_str, const char *format, ...); /** * Check a condition or return. * * If the condition is true, do nothing. If it is false, set * errno and return the specified value. * * @param cond * The condition to test. * @param ret * The value to return. * @param err * The errno to set. * * @internal */ #define EC_CHECK_ARG(cond, ret, err) \ do { \ if (!(cond)) { \ errno = err; \ return ret; \ } \ } while (0) /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/complete.h������������������������������������������������������������0000664�0000000�0000000�00000035447�15203622040�0020067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_complete Complete * @{ * * @brief Complete string input using a grammar graph. * * This file provides helpers to list and manipulate the possible * completions for a given input. * * Use ::ec_complete_strvec() to complete a vector of strings when * the input is already split into several tokens. You can use * ::ec_complete() if you know that the size of the vector is * 1. This is common if your grammar graph includes a lexer that * will tokenize the input. * * These two functions return a pointer to an ::ec_comp structure, that * lists the possible completions. The completions are grouped into * ::ec_comp_group. All completion items of a group share the same parsing * state and are issued by the same node. */ #pragma once #include <stdio.h> #include <sys/queue.h> #include <sys/types.h> struct ec_node; struct ec_strvec; /** A completion item. */ struct ec_comp_item; /** A group of completion items sharing the same parsing state. */ struct ec_comp_group; /** A list of completion items and groups. */ struct ec_comp; /** * Completion item type. */ enum ec_comp_type { EC_COMP_UNKNOWN = 0x1, /**< Valid token but completion not possible. */ EC_COMP_FULL = 0x2, /**< The item is fully completed. */ EC_COMP_PARTIAL = 0x4, /**< The item is partially completed. */ EC_COMP_ALL = 0x7, /**< All completion types. */ }; /** * Get the list of completions from a string input. * * It is equivalent to calling ec_complete_strvec() with a * vector that only contains 1 element, the input string. Using this * function is often more convenient if you get your input from a * buffer, because you won't have to create a vector. Usually, it means * you have a lexer in your grammar graph that will tokenize the input. * * See ec_complete_strvec() for more details. * * @param node * The grammar graph. * @param str * The input string. * @return * A pointer to the completion list on success, or NULL * on error (errno is set). */ struct ec_comp *ec_complete(const struct ec_node *node, const char *str); /** * Get the list of completions from a string vector input. * * This function tries to complete the last element of the given string * vector. For instance, to complete with file names in an equivalent of * the `cat` shell command, the passed vector should be `["cat", ""]` (and * not `["cat"]`). To complete with files starting with `"x"`, the passed * vector should be `["cat", "x"]`. * * To get the completion list, the engine parses the beginning of the * input using the grammar graph. The resulting parsing tree is saved and * attached to each completion group. * * The result is a ::ec_comp structure pointer, which contains several * groups of completion items. * * @param node * The grammar graph. * @param strvec * The input string vector. * @return * A pointer to the completion list on success, or NULL * on error (errno is set). */ struct ec_comp *ec_complete_strvec(const struct ec_node *node, const struct ec_strvec *strvec); /** * Return a new string vector based on the provided one using completion to * expand non-ambiguous tokens to their full value. * * @param node * The grammar graph. * @param type * Completion item type to consider. * @param strvec * The input string vector. * @return * A new string vector with non-ambiguous tokens expanded, or NULL * on error (errno is set). */ struct ec_strvec *ec_complete_strvec_expand( const struct ec_node *node, enum ec_comp_type type, const struct ec_strvec *strvec ); /** * Get the list of completions of a child node. * * This function is to be used by intermediate ecoli nodes, i.e., nodes * which have children (e.g., ec_node_seq(), ec_node_or(), ...). It fills an * existing ::ec_comp structure, passed by the parent node. * * @param node * The grammar graph. * @param comp * The current completion list to be filled. * @param strvec * The input string vector. * @return * 0 on success, or -1 on error (errno is set). */ int ec_complete_child( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ); /** * Create an empty completion object (list of completion items). * * @return * A pointer to the completion structure on success, or NULL on error * (errno is set). */ struct ec_comp *ec_comp(void); /** * Free a completion object and all its items. * * @param comp * The pointer to the completion structure to free. */ void ec_comp_free(struct ec_comp *comp); /** * Dump the content of a completions list. * * @param out * The stream where the dump is sent. * @param comp * The pointer to the completion list structure. */ void ec_comp_dump(FILE *out, const struct ec_comp *comp); /** * Merge items contained in @p from into @p to. * * The @p from comp struct is freed. * * @param to * The destination completion list. * @param from * The source completion list. * @return * 0 on success, or -1 on error (errno is set). */ int ec_comp_merge(struct ec_comp *to, struct ec_comp *from); /** * Get current parsing state of completion. * * This function can be called by a node during the completion process. * * When processing the list of completions for a given input, * an incomplete parsing tree is generated before the completion * callback is invoked. A node may use it if the completions list * depend on what was previously parsed. For instance, the ec_node_once() * node checks in the parsing tree if the node is already parsed. * In this case, no completion is issued. * * @param comp * The current completion list. * @return * The current parsing state (cannot be NULL). */ struct ec_pnode *ec_comp_get_cur_pstate(const struct ec_comp *comp); /** * Get current completion group. * * This function can be called by a node during the completion process. * * A completion group is a list of completion items that share the same * parsing state and are issued by the same grammar node. The completion * group is created when the first item is added, thus this function * returns NULL if no item has been added in the group. * * @param comp * The current completion list. * @return * The current completion group (can be NULL). */ struct ec_comp_group *ec_comp_get_cur_group(const struct ec_comp *comp); /** * Get completion attributes. * * Arbitrary attributes (stored in a dictionary) can be attached to a * completion state. * * @param comp * The completion list. * @return * The associated attributes. */ struct ec_dict *ec_comp_get_attrs(const struct ec_comp *comp); /** * Add an item in completion list. * * This function can be called by a node during the completion process, * for each completion item that should be added to the list. This is * typically done in terminal nodes, like ec_node_str() or ec_node_file(). * * Create a new completion item, and add it into the completion * list. A completion item has a type, which can be: * * - ::EC_COMP_FULL - the item is fully completed (common case, used * for instance in the str node) * - ::EC_COMP_PARTIAL - the item is only partially completed (this * happens rarely, for instance in the file node, when a completion * goes up to the next slash). * - ::EC_COMP_UNKNOWN - the node detects a valid token, but does not know * how to complete it (e.g., the int node). * * @param comp * The current completion list. * @param node * The node issuing the completion item. * @param type * The type of the item. * @param current * The incomplete string being completed. * @param full * The string fully completed. * @return * The item that was added in the list on success, or NULL * on error. Note: do not free the returned value, as it is referenced * by the completion list. It is returned in case it needs to be * modified, for instance with ec_comp_item_set_display(). */ struct ec_comp_item *ec_comp_add_item( struct ec_comp *comp, const struct ec_node *node, enum ec_comp_type type, const char *current, const char *full ); /** * Get the string value of a completion item. * * @param item * The completion item. * @return * The completion string of this item. */ const char *ec_comp_item_get_str(const struct ec_comp_item *item); /** * Get the display string value of a completion item. * * The display string corresponds to what is displayed when * listing the possible completions. * * @param item * The completion item. * @return * The display string of this item. */ const char *ec_comp_item_get_display(const struct ec_comp_item *item); /** * Get the completion string value of a completion item. * * The completion string corresponds to what should be added to * the incomplete token to complete it. * * @param item * The completion item. * @return * The completion string of this item. */ const char *ec_comp_item_get_completion(const struct ec_comp_item *item); /** * Get the current string value (before completion) of a completion item. * * @param item * The completion item. * @return * The current string of this item. */ const char *ec_comp_item_get_current(const struct ec_comp_item *item); /** * Get the group of a completion item. * * The completion group corresponds to the list of items that share * the same parsing state and are issued by the same node. * * @param item * The completion item. * @return * The completion group of this item. */ const struct ec_comp_group *ec_comp_item_get_grp(const struct ec_comp_item *item); /** * Get the type of a completion item. * * @param item * The completion item. * @return * The type of this item (::EC_COMP_UNKNOWN, ::EC_COMP_PARTIAL or * ::EC_COMP_FULL). */ enum ec_comp_type ec_comp_item_get_type(const struct ec_comp_item *item); /** * Get the node associated with a completion item. * * @param item * The completion item. * @return * The node that issued the completion item. */ const struct ec_node *ec_comp_item_get_node(const struct ec_comp_item *item); /** * Set the completion item string. * * Some intermediate nodes like sh_lex modify the completion string of * items generated by their children, for instance to add quotes. * * @param item * The completion item to update. * @param str * The new item string. * @return * 0 on success, or -1 on error (errno is set). */ int ec_comp_item_set_str(struct ec_comp_item *item, const char *str); /** * Set the display value of an item. * * The display string corresponds to what is displayed when listing the * possible completions. Some nodes like ec_node_file() change the default * value to display the base name instead of the full path. * * @param item * The completion item to update. * @param display * The new display string. * @return * 0 on success, or -1 on error (errno is set). */ int ec_comp_item_set_display(struct ec_comp_item *item, const char *display); /** * Set the completion value of an item. * * The completion string corresponds to what should be added to * the incomplete token to complete it. Some nodes like sh_lex * modify it in the items generated by their children, for instance * to add a terminating quote. * * @param item * The completion item to update. * @param completion * The new completion string. * @return * 0 on success, or -1 on error (errno is set). */ int ec_comp_item_set_completion(struct ec_comp_item *item, const char *completion); /** * Get the completion group node. * * @param grp * The completion group. */ const struct ec_node *ec_comp_group_get_node(const struct ec_comp_group *grp); /** * Get the completion group parsing state. * * The parsing state contains the parsing result of the input data * preceding the completion. All items of a completion group share the * same parsing state. * * @param grp * The completion group. * @return * The parsing state of the completion group. */ const struct ec_pnode *ec_comp_group_get_pstate(const struct ec_comp_group *grp); /** * Get the completion group attributes. * * Arbitrary attributes (stored in a dictionary) can be attached to a * completion group. * * @param grp * The completion group. * @return * The attributes of the completion group. */ const struct ec_dict *ec_comp_group_get_attrs(const struct ec_comp_group *grp); /** * Default node completion callback * * This function is the default completion method for nodes that do * not define one. * * This helper adds a completion item of type ::EC_COMP_UNKNOWN if the * input string vector contains one element, to indicate that everything * before the last element of the string vector has been parsed * successfully, but the node doesn't know how to complete the last * element. * * @param node * The completion node. * @param comp * The completion list to update. * @param strvec * The input string vector. * @return * 0 on success, or -1 on error (errno is set). */ int ec_complete_unknown( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ); /** * Get the number of completion items. * * Return the number of completion items that match a given type in a * completion list. * * @param comp * The completion list. * @param type * A logical OR of flags among ::EC_COMP_UNKNOWN, ::EC_COMP_PARTIAL and * ::EC_COMP_FULL, to select the type to match. * @return * The number of matching items. */ size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type); /** * Get the first completion item matching the type. * * Also see EC_COMP_FOREACH(). * * @param comp * The completion list. * @param type * A logical OR of flags among ::EC_COMP_UNKNOWN, ::EC_COMP_PARTIAL and * ::EC_COMP_FULL, to select the type to iterate. * @return * A completion item. */ struct ec_comp_item *ec_comp_iter_first(const struct ec_comp *comp, enum ec_comp_type type); /** * Get the next completion item matching the type. * * Also see EC_COMP_FOREACH(). * * @param item * The current completion item. * @param type * A logical OR of flags among ::EC_COMP_UNKNOWN, ::EC_COMP_PARTIAL and * ::EC_COMP_FULL, to select the type to iterate. * @return * The next completion item, or NULL if there is no more. */ struct ec_comp_item *ec_comp_iter_next(struct ec_comp_item *item, enum ec_comp_type type); /** * Iterate items matching a given type. * * @param item * The item that will be set at each iteration. * @param comp * The completion list. * @param type * A logical OR of flags among ::EC_COMP_UNKNOWN, ::EC_COMP_PARTIAL and * ::EC_COMP_FULL, to select the type to iterate. */ #define EC_COMP_FOREACH(item, comp, type) \ for (item = ec_comp_iter_first(comp, type); item != NULL; \ item = ec_comp_iter_next(item, type)) /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/config.h��������������������������������������������������������������0000664�0000000�0000000�00000023705�15203622040�0017516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_config Node configuration * @{ * * @brief Configure nodes behavior through generic API. */ #pragma once #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <sys/queue.h> struct ec_config; struct ec_dict; /** * The type identifier for a config value. */ enum ec_config_type { EC_CONFIG_TYPE_NONE = 0, EC_CONFIG_TYPE_BOOL, EC_CONFIG_TYPE_INT64, EC_CONFIG_TYPE_UINT64, EC_CONFIG_TYPE_STRING, EC_CONFIG_TYPE_NODE, EC_CONFIG_TYPE_LIST, EC_CONFIG_TYPE_DICT, }; /** * Flags related to a configuration schema. */ enum ec_config_flags { EC_CONFIG_F_MANDATORY = 1 << 0, }; /** * Structure describing the format of a configuration value. * * This structure is used in a const array which is referenced by a * struct ::ec_config. Each entry of the array represents a key/value * storage of the configuration dictionary. */ struct ec_config_schema { const char *key; /**< The key string (NULL for list elts). */ const char *desc; /**< A description of the value. */ enum ec_config_type type; /**< Type of the value */ enum ec_config_flags flags; /**< Flags, see ::ec_config_flags. */ /** If type is dict or list, the schema of the dict or list * elements. Otherwise, must be NULL. */ const struct ec_config_schema *subschema; }; TAILQ_HEAD(ec_config_list, ec_config); /** * Structure storing the configuration data. */ struct ec_config { /** Type of value stored in the union. */ enum ec_config_type type; union { bool boolean; /**< Boolean value */ int64_t i64; /**< Signed integer value */ uint64_t u64; /**< Unsigned integer value */ char *string; /**< String value */ struct ec_node *node; /**< Node value */ struct ec_dict *dict; /**< Hash table value */ struct ec_config_list list; /**< List value */ }; /** * Next in list, only valid if type is list. */ TAILQ_ENTRY(ec_config) next; }; /** * Validate a configuration schema array. * * @param schema * Pointer to the first element of the schema array. The array * must be terminated by a sentinel entry `{.type = EC_CONFIG_TYPE_NONE}`. * @return * 0 if the schema is valid, or -1 on error (errno is set). */ int ec_config_schema_validate(const struct ec_config_schema *schema); /** * Dump a configuration schema array. * * @param out * Output stream on which the dump will be sent. * @param schema * Pointer to the first element of the schema array. The array * must be terminated by a sentinel entry `{.type == EC_CONFIG_TYPE_NONE}`. * @param name * The name of the schema. */ void ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema, const char *name); /** * Find a schema entry matching the key. * * Browse the schema array and look up the given key. * * @param schema * Pointer to the first element of the schema array. The array * must be terminated by a sentinel entry `{.type == EC_CONFIG_TYPE_NONE}`. * @param key * Schema key name. * @return * The schema entry if it matches a key, or NULL if not found. */ const struct ec_config_schema * ec_config_schema_lookup(const struct ec_config_schema *schema, const char *key); /** * Get the type of a schema entry. * * @param schema_elt * Pointer to an element of the schema array. * @return * The type of the schema entry. */ enum ec_config_type ec_config_schema_type(const struct ec_config_schema *schema_elt); /** * Get the subschema of a schema entry. * * @param schema_elt * Pointer to an element of the schema array. * @return * The subschema if any, or NULL. */ const struct ec_config_schema *ec_config_schema_sub(const struct ec_config_schema *schema_elt); /** * Check if a key name is reserved in a config dict. * * Some key names are reserved and should not be used in configs. * * @param name * The name of the key to test. * @return * True if the key name is reserved and must not be used, else false. */ bool ec_config_key_is_reserved(const char *name); /** * Array of reserved key names. */ extern const char *ec_config_reserved_keys[]; /** * Get the type of the configuration. * * @param config * The configuration. * @return * The configuration type. */ enum ec_config_type ec_config_get_type(const struct ec_config *config); /** * Create a boolean configuration value. * * @param boolean * The boolean value to be set. * @return * The configuration object, or NULL on error (errno is set). */ struct ec_config *ec_config_bool(bool boolean); /** * Create a signed integer configuration value. * * @param i64 * The signed integer value to be set. * @return * The configuration object, or NULL on error (errno is set). */ struct ec_config *ec_config_i64(int64_t i64); /** * Create an unsigned configuration value. * * @param u64 * The unsigned integer value to be set. * @return * The configuration object, or NULL on error (errno is set). */ struct ec_config *ec_config_u64(uint64_t u64); /** * Create a string configuration value. * * @param string * The string value to be set. The string is copied into the * configuration object. * @return * The configuration object, or NULL on error (errno is set). */ struct ec_config *ec_config_string(const char *string); /** * Create a node configuration value. * * @param node * The node pointer to be set. The node is "consumed" by * the function and should not be used by the caller, even * on error. The caller can use ec_node_clone() to keep a * reference on the node. * @return * The configuration object, or NULL on error (errno is set). */ struct ec_config *ec_config_node(struct ec_node *node); /** * Create a hash table configuration value. * * @return * A configuration object containing an empty hash table, or NULL on * error (errno is set). */ struct ec_config *ec_config_dict(void); /** * Create a list configuration value. * * @return * The configuration object containing an empty list, or NULL on * error (errno is set). */ struct ec_config *ec_config_list(void); /** * Add a config object into a list. * * @param list * The list configuration in which the value will be added. * @param value * The value configuration to add in the list. The value object * will be freed when freeing the list object. On error, the * value object is also freed. * @return * 0 on success, else -1 (errno is set). */ int ec_config_list_add(struct ec_config *list, struct ec_config *value); /** * Remove an element from a list. * * The element is freed and should not be accessed. * * @param list * The list configuration. * @param config * The element to remove from the list. * @return * 0 on success, -1 on error (errno is set). */ int ec_config_list_del(struct ec_config *list, struct ec_config *config); /** * Count the number of elements in a list or dict. * * @param config * The configuration that must be a list or a dict. * @return * The number of elements, or -1 on error (errno is set). */ ssize_t ec_config_count(const struct ec_config *config); /** * Validate a configuration. * * @param dict * A hash table configuration to validate. * @param schema * Pointer to the first element of the schema array. The array * must be terminated by a sentinel entry (type == EC_CONFIG_TYPE_NONE). * @return * 0 on success, -1 on error (errno is set). */ int ec_config_validate(const struct ec_config *dict, const struct ec_config_schema *schema); /** * Set a value in a hash table configuration. * * @param dict * The hash table configuration. * @param key * The key to update. * @param value * The value to set. The value object will be freed when freeing the * dict object. On error, the value object is also freed. * @return * 0 on success, -1 on error (errno is set). */ int ec_config_dict_set(struct ec_config *dict, const char *key, struct ec_config *value); /** * Remove an element from a hash table configuration. * * The element is freed and should not be accessed. * * @param dict * The hash table configuration. * @param key * The key of the configuration to delete. * @return * 0 on success, -1 on error (errno is set). */ int ec_config_dict_del(struct ec_config *dict, const char *key); /** * Compare two configurations. */ int ec_config_cmp(const struct ec_config *config1, const struct ec_config *config2); /** * Get configuration value. */ struct ec_config *ec_config_dict_get(const struct ec_config *config, const char *key); /** * Get the first element of a list. * * Example of use: * * @code{.c} * for (config = ec_config_list_first(list); * config != NULL; * config = ec_config_list_next(list, config)) { * ... * } * @endcode * * @param list * The list configuration to iterate. * @return * The first configuration element, or NULL on error (errno is set). */ struct ec_config *ec_config_list_first(struct ec_config *list); /** * Get next element in list. * * @param list * The list configuration being iterated. * @param config * The current configuration element. * @return * The next configuration element, or NULL if there is no more element. */ struct ec_config *ec_config_list_next(struct ec_config *list, struct ec_config *config); /** * Free a configuration. * * @param config * The element to free. */ void ec_config_free(struct ec_config *config); /** * Compare two configurations. * * @return * 0 if the configurations are equal, else -1. */ int ec_config_cmp(const struct ec_config *value1, const struct ec_config *value2); /** * Duplicate a configuration. * * @param config * The configuration to duplicate. * @return * The duplicated configuration, or NULL on error (errno is set). */ struct ec_config *ec_config_dup(const struct ec_config *config); /** * Dump a configuration. * * @param out * Output stream on which the dump will be sent. * @param config * The configuration to dump. */ void ec_config_dump(FILE *out, const struct ec_config *config); /** @} */ �����������������������������������������������������������libecoli-0.11.6/include/ecoli/dict.h����������������������������������������������������������������0000664�0000000�0000000�00000010513�15203622040�0017165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_dict Dictionary * @{ * * @brief Simple hash table API (string keys) * * This file provides functions to store objects in hash tables, using strings * as keys. */ #pragma once #include <stdbool.h> #include <stdio.h> /** Callback function for freeing dictionary elements. */ typedef void (*ec_dict_elt_free_t)(void *); /** Dictionary (string key hash table). */ struct ec_dict; /** Dictionary element reference. */ struct ec_dict_elt_ref; /** * Create a hash table. * * @return * The hash table, or NULL on error (errno is set). */ struct ec_dict *ec_dict(void); /** * Get a value from the hash table. * * @param dict * The hash table. * @param key * The key string. Must not be NULL. * @return * The element if it is found, or NULL on error (errno is set). * In case of success but the element is NULL, errno is set to 0. * If key is NULL, errno is set to EINVAL. */ void *ec_dict_get(const struct ec_dict *dict, const char *key); /** * Check if the hash table contains this key. * * @param dict * The hash table. * @param key * The key string. Must not be NULL. * @return * true if it contains the key, false otherwise. * If key is NULL, false is returned and errno is set to EINVAL. */ bool ec_dict_has_key(const struct ec_dict *dict, const char *key); /** * Delete an object from the hash table. * * @param dict * The hash table. * @param key * The key string. Must not be NULL. * @return * 0 on success, or -1 on error (errno is set). */ int ec_dict_del(struct ec_dict *dict, const char *key); /** * Add/replace an object in the hash table. * * @param dict * The hash table. * @param key * The key string. Must not be NULL. * @param val * The pointer to be saved in the hash table. * @param free_cb * An optional pointer to a destructor function called when an * object is destroyed (ec_dict_del() or ec_dict_free()). * @return * 0 on success, or -1 on error (errno is set). * On error, the passed value is freed (free_cb(val) is called). */ int ec_dict_set(struct ec_dict *dict, const char *key, void *val, ec_dict_elt_free_t free_cb); /** * Free a hash table and all its objects. * * @param dict * The hash table. */ void ec_dict_free(struct ec_dict *dict); /** * Get the length of a hash table. * * @param dict * The hash table. * @return * The length of the hash table. */ size_t ec_dict_len(const struct ec_dict *dict); /** * Duplicate a hash table. * * A reference counter is shared between the clones of * hash tables so that the objects are freed only when * the last reference is destroyed. * * @param dict * The hash table. * @return * The duplicated hash table, or NULL on error (errno is set). */ struct ec_dict *ec_dict_dup(const struct ec_dict *dict); /** * Dump a hash table. * * @param out * The stream where the dump is sent. * @param dict * The hash table. */ void ec_dict_dump(FILE *out, const struct ec_dict *dict); /** * Iterate the elements in the hash table. * * The typical usage is as below: * * @code{.c} * for (iter = ec_dict_iter(dict); * iter != NULL; * iter = ec_dict_iter_next(iter)) { * printf(" %s: %p\n", * ec_dict_iter_get_key(iter), * ec_dict_iter_get_val(iter)); * } * @endcode * * @param dict * The hash table. * @return * An iterator element, or NULL if the dict is empty. */ struct ec_dict_elt_ref *ec_dict_iter(const struct ec_dict *dict); /** * Make the iterator point to the next element in the hash table. * * @param iter * The hash table iterator. * @return * An iterator element, or NULL if there is no more element. */ struct ec_dict_elt_ref *ec_dict_iter_next(struct ec_dict_elt_ref *iter); /** * Get a pointer to the key of the current element. * * @param iter * The hash table iterator. * @return * The current element key, or NULL if the iterator points to an * invalid element. */ const char *ec_dict_iter_get_key(const struct ec_dict_elt_ref *iter); /** * Get the value of the current element. * * @param iter * The hash table iterator. * @return * The current element value, or NULL if the iterator points to an * invalid element. */ void *ec_dict_iter_get_val(const struct ec_dict_elt_ref *iter); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/editline.h������������������������������������������������������������0000664�0000000�0000000�00000027563�15203622040�0020054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_editline Editline * @{ * * @brief Helpers for editline * * Helpers that can be used to associate an editline instance with * an ecoli node tree. */ #pragma once #include <stdbool.h> #include <stdio.h> struct ec_node; struct ec_pnode; struct ec_comp; struct editline; /** Editline instance for interactive command line editing. */ struct ec_editline; /** * Default history size. */ #define EC_EDITLINE_HISTORY_SIZE 128 /** * Flags passed at ec_editline initialization. */ enum ec_editline_init_flags { /** * Ask the terminal to not send signals (STOP, SUSPEND, ...). The * `ctrl-c`, `ctrl-z` will be interpreted as standard characters. An * action can be associated with these characters with: * * @code{.c} * static int cb(EditLine *editline, int c) { * { * see editline documentation for details * } * * if (el_set(el, EL_ADDFN, "ed-foobar", "Help string about foobar", cb)) * handle_error; * if (el_set(el, EL_BIND, "^C", "ed-break", NULL)) * handle_error; * @endcode * * The default behavior (without this flag) is to let the signal pass: ctrl-c * will stop program and ctrl-z will suspend it. */ EC_EDITLINE_DISABLE_SIGNALS = 1 << 0, /** * Disable history. The default behavior creates a history with * ::EC_EDITLINE_HISTORY_SIZE entries. To change this value, use * ec_editline_set_history(). */ EC_EDITLINE_DISABLE_HISTORY = 1 << 1, /** * Disable completion. The default behavior is to complete when * `?` or `<tab>` is hit. You can register your own callback with: * * @code{.c} * if (el_set(el, EL_ADDFN, "ed-complete", "Complete buffer", callback)) * handle_error; * if (el_set(el, EL_BIND, "^I", "ed-complete", NULL)) * handle_error; * @endcode * * The default used callback is ec_editline_complete(). */ EC_EDITLINE_DISABLE_COMPLETION = 1 << 2, /** * Use editline own signal handler for the following signals when reading * command input: SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP, * and SIGWINCH. Otherwise, the current signal handlers will be used. */ EC_EDITLINE_DEFAULT_SIGHANDLER = 1 << 3, }; /** * Type of callback invoked by ec_editline_interact() to check if loop should * exit (when the function return true), or continue (when the function return * false). */ typedef int (*ec_editline_check_exit_cb_t)(void *opaque); /** * Create an editline instance with default behavior. * * It allocates and initializes an ::ec_editline structure, calls editline's * el_init(), and does the editline configuration according to given flags. * * After that, the user must call ec_editline_set_node() to attach the grammar * to the editline. * * @param prog * The name of the invoking program, used when reading the editrc(5) file * to determine which settings to use. * @param f_in * The input stream to use. * @param f_out * The output stream to use. * @param f_err * The error stream to use. * @param flags * Flags to customize initialization. See ::ec_editline_init_flags. * @return * The allocated ec_editline structure, or NULL on error. */ struct ec_editline *ec_editline( const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags ); /** * Free an editline structure allocated with ec_editline(). * * @param editline * The pointer to the ::ec_editline structure to free. */ void ec_editline_free(struct ec_editline *editline); /** * Return the wrapped editline instance attached to the ::ec_editline structure. * * @param editline * The pointer to the ::ec_editline structure. */ struct editline *ec_editline_get_el(struct ec_editline *editline); /** * Get terminal width and height. * * @param editline * The pointer to the ::ec_editline structure. * @param width * The pointer where the number of columns is stored on success. * @param height * The pointer where the number of rows is stored on success. * @return * 0 on success, or -1 on error (in this case the value pointed by width and * height are not modified). */ int ec_editline_term_size( const struct ec_editline *editline, unsigned int *width, unsigned int *height ); /** * Attach an ecoli node to the editline structure. * * This node must be an sh_lex node, with its grammar subtree. It will be used * for completion and contextual help. The contextual help description is * attached as a string to nodes using a node attribute EC_EDITLINE_HELP_ATTR. * * @param editline * The pointer to the ::ec_editline structure. * @param node * The pointer to the sh_lex ::ec_node to use as grammar. * @return * 0 on success, or -1 on error. errno is set to EINVAL if the node is not * of type sh_lex. */ int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node); /** * Return the ecoli node attached to the editline structure. * * @param editline * The pointer to the ::ec_editline structure. * @return * The pointer to the ::ec_node. */ const struct ec_node *ec_editline_get_node(const struct ec_editline *editline); /** * Change the history size. * * The default behavior is to have a history whose size * is ::EC_EDITLINE_HISTORY_SIZE. This can be changed with this * function. * * @param editline * The pointer to the ::ec_editline structure. * @param hist_size * The desired size of the history. * @param hist_file * Optional file path to load and write the history to. * @return * 0 on success, or -1 on error (errno is set). */ int ec_editline_set_history(struct ec_editline *editline, size_t hist_size, const char *hist_file); /** * Set editline prompt. * * @param editline * The pointer to the ::ec_editline structure. * @param prompt * The prompt string to use. * @return * 0 on success, or -1 on error. */ int ec_editline_set_prompt(struct ec_editline *editline, const char *prompt); /** * Set editline escaped prompt. * * From el_set(3): * * If the start/stop delim character is found in the prompt, the character * itself is not printed, but characters after it are printed directly to the * terminal without affecting the state of the current line. A subsequent second * start/stop literal character ends this behavior. This is typically used to * embed literal escape sequences that change the color/style of the terminal in * the prompt. Note that the literal escape character cannot be the last * character in the prompt, as the escape sequence is attached to the next * character in the prompt. * * @param editline * The pointer to the ::ec_editline structure. * @param prompt * The prompt string to use. * @param delim * The start/stop literal prompt character. * @return * 0 on success, or -1 on error. */ int ec_editline_set_prompt_esc(struct ec_editline *editline, const char *prompt, char delim); /** * Get the current edited line. * * @param editline * The pointer to the ::ec_editline structure. * @param trim_after_cursor * If true, remove all characters starting from cursor (included). * @return * An allocated string containing the current line. It must be freed by the * user. Return NULL on error. */ char *ec_editline_curline(const struct ec_editline *editline, bool trim_after_cursor); /** * Get a line interactively (with completion). * * Wrapper to libedit el_gets(). It returns the edited line without its "\n", * and adds it to the history. * * @param editline * The pointer to the ::ec_editline structure. * @return * An allocated string containing the edited line, that must be freed by the * caller using free(). */ char *ec_editline_gets(struct ec_editline *editline); /** * Get a line interactively (with completion), and parse it. * * Similar to ec_editline_gets(), except that the string result is parsed using * the grammar node on success. * * @param editline * The pointer to the ::ec_editline structure. * @return * An allocated ::ec_pnode containing the parse result. It must be freed by the * caller using ec_pnode_free(). Return NULL on error. */ struct ec_pnode *ec_editline_parse(struct ec_editline *editline); /** * Interact with the user until exit. * * Run the command line, parsing and completing commands on demand. When a * command is parsed successfully, invoke the callback attached to the grammar * node as a ::EC_INTERACT_CB_ATTR attribute. The callback type must be * ::ec_interact_command_cb_t. * * On EOF, or if check_exit() function returns true, exit from the interactive * loop. * * @param editline * The pointer to the ::ec_editline structure. * @param check_exit_cb * Optional function pointer executed before each loop iteration to check if * loop should be exited (when the function return true). * @param opaque * User pointer, passed as-is to check_exit_cb(). * @return * 0 if interactive loop exited gracefully, or -1 on error. */ int ec_editline_interact( struct ec_editline *editline, ec_editline_check_exit_cb_t check_exit_cb, void *opaque ); /** * Default completion callback used by editline. * * It displays the list of completions with TAB, and a contextual help * with '?'. * * @param el * The pointer to libedit Editline structure. * @param c * The character used to complete. * @return * An editline error code: CC_REFRESH, CC_ERROR, or CC_REDISPLAY. */ int ec_editline_complete(struct editline *el, int c); /* * Deprecated aliases for symbols moved to interact.h. */ #include "interact.h" #include "utils.h" /** @cond DEPRECATED */ #define EC_EDITLINE_HELP_ATTR EC_DEPRECATED_MACRO("use EC_INTERACT_HELP_ATTR") EC_INTERACT_HELP_ATTR #define EC_EDITLINE_CB_ATTR EC_DEPRECATED_MACRO("use EC_INTERACT_CB_ATTR") EC_INTERACT_CB_ATTR #define EC_EDITLINE_DESC_ATTR EC_DEPRECATED_MACRO("use EC_INTERACT_DESC_ATTR") EC_INTERACT_DESC_ATTR struct EC_DEPRECATED("use struct ec_interact_help") ec_editline_help { char *desc; char *help; }; typedef ec_interact_command_cb_t ec_editline_command_cb_t EC_DEPRECATED("use ec_interact_command_cb_t"); /* * Suppress deprecation warnings for function declarations that reference * deprecated types in their signatures. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" EC_DEPRECATED("use ec_interact_get_completions") ssize_t ec_editline_get_completions(const struct ec_comp *cmpl, char ***matches_out); EC_DEPRECATED("use ec_interact_free_completions") void ec_editline_free_completions(char **matches, size_t n); EC_DEPRECATED("use ec_interact_print_cols") int ec_editline_print_cols(struct ec_editline *editline, char const *const *matches, size_t n); EC_DEPRECATED("use ec_interact_append_chars") char *ec_editline_append_chars(const struct ec_comp *cmpl); EC_DEPRECATED("use ec_interact_get_helps") ssize_t ec_editline_get_helps( const struct ec_editline *editline, const char *line, struct ec_editline_help **helps_out ); EC_DEPRECATED("use ec_interact_print_helps") int ec_editline_print_helps( const struct ec_editline *editline, const struct ec_editline_help *helps, size_t n ); EC_DEPRECATED("use ec_interact_free_helps") void ec_editline_free_helps(struct ec_editline_help *helps, size_t n); EC_DEPRECATED("use ec_interact_get_error_helps") ssize_t ec_editline_get_error_helps( const struct ec_editline *editline, struct ec_editline_help **helps_out, size_t *char_idx ); EC_DEPRECATED("use ec_interact_print_error_helps") int ec_editline_print_error_helps( const struct ec_editline *editline, const struct ec_editline_help *helps, size_t n, size_t char_idx ); EC_DEPRECATED("use ec_interact_set_help") int ec_editline_set_help(struct ec_node *node, const char *help); EC_DEPRECATED("use ec_interact_set_callback") int ec_editline_set_callback(struct ec_node *node, ec_interact_command_cb_t cb); EC_DEPRECATED("use ec_interact_set_desc") int ec_editline_set_desc(struct ec_node *node, const char *desc); #pragma GCC diagnostic pop /** @endcond */ /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/htable.h��������������������������������������������������������������0000664�0000000�0000000�00000011761�15203622040�0017507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_htable Hash table * @{ * * @brief Simple hash table API (any key) * * This file provides functions to store objects in hash tables, * using arbitrary data as keys. */ #pragma once #include <stdbool.h> #include <stdint.h> #include <stdio.h> /** Callback function for freeing hash table elements. */ typedef void (*ec_htable_elt_free_t)(void *); /** Hash table with arbitrary key types. */ struct ec_htable; /** Hash table element reference for iteration. */ struct ec_htable_elt_ref; /** * Create a hash table. * * @return * The hash table, or NULL on error (errno is set). */ struct ec_htable *ec_htable(void); /** * Get a value from the hash table. * * @param htable * The hash table. * @param key * The key. * @param key_len * The key length. * @return * The element if it is found, or NULL on error (errno is set). * In case of success but the element is NULL, errno is set to 0. */ void *ec_htable_get(const struct ec_htable *htable, const void *key, size_t key_len); /** * Check if the hash table contains this key. * * @param htable * The hash table. * @param key * The key. * @param key_len * The key length. * @return * true if it contains the key, else false. */ bool ec_htable_has_key(const struct ec_htable *htable, const void *key, size_t key_len); /** * Delete an object from the hash table. * * @param htable * The hash table. * @param key * The key. * @param key_len * The key length. * @return * 0 on success, or -1 on error (errno is set). */ int ec_htable_del(struct ec_htable *htable, const void *key, size_t key_len); /** * Add/replace an object in the hash table. * * @param htable * The hash table. * @param key * The key. * @param key_len * The key length. * @param val * The pointer to be saved in the hash table. * @param free_cb * An optional pointer to a destructor function called when an * object is destroyed (ec_htable_del() or ec_htable_free()). * @return * 0 on success, or -1 on error (errno is set). * On error, the passed value is freed (free_cb(val) is called). */ int ec_htable_set( struct ec_htable *htable, const void *key, size_t key_len, void *val, ec_htable_elt_free_t free_cb ); /** * Free a hash table and all its objects. * * @param htable * The hash table. */ void ec_htable_free(struct ec_htable *htable); /** * Get the length of a hash table. * * @param htable * The hash table. * @return * The length of the hash table. */ size_t ec_htable_len(const struct ec_htable *htable); /** * Duplicate a hash table. * * A reference counter is shared between the clones of * hash tables so that the objects are freed only when * the last reference is destroyed. * * @param htable * The hash table. * @return * The duplicated hash table, or NULL on error (errno is set). */ struct ec_htable *ec_htable_dup(const struct ec_htable *htable); /** * Force a seed for the hash function. * This function must be called *before* ec_init(). * By default, a random value is determined during ec_init(). * * @param seed * The seed value. */ void ec_htable_force_seed(uint32_t seed); /** * Dump a hash table. * * @param out * The stream where the dump is sent. * @param htable * The hash table. */ void ec_htable_dump(FILE *out, const struct ec_htable *htable); /** * Iterate the elements in the hash table. * * The typical usage is as below: * * @code{.c} * for (iter = ec_htable_iter(htable); * iter != NULL; * iter = ec_htable_iter_next(iter)) { * printf(" %s: %p\n", * ec_htable_iter_get_key(iter), * ec_htable_iter_get_val(iter)); * } * @endcode * * @param htable * The hash table. * @return * An iterator element, or NULL if the htable is empty. */ struct ec_htable_elt_ref *ec_htable_iter(const struct ec_htable *htable); /** * Make the iterator point to the next element in the hash table. * * @param iter * The hash table iterator. * @return * An iterator element, or NULL if there is no more element. */ struct ec_htable_elt_ref *ec_htable_iter_next(struct ec_htable_elt_ref *iter); /** * Get the key of the current element. * * @param iter * The hash table iterator. * @return * The current element key, or NULL if the iterator points to an * invalid element. */ const void *ec_htable_iter_get_key(const struct ec_htable_elt_ref *iter); /** * Get the key length of the current element. * * @param iter * The hash table iterator. * @return * The current element key length, or 0 if the iterator points to an * invalid element. */ size_t ec_htable_iter_get_key_len(const struct ec_htable_elt_ref *iter); /** * Get the value of the current element. * * @param iter * The hash table iterator. * @return * The current element value, or NULL if the iterator points to an * invalid element. */ void *ec_htable_iter_get_val(const struct ec_htable_elt_ref *iter); /** @} */ ���������������libecoli-0.11.6/include/ecoli/init.h����������������������������������������������������������������0000664�0000000�0000000�00000004177�15203622040�0017216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_init Initialization * @{ * * @brief Library initialization and cleanup functions. */ #pragma once #include <sys/queue.h> /** * Register initialization and exit callbacks. These callbacks are * ordered by priority: for initialization, the lowest priority is called * first. For exit, the callbacks are invoked in reverse order. * * Priority policy: * 0 .. 99 : reserved for libecoli internal use. * 100 .. : available for user code (recommended). * * Do not use priorities < 100 for application code; internal libecoli * components may depend on those priorities and using them can lead to * uninitialized state, crashes, or undefined behavior. */ #define EC_INIT_REGISTER(t) \ static void ec_init_init_##t(void); \ static void __attribute__((constructor, used)) ec_init_init_##t(void) \ { \ ec_init_register(&t); \ } /** * Type of init function. Return 0 on success, -1 on error. */ typedef int(ec_init_t)(void); /** * Type of exit function. */ typedef void(ec_exit_t)(void); TAILQ_HEAD(ec_init_list, ec_init); /** * A structure describing initialization callbacks. */ struct ec_init { TAILQ_ENTRY(ec_init) next; /**< Next in list. */ ec_init_t *init; /**< Init function. */ ec_exit_t *exit; /**< Exit function. */ unsigned int priority; /**< Priority (0=first, 99=last) for internal */ }; /** * Register an initialization function. * * @param test * A pointer to an ::ec_init structure to be registered. */ void ec_init_register(struct ec_init *test); /** * Initialize ecoli library. * * Must be called before any other function from libecoli. * * @return * 0 on success, -1 on error (errno is set). */ int ec_init(void); /** * Uninitialize ecoli library. */ void ec_exit(void); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/interact.h������������������������������������������������������������0000664�0000000�0000000�00000015445�15203622040�0020064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_interact Interactive command line * @{ * * @brief Helpers for interactive command line (editline, readline, ...) */ #pragma once #include <stdbool.h> #include <stdio.h> #include "complete.h" struct ec_node; struct ec_pnode; struct ec_comp; /** * A structure describing a contextual help. */ struct ec_interact_help { char *desc; /**< The short description of the item. */ char *help; /**< The associated help. */ }; /** * The key of the node attribute storing the contextual help. */ #define EC_INTERACT_HELP_ATTR "_help" /** * The key of the node attribute storing the command callback. */ #define EC_INTERACT_CB_ATTR "_cb" /** * The key of the node attribute storing the short description. */ #define EC_INTERACT_DESC_ATTR "_desc" /** * Type of callback attached with ::EC_INTERACT_CB_ATTR attribute. */ typedef int (*ec_interact_command_cb_t)(const struct ec_pnode *); /** * Get completion matches as an array of strings. * * @param cmpl * The completions, as returned by ec_complete(). * @param matches_out * The pointer where the matches array will be returned. * @param type_mask * The mask of completion types to return (e.g., `EC_COMP_FULL | EC_COMP_PARTIAL`). * @return * The size of the array on success (>= 0), or -1 on error. */ ssize_t ec_interact_get_completions( const struct ec_comp *cmpl, char ***matches_out, enum ec_comp_type type_mask ); /** * Free the array of completion matches. * * @param matches * The array of matches. * @param n * The size of the array. */ void ec_interact_free_completions(char **matches, size_t n); /** * Print completion matches as columns. * * @param out * The pointer to the output stream. * @param width * The number of columns on terminal. * @param matches * The string array of matches to display. * @param n * The size of the array. * @return * 0 on success, or -1 on error. */ int ec_interact_print_cols(FILE *out, unsigned int width, char const *const *matches, size_t n); /** * Get characters to append to the line for a completion. * * @param cmpl * The completion object containing all the completion items. * @return * An allocated string to be appended to the current line (must be freed by * the caller using free()). This string can be empty if there is no * completion or if completions start with a different letter. Return NULL on * error. */ char *ec_interact_append_chars(const struct ec_comp *cmpl); /** * Get contextual helps from the current line. * * @param node * The pointer to the sh_lex grammar node. * @param line * The line from which to get help. * @param helps_out * The pointer where the helps array will be returned. * @return * The size of the array on success (>= 0), or -1 on error. */ ssize_t ec_interact_get_helps( const struct ec_node *node, const char *line, struct ec_interact_help **helps_out ); /** * Print helps generated with ec_interact_get_helps(). * * @param out * The pointer to the output stream. * @param width * The number of columns on terminal. * @param helps * The helps array returned by ec_interact_get_helps(). * @param n * The array size returned by ec_interact_get_helps(). * @return * 0 on success, or -1 on error. */ int ec_interact_print_helps( FILE *out, unsigned int width, const struct ec_interact_help *helps, size_t n ); /** * Free contextual helps. * * Free helps generated with ec_interact_get_helps() or * ec_interact_get_error_helps(). * * @param helps * The helps array. * @param n * The array size. */ void ec_interact_free_helps(struct ec_interact_help *helps, size_t n); /** * Get suggestions after a parsing error for the current line. * * @param node * The pointer to the sh_lex grammar node. * @param line * The line from which to get error help. * @param helps_out * The pointer where the helps array will be returned. * @param char_idx * A pointer to an integer where the index of the error in the line string * is returned. * @return * The size of the array on success (>= 0), or -1 on error. */ ssize_t ec_interact_get_error_helps( const struct ec_node *node, const char *line, struct ec_interact_help **helps_out, size_t *char_idx ); /** * Print suggestions generated with ec_interact_get_error_helps(). * * @param out * The pointer to the output stream. * @param width * The number of columns on terminal. * @param line * The line used to generate the error helps. * @param helps * The helps array returned by ec_interact_get_helps(). * @param n * The array size returned by ec_interact_get_helps(). * @param char_idx * The index of the error in the line string. * @return * 0 on success, or -1 on error. */ int ec_interact_print_error_helps( FILE *out, unsigned int width, const char *line, const struct ec_interact_help *helps, size_t n, size_t char_idx ); /** * Set help on a grammar node. * * Set the node attribute ::EC_INTERACT_HELP_ATTR on the node, containing the * given string. It is used by the ec_interact functions to display contextual * helps on completion or parsing error. * * @param node * The ec_node on which to add the help attribute. * @param help * The help string. * @return * 0 on success, or -1 on error. */ int ec_interact_set_help(struct ec_node *node, const char *help); /** * Set callback function on a grammar node. * * Set the node attribute ::EC_INTERACT_CB_ATTR on the node, containing the * pointer to a function invoked on successful parsing. * * @param node * The ec_node on which to add the callback attribute. * @param cb * The callback function pointer. * @return * 0 on success, or -1 on error. */ int ec_interact_set_callback(struct ec_node *node, ec_interact_command_cb_t cb); /** * Set short description of a grammar node. * * Set the node attribute ::EC_INTERACT_DESC_ATTR on the node, containing the * given string. It is used by the ec_interact functions to display the short * description of the contextual help, overriding the value provided by * ec_node_desc(). * * @param node * The ec_node on which to add the desc attribute. * @param desc * The short description string. * @return * 0 on success, or -1 on error. */ int ec_interact_set_desc(struct ec_node *node, const char *desc); /** * Get callback attached to a parse tree. * * This function browses the parse tree and try to find an attribute * ::EC_INTERACT_CB_ATTR attached to a grammar node referenced in the tree. * Return the value of this attribute, which is a function pointer. * * @param parse * The parsed tree. * @return * The function pointer used as command callback. */ ec_interact_command_cb_t ec_interact_get_callback(struct ec_pnode *parse); /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/log.h�����������������������������������������������������������������0000664�0000000�0000000�00000016011�15203622040�0017022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_log Log * @{ * * @brief Log API * * This file provides logging helpers: * - logging functions, supporting printf-like format * - several debug levels (similar to syslog) * - named log types * - redirection of logs to user functions (default logs nothing) */ #pragma once #include <stdarg.h> #include <sys/queue.h> #include <syslog.h> #include <ecoli/assert.h> /** * Log levels, from most critical to least critical. */ enum ec_log_level { EC_LOG_EMERG = 0, /**< system is unusable */ EC_LOG_ALERT = 1, /**< action must be taken immediately */ EC_LOG_CRIT = 2, /**< critical conditions */ EC_LOG_ERR = 3, /**< error conditions */ EC_LOG_WARNING = 4, /**< warning conditions */ EC_LOG_NOTICE = 5, /**< normal but significant condition */ EC_LOG_INFO = 6, /**< informational */ EC_LOG_DEBUG = 7, /**< debug-level messages */ }; /** * A structure describing a log type. * * It is defined as a static structure by the ::EC_LOG_TYPE_REGISTER() macro. */ struct ec_log_type { TAILQ_ENTRY(ec_log_type) next; /**< Next in list. */ const char *name; /**< Log type name. */ enum ec_log_level level; /**< The log level for this type. */ int id; /**< The log identifier. */ }; /** * Register a log type. * * This macro defines a function that will be called at startup (using * the "constructor" attribute). This function registers the named type * passed as argument, and sets a static global variable * `ec_log_local_type`. This variable is used as the default log type * for this file when using EC_LOG() or EC_VLOG(). * * This macro can be present several times in a file. In this case, the * local log type is set to the last registered type. * * On error, the function aborts. * * @param name * The name of the log to be registered. */ #define EC_LOG_TYPE_REGISTER(name) \ static struct ec_log_type name##_log_type = { \ .n##ame = #name, \ }; \ static int ec_log_local_type; \ __attribute__((constructor, used)) static void ec_log_register_##name(void) \ { \ ec_log_local_type = ec_log_type_register(&name##_log_type); \ ec_assert_print(ec_log_local_type >= 0, "cannot register log type.\n"); \ } /** * User log function type. * * It is advised that a user-defined log function drops all messages * that are less critical than ec_log_level_get(), as done by the * default handler. * * @param type * The log type identifier. * @param level * The log level. * @param opaque * The opaque pointer that was passed to ec_log_fct_register(). * @param str * The string to log. * @return * 0 on success, -1 on error (errno is set). */ typedef int (*ec_log_t)(int type, enum ec_log_level level, void *opaque, const char *str); /** * Register a user log function. * * @param usr_log * Function pointer that will be invoked for each log call. * If the parameter is NULL, ec_log_default_cb() is used. * @param opaque * Opaque pointer passed to the log function. * @return * 0 on success, -1 on error (errno is set). */ int ec_log_fct_register(ec_log_t usr_log, void *opaque); /** * Register a named log type. * * Register a new log type, which is identified by its name. The * function returns a log identifier associated with the log name. If the * name is already registered, the function just returns its identifier. * * @param type * The log type to register. * @return * The log type identifier on success (positive or zero), -1 on * error (errno is set). */ int ec_log_type_register(struct ec_log_type *type); /** * Return the log name associated with the log type identifier. * * @param type * The log type identifier. * @return * The name associated with the log type, or "unknown". It always returns * a valid string (never NULL). */ const char *ec_log_name(int type); /** * Log a formatted string. * * @param type * The log type identifier. * @param level * The log level. * @param format * The format string, followed by optional arguments. * @return * 0 on success, -1 on error (errno is set). */ int ec_log(int type, enum ec_log_level level, const char *format, ...) __attribute__((format(__printf__, 3, 4))); /** * Log a formatted string. * * @param type * The log type identifier. * @param level * The log level. * @param format * The format string. * @param ap * The list of arguments. * @return * 0 on success, -1 on error (errno is set). */ int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap); /** * Log a formatted string using the local log type. * * This macro requires that a log type is previously registered with * ::EC_LOG_TYPE_REGISTER() since it uses the `ec_log_local_type` * variable. * * @param level * The log level. * @param args * The format string, followed by optional arguments. * @return * 0 on success, -1 on error (errno is set). */ #define EC_LOG(level, args...) ec_log(ec_log_local_type, level, args) /** * Log a formatted string using the local log type. * * This macro requires that a log type is previously registered with * ::EC_LOG_TYPE_REGISTER() since it uses the `ec_log_local_type` * variable. * * @param level * The log level. * @param fmt * The format string. * @param ap * The list of arguments. * @return * 0 on success, -1 on error (errno is set). */ #define EC_VLOG(level, fmt, ap) ec_vlog(ec_log_local_type, level, fmt, ap) /** * Default log handler. * * This is the default log function that is used by the library. By * default, it prints all logs whose level is WARNING or more critical. * This level can be changed with ec_log_level_set(). * * @param type * The log type identifier. * @param level * The log level. * @param opaque * Unused. * @param str * The string to be logged. * @return * 0 on success, -1 on error (errno is set). */ int ec_log_default_cb(int type, enum ec_log_level level, void *opaque, const char *str); /** * Set the global log level. * * This level is used by the default log handler, ec_log_default_cb(). * All messages that are at least as critical as the default level are * displayed. * * @param level * The log level to be set. * @return * 0 on success, -1 on error. */ int ec_log_level_set(enum ec_log_level level); /** * Get the global log level. * * This level is used by the default log handler, ec_log_default_cb(). * All messages that are at least as critical as the default level are * displayed. * * @return * The current global log level */ enum ec_log_level ec_log_level_get(void); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/murmurhash.h����������������������������������������������������������0000664�0000000�0000000�00000002727�15203622040�0020445�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_murmurhash Murmurhash * @{ * * @brief Hash calculation using murmurhash algorithm * * MurmurHash3 is a hash implementation that was written by Austin Appleby, and * is placed in the public domain. The author hereby disclaims copyright to this * source code. */ #pragma once #include <stdint.h> /** Hash rotation */ static inline uint32_t ec_murmurhash_rotl32(uint32_t x, int8_t r) { return (x << r) | (x >> (32 - r)); } /** Add 32-bit to the hash */ static inline uint32_t ec_murmurhash3_add32(uint32_t h, uint32_t data) { data *= 0xcc9e2d51; data = ec_murmurhash_rotl32(data, 15); data *= 0x1b873593; h ^= data; return h; } /** Intermediate mix */ static inline uint32_t ec_murmurhash3_mix32(uint32_t h) { h = ec_murmurhash_rotl32(h, 13); h = h * 5 + 0xe6546b64; return h; } /** Final mix: force all bits of a hash block to avalanche */ static inline uint32_t ec_murmurhash3_fmix32(uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } /** * Calculate a 32-bit murmurhash3 * * @param key * The key (the unaligned variable-length array of bytes). * @param len * The length of the key, counting by bytes. * @param seed * Can be any 4-byte value initialization value. * @return * A 32-bit hash. */ uint32_t ec_murmurhash3(const void *key, int len, uint32_t seed); /** @} */ �����������������������������������������libecoli-0.11.6/include/ecoli/node.h����������������������������������������������������������������0000664�0000000�0000000�00000050416�15203622040�0017175�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_nodes Grammar nodes * @{ * * @brief Libecoli grammar nodes. * * The grammar node is a main structure of the ecoli library, used to define how * to match and complete the input tokens. A node is a generic object that * implements: * * - a `parse(node, input)` method: check if an input matches * - a `complete(node, input)` method: return possible completions for a given * input * - some other methods to initialize, free, ... * * One basic example is the string node (ec_node_str()). A node * `ec_node_str("foo")` will match any token list starting with `"foo"`, for * example: * * - `["foo"]` * - `["foo", "bar", ...]` * But will not match: * - `[]` * - `["bar", ...]` * * A node `ec_node_str("foo")` will complete with `"foo"` if the input contains * one token, with the same beginning as `"foo"`: * * - `[""]` * - `["f"]` * - `["fo"]` * - `["foo"]` * * But it will not complete: * * - `[]` * - `["bar"]` * - `["f", ""]` * - `["", "f"]` * * A node can have child nodes. For instance, a sequence node * `ec_node_seq(ec_node_str("foo"), ec_node_str("bar"))` will match a sequence: * `["foo", "bar"]`. * * Note: at some places in the documentation and the code, we may talk about the * grammar tree, but as loops are allowed, we should instead talk about grammar * graph. */ #pragma once #include <stdbool.h> #include <stdio.h> #include <sys/queue.h> #include <sys/types.h> /** * Node has no identifier. */ #define EC_NO_ID "" /** Grammar tree node. */ struct ec_node; /** An iterator on a grammar tree */ struct ec_node_iter; struct ec_pnode; struct ec_comp; struct ec_strvec; struct ec_dict; struct ec_config; struct ec_config_schema; /** * Register a node type at library load. * * The node type is registered in a function that has the * constructor attribute: the function is called at library load. * * @param t * The name of the ::ec_node_type structure variable. */ #define EC_NODE_TYPE_REGISTER(t) \ static void ec_node_init_##t(void); \ static void __attribute__((constructor, used)) ec_node_init_##t(void) \ { \ if (ec_node_type_register(&t, 0) < 0) \ fprintf(stderr, "cannot register node type %s\n", t.name); \ } /** * Register a node type at library load, overriding previous registration. * * The node type is registered in a function that has the * constructor attribute: the function is called at library load. * * Be careful when using this macro, as the last type with a given name * is the one that is actually registered. The call order may be hard to * predict, especially within the same binary. * * @param t * The name of the ::ec_node_type structure variable. */ #define EC_NODE_TYPE_REGISTER_OVERRIDE(t) \ static void ec_node_init_##t(void); \ static void __attribute__((constructor, used)) ec_node_init_##t(void) \ { \ if (ec_node_type_register(&t, 1) < 0) \ fprintf(stderr, "cannot register node type %s\n", t.name); \ } /** * Function type used to configure a node. * * The function pointer is not called directly, the helper ec_node_set_config() * should be used instead. * * The configuration passed to this function pointer is valid, i.e. * ec_config_validate() returned 0 on it. * * This function provided by a node type is supposed to do additional checks on * the configuration and store private data, if needed. If it returns 0, * ec_node_set_config() stores the generic configuration in the node. The * function can just return 0 if nothing needs to be stored in private data. * * @param node * The node to configure. * @param config * The configuration to apply to the node. * @return * 0 on success, negative on error (errno is set). */ typedef int (*ec_node_set_config_t)(struct ec_node *node, const struct ec_config *config); /** * Parse a string vector using the given grammar graph. * * The function pointer is not called directly, the helpers ec_parse(), * ec_parse_strvec() or ec_parse_child() should be used instead. * * The implementation of this method for a node that manages children * will call ec_parse_child(child, pstate, child_strvec). * * @param node * The grammar graph. * @param pstate * A pointer to the leaf being parsed in the parsing tree. It can be * used by a node to retrieve information from the current parsing * tree. To get the root of the tree, ec_pnode_get_root(pstate) should * be used. * @param strvec * The string vector to be parsed. * @return * On success, return the number of consumed items in the string vector * (can be 0) or ::EC_PARSE_NOMATCH if the node cannot parse the string * vector. On error, a negative value is returned and errno is set. */ typedef int (*ec_parse_t)( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ); /** * Get completion items using the given grammar graph. * * The function pointer should not be called directly, the helpers * ec_complete(), ec_complete_strvec() or ec_complete_child() should be * used instead. * * This function completes the last element of the string vector. * For instance, node.type->complete(node, comp, ["ls"]) will * list all commands that start with "ls", while * node.type->complete(node, comp, ["ls", ""]) will list all * possible values for the next argument. * * The implementation of this function in the node is supposed * to either: * - call ec_comp_add_item() for each completion item * that should be added to the list. This is typically done in * terminal nodes, for example in ec_node_str() or ec_node_file(). * - call ec_complete_child() to let * the children nodes add their own completion. This is the * case of ec_node_or which trivially calls ec_complete_child() * on all its children, and of ec_node_seq, which has to * do a more complex job (parsing strvec). * * A node that does not provide any method for the completion * will fall back to ec_complete_unknown(): this helper returns * a completion item of type ::EC_COMP_UNKNOWN, just to indicate * that everything before the last element of the string vector * has been parsed successfully, but we don't know how to * complete the last element. * * @param node * The root node of the grammar graph. * @param comp * The current list of completion items, to be filled by the * node.type->complete() method. * @param strvec * The string vector to be completed. * @return * 0 on success, or a negative value on error (errno is set). */ typedef int (*ec_complete_t)( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ); /** * Get the short description of a grammar node. * * This function pointer should not be called directly. The * ec_node_desc() helper should be used instead. * * This callback is typically used when building a help string for a * grammar graph. It is used in ecoli editline interface to generate * contextual help like this (first column): * * `<int>` An integer. * foo The foo string. * bar The bar string. * * If this callback is set to NULL in the node type, the * default behavior is to return the node type name inside `<>`, for * instance `<int>`. The string node type implements this method to * return the string value. An integer node could implement it * to return its range (e.g., "1..10"). * * The returned value is a pointer that must be freed by * the caller with free(). * * On error, NULL is returned and errno is set. */ typedef char *(*ec_node_desc_t)(const struct ec_node *); /** * Initialize the node private area. * * This function pointer should not be called directly. The ec_node() * and ec_node_from_type() helpers, that allocate new nodes, should be * used instead. * * If not NULL, this function is called when a node is instantiated, to * initialize the private area of a node. In any case, the private area * is first zeroed. * * On success, 0 is returned. On error, a negative value is returned and * errno is set. */ typedef int (*ec_node_init_priv_t)(struct ec_node *); /** * Free the node private area. * * This function pointer should not be called directly. The * ec_node_free() helper should be used instead. * * When a node is deleted, this function is called to free the resources * referenced in the node private area. */ typedef void (*ec_node_free_priv_t)(struct ec_node *); /** * Count the number of node children. * * This function pointer should not be called directly. The * ec_node_get_children_count() helper should be used instead. * * Some grammar nodes like seq, or, many, (...), reference children * nodes in the grammar graph. This function returns the number of * children. */ typedef size_t (*ec_node_get_children_count_t)(const struct ec_node *); /** * Get the i-th child node. * * This function pointer should not be called directly. The * ec_node_get_child() helper should be used instead. * * Some grammar nodes like seq, or, many, (...), reference children * nodes in the grammar graph. This function sets the i-th child (with i * lower than the return value of ec_node_get_children_count()) in the * child pointer. It also returns the number of references to the child * owned by the parent. This information is used by the algorithm that * frees a grammar graph taking care of loops. * * On success, 0 is returned. On error, a negative value is returned and * errno is set. */ typedef int (*ec_node_get_child_t)( const struct ec_node *, size_t i, struct ec_node **child, unsigned int *refs ); /** * A structure describing a grammar node type. * * It is usually defined as a static const structure in the code * defining a new grammar node type. Examples can be found in * `ecoli_node_<type>.c` files. */ struct ec_node_type { TAILQ_ENTRY(ec_node_type) next; /**< Next in list. */ const char *name; /**< Node type name. */ /** Configuration schema array, must be terminated by a sentinel * `{.type = EC_CONFIG_TYPE_NONE}`. */ const struct ec_config_schema *schema; size_t size; /**< Size of private area */ ec_node_set_config_t set_config; /**< Validate and set configuration. */ ec_parse_t parse; /**< Parse a string vector. */ ec_complete_t complete; /**< Get completion items. */ ec_node_desc_t desc; /**< Get short description. */ ec_node_init_priv_t init_priv; /**< Initialize private area. */ ec_node_free_priv_t free_priv; /**< Free node resources. */ /** Get children count. */ ec_node_get_children_count_t get_children_count; ec_node_get_child_t get_child; /**< Get the i-th child. */ }; /** * A list of node types. */ TAILQ_HEAD(ec_node_type_list, ec_node_type); /** * The list of registered node types. Must not be modified by user, except at * initialization using the EC_NODE_TYPE_REGISTER() or * EC_NODE_TYPE_REGISTER_OVERRIDE() macros. */ extern struct ec_node_type_list node_type_list; /** * Register a node type. * * The name of the type being registered is a unique identifier. However, * it is possible to force the registration of a type with an existing * name by setting "override" to true. Note that the initial type is not * removed from the list, instead the new one is added before in the * list. * * @param type * The node type to be registered. * @param override * Allow the registration of an existing type. * @return * 0 on success, negative value on error. */ int ec_node_type_register(struct ec_node_type *type, bool override); /** * Lookup node type by name. * * @param name * The name of the node type to search. * @return * The (read-only) node type if found, or NULL on error. */ const struct ec_node_type *ec_node_type_lookup(const char *name); /** * Dump registered node types. * * @param out * The stream where the dump is sent. */ void ec_node_type_dump(FILE *out); /** * Get the config schema of a node type. * * @param type * The node type. * @return * The (read-only) config schema of the node type, or NULL * if the node type has no config schema. */ const struct ec_config_schema *ec_node_type_schema(const struct ec_node_type *type); /** * Get the name of a node type. * * @param type * The node type. * @return * The (read-only) name of the node type. */ const char *ec_node_type_name(const struct ec_node_type *type); /** * Create a new node when the type is known. * * This function is typically called from the node constructor. * * @param type * The type of the node to create. * @param id * The node identifier. * @return * The new node on success, or NULL on error. */ struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *id); /** * Create a new node from its type name. * * This function is typically called from user code, for node types that do not * provide a specific constructor. * * @param typename * The type name of the node to create. * @param id * The node identifier. * @return * The new node on success, or NULL on error. */ struct ec_node *ec_node(const char *typename, const char *id); /** * Clone a grammar node. * * This function takes a reference to the node. If ec_node_free() is later * called on this node, it will decrease only the reference. The free is * effective when the reference count reaches 0. The reference counter is * initialized to 1 at node creation. * * @param node * The node to clone. * @return * The pointer to the cloned node, always equal to the parameter. It returns * NULL if the parameter was NULL: in this case, nothing is done. */ struct ec_node *ec_node_clone(struct ec_node *node); /** * Decrement node reference counter and free the node if it is the last * reference. * * @param node * The grammar node to free. */ void ec_node_free(struct ec_node *node); /** * Set node configuration. * * For a node that supports generic configuration, set its configuration. * * After a call to this function, the config is owned by the node and must not * be used by the caller anymore. * * @param node * The grammar node to configure. * @param config * The configuration to apply on the node. * @return * 0 on success, or a negative value on error (in this case the config passed * as parameter is freed). */ int ec_node_set_config(struct ec_node *node, struct ec_config *config); /** * Get the current node configuration. * * For a node that supports generic configuration, get its configuration. * * @param node * The grammar node. * @return * The generic node configuration on success, or NULL on error. */ const struct ec_config *ec_node_get_config(const struct ec_node *node); /** * Return the number of children for a node. * * @param node * The grammar node. * @return * The number of children. */ size_t ec_node_get_children_count(const struct ec_node *node); /** * Get the n-th child of a node. * * @param node * The grammar node. * @param i * The index of the child node, that must be lower than the number of children. * @param child * The pointer where the child node pointer will be stored on success. * @return * 0 on success, -1 on error. */ int ec_node_get_child(const struct ec_node *node, size_t i, struct ec_node **child); /** * Get the type of a node. * * @param node * The grammar node. * @return * The type structure of this node. */ const struct ec_node_type *ec_node_type(const struct ec_node *node); /** * Get the attributes dict of the node. * * A user can add any attribute to a node. The attributes keys starting with an * underscore are reserved. * * @param node * The grammar node. * @return * The attribute dictionary of this node. */ struct ec_dict *ec_node_attrs(const struct ec_node *node); /** * Get node identifier. * * @param node * The grammar node. * @return * The node identifier string. */ const char *ec_node_id(const struct ec_node *node); /** * Get node short description. * * @param node * The grammar node. * @return * An allocated string containing the node short description that must be * freed by the caller. Return NULL on error. */ char *ec_node_desc(const struct ec_node *node); /** * Dump a grammar tree. * * @param out * The stream where the dump is sent. * @param node * The grammar tree to dump. */ void ec_node_dump(FILE *out, const struct ec_node *node); /** * Find a node from its identifier string. * * Browse the tree using pre-order depth traversal, and return the first node * that matches the given identifier. * * @param node * The grammar tree. * @param id * The identifier to match. * @return * A node matching the identifier. */ struct ec_node *ec_node_find(struct ec_node *node, const char *id); /** * Create an iterator on a grammar tree. * * A grammar tree is actually not really a tree, it is an oriented graph where * loops can exist. For this reason, it is not possible to iterate the grammar * graph without storing somewhere the list of browsed nodes in order to break * loops. * * Another property of grammar graphs is that the same node can appear several * times at different places in the graph, having a different parent. This kind * of node will be iterated only once. * * The typical use is: * * @code{.c} * struct ec_node_iter *iter_root, *iter; * * iter_root = ec_node_iter(node); * if (iter_root == NULL) * goto fail; * * for (iter = iter_root; iter != NULL; * iter = ec_node_iter_next(iter_root, iter, true)) { * do_something_with(ec_node_iter_get_node(iter)); * } * * ec_node_iter_free(iter_root); * @endcode * * Technically, this function builds a tree from the grammar graph. Each node of * this tree references a node of the grammar graph. * * @param node * The grammar graph to iterate * @return * The grammar graph iterator on success, that must be freed using * ec_node_iter_free(). Return NULL on error. */ struct ec_node_iter *ec_node_iter(struct ec_node *node); /** * Iterate to the next node. * * See ec_node_iter(). * * @param root * The iterator returned by ec_node_iter(). * @param iter * The current node in the iterator. * @param iter_children * True to iterate the children of "iter", false to skip them. * @return * The next node of the iterator, or NULL if iteration is finished. */ struct ec_node_iter * ec_node_iter_next(struct ec_node_iter *root, struct ec_node_iter *iter, bool iter_children); /** * Free a grammar graph iterator. * * @param iter * The iterator returned by ec_node_iter() to free. */ void ec_node_iter_free(struct ec_node_iter *iter); /** * Get the grammar node referenced by the iterator node. * * @param iter * The iterator node. * @return * The associated grammar node. */ struct ec_node *ec_node_iter_get_node(struct ec_node_iter *iter); /** * Get the parent of an iterator node. * * @param iter * The iterator node. * @return * The parent of the iterator node in the iterator tree. Return NULL if it's * the root. */ struct ec_node_iter *ec_node_iter_get_parent(struct ec_node_iter *iter); /** * Check the type of a node. * * @param node * The grammar node. * @param type * The pointer to the type structure. * @return * 0 if the node is of specified type, else -1. */ int ec_node_check_type(const struct ec_node *node, const struct ec_node_type *type); /** * Get the type name of a grammar node. * * @param node * The grammar node. * @return * The type name of the node. */ const char *ec_node_get_type_name(const struct ec_node *node); /** * Get the pointer to the node private area. * * @param node * The grammar node. * @return * The pointer to the node private area. */ void *ec_node_priv(const struct ec_node *node); /** * Dump the node configuration schema. * * @param out * The stream where the dump is sent. * @param node * The grammar node. */ void ec_node_schema_dump(FILE *out, const struct ec_node *node); /** @} */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_any.h������������������������������������������������������������0000664�0000000�0000000�00000001233�15203622040�0020035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_any Any node * @ingroup ecoli_nodes * @{ * * @brief A node that matches any single token. */ #pragma once #include <ecoli/node.h> /** * Create a "any" node. * * This node always matches 1 string in the vector. * An optional strvec attribute can be checked too. These * attributes are usually set by a lexer node. * * @param id * The node identifier. * @param attr * The strvec attribute to match, or NULL. * @return * The ecoli node. */ struct ec_node *ec_node_any(const char *id, const char *attr); /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_bypass.h���������������������������������������������������������0000664�0000000�0000000�00000001151�15203622040�0020546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2019, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_bypass Bypass node * @ingroup ecoli_nodes * @{ * * @brief A pass-through node useful for building graph loops. */ #pragma once #include <ecoli/node.h> /** * A node that does nothing other than calling the child node. * It can be helpful to build loops in a node graph. */ struct ec_node *ec_node_bypass(const char *id, struct ec_node *node); /** * Attach a child to a bypass node. */ int ec_node_bypass_set_child(struct ec_node *gen_node, struct ec_node *child); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_cmd.h������������������������������������������������������������0000664�0000000�0000000�00000001373�15203622040�0020016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_cmd Command node * @ingroup ecoli_nodes * @{ * * @brief A node that parses commands using a format string. */ #pragma once #include <ecoli/node.h> /** * Create a command node from a format string. * * The format string describes a command grammar using a simple syntax. * Child nodes are passed as variadic arguments and referenced by name * in the format string. * * Example: * * @code{.c} * EC_NODE_CMD("mycmd", "show NAME", ec_node_str("NAME", NULL)); * @endcode */ #define EC_NODE_CMD(args...) __ec_node_cmd(args, EC_VA_END) struct ec_node *__ec_node_cmd(const char *id, const char *cmd_str, ...); /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_cond.h�����������������������������������������������������������0000664�0000000�0000000�00000001425�15203622040�0020174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2019, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_cond Condition node * @ingroup ecoli_nodes * @{ * * @brief A node that conditionally matches based on an expression. */ #pragma once struct ec_node; /** * Create a condition node. * * The condition node checks that an expression is true before * parsing/completing the child node. If it is false, the node * doesn't match anything. * * @param id * The node identifier. * @param cond_str * The condition string. This is a function-based expression. * @param child * The ecoli child node. * @return * The new ecoli cond node. */ struct ec_node *ec_node_cond(const char *id, const char *cond_str, struct ec_node *child); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_dynamic.h��������������������������������������������������������0000664�0000000�0000000�00000001325�15203622040�0020674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2017, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_dynamic Dynamic node * @ingroup ecoli_nodes * @{ * * @brief A node built dynamically at parse time by a callback. */ #pragma once struct ec_node; struct ec_pnode; /** Callback invoked by parse() or complete() to build the dynamic node. * The behavior of the node can depend on what is already parsed. */ typedef struct ec_node *(*ec_node_dynamic_build_t)(struct ec_pnode *pstate, void *opaque); /** * Dynamic node where parsing/validation is done in a user-provided callback. */ struct ec_node *ec_node_dynamic(const char *id, ec_node_dynamic_build_t build, void *opaque); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_dynlist.h��������������������������������������������������������0000664�0000000�0000000�00000004173�15203622040�0020742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_dynlist Dynamic list node * @ingroup ecoli_nodes * @{ * * @brief A node that matches names from a dynamic list. * * This node is able to parse a list of object names, returned by a user-defined * function as a string vector. * * Some flags can alter the behavior of parsing and completion: * - Match names returned by the user callback. * - Match names from a regular expression pattern. * - Don't match names returned by the user callback, even if it matches the regexp. */ #pragma once struct ec_node; struct ec_pnode; /** * Callback invoked by parse() or complete() to build the strvec containing the * list of object names. * * @param pstate * The current parsing state. * @param opaque * The user pointer passed at node creation. * @return * A string vector containing the list of object names. */ typedef struct ec_strvec *(*ec_node_dynlist_get_t)(struct ec_pnode *pstate, void *opaque); /** * Flags passed at ec_node_dynlist creation. */ enum ec_node_dynlist_flags { /** * Match names returned by the user callback. */ DYNLIST_MATCH_LIST = 1 << 0, /** * Match names from regexp pattern. */ DYNLIST_MATCH_REGEXP = 1 << 1, /** * Don't match names returned by the user callback, even if it matches * the regexp. */ DYNLIST_EXCLUDE_LIST = 1 << 2, }; /** * Create a dynlist node. * * The parsing and completion depend on a list returned by a user-provided * callback, a regular expression, and flags. * * @param id * The node identifier. * @param get * The function that returns the list of object names as a string vector. * @param opaque * A user pointer passed to the get function. * @param re_str * The regular expression defining the valid pattern for object names. * @param flags * Customize parsing and completion behavior. * @return * The dynlist grammar node, or NULL on error. */ struct ec_node *ec_node_dynlist( const char *id, ec_node_dynlist_get_t get, void *opaque, const char *re_str, enum ec_node_dynlist_flags flags ); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_empty.h����������������������������������������������������������0000664�0000000�0000000�00000000551�15203622040�0020406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_empty Empty node * @ingroup ecoli_nodes * @{ * * @brief A node that matches an empty input. */ #pragma once /** * This node always matches an empty string vector */ struct ec_node *ec_node_empty(const char *id); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_expr.h�����������������������������������������������������������0000664�0000000�0000000�00000015714�15203622040�0020235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_expr Expression node * @ingroup ecoli_nodes * @{ * * @brief A node for parsing and evaluating expressions with operators. */ #pragma once #include <ecoli/node.h> /** * Callback function type for evaluating a variable * * @param result * On success, this pointer must be set by the user to point * to a user structure describing the evaluated result. * @param userctx * A user-defined context passed to all callback functions, which * can be used to maintain a state or store global information. * @param var * The parse result referencing the variable. * @return * 0 on success (*result must be set), or -errno on error (*result * is undefined). */ typedef int (*ec_node_expr_eval_var_t)(void **result, void *userctx, const struct ec_pnode *var); /** * Callback function type for evaluating a prefix-operator * * @param result * On success, this pointer must be set by the user to point * to a user structure describing the evaluated result. * @param userctx * A user-defined context passed to all callback functions, which * can be used to maintain a state or store global information. * @param operand * The evaluated expression on which the operation should be applied. * @param operator * The parse result referencing the operator. * @return * 0 on success (*result must be set, operand is freed), * or -errno on error (*result is undefined, operand is not freed). */ typedef int (*ec_node_expr_eval_pre_op_t)( void **result, void *userctx, void *operand, const struct ec_pnode *operator ); /** * Callback function type for evaluating a postfix-operator. * * Same as ec_node_expr_eval_pre_op_t but for postfix operators. */ typedef int (*ec_node_expr_eval_post_op_t)( void **result, void *userctx, void *operand, const struct ec_pnode *operator ); /** * Callback function type for evaluating a binary operator. * * @param result * On success, this pointer must be set by the user to point * to a user structure describing the evaluated result. * @param userctx * A user-defined context passed to all callback functions. * @param operand1 * The evaluated left operand. * @param operator * The parse result referencing the operator. * @param operand2 * The evaluated right operand. * @return * 0 on success (*result must be set, operands are freed), * or -errno on error (*result is undefined, operands are not freed). */ typedef int (*ec_node_expr_eval_bin_op_t)( void **result, void *userctx, void *operand1, const struct ec_pnode *operator, void * operand2 ); /** * Callback function type for evaluating a parenthesized expression. * * @param result * On success, this pointer must be set by the user to point * to a user structure describing the evaluated result. * @param userctx * A user-defined context passed to all callback functions. * @param open_paren * The parse result referencing the opening parenthesis. * @param close_paren * The parse result referencing the closing parenthesis. * @param value * The evaluated expression inside the parentheses. * @return * 0 on success (*result must be set, value is freed), * or -errno on error (*result is undefined, value is not freed). */ typedef int (*ec_node_expr_eval_parenthesis_t)( void **result, void *userctx, const struct ec_pnode *open_paren, const struct ec_pnode *close_paren, void *value ); /** * Callback function type for freeing an evaluated result. * * @param result * The result to free. * @param userctx * A user-defined context passed to all callback functions. */ typedef void (*ec_node_expr_eval_free_t)(void *result, void *userctx); /** * Create an expression node. * * @param id * The node identifier. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_expr(const char *id); /** * Set the value (terminal) node for an expression. * * @param gen_node * The expression node. * @param val_node * The node matching values. It is consumed and will be freed when the * parent is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node); /** * Add a binary operator to an expression node. * * @param gen_node * The expression node. * @param op * The operator node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op); /** * Add a prefix operator to an expression node. * * @param gen_node * The expression node. * @param op * The operator node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op); /** * Add a postfix operator to an expression node. * * @param gen_node * The expression node. * @param op * The operator node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op); /** * Add parentheses to an expression node. * * @param gen_node * The expression node. * @param open * The opening parenthesis node. It is consumed and will be freed when * the parent is freed, or immediately on error. * @param close * The closing parenthesis node. It is consumed and will be freed when * the parent is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_add_parenthesis( struct ec_node *gen_node, struct ec_node *open, struct ec_node *close ); /** * Expression evaluation operations. */ struct ec_node_expr_eval_ops { ec_node_expr_eval_var_t eval_var; /**< Evaluate a variable. */ ec_node_expr_eval_pre_op_t eval_pre_op; /**< Evaluate a prefix operator. */ ec_node_expr_eval_post_op_t eval_post_op; /**< Evaluate a postfix operator. */ ec_node_expr_eval_bin_op_t eval_bin_op; /**< Evaluate a binary operator. */ ec_node_expr_eval_parenthesis_t eval_parenthesis; /**< Evaluate parentheses. */ ec_node_expr_eval_free_t eval_free; /**< Free an evaluated result. */ }; /** * Evaluate a parsed expression. * * @param result * On success, this pointer will be set to the evaluated result. * @param node * The expression node. * @param parse * The parse tree to evaluate. * @param ops * The evaluation callbacks. * @param userctx * A user-defined context passed to all callbacks. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_expr_eval( void **result, const struct ec_node *node, struct ec_pnode *parse, const struct ec_node_expr_eval_ops *ops, void *userctx ); /** @} */ ����������������������������������������������������libecoli-0.11.6/include/ecoli/node_file.h�����������������������������������������������������������0000664�0000000�0000000�00000001463�15203622040�0020172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_file File node * @ingroup ecoli_nodes * @{ * * @brief A node that matches and completes file paths. */ #pragma once #include <dirent.h> #include <sys/stat.h> #include <ecoli/node.h> /** @internal below functions pointers are only useful for test */ struct ec_node_file_ops { int (*lstat)(const char *pathname, struct stat *buf); DIR *(*opendir)(const char *name); struct dirent *(*readdir)(DIR *dirp); int (*closedir)(DIR *dirp); int (*dirfd)(DIR *dirp); int (*fstatat)(int dirfd, const char *pathname, struct stat *buf, int flags); }; /** * Set custom file operations for testing. * @internal */ void ec_node_file_set_ops(const struct ec_node_file_ops *ops); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_helper.h���������������������������������������������������������0000664�0000000�0000000�00000003245�15203622040�0020532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @addtogroup ecoli_config * @{ * * Helpers that are commonly used in nodes. */ #pragma once #include <stdarg.h> #include <stddef.h> #include <ecoli/config.h> struct ec_node; /** * Build a node table from a node list in an ::ec_config. * * The function takes a node configuration as parameter, which must be a * node list. From it, a node table is built. A reference is taken for * each node. * * On error, no reference is taken. * * @param config * The configuration (type must be a list of nodes). If it is * NULL, an error is returned. * @param len * The length of the allocated table on success, or 0 on error. * @return * The allocated node table, that must be freed by the caller: * each entry must be freed with ec_node_free() and the table * with free(). On error, NULL is returned and errno is set. */ struct ec_node **ec_node_config_node_list_to_table(const struct ec_config *config, size_t *len); /** * Build a list of config nodes from variable arguments. * * The va_list argument is a list of pointer to ec_node structures, * terminated with EC_VA_END. * * This helper is used by nodes that contain a list of nodes, * like "seq", "or", ... * * @param ap * List of pointer to ec_node structures, terminated with * EC_VA_END. * @return * A pointer to an ::ec_config structure. In this case, the * nodes will be freed when the config structure will be freed. * On error, NULL is returned (and errno is set), and the * nodes are freed. */ struct ec_config *ec_node_config_node_list_from_vargs(va_list ap); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_int.h������������������������������������������������������������0000664�0000000�0000000�00000003533�15203622040�0020045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_int Integer node * @ingroup ecoli_nodes * @{ * * @brief Nodes that match signed or unsigned integers. */ #pragma once #include <stdint.h> #include <ecoli/node.h> /** * Create a signed integer node. * * @param id * The node identifier. * @param min * The minimum valid value (included). * @param max * The maximum valid value (included). * @param base * The base to use for parsing. If 0, try to guess from prefix. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_int(const char *id, int64_t min, int64_t max, unsigned int base); /** * Get the value of a parsed signed integer. * * @param node * The integer node. * @param str * The string to parse. * @param result * Pointer where the result will be stored on success. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_int_getval(const struct ec_node *node, const char *str, int64_t *result); /** * Create an unsigned integer node. * * @param id * The node identifier. * @param min * The minimum valid value (included). * @param max * The maximum valid value (included). * @param base * The base to use for parsing. If 0, try to guess from prefix. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_uint(const char *id, uint64_t min, uint64_t max, unsigned int base); /** * Get the value of a parsed unsigned integer. * * @param node * The unsigned integer node. * @param str * The string to parse. * @param result * Pointer where the result will be stored on success. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_uint_getval(const struct ec_node *node, const char *str, uint64_t *result); /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_many.h�����������������������������������������������������������0000664�0000000�0000000�00000002502�15203622040�0020212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_many Many node * @ingroup ecoli_nodes * @{ * * @brief A node that matches its child multiple times. */ #pragma once /** * Create a many node that matches its child multiple times. * * @param id * The node identifier. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @param min * Minimum number of repetitions. Use 0 for no minimum. * @param max * Maximum number of repetitions. Use 0 for no maximum. * @return * The node, or NULL on error (errno is set). */ struct ec_node * ec_node_many(const char *id, struct ec_node *child, unsigned int min, unsigned int max); /** * Set the parameters of a many node. * * @param gen_node * The many node. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @param min * Minimum number of repetitions. Use 0 for no minimum. * @param max * Maximum number of repetitions. Use 0 for no maximum. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_many_set_params( struct ec_node *gen_node, struct ec_node *child, unsigned int min, unsigned int max ); /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_none.h�����������������������������������������������������������0000664�0000000�0000000�00000000374�15203622040�0020212�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_none None node * @ingroup ecoli_nodes * @{ * * @brief A node that never matches anything. */ #pragma once /** @} */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_once.h�����������������������������������������������������������0000664�0000000�0000000�00000001617�15203622040�0020200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_once Once node * @ingroup ecoli_nodes * @{ * * @brief A node that prevents its child from being parsed more than once. */ #pragma once #include <ecoli/node.h> /** * This node behaves like its child, but prevents it from being parsed more than once. * * Example: * * @code{.c} * many( * or( * once(str("foo")), * str("bar"))) * @endcode * * Matches: `[]`, `["foo", "bar"]`, `["bar", "bar"]`, `["foo", "bar", "bar"]`, ... * But not: `["foo", "foo"]`, `["foo", "bar", "foo"]`, ... * * On error, the child is not freed. */ struct ec_node *ec_node_once(const char *id, struct ec_node *child); /** Set the child of a once node. On error, the child is freed. */ int ec_node_once_set_child(struct ec_node *node, struct ec_node *child); /** @} */ �����������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_option.h���������������������������������������������������������0000664�0000000�0000000�00000001744�15203622040�0020565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_option Option node * @ingroup ecoli_nodes * @{ * * @brief A node that makes its child optional. */ #pragma once #include <ecoli/node.h> /** * Create an option node that makes its child optional. * * @param id * The node identifier. * @param node * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_option(const char *id, struct ec_node *node); /** * Set the child of an option node. * * @param gen_node * The option node. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_option_set_child(struct ec_node *gen_node, struct ec_node *child); /** @} */ ����������������������������libecoli-0.11.6/include/ecoli/node_or.h�������������������������������������������������������������0000664�0000000�0000000�00000001736�15203622040�0017676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_or Or node * @ingroup ecoli_nodes * @{ * * @brief A node that matches one of its child nodes. */ #pragma once #include <ecoli/node.h> #include <ecoli/utils.h> /** * Create a new "or" node from an arbitrary list of child nodes. * All nodes given in the list will be freed when freeing this one, * or immediately on error. */ #define EC_NODE_OR(args...) __ec_node_or(args, EC_VA_END) /** * Avoid using this function directly, prefer the macro EC_NODE_OR() or * ec_node_or() + ec_node_or_add() */ struct ec_node *__ec_node_or(const char *id, ...); /** * Create an empty "or" node. */ struct ec_node *ec_node_or(const char *id); /** * Add a child to an "or" node. * * The child is consumed and will be freed when the parent is freed, * or immediately on error. */ int ec_node_or_add(struct ec_node *node, struct ec_node *child); /** @} */ ����������������������������������libecoli-0.11.6/include/ecoli/node_re.h�������������������������������������������������������������0000664�0000000�0000000�00000001553�15203622040�0017661�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_re Regex node * @ingroup ecoli_nodes * @{ * * @brief A node that matches input against a regular expression. */ #pragma once #include <ecoli/node.h> /** * Create a regular expression node. * * @param id * The node identifier. * @param str * The regular expression pattern (POSIX extended regex). * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_re(const char *id, const char *str); /** * Set the regular expression on a regex node. * * @param node * The regex node. * @param re * The regular expression pattern. It is duplicated internally. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_re_set_regexp(struct ec_node *node, const char *re); /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_re_lex.h���������������������������������������������������������0000664�0000000�0000000�00000002456�15203622040�0020534�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_re_lex Regex lexer node * @ingroup ecoli_nodes * @{ * * @brief A lexer node using regular expressions for tokenization. */ #pragma once #include <ecoli/node.h> /** * Create a regex-based lexer node. * * This node tokenizes the input using regular expressions added with * ec_node_re_lex_add() and passes the resulting tokens to the child node. * * @param id * The node identifier. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child); /** * Add a token pattern to a regex lexer node. * * @param gen_node * The regex lexer node. * @param pattern * The regular expression pattern for matching tokens. * @param keep * If non-zero, include matched tokens in the output; if zero, discard them. * @param attr_name * Optional attribute name to attach to matched tokens, or NULL. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_re_lex_add( struct ec_node *gen_node, const char *pattern, int keep, const char *attr_name ); /** @} */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_seq.h������������������������������������������������������������0000664�0000000�0000000�00000002742�15203622040�0020044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_seq Sequence node * @ingroup ecoli_nodes * @{ * * @brief A node that matches a sequence of child nodes in order. */ #pragma once #include <ecoli/node.h> /** * Create a sequence node from a list of child nodes. * * All child nodes passed as arguments are consumed and will be freed * when the sequence node is freed, or immediately on error. * * Example: * * @code{.c} * EC_NODE_SEQ("myseq", child1, child2, child3) * @endcode */ #define EC_NODE_SEQ(args...) __ec_node_seq(args, EC_VA_END) /* list must be terminated with EC_VA_END */ /* all nodes given in the list will be freed when freeing this one */ /* avoid using this function directly, prefer the macro EC_NODE_SEQ() or * ec_node_seq() + ec_node_seq_add() */ struct ec_node *__ec_node_seq(const char *id, ...); /** * Create an empty sequence node. * * Use ec_node_seq_add() to add children. * * @param id * The node identifier. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_seq(const char *id); /** * Add a child to a sequence node. * * @param node * The sequence node. * @param child * The child node to add. It is consumed and will be freed when the * parent is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_seq_add(struct ec_node *node, struct ec_node *child); /** @} */ ������������������������������libecoli-0.11.6/include/ecoli/node_sh_lex.h���������������������������������������������������������0000664�0000000�0000000�00000002320�15203622040�0020526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_sh_lex Shell lexer node * @ingroup ecoli_nodes * @{ * * @brief A lexer node using shell-like tokenization rules. */ #pragma once #include <ecoli/node.h> /** * Create a shell lexer node. * * This node tokenizes the input using shell-like lexing rules (handling * quotes, escapes, etc.) and passes the resulting tokens to the child node. * * @param id * The node identifier. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child); /** * Create a shell lexer node with variable expansion. * * Same as ec_node_sh_lex() but with shell variable expansion enabled. * * @param id * The node identifier. * @param child * The child node. It is consumed and will be freed when the parent * is freed, or immediately on error. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_sh_lex_expand(const char *id, struct ec_node *child); /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_space.h����������������������������������������������������������0000664�0000000�0000000�00000000673�15203622040�0020350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_space Space node * @ingroup ecoli_nodes * @{ * * @brief A node that matches whitespace. * * This node matches one string in the vector if it is only composed of * spaces, as interpreted by isspace(). */ #pragma once /* no API for now, since there is no specific configuration for this node */ /** @} */ ���������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_str.h������������������������������������������������������������0000664�0000000�0000000�00000001502�15203622040�0020055�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_str String node * @ingroup ecoli_nodes * @{ * * @brief A node that matches a specific string. */ #pragma once #include <ecoli/node.h> /** * Create a string node that matches a specific string. * * @param id * The node identifier. * @param str * The string to match. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_str(const char *id, const char *str); /** * Set the string to match on a string node. * * @param node * The string node. * @param str * The string to match. It is duplicated internally. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_str_set_str(struct ec_node *node, const char *str); /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/node_subset.h���������������������������������������������������������0000664�0000000�0000000�00000005171�15203622040�0020560�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_node_subset Subset node * @ingroup ecoli_nodes * @{ * * @brief A node that matches any subset of its children in any order. */ #pragma once #include <ecoli/node.h> /** * Create a subset node from a list of child nodes. * * A subset node matches any permutation of a subset of its children. * All child nodes passed as arguments are consumed and will be freed * when the subset node is freed, or immediately on error. * * Example: * * @code{.c} * EC_NODE_SUBSET("mysubset", child1, child2, child3) * @endcode */ #define EC_NODE_SUBSET(args...) __ec_node_subset(args, EC_VA_END) /* list must be terminated with EC_VA_END */ /* all nodes given in the list will be freed when freeing this one */ /* avoid using this function directly, prefer the macro EC_NODE_SUBSET() or * ec_node_subset() + ec_node_subset_add() */ struct ec_node *__ec_node_subset(const char *id, ...); /** * Create an empty subset node. * * Use ec_node_subset_add() to add children. * * @param id * The node identifier. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_subset(const char *id); /** * Create an empty subset node with a minimum number of children that must match. * * Use ec_node_subset_add() to add children. * * @param id * The node identifier. * @param min * The minimum number of children that must match. * @return * The node, or NULL on error (errno is set). */ struct ec_node *ec_node_subset_min(const char *id, unsigned int min); /** * Add a child to a subset node. * * @param node * The subset node. * @param child * The child node to add. It is consumed and will be freed when the * parent is freed, or immediately on error. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_subset_add(struct ec_node *node, struct ec_node *child); /** * Set the minimum number of children that must match. * * By default, a subset node accepts zero matching children. Use this * function to require a minimum number of children to match for the * parse to succeed. * * @param node * The subset node. * @param min * The minimum number of children that must match. * @return * 0 on success, -1 on error (errno is set). */ int ec_node_subset_set_min(struct ec_node *node, unsigned int min); /** * Get the minimum number of children that must match. * * @param node * The subset node. * @return * The minimum number, or 0 if the node is not a subset. */ unsigned int ec_node_subset_get_min(const struct ec_node *node); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/parse.h���������������������������������������������������������������0000664�0000000�0000000�00000033000�15203622040�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_parse Parse nodes * @{ * * @brief Create parse tree from string input and grammar graph * * Node parse API. * * The parse operation is to check if an input (a string or vector of * strings) matches the node tree. On success, the result is stored in a * tree that describes which part of the input matches which node. * * The parsing tree is sometimes referenced by another node than the * root node. Use ::ec_pnode_get_root() to get the root node in that case. */ #pragma once #include <limits.h> #include <stdbool.h> #include <stdio.h> #include <sys/queue.h> #include <sys/types.h> struct ec_node; /** Parse tree node. */ struct ec_pnode; /** * Create an empty parsing tree. * * This function is used internally when parsing an input using * a grammar tree. * * @param node * The grammar node. * @return * The empty parse tree. */ struct ec_pnode *ec_pnode(const struct ec_node *node); /** * Free a parsing tree. * * @param pnode * The root of the parsing tree to be freed. It must not have * any parent. */ void ec_pnode_free(struct ec_pnode *pnode); /** * Remove and free all the children of a parsing tree node. * * @param pnode * Node whose children will be freed. */ void ec_pnode_free_children(struct ec_pnode *pnode); /** * Duplicate a parsing tree. * * @param pnode * A node inside a parsing tree. * @return * A pointer to the copy of the input node, at the same place in the * copy of the parsing tree. Return NULL on error (errno is set). */ struct ec_pnode *ec_pnode_dup(const struct ec_pnode *pnode); /** * Get the string vector associated with a parsing node. * * When an input is parsed successfully (i.e., the input string vector * matches the grammar tree), the matching string vector is copied * inside the associated parsing node. * * For instance, parsing the input `["foo", "bar"]` on a grammar which is * a sequence of strings, the attached string vector will be `["foo", * "bar"]` on the root pnode, `["foo"]` on the first leaf, and `["bar"]` on * the second leaf. * * If the parsing tree does not match (see ec_pnode_matches()), * the associated string vector is NULL. * * @param pnode * The parsing node. If NULL, the function returns NULL. * @return * The string vector associated with the parsing node, or NULL * if the node is not yet parsed (this happens when building the * parsing tree), or if the parsing tree does not match the * input. */ const struct ec_strvec *ec_pnode_get_strvec(const struct ec_pnode *pnode); /** * Parse a string using a grammar tree. * * This is equivalent to calling ec_parse_strvec() on the same * node, with a string vector containing only the argument string str. * * @param node * The grammar node. * @param str * The input string. * @return * A parsing tree, or NULL on error (errno is set). */ struct ec_pnode *ec_parse(const struct ec_node *node, const char *str); /** * Parse a string vector using a grammar tree. * * Generate a parsing tree by parsing the input string vector using the * given grammar tree. * * The parsing tree is composed of ::ec_pnode, and each of them is * associated with a ::ec_node (the grammar node), to the string vector * that matched the subtree, and to optional attributes. * * When the input matches the grammar tree, the string vector associated * to the root node of the returned parsing tree is the same than the * strvec argument. Calling ec_pnode_matches() on this tree returns true. * * If the input does not match the grammar tree, the returned parsing * tree only contains one root node, with no associated string vector. * Calling ec_pnode_matches() on this tree returns false. * * @param node * The grammar node. * @param strvec * The input string vector. * @return * A parsing tree, or NULL on error (errno is set). */ struct ec_pnode *ec_parse_strvec(const struct ec_node *node, const struct ec_strvec *strvec); /** * Return value of ec_parse_child() when input does not match grammar. */ #define EC_PARSE_NOMATCH INT_MAX /** * Parse a string vector using a grammar tree, from a parent node. * * This function is usually called from an intermediate node (like * ec_node_seq(), ec_node_or(), ...) to backfill the parsing tree, which is * built piece by piece while browsing the grammar tree. * * ec_parse_child() creates a new child in this parsing tree, and calls * the parse() method for the child node, with pstate pointing to this * new child. If it does not match, the child is removed in the state, * else it is kept, with its possible descendants. * * @param node * The grammar node. * @param pstate * The node of the parsing tree. * @param strvec * The input string vector. * @return * On success: the number of matched elements in the input string * vector (which can be 0), or ::EC_PARSE_NOMATCH (which is a positive * value) if the input does not match the grammar. On error, -1 is * returned, and errno is set. */ int ec_parse_child( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ); /** * Link a parsing node to a parsing tree. * * Add a child to a node in a parsing tree, at the end of the children * list. * * @param pnode * The node of the parsing tree where the child is added. * @param child * The node (or subtree) to add in the children list. */ void ec_pnode_link_child(struct ec_pnode *pnode, struct ec_pnode *child); /** * Remove a child node from parsing tree. * * Remove a child node (and its children) from the children list its * parent node in the parsing tree. The child node is not freed. * * @param child * The node (or subtree) to remove. */ void ec_pnode_unlink_child(struct ec_pnode *child); /** * Get the root of the parsing tree. * * Get the root of the parsing tree, keeping the const statement * if the argument has it. * * @param parse * A node in the parsing tree. * @return * The root of the parsing tree. */ #define EC_PNODE_GET_ROOT(parse) \ ({ \ const struct ec_pnode *p_ = parse; /* check type */ \ struct ec_pnode *pnode_ = (struct ec_pnode *)parse; \ __typeof__(parse) res_; \ (void)p_; \ res_ = ec_pnode_get_root(pnode_); \ res_; \ }) /** * Get the root of the parsing tree. * * You may also use EC_PNODE_GET_ROOT() instead, that keeps the * const statement. * * @param pnode * A node in the parsing tree. * @return * The root of the parsing tree. */ struct ec_pnode *ec_pnode_get_root(struct ec_pnode *pnode); /** * Get the parent node in the parsing tree. * * @param pnode * A node in the parsing tree. * @return * The parent node, or NULL if it is the root. */ struct ec_pnode *ec_pnode_get_parent(const struct ec_pnode *pnode); /** * Get the first child of a node in the parsing tree. * * @param pnode * A node in the parsing tree. * @return * The first child node, or NULL if it has no child. */ struct ec_pnode *ec_pnode_get_first_child(const struct ec_pnode *pnode); /** * Get the last child of a node in the parsing tree. * * @param pnode * A node in the parsing tree. * @return * The last child node, or NULL if it has no child. */ struct ec_pnode *ec_pnode_get_last_child(const struct ec_pnode *pnode); /** * Get the next sibling node. * * If pnode is the root of the parsing tree, return NULL. Otherwise, return * the next sibling node. * * @param pnode * A node in the parsing tree. * @return * The next sibling, or NULL if there is no sibling. */ struct ec_pnode *ec_pnode_next(const struct ec_pnode *pnode); /** * Iterate the children of a node. * * @param child * The item that will be set at each iteration. * @param pnode * The parent node in the parsing tree. */ #define EC_PNODE_FOREACH_CHILD(child, pnode) \ for (child = ec_pnode_get_first_child(pnode); child != NULL; child = ec_pnode_next(child)) /** * Get the grammar node corresponding to the parsing node. * * @param pnode * A node in the parsing tree. * @return * The corresponding grammar node, that issued the parse. */ const struct ec_node *ec_pnode_get_node(const struct ec_pnode *pnode); /** * Unlink and free the last child. * * Shortcut to unlink and free the last child of a node. It is a quite * common operation in intermediate nodes (like ec_node_seq(), * ec_node_many(), ...) to remove a subtree that was temporarily added * during the completion process. * * @param pnode * A node in the parsing tree which has at least one child. */ void ec_pnode_del_last_child(struct ec_pnode *pnode); /** * Get attributes associated with a node in a parsing tree. * * Attributes are key/values that are stored in a dictionary * and attached to a node in the parsing tree. An attribute can be * added to a node by the parsing or completion method of an ec_node. * * @param pnode * A node in the parsing tree. * @return * The dictionary containing the attributes. */ struct ec_dict *ec_pnode_get_attrs(const struct ec_pnode *pnode); /** * Dump a parsing tree. * * @param out * The stream where the dump is done. * @param pnode * The parsing tree to dump. */ void ec_pnode_dump(FILE *out, const struct ec_pnode *pnode); /** * Find a node from its identifier. * * Find the first node in the parsing tree which has the given * node identifier. The search is a depth-first search. * * @param root * The node of the parsing tree where the search starts. * @param id * The node identifier string to match. * @return * The first node matching the identifier, or NULL if not found. */ const struct ec_pnode *ec_pnode_find(const struct ec_pnode *root, const char *id); /** * Find the next node matching an identifier. * * After a successful call to ec_pnode_find() or ec_pnode_find_next(), it * is possible to get the next node that has the specified id. There are * 2 options: * - continue the depth-first search where it was interrupted. * - skip the children of the current node, and continue the depth-first * search. * * @param root * The root of the search, as passed to ec_pnode_find(). * @param prev * The node returned by the previous search. * @param id * The node identifier string to match. * @param iter_children * True to iterate the children of "prev", false to skip them. * @return * The next node matching the identifier, or NULL if not found. */ const struct ec_pnode *ec_pnode_find_next( const struct ec_pnode *root, const struct ec_pnode *prev, const char *id, bool iter_children ); /** * Count node occurrences in a parse subtree. * * @param root * The node of the parsing tree where the search starts. * @param id * The node identifier string to match. * @return * The number of nodes matching the identifier. */ unsigned int ec_pnode_count(const struct ec_pnode *root, const char *id); /** * Iterate over a parse tree * * Use it with: * * @code{.c} * for (iter = pnode; iter != NULL; iter = EC_PNODE_ITER_NEXT(pnode, iter, 1)) * @endcode */ struct ec_pnode * __ec_pnode_iter_next(const struct ec_pnode *root, struct ec_pnode *pnode, bool iter_children); /** * Get the next node in a parse tree iteration. * * @param root * The root of the parse tree being iterated. * @param parse * The current parse node. * @param iter_children * If true, iterate over children; if false, skip to siblings. * @return * The next parse node, or NULL if iteration is complete. */ #define EC_PNODE_ITER_NEXT(root, parse, iter_children) \ ({ \ const struct ec_pnode *p_ = parse; /* check type */ \ struct ec_pnode *pnode_ = (struct ec_pnode *)parse; \ __typeof__(parse) res_; \ (void)p_; \ res_ = __ec_pnode_iter_next(root, pnode_, iter_children); \ res_; \ }) /** * Iterate depth-first over a parsing tree. * * @param iter * The item that will be set at each iteration. * @param root * The root of the parsing tree to iterate. */ #define EC_PNODE_FOREACH(iter, root) \ for ((iter) = (root); (iter) != NULL; (iter) = EC_PNODE_ITER_NEXT((root), (iter), true)) /** * Get the number of strings in the parsed string vector. * * @param pnode * A node in the parsing tree. * @return * The number of strings in the parsed string vector, or 0 if the * node is NULL or has no string vector. */ size_t ec_pnode_len(const struct ec_pnode *pnode); /** * Check if the parsing tree matches the input. * * @param pnode * A node in the parsing tree. * @return * true if the parsing tree matches (has a string vector), false otherwise. */ bool ec_pnode_matches(const struct ec_pnode *pnode); /** @} */ libecoli-0.11.6/include/ecoli/string.h��������������������������������������������������������������0000664�0000000�0000000�00000005031�15203622040�0017547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_string String manipulation * @{ * * @brief Helpers for strings manipulation. */ #pragma once #include <stdarg.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> /** Count the number of identical characters at the beginning of two strings. */ size_t ec_strcmp_count(const char *s1, const char *s2); /** Return 1 if the string starts with the given prefix. */ int ec_str_startswith(const char *s, const char *beginning); /** Return true if the string contains only whitespace characters. */ bool ec_str_is_space(const char *s); /** * Parse a string for a signed integer. * * @param str * The string to parse. * @param base * The base (0 means "guess"). * @param min * The minimum allowed value. * @param max * The maximum allowed value. * @param val * The pointer to the value to be set on success. * @return * On success, return 0. Otherwise, return -1 and set errno. */ int ec_str_parse_llint(const char *str, unsigned int base, int64_t min, int64_t max, int64_t *val); /** * Parse a string for an unsigned integer. * * @param str * The string to parse. * @param base * The base (0 means "guess"). * @param min * The minimum allowed value. * @param max * The maximum allowed value. * @param val * The pointer to the value to be set on success. * @return * On success, return 0. Otherwise, return -1 and set errno. */ int ec_str_parse_ullint( const char *str, unsigned int base, uint64_t min, uint64_t max, uint64_t *val ); /** * Quote a string, escaping nested quotes. * * @param str * The string to quote. * @param quote * The quote character to use: usually " or ' but can be anything. If 0, * select between " or ' automatically. * @param force * If true, always add quotes, else add them only if the string contains * spaces or quotes. * @return * An allocated string, that must be freed by the caller using free(). */ char *ec_str_quote(const char *str, char quote, bool force); /** * Wrap a text to a maximum number of columns. * * @param str * The input text. * @param max_cols * The maximum number of columns. * @param start_off * The number of already consumed columns on the first line, filled with * padding in other lines. * @return * An allocated string containing the wrapped text. It must be freed by the * user using free(). */ char *ec_str_wrap(const char *str, size_t max_cols, size_t start_off); /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/strvec.h��������������������������������������������������������������0000664�0000000�0000000�00000015032�15203622040�0017551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_strvec String vector * @{ * * @brief Helpers for strings vectors manipulation. * * The ::ec_strvec API provides helpers to manipulate string vectors. * When duplicating vectors, the strings are not duplicated in memory, * a reference counter is used. */ #pragma once #include <stdio.h> /** String vector */ struct ec_strvec; /** * Allocate a new empty string vector. * * @return * The new strvec object, or NULL on error (errno is set). */ struct ec_strvec *ec_strvec(void); /** * Allocate a new string vector. * * The string vector is initialized with the list of const strings * passed as arguments. * * @return * The new strvec object, or NULL on error (errno is set). */ #define EC_STRVEC(args...) \ ({ \ const char *_arr[] = {args}; \ ec_strvec_from_array(_arr, EC_COUNT_OF(_arr)); \ }) /** * Allocate a new string vector. * * The string vector is initialized with the array of const strings * passed as arguments. * * @param strarr * The array of const strings. * @param n * The number of strings in the array. * @return * The new strvec object, or NULL on error (errno is set). */ struct ec_strvec *ec_strvec_from_array(const char *const *strarr, size_t n); /** * Options for ec_strvec_sh_lex_str(). */ typedef enum { /** * Fail if a quote is not closed properly or if the provided * string ends with an unterminated escape sequence. */ EC_STRVEC_STRICT = 0x1, /** * If there is trailing white space, add an empty element to the * output string vector. */ EC_STRVEC_TRAILSP = 0x2, } ec_strvec_flag_t; /** * All elements of the vector returned by ec_strvec_sh_lex_str() have two * attributes accessible via ec_strvec_get_attrs(). */ /** The start index in the original line passed to ec_strvec_sh_lex_str(). */ #define EC_STRVEC_ATTR_START "start" /** The end index in the original line passed to ec_strvec_sh_lex_str(). */ #define EC_STRVEC_ATTR_END "end" /** * Split a string into multiple tokens following basic shell lexing rules. * * @param str * The string to split. * @param flags * Options for controlling behavior. * @param unclosed_quote * If not NULL and if the provided string has an unterminated quote. The * opening quote will be stored here. * * @return * The new strvec object, or NULL on error (errno is set). */ struct ec_strvec * ec_strvec_sh_lex_str(const char *str, ec_strvec_flag_t flags, char *unclosed_quote); /** * Set a string in the vector at specified index. * * @param strvec * The pointer to the string vector. * @param idx * The index of the string to set. * @param s * The string to be set. * @return * 0 on success or -1 on error (errno is set). */ int ec_strvec_set(struct ec_strvec *strvec, size_t idx, const char *s); /** * Add a string in a vector. * * @param strvec * The pointer to the string vector. * @param s * The string to be added at the end of the vector. * @return * 0 on success or -1 on error (errno is set). */ int ec_strvec_add(struct ec_strvec *strvec, const char *s); /** * Delete the last entry in the string vector. * * @param strvec * The pointer to the string vector. * @return * 0 on success or -1 on error (errno is set). */ int ec_strvec_del_last(struct ec_strvec *strvec); /** * Duplicate a string vector. * * Attributes are duplicated if any. * * @param strvec * The pointer to the string vector. * @return * The duplicated strvec object, or NULL on error (errno is set). */ struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec); /** * Duplicate a part of a string vector. * * Attributes are duplicated if any. * * @param strvec * The pointer to the string vector. * @param off * The index of the first string to duplicate. * @param len * The number of strings to duplicate. * @return * The duplicated strvec object, or NULL on error (errno is set). */ struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off, size_t len); /** * Free a string vector. * * @param strvec * The pointer to the string vector. */ void ec_strvec_free(struct ec_strvec *strvec); /** * Get the length of a string vector. * * @param strvec * The pointer to the string vector. * @return * The length of the vector. */ size_t ec_strvec_len(const struct ec_strvec *strvec); /** * Get a string element from a vector. * * @param strvec * The pointer to the string vector. * @param idx * The index of the string to get. * @return * The string stored at given index, or NULL on error (strvec is NULL * or invalid index). */ const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx); /** * Get the attributes of a vector element. * * @param strvec * The pointer to the string vector. * @param idx * The index of the string to get. * @return * The read-only attributes (dictionary) of the string at specified * index, or NULL if there is no attribute. */ const struct ec_dict *ec_strvec_get_attrs(const struct ec_strvec *strvec, size_t idx); /** * Set the attributes of a vector element. * * @param strvec * The pointer to the string vector. * @param idx * The index of the string to get. * @param attrs * The attributes to be set. * @return * 0 on success, -1 on error (errno is set). On error, attrs * are freed and must not be used by the caller. */ int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx, struct ec_dict *attrs); /** * Compare two string vectors. * * @param strvec1 * The pointer to the first string vector. * @param strvec2 * The pointer to the second string vector. * @return * 0 if the string vectors are equal. */ int ec_strvec_cmp(const struct ec_strvec *strvec1, const struct ec_strvec *strvec2); /** * Sort the string vector. * * Attributes are not compared. * * @param strvec * The pointer to the first string vector. * @param str_cmp * The sort function to use. If NULL, use strcmp. */ void ec_strvec_sort(struct ec_strvec *strvec, int (*str_cmp)(const char *s1, const char *s2)); /** * Dump a string vector. * * @param out * The stream where the dump is sent. * @param strvec * The pointer to the string vector. */ void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec); /** @} */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/utils.h���������������������������������������������������������������0000664�0000000�0000000�00000002226�15203622040�0017404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_utils Utils * @{ * * @brief Misc utils */ #pragma once /** * Cast a variable into a type, ensuring its initial type first */ #define EC_CAST(x, old_type, new_type) \ ({ \ __typeof__(old_type) __x = (x); \ (__typeof__(new_type))__x; \ }) /** * Mark the end of the arguments list in some functions. */ #define EC_VA_END ((void *)1) /** * Count number of elements in an array. */ #define EC_COUNT_OF(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x]))))) /** * Mark a function or type as deprecated. */ #define EC_DEPRECATED(msg) __attribute__((deprecated(msg))) /** * Emit a deprecation warning when a macro is expanded. */ #define EC_DEPRECATED_MACRO_(x) _Pragma(#x) #define EC_DEPRECATED_MACRO(msg) EC_DEPRECATED_MACRO_(GCC warning msg) /** @} */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/vec.h�����������������������������������������������������������������0000664�0000000�0000000�00000003423�15203622040�0017021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_vec Vectors * @{ * * @brief Helpers for vectors manipulation. * * The ::ec_vec API provides helpers to manipulate vectors of objects * of any kind. */ #pragma once #include <stdint.h> #include <sys/types.h> /** * Custom free callback. * * If NULL, the default does nothing. */ typedef void (*ec_vec_elt_free_t)(void *ptr); /** * Custom copy callback. * * If NULL, the default is: `memcpy(dst, src, vec->elt_size)`. */ typedef void (*ec_vec_elt_copy_t)(void *dst, void *src); /** Create a new vector. */ struct ec_vec *ec_vec(size_t elt_size, size_t size, ec_vec_elt_copy_t copy, ec_vec_elt_free_t free); /** Add reference to a vector. */ int ec_vec_add_by_ref(struct ec_vec *vec, void *ptr); /** Add opaque element to a vector. */ int ec_vec_add_ptr(struct ec_vec *vec, void *elt); /** Add uint8_t value to a vector. */ int ec_vec_add_u8(struct ec_vec *vec, uint8_t elt); /** Add uint16_t value to a vector. */ int ec_vec_add_u16(struct ec_vec *vec, uint16_t elt); /** Add uint32_t value to a vector. */ int ec_vec_add_u32(struct ec_vec *vec, uint32_t elt); /** Add uint64_t value to a vector. */ int ec_vec_add_u64(struct ec_vec *vec, uint64_t elt); /** Get element located at an offset. */ int ec_vec_get(void *ptr, const struct ec_vec *vec, size_t idx); /** Duplicate a vector. */ struct ec_vec *ec_vec_dup(const struct ec_vec *vec); /** Duplicate a portion of a vector. */ struct ec_vec *ec_vec_ndup(const struct ec_vec *vec, size_t off, size_t len); /** Free a vector and all its contents. */ void ec_vec_free(struct ec_vec *vec); /** Get the number of elements in a vector. */ __attribute__((pure)) size_t ec_vec_len(const struct ec_vec *vec); /** @} */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/ecoli/yaml.h����������������������������������������������������������������0000664�0000000�0000000�00000002260�15203622040�0017204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_yaml YAML import/export * @{ * * Interface to import/export ecoli data structures in YAML. */ #pragma once #include <stdio.h> struct ec_node; /** * Parse a YAML file and build an ::ec_node tree from it. * * @param filename * The path to the file to be parsed. * @return * The ec_node tree on success, or NULL on error (errno is set). * The returned node must be freed by the caller with ec_node_free(). */ struct ec_node *ec_yaml_import(const char *filename); /** * Export an ::ec_node tree to a YAML formatted stream. * * This function traverses the ::ec_node tree and outputs a YAML representation * of the grammar structure including node type, id, help, attributes and * configuration. The output can be used as a template or documentation * for grammar definitions. * * @param out * The output stream where YAML content will be written. * @param node * The root node of the grammar tree to export. * @return * 0 on success, or -1 on error (errno is set). */ int ec_yaml_export(FILE *out, const struct ec_node *node); /** @} */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/include/meson.build�����������������������������������������������������������������0000664�0000000�0000000�00000002041�15203622040�0017135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> libecoli_headers = files( 'ecoli.h', 'ecoli/assert.h', 'ecoli/complete.h', 'ecoli/config.h', 'ecoli/dict.h', 'ecoli/editline.h', 'ecoli/htable.h', 'ecoli/init.h', 'ecoli/interact.h', 'ecoli/log.h', 'ecoli/murmurhash.h', 'ecoli/node.h', 'ecoli/node_any.h', 'ecoli/node_bypass.h', 'ecoli/node_cmd.h', 'ecoli/node_cond.h', 'ecoli/node_dynamic.h', 'ecoli/node_dynlist.h', 'ecoli/node_empty.h', 'ecoli/node_expr.h', 'ecoli/node_file.h', 'ecoli/node_helper.h', 'ecoli/node_int.h', 'ecoli/node_many.h', 'ecoli/node_none.h', 'ecoli/node_once.h', 'ecoli/node_option.h', 'ecoli/node_or.h', 'ecoli/node_re.h', 'ecoli/node_re_lex.h', 'ecoli/node_seq.h', 'ecoli/node_sh_lex.h', 'ecoli/node_space.h', 'ecoli/node_str.h', 'ecoli/node_subset.h', 'ecoli/parse.h', 'ecoli/string.h', 'ecoli/strvec.h', 'ecoli/utils.h', 'ecoli/vec.h', 'ecoli/yaml.h', ) doc_sources += libecoli_headers install_headers(libecoli_headers, preserve_path: true) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/libshell/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0015151�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/libshell/libecoli.sh����������������������������������������������������������������0000664�0000000�0000000�00000003210�15203622040�0017263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> # Source this file from a shell script to add libecoli helpers. # use a safer version of echo (no option) ec_echo() { printf "%s\n" "$*" } ec_debug() { ec_echo "$@" >&2 } # $1: node sequence number (ex: ec_node4) ec_pnode_get_first_child() { local first_child=${1}_first_child ec_echo $(eval 'ec_echo ${'$first_child'}') } # $1: node sequence number (ex: ec_node4) ec_pnode_get_next() { local next=${1}_next ec_echo $(eval 'ec_echo ${'$next'}') } # $1: node sequence number (ex: ec_node4) ec_pnode_iter_next() { local seq=${1#ec_node} seq=$((seq+1)) local next=ec_node${seq} if [ "$(ec_pnode_get_type $next)" != "" ]; then ec_echo $next fi } # $1: node sequence number (ex: ec_node4) ec_pnode_get_id() { local id=${1}_id ec_echo $(eval 'ec_echo ${'$id'}') } # $1: node sequence number (ex: ec_node4) ec_pnode_get_type() { local type=${1}_type ec_echo $(eval 'ec_echo ${'$type'}') } # $1: node sequence number (ex: ec_node4) ec_pnode_get_strvec_len() { local strvec_len=${1}_strvec_len ec_echo $(eval 'ec_echo ${'$strvec_len'}') } # $1: node sequence number (ex: ec_node4) # $2: index in strvec ec_pnode_get_str() { if [ $# -ne 2 ]; then return fi local str=${1}_str${2} ec_echo $(eval 'ec_echo ${'$str'}') } # $1: node sequence number (ex: ec_node4) # $2: node id (string) ec_pnode_find_first() { if [ $# -ne 2 ]; then return fi local node_seq=$1 while [ "$node_seq" != "" ]; do local id=$(ec_pnode_get_id $node_seq) if [ "$id" = "$2" ]; then ec_echo $node_seq return 0 fi node_seq=$(ec_pnode_iter_next $node_seq) done } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/meson.build�������������������������������������������������������������������������0000664�0000000�0000000�00000002322�15203622040�0015514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> project('libecoli', 'c', version : '0.11.6', license : 'BSD-3-clause', default_options: [ 'warning_level=2', 'werror=true', 'c_std=c99', ]) edit_dep = dependency('libedit', required: get_option('editline')) yaml_dep = dependency('yaml-0.1', required: get_option('yaml')) add_project_arguments('-Wmissing-prototypes', language : 'c') add_project_arguments('-D_GNU_SOURCE', language : 'c') inc = include_directories('include') libecoli_sources = [] doc_sources = [] subdir('src') subdir('include') if meson.is_subproject() libecoli = static_library('ecoli', libecoli_sources, include_directories : inc, dependencies : deps, install : false ) libecoli_dep = declare_dependency( link_whole: libecoli, include_directories: inc, ) else libecoli = shared_library('ecoli', libecoli_sources, include_directories : inc, dependencies : deps, version : meson.project_version(), install : true ) pkg_mod = import('pkgconfig') pkg_mod.generate(libecoli, name : 'libecoli', filebase : 'libecoli', description : 'Extensible COmmand LIne library.') endif subdir('test') subdir('examples') subdir('doc') ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/meson_options.txt�������������������������������������������������������������������0000664�0000000�0000000�00000001152�15203622040�0017007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2019, Olivier MATZ <zer0@droids-corp.org> option('yaml', type : 'feature', value : 'auto', description: 'Compile with yaml support using libyaml.') option('editline', type : 'feature', value : 'auto', description: 'Compile with editline support using libedit.') option('doc', type : 'feature', value : 'auto', description: 'Generate project documentation.') option('tests', type : 'feature', value : 'auto', description: 'Build test binaries.') option('examples', type : 'feature', value : 'auto', description: 'Build examples.') ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0014142�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/assert.c������������������������������������������������������������������������0000664�0000000�0000000�00000000730�15203622040�0015607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <ecoli/assert.h> void __ec_assert_print(bool expr, const char *expr_str, const char *format, ...) { va_list ap; if (expr) return; va_start(ap, format); fprintf(stderr, "assertion failed: '%s' is false\n", expr_str); vfprintf(stderr, format, ap); va_end(ap); abort(); } ����������������������������������������libecoli-0.11.6/src/complete.c����������������������������������������������������������������������0000664�0000000�0000000�00000033725�15203622040�0016130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_or.h> #include <ecoli/node_seq.h> #include <ecoli/node_sh_lex.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(comp); struct ec_comp_item { TAILQ_ENTRY(ec_comp_item) next; enum ec_comp_type type; struct ec_comp_group *grp; char *current; /**< The initial token */ char *full; /**< The full token after completion */ char *completion; /**< Chars that are added, NULL if not applicable */ char *display; /**< What should be displayed by help/completers */ struct ec_dict *attrs; }; TAILQ_HEAD(ec_comp_item_list, ec_comp_item); struct ec_comp_group { TAILQ_ENTRY(ec_comp_group) next; const struct ec_comp *comp; const struct ec_node *node; struct ec_comp_item_list items; struct ec_pnode *pstate; struct ec_dict *attrs; }; TAILQ_HEAD(ec_comp_group_list, ec_comp_group); struct ec_comp { size_t count; size_t count_full; size_t count_partial; size_t count_unknown; struct ec_pnode *cur_pstate; struct ec_comp_group *cur_group; struct ec_comp_group_list groups; struct ec_dict *attrs; }; struct ec_comp *ec_comp(void) { struct ec_comp *comp = NULL; comp = calloc(1, sizeof(*comp)); if (comp == NULL) goto fail; comp->attrs = ec_dict(); if (comp->attrs == NULL) goto fail; TAILQ_INIT(&comp->groups); return comp; fail: if (comp != NULL) ec_dict_free(comp->attrs); free(comp); return NULL; } struct ec_pnode *ec_comp_get_cur_pstate(const struct ec_comp *comp) { return comp->cur_pstate; } struct ec_comp_group *ec_comp_get_cur_group(const struct ec_comp *comp) { return comp->cur_group; } struct ec_dict *ec_comp_get_attrs(const struct ec_comp *comp) { return comp->attrs; } int ec_complete_child( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_pnode *child_pstate, *cur_pstate; struct ec_comp_group *cur_group; ec_complete_t complete_cb; int ret; /* get the complete method, falling back to ec_complete_unknown() */ complete_cb = ec_node_type(node)->complete; if (complete_cb == NULL) complete_cb = ec_complete_unknown; /* save previous parse state, prepare child state */ cur_pstate = comp->cur_pstate; child_pstate = ec_pnode(node); if (child_pstate == NULL) return -1; if (cur_pstate != NULL) ec_pnode_link_child(cur_pstate, child_pstate); comp->cur_pstate = child_pstate; cur_group = comp->cur_group; comp->cur_group = NULL; /* fill the comp struct with items */ ret = complete_cb(node, comp, strvec); /* restore parent parse state */ if (cur_pstate != NULL) { ec_pnode_unlink_child(child_pstate); assert(ec_pnode_get_first_child(child_pstate) == NULL); } ec_pnode_free(child_pstate); comp->cur_pstate = cur_pstate; comp->cur_group = cur_group; if (ret < 0) return -1; return 0; } struct ec_comp *ec_complete_strvec(const struct ec_node *node, const struct ec_strvec *strvec) { struct ec_comp *comp = NULL; int ret; comp = ec_comp(); if (comp == NULL) goto fail; ret = ec_complete_child(node, comp, strvec); if (ret < 0) goto fail; return comp; fail: ec_comp_free(comp); return NULL; } struct ec_comp *ec_complete(const struct ec_node *node, const char *str) { struct ec_strvec *strvec = NULL; struct ec_comp *comp; errno = ENOMEM; strvec = ec_strvec(); if (strvec == NULL) goto fail; if (ec_strvec_add(strvec, str) < 0) goto fail; comp = ec_complete_strvec(node, strvec); if (comp == NULL) goto fail; ec_strvec_free(strvec); return comp; fail: ec_strvec_free(strvec); return NULL; } struct ec_strvec *ec_complete_strvec_expand( const struct ec_node *node, enum ec_comp_type type, const struct ec_strvec *strvec ) { struct ec_strvec *expanded = NULL; const struct ec_comp_item *item; struct ec_comp *comp = NULL; const char *exp; unsigned i; if (node == NULL || strvec == NULL) { errno = EINVAL; goto err; } if ((expanded = ec_strvec()) == NULL) goto err; for (i = 0; i < ec_strvec_len(strvec); i++) { const char *s = ec_strvec_val(strvec, i); if (ec_strvec_add(expanded, s) < 0) goto err; if ((comp = ec_complete_strvec(node, expanded)) == NULL) goto err; if (ec_comp_count(comp, type) == 1) { item = ec_comp_iter_first(comp, type); exp = ec_comp_item_get_str(item); if (exp != NULL && strcmp(s, exp) != 0) { /* * The string expands to exactly one * non-ambiguous completion. Replace it with * the expanded word. */ if (ec_strvec_set(expanded, i, exp) < 0) goto err; } } ec_comp_free(comp); comp = NULL; } return expanded; err: ec_strvec_free(expanded); ec_comp_free(comp); return NULL; } static struct ec_comp_group * ec_comp_group(const struct ec_comp *comp, const struct ec_node *node, struct ec_pnode *parse) { struct ec_comp_group *grp = NULL; grp = calloc(1, sizeof(*grp)); if (grp == NULL) return NULL; grp->comp = comp; grp->attrs = ec_dict(); if (grp->attrs == NULL) goto fail; grp->pstate = ec_pnode_dup(parse); if (grp->pstate == NULL) goto fail; grp->node = node; TAILQ_INIT(&grp->items); return grp; fail: if (grp != NULL) { ec_pnode_free(grp->pstate); ec_dict_free(grp->attrs); } free(grp); return NULL; } static struct ec_comp_item * ec_comp_item(enum ec_comp_type type, const char *current, const char *full) { struct ec_comp_item *item = NULL; struct ec_dict *attrs = NULL; char *comp_cp = NULL, *current_cp = NULL; char *full_cp = NULL, *display_cp = NULL; if (type == EC_COMP_UNKNOWN && full != NULL) { errno = EINVAL; return NULL; } if (type != EC_COMP_UNKNOWN && full == NULL) { errno = EINVAL; return NULL; } item = calloc(1, sizeof(*item)); if (item == NULL) goto fail; attrs = ec_dict(); if (attrs == NULL) goto fail; if (current == NULL && full != NULL) goto fail; if (current != NULL && full == NULL) goto fail; if (current != NULL && full != NULL) { if (!ec_str_startswith(full, current)) goto fail; current_cp = strdup(current); if (current_cp == NULL) goto fail; comp_cp = strdup(&full[strlen(current)]); if (comp_cp == NULL) goto fail; full_cp = strdup(full); if (full_cp == NULL) goto fail; display_cp = strdup(full); if (display_cp == NULL) goto fail; } item->type = type; item->current = current_cp; item->full = full_cp; item->completion = comp_cp; item->display = display_cp; item->attrs = attrs; return item; fail: ec_dict_free(attrs); free(comp_cp); free(current_cp); free(full_cp); free(display_cp); free(item); return NULL; } int ec_comp_item_set_display(struct ec_comp_item *item, const char *display) { char *display_copy = NULL; if (item == NULL || display == NULL || item->type == EC_COMP_UNKNOWN) { errno = EINVAL; return -1; } display_copy = strdup(display); if (display_copy == NULL) goto fail; free(item->display); item->display = display_copy; return 0; fail: free(display_copy); return -1; } int ec_comp_item_set_completion(struct ec_comp_item *item, const char *completion) { char *completion_copy = NULL; if (item == NULL || completion == NULL || item->type == EC_COMP_UNKNOWN) { errno = EINVAL; return -1; } completion_copy = strdup(completion); if (completion_copy == NULL) goto fail; free(item->completion); item->completion = completion_copy; return 0; fail: free(completion_copy); return -1; } int ec_comp_item_set_str(struct ec_comp_item *item, const char *str) { char *str_copy = NULL; if (item == NULL || str == NULL || item->type == EC_COMP_UNKNOWN) { errno = EINVAL; return -1; } str_copy = strdup(str); if (str_copy == NULL) goto fail; free(item->full); item->full = str_copy; return 0; fail: free(str_copy); return -1; } static int ec_comp_item_add(struct ec_comp *comp, const struct ec_node *node, struct ec_comp_item *item) { if (comp == NULL || item == NULL) { errno = EINVAL; return -1; } switch (item->type) { case EC_COMP_UNKNOWN: comp->count_unknown++; break; case EC_COMP_FULL: comp->count_full++; break; case EC_COMP_PARTIAL: comp->count_partial++; break; default: errno = EINVAL; return -1; } if (comp->cur_group == NULL) { struct ec_comp_group *grp; grp = ec_comp_group(comp, node, comp->cur_pstate); if (grp == NULL) return -1; TAILQ_INSERT_TAIL(&comp->groups, grp, next); comp->cur_group = grp; } comp->count++; TAILQ_INSERT_TAIL(&comp->cur_group->items, item, next); item->grp = comp->cur_group; return 0; } const char *ec_comp_item_get_str(const struct ec_comp_item *item) { return item->full; } const char *ec_comp_item_get_display(const struct ec_comp_item *item) { return item->display; } const char *ec_comp_item_get_completion(const struct ec_comp_item *item) { return item->completion; } const char *ec_comp_item_get_current(const struct ec_comp_item *item) { return item->current; } enum ec_comp_type ec_comp_item_get_type(const struct ec_comp_item *item) { return item->type; } const struct ec_comp_group *ec_comp_item_get_grp(const struct ec_comp_item *item) { return item->grp; } const struct ec_node *ec_comp_item_get_node(const struct ec_comp_item *item) { return ec_comp_item_get_grp(item)->node; } static void ec_comp_item_free(struct ec_comp_item *item) { if (item == NULL) return; free(item->full); free(item->current); free(item->completion); free(item->display); ec_dict_free(item->attrs); free(item); } struct ec_comp_item *ec_comp_add_item( struct ec_comp *comp, const struct ec_node *node, enum ec_comp_type type, const char *current, const char *full ) { struct ec_comp_item *item = NULL; int ret; item = ec_comp_item(type, current, full); if (item == NULL) return NULL; ret = ec_comp_item_add(comp, node, item); if (ret < 0) goto fail; return item; fail: ec_comp_item_free(item); return NULL; } /* return a completion item of type "unknown" */ int ec_complete_unknown( const struct ec_node *gen_node, struct ec_comp *comp, const struct ec_strvec *strvec ) { const struct ec_comp_item *item = NULL; if (ec_strvec_len(strvec) != 1) return 0; item = ec_comp_add_item(comp, gen_node, EC_COMP_UNKNOWN, NULL, NULL); if (item == NULL) return -1; return 0; } static void ec_comp_group_free(struct ec_comp_group *grp) { struct ec_comp_item *item; if (grp == NULL) return; while (!TAILQ_EMPTY(&grp->items)) { item = TAILQ_FIRST(&grp->items); TAILQ_REMOVE(&grp->items, item, next); ec_comp_item_free(item); } ec_pnode_free(ec_pnode_get_root(grp->pstate)); ec_dict_free(grp->attrs); free(grp); } const struct ec_node *ec_comp_group_get_node(const struct ec_comp_group *grp) { return grp->node; } const struct ec_pnode *ec_comp_group_get_pstate(const struct ec_comp_group *grp) { return grp->pstate; } const struct ec_dict *ec_comp_group_get_attrs(const struct ec_comp_group *grp) { return grp->attrs; } void ec_comp_free(struct ec_comp *comp) { struct ec_comp_group *grp; if (comp == NULL) return; while (!TAILQ_EMPTY(&comp->groups)) { grp = TAILQ_FIRST(&comp->groups); TAILQ_REMOVE(&comp->groups, grp, next); ec_comp_group_free(grp); } ec_dict_free(comp->attrs); free(comp); } void ec_comp_dump(FILE *out, const struct ec_comp *comp) { struct ec_comp_group *grp; struct ec_comp_item *item; if (comp == NULL || comp->count == 0) { fprintf(out, "no completion\n"); return; } fprintf(out, "completion: count=%zu full=%zu partial=%zu unknown=%zu\n", comp->count, comp->count_full, comp->count_partial, comp->count_unknown); TAILQ_FOREACH (grp, &comp->groups, next) { fprintf(out, "node=%p, node_type=%s\n", grp->node, ec_node_type(grp->node)->name); TAILQ_FOREACH (item, &grp->items, next) { const char *typestr; switch (item->type) { case EC_COMP_UNKNOWN: typestr = "unknown"; break; case EC_COMP_FULL: typestr = "full"; break; case EC_COMP_PARTIAL: typestr = "partial"; break; default: typestr = "unknown"; break; } fprintf(out, " type=%s str=<%s> comp=<%s> disp=<%s>\n", typestr, item->full, item->completion, item->display); } } } int ec_comp_merge(struct ec_comp *to, struct ec_comp *from) { struct ec_comp_group *grp; while (!TAILQ_EMPTY(&from->groups)) { grp = TAILQ_FIRST(&from->groups); TAILQ_REMOVE(&from->groups, grp, next); TAILQ_INSERT_TAIL(&to->groups, grp, next); } to->count += from->count; to->count_full += from->count_full; to->count_partial += from->count_partial; to->count_unknown += from->count_unknown; ec_comp_free(from); return 0; } size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type) { size_t count = 0; if (comp == NULL) return count; if (type & EC_COMP_FULL) count += comp->count_full; if (type & EC_COMP_PARTIAL) count += comp->count_partial; if (type & EC_COMP_UNKNOWN) count += comp->count_unknown; return count; } static struct ec_comp_item * __ec_comp_iter_next(const struct ec_comp *comp, struct ec_comp_item *item, enum ec_comp_type type) { struct ec_comp_group *cur_grp; struct ec_comp_item *cur_match; if (item == NULL) { /* first call */ cur_grp = TAILQ_FIRST(&comp->groups); cur_match = NULL; } else { cur_grp = item->grp; cur_match = TAILQ_NEXT(item, next); if (cur_match == NULL) cur_grp = TAILQ_NEXT(cur_grp, next); } while (cur_grp != NULL) { if (cur_match == NULL) cur_match = TAILQ_FIRST(&cur_grp->items); while (cur_match != NULL) { if (cur_match->type & type) return cur_match; cur_match = TAILQ_NEXT(cur_match, next); } cur_grp = TAILQ_NEXT(cur_grp, next); } return NULL; } struct ec_comp_item *ec_comp_iter_next(struct ec_comp_item *item, enum ec_comp_type type) { if (item == NULL) return NULL; return __ec_comp_iter_next(item->grp->comp, item, type); } struct ec_comp_item *ec_comp_iter_first(const struct ec_comp *comp, enum ec_comp_type type) { return __ec_comp_iter_next(comp, NULL, type); } �������������������������������������������libecoli-0.11.6/src/config.c������������������������������������������������������������������������0000664�0000000�0000000�00000044476�15203622040�0015572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <inttypes.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/string.h> #include <ecoli/utils.h> EC_LOG_TYPE_REGISTER(config); const char *ec_config_reserved_keys[] = { "id", "attrs", "help", "type", }; static int __ec_config_dump(FILE *out, const char *key, const struct ec_config *config, size_t indent); static int ec_config_dict_validate(const struct ec_dict *dict, const struct ec_config_schema *schema); bool ec_config_key_is_reserved(const char *name) { size_t i; for (i = 0; i < EC_COUNT_OF(ec_config_reserved_keys); i++) { if (!strcmp(name, ec_config_reserved_keys[i])) return true; } return false; } /* return ec_value type as a string */ static const char *ec_config_type_str(enum ec_config_type type) { switch (type) { case EC_CONFIG_TYPE_BOOL: return "bool"; case EC_CONFIG_TYPE_INT64: return "int64"; case EC_CONFIG_TYPE_UINT64: return "uint64"; case EC_CONFIG_TYPE_STRING: return "string"; case EC_CONFIG_TYPE_NODE: return "node"; case EC_CONFIG_TYPE_LIST: return "list"; case EC_CONFIG_TYPE_DICT: return "dict"; default: return "unknown"; } } static size_t ec_config_schema_len(const struct ec_config_schema *schema) { size_t i; if (schema == NULL) return 0; for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) ; return i; } static int __ec_config_schema_validate(const struct ec_config_schema *schema, enum ec_config_type type) { size_t i, j; int ret; if (type == EC_CONFIG_TYPE_LIST) { if (schema[0].key != NULL) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "list schema key must be NULL\n"); return -1; } } else if (type == EC_CONFIG_TYPE_DICT) { for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) { if (schema[i].key == NULL) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "dict schema key should not be NULL\n"); return -1; } } } else { errno = EINVAL; EC_LOG(EC_LOG_ERR, "invalid schema type\n"); return -1; } for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) { if (schema[i].key != NULL && ec_config_key_is_reserved(schema[i].key)) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "key name <%s> is reserved\n", schema[i].key); return -1; } /* check for duplicate name if more than one element */ for (j = i + 1; schema[j].type != EC_CONFIG_TYPE_NONE; j++) { if (!strcmp(schema[i].key, schema[j].key)) { errno = EEXIST; EC_LOG(EC_LOG_ERR, "duplicate key <%s> in schema\n", schema[i].key); return -1; } } switch (schema[i].type) { case EC_CONFIG_TYPE_BOOL: case EC_CONFIG_TYPE_INT64: case EC_CONFIG_TYPE_UINT64: case EC_CONFIG_TYPE_STRING: case EC_CONFIG_TYPE_NODE: if (schema[i].subschema != NULL || ec_config_schema_len(schema[i].subschema) != 0) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "key <%s> should not have subtype/subschema\n", schema[i].key); return -1; } break; case EC_CONFIG_TYPE_LIST: if (schema[i].subschema == NULL || ec_config_schema_len(schema[i].subschema) != 1) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "key <%s> must have subschema of length 1\n", schema[i].key); return -1; } break; case EC_CONFIG_TYPE_DICT: if (schema[i].subschema == NULL || ec_config_schema_len(schema[i].subschema) == 0) { errno = EINVAL; EC_LOG(EC_LOG_ERR, "key <%s> must have subschema\n", schema[i].key); return -1; } break; default: EC_LOG(EC_LOG_ERR, "invalid type for key <%s>\n", schema[i].key); errno = EINVAL; return -1; } if (schema[i].subschema == NULL) continue; ret = __ec_config_schema_validate(schema[i].subschema, schema[i].type); if (ret < 0) { EC_LOG(EC_LOG_ERR, "cannot parse subschema %s%s\n", schema[i].key ? "key=" : "", schema[i].key ?: ""); return ret; } } return 0; } int ec_config_schema_validate(const struct ec_config_schema *schema) { return __ec_config_schema_validate(schema, EC_CONFIG_TYPE_DICT); } static void __ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema, size_t indent) { const struct ec_config_schema *sch; char *wrapped_desc = NULL; char *desc = NULL; size_t i; for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) { sch = &schema[i]; fprintf(out, "%*s%s %s%s {\n", (int)indent * 4, "", ec_config_type_str(sch->type), sch->key ?: "", sch->key ? " " : ""); indent += 1; wrapped_desc = NULL; desc = ec_str_quote(sch->desc, 0, true); if (desc != NULL) wrapped_desc = ec_str_wrap(desc, 100, 12 + 4 * indent); fprintf(out, "%*sdescription %s;\n", (int)indent * 4, "", wrapped_desc); free(desc); free(wrapped_desc); if (sch->flags & EC_CONFIG_F_MANDATORY) fprintf(out, "%*smandatory true;\n", (int)indent * 4, ""); if (sch->subschema != NULL) { fprintf(out, "\n"); __ec_config_schema_dump(out, sch->subschema, indent); } indent -= 1; fprintf(out, "%*s}\n", (int)indent * 4, ""); } } void ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema, const char *name) { if (schema == NULL) { fprintf(out, "no schema\n"); return; } fprintf(out, "schema %s {\n", name ?: "unknown-name"); __ec_config_schema_dump(out, schema, 1); fprintf(out, "}\n"); } enum ec_config_type ec_config_get_type(const struct ec_config *config) { return config->type; } struct ec_config *ec_config_bool(bool boolean) { struct ec_config *value = NULL; value = calloc(1, sizeof(*value)); if (value == NULL) return NULL; value->type = EC_CONFIG_TYPE_BOOL; value->boolean = boolean; return value; } struct ec_config *ec_config_i64(int64_t i64) { struct ec_config *value = NULL; value = calloc(1, sizeof(*value)); if (value == NULL) return NULL; value->type = EC_CONFIG_TYPE_INT64; value->i64 = i64; return value; } struct ec_config *ec_config_u64(uint64_t u64) { struct ec_config *value = NULL; value = calloc(1, sizeof(*value)); if (value == NULL) return NULL; value->type = EC_CONFIG_TYPE_UINT64; value->u64 = u64; return value; } /* duplicate string */ struct ec_config *ec_config_string(const char *string) { struct ec_config *value = NULL; char *s = NULL; if (string == NULL) goto fail; s = strdup(string); if (s == NULL) goto fail; value = calloc(1, sizeof(*value)); if (value == NULL) goto fail; value->type = EC_CONFIG_TYPE_STRING; value->string = s; return value; fail: free(value); free(s); return NULL; } /* "consume" the node */ struct ec_config *ec_config_node(struct ec_node *node) { struct ec_config *value = NULL; if (node == NULL) goto fail; value = calloc(1, sizeof(*value)); if (value == NULL) goto fail; value->type = EC_CONFIG_TYPE_NODE; value->node = node; return value; fail: ec_node_free(node); free(value); return NULL; } struct ec_config *ec_config_dict(void) { struct ec_config *value = NULL; struct ec_dict *dict = NULL; dict = ec_dict(); if (dict == NULL) goto fail; value = calloc(1, sizeof(*value)); if (value == NULL) goto fail; value->type = EC_CONFIG_TYPE_DICT; value->dict = dict; return value; fail: ec_dict_free(dict); free(value); return NULL; } struct ec_config *ec_config_list(void) { struct ec_config *value = NULL; value = calloc(1, sizeof(*value)); if (value == NULL) return NULL; value->type = EC_CONFIG_TYPE_LIST; TAILQ_INIT(&value->list); return value; } ssize_t ec_config_count(const struct ec_config *config) { const struct ec_config *child; ssize_t n; switch (config->type) { case EC_CONFIG_TYPE_LIST: n = 0; TAILQ_FOREACH (child, &config->list, next) n++; return n; case EC_CONFIG_TYPE_DICT: /* TODO */ default: errno = EINVAL; return -1; } } const struct ec_config_schema * ec_config_schema_lookup(const struct ec_config_schema *schema, const char *key) { size_t i; for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) { if (!strcmp(key, schema[i].key)) return &schema[i]; } errno = ENOENT; return NULL; } enum ec_config_type ec_config_schema_type(const struct ec_config_schema *schema_elt) { return schema_elt->type; } const struct ec_config_schema *ec_config_schema_sub(const struct ec_config_schema *schema_elt) { return schema_elt->subschema; } void ec_config_free(struct ec_config *value) { if (value == NULL) return; switch (value->type) { case EC_CONFIG_TYPE_STRING: free(value->string); break; case EC_CONFIG_TYPE_NODE: ec_node_free(value->node); break; case EC_CONFIG_TYPE_LIST: while (!TAILQ_EMPTY(&value->list)) { struct ec_config *v; v = TAILQ_FIRST(&value->list); TAILQ_REMOVE(&value->list, v, next); ec_config_free(v); } break; case EC_CONFIG_TYPE_DICT: ec_dict_free(value->dict); break; default: break; } free(value); } static int ec_config_list_cmp(const struct ec_config_list *list1, const struct ec_config_list *list2) { const struct ec_config *v1, *v2; for (v1 = TAILQ_FIRST(list1), v2 = TAILQ_FIRST(list2); v1 != NULL && v2 != NULL; v1 = TAILQ_NEXT(v1, next), v2 = TAILQ_NEXT(v2, next)) { if (ec_config_cmp(v1, v2)) return -1; } if (v1 != NULL || v2 != NULL) return -1; return 0; } static int ec_config_dict_cmp(const struct ec_dict *d1, const struct ec_dict *d2) { const struct ec_config *v1, *v2; struct ec_dict_elt_ref *iter = NULL; const char *key; if (ec_dict_len(d1) != ec_dict_len(d2)) return -1; for (iter = ec_dict_iter(d1); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); v1 = ec_dict_iter_get_val(iter); v2 = ec_dict_get(d2, key); if (ec_config_cmp(v1, v2)) goto fail; } return 0; fail: return -1; } int ec_config_cmp(const struct ec_config *value1, const struct ec_config *value2) { if (value1 == NULL || value2 == NULL) { errno = EINVAL; return -1; } if (value1->type != value2->type) return -1; switch (value1->type) { case EC_CONFIG_TYPE_BOOL: if (value1->boolean == value2->boolean) return 0; break; case EC_CONFIG_TYPE_INT64: if (value1->i64 == value2->i64) return 0; break; case EC_CONFIG_TYPE_UINT64: if (value1->u64 == value2->u64) return 0; break; case EC_CONFIG_TYPE_STRING: if (!strcmp(value1->string, value2->string)) return 0; break; case EC_CONFIG_TYPE_NODE: if (value1->node == value2->node) return 0; break; case EC_CONFIG_TYPE_LIST: return ec_config_list_cmp(&value1->list, &value2->list); case EC_CONFIG_TYPE_DICT: return ec_config_dict_cmp(value1->dict, value2->dict); default: break; } return -1; } static int ec_config_list_validate(const struct ec_config_list *list, const struct ec_config_schema *sch) { const struct ec_config *value; TAILQ_FOREACH (value, list, next) { if (value->type != sch->type) { errno = EBADMSG; return -1; } if (value->type == EC_CONFIG_TYPE_LIST) { if (ec_config_list_validate(&value->list, sch->subschema) < 0) return -1; } else if (value->type == EC_CONFIG_TYPE_DICT) { if (ec_config_dict_validate(value->dict, sch->subschema) < 0) return -1; } } return 0; } static int ec_config_dict_validate(const struct ec_dict *dict, const struct ec_config_schema *schema) { struct ec_dict_elt_ref *iter = NULL; const struct ec_config_schema *sch; const struct ec_config *value; const char *key; size_t i; for (i = 0; schema[i].type != EC_CONFIG_TYPE_NONE; i++) { key = schema[i].key; sch = &schema[i]; value = ec_dict_get(dict, key); if (value == NULL && (sch->flags & EC_CONFIG_F_MANDATORY)) { errno = EBADMSG; goto fail; } if (value == NULL) continue; if (sch->type != value->type) { errno = EBADMSG; goto fail; } if (value->type == EC_CONFIG_TYPE_LIST) { if (ec_config_list_validate(&value->list, sch->subschema) < 0) goto fail; } else if (value->type == EC_CONFIG_TYPE_DICT) { if (ec_config_dict_validate(value->dict, sch->subschema) < 0) goto fail; } } for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); sch = ec_config_schema_lookup(schema, key); if (sch == NULL) { errno = EBADMSG; goto fail; } } return 0; fail: return -1; } int ec_config_validate(const struct ec_config *dict, const struct ec_config_schema *schema) { if (dict->type != EC_CONFIG_TYPE_DICT || schema == NULL) { errno = EINVAL; goto fail; } if (ec_config_dict_validate(dict->dict, schema) < 0) goto fail; return 0; fail: return -1; } struct ec_config *ec_config_dict_get(const struct ec_config *config, const char *key) { if (config == NULL) { errno = EINVAL; return NULL; } if (config->type != EC_CONFIG_TYPE_DICT) { errno = EINVAL; return NULL; } return ec_dict_get(config->dict, key); } struct ec_config *ec_config_list_first(struct ec_config *list) { if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) { errno = EINVAL; return NULL; } return TAILQ_FIRST(&list->list); } struct ec_config *ec_config_list_next(struct ec_config *list, struct ec_config *config) { (void)list; return TAILQ_NEXT(config, next); } /* value is consumed */ int ec_config_dict_set(struct ec_config *config, const char *key, struct ec_config *value) { void (*free_cb)(struct ec_config *) = ec_config_free; if (config == NULL || key == NULL || value == NULL) { errno = EINVAL; goto fail; } if (config->type != EC_CONFIG_TYPE_DICT) { errno = EINVAL; goto fail; } return ec_dict_set(config->dict, key, value, (void (*)(void *))free_cb); fail: ec_config_free(value); return -1; } int ec_config_dict_del(struct ec_config *config, const char *key) { if (config == NULL || key == NULL) { errno = EINVAL; return -1; } if (config->type != EC_CONFIG_TYPE_DICT) { errno = EINVAL; return -1; } return ec_dict_del(config->dict, key); } /* value is consumed */ int ec_config_list_add(struct ec_config *list, struct ec_config *value) { if (list == NULL || list->type != EC_CONFIG_TYPE_LIST || value == NULL) { errno = EINVAL; goto fail; } TAILQ_INSERT_TAIL(&list->list, value, next); return 0; fail: ec_config_free(value); return -1; } int ec_config_list_del(struct ec_config *list, struct ec_config *config) { if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) { errno = EINVAL; return -1; } TAILQ_REMOVE(&list->list, config, next); ec_config_free(config); return 0; } static struct ec_config *ec_config_list_dup(const struct ec_config_list *list) { struct ec_config *dup = NULL, *v, *value; dup = ec_config_list(); if (dup == NULL) goto fail; TAILQ_FOREACH (v, list, next) { value = ec_config_dup(v); if (value == NULL) goto fail; if (ec_config_list_add(dup, value) < 0) goto fail; } return dup; fail: ec_config_free(dup); return NULL; } static struct ec_config *ec_config_dict_dup(const struct ec_dict *dict) { struct ec_config *dup = NULL, *value; struct ec_dict_elt_ref *iter = NULL; const char *key; dup = ec_config_dict(); if (dup == NULL) goto fail; for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); value = ec_config_dup(ec_dict_iter_get_val(iter)); if (value == NULL) goto fail; if (ec_config_dict_set(dup, key, value) < 0) goto fail; } return dup; fail: ec_config_free(dup); return NULL; } struct ec_config *ec_config_dup(const struct ec_config *config) { if (config == NULL) { errno = EINVAL; return NULL; } switch (config->type) { case EC_CONFIG_TYPE_BOOL: return ec_config_bool(config->boolean); case EC_CONFIG_TYPE_INT64: return ec_config_i64(config->i64); case EC_CONFIG_TYPE_UINT64: return ec_config_u64(config->u64); case EC_CONFIG_TYPE_STRING: return ec_config_string(config->string); case EC_CONFIG_TYPE_NODE: return ec_config_node(ec_node_clone(config->node)); case EC_CONFIG_TYPE_LIST: return ec_config_list_dup(&config->list); case EC_CONFIG_TYPE_DICT: return ec_config_dict_dup(config->dict); default: errno = EINVAL; break; } return NULL; } static int ec_config_list_dump(FILE *out, const char *key, const struct ec_config_list *list, size_t indent) { const struct ec_config *v; fprintf(out, "%*s" "%s%s%stype=list\n", (int)indent * 4, "", key ? "key=" : "", key ? key : "", key ? " " : ""); TAILQ_FOREACH (v, list, next) { if (__ec_config_dump(out, NULL, v, indent + 1) < 0) return -1; } return 0; } static int ec_config_dict_dump(FILE *out, const char *key, const struct ec_dict *dict, size_t indent) { const struct ec_config *value; struct ec_dict_elt_ref *iter; const char *k; fprintf(out, "%*s" "%s%s%stype=dict\n", (int)indent * 4, "", key ? "key=" : "", key ? key : "", key ? " " : ""); for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { k = ec_dict_iter_get_key(iter); value = ec_dict_iter_get_val(iter); if (__ec_config_dump(out, k, value, indent + 1) < 0) goto fail; } return 0; fail: return -1; } static int __ec_config_dump(FILE *out, const char *key, const struct ec_config *value, size_t indent) { char *val_str = NULL; int ret; switch (value->type) { case EC_CONFIG_TYPE_BOOL: if (value->boolean) ret = asprintf(&val_str, "true"); else ret = asprintf(&val_str, "false"); break; case EC_CONFIG_TYPE_INT64: ret = asprintf(&val_str, "%" PRIi64, value->i64); break; case EC_CONFIG_TYPE_UINT64: ret = asprintf(&val_str, "%" PRIu64, value->u64); break; case EC_CONFIG_TYPE_STRING: ret = asprintf(&val_str, "%s", value->string); break; case EC_CONFIG_TYPE_NODE: ret = asprintf(&val_str, "%p", value->node); break; case EC_CONFIG_TYPE_LIST: return ec_config_list_dump(out, key, &value->list, indent); case EC_CONFIG_TYPE_DICT: return ec_config_dict_dump(out, key, value->dict, indent); default: ret = -1; errno = EINVAL; break; } /* errno is already set on error */ if (ret < 0 || val_str == NULL) goto fail; fprintf(out, "%*s" "%s%s%stype=%s val=%s\n", (int)indent * 4, "", key ? "key=" : "", key ? key : "", key ? " " : "", ec_config_type_str(value->type), val_str); free(val_str); return 0; fail: free(val_str); return -1; } void ec_config_dump(FILE *out, const struct ec_config *config) { fprintf(out, "------------------- config dump:\n"); if (config == NULL) { fprintf(out, "no config\n"); return; } if (__ec_config_dump(out, NULL, config, 0) < 0) fprintf(out, "error while dumping\n"); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/dict.c��������������������������������������������������������������������������0000664�0000000�0000000�00000005064�15203622040�0015236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <ecoli/dict.h> #include <ecoli/init.h> #include <ecoli/log.h> #include "htable_private.h" EC_LOG_TYPE_REGISTER(dict); struct ec_dict_elt { struct ec_htable_elt htable; }; struct ec_dict_elt_ref { struct ec_htable_elt_ref htable; }; struct ec_dict { struct ec_htable htable; }; struct ec_dict *ec_dict(void) { return (struct ec_dict *)ec_htable(); } bool ec_dict_has_key(const struct ec_dict *dict, const char *key) { if (key == NULL) { errno = EINVAL; return false; } return ec_htable_has_key(&dict->htable, key, strlen(key) + 1); } void *ec_dict_get(const struct ec_dict *dict, const char *key) { if (key == NULL) { errno = EINVAL; return NULL; } return ec_htable_get(&dict->htable, key, strlen(key) + 1); } int ec_dict_del(struct ec_dict *dict, const char *key) { if (key == NULL) { errno = EINVAL; return -1; } return ec_htable_del(&dict->htable, key, strlen(key) + 1); } int ec_dict_set(struct ec_dict *dict, const char *key, void *val, ec_dict_elt_free_t free_cb) { if (key == NULL) { errno = EINVAL; return -1; } return ec_htable_set(&dict->htable, key, strlen(key) + 1, val, free_cb); } void ec_dict_free(struct ec_dict *dict) { ec_htable_free(&dict->htable); } size_t ec_dict_len(const struct ec_dict *dict) { return ec_htable_len(&dict->htable); } struct ec_dict_elt_ref *ec_dict_iter(const struct ec_dict *dict) { return (struct ec_dict_elt_ref *)ec_htable_iter(&dict->htable); } struct ec_dict_elt_ref *ec_dict_iter_next(struct ec_dict_elt_ref *iter) { return (struct ec_dict_elt_ref *)ec_htable_iter_next(&iter->htable); } const char *ec_dict_iter_get_key(const struct ec_dict_elt_ref *iter) { return (const char *)ec_htable_iter_get_key(&iter->htable); } void *ec_dict_iter_get_val(const struct ec_dict_elt_ref *iter) { return (struct ec_dict_elt_ref *)ec_htable_iter_get_val(&iter->htable); } void ec_dict_dump(FILE *out, const struct ec_dict *dict) { struct ec_dict_elt_ref *iter; if (dict == NULL) { fprintf(out, "empty dict\n"); return; } fprintf(out, "dict:\n"); for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { fprintf(out, " %s: %p\n", ec_dict_iter_get_key(iter), ec_dict_iter_get_val(iter)); } } struct ec_dict *ec_dict_dup(const struct ec_dict *dict) { return (struct ec_dict *)ec_htable_dup(&dict->htable); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/editline.c����������������������������������������������������������������������0000664�0000000�0000000�00000036654�15203622040�0016121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <histedit.h> #include <ecoli/complete.h> #include <ecoli/dict.h> #include <ecoli/editline.h> #include <ecoli/interact.h> #include <ecoli/node.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> #include <ecoli/utils.h> struct ec_editline { struct editline *el; History *history; char *hist_file; HistEvent histev; const struct ec_node *node; char *prompt; }; int ec_editline_term_size( const struct ec_editline *editline, unsigned int *width, unsigned int *height ) { unsigned int _width, _height; if (el_get(editline->el, EL_GETTC, "co", &_width, (void *)0)) return -1; if (el_get(editline->el, EL_GETTC, "li", &_height, (void *)0)) return -1; *width = _width; *height = _height; return 0; } static char *prompt_cb(struct editline *el) { struct ec_editline *editline; void *clientdata; if (el_get(el, EL_CLIENTDATA, &clientdata)) return "> "; editline = clientdata; if (editline == NULL) return "> "; return editline->prompt; } int ec_editline_set_prompt(struct ec_editline *editline, const char *prompt) { char *copy = NULL; if (prompt != NULL) { copy = strdup(prompt); if (copy == NULL) return -1; } if (el_set(editline->el, EL_PROMPT, prompt_cb)) goto fail; free(editline->prompt); editline->prompt = copy; return 0; fail: free(copy); return -1; } int ec_editline_set_prompt_esc(struct ec_editline *editline, const char *prompt, char delim) { char *copy = NULL; if (prompt != NULL) { copy = strdup(prompt); if (copy == NULL) return -1; } if (el_set(editline->el, EL_PROMPT_ESC, prompt_cb, delim) < 0) goto fail; free(editline->prompt); editline->prompt = copy; return 0; fail: free(copy); return -1; } struct ec_editline *ec_editline( const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags ) { struct ec_editline *editline = NULL; struct editline *el; if (f_in == NULL || f_out == NULL || f_err == NULL) { errno = EINVAL; goto fail; } editline = calloc(1, sizeof(*editline)); if (editline == NULL) goto fail; el = el_init(prog, f_in, f_out, f_err); if (el == NULL) goto fail; editline->el = el; /* save editline pointer as user data */ if (el_set(el, EL_CLIENTDATA, editline)) goto fail; /* install default editline signals */ if (el_set(el, EL_SIGNAL, 1)) goto fail; if (el_set(el, EL_PREP_TERM, 0)) goto fail; /* use emacs bindings */ if (el_set(el, EL_EDITOR, "emacs")) goto fail; if (el_set(el, EL_BIND, "^W", "ed-delete-prev-word", NULL)) goto fail; /* ask terminal to not send signals */ if (flags & EC_EDITLINE_DISABLE_SIGNALS) { if (el_set(el, EL_SETTY, "-d", "-isig", NULL)) goto fail; } else if (flags & EC_EDITLINE_DEFAULT_SIGHANDLER) { if (el_set(el, EL_SIGNAL, 1)) goto fail; } /* set prompt */ editline->prompt = strdup("> "); if (editline->prompt == NULL) goto fail; if (el_set(el, EL_PROMPT, prompt_cb)) goto fail; /* set up history */ if ((flags & EC_EDITLINE_DISABLE_HISTORY) == 0) { if (ec_editline_set_history(editline, EC_EDITLINE_HISTORY_SIZE, NULL) < 0) goto fail; } /* register completion callback */ if ((flags & EC_EDITLINE_DISABLE_COMPLETION) == 0) { if (el_set(el, EL_ADDFN, "ed-complete", "Complete buffer", ec_editline_complete)) goto fail; if (el_set(el, EL_BIND, "^I", "ed-complete", NULL)) goto fail; if (el_set(el, EL_BIND, "?", "ed-complete", NULL)) goto fail; } return editline; fail: ec_editline_free(editline); return NULL; } void ec_editline_free(struct ec_editline *editline) { if (editline == NULL) return; if (editline->el != NULL) el_end(editline->el); if (editline->history != NULL) history_end(editline->history); free(editline->hist_file); free(editline->prompt); free(editline); } struct editline *ec_editline_get_el(struct ec_editline *editline) { return editline->el; } const struct ec_node *ec_editline_get_node(const struct ec_editline *editline) { return editline->node; } int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node) { if (strcmp(ec_node_get_type_name(node), "sh_lex")) { errno = EINVAL; return -1; } editline->node = node; return 0; } int ec_editline_set_history(struct ec_editline *editline, size_t hist_size, const char *hist_file) { struct editline *el = editline->el; if (editline->history != NULL) history_end(editline->history); if (editline->hist_file != NULL) { free(editline->hist_file); editline->hist_file = NULL; } if (hist_size == 0) return 0; editline->history = history_init(); if (editline->history == NULL) goto fail; if (history(editline->history, &editline->histev, H_SETSIZE, hist_size) < 0) goto fail; if (history(editline->history, &editline->histev, H_SETUNIQUE, 1)) goto fail; if (hist_file != NULL) { editline->hist_file = strdup(hist_file); if (editline->hist_file == NULL) goto fail; /* ignore errors */ history(editline->history, &editline->histev, H_LOAD, editline->hist_file); } if (el_set(el, EL_HIST, history, editline->history)) goto fail; return 0; fail: if (editline->history != NULL) { history_end(editline->history); editline->history = NULL; } return -1; } char *ec_editline_curline(const struct ec_editline *editline, bool trim_after_cursor) { const LineInfo *line_info = NULL; char *line = NULL; int pos; if ((line_info = el_line(editline->el)) == NULL) { errno = ENOENT; goto fail; } if (trim_after_cursor) pos = line_info->cursor - line_info->buffer; else pos = line_info->lastchar - line_info->buffer; if (asprintf(&line, "%.*s", pos, line_info->buffer) < 0) { errno = ENOMEM; goto fail; } return line; fail: free(line); return NULL; } int ec_editline_complete(struct editline *el, int c) { struct ec_editline *editline; int ret = CC_REFRESH; struct ec_comp *cmpl = NULL; char *append = NULL; unsigned int height; unsigned int width; size_t comp_count; void *clientdata; char *line = NULL; FILE *out, *err; if (el_get(el, EL_GETFP, 1, &out)) return -1; if (el_get(el, EL_GETFP, 2, &err)) return -1; (void)c; if (el_get(el, EL_CLIENTDATA, &clientdata)) { fprintf(err, "completion failure: no client data\n"); goto fail; } editline = clientdata; (void)editline; line = ec_editline_curline(editline, true); if (line == NULL) { fprintf(err, "completion failure: %s\n", strerror(errno)); goto fail; } if (editline->node == NULL) { fprintf(err, "completion failure: no ec_node\n"); goto fail; } ec_editline_term_size(editline, &width, &height); if (width > 100) width = 100; if (width < 50) width = 50; cmpl = ec_complete(editline->node, line); if (cmpl == NULL) goto fail; append = ec_interact_append_chars(cmpl); comp_count = ec_comp_count(cmpl, EC_COMP_FULL) + ec_comp_count(cmpl, EC_COMP_PARTIAL); if (c == '?') { struct ec_interact_help *helps = NULL; ssize_t count = 0; size_t char_idx; count = ec_interact_get_helps(editline->node, line, &helps); if (count < 0) { fprintf(err, "completion failure: failed to get helps\n"); goto fail; } fprintf(out, "\n"); if (count != 0 && ec_interact_print_helps(out, width, helps, count) < 0) { fprintf(err, "completion failure: cannot show help\n"); ec_interact_free_helps(helps, count); goto fail; } ec_interact_free_helps(helps, count); if (count == 0) { count = ec_interact_get_error_helps( editline->node, line, &helps, &char_idx ); if (count < 0) { fprintf(err, "completion failure: failed to get error helps\n"); goto fail; } if (count != 0 && ec_interact_print_error_helps( out, width, line, helps, count, char_idx ) < 0) { fprintf(err, "completion failure: cannot show help\n"); ec_interact_free_helps(helps, count); goto fail; } ec_interact_free_helps(helps, count); } ret = CC_REDISPLAY; } else if (append == NULL || (strcmp(append, "") == 0 && comp_count != 1)) { char **matches = NULL; ssize_t count = 0; count = ec_interact_get_completions(cmpl, &matches, EC_COMP_FULL | EC_COMP_PARTIAL); if (count < 0) { fprintf(err, "completion failure: cannot get completions\n"); goto fail; } if (ec_interact_print_cols( out, width, EC_CAST(matches, char **, char const *const *), count ) < 0) { fprintf(err, "completion failure: cannot print\n"); ec_interact_free_completions(matches, count); goto fail; } ec_interact_free_completions(matches, count); ret = CC_REDISPLAY; } else { if (strcmp(append, "") != 0 && el_insertstr(el, append) < 0) { fprintf(err, "completion failure: cannot insert\n"); goto fail; } if (comp_count == 1 && ec_comp_count(cmpl, EC_COMP_FULL) == 1) { if (el_insertstr(el, " ") < 0) { fprintf(err, "completion failure: cannot insert space\n"); goto fail; } } } ec_comp_free(cmpl); free(line); free(append); return ret; fail: ec_comp_free(cmpl); free(line); free(append); return CC_ERROR; } char *ec_editline_gets(struct ec_editline *editline) { struct editline *el = editline->el; char *line_copy = NULL; const char *line; int count; line = el_gets(el, &count); if (line == NULL) return NULL; line_copy = strdup(line); if (line_copy == NULL) goto fail; line_copy[strcspn(line_copy, "\r\n")] = '\0'; /* strip line end characters */ if (editline->history != NULL && !ec_str_is_space(line_copy)) { history(editline->history, &editline->histev, H_ENTER, line_copy); if (editline->hist_file != NULL) history(editline->history, &editline->histev, H_SAVE, editline->hist_file); } return line_copy; fail: free(line_copy); return NULL; } struct ec_pnode *ec_editline_parse(struct ec_editline *editline) { struct ec_pnode *parse = NULL; const struct ec_node *node; char *line = NULL; node = ec_editline_get_node(editline); if (node == NULL) goto fail; line = ec_editline_gets(editline); if (line == NULL) goto fail; parse = ec_parse(node, line); if (parse == NULL) goto fail; free(line); return parse; fail: free(line); ec_pnode_free(parse); return NULL; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* Deprecated wrappers for symbols moved to interact.h. */ ssize_t ec_editline_get_completions(const struct ec_comp *cmpl, char ***matches_out) { return ec_interact_get_completions(cmpl, matches_out, EC_COMP_FULL | EC_COMP_PARTIAL); } void ec_editline_free_completions(char **matches, size_t n) { ec_interact_free_completions(matches, n); } int ec_editline_print_cols(struct ec_editline *editline, char const *const *matches, size_t n) { unsigned int width = 80, height; FILE *out; if (el_get(editline->el, EL_GETFP, 1, &out)) return -1; ec_editline_term_size(editline, &width, &height); return ec_interact_print_cols(out, width, matches, n); } char *ec_editline_append_chars(const struct ec_comp *cmpl) { return ec_interact_append_chars(cmpl); } ssize_t ec_editline_get_helps( const struct ec_editline *editline, const char *line, struct ec_editline_help **helps_out ) { char *curline = NULL; ssize_t ret; if (line == NULL) { curline = ec_editline_curline(editline, true); if (curline == NULL) return -1; line = curline; } ret = ec_interact_get_helps(editline->node, line, (struct ec_interact_help **)helps_out); free(curline); return ret; } int ec_editline_print_helps( const struct ec_editline *editline, const struct ec_editline_help *helps, size_t n ) { unsigned int width = 80, height; FILE *out; if (el_get(editline->el, EL_GETFP, 1, &out)) return -1; ec_editline_term_size(editline, &width, &height); if (width > 100) width = 100; if (width < 50) width = 50; return ec_interact_print_helps(out, width, (const struct ec_interact_help *)helps, n); } void ec_editline_free_helps(struct ec_editline_help *helps, size_t n) { ec_interact_free_helps((struct ec_interact_help *)helps, n); } ssize_t ec_editline_get_error_helps( const struct ec_editline *editline, struct ec_editline_help **helps_out, size_t *char_idx ) { char *line; if (editline == NULL || helps_out == NULL) { errno = EINVAL; return -1; } line = ec_editline_curline(editline, false); if (line == NULL) return 0; ssize_t ret = ec_interact_get_error_helps( editline->node, line, (struct ec_interact_help **)helps_out, char_idx ); free(line); return ret; } int ec_editline_print_error_helps( const struct ec_editline *editline, const struct ec_editline_help *helps, size_t n, size_t char_idx ) { unsigned int width = 80, height; char *line = NULL; FILE *out; int ret; if (el_get(editline->el, EL_GETFP, 1, &out)) return -1; ec_editline_term_size(editline, &width, &height); if (width > 100) width = 100; if (width < 50) width = 50; line = ec_editline_curline(editline, false); if (line == NULL) return -1; ret = ec_interact_print_error_helps( out, width, line, (const struct ec_interact_help *)helps, n, char_idx ); free(line); return ret; } int ec_editline_set_help(struct ec_node *node, const char *help) { return ec_interact_set_help(node, help); } int ec_editline_set_callback(struct ec_node *node, ec_interact_command_cb_t cb) { return ec_interact_set_callback(node, cb); } int ec_editline_set_desc(struct ec_node *node, const char *desc) { return ec_interact_set_desc(node, desc); } #pragma GCC diagnostic pop int ec_editline_interact( struct ec_editline *editline, ec_editline_check_exit_cb_t check_exit_cb, void *opaque ) { struct ec_interact_help *helps = NULL; struct ec_strvec *line_vec = NULL; struct ec_pnode *parse = NULL; ec_interact_command_cb_t cb; const struct ec_node *node; size_t char_idx = 0; unsigned int height; unsigned int width; char *line = NULL; ssize_t n; FILE *out; FILE *err; if (el_get(editline->el, EL_GETFP, 1, &out)) return -1; if (el_get(editline->el, EL_GETFP, 2, &err)) return -1; node = ec_editline_get_node(editline); if (node == NULL) goto fail; while (check_exit_cb == NULL || !check_exit_cb(opaque)) { line = ec_editline_gets(editline); if (line == NULL) { fprintf(err, "\nExit using ctrl-d\n"); goto fail; } line_vec = ec_strvec_sh_lex_str(line, EC_STRVEC_STRICT, NULL); if (line_vec == NULL) { if (errno == EBADMSG) { fprintf(err, "Unterminated quote\n"); goto again; } fprintf(err, "Failed to split line\n"); goto fail; } if (ec_strvec_len(line_vec) == 0) goto again; parse = ec_parse(node, line); if (parse == NULL) { fprintf(err, "Failed to parse command\n"); goto fail; } if (!ec_pnode_matches(parse)) { ec_editline_term_size(editline, &width, &height); if (width > 100) width = 100; if (width < 50) width = 50; n = ec_interact_get_error_helps(editline->node, line, &helps, &char_idx); if (n < 0 || ec_interact_print_error_helps(out, width, line, helps, n, char_idx) < 0) fprintf(err, "Invalid command\n"); if (n >= 0) ec_interact_free_helps(helps, n); helps = NULL; goto again; } cb = ec_interact_get_callback(parse); if (cb == NULL) { fprintf(err, "Callback function missing\n"); goto fail; } if (cb(parse) < 0) { fprintf(err, "Command function returned an error\n"); goto again; } again: free(line); line = NULL; ec_pnode_free(parse); parse = NULL; ec_strvec_free(line_vec); line_vec = NULL; } return 0; fail: free(line); ec_pnode_free(parse); ec_strvec_free(line_vec); return -1; } ������������������������������������������������������������������������������������libecoli-0.11.6/src/htable.c������������������������������������������������������������������������0000664�0000000�0000000�00000016025�15203622040�0015551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <ecoli/htable.h> #include <ecoli/init.h> #include <ecoli/log.h> #include <ecoli/murmurhash.h> #include "htable_private.h" #define FACTOR 3 EC_LOG_TYPE_REGISTER(htable); static bool seed_forced; static uint32_t ec_htable_seed; struct ec_htable *ec_htable(void) { struct ec_htable *htable; htable = calloc(1, sizeof(*htable)); if (htable == NULL) return NULL; TAILQ_INIT(&htable->list); return htable; } static struct ec_htable_elt_ref * ec_htable_lookup(const struct ec_htable *htable, const void *key, size_t key_len) { struct ec_htable_elt_ref *ref; uint32_t h, mask = htable->table_size - 1; if (htable == NULL || key == NULL) { errno = EINVAL; return NULL; } if (htable->table_size == 0) { errno = ENOENT; return NULL; } h = ec_murmurhash3(key, key_len, ec_htable_seed); TAILQ_FOREACH (ref, &htable->table[h & mask], hnext) { if (ref->elt->key_len != key_len) continue; if (memcmp(ref->elt->key, key, key_len) == 0) return ref; } errno = ENOENT; return NULL; } static void ec_htable_elt_ref_free(struct ec_htable_elt_ref *ref) { struct ec_htable_elt *elt; if (ref == NULL) return; elt = ref->elt; if (elt != NULL && --elt->refcount == 0) { free(elt->key); if (elt->free != NULL) elt->free(elt->val); free(elt); } free(ref); } bool ec_htable_has_key(const struct ec_htable *htable, const void *key, size_t key_len) { return !!ec_htable_lookup(htable, key, key_len); } void *ec_htable_get(const struct ec_htable *htable, const void *key, size_t key_len) { struct ec_htable_elt_ref *ref; ref = ec_htable_lookup(htable, key, key_len); if (ref == NULL) return NULL; return ref->elt->val; } int ec_htable_del(struct ec_htable *htable, const void *key, size_t key_len) { struct ec_htable_elt_ref *ref; size_t idx; ref = ec_htable_lookup(htable, key, key_len); if (ref == NULL) return -1; /* we could resize table here */ TAILQ_REMOVE(&htable->list, ref, next); idx = ref->elt->hash & (htable->table_size - 1); TAILQ_REMOVE(&htable->table[idx], ref, hnext); ec_htable_elt_ref_free(ref); htable->len--; return 0; } static int ec_htable_table_resize(struct ec_htable *htable, size_t new_size) { struct ec_htable_elt_ref_list *new_table; struct ec_htable_elt_ref *ref; size_t i; if (new_size == 0 || (new_size & (new_size - 1))) { errno = EINVAL; return -1; } new_table = calloc(new_size, sizeof(*htable->table)); if (new_table == NULL) return -1; for (i = 0; i < new_size; i++) TAILQ_INIT(&new_table[i]); TAILQ_FOREACH (ref, &htable->list, next) { i = ref->elt->hash & (htable->table_size - 1); TAILQ_REMOVE(&htable->table[i], ref, hnext); i = ref->elt->hash & (new_size - 1); TAILQ_INSERT_TAIL(&new_table[i], ref, hnext); } free(htable->table); htable->table = new_table; htable->table_size = new_size; return 0; } static int __ec_htable_set(struct ec_htable *htable, struct ec_htable_elt_ref *ref) { size_t new_size; uint32_t mask; int ret; /* remove previous entry if any */ ec_htable_del(htable, ref->elt->key, ref->elt->key_len); if (htable->len >= htable->table_size) { if (htable->table_size != 0) new_size = htable->table_size << FACTOR; else new_size = 1 << FACTOR; ret = ec_htable_table_resize(htable, new_size); if (ret < 0) return ret; } mask = htable->table_size - 1; TAILQ_INSERT_TAIL(&htable->table[ref->elt->hash & mask], ref, hnext); TAILQ_INSERT_TAIL(&htable->list, ref, next); htable->len++; return 0; } int ec_htable_set( struct ec_htable *htable, const void *key, size_t key_len, void *val, ec_htable_elt_free_t free_cb ) { struct ec_htable_elt *elt = NULL; struct ec_htable_elt_ref *ref = NULL; uint32_t h; if (htable == NULL || key == NULL || key_len == 0) { errno = EINVAL; return -1; } ref = calloc(1, sizeof(*ref)); if (ref == NULL) goto fail; elt = calloc(1, sizeof(*elt)); if (elt == NULL) goto fail; ref->elt = elt; elt->refcount = 1; elt->val = val; val = NULL; elt->free = free_cb; elt->key_len = key_len; elt->key = malloc(key_len); if (elt->key == NULL) goto fail; memcpy(elt->key, key, key_len); h = ec_murmurhash3(key, key_len, ec_htable_seed); elt->hash = h; if (__ec_htable_set(htable, ref) < 0) goto fail; return 0; fail: if (free_cb != NULL && val != NULL) free_cb(val); ec_htable_elt_ref_free(ref); return -1; } void ec_htable_free(struct ec_htable *htable) { struct ec_htable_elt_ref *ref; size_t idx; if (htable == NULL) return; while (!TAILQ_EMPTY(&htable->list)) { ref = TAILQ_FIRST(&htable->list); TAILQ_REMOVE(&htable->list, ref, next); idx = ref->elt->hash & (htable->table_size - 1); TAILQ_REMOVE(&htable->table[idx], ref, hnext); ec_htable_elt_ref_free(ref); } free(htable->table); free(htable); } size_t ec_htable_len(const struct ec_htable *htable) { return htable->len; } struct ec_htable_elt_ref *ec_htable_iter(const struct ec_htable *htable) { if (htable == NULL) return NULL; return TAILQ_FIRST(&htable->list); } struct ec_htable_elt_ref *ec_htable_iter_next(struct ec_htable_elt_ref *iter) { if (iter == NULL) return NULL; return TAILQ_NEXT(iter, next); } const void *ec_htable_iter_get_key(const struct ec_htable_elt_ref *iter) { if (iter == NULL) return NULL; return iter->elt->key; } size_t ec_htable_iter_get_key_len(const struct ec_htable_elt_ref *iter) { if (iter == NULL) return 0; return iter->elt->key_len; } void *ec_htable_iter_get_val(const struct ec_htable_elt_ref *iter) { if (iter == NULL) return NULL; return iter->elt->val; } void ec_htable_dump(FILE *out, const struct ec_htable *htable) { if (htable == NULL) { fprintf(out, "empty htable\n"); return; } fprintf(out, "htable: %zd elements\n", htable->len); } struct ec_htable *ec_htable_dup(const struct ec_htable *htable) { struct ec_htable *dup = NULL; struct ec_htable_elt_ref *ref, *dup_ref = NULL; dup = ec_htable(); if (dup == NULL) return NULL; TAILQ_FOREACH (ref, &htable->list, next) { dup_ref = calloc(1, sizeof(*ref)); if (dup_ref == NULL) goto fail; dup_ref->elt = ref->elt; ref->elt->refcount++; if (__ec_htable_set(dup, dup_ref) < 0) goto fail; } return dup; fail: ec_htable_elt_ref_free(dup_ref); ec_htable_free(dup); return NULL; } void ec_htable_force_seed(uint32_t seed) { ec_htable_seed = seed; seed_forced = true; } static int ec_htable_init_func(void) { int fd; ssize_t ret; if (seed_forced) return 0; fd = open("/dev/urandom", 0); if (fd == -1) { fprintf(stderr, "failed to open /dev/urandom\n"); return -1; } ret = read(fd, &ec_htable_seed, sizeof(ec_htable_seed)); if (ret != sizeof(ec_htable_seed)) { fprintf(stderr, "failed to read /dev/urandom\n"); close(fd); return -1; } close(fd); return 0; } static struct ec_init ec_htable_init = { .init = ec_htable_init_func, .priority = 50, }; EC_INIT_REGISTER(ec_htable_init); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/htable_private.h����������������������������������������������������������������0000664�0000000�0000000�00000001205�15203622040�0017302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2019, Olivier MATZ <zer0@droids-corp.org> */ #pragma once #include <stdint.h> #include <sys/queue.h> #include <ecoli/htable.h> struct ec_htable_elt { void *key; size_t key_len; void *val; uint32_t hash; ec_htable_elt_free_t free; unsigned int refcount; }; struct ec_htable_elt_ref { TAILQ_ENTRY(ec_htable_elt_ref) next; TAILQ_ENTRY(ec_htable_elt_ref) hnext; struct ec_htable_elt *elt; }; TAILQ_HEAD(ec_htable_elt_ref_list, ec_htable_elt_ref); struct ec_htable { size_t len; size_t table_size; struct ec_htable_elt_ref_list list; struct ec_htable_elt_ref_list *table; }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/init.c��������������������������������������������������������������������������0000664�0000000�0000000�00000001710�15203622040�0015250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/init.h> static struct ec_init_list init_list = TAILQ_HEAD_INITIALIZER(init_list); /* register an init function */ void ec_init_register(struct ec_init *init) { struct ec_init *cur; if (TAILQ_EMPTY(&init_list)) { TAILQ_INSERT_HEAD(&init_list, init, next); return; } TAILQ_FOREACH (cur, &init_list, next) { if (init->priority > cur->priority) continue; TAILQ_INSERT_BEFORE(cur, init, next); return; } TAILQ_INSERT_TAIL(&init_list, init, next); } int ec_init(void) { struct ec_init *init; TAILQ_FOREACH (init, &init_list, next) { if (init->init != NULL && init->init() < 0) return -1; } return 0; } void ec_exit(void) { struct ec_init *init; TAILQ_FOREACH_REVERSE(init, &init_list, ec_init_list, next) { if (init->exit != NULL) init->exit(); } } ��������������������������������������������������������libecoli-0.11.6/src/interact.c����������������������������������������������������������������������0000664�0000000�0000000�00000025111�15203622040�0016117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <ecoli/complete.h> #include <ecoli/dict.h> #include <ecoli/interact.h> #include <ecoli/node.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> #include <ecoli/utils.h> /* Show the matches as a multi-columns list */ int ec_interact_print_cols(FILE *out, unsigned int width, char const *const *matches, size_t n) { size_t max_strlen = 0, len, i, j, ncols; const char *space; fprintf(out, "\n"); if (n == 0) return 0; /* get max string length */ for (i = 0; i < n; i++) { len = strlen(matches[i]); if (len > max_strlen) max_strlen = len; } /* write the columns */ ncols = width / (max_strlen + 4); if (ncols == 0) ncols = 1; for (i = 0; i < n; i += ncols) { for (j = 0; j < ncols; j++) { if (i + j >= n) break; if (j == 0) space = ""; else space = " "; fprintf(out, "%s%-*s", space, (int)max_strlen, matches[i + j]); } fprintf(out, "\n"); } return 0; } /* Show the helps on command line output */ int ec_interact_print_helps( FILE *out, unsigned int width, const struct ec_interact_help *helps, size_t len ) { char *wrapped_help = NULL; size_t i; for (i = 0; i < len; i++) { wrapped_help = ec_str_wrap(helps[i].help, width, 23); if (wrapped_help == NULL) goto fail; if (strlen(helps[i].desc) > 20) { if (fprintf(out, " %s\n", helps[i].desc) < 0) goto fail; if (fprintf(out, " %-20s %s\n", "", wrapped_help) < 0) goto fail; } else { if (fprintf(out, " %-20s %s\n", helps[i].desc, wrapped_help) < 0) goto fail; } free(wrapped_help); wrapped_help = NULL; } return 0; fail: free(wrapped_help); return -1; } void ec_interact_free_helps(struct ec_interact_help *helps, size_t n) { size_t i; if (helps == NULL) return; for (i = 0; i < n; i++) { free(helps[i].desc); free(helps[i].help); } free(helps); } void ec_interact_free_completions(char **matches, size_t len) { size_t i; if (matches == NULL) return; for (i = 0; i < len; i++) free(matches[i]); free(matches); } static int comp_strcasecmp_cb(const void *p1, const void *p2) { char *const *s1 = p1; char *const *s2 = p2; return strcasecmp(*s1, *s2); } ssize_t ec_interact_get_completions( const struct ec_comp *cmpl, char ***matches_out, enum ec_comp_type type_mask ) { struct ec_comp_item *item; char **matches = NULL; size_t count = 0; EC_COMP_FOREACH (item, cmpl, type_mask) { char **tmp; tmp = realloc(matches, (count + 1) * sizeof(char *)); if (tmp == NULL) goto fail; matches = tmp; matches[count] = strdup(ec_comp_item_get_display(item)); if (matches[count] == NULL) goto fail; count++; } qsort(matches, count, sizeof(char *), comp_strcasecmp_cb); *matches_out = matches; return count; fail: ec_interact_free_completions(matches, count); *matches_out = NULL; return -1; } char *ec_interact_append_chars(const struct ec_comp *cmpl) { struct ec_comp_item *item; const char *append; char *ret = NULL; size_t n; EC_COMP_FOREACH (item, cmpl, EC_COMP_FULL | EC_COMP_PARTIAL) { append = ec_comp_item_get_completion(item); if (ret == NULL) { ret = strdup(append); if (ret == NULL) goto fail; } else { n = ec_strcmp_count(ret, append); ret[n] = '\0'; } } if (ret == NULL) ret = strdup(""); return ret; fail: free(ret); return NULL; } static int help_strcasecmp_cb(const void *p1, const void *p2) { const struct ec_interact_help *h1 = p1; const struct ec_interact_help *h2 = p2; return strcasecmp(h1->desc, h2->desc); } /* this function builds the help string */ static int get_node_help(const struct ec_comp_item *item, struct ec_interact_help *help) { const struct ec_comp_group *grp; const struct ec_pnode *pstate; const struct ec_node *node; const char *node_help = NULL; const char *node_desc = NULL; char *desc_to_free = NULL; help->desc = NULL; help->help = NULL; grp = ec_comp_item_get_grp(item); for (pstate = ec_comp_group_get_pstate(grp); pstate != NULL; pstate = ec_pnode_get_parent(pstate)) { node = ec_pnode_get_node(pstate); if (node_help == NULL) node_help = ec_dict_get(ec_node_attrs(node), EC_INTERACT_HELP_ATTR); if (node_desc == NULL) node_desc = ec_dict_get(ec_node_attrs(node), EC_INTERACT_DESC_ATTR); if (node_desc == NULL) { node_desc = ec_node_desc(node); if (node_desc == NULL) goto fail; desc_to_free = (char *)node_desc; } if (node_desc == NULL) goto fail; } if (node_desc == NULL) goto fail; if (node_help == NULL) node_help = ""; help->desc = strdup(node_desc); help->help = strdup(node_help); if (help->help == NULL || help->desc == NULL) goto fail; free(desc_to_free); return 0; fail: free(desc_to_free); free(help->help); free(help->desc); help->desc = NULL; help->help = NULL; return -1; } ssize_t ec_interact_get_helps( const struct ec_node *node, const char *line, struct ec_interact_help **helps_out ) { const struct ec_comp_group *grp, *prev_grp = NULL; struct ec_comp_item *item; struct ec_comp *cmpl = NULL; struct ec_pnode *parse = NULL; unsigned int count = 0; struct ec_interact_help *helps = NULL; *helps_out = NULL; /* check if the current line matches */ parse = ec_parse(node, line); if (ec_pnode_matches(parse)) count = 1; ec_pnode_free(parse); parse = NULL; /* complete at current cursor position */ cmpl = ec_complete(node, line); if (cmpl == NULL) goto fail; helps = calloc(1, sizeof(*helps)); if (helps == NULL) goto fail; if (count == 1) { helps[0].desc = strdup("<return>"); if (helps[0].desc == NULL) goto fail; helps[0].help = strdup("Validate command."); if (helps[0].help == NULL) goto fail; } /* let's display one contextual help per node */ EC_COMP_FOREACH (item, cmpl, EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL) { struct ec_interact_help *tmp = NULL; /* keep one help per group, skip other items */ grp = ec_comp_item_get_grp(item); if (grp == prev_grp) continue; prev_grp = grp; tmp = realloc(helps, (count + 1) * sizeof(*helps)); if (tmp == NULL) goto fail; helps = tmp; if (get_node_help(item, &helps[count]) < 0) goto fail; count++; } ec_comp_free(cmpl); qsort(helps, count, sizeof(struct ec_interact_help), help_strcasecmp_cb); *helps_out = helps; return count; fail: ec_pnode_free(parse); ec_comp_free(cmpl); if (helps != NULL) { while (count--) { free(helps[count].desc); free(helps[count].help); } free(helps); } return -1; } ssize_t ec_interact_get_error_helps( const struct ec_node *node, const char *line, struct ec_interact_help **helps_out, size_t *char_idx ) { struct ec_strvec *line_vec_partial = NULL; const struct ec_strvec *parsed_vec = NULL; struct ec_strvec *line_vec = NULL; struct ec_pnode *parse = NULL; struct ec_comp *comp = NULL; const struct ec_dict *attrs; struct ec_node *cmdlist; char *line_copy = NULL; size_t parsed_vec_len; int ret = 0; size_t len; int i; if (helps_out == NULL) { errno = EINVAL; goto out; } *helps_out = NULL; /* one additional char to add a space at the end */ line_copy = malloc(strlen(line) + 2); if (line_copy == NULL) goto fail; strcpy(line_copy, line); line_copy[strlen(line)] = ' '; line_copy[strlen(line) + 1] = '\0'; if (ec_node_get_child(node, 0, &cmdlist) < 0) goto fail; line_vec = ec_strvec_sh_lex_str(line, EC_STRVEC_STRICT, NULL); if (line_vec == NULL && errno == EBADMSG) goto fail; if (line_vec == NULL) goto fail; len = ec_strvec_len(line_vec); for (i = len; i >= 0; i--) { /* build an strvec from the first i tokens + an empty token */ line_vec_partial = ec_strvec_ndup(line_vec, 0, i); if (line_vec_partial == NULL) goto fail; if (ec_strvec_add(line_vec_partial, "") < 0) goto fail; /* try to parse and complete this strvec */ parse = ec_parse_strvec(cmdlist, line_vec_partial); if (parse == NULL) goto fail; comp = ec_complete_strvec(cmdlist, line_vec_partial); if (comp == NULL) goto fail; /* get the length of the parsed vec, if any */ parsed_vec = ec_pnode_get_strvec(parse); if (parsed_vec != NULL) parsed_vec_len = ec_strvec_len(parsed_vec); else parsed_vec_len = 0; /* if it matches or if it completes, return the helps */ if ((ec_pnode_matches(parse) && (int)parsed_vec_len == i) || ec_comp_count(comp, EC_COMP_ALL) > 0) { /* get the position of the error and store it in char_idx */ if (i == (int)len) { attrs = ec_strvec_get_attrs(line_vec, i - 1); if (attrs == NULL) goto fail; *char_idx = (uintptr_t)ec_dict_get(attrs, EC_STRVEC_ATTR_END) + 1; } else { attrs = ec_strvec_get_attrs(line_vec, i); if (attrs == NULL) goto fail; *char_idx = (uintptr_t)ec_dict_get(attrs, EC_STRVEC_ATTR_START); } /* build the partial line string */ line_copy[*char_idx] = '\0'; ret = ec_interact_get_helps(node, line_copy, helps_out); goto out; } ec_pnode_free(parse); parse = NULL; ec_comp_free(comp); comp = NULL; ec_strvec_free(line_vec_partial); line_vec_partial = NULL; } ret = 0; out: ec_strvec_free(line_vec_partial); ec_strvec_free(line_vec); free(line_copy); ec_pnode_free(parse); ec_comp_free(comp); return ret; fail: ret = -1; goto out; } int ec_interact_print_error_helps( FILE *out, unsigned int width, const char *line, const struct ec_interact_help *helps, size_t n, size_t char_idx ) { fprintf(out, " %s", line); if (strcspn(line, "\n") == strlen(line)) fprintf(out, "\n"); fprintf(out, " %*s^\n", (int)char_idx, ""); fprintf(out, "Expected:\n"); if (ec_interact_print_helps(out, width, helps, n) < 0) return -1; return 0; } int ec_interact_set_help(struct ec_node *node, const char *help) { char *copy = strdup(help); if (copy == NULL) return -1; return ec_dict_set(ec_node_attrs(node), EC_INTERACT_HELP_ATTR, copy, free); } int ec_interact_set_callback(struct ec_node *node, ec_interact_command_cb_t cb) { return ec_dict_set(ec_node_attrs(node), EC_INTERACT_CB_ATTR, cb, NULL); } int ec_interact_set_desc(struct ec_node *node, const char *desc) { char *copy = strdup(desc); if (copy == NULL) return -1; return ec_dict_set(ec_node_attrs(node), EC_INTERACT_DESC_ATTR, copy, free); } ec_interact_command_cb_t ec_interact_get_callback(struct ec_pnode *parse) { struct ec_pnode *iter; ec_interact_command_cb_t cb; for (iter = parse; iter != NULL; iter = EC_PNODE_ITER_NEXT(parse, iter, 1)) { cb = ec_dict_get(ec_node_attrs(ec_pnode_get_node(iter)), EC_INTERACT_CB_ATTR); if (cb != NULL) return cb; } return NULL; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/log.c���������������������������������������������������������������������������0000664�0000000�0000000�00000004524�15203622040�0015074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <syslog.h> #include <ecoli/log.h> #include <ecoli/string.h> /* Test exports */ int ec_log_lookup(const char *name); EC_LOG_TYPE_REGISTER(log); ec_log_t ec_log_fct = ec_log_default_cb; void *ec_log_opaque; static size_t log_types_len; static enum ec_log_level global_level = EC_LOG_WARNING; TAILQ_HEAD(ec_log_type_list, ec_log_type); static struct ec_log_type_list log_type_list = TAILQ_HEAD_INITIALIZER(log_type_list); int ec_log_level_set(enum ec_log_level level) { if (level > EC_LOG_DEBUG) return -1; global_level = level; return 0; } enum ec_log_level ec_log_level_get(void) { return global_level; } int ec_log_default_cb(int type, enum ec_log_level level, void *opaque, const char *str) { (void)opaque; if (level > ec_log_level_get()) return 0; if (fprintf(stderr, "[%d] %-12s %s", level, ec_log_name(type), str) < 0) return -1; return 0; } int ec_log_fct_register(ec_log_t usr_log, void *opaque) { if (usr_log == NULL) { ec_log_fct = ec_log_default_cb; ec_log_opaque = NULL; } else { ec_log_fct = usr_log; ec_log_opaque = opaque; } return 0; } int ec_log_lookup(const char *name) { struct ec_log_type *type; TAILQ_FOREACH (type, &log_type_list, next) { if (type->name != NULL && strcmp(name, type->name) == 0) return type->id; } return -1; } int ec_log_type_register(struct ec_log_type *type) { int id; id = ec_log_lookup(type->name); if (id >= 0) return id; TAILQ_INSERT_HEAD(&log_type_list, type, next); type->level = EC_LOG_DEBUG; type->id = log_types_len++; return type->id; } const char *ec_log_name(int type_id) { struct ec_log_type *type; TAILQ_FOREACH (type, &log_type_list, next) { if (type->id == type_id && type->name != NULL) return type->name; } return "unknown"; } int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap) { char *s; int ret; ret = vasprintf(&s, format, ap); if (ret < 0) return ret; ret = ec_log_fct(type, level, ec_log_opaque, s); free(s); return ret; } int ec_log(int type, enum ec_log_level level, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = ec_vlog(type, level, format, ap); va_end(ap); return ret; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/meson.build���������������������������������������������������������������������0000664�0000000�0000000�00000001564�15203622040�0016312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright 2018, Olivier MATZ <zer0@droids-corp.org> libecoli_sources += files( 'assert.c', 'complete.c', 'config.c', 'dict.c', 'htable.c', 'init.c', 'interact.c', 'log.c', 'murmurhash.c', 'node.c', 'node_any.c', 'node_bypass.c', 'node_cmd.c', 'node_cond.c', 'node_dynamic.c', 'node_dynlist.c', 'node_empty.c', 'node_expr.c', 'node_file.c', 'node_helper.c', 'node_int.c', 'node_many.c', 'node_none.c', 'node_once.c', 'node_option.c', 'node_or.c', 'node_re.c', 'node_re_lex.c', 'node_seq.c', 'node_sh_lex.c', 'node_space.c', 'node_str.c', 'node_subset.c', 'parse.c', 'string.c', 'strvec.c', 'vec.c', ) deps = [] if yaml_dep.found() libecoli_sources += files( 'yaml.c', ) deps += [ yaml_dep, ] endif if edit_dep.found() libecoli_sources += files( 'editline.c', ) deps += [ edit_dep, ] endif ��������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/murmurhash.c��������������������������������������������������������������������0000664�0000000�0000000�00000001524�15203622040�0016503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <ecoli/murmurhash.h> uint32_t ec_murmurhash3(const void *key, int len, uint32_t seed) { const uint8_t *data = (const uint8_t *)key; const uint8_t *tail; const int nblocks = len / 4; uint32_t h1 = seed; uint32_t k1; const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); int i; for (i = -nblocks; i; i++) { k1 = blocks[i]; h1 = ec_murmurhash3_add32(h1, k1); h1 = ec_murmurhash3_mix32(h1); } tail = (const uint8_t *)(data + nblocks * 4); k1 = 0; switch (len & 3) { case 3: k1 ^= tail[2] << 16; /* fallthrough */ case 2: k1 ^= tail[1] << 8; /* fallthrough */ case 1: k1 ^= tail[0]; h1 = ec_murmurhash3_add32(h1, k1); }; /* finalization */ h1 ^= len; h1 = ec_murmurhash3_fmix32(h1); return h1; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node.c��������������������������������������������������������������������������0000664�0000000�0000000�00000030714�15203622040�0015240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/htable.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_int.h> #include <ecoli/node_or.h> #include <ecoli/node_seq.h> #include <ecoli/node_str.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node); /* These states are used to mark the grammar graph when freeing, to * detect loop. */ enum ec_node_free_state { EC_NODE_FREE_STATE_NONE, EC_NODE_FREE_STATE_TRAVERSED, EC_NODE_FREE_STATE_FREEABLE, EC_NODE_FREE_STATE_NOT_FREEABLE, EC_NODE_FREE_STATE_FREEING, }; /** * The grammar node structure. */ struct ec_node { const struct ec_node_type *type; /**< The node type. */ struct ec_config *config; /**< Node configuration. */ char *id; /**< Node identifier (EC_NO_ID if none). */ struct ec_dict *attrs; /**< Attributes of the node. */ unsigned int refcnt; /**< Reference counter. */ struct { enum ec_node_free_state state; /**< State of loop detection. */ unsigned int refcnt; /**< Number of reachable references * starting from node being freed. */ } free; /**< Freeing state: used for loop detection */ }; struct ec_node_type_list node_type_list = TAILQ_HEAD_INITIALIZER(node_type_list); static int __ec_node_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ); const struct ec_node_type *ec_node_type_lookup(const char *name) { struct ec_node_type *type; TAILQ_FOREACH (type, &node_type_list, next) { if (!strcmp(name, type->name)) return type; } errno = ENOENT; return NULL; } int ec_node_type_register(struct ec_node_type *type, bool override) { if (!override && ec_node_type_lookup(type->name) != NULL) { errno = EEXIST; return -1; } TAILQ_INSERT_HEAD(&node_type_list, type, next); return 0; } void ec_node_type_dump(FILE *out) { struct ec_node_type *type; TAILQ_FOREACH (type, &node_type_list, next) fprintf(out, "%s\n", type->name); } struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *id) { struct ec_node *node = NULL; EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n", type->name, id); if (id == NULL) { errno = EINVAL; goto fail; } node = calloc(1, sizeof(*node) + type->size); if (node == NULL) goto fail; node->type = type; node->refcnt = 1; node->id = strdup(id); if (node->id == NULL) goto fail; node->attrs = ec_dict(); if (node->attrs == NULL) goto fail; if (type->init_priv != NULL) { if (type->init_priv(node) < 0) goto fail; } return node; fail: if (node != NULL) { ec_dict_free(node->attrs); free(node->id); } free(node); return NULL; } const struct ec_config_schema *ec_node_type_schema(const struct ec_node_type *type) { return type->schema; } const char *ec_node_type_name(const struct ec_node_type *type) { return type->name; } struct ec_node *ec_node(const char *typename, const char *id) { const struct ec_node_type *type; type = ec_node_type_lookup(typename); if (type == NULL) { EC_LOG(EC_LOG_ERR, "type=%s does not exist\n", typename); return NULL; } return ec_node_from_type(type, id); } static void count_references(struct ec_node *node, unsigned int refs) { struct ec_node *child; size_t i, n; int ret; if (node->free.state == EC_NODE_FREE_STATE_TRAVERSED) { node->free.refcnt += refs; return; } node->free.refcnt = refs; node->free.state = EC_NODE_FREE_STATE_TRAVERSED; n = ec_node_get_children_count(node); for (i = 0; i < n; i++) { ret = __ec_node_get_child(node, i, &child, &refs); assert(ret == 0); count_references(child, refs); } } static void mark_freeable(struct ec_node *node, enum ec_node_free_state mark) { struct ec_node *child; size_t i, n; int ret; if (mark == node->free.state) return; if (node->refcnt > node->free.refcnt) mark = EC_NODE_FREE_STATE_NOT_FREEABLE; assert(node->refcnt >= node->free.refcnt); node->free.state = mark; n = ec_node_get_children_count(node); for (i = 0; i < n; i++) { ret = ec_node_get_child(node, i, &child); assert(ret == 0); mark_freeable(child, mark); } } static void reset_mark(struct ec_node *node) { struct ec_node *child; size_t i, n; int ret; if (node->free.state == EC_NODE_FREE_STATE_NONE) return; node->free.state = EC_NODE_FREE_STATE_NONE; node->free.refcnt = 0; n = ec_node_get_children_count(node); for (i = 0; i < n; i++) { ret = ec_node_get_child(node, i, &child); assert(ret == 0); reset_mark(child); } } /* free a node, taking care of loops in the node graph */ void ec_node_free(struct ec_node *node) { size_t n; if (node == NULL) return; assert(node->refcnt > 0); if (node->free.state == EC_NODE_FREE_STATE_NONE && node->refcnt != 1) { /* Traverse the node tree starting from this node, and for each * node, count the number of reachable references. Then, all * nodes whose reachable references == total reference are * marked as freeable, and other are marked as unfreeable. Any * node reachable from an unfreeable node is also marked as * unfreeable. */ if (node->free.state == EC_NODE_FREE_STATE_NONE) { count_references(node, 1); mark_freeable(node, EC_NODE_FREE_STATE_FREEABLE); } } if (node->free.state == EC_NODE_FREE_STATE_NOT_FREEABLE) { node->refcnt--; reset_mark(node); return; } if (node->free.state != EC_NODE_FREE_STATE_FREEING) { node->free.state = EC_NODE_FREE_STATE_FREEING; /* children will be freed by config_free() and free_priv() */ ec_config_free(node->config); node->config = NULL; n = ec_node_get_children_count(node); assert(n == 0 || node->type->free_priv != NULL); if (node->type->free_priv != NULL) node->type->free_priv(node); free(node->id); ec_dict_free(node->attrs); } node->refcnt--; if (node->refcnt != 0) return; node->free.state = EC_NODE_FREE_STATE_NONE; node->free.refcnt = 0; free(node); } struct ec_node *ec_node_clone(struct ec_node *node) { if (node != NULL) node->refcnt++; return node; } size_t ec_node_get_children_count(const struct ec_node *node) { if (node->type->get_children_count == NULL) return 0; return node->type->get_children_count(node); } static int __ec_node_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { *child = NULL; *refs = 0; if (node->type->get_child == NULL) return -1; return node->type->get_child(node, i, child, refs); } int ec_node_get_child(const struct ec_node *node, size_t i, struct ec_node **child) { unsigned int refs = 0; return __ec_node_get_child(node, i, child, &refs); } int ec_node_set_config(struct ec_node *node, struct ec_config *config) { if (node->type->schema == NULL) { errno = ENOTSUP; goto fail; } if (ec_config_validate(config, node->type->schema) < 0) goto fail; if (node->type->set_config == NULL) { errno = ENOTSUP; goto fail; } if (node->type->set_config(node, config) < 0) goto fail; ec_config_free(node->config); node->config = config; return 0; fail: ec_config_free(config); return -1; } const struct ec_config *ec_node_get_config(const struct ec_node *node) { return node->config; } struct ec_node *ec_node_find(struct ec_node *node, const char *id) { struct ec_node_iter *iter_root, *iter; struct ec_node *iter_node; iter_root = ec_node_iter(node); for (iter = iter_root; iter != NULL; iter = ec_node_iter_next(iter_root, iter, true)) { iter_node = ec_node_iter_get_node(iter); if (!strcmp(ec_node_id(iter_node), id)) break; } ec_node_iter_free(iter_root); if (iter == NULL) return NULL; return iter_node; } TAILQ_HEAD(ec_node_iter_list, ec_node_iter); struct ec_node_iter { TAILQ_ENTRY(ec_node_iter) next; struct ec_node_iter_list children; struct ec_node *node; struct ec_node_iter *parent; }; void ec_node_iter_free(struct ec_node_iter *iter) { struct ec_node_iter *child = NULL; if (iter == NULL) return; while (!TAILQ_EMPTY(&iter->children)) { child = TAILQ_FIRST(&iter->children); TAILQ_REMOVE(&iter->children, child, next); child->parent = NULL; ec_node_iter_free(child); } free(iter); } struct ec_node_iter * ec_node_iter_next(struct ec_node_iter *root, struct ec_node_iter *iter, bool iter_children) { struct ec_node_iter *child, *parent, *next; if (iter_children) { child = TAILQ_FIRST(&iter->children); if (child != NULL) return child; } parent = iter->parent; while (parent != NULL && iter != root) { next = TAILQ_NEXT(iter, next); if (next != NULL) return next; iter = parent; parent = iter->parent; } return NULL; } static int __ec_node_iter(struct ec_node_iter *iter, struct ec_htable *seen_nodes) { struct ec_node_iter *child = NULL; struct ec_node *child_node; size_t n, i; int ret; n = ec_node_get_children_count(iter->node); for (i = 0; i < n; i++) { ret = ec_node_get_child(iter->node, i, &child_node); assert(ret == 0); if (ec_htable_has_key(seen_nodes, child_node, sizeof(*child_node))) continue; if (ec_htable_set(seen_nodes, child_node, sizeof(*child_node), NULL, NULL) < 0) return -1; child = calloc(1, sizeof(*child)); if (child == NULL) return -1; TAILQ_INSERT_TAIL(&iter->children, child, next); child->parent = iter; child->node = child_node; TAILQ_INIT(&child->children); if (__ec_node_iter(child, seen_nodes) < 0) return -1; } return 0; } struct ec_node_iter *ec_node_iter(struct ec_node *node) { struct ec_htable *seen_nodes = NULL; struct ec_node_iter *iter = NULL; seen_nodes = ec_htable(); if (seen_nodes == NULL) goto fail; iter = calloc(1, sizeof(*iter)); if (iter == NULL) goto fail; TAILQ_INIT(&iter->children); iter->node = node; if (__ec_node_iter(iter, seen_nodes) < 0) goto fail; ec_htable_free(seen_nodes); return iter; fail: ec_htable_free(seen_nodes); ec_node_iter_free(iter); return NULL; } struct ec_node *ec_node_iter_get_node(struct ec_node_iter *iter) { if (iter == NULL) return NULL; return iter->node; } struct ec_node_iter *ec_node_iter_get_parent(struct ec_node_iter *iter) { if (iter == NULL) return NULL; return iter->parent; } const struct ec_node_type *ec_node_type(const struct ec_node *node) { return node->type; } struct ec_dict *ec_node_attrs(const struct ec_node *node) { return node->attrs; } const char *ec_node_id(const struct ec_node *node) { return node->id; } static void __ec_node_dump(FILE *out, const struct ec_node *node, size_t indent, struct ec_dict *dict) { const char *id, *typename; struct ec_node *child; char buf[32]; size_t i, n; char *desc; int ret; id = ec_node_id(node); desc = ec_node_desc(node); typename = node->type->name; snprintf(buf, sizeof(buf), "%p", node); if (ec_dict_has_key(dict, buf)) { fprintf(out, "%*s" "%s type=%s id=%s %p... (loop)\n", (int)indent * 4, "", desc, typename, id, node); goto end; } ec_dict_set(dict, buf, NULL, NULL); fprintf(out, "%*s" "%s type=%s id=%s %p refs=%u free_state=%d free_refs=%d\n", (int)indent * 4, "", desc, typename, id, node, node->refcnt, node->free.state, node->free.refcnt); n = ec_node_get_children_count(node); for (i = 0; i < n; i++) { ret = ec_node_get_child(node, i, &child); assert(ret == 0); __ec_node_dump(out, child, indent + 1, dict); } end: free(desc); } /* XXX this is too much debug-oriented, we should have a parameter or 2 funcs */ void ec_node_dump(FILE *out, const struct ec_node *node) { struct ec_dict *dict = NULL; fprintf(out, "------------------- node dump:\n"); if (node == NULL) { fprintf(out, "node is NULL\n"); return; } dict = ec_dict(); if (dict == NULL) goto fail; __ec_node_dump(out, node, 0, dict); ec_dict_free(dict); return; fail: ec_dict_free(dict); EC_LOG(EC_LOG_ERR, "failed to dump node\n"); } char *ec_node_desc(const struct ec_node *node) { char *desc = NULL; if (node->type->desc != NULL) return node->type->desc(node); if (asprintf(&desc, "<%s>", node->type->name) < 0) return NULL; return desc; } int ec_node_check_type(const struct ec_node *node, const struct ec_node_type *type) { if (strcmp(node->type->name, type->name)) { errno = EINVAL; return -1; } return 0; } const char *ec_node_get_type_name(const struct ec_node *node) { return node->type->name; } void *ec_node_priv(const struct ec_node *node) { if (node == NULL) return NULL; return (void *)(node + 1); } void ec_node_schema_dump(FILE *out, const struct ec_node *node) { ec_config_schema_dump(out, node->type->schema, node->type->name); } ����������������������������������������������������libecoli-0.11.6/src/node_any.c����������������������������������������������������������������������0000664�0000000�0000000�00000004736�15203622040�0016114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_any.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_any); struct ec_node_any { char *attr_name; }; static int ec_node_any_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_any *priv = ec_node_priv(node); const struct ec_dict *attrs; (void)pstate; if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; if (priv->attr_name != NULL) { attrs = ec_strvec_get_attrs(strvec, 0); if (attrs == NULL || !ec_dict_has_key(attrs, priv->attr_name)) return EC_PARSE_NOMATCH; } return 1; } static void ec_node_any_free_priv(struct ec_node *node) { struct ec_node_any *priv = ec_node_priv(node); free(priv->attr_name); } static const struct ec_config_schema ec_node_any_schema[] = { { .key = "attr", .desc = "The optional attribute name to attach.", .type = EC_CONFIG_TYPE_STRING, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_any_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_any *priv = ec_node_priv(node); const struct ec_config *value = NULL; char *s = NULL; value = ec_config_dict_get(config, "attr"); if (value != NULL) { s = strdup(value->string); if (s == NULL) goto fail; } free(priv->attr_name); priv->attr_name = s; return 0; fail: free(s); return -1; } static struct ec_node_type ec_node_any_type = { .name = "any", .schema = ec_node_any_schema, .set_config = ec_node_any_set_config, .parse = ec_node_any_parse, .size = sizeof(struct ec_node_any), .free_priv = ec_node_any_free_priv, }; EC_NODE_TYPE_REGISTER(ec_node_any_type); struct ec_node *ec_node_any(const char *id, const char *attr) { struct ec_config *config = NULL; struct ec_node *node = NULL; int ret; node = ec_node_from_type(&ec_node_any_type, id); if (node == NULL) return NULL; config = ec_config_dict(); if (config == NULL) goto fail; if (attr != NULL) { ret = ec_config_dict_set(config, "attr", ec_config_string(attr)); if (ret < 0) goto fail; } ret = ec_node_set_config(node, config); config = NULL; if (ret < 0) goto fail; return node; fail: ec_config_free(config); ec_node_free(node); return NULL; } ����������������������������������libecoli-0.11.6/src/node_bypass.c�������������������������������������������������������������������0000664�0000000�0000000�00000007406�15203622040�0016623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2019, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_bypass.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_bypass); struct ec_node_bypass { struct ec_node *child; }; static int ec_node_bypass_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_bypass *priv = ec_node_priv(node); if (priv->child == NULL) { errno = ENOENT; return -1; } return ec_parse_child(priv->child, pstate, strvec); } static int ec_node_bypass_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_bypass *priv = ec_node_priv(node); if (priv->child == NULL) { errno = ENOENT; return -1; } return ec_complete_child(priv->child, comp, strvec); } static void ec_node_bypass_free_priv(struct ec_node *node) { struct ec_node_bypass *priv = ec_node_priv(node); ec_node_free(priv->child); } static size_t ec_node_bypass_get_children_count(const struct ec_node *node) { struct ec_node_bypass *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_bypass_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_bypass *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 2; return 0; } static const struct ec_config_schema ec_node_bypass_schema[] = { { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_bypass_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_bypass *priv = ec_node_priv(node); const struct ec_config *child; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } if (priv->child != NULL) ec_node_free(priv->child); priv->child = ec_node_clone(child->node); return 0; fail: return -1; } static struct ec_node_type ec_node_bypass_type = { .name = "bypass", .schema = ec_node_bypass_schema, .set_config = ec_node_bypass_set_config, .parse = ec_node_bypass_parse, .complete = ec_node_bypass_complete, .size = sizeof(struct ec_node_bypass), .free_priv = ec_node_bypass_free_priv, .get_children_count = ec_node_bypass_get_children_count, .get_child = ec_node_bypass_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_bypass_type); int ec_node_bypass_set_child(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_bypass_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; /* freed */ ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *ec_node_bypass(const char *id, struct ec_node *child) { struct ec_node *node = NULL; if (child == NULL) goto fail; node = ec_node_from_type(&ec_node_bypass_type, id); if (node == NULL) goto fail; if (ec_node_bypass_set_child(node, child) < 0) goto fail; child = NULL; return node; fail: ec_node_free(child); return NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_cmd.c����������������������������������������������������������������������0000664�0000000�0000000�00000036120�15203622040�0016060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/init.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_cmd.h> #include <ecoli/node_expr.h> #include <ecoli/node_helper.h> #include <ecoli/node_int.h> #include <ecoli/node_many.h> #include <ecoli/node_option.h> #include <ecoli/node_or.h> #include <ecoli/node_re.h> #include <ecoli/node_re_lex.h> #include <ecoli/node_seq.h> #include <ecoli/node_str.h> #include <ecoli/node_subset.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_cmd); static struct ec_node *ec_node_cmd_parser; /* the expression parser. */ static struct ec_node *ec_node_cmd_expr; /* the expr parser without lexer. */ struct ec_node_cmd { char *cmd_str; /* the command string. */ struct ec_node *cmd; /* the command node. */ struct ec_node **table; /* table of node referenced in command. */ unsigned int len; /* len of the table. */ }; /* passed as user context to expression parser */ struct ec_node_cmd_ctx { struct ec_node **table; unsigned int len; }; static int ec_node_cmd_eval_var(void **result, void *userctx, const struct ec_pnode *var) { const struct ec_strvec *vec; struct ec_node_cmd_ctx *ctx = userctx; struct ec_node *eval = NULL; const char *str, *id; unsigned int i; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(var); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } str = ec_strvec_val(vec, 0); for (i = 0; i < ctx->len; i++) { id = ec_node_id(ctx->table[i]); if (id == NULL) continue; if (strcmp(str, id)) continue; /* if id matches, use a node provided by the user... */ eval = ec_node_clone(ctx->table[i]); if (eval == NULL) return -1; break; } /* ...or create a string node */ if (eval == NULL) { eval = ec_node_str(EC_NO_ID, str); if (eval == NULL) return -1; } *result = eval; return 0; } static int ec_node_cmd_eval_pre_op( void **result, void *userctx, void *operand, const struct ec_pnode *operator ) { (void)result; (void)userctx; (void)operand; (void)operator; errno = EINVAL; return -1; } static int ec_node_cmd_eval_post_op( void **result, void *userctx, void *operand, const struct ec_pnode *operator ) { const struct ec_strvec *vec; struct ec_node *in = operand; struct ec_node *out = NULL; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } if (!strcmp(ec_strvec_val(vec, 0), "*")) { out = ec_node_many(EC_NO_ID, ec_node_clone(in), 0, 0); if (out == NULL) return -1; ec_node_free(in); *result = out; } else if (!strcmp(ec_strvec_val(vec, 0), "+")) { out = ec_node_many(EC_NO_ID, ec_node_clone(in), 1, 0); if (out == NULL) return -1; ec_node_free(in); *result = out; } else { errno = EINVAL; return -1; } return 0; } static int ec_node_cmd_eval_bin_op( void **result, void *userctx, void *operand1, const struct ec_pnode *operator, void *operand2 ) { const struct ec_strvec *vec; struct ec_node *out = NULL; struct ec_node *in1 = operand1; struct ec_node *in2 = operand2; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) > 1) { errno = EINVAL; return -1; } if (ec_strvec_len(vec) == 0) { if (!strcmp(ec_node_get_type_name(in1), "seq")) { if (ec_node_seq_add(in1, ec_node_clone(in2)) < 0) return -1; ec_node_free(in2); *result = in1; } else { out = EC_NODE_SEQ(EC_NO_ID, ec_node_clone(in1), ec_node_clone(in2)); if (out == NULL) return -1; ec_node_free(in1); ec_node_free(in2); *result = out; } } else if (!strcmp(ec_strvec_val(vec, 0), "|")) { if (!strcmp(ec_node_get_type_name(in2), "or")) { if (ec_node_or_add(in2, ec_node_clone(in1)) < 0) return -1; ec_node_free(in1); *result = in2; } else if (!strcmp(ec_node_get_type_name(in1), "or")) { if (ec_node_or_add(in1, ec_node_clone(in2)) < 0) return -1; ec_node_free(in2); *result = in1; } else { out = EC_NODE_OR(EC_NO_ID, ec_node_clone(in1), ec_node_clone(in2)); if (out == NULL) return -1; ec_node_free(in1); ec_node_free(in2); *result = out; } } else if (!strcmp(ec_strvec_val(vec, 0), ",")) { if (!strcmp(ec_node_get_type_name(in2), "subset") && ec_node_subset_get_min(in2) == 1) { if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0) return -1; ec_node_free(in1); *result = in2; } else if ( /* clang-format off */ !strcmp(ec_node_get_type_name(in1), "subset") && ec_node_subset_get_min(in1) == 1 /* clang-format on */ ) { if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0) return -1; ec_node_free(in2); *result = in1; } else { out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1), ec_node_clone(in2)); if (out == NULL) return -1; ec_node_free(in1); ec_node_free(in2); *result = out; } if (ec_node_subset_set_min(*result, 1) < 0) return -1; } else if (!strcmp(ec_strvec_val(vec, 0), "&")) { if (!strcmp(ec_node_get_type_name(in1), "subset") && ec_node_subset_get_min(in1) > 1) { if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0) return -1; ec_node_free(in2); *result = in1; } else if ( /* clang-format off */ !strcmp(ec_node_get_type_name(in2), "subset") && ec_node_subset_get_min(in2) > 1 /* clang-format on */ ) { if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0) return -1; ec_node_free(in1); *result = in2; } else { out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1), ec_node_clone(in2)); if (out == NULL) return -1; ec_node_free(in1); ec_node_free(in2); *result = out; } if (ec_node_subset_set_min(*result, ec_node_get_children_count(*result)) < 0) return -1; } else { errno = EINVAL; return -1; } return 0; } static int ec_node_cmd_eval_parenthesis( void **result, void *userctx, const struct ec_pnode *open_paren, const struct ec_pnode *close_paren, void *value ) { const struct ec_strvec *vec; struct ec_node *in = value; struct ec_node *out = NULL; (void)userctx; (void)close_paren; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(open_paren); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } if (!strcmp(ec_strvec_val(vec, 0), "[")) { out = ec_node_option(EC_NO_ID, ec_node_clone(in)); if (out == NULL) return -1; ec_node_free(in); } else if (!strcmp(ec_strvec_val(vec, 0), "(")) { out = in; } else { errno = EINVAL; return -1; } *result = out; return 0; } static void ec_node_cmd_eval_free(void *result, void *userctx) { (void)userctx; ec_node_free(result); } static const struct ec_node_expr_eval_ops expr_ops = { .eval_var = ec_node_cmd_eval_var, .eval_pre_op = ec_node_cmd_eval_pre_op, .eval_post_op = ec_node_cmd_eval_post_op, .eval_bin_op = ec_node_cmd_eval_bin_op, .eval_parenthesis = ec_node_cmd_eval_parenthesis, .eval_free = ec_node_cmd_eval_free, }; static struct ec_node *ec_node_cmd_build_expr(void) { struct ec_node *expr = NULL; int ret; /* build the expression parser */ expr = ec_node("expr", "expr"); if (expr == NULL) goto fail; ret = ec_node_expr_set_val_node(expr, ec_node_re(EC_NO_ID, "[a-zA-Z0-9._-]+")); if (ret < 0) goto fail; ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "&")); if (ret < 0) goto fail; ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, ",")); if (ret < 0) goto fail; ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "|")); if (ret < 0) goto fail; ret = ec_node_expr_add_bin_op(expr, ec_node("empty", EC_NO_ID)); if (ret < 0) goto fail; ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "+")); if (ret < 0) goto fail; ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "*")); if (ret < 0) goto fail; ret = ec_node_expr_add_parenthesis( expr, ec_node_str(EC_NO_ID, "["), ec_node_str(EC_NO_ID, "]") ); if (ret < 0) goto fail; ret = ec_node_expr_add_parenthesis( expr, ec_node_str(EC_NO_ID, "("), ec_node_str(EC_NO_ID, ")") ); if (ret < 0) goto fail; return expr; fail: ec_node_free(expr); return NULL; } static struct ec_node *ec_node_cmd_build_parser(struct ec_node *expr) { struct ec_node *lex = NULL; int ret; /* prepend a lexer to the expression node */ lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr)); if (lex == NULL) goto fail; ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9._-]+", 1, NULL); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "[*+|,&()]", 1, NULL); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "\\[", 1, NULL); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "\\]", 1, NULL); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "[ ]+", 0, NULL); if (ret < 0) goto fail; return lex; fail: ec_node_free(lex); return NULL; } static struct ec_node *ec_node_cmd_build(const char *cmd_str, struct ec_node **table, size_t len) { struct ec_node_cmd_ctx ctx = {table, len}; struct ec_pnode *p = NULL; void *result; int ret; /* parse the command expression */ p = ec_parse(ec_node_cmd_parser, cmd_str); if (p == NULL) goto fail; if (!ec_pnode_matches(p)) { errno = EINVAL; goto fail; } ret = ec_node_expr_eval( &result, ec_node_cmd_expr, ec_pnode_get_first_child(p), &expr_ops, &ctx ); if (ret < 0) goto fail; ec_pnode_free(p); return result; fail: ec_pnode_free(p); return NULL; } static int ec_node_cmd_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_cmd *priv = ec_node_priv(node); if (priv->cmd == NULL) { errno = ENOENT; return -1; } return ec_parse_child(priv->cmd, pstate, strvec); } static int ec_node_cmd_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_cmd *priv = ec_node_priv(node); if (priv->cmd == NULL) { errno = ENOENT; return -1; } return ec_complete_child(priv->cmd, comp, strvec); } static void ec_node_cmd_free_priv(struct ec_node *node) { struct ec_node_cmd *priv = ec_node_priv(node); size_t i; free(priv->cmd_str); priv->cmd_str = NULL; ec_node_free(priv->cmd); priv->cmd = NULL; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = NULL; priv->len = 0; } static const struct ec_config_schema ec_node_cmd_subschema[] = { { .desc = "A child node whose id is referenced in the expression.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema ec_node_cmd_schema[] = { { .key = "expr", .desc = "The expression to match. Supported operators " "are or '|', list ',', all '&', many '+', many-or-zero '*', " "option '[]', group '()'. An identifier (alphanumeric) can " "reference a node whose node_id matches. Else it is " "interpreted as ec_node_str() matching this string. " "The ',' operator requires at least one match, " "the '&' operator requires all to match (in any order). " "Example: command [option] (subset1, subset2) x|y", .type = EC_CONFIG_TYPE_STRING, }, { .key = "children", .desc = "The list of children nodes.", .type = EC_CONFIG_TYPE_LIST, .subschema = ec_node_cmd_subschema, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_cmd_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_cmd *priv = ec_node_priv(node); const struct ec_config *expr = NULL; struct ec_node *cmd = NULL; struct ec_node **table = NULL; char *cmd_str = NULL; size_t len = 0, i; /* retrieve config locally */ expr = ec_config_dict_get(config, "expr"); if (expr == NULL) { errno = EINVAL; goto fail; } table = ec_node_config_node_list_to_table(ec_config_dict_get(config, "children"), &len); if (table == NULL) goto fail; cmd_str = strdup(expr->string); if (cmd_str == NULL) goto fail; /* parse expression to build the cmd child node */ cmd = ec_node_cmd_build(cmd_str, table, len); if (cmd == NULL) goto fail; /* ok, store the config */ ec_node_free(priv->cmd); priv->cmd = cmd; free(priv->cmd_str); priv->cmd_str = cmd_str; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = table; priv->len = len; return 0; fail: if (table != NULL) { for (i = 0; i < len; i++) ec_node_free(table[i]); } free(table); free(cmd_str); ec_node_free(cmd); return -1; } static size_t ec_node_cmd_get_children_count(const struct ec_node *node) { struct ec_node_cmd *priv = ec_node_priv(node); if (priv->cmd == NULL) return 0; return 1; } static int ec_node_cmd_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_cmd *priv = ec_node_priv(node); if (i > 0) return -1; *child = priv->cmd; *refs = 1; return 0; } static struct ec_node_type ec_node_cmd_type = { .name = "cmd", .schema = ec_node_cmd_schema, .set_config = ec_node_cmd_set_config, .parse = ec_node_cmd_parse, .complete = ec_node_cmd_complete, .size = sizeof(struct ec_node_cmd), .free_priv = ec_node_cmd_free_priv, .get_children_count = ec_node_cmd_get_children_count, .get_child = ec_node_cmd_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_cmd_type); struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...) { struct ec_config *config = NULL, *children = NULL; struct ec_node *node = NULL; va_list ap; int ret; /* this block must stay first, it frees the nodes on error */ va_start(ap, cmd); children = ec_node_config_node_list_from_vargs(ap); va_end(ap); if (children == NULL) goto fail; node = ec_node_from_type(&ec_node_cmd_type, id); if (node == NULL) goto fail; config = ec_config_dict(); if (config == NULL) goto fail; if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0) goto fail; if (ec_config_dict_set(config, "children", children) < 0) { children = NULL; /* freed */ goto fail; } children = NULL; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return node; fail: ec_node_free(node); /* will also free added children */ ec_config_free(children); ec_config_free(config); return NULL; } static int ec_node_cmd_init_func(void) { ec_node_cmd_expr = ec_node_cmd_build_expr(); if (ec_node_cmd_expr == NULL) goto fail; ec_node_cmd_parser = ec_node_cmd_build_parser(ec_node_cmd_expr); if (ec_node_cmd_parser == NULL) goto fail; return 0; fail: EC_LOG(EC_LOG_ERR, "Failed to initialize command parser\n"); ec_node_free(ec_node_cmd_expr); ec_node_cmd_expr = NULL; ec_node_free(ec_node_cmd_parser); ec_node_cmd_parser = NULL; return -1; } static void ec_node_cmd_exit_func(void) { ec_node_free(ec_node_cmd_expr); ec_node_cmd_expr = NULL; ec_node_free(ec_node_cmd_parser); ec_node_cmd_parser = NULL; } static struct ec_init ec_node_cmd_init = { .init = ec_node_cmd_init_func, .exit = ec_node_cmd_exit_func, .priority = 75, }; EC_INIT_REGISTER(ec_node_cmd_init); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_cond.c���������������������������������������������������������������������0000664�0000000�0000000�00000047426�15203622040�0016253�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2019, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/queue.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/htable.h> #include <ecoli/init.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_any.h> #include <ecoli/node_bypass.h> #include <ecoli/node_cond.h> #include <ecoli/node_many.h> #include <ecoli/node_option.h> #include <ecoli/node_or.h> #include <ecoli/node_re.h> #include <ecoli/node_re_lex.h> #include <ecoli/node_seq.h> #include <ecoli/node_str.h> #include <ecoli/node_subset.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_cond); static struct ec_node *ec_node_cond_parser; /* the expression parser. */ static struct ec_dict *ec_node_cond_functions; /* functions dictionary */ struct ec_node_cond { char *cond_str; /* the condition string. */ struct ec_pnode *parsed_cond; /* the parsed condition. */ struct ec_node *child; /* the child node. */ }; enum cond_result_type { NODESET, BOOLEAN, INT, STR, }; /* * XXX missing: * - find by attrs? get_attr ? * - get/set variable */ struct cond_result { enum cond_result_type type; union { struct ec_htable *htable; char *str; int64_t int64; bool boolean; }; }; typedef struct cond_result *(cond_func_t)(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len); static void cond_result_free(struct cond_result *res) { if (res == NULL) return; switch (res->type) { case NODESET: ec_htable_free(res->htable); break; case STR: free(res->str); break; case BOOLEAN: case INT: break; } free(res); } static void cond_result_table_free(struct cond_result **table, size_t len) { size_t i; for (i = 0; i < len; i++) { cond_result_free(table[i]); table[i] = NULL; } free(table); } static struct ec_node *ec_node_cond_build_parser(void) { struct ec_node *lex = NULL; struct ec_node *expr = NULL; int ret; expr = ec_node("or", "id_arg"); if (expr == NULL) goto fail; if (ec_node_or_add( expr, EC_NODE_SEQ( "id_function", ec_node_any("id_function_name", "a_identifier"), ec_node_any(EC_NO_ID, "a_open"), ec_node_option( "id_arg_list", EC_NODE_SEQ( EC_NO_ID, ec_node_clone(expr), ec_node_many( EC_NO_ID, EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, ","), ec_node_clone(expr) ), 0, 0 ) ) ), ec_node_any(EC_NO_ID, "a_close") ) ) < 0) goto fail; if (ec_node_or_add(expr, ec_node_any("id_value_str", "a_identifier")) < 0) goto fail; if (ec_node_or_add(expr, ec_node_any("id_value_int", "a_int")) < 0) goto fail; /* prepend a lexer to the expression node */ lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr)); if (lex == NULL) goto fail; ec_node_free(expr); expr = NULL; ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1, "a_identifier"); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "[0-9]+", 1, "a_int"); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open"); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close"); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, ",", 1, NULL); if (ret < 0) goto fail; ret = ec_node_re_lex_add(lex, "[ \t]", 0, NULL); if (ret < 0) goto fail; return lex; fail: ec_node_free(lex); ec_node_free(expr); return NULL; } static struct ec_pnode *ec_node_cond_build(const char *cond_str) { struct ec_pnode *p = NULL; /* parse the condition expression */ p = ec_parse(ec_node_cond_parser, cond_str); if (p == NULL) goto fail; if (!ec_pnode_matches(p)) { errno = EINVAL; goto fail; } return p; fail: ec_pnode_free(p); return NULL; } static struct cond_result * eval_root(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; const struct ec_pnode *root = NULL; (void)in; if (in_len != 0) { EC_LOG(LOG_ERR, "root() does not take any argument\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = NODESET; out->htable = ec_htable(); if (out->htable == NULL) goto fail; root = EC_PNODE_GET_ROOT(pstate); if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0) goto fail; cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_current(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; (void)in; if (in_len != 0) { EC_LOG(LOG_ERR, "current() does not take any argument\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = NODESET; out->htable = ec_htable(); if (out->htable == NULL) goto fail; if (ec_htable_set(out->htable, &pstate, sizeof(pstate), NULL, NULL) < 0) goto fail; cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static bool boolean_value(const struct cond_result *res) { switch (res->type) { case NODESET: return ec_htable_len(res->htable) > 0; case BOOLEAN: return res->boolean; case INT: return res->int64 != 0; case STR: return res->str[0] != 0; } return false; } static struct cond_result * eval_bool(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; (void)pstate; if (in_len != 1) { EC_LOG(LOG_ERR, "bool() takes one argument.\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = BOOLEAN; out->boolean = boolean_value(in[0]); cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_or(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; size_t i; (void)pstate; if (in_len < 2) { EC_LOG(LOG_ERR, "or() takes at least two arguments\n"); errno = EINVAL; goto fail; } /* return the first true element, or the last one */ for (i = 0; i < in_len; i++) { if (boolean_value(in[i])) break; } if (i == in_len) i--; out = in[i]; in[i] = NULL; cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_and(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; size_t i; (void)pstate; if (in_len < 2) { EC_LOG(LOG_ERR, "or() takes at least two arguments\n"); errno = EINVAL; goto fail; } /* return the first false element, or the last one */ for (i = 0; i < in_len; i++) { if (!boolean_value(in[i])) break; } if (i == in_len) i--; out = in[i]; in[i] = NULL; cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_first_child(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; struct ec_htable_elt_ref *iter; const struct ec_pnode *const *pparse; struct ec_pnode *parse; (void)pstate; if (in_len != 1 || in[0]->type != NODESET) { EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = NODESET; out->htable = ec_htable(); if (out->htable == NULL) goto fail; for (iter = ec_htable_iter(in[0]->htable); iter != NULL; iter = ec_htable_iter_next(iter)) { pparse = ec_htable_iter_get_key(iter); parse = ec_pnode_get_first_child(*pparse); if (parse == NULL) continue; if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL, NULL) < 0) goto fail; } cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_find(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; struct ec_htable_elt_ref *iter; struct ec_pnode *const *pparse; const struct ec_pnode *parse; const char *id; (void)pstate; if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) { EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = NODESET; out->htable = ec_htable(); if (out->htable == NULL) goto fail; id = in[1]->str; for (iter = ec_htable_iter(in[0]->htable); iter != NULL; iter = ec_htable_iter_next(iter)) { pparse = ec_htable_iter_get_key(iter); parse = ec_pnode_find(*pparse, id); while (parse != NULL) { if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL, NULL) < 0) goto fail; parse = ec_pnode_find_next(*pparse, parse, id, 1); } } cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_cmp(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; struct ec_htable_elt_ref *iter; bool eq = false, gt = false; (void)pstate; if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) { EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n"); errno = EINVAL; goto fail; } if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") && strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") && strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) { EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n"); errno = EINVAL; goto fail; } if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") && in[1]->type != INT) { EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n"); errno = EINVAL; goto fail; } if (in[1]->type == INT) { eq = in[1]->int64 == in[2]->int64; gt = in[1]->int64 > in[2]->int64; } else if ( /* clang-format off */ in[1]->type == NODESET && ec_htable_len(in[1]->htable) != ec_htable_len(in[2]->htable) /* clang-format on */ ) { eq = false; } else if (in[1]->type == NODESET) { eq = true; for (iter = ec_htable_iter(in[1]->htable); iter != NULL; iter = ec_htable_iter_next(iter)) { if (ec_htable_get( in[2]->htable, ec_htable_iter_get_key(iter), sizeof(struct ec_pnode *) ) == NULL) { eq = false; break; } } } else if (in[1]->type == STR) { eq = !strcmp(in[1]->str, in[2]->str); } else if (in[1]->type == BOOLEAN) { eq = in[1]->boolean == in[2]->boolean; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = BOOLEAN; if (!strcmp(in[0]->str, "eq")) out->boolean = eq; else if (!strcmp(in[0]->str, "ne")) out->boolean = !eq; else if (!strcmp(in[0]->str, "lt")) out->boolean = !gt && !eq; else if (!strcmp(in[0]->str, "gt")) out->boolean = gt && !eq; else if (!strcmp(in[0]->str, "le")) out->boolean = !gt || eq; else if (!strcmp(in[0]->str, "ge")) out->boolean = gt || eq; cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_count(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { struct cond_result *out = NULL; (void)pstate; if (in_len != 1 || in[0]->type != NODESET) { EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n"); errno = EINVAL; goto fail; } out = malloc(sizeof(*out)); if (out == NULL) goto fail; out->type = INT; out->int64 = ec_htable_len(in[0]->htable); cond_result_table_free(in, in_len); return out; fail: cond_result_free(out); cond_result_table_free(in, in_len); return NULL; } static struct cond_result * eval_func(const char *name, const struct ec_pnode *pstate, struct cond_result **in, size_t in_len) { cond_func_t *f; f = ec_dict_get(ec_node_cond_functions, name); if (f == NULL) { EC_LOG(LOG_ERR, "No such function <%s>\n", name); errno = ENOENT; cond_result_table_free(in, in_len); return NULL; } return f(pstate, in, in_len); } static struct cond_result * eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate) { const struct ec_pnode *iter; struct cond_result *res = NULL; struct cond_result **args = NULL; const struct ec_pnode *func = NULL, *func_name = NULL, *arg_list = NULL; const struct ec_pnode *value = NULL; const char *id; size_t n_arg = 0; /* XXX fix cast (x3) */ func = ec_pnode_find((void *)cond, "id_function"); if (func != NULL) { EC_PNODE_FOREACH_CHILD (iter, func) { id = ec_node_id(ec_pnode_get_node(iter)); if (!strcmp(id, "id_function_name")) func_name = iter; if (!strcmp(id, "id_arg_list")) arg_list = iter; } iter = ec_pnode_find((void *)arg_list, "id_arg"); while (iter != NULL) { struct cond_result **tmp; tmp = realloc(args, (n_arg + 1) * sizeof(*args)); if (tmp == NULL) goto fail; args = tmp; args[n_arg] = eval_condition(iter, pstate); if (args[n_arg] == NULL) goto fail; n_arg++; iter = ec_pnode_find_next((void *)arg_list, (void *)iter, "id_arg", 0); } res = eval_func( ec_strvec_val(ec_pnode_get_strvec(func_name), 0), pstate, args, n_arg ); args = NULL; return res; } value = ec_pnode_find((void *)cond, "id_value_str"); if (value != NULL) { res = malloc(sizeof(*res)); if (res == NULL) goto fail; res->type = STR; res->str = strdup(ec_strvec_val(ec_pnode_get_strvec(value), 0)); if (res->str == NULL) goto fail; return res; } value = ec_pnode_find((void *)cond, "id_value_int"); if (value != NULL) { res = malloc(sizeof(*res)); if (res == NULL) goto fail; res->type = INT; if (ec_str_parse_llint( ec_strvec_val(ec_pnode_get_strvec(value), 0), 0, LLONG_MIN, LLONG_MAX, &res->int64 ) < 0) goto fail; return res; } fail: cond_result_free(res); cond_result_table_free(args, n_arg); return NULL; } static int validate_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate) { struct cond_result *res; int ret; res = eval_condition(cond, pstate); if (res == NULL) return -1; ret = boolean_value(res); cond_result_free(res); return ret; } static int ec_node_cond_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_cond *priv = ec_node_priv(node); struct ec_pnode *child; int ret, valid; if (priv->child == NULL || priv->parsed_cond == NULL) { errno = ENOENT; return -1; } ret = ec_parse_child(priv->child, pstate, strvec); if (ret <= 0) return ret; valid = validate_condition(priv->parsed_cond, pstate); if (valid < 0) return valid; if (valid == 0) { child = ec_pnode_get_last_child(pstate); ec_pnode_unlink_child(child); ec_pnode_free(child); return EC_PARSE_NOMATCH; } return ret; } static int ec_node_cond_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_cond *priv = ec_node_priv(node); if (priv->child == NULL || priv->parsed_cond == NULL) { errno = ENOENT; return -1; } /* XXX eval condition */ /* XXX before or after completing ? configurable ? */ return ec_complete_child(priv->child, comp, strvec); } static void ec_node_cond_free_priv(struct ec_node *node) { struct ec_node_cond *priv = ec_node_priv(node); free(priv->cond_str); priv->cond_str = NULL; ec_pnode_free(priv->parsed_cond); priv->parsed_cond = NULL; ec_node_free(priv->child); } static const struct ec_config_schema ec_node_cond_schema[] = { { .key = "expr", .desc = "XXX", .type = EC_CONFIG_TYPE_STRING, }, { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_cond_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_cond *priv = ec_node_priv(node); const struct ec_config *cond = NULL; struct ec_pnode *parsed_cond = NULL; const struct ec_config *child; char *cond_str = NULL; cond = ec_config_dict_get(config, "expr"); if (cond == NULL) { errno = EINVAL; goto fail; } cond_str = strdup(cond->string); if (cond_str == NULL) goto fail; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; /* parse expression to build the cmd child node */ parsed_cond = ec_node_cond_build(cond_str); if (parsed_cond == NULL) goto fail; /* ok, store the config */ ec_pnode_free(priv->parsed_cond); priv->parsed_cond = parsed_cond; free(priv->cond_str); priv->cond_str = cond_str; ec_node_free(priv->child); priv->child = ec_node_clone(child->node); return 0; fail: ec_pnode_free(parsed_cond); free(cond_str); return -1; } static size_t ec_node_cond_get_children_count(const struct ec_node *node) { struct ec_node_cond *priv = ec_node_priv(node); if (priv->child == NULL) return 0; return 1; } static int ec_node_cond_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_cond *priv = ec_node_priv(node); if (i > 0) return -1; *child = priv->child; *refs = 1; return 0; } static struct ec_node_type ec_node_cond_type = { .name = "cond", .schema = ec_node_cond_schema, .set_config = ec_node_cond_set_config, .parse = ec_node_cond_parse, .complete = ec_node_cond_complete, .size = sizeof(struct ec_node_cond), .free_priv = ec_node_cond_free_priv, .get_children_count = ec_node_cond_get_children_count, .get_child = ec_node_cond_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_cond_type); struct ec_node *ec_node_cond(const char *id, const char *cmd, struct ec_node *child) { struct ec_config *config = NULL; struct ec_node *node = NULL; int ret; if (child == NULL) return NULL; node = ec_node_from_type(&ec_node_cond_type, id); if (node == NULL) goto fail; config = ec_config_dict(); if (config == NULL) goto fail; if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return node; fail: ec_node_free(node); ec_node_free(child); ec_config_free(config); return NULL; } static void ec_node_cond_exit_func(void) { ec_node_free(ec_node_cond_parser); ec_node_cond_parser = NULL; ec_dict_free(ec_node_cond_functions); ec_node_cond_functions = NULL; } static int add_func(const char *name, cond_func_t *f) { return ec_dict_set(ec_node_cond_functions, name, f, NULL); } static int ec_node_cond_init_func(void) { ec_node_cond_parser = ec_node_cond_build_parser(); if (ec_node_cond_parser == NULL) goto fail; ec_node_cond_functions = ec_dict(); if (ec_node_cond_functions == NULL) goto fail; if (add_func("root", eval_root) < 0) goto fail; if (add_func("current", eval_current) < 0) goto fail; if (add_func("bool", eval_bool) < 0) goto fail; if (add_func("or", eval_or) < 0) goto fail; if (add_func("and", eval_and) < 0) goto fail; if (add_func("first_child", eval_first_child) < 0) goto fail; if (add_func("find", eval_find) < 0) goto fail; if (add_func("cmp", eval_cmp) < 0) goto fail; if (add_func("count", eval_count) < 0) goto fail; return 0; fail: EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n"); ec_node_cond_exit_func(); return -1; } static struct ec_init ec_node_cond_init = { .init = ec_node_cond_init_func, .exit = ec_node_cond_exit_func, .priority = 75, }; EC_INIT_REGISTER(ec_node_cond_init); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_dynamic.c������������������������������������������������������������������0000664�0000000�0000000�00000005533�15203622040�0016745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2017, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_dynamic.h> #include <ecoli/node_many.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_dynamic); struct ec_node_dynamic { ec_node_dynamic_build_t build; void *opaque; }; static int ec_node_dynamic_parse( const struct ec_node *node, struct ec_pnode *parse, const struct ec_strvec *strvec ) { struct ec_node_dynamic *priv = ec_node_priv(node); struct ec_node *child = NULL; void (*node_free)(struct ec_node *) = ec_node_free; char key[64]; int ret = -1; if (priv->build == NULL) { errno = ENOENT; return -1; } child = priv->build(parse, priv->opaque); if (child == NULL) goto fail; /* add the node pointer in the attributes, so it will be freed * when parse is freed */ snprintf(key, sizeof(key), "_dyn_%p", child); ret = ec_dict_set(ec_pnode_get_attrs(parse), key, child, (void *)node_free); if (ret < 0) { child = NULL; /* already freed */ goto fail; } return ec_parse_child(child, parse, strvec); fail: ec_node_free(child); return ret; } static int ec_node_dynamic_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_dynamic *priv = ec_node_priv(node); struct ec_pnode *parse; struct ec_node *child = NULL; void (*node_free)(struct ec_node *) = ec_node_free; char key[64]; int ret = -1; if (priv->build == NULL) { errno = ENOENT; return -1; } parse = ec_comp_get_cur_pstate(comp); child = priv->build(parse, priv->opaque); if (child == NULL) goto fail; /* add the node pointer in the attributes, so it will be freed * when parse is freed */ snprintf(key, sizeof(key), "_dyn_%p", child); ret = ec_dict_set(ec_comp_get_attrs(comp), key, child, (void *)node_free); if (ret < 0) { child = NULL; /* already freed */ goto fail; } return ec_complete_child(child, comp, strvec); fail: ec_node_free(child); return ret; } static struct ec_node_type ec_node_dynamic_type = { .name = "dynamic", .parse = ec_node_dynamic_parse, .complete = ec_node_dynamic_complete, .size = sizeof(struct ec_node_dynamic), }; struct ec_node *ec_node_dynamic(const char *id, ec_node_dynamic_build_t build, void *opaque) { struct ec_node *node = NULL; struct ec_node_dynamic *priv; if (build == NULL) { errno = EINVAL; goto fail; } node = ec_node_from_type(&ec_node_dynamic_type, id); if (node == NULL) goto fail; priv = ec_node_priv(node); priv->build = build; priv->opaque = opaque; return node; fail: ec_node_free(node); return NULL; } EC_NODE_TYPE_REGISTER(ec_node_dynamic_type); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_dynlist.c������������������������������������������������������������������0000664�0000000�0000000�00000007771�15203622040�0017015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <regex.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_dynlist.h> #include <ecoli/node_many.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_dynlist); struct ec_node_dynlist { ec_node_dynlist_get_t get; void *opaque; enum ec_node_dynlist_flags flags; char *re_str; regex_t re; }; static int ec_node_dynlist_parse( const struct ec_node *node, struct ec_pnode *parse, const struct ec_strvec *strvec ) { struct ec_node_dynlist *priv = ec_node_priv(node); struct ec_strvec *names = NULL; const char *name; const char *str; regmatch_t pos; size_t len; size_t i; int ret; if (priv->get == NULL || priv->re_str == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; str = ec_strvec_val(strvec, 0); names = priv->get(parse, priv->opaque); if (names == NULL) goto fail; len = ec_strvec_len(names); for (i = 0; i < len; i++) { name = ec_strvec_val(names, i); if (strcmp(name, str)) continue; if (priv->flags & DYNLIST_EXCLUDE_LIST) { ret = EC_PARSE_NOMATCH; goto end; } if (priv->flags & DYNLIST_MATCH_LIST) { ret = 1; goto end; ; } } if (priv->re_str != NULL && priv->flags & DYNLIST_MATCH_REGEXP) { if (regexec(&priv->re, str, 1, &pos, 0) == 0 && pos.rm_so == 0 && pos.rm_eo == (int)strlen(str)) { ret = 1; goto end; } } ret = EC_PARSE_NOMATCH; end: ec_strvec_free(names); return ret; fail: ret = -1; goto end; } static int ec_node_dynlist_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_dynlist *priv = ec_node_priv(node); const struct ec_comp_item *item = NULL; struct ec_strvec *names = NULL; const char *name; const char *str; size_t len; size_t i; if (priv->get == NULL || priv->re_str == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) != 1) return 0; str = ec_strvec_val(strvec, 0); item = ec_comp_add_item(comp, node, EC_COMP_UNKNOWN, NULL, NULL); if (item == NULL) goto fail; if (priv->flags & DYNLIST_MATCH_LIST) { names = priv->get(ec_comp_get_cur_pstate(comp), priv->opaque); if (names == NULL) goto fail; len = ec_strvec_len(names); for (i = 0; i < len; i++) { name = ec_strvec_val(names, i); if (!ec_str_startswith(name, str)) continue; item = ec_comp_add_item(comp, node, EC_COMP_FULL, str, name); if (item == NULL) goto fail; } } ec_strvec_free(names); return 0; fail: ec_strvec_free(names); return -1; } static void ec_node_dynlist_free_priv(struct ec_node *node) { struct ec_node_dynlist *priv = ec_node_priv(node); if (priv->re_str != NULL) { free(priv->re_str); regfree(&priv->re); } } static struct ec_node_type ec_node_dynlist_type = { .name = "dynlist", .parse = ec_node_dynlist_parse, .complete = ec_node_dynlist_complete, .size = sizeof(struct ec_node_dynlist), .free_priv = ec_node_dynlist_free_priv, }; struct ec_node *ec_node_dynlist( const char *id, ec_node_dynlist_get_t get, void *opaque, const char *re_str, enum ec_node_dynlist_flags flags ) { struct ec_node *node = NULL; struct ec_node_dynlist *priv; regex_t re; int ret; if (get == NULL) { errno = EINVAL; goto fail; } node = ec_node_from_type(&ec_node_dynlist_type, id); if (node == NULL) goto fail; priv = ec_node_priv(node); priv->re_str = strdup(re_str); if (priv->re_str == NULL) goto fail; ret = regcomp(&re, re_str, REG_EXTENDED); if (ret != 0) { if (ret == REG_ESPACE) errno = ENOMEM; else errno = EINVAL; goto fail; } priv->re = re; priv->get = get; priv->opaque = opaque; priv->flags = flags; return node; fail: ec_node_free(node); return NULL; } EC_NODE_TYPE_REGISTER(ec_node_dynlist_type); �������libecoli-0.11.6/src/node_empty.c��������������������������������������������������������������������0000664�0000000�0000000�00000001424�15203622040�0016452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_empty.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_empty); struct ec_node_empty { }; static int ec_node_empty_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { (void)node; (void)pstate; (void)strvec; return 0; } static struct ec_node_type ec_node_empty_type = { .name = "empty", .parse = ec_node_empty_parse, .size = sizeof(struct ec_node_empty), }; struct ec_node *ec_node_empty(const char *id) { return ec_node_from_type(&ec_node_empty_type, id); } EC_NODE_TYPE_REGISTER(ec_node_empty_type); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_expr.c���������������������������������������������������������������������0000664�0000000�0000000�00000031723�15203622040�0016277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_expr.h> #include <ecoli/node_many.h> #include <ecoli/node_or.h> #include <ecoli/node_seq.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_expr); struct ec_node_expr { /* the built node */ struct ec_node *child; /* the configuration nodes */ struct ec_node *val_node; struct ec_node **bin_ops; unsigned int bin_ops_len; struct ec_node **pre_ops; unsigned int pre_ops_len; struct ec_node **post_ops; unsigned int post_ops_len; struct ec_node **open_ops; struct ec_node **close_ops; unsigned int paren_len; }; static int ec_node_expr_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_expr *priv = ec_node_priv(node); if (priv->child == NULL) { errno = ENOENT; return -1; } return ec_parse_child(priv->child, pstate, strvec); } static int ec_node_expr_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_expr *priv = ec_node_priv(node); if (priv->child == NULL) { errno = ENOENT; return -1; } return ec_complete_child(priv->child, comp, strvec); } static void ec_node_expr_free_priv(struct ec_node *node) { struct ec_node_expr *priv = ec_node_priv(node); unsigned int i; ec_node_free(priv->child); ec_node_free(priv->val_node); for (i = 0; i < priv->bin_ops_len; i++) ec_node_free(priv->bin_ops[i]); free(priv->bin_ops); for (i = 0; i < priv->pre_ops_len; i++) ec_node_free(priv->pre_ops[i]); free(priv->pre_ops); for (i = 0; i < priv->post_ops_len; i++) ec_node_free(priv->post_ops[i]); free(priv->post_ops); for (i = 0; i < priv->paren_len; i++) { ec_node_free(priv->open_ops[i]); ec_node_free(priv->close_ops[i]); } free(priv->open_ops); free(priv->close_ops); } static int ec_node_expr_build(struct ec_node_expr *priv) { struct ec_node *term = NULL, *expr = NULL, *next = NULL, *pre_op = NULL, *post_op = NULL, *ref = NULL, *post = NULL; unsigned int i; ec_node_free(priv->child); priv->child = NULL; if (priv->val_node == NULL) { errno = EINVAL; return -1; } if (priv->bin_ops_len == 0 && priv->pre_ops_len == 0 && priv->post_ops_len == 0) { errno = EINVAL; return -1; } /* * Example of created grammar: * * pre_op = "!" * post_op = "^" * post = val | * pre_op expr | * "(" expr ")" * term = post post_op* * prod = term ( "*" term )* * sum = prod ( "+" prod )* * expr = sum */ /* we use this as a ref, will be set later */ ref = ec_node("seq", "ref"); if (ref == NULL) return -1; /* prefix unary operators */ pre_op = ec_node("or", "pre-op"); if (pre_op == NULL) goto fail; for (i = 0; i < priv->pre_ops_len; i++) { if (ec_node_or_add(pre_op, ec_node_clone(priv->pre_ops[i])) < 0) goto fail; } /* suffix unary operators */ post_op = ec_node("or", "post-op"); if (post_op == NULL) goto fail; for (i = 0; i < priv->post_ops_len; i++) { if (ec_node_or_add(post_op, ec_node_clone(priv->post_ops[i])) < 0) goto fail; } post = ec_node("or", "post"); if (post == NULL) goto fail; if (ec_node_or_add(post, ec_node_clone(priv->val_node)) < 0) goto fail; if (ec_node_or_add(post, EC_NODE_SEQ(EC_NO_ID, ec_node_clone(pre_op), ec_node_clone(ref))) < 0) goto fail; for (i = 0; i < priv->paren_len; i++) { if (ec_node_or_add( post, EC_NODE_SEQ( EC_NO_ID, ec_node_clone(priv->open_ops[i]), ec_node_clone(ref), ec_node_clone(priv->close_ops[i]) ) ) < 0) goto fail; } term = EC_NODE_SEQ( "term", ec_node_clone(post), ec_node_many(EC_NO_ID, ec_node_clone(post_op), 0, 0) ); if (term == NULL) goto fail; for (i = 0; i < priv->bin_ops_len; i++) { next = EC_NODE_SEQ( "next", ec_node_clone(term), ec_node_many( EC_NO_ID, EC_NODE_SEQ( EC_NO_ID, ec_node_clone(priv->bin_ops[i]), ec_node_clone(term) ), 0, 0 ) ); ec_node_free(term); term = next; if (term == NULL) goto fail; } expr = term; term = NULL; /* free the initial references */ ec_node_free(pre_op); pre_op = NULL; ec_node_free(post_op); post_op = NULL; ec_node_free(post); post = NULL; if (ec_node_seq_add(ref, ec_node_clone(expr)) < 0) goto fail; ec_node_free(ref); ref = NULL; priv->child = expr; return 0; fail: ec_node_free(term); ec_node_free(expr); ec_node_free(pre_op); ec_node_free(post_op); ec_node_free(post); ec_node_free(ref); return -1; } static size_t ec_node_expr_get_children_count(const struct ec_node *node) { struct ec_node_expr *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_expr_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_expr *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 1; return 0; } static struct ec_node_type ec_node_expr_type = { .name = "expr", .parse = ec_node_expr_parse, .complete = ec_node_expr_complete, .size = sizeof(struct ec_node_expr), .free_priv = ec_node_expr_free_priv, .get_children_count = ec_node_expr_get_children_count, .get_child = ec_node_expr_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_expr_type); struct ec_node *ec_node_expr(const char *id) { return ec_node_from_type(&ec_node_expr_type, id); } int ec_node_expr_set_val_node(struct ec_node *node, struct ec_node *val_node) { struct ec_node_expr *priv = ec_node_priv(node); if (ec_node_check_type(node, &ec_node_expr_type) < 0) goto fail; if (val_node == NULL) { errno = EINVAL; goto fail; } ec_node_free(priv->val_node); priv->val_node = val_node; ec_node_expr_build(priv); return 0; fail: ec_node_free(val_node); return -1; } /* add a binary operator */ int ec_node_expr_add_bin_op(struct ec_node *node, struct ec_node *op) { struct ec_node_expr *priv = ec_node_priv(node); struct ec_node **bin_ops; if (ec_node_check_type(node, &ec_node_expr_type) < 0) goto fail; if (node == NULL || op == NULL) { errno = EINVAL; goto fail; } bin_ops = realloc(priv->bin_ops, (priv->bin_ops_len + 1) * sizeof(*priv->bin_ops)); if (bin_ops == NULL) goto fail; priv->bin_ops = bin_ops; bin_ops[priv->bin_ops_len] = op; priv->bin_ops_len++; ec_node_expr_build(priv); return 0; fail: ec_node_free(op); return -1; } /* add a unary pre-operator */ int ec_node_expr_add_pre_op(struct ec_node *node, struct ec_node *op) { struct ec_node_expr *priv = ec_node_priv(node); struct ec_node **pre_ops; if (ec_node_check_type(node, &ec_node_expr_type) < 0) goto fail; if (node == NULL || op == NULL) { errno = EINVAL; goto fail; } pre_ops = realloc(priv->pre_ops, (priv->pre_ops_len + 1) * sizeof(*priv->pre_ops)); if (pre_ops == NULL) goto fail; priv->pre_ops = pre_ops; pre_ops[priv->pre_ops_len] = op; priv->pre_ops_len++; ec_node_expr_build(priv); return 0; fail: ec_node_free(op); return -1; } /* add a unary post-operator */ int ec_node_expr_add_post_op(struct ec_node *node, struct ec_node *op) { struct ec_node_expr *priv = ec_node_priv(node); struct ec_node **post_ops; if (ec_node_check_type(node, &ec_node_expr_type) < 0) goto fail; if (node == NULL || op == NULL) { errno = EINVAL; goto fail; } post_ops = realloc(priv->post_ops, (priv->post_ops_len + 1) * sizeof(*priv->post_ops)); if (post_ops == NULL) goto fail; priv->post_ops = post_ops; post_ops[priv->post_ops_len] = op; priv->post_ops_len++; ec_node_expr_build(priv); return 0; fail: ec_node_free(op); return -1; } /* add parenthesis symbols */ int ec_node_expr_add_parenthesis(struct ec_node *node, struct ec_node *open, struct ec_node *close) { struct ec_node_expr *priv = ec_node_priv(node); struct ec_node **close_ops = NULL; struct ec_node **open_ops = NULL; if (ec_node_check_type(node, &ec_node_expr_type) < 0) goto fail; if (node == NULL || open == NULL || close == NULL) { errno = EINVAL; goto fail; } open_ops = malloc((priv->paren_len + 1) * sizeof(*priv->open_ops)); if (open_ops == NULL) goto fail; close_ops = malloc((priv->paren_len + 1) * sizeof(*priv->close_ops)); if (close_ops == NULL) goto fail; memcpy(open_ops, priv->open_ops, priv->paren_len * sizeof(*priv->open_ops)); memcpy(close_ops, priv->close_ops, priv->paren_len * sizeof(*priv->close_ops)); open_ops[priv->paren_len] = open; close_ops[priv->paren_len] = close; free(priv->open_ops); free(priv->close_ops); priv->open_ops = open_ops; priv->close_ops = close_ops; priv->paren_len++; ec_node_expr_build(priv); return 0; fail: ec_node_free(open); ec_node_free(close); free(close_ops); free(open_ops); return -1; } enum expr_node_type { NONE, VAL, BIN_OP, PRE_OP, POST_OP, PAREN_OPEN, PAREN_CLOSE, }; static enum expr_node_type get_node_type(const struct ec_node *expr_node, const struct ec_node *check) { struct ec_node_expr *expr_priv = ec_node_priv(expr_node); size_t i; if (check == expr_priv->val_node) return VAL; for (i = 0; i < expr_priv->bin_ops_len; i++) { if (check == expr_priv->bin_ops[i]) return BIN_OP; } for (i = 0; i < expr_priv->pre_ops_len; i++) { if (check == expr_priv->pre_ops[i]) return PRE_OP; } for (i = 0; i < expr_priv->post_ops_len; i++) { if (check == expr_priv->post_ops[i]) return POST_OP; } for (i = 0; i < expr_priv->paren_len; i++) { if (check == expr_priv->open_ops[i]) return PAREN_OPEN; } for (i = 0; i < expr_priv->paren_len; i++) { if (check == expr_priv->close_ops[i]) return PAREN_CLOSE; } return NONE; } struct result { bool has_val; void *val; const struct ec_pnode *op; enum expr_node_type op_type; }; /* merge x and y results in x */ static int merge_results( void *userctx, const struct ec_node_expr_eval_ops *ops, struct result *x, const struct result *y ) { if (y->has_val == 0 && y->op == NULL) return 0; if (x->has_val == 0 && x->op == NULL) { *x = *y; return 0; } if (x->has_val && y->has_val && y->op != NULL) { if (y->op_type == BIN_OP) { if (ops->eval_bin_op(&x->val, userctx, x->val, y->op, y->val) < 0) return -1; return 0; } } if (x->has_val == 0 && x->op != NULL && y->has_val && y->op == NULL) { if (x->op_type == PRE_OP) { if (ops->eval_pre_op(&x->val, userctx, y->val, x->op) < 0) return -1; x->has_val = true; x->op_type = NONE; x->op = NULL; return 0; } else if (x->op_type == BIN_OP) { x->val = y->val; x->has_val = true; return 0; } } if (x->has_val && x->op == NULL && y->has_val == 0 && y->op != NULL) { if (ops->eval_post_op(&x->val, userctx, x->val, y->op) < 0) return -1; return 0; } assert(false); /* we should not get here */ return -1; } static int eval_expression( struct result *result, void *userctx, const struct ec_node_expr_eval_ops *ops, const struct ec_node *expr_node, const struct ec_pnode *parse ) { struct ec_pnode *open = NULL, *close = NULL; struct result child_result; struct ec_pnode *child; enum expr_node_type type; memset(result, 0, sizeof(*result)); memset(&child_result, 0, sizeof(child_result)); type = get_node_type(expr_node, ec_pnode_get_node(parse)); if (type == VAL) { if (ops->eval_var(&result->val, userctx, parse) < 0) goto fail; result->has_val = 1; } else if (type == PRE_OP || type == POST_OP || type == BIN_OP) { result->op = parse; result->op_type = type; } EC_PNODE_FOREACH_CHILD (child, parse) { type = get_node_type(expr_node, ec_pnode_get_node(child)); if (type == PAREN_OPEN) { open = child; continue; } else if (type == PAREN_CLOSE) { close = child; continue; } if (eval_expression(&child_result, userctx, ops, expr_node, child) < 0) goto fail; if (merge_results(userctx, ops, result, &child_result) < 0) goto fail; memset(&child_result, 0, sizeof(child_result)); } if (open != NULL && close != NULL) { if (ops->eval_parenthesis(&result->val, userctx, open, close, result->val) < 0) goto fail; } return 0; fail: if (result->has_val) ops->eval_free(result->val, userctx); if (child_result.has_val) ops->eval_free(child_result.val, userctx); memset(result, 0, sizeof(*result)); return -1; } int ec_node_expr_eval( void **user_result, const struct ec_node *node, struct ec_pnode *parse, const struct ec_node_expr_eval_ops *ops, void *userctx ) { struct result result; if (ops == NULL || ops->eval_var == NULL || ops->eval_pre_op == NULL || ops->eval_post_op == NULL || ops->eval_bin_op == NULL || ops->eval_parenthesis == NULL || ops->eval_free == NULL) { errno = EINVAL; return -1; } if (ec_node_check_type(node, &ec_node_expr_type) < 0) return -1; if (!ec_pnode_matches(parse)) { errno = EINVAL; return -1; } if (eval_expression(&result, userctx, ops, node, parse) < 0) return -1; assert(result.has_val); assert(result.op == NULL); *user_result = result.val; return 0; } ���������������������������������������������libecoli-0.11.6/src/node_file.c���������������������������������������������������������������������0000664�0000000�0000000�00000011742�15203622040�0016237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_file.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_file); static struct ec_node_file_ops file_ops = { .lstat = lstat, .opendir = opendir, .readdir = readdir, .closedir = closedir, .dirfd = dirfd, .fstatat = fstatat, }; void ec_node_file_set_ops(const struct ec_node_file_ops *ops) { file_ops = *ops; } static int ec_node_file_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { (void)node; (void)pstate; if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; return 1; } /* * Almost the same than dirname (3) and basename (3) except that: * - it always returns a substring of the given path, which can * be empty. * - the behavior is different when the path finishes with a '/' * - the path argument is not modified * - the outputs are allocated and must be freed with free(). * * path dirname basename split_path * /usr/lib /usr lib /usr/ lib * /usr/ / usr /usr/ * usr . usr usr * / / / / * . . . . * .. . .. .. */ static int split_path(const char *path, char **dname_p, char **bname_p) { const char *last_slash; size_t dirlen; char *dname, *bname; *dname_p = NULL; *bname_p = NULL; last_slash = strrchr(path, '/'); if (last_slash == NULL) dirlen = 0; else dirlen = last_slash - path + 1; dname = strdup(path); if (dname == NULL) return -1; dname[dirlen] = '\0'; bname = strdup(path + dirlen); if (bname == NULL) { free(dname); return -1; } *dname_p = dname; *bname_p = bname; return 0; } static int ec_node_file_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { char *dname = NULL, *bname = NULL, *effective_dir; struct ec_comp_item *item = NULL; enum ec_comp_type type; struct stat st, st2; const char *input; size_t bname_len; struct dirent *de = NULL; DIR *dir = NULL; char *comp_str = NULL; char *disp_str = NULL; int is_dir = 0; /* * Example with this file tree: * / * ├── dir1 * │   ├── file1 * │   ├── file2 * │   └── subdir * │   └── file3 * ├── dir2 * │   └── file4 * └── file5 * * Input Output completions * / [dir1/, dir2/, file5] * /d [dir1/, dir2/] * /f [file5] * /dir1/ [file1, file2, subdir/] * * * */ if (ec_strvec_len(strvec) != 1) return 0; input = ec_strvec_val(strvec, 0); if (split_path(input, &dname, &bname) < 0) return -1; if (strcmp(dname, "") == 0) effective_dir = "."; else effective_dir = dname; if (file_ops.lstat(effective_dir, &st) < 0) goto out; if (!S_ISDIR(st.st_mode)) goto out; dir = file_ops.opendir(effective_dir); if (dir == NULL) goto out; bname_len = strlen(bname); while (1) { de = file_ops.readdir(dir); if (de == NULL) goto out; if (!ec_str_startswith(de->d_name, bname)) continue; if (bname[0] != '.' && de->d_name[0] == '.') continue; /* add '/' if it's a dir */ if (de->d_type == DT_DIR) { is_dir = 1; } else if (de->d_type == DT_UNKNOWN) { int dir_fd = file_ops.dirfd(dir); if (dir_fd < 0) goto out; if (file_ops.fstatat(dir_fd, de->d_name, &st2, 0) < 0) goto out; if (S_ISDIR(st2.st_mode)) is_dir = 1; else is_dir = 0; } else { is_dir = 0; } if (is_dir) { type = EC_COMP_PARTIAL; if (asprintf(&comp_str, "%s%s/", input, &de->d_name[bname_len]) < 0) goto fail; if (asprintf(&disp_str, "%s/", de->d_name) < 0) goto fail; } else { type = EC_COMP_FULL; if (asprintf(&comp_str, "%s%s", input, &de->d_name[bname_len]) < 0) goto fail; if (asprintf(&disp_str, "%s", de->d_name) < 0) goto fail; } item = ec_comp_add_item(comp, node, type, input, comp_str); if (item == NULL) goto fail; /* fix the display string: we don't want to display the full * path. */ if (ec_comp_item_set_display(item, disp_str) < 0) goto fail; item = NULL; free(comp_str); comp_str = NULL; free(disp_str); disp_str = NULL; } out: free(comp_str); free(disp_str); free(dname); free(bname); if (dir != NULL) file_ops.closedir(dir); return 0; fail: free(comp_str); free(disp_str); free(dname); free(bname); if (dir != NULL) file_ops.closedir(dir); return -1; } static struct ec_node_type ec_node_file_type = { .name = "file", .parse = ec_node_file_parse, .complete = ec_node_file_complete, }; EC_NODE_TYPE_REGISTER(ec_node_file_type); ������������������������������libecoli-0.11.6/src/node_helper.c�������������������������������������������������������������������0000664�0000000�0000000�00000003224�15203622040�0016573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdarg.h> #include <stddef.h> #include <stdlib.h> #include <sys/queue.h> #include <sys/types.h> #include <ecoli/config.h> #include <ecoli/node.h> #include <ecoli/node_helper.h> #include <ecoli/utils.h> struct ec_node **ec_node_config_node_list_to_table(const struct ec_config *config, size_t *len) { struct ec_node **table = NULL; struct ec_config *child; ssize_t n, i; *len = 0; if (config == NULL) { errno = EINVAL; return NULL; } if (ec_config_get_type(config) != EC_CONFIG_TYPE_LIST) { errno = EINVAL; return NULL; } n = ec_config_count(config); if (n < 0) return NULL; table = calloc(n, sizeof(*table)); if (table == NULL) goto fail; n = 0; TAILQ_FOREACH (child, &config->list, next) { if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } table[n] = ec_node_clone(child->node); n++; } *len = n; return table; fail: if (table != NULL) { for (i = 0; i < n; i++) ec_node_free(table[i]); } free(table); return NULL; } struct ec_config *ec_node_config_node_list_from_vargs(va_list ap) { struct ec_config *list = NULL; struct ec_node *node = va_arg(ap, struct ec_node *); list = ec_config_list(); if (list == NULL) goto fail; for (; node != EC_VA_END; node = va_arg(ap, struct ec_node *)) { if (node == NULL) goto fail; if (ec_config_list_add(list, ec_config_node(node)) < 0) goto fail; } return list; fail: for (; node != EC_VA_END; node = va_arg(ap, struct ec_node *)) ec_node_free(node); ec_config_free(list); return NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_int.c����������������������������������������������������������������������0000664�0000000�0000000�00000016213�15203622040�0016110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <limits.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_int.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_int); /* common to int and uint */ struct ec_node_int_uint { bool is_signed; bool check_min; bool check_max; union { int64_t min; uint64_t umin; }; union { int64_t max; uint64_t umax; }; unsigned int base; }; static int parse_llint(struct ec_node_int_uint *priv, const char *str, int64_t *val) { int64_t min, max; if (priv->check_min) min = priv->min; else min = LLONG_MIN; if (priv->check_max) max = priv->max; else max = LLONG_MAX; return ec_str_parse_llint(str, priv->base, min, max, val); } static int parse_ullint(struct ec_node_int_uint *priv, const char *str, uint64_t *val) { uint64_t min, max; if (priv->check_min) min = priv->min; else min = 0; if (priv->check_max) max = priv->max; else max = ULLONG_MAX; return ec_str_parse_ullint(str, priv->base, min, max, val); } static int ec_node_int_uint_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_int_uint *priv = ec_node_priv(node); const char *str; uint64_t u64; int64_t i64; (void)pstate; if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; str = ec_strvec_val(strvec, 0); if (priv->is_signed) { if (parse_llint(priv, str, &i64) < 0) return EC_PARSE_NOMATCH; } else { if (parse_ullint(priv, str, &u64) < 0) return EC_PARSE_NOMATCH; } return 1; } static int ec_node_uint_init_priv(struct ec_node *node) { struct ec_node_int_uint *priv = ec_node_priv(node); priv->is_signed = true; return 0; } static const struct ec_config_schema ec_node_int_schema[] = { { .key = "min", .desc = "The minimum valid value (included).", .type = EC_CONFIG_TYPE_INT64, }, { .key = "max", .desc = "The maximum valid value (included).", .type = EC_CONFIG_TYPE_INT64, }, { .key = "base", .desc = "The base to use. If unset or 0, try to guess.", .type = EC_CONFIG_TYPE_UINT64, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_int_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_int_uint *priv = ec_node_priv(node); const struct ec_config *min_value = NULL; const struct ec_config *max_value = NULL; const struct ec_config *base_value = NULL; char *s = NULL; min_value = ec_config_dict_get(config, "min"); max_value = ec_config_dict_get(config, "max"); base_value = ec_config_dict_get(config, "base"); if (min_value && max_value && min_value->i64 > max_value->i64) { errno = EINVAL; goto fail; } if (min_value != NULL) { priv->check_min = true; priv->min = min_value->i64; } else { priv->check_min = false; } if (max_value != NULL) { priv->check_max = true; priv->max = max_value->i64; } else { priv->check_max = false; } if (base_value != NULL) priv->base = base_value->u64; else priv->base = 0; return 0; fail: free(s); return -1; } static struct ec_node_type ec_node_int_type = { .name = "int", .schema = ec_node_int_schema, .set_config = ec_node_int_set_config, .parse = ec_node_int_uint_parse, .size = sizeof(struct ec_node_int_uint), .init_priv = ec_node_uint_init_priv, }; EC_NODE_TYPE_REGISTER(ec_node_int_type); struct ec_node *ec_node_int(const char *id, int64_t min, int64_t max, unsigned int base) { struct ec_config *config = NULL; struct ec_node *node = NULL; int ret; node = ec_node_from_type(&ec_node_int_type, id); if (node == NULL) return NULL; config = ec_config_dict(); if (config == NULL) goto fail; ret = ec_config_dict_set(config, "min", ec_config_i64(min)); if (ret < 0) goto fail; ret = ec_config_dict_set(config, "max", ec_config_i64(max)); if (ret < 0) goto fail; ret = ec_config_dict_set(config, "base", ec_config_u64(base)); if (ret < 0) goto fail; ret = ec_node_set_config(node, config); config = NULL; if (ret < 0) goto fail; return node; fail: ec_config_free(config); ec_node_free(node); return NULL; } static const struct ec_config_schema ec_node_uint_schema[] = { { .key = "min", .desc = "The minimum valid value (included).", .type = EC_CONFIG_TYPE_UINT64, }, { .key = "max", .desc = "The maximum valid value (included).", .type = EC_CONFIG_TYPE_UINT64, }, { .key = "base", .desc = "The base to use. If unset or 0, try to guess.", .type = EC_CONFIG_TYPE_UINT64, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_uint_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_int_uint *priv = ec_node_priv(node); const struct ec_config *min_value = NULL; const struct ec_config *max_value = NULL; const struct ec_config *base_value = NULL; char *s = NULL; min_value = ec_config_dict_get(config, "min"); max_value = ec_config_dict_get(config, "max"); base_value = ec_config_dict_get(config, "base"); if (min_value && max_value && min_value->u64 > max_value->u64) { errno = EINVAL; goto fail; } if (min_value != NULL) { priv->check_min = true; priv->min = min_value->u64; } else { priv->check_min = false; } if (max_value != NULL) { priv->check_max = true; priv->max = max_value->u64; } else { priv->check_max = false; } if (base_value != NULL) priv->base = base_value->u64; else priv->base = 0; return 0; fail: free(s); return -1; } static struct ec_node_type ec_node_uint_type = { .name = "uint", .schema = ec_node_uint_schema, .set_config = ec_node_uint_set_config, .parse = ec_node_int_uint_parse, .size = sizeof(struct ec_node_int_uint), }; EC_NODE_TYPE_REGISTER(ec_node_uint_type); struct ec_node *ec_node_uint(const char *id, uint64_t min, uint64_t max, unsigned int base) { struct ec_config *config = NULL; struct ec_node *node = NULL; int ret; node = ec_node_from_type(&ec_node_uint_type, id); if (node == NULL) return NULL; config = ec_config_dict(); if (config == NULL) goto fail; ret = ec_config_dict_set(config, "min", ec_config_u64(min)); if (ret < 0) goto fail; ret = ec_config_dict_set(config, "max", ec_config_u64(max)); if (ret < 0) goto fail; ret = ec_config_dict_set(config, "base", ec_config_u64(base)); if (ret < 0) goto fail; ret = ec_node_set_config(node, config); config = NULL; if (ret < 0) goto fail; return node; fail: ec_config_free(config); ec_node_free(node); return NULL; } int ec_node_int_getval(const struct ec_node *node, const char *str, int64_t *result) { struct ec_node_int_uint *priv = ec_node_priv(node); int ret; ret = ec_node_check_type(node, &ec_node_int_type); if (ret < 0) return ret; if (parse_llint(priv, str, result) < 0) return -1; return 0; } int ec_node_uint_getval(const struct ec_node *node, const char *str, uint64_t *result) { struct ec_node_int_uint *priv = ec_node_priv(node); int ret; ret = ec_node_check_type(node, &ec_node_uint_type); if (ret < 0) return ret; if (parse_ullint(priv, str, result) < 0) return -1; return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_many.c���������������������������������������������������������������������0000664�0000000�0000000�00000015111�15203622040�0016256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_many.h> #include <ecoli/node_option.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_many); struct ec_node_many { unsigned int min; unsigned int max; struct ec_node *child; }; static int ec_node_many_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_many *priv = ec_node_priv(node); struct ec_pnode *child_parse; struct ec_strvec *childvec = NULL; size_t off = 0, count; int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } for (count = 0; priv->max == 0 || count < priv->max; count++) { childvec = ec_strvec_ndup(strvec, off, ec_strvec_len(strvec) - off); if (childvec == NULL) goto fail; ret = ec_parse_child(priv->child, pstate, childvec); if (ret < 0) goto fail; ec_strvec_free(childvec); childvec = NULL; if (ret == EC_PARSE_NOMATCH) break; /* it matches an empty strvec, break if priv->max == 0 to avoid an infinite loop */ if (ret == 0 && priv->max == 0) { child_parse = ec_pnode_get_last_child(pstate); ec_pnode_unlink_child(child_parse); ec_pnode_free(child_parse); break; } off += ret; } if (count < priv->min) { ec_pnode_free_children(pstate); return EC_PARSE_NOMATCH; } return off; fail: ec_strvec_free(childvec); return -1; } static int ec_node_many_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_pnode *parse = ec_comp_get_cur_pstate(comp); struct ec_node_many *priv = ec_node_priv(node); struct ec_strvec *childvec = NULL; struct ec_strvec *tmpvec = NULL; unsigned int children = 0; unsigned int i; int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } childvec = ec_strvec_dup(strvec); if (childvec == NULL) goto fail; while (1) { ret = ec_complete_child(priv->child, comp, childvec); if (ret < 0) goto fail; /* we're done, we reached the max number of nodes */ if (children + 1 == priv->max) break; ret = ec_parse_child(priv->child, parse, childvec); if (ret < 0) goto fail; if (ret == EC_PARSE_NOMATCH) break; children++; /* avoid infinite loop if parsing does not consume a token */ if (priv->max == 0 && ret == 0) break; tmpvec = ec_strvec_ndup(childvec, ret, ec_strvec_len(childvec) - ret); if (tmpvec == NULL) goto fail; ec_strvec_free(childvec); childvec = tmpvec; tmpvec = NULL; } ret = 0; end: for (i = 0; i < children; i++) ec_pnode_del_last_child(parse); ec_strvec_free(childvec); ec_strvec_free(tmpvec); return ret; fail: ret = -1; goto end; } static void ec_node_many_free_priv(struct ec_node *node) { struct ec_node_many *priv = ec_node_priv(node); ec_node_free(priv->child); } static size_t ec_node_many_get_children_count(const struct ec_node *node) { struct ec_node_many *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_many_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_many *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 2; return 0; } static const struct ec_config_schema ec_node_many_schema[] = { { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .key = "min", .desc = "The minimum number of matches (default = 0).", .type = EC_CONFIG_TYPE_UINT64, }, { .key = "max", .desc = "The maximum number of matches. If 0, there is " "no maximum (default = 0).", .type = EC_CONFIG_TYPE_UINT64, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_many_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_many *priv = ec_node_priv(node); const struct ec_config *child, *min, *max; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } min = ec_config_dict_get(config, "min"); if (min != NULL && (ec_config_get_type(min) != EC_CONFIG_TYPE_UINT64 || min->u64 >= UINT_MAX)) { errno = EINVAL; goto fail; } max = ec_config_dict_get(config, "max"); if (max != NULL && (ec_config_get_type(max) != EC_CONFIG_TYPE_UINT64 || max->u64 >= UINT_MAX)) { errno = EINVAL; goto fail; } if (priv->child != NULL) ec_node_free(priv->child); priv->child = ec_node_clone(child->node); if (min == NULL) priv->min = 0; else priv->min = min->u64; if (max == NULL) priv->max = 0; else priv->max = max->u64; return 0; fail: return -1; } static struct ec_node_type ec_node_many_type = { .name = "many", .schema = ec_node_many_schema, .set_config = ec_node_many_set_config, .parse = ec_node_many_parse, .complete = ec_node_many_complete, .size = sizeof(struct ec_node_many), .free_priv = ec_node_many_free_priv, .get_children_count = ec_node_many_get_children_count, .get_child = ec_node_many_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_many_type); int ec_node_many_set_params( struct ec_node *node, struct ec_node *child, unsigned int min, unsigned int max ) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_many_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; /* freed */ if (ec_config_dict_set(config, "min", ec_config_u64(min)) < 0) goto fail; if (ec_config_dict_set(config, "max", ec_config_u64(max)) < 0) goto fail; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node * ec_node_many(const char *id, struct ec_node *child, unsigned int min, unsigned int max) { struct ec_node *node = NULL; if (child == NULL) return NULL; node = ec_node_from_type(&ec_node_many_type, id); if (node == NULL) goto fail; if (ec_node_many_set_params(node, child, min, max) < 0) { child = NULL; goto fail; } child = NULL; return node; fail: ec_node_free(node); ec_node_free(child); return NULL; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_none.c���������������������������������������������������������������������0000664�0000000�0000000�00000001610�15203622040�0016250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_none.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_none); struct ec_node_none { }; static int ec_node_none_parse( const struct ec_node *node, struct ec_pnode *state, const struct ec_strvec *strvec ) { (void)node; (void)state; (void)strvec; return EC_PARSE_NOMATCH; } static int ec_node_none_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { (void)node; (void)comp; (void)strvec; return 0; } static struct ec_node_type ec_node_none_type = { .name = "none", .parse = ec_node_none_parse, .complete = ec_node_none_complete, .size = sizeof(struct ec_node_none), }; EC_NODE_TYPE_REGISTER(ec_node_none_type); ������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_once.c���������������������������������������������������������������������0000664�0000000�0000000�00000011100�15203622040�0016230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_many.h> #include <ecoli/node_once.h> #include <ecoli/node_or.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_once); struct ec_node_once { struct ec_node *child; }; static unsigned int count_node(struct ec_pnode *parse, const struct ec_node *node) { struct ec_pnode *child; unsigned int count = 0; if (parse == NULL) return 0; if (ec_pnode_get_node(parse) == node) count++; EC_PNODE_FOREACH_CHILD (child, parse) count += count_node(child, node); return count; } static int ec_node_once_parse( const struct ec_node *node, struct ec_pnode *state, const struct ec_strvec *strvec ) { struct ec_node_once *priv = ec_node_priv(node); unsigned int count; if (priv->child == NULL) { errno = ENOENT; return -1; } /* count the number of occurrences of the node: if already parsed, * do not match */ count = count_node(ec_pnode_get_root(state), priv->child); if (count > 0) return EC_PARSE_NOMATCH; return ec_parse_child(priv->child, state, strvec); } static int ec_node_once_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_once *priv = ec_node_priv(node); struct ec_pnode *parse = ec_comp_get_cur_pstate(comp); unsigned int count; int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } /* count the number of occurrences of the node: if already parsed, * do not match */ count = count_node(ec_pnode_get_root(parse), priv->child); if (count > 0) return 0; ret = ec_complete_child(priv->child, comp, strvec); if (ret < 0) return ret; return 0; } static void ec_node_once_free_priv(struct ec_node *node) { struct ec_node_once *priv = ec_node_priv(node); ec_node_free(priv->child); } static size_t ec_node_once_get_children_count(const struct ec_node *node) { struct ec_node_once *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_once_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_once *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 2; return 0; } static const struct ec_config_schema ec_node_once_schema[] = { { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_once_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_once *priv = ec_node_priv(node); const struct ec_config *child; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } if (priv->child != NULL) ec_node_free(priv->child); priv->child = ec_node_clone(child->node); return 0; fail: return -1; } static struct ec_node_type ec_node_once_type = { .name = "once", .schema = ec_node_once_schema, .set_config = ec_node_once_set_config, .parse = ec_node_once_parse, .complete = ec_node_once_complete, .size = sizeof(struct ec_node_once), .free_priv = ec_node_once_free_priv, .get_children_count = ec_node_once_get_children_count, .get_child = ec_node_once_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_once_type); int ec_node_once_set_child(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_once_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; /* freed */ ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *ec_node_once(const char *id, struct ec_node *child) { struct ec_node *node = NULL; if (child == NULL) return NULL; node = ec_node_from_type(&ec_node_once_type, id); if (node == NULL) goto fail; if (ec_node_once_set_child(node, child) < 0) goto fail; child = NULL; return node; fail: ec_node_free(child); return NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_option.c�������������������������������������������������������������������0000664�0000000�0000000�00000007544�15203622040�0016635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_option.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_option); struct ec_node_option { struct ec_node *child; }; static int ec_node_option_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_option *priv = ec_node_priv(node); int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } ret = ec_parse_child(priv->child, pstate, strvec); if (ret < 0) return ret; if (ret == EC_PARSE_NOMATCH) return 0; return ret; } static int ec_node_option_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_option *priv = ec_node_priv(node); if (priv->child == NULL) { errno = ENOENT; return -1; } return ec_complete_child(priv->child, comp, strvec); } static void ec_node_option_free_priv(struct ec_node *node) { struct ec_node_option *priv = ec_node_priv(node); ec_node_free(priv->child); } static size_t ec_node_option_get_children_count(const struct ec_node *node) { struct ec_node_option *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_option_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_option *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 2; return 0; } static const struct ec_config_schema ec_node_option_schema[] = { { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_option_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_option *priv = ec_node_priv(node); const struct ec_config *child; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } if (priv->child != NULL) ec_node_free(priv->child); priv->child = ec_node_clone(child->node); return 0; fail: return -1; } static struct ec_node_type ec_node_option_type = { .name = "option", .schema = ec_node_option_schema, .set_config = ec_node_option_set_config, .parse = ec_node_option_parse, .complete = ec_node_option_complete, .size = sizeof(struct ec_node_option), .free_priv = ec_node_option_free_priv, .get_children_count = ec_node_option_get_children_count, .get_child = ec_node_option_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_option_type); int ec_node_option_set_child(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_option_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; /* freed */ ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *ec_node_option(const char *id, struct ec_node *child) { struct ec_node *node = NULL; if (child == NULL) goto fail; node = ec_node_from_type(&ec_node_option_type, id); if (node == NULL) goto fail; if (ec_node_option_set_child(node, child) < 0) goto fail; child = NULL; return node; fail: ec_node_free(child); return NULL; } ������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_or.c�����������������������������������������������������������������������0000664�0000000�0000000�00000012746�15203622040�0015745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_helper.h> #include <ecoli/node_or.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_or); struct ec_node_or { struct ec_node **table; size_t len; }; static int ec_node_or_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_or *priv = ec_node_priv(node); unsigned int i; int ret; for (i = 0; i < priv->len; i++) { ret = ec_parse_child(priv->table[i], pstate, strvec); if (ret == EC_PARSE_NOMATCH) continue; return ret; } return EC_PARSE_NOMATCH; } static int ec_node_or_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_or *priv = ec_node_priv(node); int ret; size_t n; for (n = 0; n < priv->len; n++) { ret = ec_complete_child(priv->table[n], comp, strvec); if (ret < 0) return ret; } return 0; } static void ec_node_or_free_priv(struct ec_node *node) { struct ec_node_or *priv = ec_node_priv(node); size_t i; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = NULL; priv->len = 0; } static const struct ec_config_schema ec_node_or_subschema[] = { { .desc = "A child node which is part of the choice.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema ec_node_or_schema[] = { { .key = "children", .desc = "The list of children nodes defining the choice " "elements.", .type = EC_CONFIG_TYPE_LIST, .subschema = ec_node_or_subschema, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_or_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_or *priv = ec_node_priv(node); struct ec_node **table = NULL; size_t i, len = 0; table = ec_node_config_node_list_to_table(ec_config_dict_get(config, "children"), &len); if (table == NULL) return -1; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = table; priv->len = len; return 0; } static size_t ec_node_or_get_children_count(const struct ec_node *node) { struct ec_node_or *priv = ec_node_priv(node); return priv->len; } static int ec_node_or_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_or *priv = ec_node_priv(node); if (i >= priv->len) return -1; *child = priv->table[i]; /* each child node is referenced twice: once in the config and * once in the priv->table[] */ *refs = 2; return 0; } static struct ec_node_type ec_node_or_type = { .name = "or", .schema = ec_node_or_schema, .set_config = ec_node_or_set_config, .parse = ec_node_or_parse, .complete = ec_node_or_complete, .size = sizeof(struct ec_node_or), .free_priv = ec_node_or_free_priv, .get_children_count = ec_node_or_get_children_count, .get_child = ec_node_or_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_or_type); struct ec_node *ec_node_or(const char *id) { return ec_node_from_type(&ec_node_or_type, id); } int ec_node_or_add(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL, *children; int ret; assert(node != NULL); if (ec_node_check_type(node, &ec_node_or_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; children = ec_config_dict_get(config, "children"); if (children == NULL) { children = ec_config_list(); if (children == NULL) goto fail; if (ec_config_dict_set(config, "children", children) < 0) goto fail; /* children list is freed on error */ } if (ec_config_list_add(children, ec_config_node(child)) < 0) { child = NULL; goto fail; } ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *__ec_node_or(const char *id, ...) { struct ec_config *config = NULL, *children = NULL; struct ec_node *node = NULL; struct ec_node *child; va_list ap; int ret; va_start(ap, id); child = va_arg(ap, struct ec_node *); node = ec_node_from_type(&ec_node_or_type, id); if (node == NULL) goto fail_free_children; config = ec_config_dict(); if (config == NULL) goto fail_free_children; children = ec_config_list(); if (children == NULL) goto fail_free_children; for (; child != EC_VA_END; child = va_arg(ap, struct ec_node *)) { if (child == NULL) goto fail_free_children; if (ec_config_list_add(children, ec_config_node(child)) < 0) { child = NULL; goto fail_free_children; } } if (ec_config_dict_set(config, "children", children) < 0) { children = NULL; /* freed */ goto fail; } children = NULL; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; va_end(ap); return node; fail_free_children: for (; child != EC_VA_END; child = va_arg(ap, struct ec_node *)) ec_node_free(child); fail: ec_node_free(node); /* will also free added children */ ec_config_free(children); ec_config_free(config); va_end(ap); return NULL; } ��������������������������libecoli-0.11.6/src/node_re.c�����������������������������������������������������������������������0000664�0000000�0000000�00000006132�15203622040�0015723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <regex.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_re.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_re); struct ec_node_re { char *re_str; regex_t re; }; static int ec_node_re_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_re *priv = ec_node_priv(node); const char *str; regmatch_t pos; (void)pstate; if (priv->re_str == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; str = ec_strvec_val(strvec, 0); if (regexec(&priv->re, str, 1, &pos, 0) != 0) return EC_PARSE_NOMATCH; if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str)) return EC_PARSE_NOMATCH; return 1; } static void ec_node_re_free_priv(struct ec_node *node) { struct ec_node_re *priv = ec_node_priv(node); if (priv->re_str != NULL) { free(priv->re_str); regfree(&priv->re); } } static const struct ec_config_schema ec_node_re_schema[] = { { .key = "pattern", .desc = "The pattern to match.", .type = EC_CONFIG_TYPE_STRING, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_re_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_re *priv = ec_node_priv(node); const struct ec_config *value = NULL; char *s = NULL; regex_t re; int ret; value = ec_config_dict_get(config, "pattern"); if (value == NULL) { errno = EINVAL; goto fail; } s = strdup(value->string); if (s == NULL) goto fail; ret = regcomp(&re, s, REG_EXTENDED); if (ret != 0) { if (ret == REG_ESPACE) errno = ENOMEM; else errno = EINVAL; goto fail; } if (priv->re_str != NULL) { free(priv->re_str); regfree(&priv->re); } priv->re_str = s; priv->re = re; return 0; fail: free(s); return -1; } static struct ec_node_type ec_node_re_type = { .name = "re", .schema = ec_node_re_schema, .set_config = ec_node_re_set_config, .parse = ec_node_re_parse, .size = sizeof(struct ec_node_re), .free_priv = ec_node_re_free_priv, }; EC_NODE_TYPE_REGISTER(ec_node_re_type); int ec_node_re_set_regexp(struct ec_node *node, const char *str) { struct ec_config *config = NULL; int ret; EC_CHECK_ARG(str != NULL, -1, EINVAL); if (ec_node_check_type(node, &ec_node_re_type) < 0) goto fail; config = ec_config_dict(); if (config == NULL) goto fail; ret = ec_config_dict_set(config, "pattern", ec_config_string(str)); if (ret < 0) goto fail; ret = ec_node_set_config(node, config); config = NULL; if (ret < 0) goto fail; return 0; fail: ec_config_free(config); return -1; } struct ec_node *ec_node_re(const char *id, const char *re_str) { struct ec_node *node = NULL; node = ec_node_from_type(&ec_node_re_type, id); if (node == NULL) goto fail; if (ec_node_re_set_regexp(node, re_str) < 0) goto fail; return node; fail: ec_node_free(node); return NULL; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_re_lex.c�������������������������������������������������������������������0000664�0000000�0000000�00000024225�15203622040�0016576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <regex.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_int.h> #include <ecoli/node_many.h> #include <ecoli/node_or.h> #include <ecoli/node_re_lex.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_re_lex); struct regexp_pattern { char *pattern; char *attr_name; regex_t r; bool keep; }; struct ec_node_re_lex { struct ec_node *child; struct regexp_pattern *table; size_t len; }; static struct ec_strvec *tokenize(struct regexp_pattern *table, size_t table_len, const char *str) { struct ec_strvec *strvec = NULL; struct ec_dict *attrs = NULL; char *dup = NULL; char c; size_t len, off = 0; size_t i; int ret = 0; regmatch_t pos = {0}; dup = strdup(str); if (dup == NULL) goto fail; strvec = ec_strvec(); if (strvec == NULL) goto fail; len = strlen(dup); while (off < len) { for (i = 0; i < table_len; i++) { ret = regexec(&table[i].r, &dup[off], 1, &pos, 0); if (ret != 0) continue; if (pos.rm_so != 0 || pos.rm_eo == 0) { ret = -1; continue; } if (table[i].keep == 0) break; c = dup[pos.rm_eo + off]; dup[pos.rm_eo + off] = '\0'; EC_LOG(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]); if (ec_strvec_add(strvec, &dup[off]) < 0) goto fail; if (table[i].attr_name != NULL) { attrs = ec_dict(); if (attrs == NULL) goto fail; if (ec_dict_set(attrs, table[i].attr_name, NULL, NULL) < 0) goto fail; if (ec_strvec_set_attrs(strvec, ec_strvec_len(strvec) - 1, attrs) < 0) { attrs = NULL; goto fail; } attrs = NULL; } dup[pos.rm_eo + off] = c; break; } if (ret != 0) goto fail; off += pos.rm_eo; } free(dup); return strvec; fail: free(dup); ec_strvec_free(strvec); return NULL; } static int ec_node_re_lex_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_re_lex *priv = ec_node_priv(node); struct ec_strvec *new_vec = NULL; struct ec_pnode *child_parse; const char *str; int ret; if (priv->child == NULL || priv->len == 0) { errno = EINVAL; goto fail; } if (ec_strvec_len(strvec) == 0) { new_vec = ec_strvec(); } else { str = ec_strvec_val(strvec, 0); new_vec = tokenize(priv->table, priv->len, str); } if (new_vec == NULL) goto fail; ret = ec_parse_child(priv->child, pstate, new_vec); if (ret < 0) goto fail; if ((unsigned)ret == ec_strvec_len(new_vec)) { ret = 1; } else if (ret != EC_PARSE_NOMATCH) { child_parse = ec_pnode_get_last_child(pstate); ec_pnode_unlink_child(child_parse); ec_pnode_free(child_parse); ret = EC_PARSE_NOMATCH; } ec_strvec_free(new_vec); new_vec = NULL; return ret; fail: ec_strvec_free(new_vec); return -1; } static void ec_node_re_lex_free_priv(struct ec_node *node) { struct ec_node_re_lex *priv = ec_node_priv(node); unsigned int i; ec_node_free(priv->child); for (i = 0; i < priv->len; i++) { free(priv->table[i].pattern); free(priv->table[i].attr_name); regfree(&priv->table[i].r); } free(priv->table); } static size_t ec_node_re_lex_get_children_count(const struct ec_node *node) { struct ec_node_re_lex *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_re_lex_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_re_lex *priv = ec_node_priv(node); if (i >= 1) return -1; *child = priv->child; *refs = 2; return 0; } static const struct ec_config_schema ec_node_re_lex_dict[] = { { .key = "pattern", .desc = "The pattern to match.", .type = EC_CONFIG_TYPE_STRING, }, { .key = "keep", .desc = "Whether to keep or drop the string matching " "the regular expression.", .type = EC_CONFIG_TYPE_BOOL, }, { .key = "attr", .desc = "The optional attribute name to attach.", .type = EC_CONFIG_TYPE_STRING, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema ec_node_re_lex_elt[] = { { .desc = "A pattern element.", .type = EC_CONFIG_TYPE_DICT, .subschema = ec_node_re_lex_dict, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema ec_node_re_lex_schema[] = { { .key = "patterns", .desc = "The list of patterns elements.", .type = EC_CONFIG_TYPE_LIST, .subschema = ec_node_re_lex_elt, }, { .key = "child", .desc = "The child node.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_re_lex_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_re_lex *priv = ec_node_priv(node); struct regexp_pattern *table = NULL; const struct ec_config *patterns, *child, *elt, *pattern, *keep, *attr; char *pattern_str = NULL, *attr_name = NULL; ssize_t i, n = 0; int ret; child = ec_config_dict_get(config, "child"); if (child == NULL) goto fail; if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) { errno = EINVAL; goto fail; } patterns = ec_config_dict_get(config, "patterns"); if (patterns != NULL) { n = ec_config_count(patterns); if (n < 0) goto fail; table = calloc(n, sizeof(*table)); if (table == NULL) goto fail; n = 0; TAILQ_FOREACH (elt, &patterns->list, next) { if (ec_config_get_type(elt) != EC_CONFIG_TYPE_DICT) { errno = EINVAL; goto fail; } pattern = ec_config_dict_get(elt, "pattern"); if (pattern == NULL) { errno = EINVAL; goto fail; } if (ec_config_get_type(pattern) != EC_CONFIG_TYPE_STRING) { errno = EINVAL; goto fail; } keep = ec_config_dict_get(elt, "keep"); if (keep == NULL) { errno = EINVAL; goto fail; } if (ec_config_get_type(keep) != EC_CONFIG_TYPE_BOOL) { errno = EINVAL; goto fail; } attr = ec_config_dict_get(elt, "attr"); if (attr != NULL && ec_config_get_type(attr) != EC_CONFIG_TYPE_STRING) { errno = EINVAL; goto fail; } pattern_str = strdup(pattern->string); if (pattern_str == NULL) goto fail; if (attr != NULL && attr->string != NULL) { attr_name = strdup(attr->string); if (attr_name == NULL) goto fail; } ret = regcomp(&table[n].r, pattern_str, REG_EXTENDED); if (ret != 0) { EC_LOG(EC_LOG_ERR, "Regular expression <%s> compilation failed: %d\n", pattern_str, ret); if (ret == REG_ESPACE) errno = ENOMEM; else errno = EINVAL; goto fail; } table[n].pattern = pattern_str; table[n].keep = keep->boolean; table[n].attr_name = attr_name; pattern_str = NULL; attr_name = NULL; n++; } } if (priv->child != NULL) ec_node_free(priv->child); priv->child = ec_node_clone(child->node); for (i = 0; i < (ssize_t)priv->len; i++) { free(priv->table[i].pattern); free(priv->table[i].attr_name); regfree(&priv->table[i].r); } free(priv->table); priv->table = table; priv->len = n; return 0; fail: if (table != NULL) { for (i = 0; i < n; i++) { if (table[i].pattern != NULL) { free(table[i].pattern); free(table[i].attr_name); regfree(&table[i].r); } } } free(table); free(pattern_str); free(attr_name); return -1; } static struct ec_node_type ec_node_re_lex_type = { .name = "re_lex", .schema = ec_node_re_lex_schema, .set_config = ec_node_re_lex_set_config, .parse = ec_node_re_lex_parse, .size = sizeof(struct ec_node_re_lex), .free_priv = ec_node_re_lex_free_priv, .get_children_count = ec_node_re_lex_get_children_count, .get_child = ec_node_re_lex_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_re_lex_type); int ec_node_re_lex_add(struct ec_node *node, const char *pattern, int keep, const char *attr_name) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL, *patterns = NULL, *elt = NULL; int ret; if (ec_node_check_type(node, &ec_node_re_lex_type) < 0) goto fail; elt = ec_config_dict(); if (elt == NULL) goto fail; if (ec_config_dict_set(elt, "pattern", ec_config_string(pattern)) < 0) goto fail; if (ec_config_dict_set(elt, "keep", ec_config_bool(keep)) < 0) goto fail; if (attr_name != NULL) { if (ec_config_dict_set(elt, "attr", ec_config_string(attr_name)) < 0) goto fail; } cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; patterns = ec_config_dict_get(config, "patterns"); if (patterns == NULL) { patterns = ec_config_list(); if (patterns == NULL) goto fail; if (ec_config_dict_set(config, "patterns", patterns) < 0) goto fail; /* patterns list is freed on error */ } if (ec_config_list_add(patterns, elt) < 0) { elt = NULL; goto fail; } elt = NULL; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_config_free(elt); return -1; } static int ec_node_re_lex_set_child(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_re_lex_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) { child = NULL; /* freed */ goto fail; } child = NULL; /* freed */ ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child) { struct ec_node *node = NULL; if (child == NULL) return NULL; node = ec_node_from_type(&ec_node_re_lex_type, id); if (node == NULL) goto fail; if (ec_node_re_lex_set_child(node, child) < 0) { child = NULL; /* freed */ goto fail; } return node; fail: ec_node_free(node); ec_node_free(child); return NULL; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_seq.c����������������������������������������������������������������������0000664�0000000�0000000�00000016677�15203622040�0016124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_helper.h> #include <ecoli/node_many.h> #include <ecoli/node_option.h> #include <ecoli/node_or.h> #include <ecoli/node_seq.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_seq); struct ec_node_seq { struct ec_node **table; size_t len; }; static int ec_node_seq_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_seq *priv = ec_node_priv(node); struct ec_strvec *childvec = NULL; size_t len = 0; unsigned int i; int ret; for (i = 0; i < priv->len; i++) { childvec = ec_strvec_ndup(strvec, len, ec_strvec_len(strvec) - len); if (childvec == NULL) goto fail; ret = ec_parse_child(priv->table[i], pstate, childvec); if (ret < 0) goto fail; ec_strvec_free(childvec); childvec = NULL; if (ret == EC_PARSE_NOMATCH) { ec_pnode_free_children(pstate); return EC_PARSE_NOMATCH; } len += ret; } return len; fail: ec_strvec_free(childvec); return -1; } static int __ec_node_seq_complete( struct ec_node **table, size_t table_len, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_pnode *parse = ec_comp_get_cur_pstate(comp); struct ec_strvec *childvec = NULL; unsigned int i; int ret; if (table_len == 0) return 0; /* * Example of completion for a sequence node = [n1,n2] and an * input = [a,b,c,d]: * * result = complete(n1, [a,b,c,d]) + * complete(n2, [b,c,d]) if n1 matches [a] + * complete(n2, [c,d]) if n1 matches [a,b] + * complete(n2, [d]) if n1 matches [a,b,c] + * complete(n2, []) if n1 matches [a,b,c,d] */ /* first, try to complete with the first node of the table */ ret = ec_complete_child(table[0], comp, strvec); if (ret < 0) goto fail; /* then, if the first node of the table matches the beginning of the * strvec, try to complete the rest */ for (i = 0; i < ec_strvec_len(strvec); i++) { childvec = ec_strvec_ndup(strvec, 0, i); if (childvec == NULL) goto fail; ret = ec_parse_child(table[0], parse, childvec); if (ret < 0) goto fail; ec_strvec_free(childvec); childvec = NULL; if ((unsigned int)ret != i) { if (ret != EC_PARSE_NOMATCH) ec_pnode_del_last_child(parse); continue; } childvec = ec_strvec_ndup(strvec, i, ec_strvec_len(strvec) - i); if (childvec == NULL) { ec_pnode_del_last_child(parse); goto fail; } ret = __ec_node_seq_complete(&table[1], table_len - 1, comp, childvec); ec_pnode_del_last_child(parse); ec_strvec_free(childvec); childvec = NULL; if (ret < 0) goto fail; } return 0; fail: ec_strvec_free(childvec); return -1; } static int ec_node_seq_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_seq *priv = ec_node_priv(node); return __ec_node_seq_complete(priv->table, priv->len, comp, strvec); } static void ec_node_seq_free_priv(struct ec_node *node) { struct ec_node_seq *priv = ec_node_priv(node); size_t i; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = NULL; priv->len = 0; } static const struct ec_config_schema ec_node_seq_subschema[] = { { .desc = "A child node which is part of the sequence.", .type = EC_CONFIG_TYPE_NODE, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema ec_node_seq_schema[] = { { .key = "children", .desc = "The list of children nodes, to be parsed in sequence.", .type = EC_CONFIG_TYPE_LIST, .subschema = ec_node_seq_subschema, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_seq_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_seq *priv = ec_node_priv(node); struct ec_node **table = NULL; size_t i, len = 0; table = ec_node_config_node_list_to_table(ec_config_dict_get(config, "children"), &len); if (table == NULL) return -1; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); priv->table = table; priv->len = len; return 0; } static size_t ec_node_seq_get_children_count(const struct ec_node *node) { struct ec_node_seq *priv = ec_node_priv(node); return priv->len; } static int ec_node_seq_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_seq *priv = ec_node_priv(node); if (i >= priv->len) return -1; *child = priv->table[i]; /* each child node is referenced twice: once in the config and * once in the priv->table[] */ *refs = 2; return 0; } static struct ec_node_type ec_node_seq_type = { .name = "seq", .schema = ec_node_seq_schema, .set_config = ec_node_seq_set_config, .parse = ec_node_seq_parse, .complete = ec_node_seq_complete, .size = sizeof(struct ec_node_seq), .free_priv = ec_node_seq_free_priv, .get_children_count = ec_node_seq_get_children_count, .get_child = ec_node_seq_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_seq_type); struct ec_node *ec_node_seq(const char *id) { return ec_node_from_type(&ec_node_seq_type, id); } int ec_node_seq_add(struct ec_node *node, struct ec_node *child) { const struct ec_config *cur_config = NULL; struct ec_config *config = NULL, *children; int ret; assert(node != NULL); if (ec_node_check_type(node, &ec_node_seq_type) < 0) goto fail; cur_config = ec_node_get_config(node); if (cur_config == NULL) config = ec_config_dict(); else config = ec_config_dup(cur_config); if (config == NULL) goto fail; children = ec_config_dict_get(config, "children"); if (children == NULL) { children = ec_config_list(); if (children == NULL) goto fail; if (ec_config_dict_set(config, "children", children) < 0) goto fail; /* children list is freed on error */ } if (ec_config_list_add(children, ec_config_node(child)) < 0) { child = NULL; goto fail; } ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; return 0; fail: ec_config_free(config); ec_node_free(child); return -1; } struct ec_node *__ec_node_seq(const char *id, ...) { struct ec_config *config = NULL, *children = NULL; struct ec_node *node = NULL; struct ec_node *child; va_list ap; int ret; va_start(ap, id); child = va_arg(ap, struct ec_node *); node = ec_node_from_type(&ec_node_seq_type, id); if (node == NULL) goto fail_free_children; config = ec_config_dict(); if (config == NULL) goto fail_free_children; children = ec_config_list(); if (children == NULL) goto fail_free_children; for (; child != EC_VA_END; child = va_arg(ap, struct ec_node *)) { if (child == NULL) goto fail_free_children; if (ec_config_list_add(children, ec_config_node(child)) < 0) { child = NULL; goto fail_free_children; } } if (ec_config_dict_set(config, "children", children) < 0) { children = NULL; /* freed */ goto fail; } children = NULL; ret = ec_node_set_config(node, config); config = NULL; /* freed */ if (ret < 0) goto fail; va_end(ap); return node; fail_free_children: for (; child != EC_VA_END; child = va_arg(ap, struct ec_node *)) ec_node_free(child); fail: ec_node_free(node); /* will also free added children */ ec_config_free(children); ec_config_free(config); va_end(ap); return NULL; } �����������������������������������������������������������������libecoli-0.11.6/src/node_sh_lex.c�������������������������������������������������������������������0000664�0000000�0000000�00000013420�15203622040�0016575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/htable.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_option.h> #include <ecoli/node_seq.h> #include <ecoli/node_sh_lex.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_sh_lex); struct ec_node_sh_lex { struct ec_node *child; bool expand; }; static int ec_node_sh_lex_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_sh_lex *priv = ec_node_priv(node); struct ec_strvec *new_vec = NULL; struct ec_pnode *child_parse; const char *str; int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) == 0) { new_vec = ec_strvec(); } else { str = ec_strvec_val(strvec, 0); new_vec = ec_strvec_sh_lex_str(str, EC_STRVEC_STRICT, NULL); } if (new_vec == NULL && errno == EBADMSG) /* quotes not closed */ return EC_PARSE_NOMATCH; if (new_vec == NULL) goto fail; if (priv->expand) { struct ec_strvec *exp; exp = ec_complete_strvec_expand(priv->child, EC_COMP_FULL, new_vec); if (exp == NULL) goto fail; ec_strvec_free(new_vec); new_vec = exp; } ret = ec_parse_child(priv->child, pstate, new_vec); if (ret < 0) goto fail; if ((unsigned)ret == ec_strvec_len(new_vec)) { ret = 1; } else if (ret != EC_PARSE_NOMATCH) { child_parse = ec_pnode_get_last_child(pstate); ec_pnode_unlink_child(child_parse); ec_pnode_free(child_parse); ret = EC_PARSE_NOMATCH; } ec_strvec_free(new_vec); new_vec = NULL; return ret; fail: ec_strvec_free(new_vec); return -1; } static int ec_node_sh_lex_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_sh_lex *priv = ec_node_priv(node); struct ec_strvec *new_vec = NULL; struct ec_comp_item *item = NULL; struct ec_htable *htable = NULL; char *new_str = NULL; const char *str, *last; char missing_quote = '\0'; int ret; if (priv->child == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) != 1) return 0; str = ec_strvec_val(strvec, 0); new_vec = ec_strvec_sh_lex_str(str, EC_STRVEC_TRAILSP, &missing_quote); if (new_vec == NULL) goto fail; /* let's store the existing full completions in a htable */ htable = ec_htable(); if (htable == NULL) goto fail; EC_COMP_FOREACH (item, comp, EC_COMP_FULL) { if (ec_htable_set(htable, &item, sizeof(item), NULL, NULL) < 0) goto fail; } /* do the completion */ if (priv->expand) { struct ec_strvec *exp; exp = ec_complete_strvec_expand(priv->child, EC_COMP_FULL, new_vec); if (exp == NULL) goto fail; ret = ec_complete_child(priv->child, comp, exp); ec_strvec_free(exp); } else { ret = ec_complete_child(priv->child, comp, new_vec); } if (ret < 0) goto fail; if (ec_strvec_len(new_vec) > 0) last = ec_strvec_val(new_vec, ec_strvec_len(new_vec) - 1); else last = NULL; EC_COMP_FOREACH (item, comp, EC_COMP_FULL) { if (ec_htable_has_key(htable, &item, sizeof(item))) continue; /* update the complete chars to compensate those in the expanded string */ if (priv->expand && last != NULL) { const char *s = ec_comp_item_get_str(item); size_t prefix = ec_strcmp_count(s, last); if (ec_comp_item_set_completion(item, s + prefix) < 0) goto fail; } /* add missing quote for any new full completions */ if (missing_quote != '\0') { str = ec_comp_item_get_str(item); if (asprintf(&new_str, "%c%s%c", missing_quote, str, missing_quote) < 0) { new_str = NULL; goto fail; } if (ec_comp_item_set_str(item, new_str) < 0) goto fail; free(new_str); new_str = NULL; str = ec_comp_item_get_completion(item); if (asprintf(&new_str, "%s%c", str, missing_quote) < 0) { new_str = NULL; goto fail; } if (ec_comp_item_set_completion(item, new_str) < 0) goto fail; free(new_str); new_str = NULL; } } ec_strvec_free(new_vec); ec_htable_free(htable); return 0; fail: ec_strvec_free(new_vec); free(new_str); ec_htable_free(htable); return -1; } static void ec_node_sh_lex_free_priv(struct ec_node *node) { struct ec_node_sh_lex *priv = ec_node_priv(node); ec_node_free(priv->child); } static size_t ec_node_sh_lex_get_children_count(const struct ec_node *node) { struct ec_node_sh_lex *priv = ec_node_priv(node); if (priv->child) return 1; return 0; } static int ec_node_sh_lex_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_sh_lex *priv = ec_node_priv(node); if (i >= 1) return -1; *refs = 1; *child = priv->child; return 0; } static struct ec_node_type ec_node_sh_lex_type = { .name = "sh_lex", .parse = ec_node_sh_lex_parse, .complete = ec_node_sh_lex_complete, .size = sizeof(struct ec_node_sh_lex), .free_priv = ec_node_sh_lex_free_priv, .get_children_count = ec_node_sh_lex_get_children_count, .get_child = ec_node_sh_lex_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_sh_lex_type); struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child) { struct ec_node *node = NULL; struct ec_node_sh_lex *priv; if (child == NULL) return NULL; node = ec_node_from_type(&ec_node_sh_lex_type, id); if (node == NULL) { ec_node_free(child); return NULL; } priv = ec_node_priv(node); priv->child = child; priv->expand = false; return node; } struct ec_node *ec_node_sh_lex_expand(const char *id, struct ec_node *child) { struct ec_node *node = ec_node_sh_lex(id, child); struct ec_node_sh_lex *priv; if (node == NULL) return NULL; priv = ec_node_priv(node); priv->expand = true; return node; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_space.c��������������������������������������������������������������������0000664�0000000�0000000�00000001725�15203622040�0016413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_space.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_space); struct ec_node_space { }; static int ec_node_space_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { const char *str; size_t len = 0; (void)pstate; (void)node; if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; str = ec_strvec_val(strvec, 0); while (isspace(str[len])) len++; if (len == 0 || len != strlen(str)) return EC_PARSE_NOMATCH; return 1; } static struct ec_node_type ec_node_space_type = { .name = "space", .parse = ec_node_space_parse, .size = sizeof(struct ec_node_space), }; EC_NODE_TYPE_REGISTER(ec_node_space_type); �������������������������������������������libecoli-0.11.6/src/node_str.c����������������������������������������������������������������������0000664�0000000�0000000�00000007037�15203622040�0016132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/config.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_str); struct ec_node_str { char *string; unsigned len; }; static int ec_node_str_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_str *priv = ec_node_priv(node); const char *str; (void)pstate; if (priv->string == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) == 0) return EC_PARSE_NOMATCH; str = ec_strvec_val(strvec, 0); if (strcmp(str, priv->string) != 0) return EC_PARSE_NOMATCH; return 1; } static int ec_node_str_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_str *priv = ec_node_priv(node); const struct ec_comp_item *item; const char *str; if (priv->string == NULL) { errno = ENOENT; return -1; } if (ec_strvec_len(strvec) != 1) return 0; str = ec_strvec_val(strvec, 0); if (!ec_str_startswith(priv->string, str)) return 0; item = ec_comp_add_item(comp, node, EC_COMP_FULL, str, priv->string); if (item == NULL) return -1; return 0; } static char *ec_node_str_desc(const struct ec_node *node) { struct ec_node_str *priv = ec_node_priv(node); if (priv->string == NULL) return NULL; return strdup(priv->string); } static void ec_node_str_free_priv(struct ec_node *node) { struct ec_node_str *priv = ec_node_priv(node); free(priv->string); } static const struct ec_config_schema ec_node_str_schema[] = { { .key = "string", .desc = "The string to match.", .type = EC_CONFIG_TYPE_STRING, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static int ec_node_str_set_config(struct ec_node *node, const struct ec_config *config) { struct ec_node_str *priv = ec_node_priv(node); const struct ec_config *value = NULL; char *s = NULL; value = ec_config_dict_get(config, "string"); if (value == NULL) { errno = EINVAL; goto fail; } s = strdup(value->string); if (s == NULL) goto fail; free(priv->string); priv->string = s; priv->len = strlen(priv->string); return 0; fail: free(s); return -1; } static struct ec_node_type ec_node_str_type = { .name = "str", .schema = ec_node_str_schema, .set_config = ec_node_str_set_config, .parse = ec_node_str_parse, .complete = ec_node_str_complete, .desc = ec_node_str_desc, .size = sizeof(struct ec_node_str), .free_priv = ec_node_str_free_priv, }; EC_NODE_TYPE_REGISTER(ec_node_str_type); int ec_node_str_set_str(struct ec_node *node, const char *str) { struct ec_config *config = NULL; int ret; if (ec_node_check_type(node, &ec_node_str_type) < 0) goto fail; if (str == NULL) { errno = EINVAL; goto fail; } config = ec_config_dict(); if (config == NULL) goto fail; ret = ec_config_dict_set(config, "string", ec_config_string(str)); if (ret < 0) goto fail; ret = ec_node_set_config(node, config); config = NULL; if (ret < 0) goto fail; return 0; fail: ec_config_free(config); return -1; } struct ec_node *ec_node_str(const char *id, const char *str) { struct ec_node *node = NULL; node = ec_node_from_type(&ec_node_str_type, id); if (node == NULL) goto fail; if (ec_node_str_set_str(node, str) < 0) goto fail; return node; fail: ec_node_free(node); return NULL; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/node_subset.c�������������������������������������������������������������������0000664�0000000�0000000�00000020017�15203622040�0016620�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/complete.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_or.h> #include <ecoli/node_str.h> #include <ecoli/node_subset.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(node_subset); struct ec_node_subset { struct ec_node **table; unsigned int len; unsigned int min; }; struct parse_result { size_t parse_len; /* number of parsed nodes */ size_t len; /* consumed strings */ }; /* recursively find the longest list of nodes that matches: the state is * updated accordingly. */ static int __ec_node_subset_parse( struct parse_result *out, struct ec_node **table, size_t table_len, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node **child_table; struct ec_strvec *childvec = NULL; size_t i, j, len = 0; struct parse_result best_result, result; struct ec_pnode *best_parse = NULL; int ret; if (table_len == 0) return 0; memset(&best_result, 0, sizeof(best_result)); child_table = calloc(table_len - 1, sizeof(*child_table)); if (child_table == NULL) goto fail; for (i = 0; i < table_len; i++) { /* try to parse elt i */ ret = ec_parse_child(table[i], pstate, strvec); if (ret < 0) goto fail; if (ret == EC_PARSE_NOMATCH) continue; /* build a new table without elt i */ for (j = 0; j < table_len; j++) { if (j < i) child_table[j] = table[j]; else if (j > i) child_table[j - 1] = table[j]; } /* build a new strvec (ret is the len of matched strvec) */ len = ret; childvec = ec_strvec_ndup(strvec, len, ec_strvec_len(strvec) - len); if (childvec == NULL) goto fail; memset(&result, 0, sizeof(result)); ret = __ec_node_subset_parse(&result, child_table, table_len - 1, pstate, childvec); ec_strvec_free(childvec); childvec = NULL; if (ret < 0) goto fail; /* if result is not the best, ignore */ if (result.parse_len < best_result.parse_len) { memset(&result, 0, sizeof(result)); ec_pnode_del_last_child(pstate); continue; } /* replace the previous best result */ ec_pnode_free(best_parse); best_parse = ec_pnode_get_last_child(pstate); ec_pnode_unlink_child(best_parse); best_result.parse_len = result.parse_len + 1; best_result.len = len + result.len; memset(&result, 0, sizeof(result)); } *out = best_result; free(child_table); if (best_parse != NULL) ec_pnode_link_child(pstate, best_parse); return 0; fail: ec_pnode_free(best_parse); ec_strvec_free(childvec); free(child_table); return -1; } static int ec_node_subset_parse( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { struct ec_node_subset *priv = ec_node_priv(node); struct ec_pnode *parse = NULL; struct parse_result result; int ret; memset(&result, 0, sizeof(result)); ret = __ec_node_subset_parse(&result, priv->table, priv->len, pstate, strvec); if (ret < 0) goto fail; if (result.parse_len < priv->min) return EC_PARSE_NOMATCH; /* if no child node matches, return a matching empty strvec */ return result.len; fail: ec_pnode_free(parse); return ret; } static int __ec_node_subset_complete( struct ec_node **table, size_t table_len, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_pnode *parse = ec_comp_get_cur_pstate(comp); struct ec_strvec *childvec = NULL; struct ec_node *save; size_t i, len; int ret; /* * example with table = [a, b, c] * subset_complete([a,b,c], strvec) returns: * complete(a, strvec) + complete(b, strvec) + complete(c, strvec) + * + __subset_complete([b, c], childvec) if a matches * + __subset_complete([a, c], childvec) if b matches * + __subset_complete([a, b], childvec) if c matches */ /* first, try to complete with each node of the table */ for (i = 0; i < table_len; i++) { if (table[i] == NULL) continue; ret = ec_complete_child(table[i], comp, strvec); if (ret < 0) goto fail; } /* then, if a node matches, advance in strvec and try to complete with * all the other nodes */ for (i = 0; i < table_len; i++) { if (table[i] == NULL) continue; ret = ec_parse_child(table[i], parse, strvec); if (ret < 0) goto fail; if (ret == EC_PARSE_NOMATCH) continue; len = ret; childvec = ec_strvec_ndup(strvec, len, ec_strvec_len(strvec) - len); if (childvec == NULL) { ec_pnode_del_last_child(parse); goto fail; } save = table[i]; table[i] = NULL; ret = __ec_node_subset_complete(table, table_len, comp, childvec); table[i] = save; ec_strvec_free(childvec); childvec = NULL; ec_pnode_del_last_child(parse); if (ret < 0) goto fail; } return 0; fail: return -1; } static int ec_node_subset_complete( const struct ec_node *node, struct ec_comp *comp, const struct ec_strvec *strvec ) { struct ec_node_subset *priv = ec_node_priv(node); return __ec_node_subset_complete(priv->table, priv->len, comp, strvec); } static void ec_node_subset_free_priv(struct ec_node *node) { struct ec_node_subset *priv = ec_node_priv(node); size_t i; for (i = 0; i < priv->len; i++) ec_node_free(priv->table[i]); free(priv->table); } static size_t ec_node_subset_get_children_count(const struct ec_node *node) { struct ec_node_subset *priv = ec_node_priv(node); return priv->len; } static int ec_node_subset_get_child( const struct ec_node *node, size_t i, struct ec_node **child, unsigned int *refs ) { struct ec_node_subset *priv = ec_node_priv(node); if (i >= priv->len) return -1; *child = priv->table[i]; *refs = 1; return 0; } static struct ec_node_type ec_node_subset_type = { .name = "subset", .parse = ec_node_subset_parse, .complete = ec_node_subset_complete, .size = sizeof(struct ec_node_subset), .free_priv = ec_node_subset_free_priv, .get_children_count = ec_node_subset_get_children_count, .get_child = ec_node_subset_get_child, }; EC_NODE_TYPE_REGISTER(ec_node_subset_type); struct ec_node *ec_node_subset(const char *id) { return ec_node_from_type(&ec_node_subset_type, id); } int ec_node_subset_add(struct ec_node *node, struct ec_node *child) { struct ec_node_subset *priv = ec_node_priv(node); struct ec_node **table; assert(node != NULL); if (child == NULL) { errno = EINVAL; goto fail; } if (ec_node_check_type(node, &ec_node_subset_type) < 0) goto fail; table = realloc(priv->table, (priv->len + 1) * sizeof(*priv->table)); if (table == NULL) { ec_node_free(child); return -1; } priv->table = table; table[priv->len] = child; priv->len++; return 0; fail: ec_node_free(child); return -1; } int ec_node_subset_set_min(struct ec_node *node, unsigned int min) { struct ec_node_subset *priv = ec_node_priv(node); if (ec_node_check_type(node, &ec_node_subset_type) < 0) return -1; priv->min = min; return 0; } unsigned int ec_node_subset_get_min(const struct ec_node *node) { const struct ec_node_subset *priv = ec_node_priv(node); if (ec_node_check_type(node, &ec_node_subset_type) < 0) return 0; return priv->min; } struct ec_node *ec_node_subset_min(const char *id, unsigned int min) { struct ec_node_subset *priv; struct ec_node *node; node = ec_node_from_type(&ec_node_subset_type, id); if (node == NULL) return NULL; priv = ec_node_priv(node); priv->min = min; return node; } struct ec_node *__ec_node_subset(const char *id, ...) { struct ec_node *node = NULL; struct ec_node *child; va_list ap; int fail = 0; va_start(ap, id); node = ec_node_from_type(&ec_node_subset_type, id); if (node == NULL) fail = 1; for (child = va_arg(ap, struct ec_node *); child != EC_VA_END; child = va_arg(ap, struct ec_node *)) { /* on error, don't quit the loop to avoid leaks */ if (fail == 1 || child == NULL || ec_node_subset_add(node, child) < 0) { fail = 1; ec_node_free(child); } } if (fail == 1) goto fail; va_end(ap); return node; fail: ec_node_free(node); /* will also free children */ va_end(ap); return NULL; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/parse.c�������������������������������������������������������������������������0000664�0000000�0000000�00000022213�15203622040�0015420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/assert.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/node_seq.h> #include <ecoli/node_sh_lex.h> #include <ecoli/node_str.h> #include <ecoli/parse.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(parse); TAILQ_HEAD(ec_pnode_list, ec_pnode); struct ec_pnode { TAILQ_ENTRY(ec_pnode) next; struct ec_pnode_list children; struct ec_pnode *parent; const struct ec_node *node; struct ec_strvec *strvec; struct ec_dict *attrs; }; static int __ec_parse_child( const struct ec_node *node, struct ec_pnode *pstate, bool is_root, const struct ec_strvec *strvec ) { struct ec_strvec *match_strvec; struct ec_pnode *child = NULL; int ret; /* XXX limit max number of recursions to avoid segfault */ if (ec_node_type(node)->parse == NULL) { errno = ENOTSUP; return -1; } if (!is_root) { child = ec_pnode(node); if (child == NULL) return -1; ec_pnode_link_child(pstate, child); } else { child = pstate; } ret = ec_node_type(node)->parse(node, child, strvec); if (ret < 0) goto fail; if (ret == EC_PARSE_NOMATCH) { if (!is_root) { ec_pnode_unlink_child(child); ec_pnode_free(child); } return ret; } match_strvec = ec_strvec_ndup(strvec, 0, ret); if (match_strvec == NULL) goto fail; child->strvec = match_strvec; return ret; fail: if (!is_root) { ec_pnode_unlink_child(child); ec_pnode_free(child); } return -1; } int ec_parse_child( const struct ec_node *node, struct ec_pnode *pstate, const struct ec_strvec *strvec ) { assert(pstate != NULL); return __ec_parse_child(node, pstate, false, strvec); } struct ec_pnode *ec_parse_strvec(const struct ec_node *node, const struct ec_strvec *strvec) { struct ec_pnode *pnode = ec_pnode(node); int ret; if (pnode == NULL) return NULL; ret = __ec_parse_child(node, pnode, true, strvec); if (ret < 0) { ec_pnode_free(pnode); return NULL; } return pnode; } struct ec_pnode *ec_parse(const struct ec_node *node, const char *str) { struct ec_strvec *strvec = NULL; struct ec_pnode *pnode = NULL; errno = ENOMEM; strvec = ec_strvec(); if (strvec == NULL) goto fail; if (ec_strvec_add(strvec, str) < 0) goto fail; pnode = ec_parse_strvec(node, strvec); if (pnode == NULL) goto fail; ec_strvec_free(strvec); return pnode; fail: ec_strvec_free(strvec); ec_pnode_free(pnode); return NULL; } struct ec_pnode *ec_pnode(const struct ec_node *node) { struct ec_pnode *pnode = NULL; pnode = calloc(1, sizeof(*pnode)); if (pnode == NULL) goto fail; TAILQ_INIT(&pnode->children); pnode->node = node; pnode->attrs = ec_dict(); if (pnode->attrs == NULL) goto fail; return pnode; fail: if (pnode != NULL) ec_dict_free(pnode->attrs); free(pnode); return NULL; } static struct ec_pnode * __ec_pnode_dup(const struct ec_pnode *root, const struct ec_pnode *ref, struct ec_pnode **new_ref) { struct ec_pnode *dup = NULL; struct ec_pnode *child, *dup_child; struct ec_dict *attrs = NULL; if (root == NULL) return NULL; dup = ec_pnode(root->node); if (dup == NULL) return NULL; if (root == ref) *new_ref = dup; attrs = ec_dict_dup(root->attrs); if (attrs == NULL) goto fail; ec_dict_free(dup->attrs); dup->attrs = attrs; if (root->strvec != NULL) { dup->strvec = ec_strvec_dup(root->strvec); if (dup->strvec == NULL) goto fail; } TAILQ_FOREACH (child, &root->children, next) { dup_child = __ec_pnode_dup(child, ref, new_ref); if (dup_child == NULL) goto fail; ec_pnode_link_child(dup, dup_child); } return dup; fail: ec_pnode_free(dup); return NULL; } struct ec_pnode *ec_pnode_dup(const struct ec_pnode *pnode) { const struct ec_pnode *root; struct ec_pnode *dup_root, *dup = NULL; root = EC_PNODE_GET_ROOT(pnode); dup_root = __ec_pnode_dup(root, pnode, &dup); if (dup_root == NULL) return NULL; assert(dup != NULL); return dup; } void ec_pnode_free_children(struct ec_pnode *pnode) { struct ec_pnode *child; if (pnode == NULL) return; while (!TAILQ_EMPTY(&pnode->children)) { child = TAILQ_FIRST(&pnode->children); TAILQ_REMOVE(&pnode->children, child, next); child->parent = NULL; ec_pnode_free(child); } } void ec_pnode_free(struct ec_pnode *pnode) { if (pnode == NULL) return; ec_assert_print(pnode->parent == NULL, "parent not NULL in ec_pnode_free()"); ec_pnode_free_children(pnode); ec_strvec_free(pnode->strvec); ec_dict_free(pnode->attrs); free(pnode); } static void __ec_pnode_dump(FILE *out, const struct ec_pnode *pnode, size_t indent) { struct ec_pnode *child; const struct ec_strvec *vec; const char *id = "none", *typename = "none"; char *desc = NULL; /* node can be null when parsing is incomplete */ if (pnode->node != NULL) { id = ec_node_id(pnode->node); typename = ec_node_type(pnode->node)->name; desc = ec_node_desc(pnode->node); } fprintf(out, "%*s" "%s type=%s id=%s vec=", (int)indent * 4, "", desc ?: "none", typename, id); vec = ec_pnode_get_strvec(pnode); ec_strvec_dump(out, vec); TAILQ_FOREACH (child, &pnode->children, next) __ec_pnode_dump(out, child, indent + 1); free(desc); } /* XXX dump in other formats? yaml? json? */ void ec_pnode_dump(FILE *out, const struct ec_pnode *pnode) { fprintf(out, "------------------- parse dump:\n"); if (pnode == NULL) { fprintf(out, "pnode is NULL\n"); return; } /* Do not dump if it does not match (strvec == NULL) and if it * does not have children. Note that an incomplete parsing tree, * like those generated by complete(), don't match but have * children that may match, and we want to dump them. */ if (!ec_pnode_matches(pnode) && TAILQ_EMPTY(&pnode->children)) { fprintf(out, "no match\n"); return; } __ec_pnode_dump(out, pnode, 0); } void ec_pnode_link_child(struct ec_pnode *pnode, struct ec_pnode *child) { TAILQ_INSERT_TAIL(&pnode->children, child, next); child->parent = pnode; } void ec_pnode_unlink_child(struct ec_pnode *child) { struct ec_pnode *parent = child->parent; if (parent != NULL) { TAILQ_REMOVE(&parent->children, child, next); child->parent = NULL; } } struct ec_pnode *ec_pnode_get_first_child(const struct ec_pnode *pnode) { return TAILQ_FIRST(&pnode->children); } struct ec_pnode *ec_pnode_get_last_child(const struct ec_pnode *pnode) { return TAILQ_LAST(&pnode->children, ec_pnode_list); } struct ec_pnode *ec_pnode_next(const struct ec_pnode *pnode) { return TAILQ_NEXT(pnode, next); } const struct ec_node *ec_pnode_get_node(const struct ec_pnode *pnode) { if (pnode == NULL) return NULL; return pnode->node; } void ec_pnode_del_last_child(struct ec_pnode *pnode) { struct ec_pnode *child; child = ec_pnode_get_last_child(pnode); if (child != NULL) { ec_pnode_unlink_child(child); ec_pnode_free(child); } } struct ec_pnode *ec_pnode_get_root(struct ec_pnode *pnode) { if (pnode == NULL) return NULL; while (pnode->parent != NULL) pnode = pnode->parent; return pnode; } struct ec_pnode *ec_pnode_get_parent(const struct ec_pnode *pnode) { if (pnode == NULL) return NULL; return pnode->parent; } struct ec_pnode * __ec_pnode_iter_next(const struct ec_pnode *root, struct ec_pnode *pnode, bool iter_children) { struct ec_pnode *child, *parent, *next; if (iter_children) { child = TAILQ_FIRST(&pnode->children); if (child != NULL) return child; } parent = pnode->parent; while (parent != NULL && pnode != root) { next = TAILQ_NEXT(pnode, next); if (next != NULL) return next; pnode = parent; parent = pnode->parent; } return NULL; } const struct ec_pnode *ec_pnode_find_next( const struct ec_pnode *root, const struct ec_pnode *prev, const char *id, bool iter_children ) { const struct ec_pnode *iter; if (root == NULL) return NULL; if (prev == NULL) prev = root; else prev = EC_PNODE_ITER_NEXT(root, prev, iter_children); for (iter = prev; iter != NULL; iter = EC_PNODE_ITER_NEXT(root, iter, 1)) { if (iter->node != NULL && !strcmp(ec_node_id(iter->node), id)) return iter; } return NULL; } const struct ec_pnode *ec_pnode_find(const struct ec_pnode *root, const char *id) { return ec_pnode_find_next(root, NULL, id, 1); } unsigned int ec_pnode_count(const struct ec_pnode *root, const char *id) { const struct ec_pnode *iter; unsigned int count = 0; if (root == NULL) return 0; for (iter = root; iter != NULL; iter = EC_PNODE_ITER_NEXT(root, iter, 1)) { if (iter->node != NULL && !strcmp(ec_node_id(iter->node), id)) count++; } return count; } struct ec_dict *ec_pnode_get_attrs(const struct ec_pnode *pnode) { if (pnode == NULL) return NULL; return pnode->attrs; } const struct ec_strvec *ec_pnode_get_strvec(const struct ec_pnode *pnode) { if (pnode == NULL) return NULL; return pnode->strvec; } /* number of strings in the parsed string vector */ size_t ec_pnode_len(const struct ec_pnode *pnode) { if (pnode == NULL || pnode->strvec == NULL) return 0; return ec_strvec_len(pnode->strvec); } bool ec_pnode_matches(const struct ec_pnode *pnode) { if (pnode == NULL) return false; if (pnode->strvec == NULL) return false; return true; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/string.c������������������������������������������������������������������������0000664�0000000�0000000�00000012510�15203622040�0015613�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <ctype.h> #include <errno.h> #include <limits.h> #include <stdarg.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ecoli/assert.h> #include <ecoli/string.h> /* count the number of identical chars at the beginning of 2 strings */ size_t ec_strcmp_count(const char *s1, const char *s2) { size_t i = 0; while (s1[i] && s2[i] && s1[i] == s2[i]) i++; return i; } int ec_str_startswith(const char *s, const char *beginning) { size_t len; len = ec_strcmp_count(s, beginning); if (beginning[len] == '\0') return 1; return 0; } bool ec_str_is_space(const char *s) { while (*s) { if (!isspace(*s)) return false; s++; } return true; } int ec_str_parse_llint(const char *str, unsigned int base, int64_t min, int64_t max, int64_t *val) { char *endptr; int save_errno = errno; errno = 0; *val = strtoll(str, &endptr, base); if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) || (errno != 0 && *val == 0)) return -1; if (*val < min) { errno = ERANGE; return -1; } if (*val > max) { errno = ERANGE; return -1; } if (*endptr != 0) { errno = EINVAL; return -1; } errno = save_errno; return 0; } int ec_str_parse_ullint( const char *str, unsigned int base, uint64_t min, uint64_t max, uint64_t *val ) { char *endptr; int save_errno = errno; /* since a negative input is silently converted to a positive * one by strtoull(), first check that it is positive */ if (strchr(str, '-')) return -1; errno = 0; *val = strtoull(str, &endptr, base); if ((errno == ERANGE && *val == ULLONG_MAX) || (errno != 0 && *val == 0)) return -1; if (*val < min) return -1; if (*val > max) return -1; if (*endptr != 0) return -1; errno = save_errno; return 0; } char *ec_str_quote(const char *str, char quote, bool force) { size_t len = 3; /* start/end quotes + \0 */ const char *s; char *out, *o; char c; if (!force) { for (s = str; *s != '\0'; s++) { if (isspace(*s) || !isprint(*s) || *s == '"' || *s == '\'' || *s == quote) break; } if (*s == '\0') return strdup(str); } if (quote == 0) { if (strchr(str, '"') == NULL) quote = '"'; else quote = '\''; } for (s = str, c = *s; c != '\0'; s++, c = *s) { if (c == quote || c == '\\') len += 2; else if (!isprint(c) && c != '\n') len += 4; else len += 1; } out = malloc(len); if (out == NULL) return NULL; o = out; *o++ = quote; for (s = str, c = *s; c != '\0'; s++, c = *s) { if (c == quote) { *o++ = '\\'; *o++ = quote; } else if (c == '\\') { *o++ = '\\'; *o++ = '\\'; } else if (!isprint(c) && c != '\n') { char buf[5]; snprintf(buf, sizeof(buf), "\\x%2.2x", (unsigned char)c); memcpy(o, buf, 4); o += 4; } else { *o++ = c; } } *o++ = quote; *o = '\0'; return out; } struct wrap_state { size_t line_no; /* current line number */ size_t line_length; /* current line length */ size_t start_off; /* start offset for first line (implies padding for other lines) */ size_t max_cols; /* wrap text at max_cols */ bool new_para; /* true if a new paragraph is needed */ char *output; /* output buffer */ size_t len; /* current buffer length */ size_t size; /* current buffer size */ }; static int append_token(struct wrap_state *state, const char *token, size_t token_len) { size_t written = state->line_length - state->start_off; int ret; /* allocate a larger buffer if needed (the "5" below is a margin above the worst case) */ if (state->output == NULL || state->size - state->len < token_len + state->start_off + 5) { size_t new_size, need; char *tmp; need = state->len + token_len + state->start_off + 5; new_size = state->size == 0 ? 256 : state->size * 2; while (new_size < need) new_size *= 2; tmp = malloc(new_size); if (tmp == NULL) return -1; memcpy(tmp, state->output, state->len); free(state->output); state->output = tmp; state->size = new_size; } /* add new line */ if (state->line_length + token_len + 1 > state->max_cols && written > 0) { ret = sprintf( &state->output[state->len], "\n%s%*s", state->new_para ? "\n" : "", (int)state->start_off, "" ); if (ret < 0) return -1; state->len += ret; state->line_length = state->start_off; state->line_no++; state->new_para = false; written = 0; } /* add token */ ret = sprintf( &state->output[state->len], "%s%.*s", written > 0 ? " " : "", (int)token_len, token ); if (ret < 0) return -1; state->len += ret; state->line_length += token_len + !!(written > 0); return 0; } char *ec_str_wrap(const char *str, size_t max_cols, size_t start_off) { struct wrap_state state = { .line_no = 0, .line_length = start_off, .start_off = start_off, .max_cols = max_cols, .new_para = false, .output = NULL, }; const char *start; size_t len; size_t cr; while (*str != '\0') { cr = 0; while (isspace(*str)) { if (*str == '\n') cr++; str++; } if (state.line_no != 0 && cr >= 2) state.new_para = true; start = str; while (!isspace(*str) && *str != '\0') str++; len = str - start; if (len > 0 && append_token(&state, start, len) < 0) goto fail; } if (state.output == NULL) return strdup(""); return state.output; fail: free(state.output); return NULL; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/strvec.c������������������������������������������������������������������������0000664�0000000�0000000�00000024230�15203622040�0015615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <ctype.h> #include <errno.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <ecoli/dict.h> #include <ecoli/log.h> #include <ecoli/node.h> #include <ecoli/string.h> #include <ecoli/strvec.h> EC_LOG_TYPE_REGISTER(strvec); struct ec_strvec_elt { unsigned int refcnt; char *str; struct ec_dict *attrs; }; struct ec_strvec { size_t len; struct ec_strvec_elt **vec; }; struct ec_strvec *ec_strvec(void) { struct ec_strvec *strvec; strvec = calloc(1, sizeof(*strvec)); if (strvec == NULL) return NULL; return strvec; } static struct ec_strvec_elt *__ec_strvec_elt(const char *s) { struct ec_strvec_elt *elt; elt = calloc(1, sizeof(*elt)); if (elt == NULL) return NULL; elt->str = strdup(s); if (elt->str == NULL) { free(elt); return NULL; } elt->refcnt = 1; return elt; } static void __ec_strvec_elt_free(struct ec_strvec_elt *elt) { elt->refcnt--; if (elt->refcnt == 0) { free(elt->str); ec_dict_free(elt->attrs); free(elt); } } int ec_strvec_set(struct ec_strvec *strvec, size_t idx, const char *s) { struct ec_strvec_elt *elt; if (strvec == NULL || s == NULL || idx >= strvec->len) { errno = EINVAL; return -1; } elt = __ec_strvec_elt(s); if (elt == NULL) return -1; __ec_strvec_elt_free(strvec->vec[idx]); strvec->vec[idx] = elt; return 0; } int ec_strvec_add(struct ec_strvec *strvec, const char *s) { struct ec_strvec_elt *elt, **new_vec; if (strvec == NULL || s == NULL) { errno = EINVAL; return -1; } new_vec = realloc(strvec->vec, sizeof(*strvec->vec) * (strvec->len + 1)); if (new_vec == NULL) return -1; strvec->vec = new_vec; elt = __ec_strvec_elt(s); if (elt == NULL) return -1; new_vec[strvec->len] = elt; strvec->len++; return 0; } struct ec_strvec *ec_strvec_from_array(const char *const *strarr, size_t n) { struct ec_strvec *strvec = NULL; size_t i; strvec = ec_strvec(); if (strvec == NULL) goto fail; for (i = 0; i < n; i++) { if (ec_strvec_add(strvec, strarr[i]) < 0) goto fail; } return strvec; fail: ec_strvec_free(strvec); return NULL; } int ec_strvec_del_last(struct ec_strvec *strvec) { if (strvec->len == 0) { errno = EINVAL; return -1; } __ec_strvec_elt_free(strvec->vec[strvec->len - 1]); strvec->len--; return 0; } struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off, size_t len) { struct ec_strvec *copy = NULL; size_t i, veclen; veclen = ec_strvec_len(strvec); if (off + len > veclen) return NULL; copy = ec_strvec(); if (copy == NULL) goto fail; if (len == 0) return copy; copy->vec = calloc(len, sizeof(*copy->vec)); if (copy->vec == NULL) goto fail; for (i = 0; i < len; i++) { copy->vec[i] = strvec->vec[i + off]; copy->vec[i]->refcnt++; } copy->len = len; return copy; fail: ec_strvec_free(copy); return NULL; } struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec) { return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec)); } void ec_strvec_free(struct ec_strvec *strvec) { struct ec_strvec_elt *elt; size_t i; if (strvec == NULL) return; for (i = 0; i < ec_strvec_len(strvec); i++) { elt = strvec->vec[i]; __ec_strvec_elt_free(elt); } free(strvec->vec); free(strvec); } size_t ec_strvec_len(const struct ec_strvec *strvec) { return strvec->len; } const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) { if (strvec == NULL || idx >= strvec->len) return NULL; return strvec->vec[idx]->str; } const struct ec_dict *ec_strvec_get_attrs(const struct ec_strvec *strvec, size_t idx) { if (strvec == NULL || idx >= strvec->len) { errno = EINVAL; return NULL; } return strvec->vec[idx]->attrs; } int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx, struct ec_dict *attrs) { struct ec_strvec_elt *elt; if (strvec == NULL || idx >= strvec->len) { errno = EINVAL; goto fail; } elt = strvec->vec[idx]; if (elt->refcnt > 1) { if (ec_strvec_set(strvec, idx, elt->str) < 0) goto fail; elt = strvec->vec[idx]; } if (elt->attrs != NULL) ec_dict_free(elt->attrs); elt->attrs = attrs; return 0; fail: ec_dict_free(attrs); return -1; } int ec_strvec_cmp(const struct ec_strvec *strvec1, const struct ec_strvec *strvec2) { size_t i; if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2)) return -1; for (i = 0; i < ec_strvec_len(strvec1); i++) { if (strcmp(ec_strvec_val(strvec1, i), ec_strvec_val(strvec2, i))) return -1; } return 0; } typedef enum { SPACE, SINGLE_QUOTE, DOUBLE_QUOTE, BACKSLASH, POUND, OTHER, } char_class_t; typedef enum { START, IN_WORD, ESCAPING, ESCAPING_QUOTED, IN_DOUBLE_QUOTES, IN_SINGLE_QUOTES, IN_COMMENT, } lexer_state_t; static char_class_t get_char_class(char c) { switch (c) { case '\'': return SINGLE_QUOTE; case '"': return DOUBLE_QUOTE; case '\\': return BACKSLASH; case '#': return POUND; default: if (isspace(c)) return SPACE; } return OTHER; } static int sh_lex_set_attrs(struct ec_strvec *strvec, size_t idx, size_t arg_start, size_t arg_end) { struct ec_dict *attrs = ec_dict(); if (attrs == NULL) return -1; if (ec_dict_set(attrs, EC_STRVEC_ATTR_START, (void *)(uintptr_t)arg_start, NULL) < 0) goto fail; if (ec_dict_set(attrs, EC_STRVEC_ATTR_END, (void *)(uintptr_t)arg_end, NULL) < 0) goto fail; return ec_strvec_set_attrs(strvec, idx, attrs); fail: ec_dict_free(attrs); return -1; } struct ec_strvec *ec_strvec_sh_lex_str(const char *str, ec_strvec_flag_t flags, char *missing_quote) { struct ec_strvec *strvec = NULL; lexer_state_t state = START; /* Weird, but we need an empty string to report as having trailing * space when EC_STRVEC_SHLEX_KEEP_TRAILING_SPACE is set in flags. */ bool trailing_space = true; size_t t, i, arg_start; char token[BUFSIZ]; char c, quote; #define append(buffer, position, character) \ do { \ if (position + 1 >= sizeof(buffer)) { \ errno = ENOBUFS; \ goto fail; \ } \ buffer[position++] = character; \ buffer[position] = '\0'; /* make sure token is always terminated */ \ } while (0) if (str == NULL) { errno = EINVAL; goto fail; } strvec = ec_strvec(); if (strvec == NULL) goto fail; t = 0; quote = '\0'; arg_start = 0; for (i = 0; i < strlen(str); i++) { c = str[i]; char_class_t cls = get_char_class(c); switch (state) { case START: switch (cls) { case SPACE: break; case POUND: state = IN_COMMENT; break; case DOUBLE_QUOTE: state = IN_DOUBLE_QUOTES; quote = c; break; case SINGLE_QUOTE: state = IN_SINGLE_QUOTES; quote = c; break; case BACKSLASH: state = ESCAPING; break; default: /* start a new token */ state = IN_WORD; append(token, t, c); break; } trailing_space = cls == SPACE; arg_start = i; break; case IN_WORD: switch (cls) { case SPACE: /* end of token */ quote = '\0'; if (ec_strvec_add(strvec, token) < 0) goto fail; if (sh_lex_set_attrs( strvec, ec_strvec_len(strvec) - 1, arg_start, i ) < 0) goto fail; state = START; trailing_space = true; arg_start = i; t = 0; break; case DOUBLE_QUOTE: state = IN_DOUBLE_QUOTES; break; case SINGLE_QUOTE: state = IN_SINGLE_QUOTES; break; case BACKSLASH: state = ESCAPING; break; default: append(token, t, c); break; } break; case ESCAPING: state = IN_WORD; append(token, t, c); break; case ESCAPING_QUOTED: state = IN_DOUBLE_QUOTES; append(token, t, c); break; case IN_DOUBLE_QUOTES: switch (cls) { case DOUBLE_QUOTE: state = IN_WORD; break; case BACKSLASH: state = ESCAPING_QUOTED; break; default: append(token, t, c); break; } break; case IN_SINGLE_QUOTES: switch (cls) { case SINGLE_QUOTE: state = IN_WORD; break; default: append(token, t, c); break; } break; case IN_COMMENT: if (c == '\n' || c == '\r') state = START; break; } } #undef append switch (state) { case START: /* fallthrough */ case IN_WORD: /* fallthrough */ case IN_COMMENT: break; default: if (missing_quote != NULL) { *missing_quote = quote; } if (flags & EC_STRVEC_STRICT) { errno = EBADMSG; goto fail; } state = IN_WORD; } if (state == IN_WORD && t > 0) { if (ec_strvec_add(strvec, token) < 0) goto fail; if (sh_lex_set_attrs(strvec, ec_strvec_len(strvec) - 1, arg_start, i) < 0) goto fail; } else if (trailing_space && (flags & EC_STRVEC_TRAILSP)) { if (ec_strvec_add(strvec, "") < 0) goto fail; if (sh_lex_set_attrs(strvec, ec_strvec_len(strvec) - 1, arg_start + 1, i + 1) < 0) goto fail; } return strvec; fail: ec_strvec_free(strvec); return NULL; } static int cmp_vec_elt(const void *p1, const void *p2, void *arg) { int (*str_cmp)(const char *s1, const char *s2) = arg; const struct ec_strvec_elt *const *e1 = p1, *const *e2 = p2; return str_cmp((*e1)->str, (*e2)->str); } void ec_strvec_sort(struct ec_strvec *strvec, int (*str_cmp)(const char *s1, const char *s2)) { if (str_cmp == NULL) str_cmp = strcmp; qsort_r(strvec->vec, ec_strvec_len(strvec), sizeof(*strvec->vec), cmp_vec_elt, str_cmp); } void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec) { size_t i; if (strvec == NULL) { fprintf(out, "none\n"); return; } fprintf(out, "strvec (len=%zu) [", strvec->len); for (i = 0; i < ec_strvec_len(strvec); i++) { char *elt = ec_str_quote(strvec->vec[i]->str, 0, true); const char *comma; if (i == 0) comma = ""; else comma = ", "; if (elt != NULL) fprintf(out, "%s%s", comma, elt); else fprintf(out, "%s\"%s\"", comma, strvec->vec[i]->str); free(elt); } fprintf(out, "]\n"); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/vec.c���������������������������������������������������������������������������0000664�0000000�0000000�00000006435�15203622040�0015073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <ecoli/assert.h> #include <ecoli/log.h> #include <ecoli/vec.h> EC_LOG_TYPE_REGISTER(vec); struct ec_vec { size_t len; size_t size; size_t elt_size; ec_vec_elt_copy_t copy; ec_vec_elt_free_t free; void *vec; }; static void *get_obj(const struct ec_vec *vec, size_t idx) { assert(vec->elt_size != 0); return (char *)vec->vec + (idx * vec->elt_size); } struct ec_vec * ec_vec(size_t elt_size, size_t size, ec_vec_elt_copy_t elt_copy, ec_vec_elt_free_t elt_free) { struct ec_vec *vec; if (elt_size == 0) { errno = EINVAL; return NULL; } vec = calloc(1, sizeof(*vec)); if (vec == NULL) return NULL; vec->elt_size = elt_size; vec->copy = elt_copy; vec->free = elt_free; if (size == 0) return vec; vec->vec = calloc(size, vec->elt_size); if (vec->vec == NULL) { free(vec); return NULL; } return vec; } int ec_vec_add_by_ref(struct ec_vec *vec, void *ptr) { void *new_vec; if (vec->len + 1 > vec->size) { new_vec = realloc(vec->vec, vec->elt_size * (vec->len + 1)); if (new_vec == NULL) return -1; vec->size = vec->len + 1; vec->vec = new_vec; } memcpy(get_obj(vec, vec->len), ptr, vec->elt_size); vec->len++; return 0; } int ec_vec_add_ptr(struct ec_vec *vec, void *elt) { EC_CHECK_ARG(vec->elt_size == sizeof(elt), -1, EINVAL); return ec_vec_add_by_ref(vec, &elt); } int ec_vec_add_u8(struct ec_vec *vec, uint8_t elt) { EC_CHECK_ARG(vec->elt_size == sizeof(elt), -1, EINVAL); return ec_vec_add_by_ref(vec, &elt); } int ec_vec_add_u16(struct ec_vec *vec, uint16_t elt) { EC_CHECK_ARG(vec->elt_size == sizeof(elt), -1, EINVAL); return ec_vec_add_by_ref(vec, &elt); } int ec_vec_add_u32(struct ec_vec *vec, uint32_t elt) { EC_CHECK_ARG(vec->elt_size == sizeof(elt), -1, EINVAL); return ec_vec_add_by_ref(vec, &elt); } int ec_vec_add_u64(struct ec_vec *vec, uint64_t elt) { EC_CHECK_ARG(vec->elt_size == sizeof(elt), -1, EINVAL); return ec_vec_add_by_ref(vec, &elt); } struct ec_vec *ec_vec_ndup(const struct ec_vec *vec, size_t off, size_t len) { struct ec_vec *copy = NULL; size_t i, veclen; veclen = ec_vec_len(vec); if (off + len > veclen) return NULL; copy = ec_vec(vec->elt_size, len, vec->copy, vec->free); if (copy == NULL) goto fail; if (len == 0) return copy; for (i = 0; i < len; i++) { if (vec->copy) vec->copy(get_obj(copy, i), get_obj(vec, i + off)); else memcpy(get_obj(copy, i), get_obj(vec, i + off), vec->elt_size); } copy->len = len; return copy; fail: ec_vec_free(copy); return NULL; } size_t ec_vec_len(const struct ec_vec *vec) { if (vec == NULL) return 0; return vec->len; } struct ec_vec *ec_vec_dup(const struct ec_vec *vec) { return ec_vec_ndup(vec, 0, ec_vec_len(vec)); } void ec_vec_free(struct ec_vec *vec) { size_t i; if (vec == NULL) return; for (i = 0; i < ec_vec_len(vec); i++) { if (vec->free) vec->free(get_obj(vec, i)); } free(vec->vec); free(vec); } int ec_vec_get(void *ptr, const struct ec_vec *vec, size_t idx) { if (vec == NULL || idx >= vec->len) { errno = EINVAL; return -1; } memcpy(ptr, get_obj(vec, idx), vec->elt_size); return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/src/yaml.c��������������������������������������������������������������������������0000664�0000000�0000000�00000043723�15203622040�0015261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <yaml.h> #include <ecoli/config.h> #include <ecoli/dict.h> #include <ecoli/interact.h> #include <ecoli/node.h> #include <ecoli/string.h> #include <ecoli/yaml.h> /* associate a yaml node to a ecoli node */ struct pair { const yaml_node_t *ynode; struct ec_node *enode; }; /* store the associations yaml_node <-> ec_node */ struct enode_table { struct pair *pair; size_t len; }; static struct ec_node * parse_ec_node(struct enode_table *table, const yaml_document_t *document, const yaml_node_t *ynode); static struct ec_config *parse_ec_config_list( struct enode_table *table, const struct ec_config_schema *schema, const yaml_document_t *document, const yaml_node_t *ynode ); static struct ec_config *parse_ec_config_dict( struct enode_table *table, const struct ec_config_schema *schema, const yaml_document_t *document, const yaml_node_t *ynode ); /* XXX to utils.c ? */ static int parse_llint(const char *str, int64_t *val) { char *endptr; int save_errno = errno; if (str == NULL) { errno = EINVAL; return -1; } errno = 0; *val = strtoll(str, &endptr, 0); if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) || (errno != 0 && *val == 0)) return -1; if (*endptr != 0) { errno = EINVAL; return -1; } errno = save_errno; return 0; } static int parse_ullint(const char *str, uint64_t *val) { char *endptr; int save_errno = errno; if (str == NULL) { errno = EINVAL; return -1; } /* since a negative input is silently converted to a positive * one by strtoull(), first check that it is positive */ if (strchr(str, '-')) return -1; errno = 0; *val = strtoull(str, &endptr, 0); if ((errno == ERANGE && *val == ULLONG_MAX) || (errno != 0 && *val == 0)) return -1; if (*endptr != 0) return -1; errno = save_errno; return 0; } static int parse_bool(const char *str, bool *val) { if (str == NULL) { errno = EINVAL; return -1; } if (!strcasecmp(str, "true")) { *val = true; return 0; } else if (!strcasecmp(str, "false")) { *val = false; return 0; } errno = EINVAL; return -1; } static int add_in_table(struct enode_table *table, const yaml_node_t *ynode, struct ec_node *enode) { struct pair *pair = NULL; pair = realloc(table->pair, (table->len + 1) * sizeof(*pair)); if (pair == NULL) return -1; ec_node_clone(enode); pair[table->len].ynode = ynode; pair[table->len].enode = enode; table->pair = pair; table->len++; return 0; } static void free_table(struct enode_table *table) { size_t i; for (i = 0; i < table->len; i++) ec_node_free(table->pair[i].enode); free(table->pair); } static struct ec_config *parse_ec_config( struct enode_table *table, const struct ec_config_schema *schema_elt, const yaml_document_t *document, const yaml_node_t *ynode ) { const struct ec_config_schema *subschema; struct ec_config *config = NULL; struct ec_node *enode = NULL; enum ec_config_type type; const char *value_str; uint64_t u64; int64_t i64; bool boolean; type = ec_config_schema_type(schema_elt); switch (type) { case EC_CONFIG_TYPE_BOOL: if (ynode->type != YAML_SCALAR_NODE) { fprintf(stderr, "Boolean should be scalar\n"); goto fail; } value_str = (const char *)ynode->data.scalar.value; if (parse_bool(value_str, &boolean) < 0) { fprintf(stderr, "Failed to parse boolean\n"); goto fail; } config = ec_config_bool(boolean); if (config == NULL) { fprintf(stderr, "Failed to create config\n"); goto fail; } break; case EC_CONFIG_TYPE_INT64: if (ynode->type != YAML_SCALAR_NODE) { fprintf(stderr, "Int64 should be scalar\n"); goto fail; } value_str = (const char *)ynode->data.scalar.value; if (parse_llint(value_str, &i64) < 0) { fprintf(stderr, "Failed to parse i64\n"); goto fail; } config = ec_config_i64(i64); if (config == NULL) { fprintf(stderr, "Failed to create config\n"); goto fail; } break; case EC_CONFIG_TYPE_UINT64: if (ynode->type != YAML_SCALAR_NODE) { fprintf(stderr, "Uint64 should be scalar\n"); goto fail; } value_str = (const char *)ynode->data.scalar.value; if (parse_ullint(value_str, &u64) < 0) { fprintf(stderr, "Failed to parse u64\n"); goto fail; } config = ec_config_u64(u64); if (config == NULL) { fprintf(stderr, "Failed to create config\n"); goto fail; } break; case EC_CONFIG_TYPE_STRING: if (ynode->type != YAML_SCALAR_NODE) { fprintf(stderr, "String should be scalar\n"); goto fail; } value_str = (const char *)ynode->data.scalar.value; config = ec_config_string(value_str); if (config == NULL) { fprintf(stderr, "Failed to create config\n"); goto fail; } break; case EC_CONFIG_TYPE_NODE: enode = parse_ec_node(table, document, ynode); if (enode == NULL) goto fail; config = ec_config_node(enode); enode = NULL; if (config == NULL) { fprintf(stderr, "Failed to create config\n"); goto fail; } break; case EC_CONFIG_TYPE_LIST: subschema = ec_config_schema_sub(schema_elt); if (subschema == NULL) { fprintf(stderr, "List has no subschema\n"); goto fail; } config = parse_ec_config_list(table, subschema, document, ynode); if (config == NULL) goto fail; break; case EC_CONFIG_TYPE_DICT: subschema = ec_config_schema_sub(schema_elt); if (subschema == NULL) { fprintf(stderr, "Dict has no subschema\n"); goto fail; } config = parse_ec_config_dict(table, subschema, document, ynode); if (config == NULL) goto fail; break; default: fprintf(stderr, "Invalid config type %d\n", type); goto fail; } return config; fail: ec_node_free(enode); ec_config_free(config); return NULL; } static struct ec_config *parse_ec_config_list( struct enode_table *table, const struct ec_config_schema *schema, const yaml_document_t *document, const yaml_node_t *ynode ) { struct ec_config *config = NULL, *subconfig = NULL; const yaml_node_item_t *item; const yaml_node_t *value; if (ynode->type != YAML_SEQUENCE_NODE) { fprintf(stderr, "Ecoli list config should be a yaml sequence\n"); goto fail; } config = ec_config_list(); if (config == NULL) { fprintf(stderr, "Failed to allocate config\n"); goto fail; } for (item = ynode->data.sequence.items.start; item < ynode->data.sequence.items.top; item++) { value = document->nodes.start + (*item) - 1; /* XXX -1 ? */ subconfig = parse_ec_config(table, schema, document, value); if (subconfig == NULL) goto fail; if (ec_config_list_add(config, subconfig) < 0) { fprintf(stderr, "Failed to add list entry\n"); goto fail; } } return config; fail: ec_config_free(config); return NULL; } static struct ec_config *parse_ec_config_dict( struct enode_table *table, const struct ec_config_schema *schema, const yaml_document_t *document, const yaml_node_t *ynode ) { const struct ec_config_schema *schema_elt; struct ec_config *config = NULL, *subconfig = NULL; const yaml_node_t *key, *value; const yaml_node_pair_t *pair; const char *key_str; if (ynode->type != YAML_MAPPING_NODE) { fprintf(stderr, "Ecoli config should be a yaml mapping node\n"); goto fail; } config = ec_config_dict(); if (config == NULL) { fprintf(stderr, "Failed to allocate config\n"); goto fail; } for (pair = ynode->data.mapping.pairs.start; pair < ynode->data.mapping.pairs.top; pair++) { key = document->nodes.start + pair->key - 1; /* XXX -1 ? */ value = document->nodes.start + pair->value - 1; key_str = (const char *)key->data.scalar.value; if (ec_config_key_is_reserved(key_str)) continue; schema_elt = ec_config_schema_lookup(schema, key_str); if (schema_elt == NULL) { fprintf(stderr, "No such config %s\n", key_str); goto fail; } subconfig = parse_ec_config(table, schema_elt, document, value); if (subconfig == NULL) goto fail; if (ec_config_dict_set(config, key_str, subconfig) < 0) { fprintf(stderr, "Failed to set dict entry\n"); goto fail; } } return config; fail: ec_config_free(config); return NULL; } static struct ec_node * parse_ec_node(struct enode_table *table, const yaml_document_t *document, const yaml_node_t *ynode) { const struct ec_config_schema *schema; const struct ec_node_type *type = NULL; const char *id = NULL; char *help = NULL; struct ec_config *config = NULL; const yaml_node_t *attrs = NULL; const yaml_node_t *key, *value; const yaml_node_pair_t *pair; const char *key_str, *value_str; struct ec_node *enode = NULL; char *value_dup = NULL; size_t i; if (ynode->type != YAML_MAPPING_NODE) { fprintf(stderr, "Ecoli node should be a yaml mapping node\n"); goto fail; } /* if it's an anchor, the node may be already parsed, reuse it */ for (i = 0; i < table->len; i++) { if (table->pair[i].ynode == ynode) return ec_node_clone(table->pair[i].enode); } for (pair = ynode->data.mapping.pairs.start; pair < ynode->data.mapping.pairs.top; pair++) { key = document->nodes.start + pair->key - 1; /* XXX -1 ? */ value = document->nodes.start + pair->value - 1; key_str = (const char *)key->data.scalar.value; value_str = (const char *)value->data.scalar.value; if (!strcmp(key_str, "type")) { if (type != NULL) { fprintf(stderr, "Duplicate type\n"); goto fail; } if (value->type != YAML_SCALAR_NODE) { fprintf(stderr, "Type must be a string\n"); goto fail; } type = ec_node_type_lookup(value_str); if (type == NULL) { fprintf(stderr, "Cannot find type %s\n", value_str); goto fail; } } else if (!strcmp(key_str, "attrs")) { if (attrs != NULL) { fprintf(stderr, "Duplicate attrs\n"); goto fail; } if (value->type != YAML_MAPPING_NODE) { fprintf(stderr, "Attrs must be a mapping\n"); goto fail; } attrs = value; } else if (!strcmp(key_str, "id")) { if (id != NULL) { fprintf(stderr, "Duplicate id\n"); goto fail; } if (value->type != YAML_SCALAR_NODE) { fprintf(stderr, "Id must be a scalar\n"); goto fail; } id = value_str; } else if (!strcmp(key_str, "help")) { if (help != NULL) { fprintf(stderr, "Duplicate help\n"); goto fail; } if (value->type != YAML_SCALAR_NODE) { fprintf(stderr, "Help must be a scalar\n"); goto fail; } help = strdup(value_str); if (help == NULL) { fprintf(stderr, "Failed to allocate help\n"); goto fail; } } } /* create the ecoli node */ if (id == NULL) id = EC_NO_ID; enode = ec_node_from_type(type, id); if (enode == NULL) { fprintf(stderr, "Cannot create ecoli node\n"); goto fail; } if (add_in_table(table, ynode, enode) < 0) { fprintf(stderr, "Cannot add node in table\n"); goto fail; } /* create its config */ schema = ec_node_type_schema(type); if (schema == NULL) { fprintf(stderr, "No configuration schema for type %s\n", ec_node_type_name(type)); goto fail; } config = parse_ec_config_dict(table, schema, document, ynode); if (config == NULL) goto fail; if (ec_node_set_config(enode, config) < 0) { config = NULL; /* freed */ fprintf(stderr, "Failed to set config\n"); goto fail; } config = NULL; /* freed */ if (help != NULL) { if (ec_dict_set(ec_node_attrs(enode), EC_INTERACT_HELP_ATTR, help, free) < 0) { fprintf(stderr, "Failed to set help\n"); help = NULL; goto fail; } help = NULL; } /* add attributes (all as string) */ if (attrs != NULL) { for (pair = attrs->data.mapping.pairs.start; pair < attrs->data.mapping.pairs.top; pair++) { key = document->nodes.start + pair->key - 1; value = document->nodes.start + pair->value - 1; key_str = (const char *)key->data.scalar.value; value_str = (const char *)value->data.scalar.value; value_dup = strdup(value_str); if (value_dup == NULL) goto fail; if (ec_dict_set(ec_node_attrs(enode), key_str, value_dup, free) < 0) { value_dup = NULL; goto fail; } value_dup = NULL; } } return enode; fail: ec_node_free(enode); ec_config_free(config); free(help); free(value_dup); return NULL; } static struct ec_node *parse_document(struct enode_table *table, const yaml_document_t *document) { yaml_node_t *node; node = document->nodes.start; return parse_ec_node(table, document, node); } struct ec_node *ec_yaml_import(const char *filename) { FILE *file; yaml_parser_t parser; yaml_document_t document; struct ec_node *root = NULL; struct enode_table table; memset(&table, 0, sizeof(table)); file = fopen(filename, "rb"); if (file == NULL) { fprintf(stderr, "Failed to open file %s\n", filename); goto fail_no_parser; } if (yaml_parser_initialize(&parser) == 0) { fprintf(stderr, "Failed to initialize yaml parser\n"); goto fail_no_parser; } yaml_parser_set_input_file(&parser, file); if (yaml_parser_load(&parser, &document) == 0) { fprintf(stderr, "Failed to load yaml document\n"); goto fail_no_doc; } if (yaml_document_get_root_node(&document) == NULL) { fprintf(stderr, "Incomplete document\n"); /* XXX check err */ goto fail; } root = parse_document(&table, &document); if (root == NULL) { fprintf(stderr, "Failed to parse document\n"); goto fail; } yaml_document_delete(&document); yaml_parser_delete(&parser); fclose(file); free_table(&table); return root; fail: yaml_document_delete(&document); fail_no_doc: yaml_parser_delete(&parser); fail_no_parser: if (file != NULL) fclose(file); free_table(&table); ec_node_free(root); return NULL; } /* export functions */ static void export_indent(FILE *out, int indent) { int i; for (i = 0; i < indent; i++) fprintf(out, " "); } static int export_ec_config( FILE *out, const struct ec_config *config, const struct ec_config_schema *schema, int indent ); static int export_ec_config_list( FILE *out, const struct ec_config *config, const struct ec_config_schema *schema, int indent ) { const struct ec_config_schema *subschema; struct ec_config *item; subschema = ec_config_schema_sub(schema); if (subschema == NULL) return -1; for (item = ec_config_list_first((struct ec_config *)config); item != NULL; item = ec_config_list_next((struct ec_config *)config, item)) { export_indent(out, indent); fprintf(out, "- "); if (export_ec_config(out, item, subschema, indent + 1) < 0) return -1; } return 0; } static int export_ec_config_dict( FILE *out, const struct ec_config *config, const struct ec_config_schema *schema, int indent ) { const struct ec_config_schema *schema_elt; struct ec_dict_elt_ref *iter; const char *key; struct ec_config *value; for (iter = ec_dict_iter(config->dict); iter != NULL; iter = ec_dict_iter_next(iter)) { key = ec_dict_iter_get_key(iter); value = ec_dict_iter_get_val(iter); if (key == NULL || value == NULL) continue; schema_elt = ec_config_schema_lookup(schema, key); if (schema_elt == NULL) continue; export_indent(out, indent); fprintf(out, "%s: ", key); if (export_ec_config(out, value, schema_elt, indent + 1) < 0) return -1; } return 0; } static int export_ec_node(FILE *out, const struct ec_node *node, int indent); static int export_ec_config( FILE *out, const struct ec_config *config, const struct ec_config_schema *schema, int indent ) { enum ec_config_type type; if (config == NULL || schema == NULL) return 0; type = ec_config_get_type(config); switch (type) { case EC_CONFIG_TYPE_BOOL: fprintf(out, "%s\n", config->boolean ? "true" : "false"); break; case EC_CONFIG_TYPE_INT64: fprintf(out, "%ld\n", (long)config->i64); break; case EC_CONFIG_TYPE_UINT64: fprintf(out, "%lu\n", (unsigned long)config->u64); break; case EC_CONFIG_TYPE_STRING: { char *quoted = ec_str_quote(config->string ? config->string : "", '"', true); if (quoted == NULL) return -1; fprintf(out, "%s\n", quoted); free(quoted); break; } case EC_CONFIG_TYPE_NODE: fprintf(out, "\n"); if (export_ec_node(out, config->node, indent) < 0) return -1; break; case EC_CONFIG_TYPE_LIST: fprintf(out, "\n"); if (export_ec_config_list(out, config, schema, indent) < 0) return -1; break; case EC_CONFIG_TYPE_DICT: fprintf(out, "\n"); if (export_ec_config_dict(out, config, schema, indent) < 0) return -1; break; default: return -1; } return 0; } static int export_ec_node(FILE *out, const struct ec_node *node, int indent) { const struct ec_node_type *type; const struct ec_config_schema *schema; const struct ec_config *config; const struct ec_dict *attrs; const char *type_name, *node_id, *help; char *quoted; if (node == NULL) return 0; type = ec_node_type(node); type_name = ec_node_get_type_name(node); node_id = ec_node_id(node); config = ec_node_get_config(node); attrs = ec_node_attrs(node); schema = ec_node_type_schema(type); /* type */ export_indent(out, indent); fprintf(out, "type: %s\n", type_name); /* id (if set) */ if (node_id != NULL && node_id[0] != '\0') { quoted = ec_str_quote(node_id, '"', false); if (quoted == NULL) return -1; export_indent(out, indent); fprintf(out, "id: %s\n", quoted); free(quoted); } /* help (special attribute) */ if (attrs != NULL) { help = ec_dict_get(attrs, EC_INTERACT_HELP_ATTR); if (help != NULL) { quoted = ec_str_quote(help, '"', true); if (quoted == NULL) return -1; export_indent(out, indent); fprintf(out, "help: %s\n", quoted); free(quoted); } } /* config (node-type specific configuration) */ if (config != NULL && schema != NULL) { const struct ec_config_schema *elt; for (elt = schema; elt->type != EC_CONFIG_TYPE_NONE; elt++) { struct ec_config *val; if (elt->key == NULL) continue; /* skip reserved keys (type, id, help, attrs) */ if (ec_config_key_is_reserved(elt->key)) continue; val = ec_config_dict_get(config, elt->key); if (val == NULL) continue; export_indent(out, indent); fprintf(out, "%s: ", elt->key); if (export_ec_config(out, val, elt, indent + 1) < 0) return -1; } } return 0; } int ec_yaml_export(FILE *out, const struct ec_node *node) { if (out == NULL || node == NULL) { errno = EINVAL; return -1; } return export_ec_node(out, node, 0); } ���������������������������������������������libecoli-0.11.6/test/�������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�15203622040�0014332�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/complete.c���������������������������������������������������������������������0000664�0000000�0000000�00000006304�15203622040�0016311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_strvec *vec1 = NULL, *vec2 = NULL; struct ec_node *node = NULL; struct ec_comp *c = NULL; struct ec_comp_item *item; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; int testres = 0; node = ec_node_sh_lex( EC_NO_ID, EC_NODE_OR(EC_NO_ID, ec_node_str("id_x", "xx"), ec_node_str("id_y", "yy")) ); if (node == NULL) goto fail; c = ec_complete(node, "xcdscds"); testres |= EC_TEST_CHECK( c != NULL && ec_comp_count(c, EC_COMP_ALL) == 0, "complete count should is not 0\n" ); ec_comp_free(c); c = ec_complete(node, "x"); testres |= EC_TEST_CHECK( c != NULL && ec_comp_count(c, EC_COMP_ALL) == 1, "complete count should is not 1\n" ); ec_comp_free(c); c = ec_complete(node, ""); testres |= EC_TEST_CHECK( c != NULL && ec_comp_count(c, EC_COMP_ALL) == 2, "complete count should is not 2\n" ); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_comp_dump(f, NULL); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "no completion"), "bad dump\n"); free(buf); buf = NULL; f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_comp_dump(f, c); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "comp=<xx>"), "bad dump\n"); testres |= EC_TEST_CHECK(strstr(buf, "comp=<yy>"), "bad dump\n"); free(buf); buf = NULL; item = ec_comp_iter_first(c, EC_COMP_ALL); if (item == NULL) goto fail; testres |= EC_TEST_CHECK( !strcmp(ec_comp_item_get_display(item), "xx"), "bad item display\n" ); testres |= EC_TEST_CHECK(ec_comp_item_get_type(item) == EC_COMP_FULL, "bad item type\n"); testres |= EC_TEST_CHECK( !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_x"), "bad item node\n" ); item = ec_comp_iter_next(item, EC_COMP_ALL); if (item == NULL) goto fail; testres |= EC_TEST_CHECK( !strcmp(ec_comp_item_get_display(item), "yy"), "bad item display\n" ); testres |= EC_TEST_CHECK(ec_comp_item_get_type(item) == EC_COMP_FULL, "bad item type\n"); testres |= EC_TEST_CHECK( !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_y"), "bad item node\n" ); item = ec_comp_iter_next(item, EC_COMP_ALL); testres |= EC_TEST_CHECK(item == NULL, "should be the last item\n"); ec_comp_free(c); ec_node_free(node); node = EC_NODE_SEQ( EC_NO_ID, ec_node_str("id_x", "xxx"), ec_node_str("id_y", "yyyyyy"), ec_node_str("id_z", "zzzzzzzzzzz") ); testres |= EC_TEST_CHECK(node != NULL, "null node"); vec1 = EC_STRVEC("x", "y", "z"); testres |= EC_TEST_CHECK(vec1 != NULL, "null vec"); vec2 = ec_complete_strvec_expand(node, EC_COMP_ALL, vec1); testres |= EC_TEST_CHECK(vec2 != NULL, "expand failed"); ec_strvec_free(vec1); vec1 = EC_STRVEC("xxx", "yyyyyy", "zzzzzzzzzzz"); testres |= EC_TEST_CHECK(vec2 != NULL, "expand failed"); testres |= EC_TEST_CHECK(ec_strvec_cmp(vec2, vec1) == 0, "expand invalid"); ec_strvec_free(vec1); ec_strvec_free(vec2); ec_node_free(node); return testres; fail: ec_strvec_free(vec1); ec_strvec_free(vec2); ec_comp_free(c); ec_node_free(node); if (f != NULL) fclose(f); free(buf); return -1; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/config.c�����������������������������������������������������������������������0000664�0000000�0000000�00000014730�15203622040�0015750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <string.h> #include "test.h" static const struct ec_config_schema sch_intlist_elt[] = { { .desc = "This is a description for int", .type = EC_CONFIG_TYPE_INT64, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema sch_dict[] = { { .key = "my_int", .desc = "This is a description for int", .type = EC_CONFIG_TYPE_INT64, }, { .key = "my_int2", .desc = "This is a description for int2", .type = EC_CONFIG_TYPE_INT64, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema sch_dictlist_elt[] = { { .desc = "This is a description for dict", .type = EC_CONFIG_TYPE_DICT, .subschema = sch_dict, }, { .type = EC_CONFIG_TYPE_NONE, }, }; static const struct ec_config_schema sch_baseconfig[] = { { .key = "my_bool", .desc = "This is a description for bool", .type = EC_CONFIG_TYPE_BOOL, }, { .key = "my_int", .desc = "This is a description for int", .type = EC_CONFIG_TYPE_INT64, }, { .key = "my_string", .desc = "This is a description for string", .type = EC_CONFIG_TYPE_STRING, }, { .key = "my_node", .desc = "This is a description for node", .type = EC_CONFIG_TYPE_NODE, }, { .key = "my_intlist", .desc = "This is a description for list", .type = EC_CONFIG_TYPE_LIST, .subschema = sch_intlist_elt, }, { .key = "my_dictlist", .desc = "This is a description for list", .type = EC_CONFIG_TYPE_LIST, .subschema = sch_dictlist_elt, }, { .type = EC_CONFIG_TYPE_NONE, }, }; EC_TEST_MAIN() { struct ec_node *node = NULL; struct ec_dict *dict = NULL; const struct ec_config *value = NULL; struct ec_config *config = NULL, *config2 = NULL; struct ec_config *list = NULL, *subconfig = NULL; struct ec_config *list_, *config_; int testres = 0; int ret; testres |= EC_TEST_CHECK(ec_config_key_is_reserved("id"), "'id' should be reserved"); testres |= EC_TEST_CHECK(!ec_config_key_is_reserved("foo"), "'foo' should not be reserved"); node = ec_node("empty", EC_NO_ID); if (node == NULL) goto fail; if (ec_config_schema_validate(sch_baseconfig) < 0) { EC_LOG(EC_LOG_ERR, "invalid config schema\n"); goto fail; } ec_config_schema_dump(stdout, sch_baseconfig, "test"); config = ec_config_dict(); if (config == NULL) goto fail; ret = ec_config_dict_set(config, "my_bool", ec_config_bool(true)); testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean"); value = ec_config_dict_get(config, "my_bool"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_BOOL && value->boolean == true, "unexpected boolean value" ); ret = ec_config_dict_set(config, "my_int", ec_config_i64(1234)); testres |= EC_TEST_CHECK(ret == 0, "cannot set int"); value = ec_config_dict_get(config, "my_int"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_INT64 && value->i64 == 1234, "unexpected int value" ); testres |= EC_TEST_CHECK( ec_config_validate(config, sch_baseconfig) == 0, "cannot validate config\n" ); ret = ec_config_dict_set(config, "my_string", ec_config_string("toto")); testres |= EC_TEST_CHECK(ret == 0, "cannot set string"); value = ec_config_dict_get(config, "my_string"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_STRING && !strcmp(value->string, "toto"), "unexpected string value" ); list = ec_config_list(); if (list == NULL) goto fail; subconfig = ec_config_dict(); if (subconfig == NULL) goto fail; ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(1)); testres |= EC_TEST_CHECK(ret == 0, "cannot set int"); value = ec_config_dict_get(subconfig, "my_int"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_INT64 && value->i64 == 1, "unexpected int value" ); ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(2)); testres |= EC_TEST_CHECK(ret == 0, "cannot set int"); value = ec_config_dict_get(subconfig, "my_int2"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_INT64 && value->i64 == 2, "unexpected int value" ); testres |= EC_TEST_CHECK( ec_config_validate(subconfig, sch_dict) == 0, "cannot validate subconfig\n" ); ret = ec_config_list_add(list, subconfig); subconfig = NULL; /* freed */ testres |= EC_TEST_CHECK(ret == 0, "cannot add in list"); subconfig = ec_config_dict(); if (subconfig == NULL) goto fail; ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(3)); testres |= EC_TEST_CHECK(ret == 0, "cannot set int"); value = ec_config_dict_get(subconfig, "my_int"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_INT64 && value->i64 == 3, "unexpected int value" ); ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(4)); testres |= EC_TEST_CHECK(ret == 0, "cannot set int"); value = ec_config_dict_get(subconfig, "my_int2"); testres |= EC_TEST_CHECK( value != NULL && value->type == EC_CONFIG_TYPE_INT64 && value->i64 == 4, "unexpected int value" ); testres |= EC_TEST_CHECK( ec_config_validate(subconfig, sch_dict) == 0, "cannot validate subconfig\n" ); ret = ec_config_list_add(list, subconfig); subconfig = NULL; /* freed */ testres |= EC_TEST_CHECK(ret == 0, "cannot add in list"); ret = ec_config_dict_set(config, "my_dictlist", list); list = NULL; testres |= EC_TEST_CHECK(ret == 0, "cannot set list"); testres |= EC_TEST_CHECK( ec_config_validate(config, sch_baseconfig) == 0, "cannot validate config\n" ); list_ = ec_config_dict_get(config, "my_dictlist"); for (config_ = ec_config_list_first(list_); config_ != NULL; config_ = ec_config_list_next(list_, config_)) { ec_config_dump(stdout, config_); } ec_config_dump(stdout, config); config2 = ec_config_dup(config); testres |= EC_TEST_CHECK(config2 != NULL, "cannot duplicate config"); testres |= EC_TEST_CHECK(ec_config_cmp(config, config2) == 0, "fail to compare config"); ec_config_free(config2); config2 = NULL; /* remove the first element */ ec_config_list_del(list_, ec_config_list_first(list_)); testres |= EC_TEST_CHECK( ec_config_validate(config, sch_baseconfig) == 0, "cannot validate config\n" ); ec_config_dump(stdout, config); ec_config_free(list); ec_config_free(subconfig); ec_config_free(config); ec_dict_free(dict); ec_node_free(node); return testres; fail: ec_config_free(list); ec_config_free(subconfig); ec_config_free(config); ec_config_free(config2); ec_dict_free(dict); ec_node_free(node); return -1; } ����������������������������������������libecoli-0.11.6/test/dict.c�������������������������������������������������������������������������0000664�0000000�0000000�00000007066�15203622040�0015432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_dict *dict, *dup; struct ec_dict_elt_ref *iter; char *val; size_t i, count; int ret, testres = 0; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; dict = ec_dict(); if (dict == NULL) { EC_LOG(EC_LOG_ERR, "cannot create dict\n"); return -1; } count = 0; for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { count++; } testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0, "bad dict len"); ret = ec_dict_set(dict, "key1", "val1", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); ret = ec_dict_set(dict, "key2", strdup("val2"), free); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2, "bad dict len"); val = ec_dict_get(dict, "key1"); testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val1"), "invalid dict value"); val = ec_dict_get(dict, "key2"); testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val2"), "invalid dict value"); val = ec_dict_get(dict, "key3"); testres |= EC_TEST_CHECK(val == NULL, "key3 should be NULL"); ret = ec_dict_set(dict, "key1", "another_val1", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); ret = ec_dict_set(dict, "key2", strdup("another_val2"), free); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2, "bad dict len"); val = ec_dict_get(dict, "key1"); testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val1"), "invalid dict value"); val = ec_dict_get(dict, "key2"); testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val2"), "invalid dict value"); testres |= EC_TEST_CHECK(ec_dict_has_key(dict, "key1"), "key1 should be in dict"); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_dict_dump(f, NULL); fclose(f); f = NULL; free(buf); buf = NULL; f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_dict_dump(f, dict); fclose(f); f = NULL; free(buf); buf = NULL; ret = ec_dict_del(dict, "key1"); testres |= EC_TEST_CHECK(ret == 0, "cannot del key1"); testres |= EC_TEST_CHECK(ec_dict_len(dict) == 1, "invalid dict len"); ret = ec_dict_del(dict, "key2"); testres |= EC_TEST_CHECK(ret == 0, "cannot del key2"); testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0, "invalid dict len"); for (i = 0; i < 100; i++) { char key[8]; snprintf(key, sizeof(key), "k%zd", i); ret = ec_dict_set(dict, key, "val", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); } dup = ec_dict_dup(dict); testres |= EC_TEST_CHECK(dup != NULL, "cannot duplicate dict"); if (dup != NULL) { for (i = 0; i < 100; i++) { char key[8]; snprintf(key, sizeof(key), "k%zd", i); val = ec_dict_get(dup, key); testres |= EC_TEST_CHECK( val != NULL && !strcmp(val, "val"), "invalid dict value" ); } ec_dict_free(dup); dup = NULL; } count = 0; for (iter = ec_dict_iter(dict); iter != NULL; iter = ec_dict_iter_next(iter)) { count++; } testres |= EC_TEST_CHECK(count == 100, "invalid count in iterator"); /* einval */ ret = ec_dict_set(dict, NULL, "val1", NULL); testres |= EC_TEST_CHECK(ret == -1, "should not be able to set key"); val = ec_dict_get(dict, NULL); testres |= EC_TEST_CHECK(val == NULL, "get(NULL) should no success"); ec_dict_free(dict); return testres; fail: ec_dict_free(dict); if (f) fclose(f); free(buf); return -1; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/htable.c�����������������������������������������������������������������������0000664�0000000�0000000�00000002717�15203622040�0015744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_htable *htable; struct ec_htable_elt_ref *iter; size_t count; int ret, testres = 0; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; /* Minimal test, since ec_dict also uses this code and is better * tested. */ htable = ec_htable(); if (htable == NULL) { EC_LOG(EC_LOG_ERR, "cannot create htable\n"); return -1; } count = 0; for (iter = ec_htable_iter(htable); iter != NULL; iter = ec_htable_iter_next(iter)) { count++; } testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); testres |= EC_TEST_CHECK(ec_htable_len(htable) == 0, "bad htable len"); ret = ec_htable_set(htable, "key1", 4, "val1", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); ret = ec_htable_set(htable, "key2", 4, strdup("val2"), free); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); testres |= EC_TEST_CHECK(ec_htable_len(htable) == 2, "bad htable len"); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_htable_dump(f, NULL); fclose(f); f = NULL; free(buf); buf = NULL; f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_htable_dump(f, htable); fclose(f); f = NULL; free(buf); buf = NULL; ec_htable_free(htable); return testres; fail: ec_htable_free(htable); if (f) fclose(f); free(buf); return -1; } �������������������������������������������������libecoli-0.11.6/test/log.c��������������������������������������������������������������������������0000664�0000000�0000000�00000003770�15203622040�0015266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <string.h> #include <syslog.h> #include "test.h" /* Internal symbols from src/log.c */ extern ec_log_t ec_log_fct; extern void *ec_log_opaque; extern int ec_log_lookup(const char *name); EC_LOG_TYPE_REGISTER(log_test); static int log_cb(int type, enum ec_log_level level, void *opaque, const char *str) { (void)type; (void)level; (void)str; *(int *)opaque = 1; return 0; } EC_TEST_MAIN() { ec_log_t prev_log_cb; void *prev_opaque; const char *logname; int testres = 0; int check_cb = 0; int logtype; int level; int ret; prev_log_cb = ec_log_fct; prev_opaque = ec_log_opaque; ret = ec_log_fct_register(log_cb, &check_cb); testres |= EC_TEST_CHECK(ret == 0, "cannot register log function\n"); EC_LOG(LOG_ERR, "test\n"); testres |= EC_TEST_CHECK(check_cb == 1, "log callback was not invoked\n"); logtype = ec_log_lookup("dsdedesdes"); testres |= EC_TEST_CHECK(logtype == -1, "lookup invalid name should return -1"); logtype = ec_log_lookup("log"); logname = ec_log_name(logtype); testres |= EC_TEST_CHECK( logname != NULL && !strcmp(logname, "log"), "cannot get log name\n" ); logname = ec_log_name(-1); testres |= EC_TEST_CHECK( logname != NULL && !strcmp(logname, "unknown"), "cannot get invalid log name\n" ); logname = ec_log_name(34324); testres |= EC_TEST_CHECK( logname != NULL && !strcmp(logname, "unknown"), "cannot get invalid log name\n" ); level = ec_log_level_get(); ret = ec_log_level_set(2); testres |= EC_TEST_CHECK(ret == 0 && ec_log_level_get() == 2, "cannot set log level\n"); ret = ec_log_level_set(10); testres |= EC_TEST_CHECK(ret != 0, "should not be able to set log level\n"); ec_log_fct_register(NULL, NULL); ec_log_level_set(LOG_DEBUG); EC_LOG(LOG_DEBUG, "test log\n"); ec_log_level_set(LOG_INFO); EC_LOG(LOG_DEBUG, "test log (not displayed)\n"); ec_log_level_set(level); ec_log_fct = prev_log_cb; ec_log_opaque = prev_opaque; return testres; } ��������libecoli-0.11.6/test/meson.build��������������������������������������������������������������������0000664�0000000�0000000�00000002023�15203622040�0016471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Robin Jarry if not get_option('tests').allowed() subdir_done() endif libecoli_tests = files( 'complete.c', 'config.c', 'dict.c', 'htable.c', 'log.c', 'node.c', 'node_any.c', 'node_bypass.c', 'node_cmd.c', 'node_cond.c', 'node_dynamic.c', 'node_dynlist.c', 'node_empty.c', 'node_expr.c', 'node_file.c', 'node_int.c', 'node_many.c', 'node_none.c', 'node_once.c', 'node_option.c', 'node_or.c', 'node_re.c', 'node_re_lex.c', 'node_seq.c', 'node_sh_lex.c', 'node_space.c', 'node_str.c', 'node_subset.c', 'parse.c', 'string.c', 'strvec.c', 'vec.c', ) if yaml_dep.found() libecoli_tests += files('yaml.c') endif fs = import('fs') foreach t : libecoli_tests test( fs.stem(t) + '_test', executable( fs.stem(t), sources: [t] + files('test.c'), link_with: libecoli, include_directories: inc, ), env: { 'ASAN_OPTIONS': 'handle_abort=0:halt_on_error=1:handle_segv=2', 'CMOCKA_TEST_ABORT': '1', }, suite: 'unit', ) endforeach �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node.c�������������������������������������������������������������������������0000664�0000000�0000000�00000012353�15203622040�0015427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "test.h" static unsigned int test_iter(struct ec_node *node) { struct ec_node_iter *iter_root, *iter; unsigned int count = 0; iter_root = ec_node_iter(node); for (iter = iter_root; iter != NULL; iter = ec_node_iter_next(iter_root, iter, true)) { count++; } ec_node_iter_free(iter_root); return count; } EC_TEST_MAIN() { struct ec_node *node = NULL, *expr = NULL; struct ec_node *expr2 = NULL, *val = NULL, *op = NULL, *seq = NULL; const struct ec_node_type *type; struct ec_node *child; unsigned int count; FILE *f = NULL; char *buf = NULL; char *desc = NULL; size_t buflen = 0; int testres = 0; int ret; node = EC_NODE_SEQ(EC_NO_ID, ec_node_str("id_x", "x"), ec_node_str("id_y", "y")); if (node == NULL) goto fail; ec_node_clone(node); ec_node_free(node); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_node_dump(f, node); ec_node_type_dump(f); ec_node_dump(f, NULL); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "type=seq id="), "bad dump\n"); testres |= EC_TEST_CHECK( strstr(buf, "type=str id=id_x") && strstr(strstr(buf, "type=str id=id_x") + 1, "type=str id=id_y"), "bad dump\n" ); free(buf); buf = NULL; desc = ec_node_desc(node); testres |= EC_TEST_CHECK( !strcmp(ec_node_type(node)->name, "seq") && !strcmp(ec_node_id(node), EC_NO_ID) && !strcmp(desc, "<seq>"), "bad child 0" ); free(desc); desc = NULL; testres |= EC_TEST_CHECK(ec_node_get_children_count(node) == 2, "bad children count\n"); ret = ec_node_get_child(node, 0, &child); testres |= EC_TEST_CHECK( ret == 0 && child != NULL && !strcmp(ec_node_type(child)->name, "str") && !strcmp(ec_node_id(child), "id_x"), "bad child 0" ); ret = ec_node_get_child(node, 1, &child); testres |= EC_TEST_CHECK( ret == 0 && child != NULL && !strcmp(ec_node_type(child)->name, "str") && !strcmp(ec_node_id(child), "id_y"), "bad child 1" ); ret = ec_node_get_child(node, 2, &child); testres |= EC_TEST_CHECK(ret != 0, "ret should be != 0"); testres |= EC_TEST_CHECK(child == NULL, "child 2 should be NULL"); child = ec_node_find(node, "id_x"); desc = ec_node_desc(child); testres |= EC_TEST_CHECK( child != NULL && !strcmp(ec_node_type(child)->name, "str") && !strcmp(ec_node_id(child), "id_x") && !strcmp(desc, "x"), "bad child id_x" ); free(desc); desc = NULL; count = test_iter(node); testres |= EC_TEST_CHECK(count == 3, "invalid node count (%u instead if %u)", count, 3); child = ec_node_find(node, "id_dezdex"); testres |= EC_TEST_CHECK(child == NULL, "child with wrong id should be NULL"); ret = ec_dict_set(ec_node_attrs(node), "key", "val", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set node attribute\n"); type = ec_node_type_lookup("seq"); testres |= EC_TEST_CHECK( type != NULL && ec_node_check_type(node, type) == 0, "cannot get seq node type" ); type = ec_node_type_lookup("str"); testres |= EC_TEST_CHECK( type != NULL && ec_node_check_type(node, type) < 0, "node type should not be str" ); ec_node_free(node); node = NULL; node = ec_node("deznuindez", EC_NO_ID); testres |= EC_TEST_CHECK(node == NULL, "should not be able to create node\n"); /* test loop */ expr = ec_node("or", EC_NO_ID); val = ec_node_int(EC_NO_ID, 0, 10, 0); op = ec_node_str(EC_NO_ID, "!"); seq = EC_NODE_SEQ(EC_NO_ID, op, ec_node_clone(expr)); op = NULL; if (expr == NULL || val == NULL || seq == NULL) goto fail; if (ec_node_or_add(expr, ec_node_clone(seq)) < 0) goto fail; ec_node_free(seq); seq = NULL; if (ec_node_or_add(expr, ec_node_clone(val)) < 0) goto fail; ec_node_free(val); val = NULL; count = test_iter(expr); testres |= EC_TEST_CHECK(count == 5, "invalid node count (%u instead if %u)", count, 5); child = ec_node_find(expr, "id_dezdex"); testres |= EC_TEST_CHECK(child == NULL, "child with wrong id should be NULL"); testres |= EC_TEST_CHECK_PARSE(expr, 1, "1"); testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1"); testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!"); ec_node_free(expr); expr = NULL; /* same loop test, but keep some refs (released later) */ expr = ec_node("or", EC_NO_ID); expr2 = ec_node_clone(expr); val = ec_node_int(EC_NO_ID, 0, 10, 0); op = ec_node_str(EC_NO_ID, "!"); seq = EC_NODE_SEQ(EC_NO_ID, op, ec_node_clone(expr)); op = NULL; if (expr == NULL || val == NULL || seq == NULL) goto fail; if (ec_node_or_add(expr, ec_node_clone(seq)) < 0) goto fail; ec_node_free(seq); seq = NULL; if (ec_node_or_add(expr, ec_node_clone(val)) < 0) goto fail; testres |= EC_TEST_CHECK_PARSE(expr, 1, "1"); testres |= EC_TEST_CHECK_PARSE(expr, 3, "!", "!", "1"); testres |= EC_TEST_CHECK_PARSE(expr, -1, "!", "!", "!"); count = test_iter(expr); testres |= EC_TEST_CHECK(count == 5, "invalid node count (%u instead if %u)", count, 5); ec_node_free(expr2); expr2 = NULL; ec_node_free(val); val = NULL; ec_node_free(expr); expr = NULL; return testres; fail: ec_node_free(expr); ec_node_free(expr2); ec_node_free(val); ec_node_free(seq); ec_node_free(node); if (f != NULL) fclose(f); free(buf); assert(errno != 0); return -1; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_any.c���������������������������������������������������������������������0000664�0000000�0000000�00000001422�15203622040�0016271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node("any", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1); ec_node_free(node); /* never completes */ node = ec_node("any", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_bypass.c������������������������������������������������������������������0000664�0000000�0000000�00000001721�15203622040�0017005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_bypass(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1); ec_node_free(node); /* test completion */ node = ec_node_bypass(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } �����������������������������������������������libecoli-0.11.6/test/node_cmd.c���������������������������������������������������������������������0000664�0000000�0000000�00000006323�15203622040�0016252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = EC_NODE_CMD( EC_NO_ID, "command [option] [subset1, subset2, subset3, subset4] x|y z*", ec_node_int("x", 0, 10, 10), ec_node_int("y", 20, 30, 10) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1"); testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1"); testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2", "1"); testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3", "subset1", "1"); testres |= EC_TEST_CHECK_PARSE( node, 6, "command", "subset3", "subset1", "subset4", "subset2", "4" ); testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23"); testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23"); testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23", "z", "z"); testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); ec_node_free(node); /* test '&' operator: all elements required in any order */ node = EC_NODE_CMD(EC_NO_ID, "command foo & bar & toto end"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "foo", "bar", "toto", "end"); testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "toto", "foo", "bar", "end"); testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "bar", "toto", "foo", "end"); testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "foo", "bar", "end"); testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "foo", "end"); testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "end"); ec_node_free(node); /* test ',' operator: at least one element required */ node = EC_NODE_CMD(EC_NO_ID, "command foo, bar, toto end"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "foo", "bar", "toto", "end"); testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "foo", "end"); testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "bar", "foo", "end"); testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "end"); ec_node_free(node); node = EC_NODE_CMD( EC_NO_ID, "good morning [count] bob|bobby|michael", ec_node_int("count", 0, 10, 10) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob"); testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "good", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "g", EC_VA_END, "good", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE( node, "good", "morning", "", EC_VA_END, "bob", "bobby", "michael", EC_VA_END ); ec_node_free(node); node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 0, "x"); ec_node_free(node); return testres; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_cond.c��������������������������������������������������������������������0000664�0000000�0000000�00000001366�15203622040�0016434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_cond( EC_NO_ID, "cmp(le, count(find(root(), id_node)), 3)", ec_node_many(EC_NO_ID, ec_node_str("id_node", "foo"), 0, 0) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo"); ec_node_free(node); /* XXX test completion */ return testres; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_dynamic.c�����������������������������������������������������������������0000664�0000000�0000000�00000002443�15203622040�0017132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" static struct ec_node *build_counter(struct ec_pnode *parse, void *opaque) { const struct ec_node *node; struct ec_pnode *root, *iter; unsigned int count = 0; char buf[32]; (void)opaque; root = ec_pnode_get_root(parse); for (iter = root; iter != NULL; iter = EC_PNODE_ITER_NEXT(root, iter, 1)) { node = ec_pnode_get_node(iter); if (!strcmp(ec_node_id(node), "my-id")) count++; } snprintf(buf, sizeof(buf), "count-%u", count); return ec_node_str("my-id", buf); } EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_many(EC_NO_ID, ec_node_dynamic(EC_NO_ID, build_counter, NULL), 1, 3); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "count-0"); testres |= EC_TEST_CHECK_PARSE(node, 3, "count-0", "count-1", "count-2"); testres |= EC_TEST_CHECK_PARSE(node, 1, "count-0", "count-0"); /* test completion */ testres |= EC_TEST_CHECK_COMPLETE(node, "c", EC_VA_END, "count-0", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE( node, "count-0", "", EC_VA_END, "count-1", EC_VA_END, "count-1" ); ec_node_free(node); return testres; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_dynlist.c�����������������������������������������������������������������0000664�0000000�0000000�00000004704�15203622040�0017176�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" static struct ec_strvec *get_names(struct ec_pnode *pstate, void *opaque) { (void)pstate; (void)opaque; return EC_STRVEC("foo", "bar", "baz"); } EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_dynlist(EC_NO_ID, get_names, NULL, "[a-z]+", DYNLIST_MATCH_LIST); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "pouet"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "pouet"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); node = ec_node_dynlist(EC_NO_ID, get_names, NULL, "[a-z]+", DYNLIST_MATCH_REGEXP); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "pouet"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "pouet"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); node = ec_node_dynlist( EC_NO_ID, get_names, NULL, "[a-z]+", DYNLIST_MATCH_REGEXP | DYNLIST_EXCLUDE_LIST ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "pouet"); testres |= EC_TEST_CHECK_PARSE(node, -1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "pouet"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); /* test completion */ node = ec_node_dynlist(EC_NO_ID, get_names, NULL, "[a-z]+", DYNLIST_MATCH_LIST); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", "bar", "baz", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, "bar", "baz", EC_VA_END); ec_node_free(node); return testres; } ������������������������������������������������������������libecoli-0.11.6/test/node_empty.c�������������������������������������������������������������������0000664�0000000�0000000�00000001521�15203622040�0016640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node("empty", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 0, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 0, "foo", "bar"); ec_node_free(node); /* never completes */ node = ec_node("empty", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_expr.c��������������������������������������������������������������������0000664�0000000�0000000�00000017133�15203622040�0016466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <assert.h> #include <errno.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include "test.h" EC_LOG_TYPE_REGISTER(node_expr); struct my_eval_result { int val; }; static int ec_node_expr_test_eval_var(void **result, void *userctx, const struct ec_pnode *var) { const struct ec_strvec *vec; const struct ec_node *node; struct my_eval_result *eval = NULL; int64_t val; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(var); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } node = ec_pnode_get_node(var); if (ec_node_int_getval(node, ec_strvec_val(vec, 0), &val) < 0) return -1; eval = malloc(sizeof(*eval)); if (eval == NULL) return -1; eval->val = val; EC_LOG(EC_LOG_DEBUG, "eval var %d\n", eval->val); *result = eval; return 0; } static int ec_node_expr_test_eval_pre_op( void **result, void *userctx, void *operand, const struct ec_pnode *operator ) { const struct ec_strvec *vec; struct my_eval_result *eval = operand; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } if (!strcmp(ec_strvec_val(vec, 0), "!")) { eval->val = !eval->val; } else { errno = EINVAL; return -1; } EC_LOG(EC_LOG_DEBUG, "eval pre_op %d\n", eval->val); *result = eval; return 0; } static int ec_node_expr_test_eval_post_op( void **result, void *userctx, void *operand, const struct ec_pnode *operator ) { const struct ec_strvec *vec; struct my_eval_result *eval = operand; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } if (!strcmp(ec_strvec_val(vec, 0), "^")) { eval->val = eval->val * eval->val; } else { errno = EINVAL; return -1; } EC_LOG(EC_LOG_DEBUG, "eval post_op %d\n", eval->val); *result = eval; return 0; } static int ec_node_expr_test_eval_bin_op( void **result, void *userctx, void *operand1, const struct ec_pnode *operator, void *operand2 ) { const struct ec_strvec *vec; struct my_eval_result *eval1 = operand1; struct my_eval_result *eval2 = operand2; (void)userctx; /* get parsed string vector, it should contain only one str */ vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; } if (!strcmp(ec_strvec_val(vec, 0), "+")) { eval1->val = eval1->val + eval2->val; } else if (!strcmp(ec_strvec_val(vec, 0), "*")) { eval1->val = eval1->val * eval2->val; } else { errno = EINVAL; return -1; } EC_LOG(EC_LOG_DEBUG, "eval bin_op %d\n", eval1->val); free(eval2); *result = eval1; return 0; } static int ec_node_expr_test_eval_parenthesis( void **result, void *userctx, const struct ec_pnode *open_paren, const struct ec_pnode *close_paren, void *value ) { (void)userctx; (void)open_paren; (void)close_paren; EC_LOG(EC_LOG_DEBUG, "eval paren\n"); *result = value; return 0; } static void ec_node_expr_test_eval_free(void *result, void *userctx) { (void)userctx; free(result); } static const struct ec_node_expr_eval_ops test_ops = { .eval_var = ec_node_expr_test_eval_var, .eval_pre_op = ec_node_expr_test_eval_pre_op, .eval_post_op = ec_node_expr_test_eval_post_op, .eval_bin_op = ec_node_expr_test_eval_bin_op, .eval_parenthesis = ec_node_expr_test_eval_parenthesis, .eval_free = ec_node_expr_test_eval_free, }; static int ec_node_expr_test_eval( struct ec_node *lex_node, const struct ec_node *expr_node, const char *str, int val ) { struct ec_pnode *p; void *result; struct my_eval_result *eval; int ret; p = ec_parse(lex_node, str); if (p == NULL) return -1; ret = ec_node_expr_eval(&result, expr_node, p, &test_ops, NULL); ec_pnode_free(p); if (ret < 0) return -1; /* the parse value is an integer */ eval = result; assert(eval != NULL); EC_LOG(EC_LOG_DEBUG, "result: %d (expected %d)\n", eval->val, val); if (eval->val == val) ret = 0; else ret = -1; free(eval); return ret; } EC_TEST_MAIN() { struct ec_node *node = NULL, *lex_node = NULL; int testres = 0; node = ec_node("expr", "my_expr"); if (node == NULL) return -1; ec_node_expr_set_val_node(node, ec_node_int(EC_NO_ID, 0, UCHAR_MAX, 0)); ec_node_expr_add_bin_op(node, ec_node_str(EC_NO_ID, "+")); ec_node_expr_add_bin_op(node, ec_node_str(EC_NO_ID, "*")); ec_node_expr_add_pre_op(node, ec_node_str(EC_NO_ID, "!")); /* not */ ec_node_expr_add_post_op(node, ec_node_str(EC_NO_ID, "^")); /* square */ ec_node_expr_add_parenthesis(node, ec_node_str(EC_NO_ID, "("), ec_node_str(EC_NO_ID, ")")); testres |= EC_TEST_CHECK_PARSE(node, 1, "1"); testres |= EC_TEST_CHECK_PARSE(node, 1, "1", "1"); testres |= EC_TEST_CHECK_PARSE(node, 1, "1", "*"); testres |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1"); testres |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1", "*"); testres |= EC_TEST_CHECK_PARSE(node, 4, "1", "+", "!", "1"); testres |= EC_TEST_CHECK_PARSE(node, 4, "1", "^", "+", "1"); testres |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "*", "1"); testres |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "+", "1"); testres |= EC_TEST_CHECK_PARSE(node, 7, "1", "*", "1", "*", "1", "*", "1"); testres |= EC_TEST_CHECK_PARSE(node, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")"); testres |= EC_TEST_CHECK_PARSE(node, 5, "1", "+", "!", "1", "^"); /* prepend a lexer to the expression node */ lex_node = ec_node_re_lex(EC_NO_ID, ec_node_clone(node)); if (lex_node == NULL) goto fail; testres |= ec_node_re_lex_add(lex_node, "[0-9]+", 1, NULL); /* vars */ testres |= ec_node_re_lex_add(lex_node, "[+*!^()]", 1, NULL); /* operators */ testres |= ec_node_re_lex_add(lex_node, "[ \t]+", 0, NULL); /* spaces */ /* valid expressions */ testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "!1"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^ + 1"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "1 + 4 * (2 + 3^)^"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1)"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "3*!3+!3*(2+ 2)"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "!!(!1)^ + !(4 + (2*3))"); testres |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1 + 1)^ * 1^"); /* invalid expressions */ testres |= EC_TEST_CHECK_PARSE(lex_node, -1, ""); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "()"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "("); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, ")"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "+1"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+*1"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+(1*1"); testres |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+!1!1)"); testres |= ec_node_expr_test_eval(lex_node, node, "1^", 1); testres |= ec_node_expr_test_eval(lex_node, node, "2^", 4); testres |= ec_node_expr_test_eval(lex_node, node, "!1", 0); testres |= ec_node_expr_test_eval(lex_node, node, "!0", 1); testres |= ec_node_expr_test_eval(lex_node, node, "1+1", 2); testres |= ec_node_expr_test_eval(lex_node, node, "1+2+3", 6); testres |= ec_node_expr_test_eval(lex_node, node, "1+1*2", 4); testres |= ec_node_expr_test_eval(lex_node, node, "2 * 2^", 8); testres |= ec_node_expr_test_eval(lex_node, node, "(1 + !0)^ * !0^", 4); testres |= ec_node_expr_test_eval(lex_node, node, "(1 + !1) * 3", 3); ec_node_free(node); ec_node_free(lex_node); return testres; fail: ec_node_free(lex_node); ec_node_free(node); return -1; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_file.c��������������������������������������������������������������������0000664�0000000�0000000�00000005502�15203622040�0016424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <dirent.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "test.h" static int test_lstat(const char *pathname, struct stat *buf) { if (!strcmp(pathname, "/tmp/toto/")) { struct stat st = {.st_mode = S_IFDIR}; memcpy(buf, &st, sizeof(*buf)); return 0; } errno = ENOENT; return -1; } static DIR *test_opendir(const char *name) { int *p; if (strcmp(name, "/tmp/toto/")) { errno = ENOENT; return NULL; } p = malloc(sizeof(int)); if (p) *p = 0; return (DIR *)p; } static struct dirent *test_readdir(DIR *dirp) { static struct dirent de[] = { {.d_type = DT_DIR, .d_name = ".."}, {.d_type = DT_DIR, .d_name = "."}, {.d_type = DT_REG, .d_name = "bar"}, {.d_type = DT_UNKNOWN, .d_name = "bar2"}, {.d_type = DT_REG, .d_name = "foo"}, {.d_type = DT_DIR, .d_name = "titi"}, {.d_type = DT_UNKNOWN, .d_name = "tutu"}, {.d_name = ""}, }; int *p = (int *)dirp; struct dirent *ret = &de[*p]; if (!strcmp(ret->d_name, "")) return NULL; *p = *p + 1; return ret; } static int test_closedir(DIR *dirp) { free(dirp); return 0; } static int test_dirfd(DIR *dirp) { int *p = (int *)dirp; return *p; } static int test_fstatat(int dirfd, const char *pathname, struct stat *buf, int flags) { (void)dirfd; (void)flags; if (!strcmp(pathname, "bar2")) { struct stat st = {.st_mode = S_IFREG}; memcpy(buf, &st, sizeof(*buf)); return 0; } else if (!strcmp(pathname, "tutu")) { struct stat st = {.st_mode = S_IFDIR}; memcpy(buf, &st, sizeof(*buf)); return 0; } errno = ENOENT; return -1; } static struct ec_node_file_ops test_ops = { .lstat = test_lstat, .opendir = test_opendir, .readdir = test_readdir, .closedir = test_closedir, .dirfd = test_dirfd, .fstatat = test_fstatat, }; EC_TEST_MAIN() { struct ec_node *node; int testres = 0; ec_node_file_set_ops(&test_ops); node = ec_node("file", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } /* any string matches */ testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "/tmp/bar"); testres |= EC_TEST_CHECK_PARSE(node, -1); /* test completion */ testres |= EC_TEST_CHECK_COMPLETE(node, EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "/tmp/toto/t", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE_PARTIAL( node, "/tmp/toto/t", EC_VA_END, "/tmp/toto/titi/", "/tmp/toto/tutu/", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE( node, "/tmp/toto/f", EC_VA_END, "/tmp/toto/foo", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE( node, "/tmp/toto/b", EC_VA_END, "/tmp/toto/bar", "/tmp/toto/bar2", EC_VA_END ); ec_node_free(node); return testres; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_int.c���������������������������������������������������������������������0000664�0000000�0000000�00000005666�15203622040�0016312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_pnode *p; struct ec_node *node; const char *s; int testres = 0; uint64_t u64; int64_t i64; node = ec_node_uint(EC_NO_ID, 1, 256, 0); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, -1, ""); testres |= EC_TEST_CHECK_PARSE(node, -1, "0"); testres |= EC_TEST_CHECK_PARSE(node, 1, "1"); testres |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "0x100"); testres |= EC_TEST_CHECK_PARSE(node, 1, " 1"); testres |= EC_TEST_CHECK_PARSE(node, -1, "-1"); testres |= EC_TEST_CHECK_PARSE(node, -1, "0x101"); testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz"); testres |= EC_TEST_CHECK_PARSE(node, -1, "0x100000000000000000"); testres |= EC_TEST_CHECK_PARSE(node, -1, "4r"); p = ec_parse(node, "1"); s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK( s != NULL && ec_node_uint_getval(node, s, &u64) == 0 && u64 == 1, "bad integer value" ); ec_pnode_free(p); p = ec_parse(node, "10"); s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK( s != NULL && ec_node_uint_getval(node, s, &u64) == 0 && u64 == 10, "bad integer value" ); ec_pnode_free(p); ec_node_free(node); node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "0"); testres |= EC_TEST_CHECK_PARSE(node, 1, "-1"); testres |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff"); testres |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff"); testres |= EC_TEST_CHECK_PARSE(node, -1, "0x8000000000000000"); testres |= EC_TEST_CHECK_PARSE(node, -1, "-2"); testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz"); testres |= EC_TEST_CHECK_PARSE(node, -1, "4r"); p = ec_parse(node, "10"); s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK( s != NULL && ec_node_int_getval(node, s, &i64) == 0 && i64 == 16, "bad integer value" ); ec_pnode_free(p); ec_node_free(node); node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "0"); testres |= EC_TEST_CHECK_PARSE(node, 1, "-1"); testres |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808"); testres |= EC_TEST_CHECK_PARSE(node, -1, "0x0"); testres |= EC_TEST_CHECK_PARSE(node, -1, "1"); ec_node_free(node); /* test completion */ node = ec_node_int(EC_NO_ID, 0, 10, 0); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "1", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ��������������������������������������������������������������������������libecoli-0.11.6/test/node_many.c��������������������������������������������������������������������0000664�0000000�0000000�00000007362�15203622040�0016457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" #define ID_EMPTY "id_empty" EC_TEST_MAIN() { struct ec_strvec *strvec = NULL; struct ec_pnode *pnode = NULL; struct ec_node *node = NULL; int testres = 0; node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 0, 0); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 0, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 0); ec_node_free(node); node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 1, 0); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_PARSE(node, -1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1); ec_node_free(node); node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 1, 2); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_PARSE(node, -1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1); ec_node_free(node); node = ec_node_many(EC_NO_ID, ec_node_empty(ID_EMPTY), 0, 0); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 0, "foo"); strvec = ec_strvec(); if (strvec == NULL) { EC_LOG(EC_LOG_ERR, "failed to create strvec\n"); goto fail; } pnode = ec_parse_strvec(node, strvec); if (pnode == NULL) { EC_LOG(EC_LOG_ERR, "failed to parse strvec\n"); goto fail; } if (ec_pnode_find(pnode, ID_EMPTY)) { EC_LOG(EC_LOG_ERR, "no ID_EMPTY pnode is expected\n"); goto fail; } ec_pnode_free(pnode); pnode = NULL; ec_strvec_free(strvec); strvec = NULL; ec_node_free(node); node = ec_node_many(EC_NO_ID, ec_node_empty(ID_EMPTY), 0, 5); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 0, "foo"); strvec = ec_strvec(); if (strvec == NULL) { EC_LOG(EC_LOG_ERR, "failed to create strvec\n"); goto fail; } pnode = ec_parse_strvec(node, strvec); if (pnode == NULL) { EC_LOG(EC_LOG_ERR, "failed to parse strvec\n"); goto fail; } if (!ec_pnode_find(pnode, ID_EMPTY)) { EC_LOG(EC_LOG_ERR, "ID_EMPTY pnodes are expected\n"); goto fail; } ec_pnode_free(pnode); pnode = NULL; ec_strvec_free(strvec); strvec = NULL; ec_node_free(node); /* test completion */ node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 2, 4); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); goto fail; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "foo", "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE( node, "foo", "foo", "foo", "", EC_VA_END, "foo", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE( node, "foo", "foo", "foo", "foo", "", EC_VA_END, EC_VA_END ); ec_node_free(node); return testres; fail: ec_node_free(node); ec_pnode_free(pnode); ec_strvec_free(strvec); return -1; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_none.c��������������������������������������������������������������������0000664�0000000�0000000�00000001426�15203622040�0016445�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2018, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node("none", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1); ec_node_free(node); /* never completes */ node = ec_node("none", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_once.c��������������������������������������������������������������������0000664�0000000�0000000�00000002530�15203622040�0016427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_many( EC_NO_ID, EC_NODE_OR( EC_NO_ID, ec_node_once(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")), ec_node_str(EC_NO_ID, "bar") ), 0, 0 ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "bar", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 0, "foox"); testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "bar", "", EC_VA_END, "foo", "bar", EC_VA_END); ec_node_free(node); return testres; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_option.c������������������������������������������������������������������0000664�0000000�0000000�00000001717�15203622040�0017021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 0, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 0); ec_node_free(node); /* test completion */ node = ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "foo")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } �������������������������������������������������libecoli-0.11.6/test/node_or.c����������������������������������������������������������������������0000664�0000000�0000000�00000003173�15203622040�0016127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = EC_NODE_OR(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, " "); testres |= EC_TEST_CHECK_PARSE(node, -1, "foox"); testres |= EC_TEST_CHECK_PARSE(node, -1, "toto"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); /* test completion */ node = EC_NODE_OR( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "bar2"), ec_node_str(EC_NO_ID, "toto"), ec_node_str(EC_NO_ID, "titi") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE( node, "", EC_VA_END, "foo", "bar", "bar2", "toto", "titi", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, "bar", "bar2", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "bar", EC_VA_END, "bar", "bar2", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "t", EC_VA_END, "toto", "titi", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "to", EC_VA_END, "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_re.c����������������������������������������������������������������������0000664�0000000�0000000�00000001211�15203622040�0016104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_re(EC_NO_ID, "fo+|bar"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); return testres; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_re_lex.c������������������������������������������������������������������0000664�0000000�0000000�00000003106�15203622040�0016761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int ret, testres = 0; node = ec_node_re_lex( EC_NO_ID, ec_node_many( EC_NO_ID, EC_NODE_OR( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_int(EC_NO_ID, 0, 1000, 0) ), 0, 0 ) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } ret = ec_node_re_lex_add(node, "[a-zA-Z]+", 1, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); ret = ec_node_re_lex_add(node, "[0-9]+", 1, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); ret = ec_node_re_lex_add(node, "=", 1, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); ret = ec_node_re_lex_add(node, "-", 1, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); ret = ec_node_re_lex_add(node, "\\+", 1, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); ret = ec_node_re_lex_add(node, "[ \t]+", 0, NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp"); if (ret != 0) { EC_LOG(EC_LOG_ERR, "cannot add regexp to node\n"); ec_node_free(node); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, " foo bar 324 bar234"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo bar324"); testres |= EC_TEST_CHECK_PARSE(node, 1, ""); testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); /* no completion */ testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_seq.c���������������������������������������������������������������������0000664�0000000�0000000�00000003620�15203622040�0016274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node = NULL; int testres = 0; node = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx"); testres |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "", "foo"); testres |= (ec_node_seq_add(node, ec_node_str(EC_NO_ID, "grr")) < 0); testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "bar", "grr"); ec_node_free(node); /* test completion */ node = EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "toto")), ec_node_str(EC_NO_ID, "bar") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "", EC_VA_END, "bar", "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "t", EC_VA_END, "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "b", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", "bar", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foobarx", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ����������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_sh_lex.c������������������������������������������������������������������0000664�0000000�0000000�00000004220�15203622040�0016763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node_sh_lex( EC_NO_ID, EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "toto")), ec_node_str(EC_NO_ID, "bar") ) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "foo bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, " foo bar"); testres |= EC_TEST_CHECK_PARSE(node, 1, " 'foo' \"bar\""); testres |= EC_TEST_CHECK_PARSE(node, 1, " 'f'oo 'toto' bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo toto bar'"); ec_node_free(node); /* test completion */ node = ec_node_sh_lex( EC_NO_ID, EC_NODE_SEQ( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "toto")), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "titi") ) ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, " ", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo ", EC_VA_END, "bar", "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo t", EC_VA_END, "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo b", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo bar", EC_VA_END, "bar", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo bar ", EC_VA_END, "titi", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo toto bar ", EC_VA_END, "titi", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo barx", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo 'b", EC_VA_END, "'bar'", EC_VA_END); ec_node_free(node); return testres; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_space.c�������������������������������������������������������������������0000664�0000000�0000000�00000001753�15203622040�0016604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = ec_node("space", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, " "); testres |= EC_TEST_CHECK_PARSE(node, 1, " ", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo "); ec_node_free(node); /* test completion */ node = ec_node("space", EC_NO_ID); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } /* never completes whatever the input */ testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, " ", EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ���������������������libecoli-0.11.6/test/node_str.c���������������������������������������������������������������������0000664�0000000�0000000�00000004015�15203622040�0016313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; char *desc = NULL; node = ec_node_str(EC_NO_ID, "foo"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } desc = ec_node_desc(node); testres |= EC_TEST_CHECK(!strcmp(desc, "foo"), "Invalid node description."); free(desc); desc = NULL; testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar"); testres |= EC_TEST_CHECK_PARSE(node, -1, " foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); node = ec_node_str(EC_NO_ID, "Здравствуйте"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте"); testres |= EC_TEST_CHECK_PARSE(node, 1, "Здравствуйте", "John!"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, ""); ec_node_free(node); /* an empty string node always matches */ node = ec_node_str(EC_NO_ID, ""); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, ""); testres |= EC_TEST_CHECK_PARSE(node, 1, "", "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); ec_node_free(node); /* test completion */ node = ec_node_str(EC_NO_ID, "foo"); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE(node, EC_VA_END, EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "foo", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/node_subset.c������������������������������������������������������������������0000664�0000000�0000000�00000010344�15203622040�0017012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include "test.h" EC_TEST_MAIN() { struct ec_node *node; int testres = 0; node = EC_NODE_SUBSET( EC_NO_ID, EC_NODE_OR(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar")), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "toto") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 0); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 1, "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "titi"); testres |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 2, "bar", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 0, " "); testres |= EC_TEST_CHECK_PARSE(node, 0, "foox"); ec_node_free(node); /* test with min=1 (at least one child must match) */ node = EC_NODE_SUBSET( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "toto") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } ec_node_subset_set_min(node, 1); testres |= EC_TEST_CHECK_PARSE(node, -1); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto"); testres |= EC_TEST_CHECK_PARSE(node, -1, "x"); ec_node_free(node); /* test with min=len (all children must match) */ node = EC_NODE_SUBSET( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "toto") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } ec_node_subset_set_min(node, 3); testres |= EC_TEST_CHECK_PARSE(node, -1); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto"); testres |= EC_TEST_CHECK_PARSE(node, 3, "toto", "bar", "foo"); testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "toto", "bar", "x"); testres |= EC_TEST_CHECK_PARSE(node, -1, "x"); ec_node_free(node); /* test that a non-matching subset with min=1 does not shadow or branches */ { struct ec_node *subset; subset = EC_NODE_SUBSET( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "toto") ); if (subset == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } ec_node_subset_set_min(subset, 1); node = EC_NODE_OR(EC_NO_ID, subset, ec_node_str(EC_NO_ID, "id")); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_PARSE(node, 1, "id"); testres |= EC_TEST_CHECK_PARSE(node, 1, "foo"); testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar"); testres |= EC_TEST_CHECK_PARSE(node, -1, "x"); ec_node_free(node); } /* test completion */ node = EC_NODE_SUBSET( EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), ec_node_str(EC_NO_ID, "bar"), ec_node_str(EC_NO_ID, "bar2"), ec_node_str(EC_NO_ID, "toto"), ec_node_str(EC_NO_ID, "titi") ); if (node == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } testres |= EC_TEST_CHECK_COMPLETE( node, "", EC_VA_END, "foo", "bar", "bar2", "toto", "titi", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE( node, "", EC_VA_END, "bar2", "bar", "foo", "toto", "titi", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE( node, "bar", "bar2", "", EC_VA_END, "foo", "toto", "titi", EC_VA_END ); testres |= EC_TEST_CHECK_COMPLETE(node, "f", EC_VA_END, "foo", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "b", EC_VA_END, "bar", "bar2", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "bar", EC_VA_END, "bar", "bar2", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "bar", "b", EC_VA_END, "bar2", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "t", EC_VA_END, "toto", "titi", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "to", EC_VA_END, "toto", EC_VA_END); testres |= EC_TEST_CHECK_COMPLETE(node, "x", EC_VA_END, EC_VA_END); ec_node_free(node); return testres; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/parse.c������������������������������������������������������������������������0000664�0000000�0000000�00000004322�15203622040�0015611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_node *node = NULL; struct ec_pnode *p = NULL, *p2 = NULL; const struct ec_pnode *pc; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; int testres = 0; int ret; node = ec_node_sh_lex( EC_NO_ID, EC_NODE_SEQ(EC_NO_ID, ec_node_str("id_x", "x"), ec_node_str("id_y", "y")) ); if (node == NULL) goto fail; p = ec_parse(node, "xcdscds"); testres |= EC_TEST_CHECK(p != NULL && !ec_pnode_matches(p), "parse should not match\n"); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_pnode_dump(f, p); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "no match"), "bad dump\n"); free(buf); buf = NULL; ec_pnode_free(p); p = ec_parse(node, "x y"); testres |= EC_TEST_CHECK(p != NULL && ec_pnode_matches(p), "parse should match\n"); testres |= EC_TEST_CHECK(ec_pnode_len(p) == 1, "bad parse len\n"); ret = ec_dict_set(ec_pnode_get_attrs(p), "key", "val", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set parse attribute\n"); p2 = ec_pnode_dup(p); testres |= EC_TEST_CHECK(p2 != NULL && ec_pnode_matches(p2), "parse should match\n"); ec_pnode_free(p2); p2 = NULL; pc = ec_pnode_find(p, "id_x"); testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_x"); testres |= EC_TEST_CHECK( pc != NULL && ec_pnode_get_parent(pc) != NULL && ec_pnode_get_parent(ec_pnode_get_parent(pc)) == p, "invalid parent\n" ); pc = ec_pnode_find(p, "id_y"); testres |= EC_TEST_CHECK(pc != NULL, "cannot find id_y"); pc = ec_pnode_find(p, "id_dezdezdez"); testres |= EC_TEST_CHECK(pc == NULL, "should not find bad id"); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_pnode_dump(f, p); fclose(f); f = NULL; testres |= EC_TEST_CHECK( strstr(buf, "type=sh_lex id=") && strstr(buf, "type=seq id=") && strstr(buf, "type=str id=id_x") && strstr(buf, "type=str id=id_x"), "bad dump\n" ); free(buf); buf = NULL; ec_pnode_free(p); ec_node_free(node); return testres; fail: ec_pnode_free(p2); ec_pnode_free(p); ec_node_free(node); if (f != NULL) fclose(f); free(buf); return -1; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/string.c�����������������������������������������������������������������������0000664�0000000�0000000�00000004604�15203622040�0016010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2025, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "test.h" /* clang-format off */ const char *text = " \n\n dedddesde desd desd desdesd des desd des des des des\n" "dedddesde desd desd desdesd des desd des des des des\n" "dedddesde desd desd desdesd des desd des des des des\n" "dedddddddddddddddddddddddddddcccddddddddcdddddddddededes\n" "dedddesde desd desd desdesd des desd des des des des\n" "dedddesde desd desd desdesd des desd des des des des\n " "\n" "dedddesde desd desd desdesd des desd des des des des \n" "dedddddddddddddddddddddddddddcccddddddddcdddddddddededes\n" "dedddesde desd desd desdesd des desd des des des des\n\n \n \n" "dedddesde desd desd desdesd des desd des des des des\n" "dedddesde desd desd desdesd des desd des des des des\n" "\n" ; const char *wrapped_text = "dedddesde desd desd desdesd des desd des\n" " des des des dedddesde desd desd desdesd\n" " des desd des des des des dedddesde desd\n" " desd desdesd des desd des des des des\n" " " "dedddddddddddddddddddddddddddcccddddddddcdddddddddededes\n" " dedddesde desd desd desdesd des desd des\n" " des des des dedddesde desd desd desdesd\n" " des desd des des des des dedddesde desd\n" "\n" " desd desdesd des desd des des des des\n" " " "dedddddddddddddddddddddddddddcccddddddddcdddddddddededes\n" " dedddesde desd desd desdesd des desd des\n" " des des des dedddesde desd desd desdesd\n" "\n" " des desd des des des des dedddesde desd\n" " desd desdesd des desd des des des des" ; /* clang-format on */ EC_TEST_MAIN() { char *out = NULL; int testres = 0; out = ec_str_wrap(text, 72, 30); if (out == NULL) { EC_TEST_ERR("cannot to wrap text\n"); goto fail; } if (strcmp(out, wrapped_text)) { EC_TEST_ERR("text wrapped incorrectly\n"); goto fail; } free(out); out = NULL; return testres; fail: free(out); return -1; } ����������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/strvec.c�����������������������������������������������������������������������0000664�0000000�0000000�00000022417�15203622040�0016012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "test.h" EC_TEST_MAIN() { struct ec_strvec *strvec = NULL; struct ec_strvec *strvec2 = NULL; const struct ec_dict *const_attrs = NULL; struct ec_dict *attrs = NULL; FILE *f = NULL; char *buf = NULL; size_t buflen = 0; int testres = 0; char quote; strvec = ec_strvec(); if (strvec == NULL) { EC_TEST_ERR("cannot create strvec\n"); goto fail; } if (ec_strvec_len(strvec) != 0) { EC_TEST_ERR("bad strvec len (0)\n"); goto fail; } if (ec_strvec_add(strvec, "0") < 0) { EC_TEST_ERR("cannot add (0) in strvec\n"); goto fail; } if (ec_strvec_len(strvec) != 1) { EC_TEST_ERR("bad strvec len (1)\n"); goto fail; } if (ec_strvec_add(strvec, "1") < 0) { EC_TEST_ERR("cannot add (1) in strvec\n"); goto fail; } if (ec_strvec_len(strvec) != 2) { EC_TEST_ERR("bad strvec len (2)\n"); goto fail; } if (strcmp(ec_strvec_val(strvec, 0), "0")) { EC_TEST_ERR("invalid element in strvec (0)\n"); goto fail; } if (strcmp(ec_strvec_val(strvec, 1), "1")) { EC_TEST_ERR("invalid element in strvec (1)\n"); goto fail; } if (ec_strvec_val(strvec, 2) != NULL) { EC_TEST_ERR("strvec val should be NULL\n"); goto fail; } strvec2 = ec_strvec_dup(strvec); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec2\n"); goto fail; } if (ec_strvec_len(strvec2) != 2) { EC_TEST_ERR("bad strvec2 len (2)\n"); goto fail; } if (strcmp(ec_strvec_val(strvec2, 0), "0")) { EC_TEST_ERR("invalid element in strvec2 (0)\n"); goto fail; } if (strcmp(ec_strvec_val(strvec2, 1), "1")) { EC_TEST_ERR("invalid element in strvec2 (1)\n"); goto fail; } if (ec_strvec_val(strvec2, 2) != NULL) { EC_TEST_ERR("strvec2 val should be NULL\n"); goto fail; } ec_strvec_free(strvec2); strvec2 = ec_strvec_ndup(strvec, 0, 0); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec2\n"); goto fail; } if (ec_strvec_len(strvec2) != 0) { EC_TEST_ERR("bad strvec2 len (0)\n"); goto fail; } if (ec_strvec_val(strvec2, 0) != NULL) { EC_TEST_ERR("strvec2 val should be NULL\n"); goto fail; } ec_strvec_free(strvec2); strvec2 = ec_strvec_ndup(strvec, 1, 1); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec2\n"); goto fail; } if (ec_strvec_len(strvec2) != 1) { EC_TEST_ERR("bad strvec2 len (1)\n"); goto fail; } if (strcmp(ec_strvec_val(strvec2, 0), "1")) { EC_TEST_ERR("invalid element in strvec2 (1)\n"); goto fail; } if (ec_strvec_val(strvec2, 1) != NULL) { EC_TEST_ERR("strvec2 val should be NULL\n"); goto fail; } ec_strvec_free(strvec2); strvec2 = ec_strvec_ndup(strvec, 3, 1); if (strvec2 != NULL) { EC_TEST_ERR("strvec2 should be NULL\n"); goto fail; } ec_strvec_free(strvec2); strvec2 = EC_STRVEC("0", "1"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec2); f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_strvec_dump(f, strvec); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "strvec (len=2) [\"0\", \"1\"]"), "bad dump\n"); free(buf); buf = NULL; ec_strvec_del_last(strvec); strvec2 = EC_STRVEC("0"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec2); strvec2 = NULL; f = open_memstream(&buf, &buflen); if (f == NULL) goto fail; ec_strvec_dump(f, NULL); fclose(f); f = NULL; testres |= EC_TEST_CHECK(strstr(buf, "none"), "bad dump\n"); free(buf); buf = NULL; ec_strvec_free(strvec); strvec = EC_STRVEC("e", "a", "f", "d", "b", "c"); if (strvec == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } attrs = ec_dict(); if (attrs == NULL) { EC_TEST_ERR("cannot create attrs\n"); goto fail; } if (ec_dict_set(attrs, "key", "value", NULL) < 0) { EC_TEST_ERR("cannot set attr\n"); goto fail; } if (ec_strvec_set_attrs(strvec, 1, attrs) < 0) { attrs = NULL; EC_TEST_ERR("cannot set attrs in strvec\n"); goto fail; } attrs = NULL; ec_strvec_sort(strvec, NULL); /* attrs are now at index 0 after sorting */ const_attrs = ec_strvec_get_attrs(strvec, 0); if (const_attrs == NULL) { EC_TEST_ERR("cannot get attrs\n"); goto fail; } testres |= EC_TEST_CHECK(ec_dict_has_key(const_attrs, "key"), "cannot get attrs key\n"); strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; /* lexing */ strvec = ec_strvec_sh_lex_str(" a b\tc d # comment", EC_STRVEC_STRICT, NULL); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "b", "c", "d"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; strvec = ec_strvec_sh_lex_str(" a b c d ", EC_STRVEC_TRAILSP, NULL); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "b", "c", "d", ""); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); for (unsigned i = 0; i < ec_strvec_len(strvec); i++) { const_attrs = ec_strvec_get_attrs(strvec, i); testres |= EC_TEST_CHECK(const_attrs != NULL, "attrs should not be NULL\n"); if (const_attrs == NULL) continue; errno = 0; unsigned s = (uintptr_t)ec_dict_get(const_attrs, EC_STRVEC_ATTR_START); unsigned e = (uintptr_t)ec_dict_get(const_attrs, EC_STRVEC_ATTR_END); testres |= EC_TEST_CHECK(errno == 0, ""); switch (i) { case 0: testres |= EC_TEST_CHECK(s == 2 && e == 3, ""); break; case 1: testres |= EC_TEST_CHECK(s == 5 && e == 6, ""); break; case 2: testres |= EC_TEST_CHECK(s == 9 && e == 10, ""); break; case 3: testres |= EC_TEST_CHECK(s == 12 && e == 13, ""); break; case 4: testres |= EC_TEST_CHECK(s == 15 && e == 16, ""); break; } } ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; strvec = ec_strvec_sh_lex_str("a b 'c d' ", EC_STRVEC_STRICT, NULL); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "b", "c d"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; strvec = ec_strvec_sh_lex_str("a b\\ e \"c \\\" d\" ", EC_STRVEC_STRICT, NULL); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "b e", "c \" d"); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; strvec = ec_strvec_sh_lex_str("a b 'c d ", EC_STRVEC_STRICT, NULL); if (strvec != NULL) { testres |= EC_TEST_CHECK(strvec == NULL, "shlex should have failed\n"); ec_strvec_free(strvec); strvec = NULL; } else { testres |= EC_TEST_CHECK( errno == EBADMSG, "ec_strvec_shlex_str should report EBADMSG\n" ); } quote = '\0'; strvec = ec_strvec_sh_lex_str("a 'b' 'c d ", EC_STRVEC_TRAILSP, "e); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "b", "c d "); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); testres |= EC_TEST_CHECK(quote == '\'', "missing quote should be '\n"); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; quote = '\0'; strvec = ec_strvec_sh_lex_str("a 'b'\"x\" 'c d' ", EC_STRVEC_TRAILSP, "e); if (strvec == NULL) { EC_TEST_ERR("cannot lex strvec from string\n"); goto fail; } strvec2 = EC_STRVEC("a", "bx", "c d", ""); if (strvec2 == NULL) { EC_TEST_ERR("cannot create strvec from array\n"); goto fail; } testres |= EC_TEST_CHECK( ec_strvec_cmp(strvec, strvec2) == 0, "strvec and strvec2 should be equal\n" ); testres |= EC_TEST_CHECK(quote == '\0', "there should be no missing quote\n"); ec_strvec_free(strvec); strvec = NULL; ec_strvec_free(strvec2); strvec2 = NULL; return testres; fail: if (f != NULL) fclose(f); ec_dict_free(attrs); ec_strvec_free(strvec); ec_strvec_free(strvec2); free(buf); return -1; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/test.c�������������������������������������������������������������������������0000664�0000000�0000000�00000004676�15203622040�0015472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdarg.h> #include <stdio.h> #include <string.h> #include "test.h" EC_LOG_TYPE_REGISTER(test); int ec_test_check_parse(struct ec_node *tk, int expected, ...) { struct ec_pnode *p; struct ec_strvec *vec = NULL; const char *s; int ret = -1, match; va_list ap; va_start(ap, expected); /* build a string vector */ vec = ec_strvec(); if (vec == NULL) goto out; for (s = va_arg(ap, const char *); s != EC_VA_END; s = va_arg(ap, const char *)) { if (s == NULL) goto out; if (ec_strvec_add(vec, s) < 0) goto out; } p = ec_parse_strvec(tk, vec); if (p == NULL) { EC_LOG(EC_LOG_ERR, "parse is NULL\n"); } if (ec_pnode_matches(p)) match = ec_pnode_len(p); else match = -1; if (expected == match) { ret = 0; } else { EC_LOG(EC_LOG_ERR, "returned parse len (%d) does not match expected (%d)\n", match, expected); } ec_pnode_free(p); out: ec_strvec_free(vec); va_end(ap); return ret; } int ec_test_check_complete(struct ec_node *tk, enum ec_comp_type type, ...) { struct ec_comp *c = NULL; struct ec_strvec *vec = NULL; const char *s; int ret = 0; size_t count = 0; va_list ap; va_start(ap, type); /* build a string vector */ vec = ec_strvec(); if (vec == NULL) goto out; for (s = va_arg(ap, const char *); s != EC_VA_END; s = va_arg(ap, const char *)) { if (s == NULL) goto out; if (ec_strvec_add(vec, s) < 0) goto out; } c = ec_complete_strvec(tk, vec); if (c == NULL) { ret = -1; goto out; } /* for each expected completion, check it is there */ for (s = va_arg(ap, const char *); s != EC_VA_END; s = va_arg(ap, const char *)) { struct ec_comp_item *item; if (s == NULL) { ret = -1; goto out; } count++; /* only check matching completions */ EC_COMP_FOREACH (item, c, type) { const char *str = ec_comp_item_get_str(item); if (str != NULL && strcmp(str, s) == 0) break; } if (item == NULL) { EC_LOG(EC_LOG_ERR, "completion <%s> not in list\n", s); ret = -1; } } /* check if we have more completions (or less) than expected */ if (count != ec_comp_count(c, type)) { EC_LOG(EC_LOG_ERR, "returned nb_completion (%zu) does not match expected (%zu)\n", ec_comp_count(c, type), count); ec_comp_dump(stdout, c); ret = -1; } out: ec_strvec_free(vec); ec_comp_free(c); va_end(ap); return ret; } ������������������������������������������������������������������libecoli-0.11.6/test/test.h�������������������������������������������������������������������������0000664�0000000�0000000�00000010227�15203622040�0015464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ /** * @defgroup ecoli_test Test * @{ * * @brief Helpers for unit tests */ #pragma once #include <ecoli.h> /** @internal */ #define EC_TEST_MAIN() \ EC_LOG_TYPE_REGISTER(__file__); \ static void __attribute__((constructor, used)) __init(void) \ { \ ec_htable_force_seed(42); \ ec_init(); \ } \ static void __attribute__((destructor, used)) __exit(void) \ { \ ec_exit(); \ } \ int main(void) /** * expected == -1 means no match * @internal */ int ec_test_check_parse(struct ec_node *node, int expected, ...); /** * Fail a test with a message. * * @internal */ #define EC_TEST_ERR(fmt, ...) \ EC_LOG(EC_LOG_ERR, "%s:%d: error: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); /** * Verify a condition or fail a test with a message. * * @internal */ #define EC_TEST_CHECK(cond, fmt, ...) \ ({ \ int ret_ = 0; \ if (!(cond)) { \ EC_TEST_ERR("(" #cond ") is wrong. " fmt, ##__VA_ARGS__); \ ret_ = -1; \ } \ ret_; \ }) /** * node, input, [expected1, expected2, ...] * * @internal */ #define EC_TEST_CHECK_PARSE(node, args...) \ ({ \ int ret_ = ec_test_check_parse(node, args, EC_VA_END); \ if (ret_) \ EC_TEST_ERR("parse test failed"); \ ret_; \ }) /** @internal */ int ec_test_check_complete(struct ec_node *node, enum ec_comp_type type, ...); /** @internal */ #define EC_TEST_CHECK_COMPLETE(node, args...) \ ({ \ int ret_ = ec_test_check_complete(node, EC_COMP_FULL, args); \ if (ret_) \ EC_TEST_ERR("complete test failed"); \ ret_; \ }) /** @internal */ #define EC_TEST_CHECK_COMPLETE_PARTIAL(node, args...) \ ({ \ int ret_ = ec_test_check_complete(node, EC_COMP_PARTIAL, args); \ if (ret_) \ EC_TEST_ERR("complete test failed"); \ ret_; \ }) /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/vec.c��������������������������������������������������������������������������0000664�0000000�0000000�00000012350�15203622040�0015254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2016, Olivier MATZ <zer0@droids-corp.org> */ #include <stdlib.h> #include <string.h> #include "test.h" EC_LOG_TYPE_REGISTER(vec); static void str_free(void *elt) { char **s = elt; free(*s); } #define GOTO_FAIL \ do { \ EC_LOG(EC_LOG_ERR, "%s:%d: test failed\n", __FILE__, __LINE__); \ goto fail; \ } while (0) EC_TEST_MAIN() { struct ec_vec *vec = NULL; struct ec_vec *vec2 = NULL; uint8_t val8; uint16_t val16; uint32_t val32; uint64_t val64; void *valp; char *vals; /* uint8_t vector */ vec = ec_vec(sizeof(val8), 0, NULL, NULL); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_u8(vec, 0) < 0) GOTO_FAIL; if (ec_vec_add_u8(vec, 1) < 0) GOTO_FAIL; if (ec_vec_add_u8(vec, 2) < 0) GOTO_FAIL; /* should fail */ if (ec_vec_add_u16(vec, 3) == 0) GOTO_FAIL; if (ec_vec_add_u32(vec, 3) == 0) GOTO_FAIL; if (ec_vec_add_u64(vec, 3) == 0) GOTO_FAIL; if (ec_vec_add_ptr(vec, (void *)3) == 0) GOTO_FAIL; if (ec_vec_get(&val8, vec, 0) < 0) GOTO_FAIL; if (val8 != 0) GOTO_FAIL; if (ec_vec_get(&val8, vec, 1) < 0) GOTO_FAIL; if (val8 != 1) GOTO_FAIL; if (ec_vec_get(&val8, vec, 2) < 0) GOTO_FAIL; if (val8 != 2) GOTO_FAIL; /* duplicate the vector */ vec2 = ec_vec_dup(vec); if (vec2 == NULL) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 0) < 0) GOTO_FAIL; if (val8 != 0) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 1) < 0) GOTO_FAIL; if (val8 != 1) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 2) < 0) GOTO_FAIL; if (val8 != 2) GOTO_FAIL; ec_vec_free(vec2); vec2 = NULL; /* dup at offset 1 */ vec2 = ec_vec_ndup(vec, 1, 2); if (vec2 == NULL) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 0) < 0) GOTO_FAIL; if (val8 != 1) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 1) < 0) GOTO_FAIL; if (val8 != 2) GOTO_FAIL; ec_vec_free(vec2); vec2 = NULL; /* len = 0, duplicate is empty */ vec2 = ec_vec_ndup(vec, 2, 0); if (vec2 == NULL) GOTO_FAIL; if (ec_vec_get(&val8, vec2, 0) == 0) GOTO_FAIL; ec_vec_free(vec2); vec2 = NULL; /* bad dup args */ vec2 = ec_vec_ndup(vec, 10, 1); if (vec2 != NULL) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* uint16_t vector */ vec = ec_vec(sizeof(val16), 0, NULL, NULL); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_u16(vec, 0) < 0) GOTO_FAIL; if (ec_vec_add_u16(vec, 1) < 0) GOTO_FAIL; if (ec_vec_add_u16(vec, 2) < 0) GOTO_FAIL; /* should fail */ if (ec_vec_add_u8(vec, 3) == 0) GOTO_FAIL; if (ec_vec_get(&val16, vec, 0) < 0) GOTO_FAIL; if (val16 != 0) GOTO_FAIL; if (ec_vec_get(&val16, vec, 1) < 0) GOTO_FAIL; if (val16 != 1) GOTO_FAIL; if (ec_vec_get(&val16, vec, 2) < 0) GOTO_FAIL; if (val16 != 2) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* uint32_t vector */ vec = ec_vec(sizeof(val32), 0, NULL, NULL); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_u32(vec, 0) < 0) GOTO_FAIL; if (ec_vec_add_u32(vec, 1) < 0) GOTO_FAIL; if (ec_vec_add_u32(vec, 2) < 0) GOTO_FAIL; if (ec_vec_get(&val32, vec, 0) < 0) GOTO_FAIL; if (val32 != 0) GOTO_FAIL; if (ec_vec_get(&val32, vec, 1) < 0) GOTO_FAIL; if (val32 != 1) GOTO_FAIL; if (ec_vec_get(&val32, vec, 2) < 0) GOTO_FAIL; if (val32 != 2) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* uint64_t vector */ vec = ec_vec(sizeof(val64), 0, NULL, NULL); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_u64(vec, 0) < 0) GOTO_FAIL; if (ec_vec_add_u64(vec, 1) < 0) GOTO_FAIL; if (ec_vec_add_u64(vec, 2) < 0) GOTO_FAIL; if (ec_vec_get(&val64, vec, 0) < 0) GOTO_FAIL; if (val64 != 0) GOTO_FAIL; if (ec_vec_get(&val64, vec, 1) < 0) GOTO_FAIL; if (val64 != 1) GOTO_FAIL; if (ec_vec_get(&val64, vec, 2) < 0) GOTO_FAIL; if (val64 != 2) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* ptr vector */ vec = ec_vec(sizeof(valp), 0, NULL, NULL); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_ptr(vec, (void *)0) < 0) GOTO_FAIL; if (ec_vec_add_ptr(vec, (void *)1) < 0) GOTO_FAIL; if (ec_vec_add_ptr(vec, (void *)2) < 0) GOTO_FAIL; if (ec_vec_get(&valp, vec, 0) < 0) GOTO_FAIL; if (valp != (void *)0) GOTO_FAIL; if (ec_vec_get(&valp, vec, 1) < 0) GOTO_FAIL; if (valp != (void *)1) GOTO_FAIL; if (ec_vec_get(&valp, vec, 2) < 0) GOTO_FAIL; if (valp != (void *)2) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* string vector */ vec = ec_vec(sizeof(valp), 0, NULL, str_free); if (vec == NULL) GOTO_FAIL; if (ec_vec_add_ptr(vec, strdup("0")) < 0) GOTO_FAIL; if (ec_vec_add_ptr(vec, strdup("1")) < 0) GOTO_FAIL; if (ec_vec_add_ptr(vec, strdup("2")) < 0) GOTO_FAIL; if (ec_vec_get(&vals, vec, 0) < 0) GOTO_FAIL; if (vals == NULL || strcmp(vals, "0")) GOTO_FAIL; if (ec_vec_get(&vals, vec, 1) < 0) GOTO_FAIL; if (vals == NULL || strcmp(vals, "1")) GOTO_FAIL; if (ec_vec_get(&vals, vec, 2) < 0) GOTO_FAIL; if (vals == NULL || strcmp(vals, "2")) GOTO_FAIL; ec_vec_free(vec); vec = NULL; /* invalid args */ vec = ec_vec(0, 0, NULL, NULL); if (vec != NULL) GOTO_FAIL; return 0; fail: ec_vec_free(vec); ec_vec_free(vec2); return -1; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libecoli-0.11.6/test/yaml.c�������������������������������������������������������������������������0000664�0000000�0000000�00000005554�15203622040�0015451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2026, Free Mobile, Vincent Jardin <vjardin@free.fr> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "test.h" /* * Test YAML import and export round-trip. * * This test verifies that: * 1. A node tree can be exported to YAML * 2. The exported YAML can be imported back * 3. The re-imported node parses the same inputs */ EC_TEST_MAIN() { struct ec_node *node1 = NULL, *node2 = NULL; struct ec_pnode *p1 = NULL, *p2 = NULL; char tmpfile[] = "/tmp/ecoli_yaml_test_XXXXXX"; FILE *fp = NULL; int fd = -1; int testres = 0; /* Test 1: export/import roundtrip */ /* Build a simple grammar: "hello" | "world" */ node1 = EC_NODE_OR( EC_NO_ID, ec_node_str("hello_id", "hello"), ec_node_str("world_id", "world") ); if (node1 == NULL) { EC_LOG(EC_LOG_ERR, "cannot create node\n"); return -1; } /* Create a temporary file for export */ fd = mkstemp(tmpfile); if (fd < 0) { EC_LOG(EC_LOG_ERR, "cannot create temp file\n"); ec_node_free(node1); return -1; } fp = fdopen(fd, "w"); if (fp == NULL) { EC_LOG(EC_LOG_ERR, "cannot fdopen temp file\n"); close(fd); unlink(tmpfile); ec_node_free(node1); return -1; } /* Export the node to YAML */ if (ec_yaml_export(fp, node1) < 0) { EC_LOG(EC_LOG_ERR, "cannot export node to YAML\n"); fclose(fp); unlink(tmpfile); ec_node_free(node1); return -1; } fclose(fp); /* Re-import the exported YAML */ node2 = ec_yaml_import(tmpfile); if (node2 == NULL) { EC_LOG(EC_LOG_ERR, "cannot re-import exported YAML\n"); unlink(tmpfile); ec_node_free(node1); return -1; } /* Verify both nodes parse "hello" */ p1 = ec_parse(node1, "hello"); p2 = ec_parse(node2, "hello"); testres |= EC_TEST_CHECK(p1 != NULL && ec_pnode_matches(p1), "node1 should match 'hello'"); testres |= EC_TEST_CHECK(p2 != NULL && ec_pnode_matches(p2), "node2 should match 'hello'"); ec_pnode_free(p1); ec_pnode_free(p2); /* Verify both nodes parse "world" */ p1 = ec_parse(node1, "world"); p2 = ec_parse(node2, "world"); testres |= EC_TEST_CHECK(p1 != NULL && ec_pnode_matches(p1), "node1 should match 'world'"); testres |= EC_TEST_CHECK(p2 != NULL && ec_pnode_matches(p2), "node2 should match 'world'"); ec_pnode_free(p1); ec_pnode_free(p2); /* Verify both nodes reject invalid input */ p1 = ec_parse(node1, "invalid"); p2 = ec_parse(node2, "invalid"); testres |= EC_TEST_CHECK( p1 == NULL || !ec_pnode_matches(p1), "node1 should not match 'invalid'" ); testres |= EC_TEST_CHECK( p2 == NULL || !ec_pnode_matches(p2), "node2 should not match 'invalid'" ); ec_pnode_free(p1); ec_pnode_free(p2); ec_node_free(node1); ec_node_free(node2); unlink(tmpfile); /* Test 2: export handles NULL arguments */ testres |= EC_TEST_CHECK( ec_yaml_export(NULL, NULL) < 0, "export should fail with NULL arguments" ); return testres; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������