This is an automated email from the ASF dual-hosted git repository.
jinterrante pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/daffodil.git
The following commit(s) were added to refs/heads/master by this push:
new a016ae6 Fix some runtime2 todos
a016ae6 is described below
commit a016ae6a1d59580ff050a8f7310a9f57ec874bd4
Author: John Interrante <[email protected]>
AuthorDate: Thu Apr 29 12:47:48 2021 -0400
Fix some runtime2 todos
Parse C executable's command line arguments with getopt instead of
argp (now have to put all options before first non-option argument and
lost ability to say "daffodil parse in.dat -o out.xml", but CLI was
meant for TDML runner rather than users anyway).
Update build instructions and CI workflow to build with clang instead
of gcc. Fix MSYS2 portability problems exposed by change (e.g., no
error() function).
Lower minimum required Mini-XML version from 3.2 to 3.0. Add
missing LICENSE and NOTICE files to daffodil-runtime2 jar.
Move everything CLI-related in libruntime out to libcli. Make error
lookup mechanism pluggable with a hook to let libcli insert its own
error lookup routine. Also make changes and move files to help
automate clang-format, include-what-you-use, and generated_code.[ch]
updates later.
DAFFODIL-2500, DAFFODIL-2505, DAFFODIL-2508
---
main.yml: Build C files with clang instead of gcc, but build mxml with
gcc on MYSYS2 since runtime2 tests break with mxml compiled by
clang. Also need diffutils on MSYS2 to avoid mxml makefile calling
Cygwin's cmp with unnecessary error message.
BUILD.md: Lower Mini-XML version to 3.0. Remove argp and gcc
instructions. Add clang instructions. Show how to set env vars on
one line. On MSYS2, install diffutils to let mxml makefile call cmp.
README.md: Lower Mini-XML version to 3.0.
.clang-format: Add StatementsMacros to allow clang-format to format
parsers.c and unparsers.c without unexpected indentation. Also make
all C files tell clang-format to leave include lines alone to prevent
clang-format from interfering with include-what-you-use (SortIncludes:
false isn't sufficient since clang-format still re-indents comments):
// clang-format off
#include ... // for ...
// clang-format on
LICENSE: Add to daffdil-runtime2 jar.
NOTICE: Add to daffdil-runtime2 jar.
Makefile: Add comments explaining how to use targets. Rename tests to
check target for consistency with GNU make standard. Put options
before non-option arguments in daffodil commands.
All C files: Tell clang-format to leave includes alone so it won't
interfere with include-what-you-use.
cli_errors.[ch]: Move all error codes and messages used by libcli
here. Use lookup tables and pluggable mechanism to allow libruntime
to look up and format libcli messages. Move/rename those CLI-related
enum constants here from libruntime:
- ERR_FILE_CLOSE (all ERR_* renamed to CLI_*)
- ERR_FILE_OPEN
- ERR_INFOSET_READ/WRITE (-> CLI_INVALD_INFOSET)
- ERR_STACK_EMPTY
- ERR_STACK_OVERFLOW
- ERR_STACK_UNDERFLOW
- ERR_STRTOBOOL
- ERR_STRTOD_ERRNO
- ERR_STRTOI_ERRNO
- ERR_STRTONUM_EMPTY
- ERR_STRTONUM_NOT
- ERR_STRTONUM_RANGE
- ERR_XML_DECL
- ERR_XML_ELEMENT
- ERR_XML_ERD
- ERR_XML_GONE
- ERR_XML_INPUT
- ERR_XML_LEFT
- ERR_XML_MISMATCH
- ERR_XML_WRITE
- LIMIT_XML_NESTING
daffodil_argp.[ch]: Rename to daffodil_getopt.[ch].
daffodil_getopt.c: Remove all argp structs and handlers. Simplify to
just a single daffodil_parse_cli function calling getopt and returning
a pointer to Error if any error happens. Note getopt has no portable
way to parse "daffodil [options] command [more options] arguments" so
callers now have to put all options before first non-option argument
("daffodil [options] command arguments").
daffodil_getopt.h: Include "errors.h" and make parse_daffodil_cli
return a pointer to Error so we can use continue_or_exit(error) for
all errors/messages.
daffodil_main.c: Remove fflush_continue_or_exit (no really good reason
to flush a stream right before closing it). Call continue_or_exit
after parse_daffodil_cli to handle any CLi error. Simplify rest of
code in main function. Rename error enumerations (ERR -> CLI) and
initialize c field with 0 instead of s field with NULL since we now
sort Error fields alphabetically (also needed in stack.c,
xml_reader.c, xml_writer.c).
errors.c: Remove <error.h> and replace error calls with fprintf/exit
calls since error's a GNU function which MYSYS2 clang doesn't provide.
Move eof_or_error, get_diagnostics, add_diagnostics up so they come
first in file. Replace error_message function containing switch
statement with error_lookup function indexing lookup table and
returning ErrorLookup structs. Change print_maybe_stop function to
call both error_lookup and cli_error_lookup (pluggable mechanism used
to look up CLI errors) and switch on ErrorField enumerations instead
of ErrorCode enumerations to print formatted messages with appropriate
Error fields. Add check_error_lookup function (after you rearrange
codes or messages or get an assertion failure in error_lookup, call
check_error_lookup from the debugger to check all codes map to
expected messages).
errors.h: Move CLI-related errors to cli_errors.h. Add ERR_ZZZ
enumeration to allow libcli's first error code to be numbered
consecutively following libruntime's last error code without a gap
between them. Add "int c" member to ErrorCode anonymous union for
getopt-related errors. Add ErrorField enumerations and ErrorLookup
struct to allow print_maybe_stop to print libcli's messages without
hardcoding any knowledge about them. Sort Error's fields
alphabetically. Move PState and UState structs back to infoset.h
where they really belong. Declare daffodil_program_version (we were
using argp_program_version before which is no longer available) and
move eof_or_error up before get_diagnostics. Declare cli_error_lookup
pluggable mechanism to allow print_maybe_stop to look up libcli's
messages without knowing anythng about them.
infoset.h: Move PState and UState structs back here.
parsers.c, unparsers.c: Initialize c field with 0 and use explicit .s
identifier to initialize s field since we now sort Error fields
alphabetically.
unparsers.h: Format from 80 to 100 columns with clang-format (missed
this one before).
CodeGenerator.scala: Pass relative/*.c filenames instead of absolute
filenames on Windows to avoid problem running MSYS2 clang compiler on
Windows. Remove "-largp" since we no longer need it on MSYS2.
Reorder pickCompiler's list of compilers to prefer ${CC}, cc, clang,
gcc in that order.
Runtime2DataProcessor.scala: Call executable with -o outfile before
parse or unparse in CLI command lines.
CodeGeneratorState.scala: Initialize c field with 0 since we now sort
Error fields alphabetically. Replace argp_program_version with
daffodil_program_version since we no longer use "argp.h".
NestedUnion.[ch] -> generated_code.[ch]: Move and regenerate with code
generator without renaming or manual editing to enable automated
update of generated_code.[ch] examples with daffodil's C code
generator in future.
ex_nums.[ch] -> generated_code.[ch]: Move and regenerate with code
generator without renaming or manual editing to enable automated
update of generated_code.[ch] examples with daffodil's C code
generator in future.
Rat.scala: Ignore generated_code.[ch] examples since daffodil's C code
generator doesn't include Apache license when generating them.
---
.github/workflows/main.yml | 11 +-
BUILD.md | 56 ++--
README.md | 2 +-
daffodil-runtime2/src/main/resources/.clang-format | 2 +
.../src/main/resources/META-INF/LICENSE | 202 ++++++++++++++
.../src/main/resources/META-INF/NOTICE | 10 +
.../org/apache/daffodil/runtime2/c/Makefile | 56 ++--
.../apache/daffodil/runtime2/c/libcli/cli_errors.c | 104 +++++++
.../apache/daffodil/runtime2/c/libcli/cli_errors.h | 67 +++++
.../daffodil/runtime2/c/libcli/daffodil_argp.c | 300 ---------------------
.../daffodil/runtime2/c/libcli/daffodil_getopt.c | 161 +++++++++++
.../libcli/{daffodil_argp.h => daffodil_getopt.h} | 25 +-
.../daffodil/runtime2/c/libcli/daffodil_main.c | 149 ++++------
.../org/apache/daffodil/runtime2/c/libcli/stack.c | 15 +-
.../org/apache/daffodil/runtime2/c/libcli/stack.h | 2 +
.../apache/daffodil/runtime2/c/libcli/xml_reader.c | 68 ++---
.../apache/daffodil/runtime2/c/libcli/xml_reader.h | 2 +
.../apache/daffodil/runtime2/c/libcli/xml_writer.c | 24 +-
.../apache/daffodil/runtime2/c/libcli/xml_writer.h | 2 +
.../apache/daffodil/runtime2/c/libruntime/errors.c | 217 +++++++--------
.../apache/daffodil/runtime2/c/libruntime/errors.h | 103 ++++---
.../daffodil/runtime2/c/libruntime/infoset.c | 2 +
.../daffodil/runtime2/c/libruntime/infoset.h | 27 +-
.../daffodil/runtime2/c/libruntime/parsers.c | 8 +-
.../daffodil/runtime2/c/libruntime/parsers.h | 4 +-
.../daffodil/runtime2/c/libruntime/unparsers.c | 6 +-
.../daffodil/runtime2/c/libruntime/unparsers.h | 28 +-
.../apache/daffodil/runtime2/CodeGenerator.scala | 20 +-
.../daffodil/runtime2/Runtime2DataProcessor.scala | 4 +-
.../runtime2/generators/CodeGeneratorState.scala | 8 +-
.../examples/NestedUnion/generated_code.c} | 27 +-
.../examples/NestedUnion/generated_code.h} | 17 --
.../runtime2/examples/ex_nums/generated_code.c} | 21 +-
.../runtime2/examples/ex_nums/generated_code.h} | 17 --
project/Rat.scala | 3 +
35 files changed, 987 insertions(+), 783 deletions(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 24fbd81..07b86e5 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -29,14 +29,20 @@ jobs:
include:
- os: ubuntu-20.04
shell: bash
+ env_cc: clang-10
+ env_ar: llvm-ar-10
- os: windows-2019
shell: msys2 {0}
+ env_cc: clang
+ env_ar: llvm-ar
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.shell }}
env:
+ AR: ${{ matrix.env_ar }}
+ CC: ${{ matrix.env_cc }}
SBT: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m
-J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }} coverage
SBTNOCOV: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m
-J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }}
steps:
@@ -53,7 +59,7 @@ jobs:
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
- install: gcc libargp-devel make pkgconf
+ install: clang diffutils make pkgconf
path-type: inherit
- name: Check out mxml source (Windows)
@@ -67,9 +73,10 @@ jobs:
- name: Install mxml library (Windows)
if: runner.os == 'Windows'
run: |
+ # Our runtime2 tests may break if mxml library is compiled with clang
+ export AR=ar CC=cc
cd mxml
./configure --prefix=/usr --disable-shared --disable-threads
- make
make install
# Workaround for sbt hanging problem
echo "COURSIER_CACHE=$temp" >> $GITHUB_ENV
diff --git a/BUILD.md b/BUILD.md
index 784e648..13a38e3 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -22,7 +22,7 @@ Daffodil's build requirements include:
* JDK 8 or higher
* SBT 0.13.8 or higher
* C compiler C99 or higher
-* Mini-XML Version 3.2 or higher
+* Mini-XML Version 3.0 or higher
You will need the Java Software Development Kit ([JDK]) and the Scala
Build Tool ([SBT]) to build Daffodil, run all tests, create packages,
@@ -31,26 +31,29 @@ the latest [SBT] version following their websites'
instructions or
install them using your operating system's package manager.
Since Daffodil now has a C backend as well as a Scala backend, you
-will need a C compiler supporting the [C99] standard or later, the
-[Mini-XML] library, and possibly the GNU [argp] library if your
-system's C library doesn't include it already. You can install either
-[gcc] or [clang] using your operating system's package manager. If
-you can't install the [Mini-XML] library using your operating system's
-package manager, you'll have to build it from source. We'll tell you
-how to do that on Windows but the commands should work on other
-operating systems too.
-
-You can set your environment variables "CC" and "AR" to the correct
+will need a C compiler supporting the [C99] standard or later and the
+[Mini-XML] library. You can install either [gcc] or [clang] using
+your operating system's package manager. If you can't install the
+[Mini-XML] library using your operating system's package manager,
+you'll have to build it from source. We'll tell you how to do that on
+Windows but the commands should work on other operating systems too.
+
+You can set your environment variables `CC` and `AR` to the correct
commands (or set them to `true` to disable C compilation altogether)
if you don't want `sbt compile` to call your C compiler with `cc` and
`ar` as the default commands.
-## Fedora 33
+## Fedora 34
-You can use the `dnf` package manager to install most of the build
-requirements needed to build Daffodil:
+You can use the `dnf` package manager to install most of the tools
+needed to build Daffodil:
- sudo dnf install gcc git java-11-openjdk-devel make mxml-devel pkgconf
+ sudo dnf install clang git java-11-openjdk-devel llvm make mxml-devel
pkgconf
+
+If you want to use clang instead of gcc, you'll have to set your
+environment variables `CC` and `AR` to the clang binaries' names:
+
+ export CC=clang AR=llvm-ar
However, Fedora has no sbt package in its default repositories.
You'll have to install the latest [SBT] version following its
@@ -61,10 +64,15 @@ commands you type will be able to call the C compiler.
## Ubuntu 20.04
-You can use the `apt` package manager to install most of the build
-requirements needed to build Daffodil:
+You can use the `apt` package manager to install most of the tools
+needed to build Daffodil:
+
+ sudo apt install build-essential clang-10 clang-format-10 default-jdk git
libmxml-dev
- sudo apt install build-essential default-jdk git libmxml-dev
+If you want to use clang instead of gcc, you'll have to set your
+environment variables `CC` and `AR` to the clang binaries' names:
+
+ export CC=clang-10 AR=llvm-ar-10
However, Ubuntu has no sbt package in its default repositories.
You'll have to install the latest [SBT] version following its
@@ -82,10 +90,15 @@ Install [MSYS2] following its website's instructions and
open a new
"MSYS2 MSYS" window. We'll need its collection of free programs and
libraries.
-Install [gcc] and [libargp][argp] using MSYS2's `pacman` package
-manager:
+You can use the `pacman` package manager to install most of the tools
+needed to build Daffodil:
+
+ pacman -S clang diffutils git make pkgconf
+
+If you want to use clang instead of gcc, you'll have to set your
+environment variables `CC` and `AR` to the clang binaries' names:
- pacman -S gcc git libargp-devel make pkgconf
+ export CC=clang AR=llvm-ar
However, MSYS2 has no [libmxml-devel][Mini-XML] package so you'll have
to build the [Mini-XML] library from source:
@@ -109,6 +122,5 @@ will be able to call the C compiler.
[Mini-XML]: https://www.msweet.org/mxml/
[MSYS2]: https://www.msys2.org/
[SBT]: https://www.scala-sbt.org/
-[argp]: https://packages.msys2.org/package/libargp-devel
[clang]: https://clang.llvm.org/get_started.html
[gcc]: https://linuxize.com/post/how-to-install-gcc-on-ubuntu-20-04/
diff --git a/README.md b/README.md
index ad7982f..926261b 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ For more information about Daffodil, see
<https://daffodil.apache.org/>.
* JDK 8 or higher
* SBT 0.13.8 or higher
* C compiler C99 or higher
-* Mini-XML Version 3.2 or higher
+* Mini-XML Version 3.0 or higher
See [BUILD.md](BUILD.md) for more details.
diff --git a/daffodil-runtime2/src/main/resources/.clang-format
b/daffodil-runtime2/src/main/resources/.clang-format
index 908818d..f388d38 100644
--- a/daffodil-runtime2/src/main/resources/.clang-format
+++ b/daffodil-runtime2/src/main/resources/.clang-format
@@ -22,3 +22,5 @@ BreakBeforeBraces: Allman
ColumnLimit: 110
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
+SortIncludes: false
+StatementMacros: ['define_parse_endian_bool', 'define_parse_endian_real',
'define_parse_endian_integer', 'define_unparse_endian_bool',
'define_unparse_endian_real', 'define_unparse_endian_integer']
diff --git a/daffodil-runtime2/src/main/resources/META-INF/LICENSE
b/daffodil-runtime2/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/daffodil-runtime2/src/main/resources/META-INF/NOTICE
b/daffodil-runtime2/src/main/resources/META-INF/NOTICE
new file mode 100644
index 0000000..ee4c6eb
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,10 @@
+Apache Daffodil
+Copyright 2021 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Based on source code originally developed by
+- The Univerisity of Illinois National Center for Supercomputing Applications
(http://www.ncsa.illinois.edu/)
+- Tresys Technology (http://www.tresys.com/)
+- International Business Machines Corporation (http://www.ibm.com)
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/Makefile
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/Makefile
index d8b9e90..d4687df 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/Makefile
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/Makefile
@@ -14,42 +14,56 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Here's how to compile the C sources into a program for running
-# parse/unparse tests (.dat <-> .xml)
+# Step 0: You will need to install the Mini-XML library and xmldiff.
+# Here's how to install both packages on Ubuntu 20.04 (first-time
+# setup only):
+
+# $ sudo apt install libmxml-dev xmldiff
+
+# Step 1: Copy your test data files here and either rename them to
+# parse.dat and unparse.xml or set PARSE_DAT and UNPARSE_XML.
+
+# $ cp ../ex_nums_parse.dat parse.dat
+# $ cp ../ex_nums_unparse_runtime2.xml unparse.xml
+
+PARSE_DAT = parse.dat
+UNPARSE_XML = unparse.xml
+
+# Step 2: Compile the C source files into an executable program which
+# can run the parse and unparse checks (e.g., .dat <-> .xml).
+
+# $ make
PROGRAM = ./daffodil
HEADERS = libcli/*.h libruntime/*.h
SOURCES = libcli/*.c libruntime/*.c
-INCLUDES = -I libcli -I libruntime
+INCLUDES = -Ilibcli -Ilibruntime
CFLAGS = -g -Wall -Wextra
LIBS = -lmxml
$(PROGRAM): $(HEADERS) $(SOURCES)
$(CC) $(CFLAGS) $(INCLUDES) $(SOURCES) $(LIBS) -o $(PROGRAM)
-# Here's how to run parse/unparse tests (.dat <-> .xml, although you
-# will need to create the .dat and .xml files first)
+# Step 3: Run the executable on the test data files and check that the
+# new temp data files match the original test data files.
-PARSE_DAT = parse.dat
-UNPARSE_XML = unparse.xml
+# $ make check
-clean:
- rm -f $(PROGRAM) test_$(PARSE_DAT) test_$(UNPARSE_XML)
+check: parse-check unparse-check
-tests: parse-test unparse-test
+parse-check: $(PROGRAM)
+ $(PROGRAM) -o temp_$(UNPARSE_XML) parse $(PARSE_DAT)
+ xmldiff $(UNPARSE_XML) temp_$(UNPARSE_XML)
-parse-test: $(PROGRAM)
- $(PROGRAM) parse $(PARSE_DAT) -o test_$(UNPARSE_XML)
- xmldiff $(UNPARSE_XML) test_$(UNPARSE_XML)
+unparse-check: $(PROGRAM)
+ $(PROGRAM) -o temp_$(PARSE_DAT) unparse $(UNPARSE_XML)
+ diff $(PARSE_DAT) temp_$(PARSE_DAT)
-unparse-test: $(PROGRAM)
- $(PROGRAM) unparse $(UNPARSE_XML) -o test_$(PARSE_DAT)
- diff $(PARSE_DAT) test_$(PARSE_DAT)
+# Step 4 (optional): Remove the executable and temp data files.
-# You will need the Mini-XML library and xmldiff - here's how to
-# install both in Ubuntu 20.04
+# $ make clean
-deps:
- sudo apt install libmxml-dev xmldiff
+clean:
+ rm -f $(PROGRAM) temp_$(PARSE_DAT) temp_$(UNPARSE_XML)
-.PHONY: deps tests parse-test unparse-test
+.PHONY: check parse-check unparse-check clean
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.c
new file mode 100644
index 0000000..e069b0d
--- /dev/null
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.c
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+#include "cli_errors.h"
+#include <assert.h> // for assert
+#include <inttypes.h> // for PRId64
+#include <stddef.h> // for NULL
+#include <stdint.h> // for uint8_t
+// clang-format on
+
+// USAGE - second line to append to CLI usage messages
+
+#define USAGE "Try using `-h` for more information\n"
+
+// error_lookup - look up an internationalized error message
+
+static const ErrorLookup *
+error_lookup(uint8_t code)
+{
+ static const ErrorLookup table[CLI_ZZZ - ERR_ZZZ] = {
+ {CLI_FILE_CLOSE, "error closing file\n", FIELD_ZZZ},
+ {CLI_FILE_OPEN, "error opening file '%s'\n", FIELD_S},
+ {CLI_HELP_USAGE,
+ "Usage: %s [OPTION...] <command> [infile]\n"
+ "\n"
+ "Options:\n"
+ " -h Give this help list\n"
+ " -I Infoset type to write or read. Must be 'xml'\n"
+ " -o Write output to file. If not given or is -,\n"
+ " output is written to stdout\n"
+ " -V Print program version\n"
+ "\n"
+ "Commands:\n"
+ " parse Parse a data file to an infoset file\n"
+ " unparse Unparse an infoset file to a data file\n"
+ "\n"
+ "Argument:\n"
+ " infile Input file to parse or unparse. If not specified,\n"
+ " or a value of -, reads from stdin\n",
+ FIELD_S},
+ {CLI_INVALID_COMMAND, "invalid command -- '%s'\n" USAGE, FIELD_S},
+ {CLI_INVALID_INFOSET, "invalid infoset type -- '%s'\n" USAGE, FIELD_S},
+ {CLI_INVALID_OPTION, "invalid option -- '%c'\n" USAGE, FIELD_C},
+ {CLI_MISSING_COMMAND, "missing command\n" USAGE, FIELD_ZZZ},
+ {CLI_MISSING_VALUE, "option requires an argument -- '%c'\n" USAGE,
FIELD_C},
+ {CLI_PROGRAM_ERROR,
+ "unexpected getopt code %" PRId64 "\n"
+ "Check for program error\n",
+ FIELD_D64},
+ {CLI_PROGRAM_VERSION, "%s\n", FIELD_S},
+ {CLI_STACK_EMPTY, "stack empty, stopping program\n", FIELD_ZZZ},
+ {CLI_STACK_OVERFLOW, "stack overflow, stopping program\n", FIELD_ZZZ},
+ {CLI_STACK_UNDERFLOW, "stack underflow, stopping program\n",
FIELD_ZZZ},
+ {CLI_STRTOBOOL, "error converting XML data '%s' to boolean\n",
FIELD_S},
+ {CLI_STRTOD_ERRNO, "error converting XML data '%s' to number\n",
FIELD_S},
+ {CLI_STRTOI_ERRNO, "error converting XML data '%s' to integer\n",
FIELD_S},
+ {CLI_STRTONUM_EMPTY, "found no number in XML data '%s'\n", FIELD_S},
+ {CLI_STRTONUM_NOT, "found non-number characters in XML data '%s'\n",
FIELD_S},
+ {CLI_STRTONUM_RANGE, "number in XML data '%s' out of range\n",
FIELD_S},
+ {CLI_UNEXPECTED_ARGUMENT, "unexpected extra argument -- '%s'\n" USAGE,
FIELD_S},
+ {CLI_XML_DECL, "error making new XML declaration\n", FIELD_ZZZ},
+ {CLI_XML_ELEMENT, "error making new XML element '%s'\n", FIELD_S},
+ {CLI_XML_ERD, "unexpected ERD typeCode %" PRId64 " while reading XML
data\n", FIELD_D64},
+ {CLI_XML_GONE, "ran out of XML data\n", FIELD_ZZZ},
+ {CLI_XML_INPUT, "unable to read XML data from input file\n",
FIELD_ZZZ},
+ {CLI_XML_LEFT, "did not consume all of the XML data, '%s' left\n",
FIELD_S},
+ {CLI_XML_MISMATCH, "found mismatch between XML data and infoset
'%s'\n", FIELD_S},
+ {CLI_XML_WRITE, "error writing XML document\n", FIELD_ZZZ},
+ };
+
+ if (code >= ERR_ZZZ && code < CLI_ZZZ)
+ {
+ const ErrorLookup *lookup = &table[code - ERR_ZZZ];
+
+ // Double check that we looked up correct row
+ assert(code == lookup->code);
+
+ return lookup;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Initialize pluggable error lookup mechanism to let libruntime print
+// our CLI errors as well as its own errors
+
+cli_error_lookup_t *cli_error_lookup = &error_lookup;
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.h
new file mode 100644
index 0000000..dbd16ec
--- /dev/null
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/cli_errors.h
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CLI_ERRORS_H
+#define CLI_ERRORS_H
+
+// clang-format off
+#include "errors.h" // for ERR_ZZZ
+// clang-format on
+
+// CliCode - identifiers of libcli errors
+
+enum CliCode
+{
+ CLI_FILE_CLOSE = ERR_ZZZ,
+ CLI_FILE_OPEN,
+ CLI_HELP_USAGE,
+ CLI_INVALID_COMMAND,
+ CLI_INVALID_INFOSET,
+ CLI_INVALID_OPTION,
+ CLI_MISSING_COMMAND,
+ CLI_MISSING_VALUE,
+ CLI_PROGRAM_ERROR,
+ CLI_PROGRAM_VERSION,
+ CLI_STACK_EMPTY,
+ CLI_STACK_OVERFLOW,
+ CLI_STACK_UNDERFLOW,
+ CLI_STRTOBOOL,
+ CLI_STRTOD_ERRNO,
+ CLI_STRTOI_ERRNO,
+ CLI_STRTONUM_EMPTY,
+ CLI_STRTONUM_NOT,
+ CLI_STRTONUM_RANGE,
+ CLI_UNEXPECTED_ARGUMENT,
+ CLI_XML_DECL,
+ CLI_XML_ELEMENT,
+ CLI_XML_ERD,
+ CLI_XML_GONE,
+ CLI_XML_INPUT,
+ CLI_XML_LEFT,
+ CLI_XML_MISMATCH,
+ CLI_XML_WRITE,
+ CLI_ZZZ,
+};
+
+// CliLimits - limits on how many elements static arrays can hold
+
+enum CliLimits
+{
+ LIMIT_XML_NESTING = 100, // limits how deep infoset elements can nest
+};
+
+#endif // CLI_ERRORS_H
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.c
deleted file mode 100644
index fa65c4b..0000000
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "daffodil_argp.h"
-#include <argp.h> // for argp_state, argp_error, error_t, argp_parse,
ARGP_ERR_UNKNOWN, ARGP_IN_ORDER, ARGP_KEY_ARG, argp, argp_option, ARGP_KEY_END
-#include <stdio.h> // for sprintf, NULL
-#include <stdlib.h> // for putenv
-#include <string.h> // for strlen, strcmp
-
-// Initialize our "daffodil parse" CLI options
-
-struct daffodil_parse_cli daffodil_parse = {
- "xml", // default infoset type
- "-", // default infile
- "-", // default outfile
-};
-
-static const struct argp_option parse_options[] = {
- {"infoset-type", 'I', "<infoset_type>", 0, "Infoset type to output. Must
be one of 'xml' or 'null'", 0},
-
- {"output", 'o', "<file>", 0,
- "Write output to a given file. If not given or is -, output is written to
"
- "stdout",
- 0},
-
- {0}};
-
-static error_t parse_handler(int key, char *arg, struct argp_state *state);
-
-static const char parse_args_doc[] = "[infile]";
-
-static const char parse_doc[] = "\n"
- "Parse a file using a DFDL schema\n"
- "\n"
- "Parse Options:"
- "\v"
- " Trailing arguments:\n"
- " infile (not required) input file to
parse. "
- "If not specified, or a value of -, reads from
stdin";
-
-static const struct argp parse_argp = {
- parse_options, // array of CLI options
- parse_handler, // function to get these CLI options
- parse_args_doc, // short usage documentation
- parse_doc, // long help documentation
- 0, // child argps parsed after this argp
- 0, // function to replace help messages
- 0 // domain name for translation lookup
-};
-
-// Handle callbacks to get our "daffodil parse" CLI options
-
-static error_t
-parse_handler(int key, char *arg, struct argp_state *state)
-{
- struct daffodil_parse_cli *parse = state->input;
-
- switch (key)
- {
- case 'I':
- parse->infoset_converter = arg;
- break;
-
- case 'o':
- parse->outfile = arg;
- break;
-
- case ARGP_KEY_ARG:
- if (state->arg_num)
- {
- argp_error(state, "too many arguments: %s", arg);
- }
- parse->infile = arg;
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
- }
-
- return 0;
-}
-
-// Parse our "daffodil parse" command line interface
-
-static error_t
-parse_daffodil_parse_cli(struct argp_state *state)
-{
- int argc = state->argc - state->next + 1;
- char **argv = &state->argv[state->next - 1];
- char * old_cmd = argv[0];
- char new_cmd[strlen(state->name) + strlen(" parse") + 1];
-
- sprintf(new_cmd, "%s parse", state->name);
- argv[0] = new_cmd;
-
- error_t status = argp_parse(&parse_argp, argc, argv, ARGP_IN_ORDER, &argc,
&daffodil_parse);
-
- argv[0] = old_cmd;
- state->next += argc - 1;
-
- return status;
-}
-
-// Initialize our "daffodil unparse" CLI options
-
-struct daffodil_unparse_cli daffodil_unparse = {
- "xml", // default infoset type
- "-", // default infile
- "-", // default outfile
-};
-
-static const struct argp_option unparse_options[] = {
- {"infoset-type", 'I', "<infoset_type>", 0, "Infoset type to unparse. Must
be 'xml'", 0},
-
- {"output", 'o', "<file>", 0,
- "Write output to file. If not given or is -, output is written to "
- "standard output",
- 0},
-
- {0}};
-
-static error_t unparse_handler(int key, char *arg, struct argp_state *state);
-
-static const char unparse_args_doc[] = "[infile]";
-
-static const char unparse_doc[] = "\n"
- "Unparse an infoset file using a DFDL
schema\n"
- "\n"
- "Unparse Options:"
- "\v"
- " Trailing arguments:\n"
- " infile (not required) input file to
unparse. If not specified, or "
- "a value of -, reads from stdin";
-
-static const struct argp unparse_argp = {
- unparse_options, // array of CLI options
- unparse_handler, // function to get these CLI options
- unparse_args_doc, // short usage documentation
- unparse_doc, // long help documentation
- 0, // child argps parsed after this argp
- 0, // function to replace help messages
- 0 // domain name for translation lookup
-};
-
-// Handle callbacks to get our "daffodil unparse" CLI options
-
-static error_t
-unparse_handler(int key, char *arg, struct argp_state *state)
-{
- struct daffodil_unparse_cli *unparse = state->input;
-
- switch (key)
- {
- case 'I':
- unparse->infoset_converter = arg;
- break;
-
- case 'o':
- unparse->outfile = arg;
- break;
-
- case ARGP_KEY_ARG:
- if (state->arg_num)
- {
- argp_error(state, "too many arguments: %s", arg);
- }
- unparse->infile = arg;
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
- }
-
- return 0;
-}
-
-// Parse our "daffodil unparse" command line interface
-
-static error_t
-parse_daffodil_unparse_cli(struct argp_state *state)
-{
- int argc = state->argc - state->next + 1;
- char **argv = &state->argv[state->next - 1];
- char * old_cmd = argv[0];
- char new_cmd[strlen(state->name) + strlen(" unparse") + 1];
-
- sprintf(new_cmd, "%s unparse", state->name);
- argv[0] = new_cmd;
-
- error_t status = argp_parse(&unparse_argp, argc, argv, ARGP_IN_ORDER,
&argc, &daffodil_unparse);
-
- argv[0] = old_cmd;
- state->next += argc - 1;
-
- return status;
-}
-
-// Initialize our "daffodil" CLI options
-
-struct daffodil_cli daffodil_cli = {
- DAFFODIL_NONE, // default subcommand
- 0, // default verbosity
-};
-
-static const struct argp_option daffodil_options[] = {
- {"verbose", 'v', 0, 0, "Increment verbosity level, one level for each -v",
-1},
-
- {0}};
-
-static error_t daffodil_handler(int key, char *arg, struct argp_state *state);
-
-static const char daffodil_args_doc[] = "<subcommand> [SUBCOMMAND_OPTION...]";
-
-static const char daffodil_doc[] = "\n"
- "Global Options:"
- "\v"
- "Subcommands:\n"
- " parse Parse data to a DFDL
infoset\n"
- " unparse Unparse a DFDL infoset\n"
- "\n"
- "Run 'daffodil <subcommand> --help' for
subcommand specific options";
-
-static const struct argp daffodil_argp = {
- daffodil_options, // array of CLI options
- daffodil_handler, // function to get these CLI options
- daffodil_args_doc, // short usage documentation
- daffodil_doc, // long help documentation
- 0, // child argps parsed after this argp
- 0, // function to replace help messages
- 0 // domain name for translation lookup
-};
-
-// Handle callbacks to get our "daffodil" CLI options
-
-static error_t
-daffodil_handler(int key, char *arg, struct argp_state *state)
-{
- struct daffodil_cli *cli = state->input;
- error_t status = 0;
-
- switch (key)
- {
- case 'v':
- cli->verbosity++;
- break;
-
- case ARGP_KEY_ARG:
- if (strcmp(arg, "parse") == 0)
- {
- cli->subcommand = DAFFODIL_PARSE;
- status = parse_daffodil_parse_cli(state);
- }
- else if (strcmp(arg, "unparse") == 0)
- {
- cli->subcommand = DAFFODIL_UNPARSE;
- status = parse_daffodil_unparse_cli(state);
- }
- else
- {
- argp_error(state, "%s is not a valid subcommand", arg);
- }
- break;
-
- case ARGP_KEY_END:
- if (cli->subcommand == DAFFODIL_NONE)
- {
- argp_error(state, "missing subcommand");
- }
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
- }
-
- return status;
-}
-
-// Parse our "daffodil" command line interface
-
-error_t
-parse_daffodil_cli(int argc, char **argv)
-{
- static char *argp_help_fmt = "ARGP_HELP_FMT=no-dup-args-note";
- putenv(argp_help_fmt); // Do not pass an automatic variable to putenv
- return argp_parse(&daffodil_argp, argc, argv, ARGP_IN_ORDER, NULL,
&daffodil_cli);
-}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.c
new file mode 100644
index 0000000..a703d3f
--- /dev/null
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.c
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+#include "daffodil_getopt.h"
+#include <string.h> // for strcmp, strrchr
+#include <unistd.h> // for optarg, getopt, optopt, optind
+#include "cli_errors.h" // for CLI_UNEXPECTED_ARGUMENT, CLI_HELP_USAGE,
CLI_INVALID_COMMAND, CLI_INVALID_INFOSET, CLI_INVALID_OPTION,
CLI_MISSING_COMMAND, CLI_MISSING_VALUE, CLI_PROGRAM_ERROR, CLI_PROGRAM_VERSION
+// clang-format on
+
+// Initialize our "daffodil" CLI options
+
+struct daffodil_cli daffodil_cli = {
+ DAFFODIL_MISSING_COMMAND, // default subcommand
+};
+
+// Initialize our "daffodil parse" CLI options
+
+struct daffodil_parse_cli daffodil_parse = {
+ "xml", // default infoset type
+ "-", // default infile
+ "-", // default outfile
+};
+
+// Initialize our "daffodil unparse" CLI options
+
+struct daffodil_unparse_cli daffodil_unparse = {
+ "xml", // default infoset type
+ "-", // default infile
+ "-", // default outfile
+};
+
+// Parse our command line interface. Note there is NO portable way to
+// parse "daffodil [options] command [more options] arguments" with
+// getopt. We will have to put all options before all arguments,
+// e.g., "daffodil [options] command arguments".
+
+const Error *
+parse_daffodil_cli(int argc, char *argv[])
+{
+ // Fill in and return if any error happens
+ static Error error;
+
+ // Get our executable's basename
+ const char *exe = strrchr(argv[0], '/');
+ exe = exe ? exe + 1 : argv[0];
+
+ // We expect callers to put all non-option arguments at the end
+ int opt = 0;
+ while ((opt = getopt(argc, argv, ":hI:o:V")) != -1)
+ {
+ switch (opt)
+ {
+ case 'h':
+ error.code = CLI_HELP_USAGE;
+ error.s = exe;
+ return &error;
+ case 'I':
+ if (strcmp("xml", optarg) != 0)
+ {
+ error.code = CLI_INVALID_INFOSET;
+ error.s = optarg;
+ return &error;
+ }
+ daffodil_parse.infoset_converter = optarg;
+ daffodil_unparse.infoset_converter = optarg;
+ break;
+ case 'o':
+ daffodil_parse.outfile = optarg;
+ daffodil_unparse.outfile = optarg;
+ break;
+ case 'V':
+ error.code = CLI_PROGRAM_VERSION;
+ error.s = daffodil_program_version;
+ return &error;
+ case ':':
+ error.code = CLI_MISSING_VALUE;
+ error.c = optopt;
+ return &error;
+ case '?':
+ error.code = CLI_INVALID_OPTION;
+ error.c = optopt;
+ return &error;
+ default:
+ // shouldn't happen unless programmer made error
+ error.code = CLI_PROGRAM_ERROR;
+ error.d64 = opt;
+ return &error;
+ }
+ }
+
+ // Get the command and the infile arg
+ for (int i = optind; i < argc; i++)
+ {
+ const char *arg = argv[i];
+
+ if (DAFFODIL_PARSE == daffodil_cli.subcommand)
+ {
+ if (strcmp("-", daffodil_parse.infile) == 0)
+ {
+ daffodil_parse.infile = arg;
+ }
+ else
+ {
+ error.code = CLI_UNEXPECTED_ARGUMENT;
+ error.s = arg;
+ return &error;
+ }
+ }
+ else if (DAFFODIL_UNPARSE == daffodil_cli.subcommand)
+ {
+ if (strcmp("-", daffodil_unparse.infile) == 0)
+ {
+ daffodil_unparse.infile = arg;
+ }
+ else
+ {
+ error.code = CLI_UNEXPECTED_ARGUMENT;
+ error.s = arg;
+ return &error;
+ }
+ }
+ else if (strcmp("parse", arg) == 0)
+ {
+ daffodil_cli.subcommand = DAFFODIL_PARSE;
+ }
+ else if (strcmp("unparse", arg) == 0)
+ {
+ daffodil_cli.subcommand = DAFFODIL_UNPARSE;
+ }
+ else
+ {
+ error.code = CLI_INVALID_COMMAND;
+ error.s = arg;
+ return &error;
+ }
+ }
+
+ if (DAFFODIL_MISSING_COMMAND == daffodil_cli.subcommand)
+ {
+ error.code = CLI_MISSING_COMMAND;
+ error.c = 0;
+ return &error;
+ }
+
+ return 0;
+}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.h
similarity index 74%
rename from
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.h
rename to
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.h
index c870123..a8c82ae 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_argp.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_getopt.h
@@ -15,27 +15,26 @@
* limitations under the License.
*/
-#ifndef DAFFODIL_ARGP_H
-#define DAFFODIL_ARGP_H
+#ifndef DAFFODIL_GETOPT_H
+#define DAFFODIL_GETOPT_H
-// Parse our "daffodil" command line interface
+// clang-format off
+#include "errors.h" // for Error
+// clang-format on
-extern int parse_daffodil_cli(int argc, char **argv);
-
-// Get our "daffodil" CLI options
+// Declare our "daffodil" CLI options
extern struct daffodil_cli
{
enum daffodil_subcommand
{
- DAFFODIL_NONE,
+ DAFFODIL_MISSING_COMMAND,
DAFFODIL_PARSE,
DAFFODIL_UNPARSE
} subcommand;
- int verbosity;
} daffodil_cli;
-// Get our "daffodil parse" CLI options
+// Declare our "daffodil parse" CLI options
extern struct daffodil_parse_cli
{
@@ -44,7 +43,7 @@ extern struct daffodil_parse_cli
const char *outfile;
} daffodil_parse;
-// Get our "daffodil unparse" CLI options
+// Declare our "daffodil unparse" CLI options
extern struct daffodil_unparse_cli
{
@@ -53,4 +52,8 @@ extern struct daffodil_unparse_cli
const char *outfile;
} daffodil_unparse;
-#endif // DAFFODIL_ARGP_H
+// Parse our command line interface
+
+extern const Error *parse_daffodil_cli(int argc, char *argv[]);
+
+#endif // DAFFODIL_GETOPT_H
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_main.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_main.c
index af9aee4..74a733a 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_main.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/daffodil_main.c
@@ -15,13 +15,16 @@
* limitations under the License.
*/
-#include <stdio.h> // for NULL, perror, FILE, fclose, fflush, fopen,
stdin, stdout
-#include <string.h> // for strcmp
-#include "daffodil_argp.h" // for daffodil_parse, daffodil_parse_cli,
daffodil_unparse, daffodil_unparse_cli, daffodil_cli, parse_daffodil_cli,
DAFFODIL_PARSE, DAFFODIL_UNPARSE
-#include "errors.h" // for continue_or_exit, Error, print_diagnostics,
PState, UState, ERR_FILE_CLOSE, ERR_FILE_FLUSH, ERR_FILE_OPEN,
ERR_INFOSET_READ, ERR_INFOSET_WRITE
-#include "infoset.h" // for walkInfoset, InfosetBase, rootElement, ERD,
VisitEventHandler
-#include "xml_reader.h" // for xmlReaderMethods, XMLReader
-#include "xml_writer.h" // for xmlWriterMethods, XMLWriter
+// clang-format off
+#include <stdio.h> // for NULL, FILE, perror, fclose, fopen, stdin,
stdout
+#include <string.h> // for strcmp
+#include "cli_errors.h" // for CLI_FILE_CLOSE, CLI_FILE_OPEN
+#include "daffodil_getopt.h" // for daffodil_cli, parse_daffodil_cli,
daffodil_parse, daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli,
DAFFODIL_PARSE, DAFFODIL_UNPARSE
+#include "errors.h" // for continue_or_exit, print_diagnostics, Error
+#include "infoset.h" // for walkInfoset, InfosetBase, PState, UState,
rootElement, ERD, VisitEventHandler
+#include "xml_reader.h" // for xmlReaderMethods, XMLReader
+#include "xml_writer.h" // for xmlWriterMethods, XMLWriter
+// clang-format on
// Open a file or exit if it can't be opened
@@ -34,31 +37,13 @@ fopen_or_exit(FILE *stream, const char *pathname, const
char *mode)
if (!stream)
{
perror("fopen");
- const Error error = {ERR_FILE_OPEN, {pathname}};
+ const Error error = {CLI_FILE_OPEN, {.s = pathname}};
continue_or_exit(&error);
}
}
return stream;
}
-// Flush all output to a file or exit if it can't be written, also
-// print and exit if any previous error occurred or continue otherwise
-
-static void
-fflush_continue_or_exit(FILE *output, const Error *error)
-{
- if (fflush(output) != 0)
- {
- perror("fflush");
- if (!error)
- {
- const Error error = {ERR_FILE_FLUSH, {NULL}};
- continue_or_exit(&error);
- }
- }
- continue_or_exit(error);
-}
-
// Close a file or exit if it can't be closed
static void
@@ -67,80 +52,64 @@ fclose_or_exit(FILE *stream, FILE *stdin_or_stdout)
if (stream != stdin_or_stdout && fclose(stream) != 0)
{
perror("fclose");
- const Error error = {ERR_FILE_CLOSE, {NULL}};
+ const Error error = {CLI_FILE_CLOSE, {0}};
continue_or_exit(&error);
}
}
-// Define our daffodil program's main entry point
+// Define our main entry point
int
main(int argc, char *argv[])
{
- // Parse our "daffodil" command line options
- int status = parse_daffodil_cli(argc, argv);
-
- if (status == 0)
- {
- FILE * input = stdin;
- FILE * output = stdout;
- InfosetBase *root = rootElement();
-
- if (daffodil_cli.subcommand == DAFFODIL_PARSE)
- {
- // Open our input and output files if given as arguments.
- input = fopen_or_exit(input, daffodil_parse.infile, "r");
- output = fopen_or_exit(output, daffodil_parse.outfile, "w");
-
- // Parse the input file into our infoset.
- PState pstate = {input, 0, NULL, NULL};
- root->erd->parseSelf(root, &pstate);
- print_diagnostics(pstate.diagnostics);
- continue_or_exit(pstate.error);
-
- if (strcmp(daffodil_parse.infoset_converter, "xml") == 0)
- {
- // Visit the infoset and print XML from it.
- XMLWriter xmlWriter = {xmlWriterMethods, output, {NULL,
NULL, 0}};
- const Error *error = walkInfoset((VisitEventHandler
*)&xmlWriter, root);
- fflush_continue_or_exit(output, error);
- }
- else
- {
- const Error error = {ERR_INFOSET_WRITE,
{daffodil_parse.infoset_converter}};
- continue_or_exit(&error);
- }
- }
- else if (daffodil_cli.subcommand == DAFFODIL_UNPARSE)
- {
- // Open our input and output files if given as arguments.
- input = fopen_or_exit(input, daffodil_unparse.infile, "r");
- output = fopen_or_exit(output, daffodil_unparse.outfile, "w");
-
- if (strcmp(daffodil_unparse.infoset_converter, "xml") == 0)
- {
- // Initialize our infoset's values from the XML data.
- XMLReader xmlReader = {xmlReaderMethods, input, root, NULL,
NULL};
- const Error *error = walkInfoset((VisitEventHandler
*)&xmlReader, root);
- continue_or_exit(error);
- }
- else
- {
- const Error error = {ERR_INFOSET_READ,
{daffodil_unparse.infoset_converter}};
- continue_or_exit(&error);
- }
+ // Parse our command line options
+ const Error *error = parse_daffodil_cli(argc, argv);
+ continue_or_exit(error);
- // Unparse our infoset to the output file.
- UState ustate = {output, 0, NULL, NULL};
- root->erd->unparseSelf(root, &ustate);
- print_diagnostics(ustate.diagnostics);
- continue_or_exit(ustate.error);
- }
+ // Get our infoset ready
+ FILE * input = stdin;
+ FILE * output = stdout;
+ InfosetBase *root = rootElement();
- // Close our input and out files if we opened them.
- fclose_or_exit(input, stdin);
- fclose_or_exit(output, stdout);
+ // Perform our command
+ if (daffodil_cli.subcommand == DAFFODIL_PARSE)
+ {
+ // Open our input and output files if given as arguments
+ input = fopen_or_exit(input, daffodil_parse.infile, "r");
+ output = fopen_or_exit(output, daffodil_parse.outfile, "w");
+
+ // Parse the input file into our infoset
+ PState pstate = {input, 0, NULL, NULL};
+ root->erd->parseSelf(root, &pstate);
+ print_diagnostics(pstate.diagnostics);
+ continue_or_exit(pstate.error);
+
+ // Visit the infoset and print XML from it
+ XMLWriter xmlWriter = {xmlWriterMethods, output, {NULL, NULL, 0}};
+ error = walkInfoset((VisitEventHandler *)&xmlWriter, root);
+ continue_or_exit(error);
}
+ else if (daffodil_cli.subcommand == DAFFODIL_UNPARSE)
+ {
+ // Open our input and output files if given as arguments
+ input = fopen_or_exit(input, daffodil_unparse.infile, "r");
+ output = fopen_or_exit(output, daffodil_unparse.outfile, "w");
+
+ // Initialize our infoset's values from the XML data
+ XMLReader xmlReader = {xmlReaderMethods, input, root, NULL, NULL};
+ error = walkInfoset((VisitEventHandler *)&xmlReader, root);
+ continue_or_exit(error);
+
+ // Unparse our infoset to the output file
+ UState ustate = {output, 0, NULL, NULL};
+ root->erd->unparseSelf(root, &ustate);
+ print_diagnostics(ustate.diagnostics);
+ continue_or_exit(ustate.error);
+ }
+
+ // Close our input and out files if we opened them
+ fclose_or_exit(input, stdin);
+ fclose_or_exit(output, stdout);
- return status;
+ return 0;
}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.c
index 8297bcd..73d00af 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.c
@@ -15,10 +15,13 @@
* limitations under the License.
*/
+// clang-format off
#include "stack.h"
-#include <stdbool.h> // for bool
-#include <stddef.h> // for NULL, ptrdiff_t
-#include "errors.h" // for continue_or_exit, Error, ERR_STACK_EMPTY,
ERR_STACK_OVERFLOW, ERR_STACK_UNDERFLOW
+#include <stdbool.h> // for bool
+#include <stddef.h> // for ptrdiff_t
+#include "cli_errors.h" // for CLI_STACK_EMPTY, CLI_STACK_OVERFLOW,
CLI_STACK_UNDERFLOW
+#include "errors.h" // for continue_or_exit, Error
+// clang-format on
// Initialize stack with preallocated array
@@ -54,7 +57,7 @@ stack_pop(stack_t *p_stack)
{
if (stack_is_empty(p_stack))
{
- const Error error = {ERR_STACK_UNDERFLOW, {NULL}};
+ const Error error = {CLI_STACK_UNDERFLOW, {0}};
continue_or_exit(&error);
}
return *(--p_stack->p_after);
@@ -67,7 +70,7 @@ stack_push(stack_t *p_stack, stack_item_t item)
{
if (stack_is_full(p_stack))
{
- const Error error = {ERR_STACK_OVERFLOW, {NULL}};
+ const Error error = {CLI_STACK_OVERFLOW, {0}};
continue_or_exit(&error);
}
*(p_stack->p_after++) = item;
@@ -80,7 +83,7 @@ stack_top(stack_t *p_stack)
{
if (stack_is_empty(p_stack))
{
- const Error error = {ERR_STACK_EMPTY, {NULL}};
+ const Error error = {CLI_STACK_EMPTY, {0}};
continue_or_exit(&error);
}
return *(p_stack->p_after - 1);
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.h
index c63a24e..ddc3a3e 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/stack.h
@@ -18,9 +18,11 @@
#ifndef STACK_H
#define STACK_H
+// clang-format off
#include <mxml.h> // for mxml_node_t
#include <stdbool.h> // for bool
#include <stddef.h> // for ptrdiff_t
+// clang-format on
// Type of element pushed into stack
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.c
index c39804f..64291c6 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.c
@@ -15,17 +15,19 @@
* limitations under the License.
*/
+// clang-format off
#include "xml_reader.h"
-#include <assert.h> // for assert
-#include <errno.h> // for errno
-#include <inttypes.h> // for strtoimax, strtoumax
-#include <mxml.h> // for mxmlWalkNext, mxmlGetElement, mxmlGetType,
MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile,
MXML_OPAQUE_CALLBACK
-#include <stdbool.h> // for bool, false, true
-#include <stdint.h> // for intmax_t, uintmax_t, int16_t, int32_t, int64_t,
int8_t, uint16_t, uint32_t, uint64_t, uint8_t, INT16_MAX, INT16_MIN, INT32_MAX,
INT32_MIN, INT64_MAX, INT64_MIN, INT8_MAX, INT8_MIN, UINT16_MAX, UINT32_MAX,
UINT64_MAX, UINT8_MAX
-#include <stdio.h> // for NULL
-#include <stdlib.h> // for strtod, strtof
-#include <string.h> // for strcmp, strlen, strncmp
-#include "errors.h" // for Error, Error::(anonymous), ERR_STRTONUM_EMPTY,
ERR_STRTONUM_NOT, ERR_XML_GONE, ERR_STRTOD_ERRNO, ERR_STRTOI_ERRNO,
ERR_STRTONUM_RANGE, ERR_XML_MISMATCH, ERR_STRTOBOOL, ERR_XML_ERD,
ERR_XML_INPUT, ERR_XML_LEFT, UNUSED
+#include <assert.h> // for assert
+#include <errno.h> // for errno
+#include <inttypes.h> // for strtoimax, strtoumax
+#include <mxml.h> // for mxmlWalkNext, mxmlGetElement, mxmlGetType,
MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile,
MXML_OPAQUE_CALLBACK
+#include <stdbool.h> // for bool, false, true
+#include <stdint.h> // for intmax_t, uintmax_t, int16_t, int32_t,
int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t, INT16_MAX, INT16_MIN,
INT32_MAX, INT32_MIN, INT64_MAX, INT64_MIN, INT8_MAX, INT8_MIN, UINT16_MAX,
UINT32_MAX, UINT64_MAX, UINT8_MAX
+#include <stdlib.h> // for strtod, strtof
+#include <string.h> // for strcmp, strlen, strncmp
+#include "cli_errors.h" // for CLI_STRTONUM_EMPTY, CLI_STRTONUM_NOT,
CLI_XML_GONE, CLI_STRTOD_ERRNO, CLI_STRTOI_ERRNO, CLI_STRTONUM_RANGE,
CLI_XML_MISMATCH, CLI_STRTOBOOL, CLI_XML_ERD, CLI_XML_INPUT, CLI_XML_LEFT
+#include "errors.h" // for Error, Error::(anonymous), UNUSED
+// clang-format on
// Convert an XML element's text to a boolean with error checking
@@ -54,7 +56,7 @@ strtobool(const char *numptr, const Error **errorptr)
}
else
{
- static Error error = {ERR_STRTOBOOL, {NULL}};
+ static Error error = {CLI_STRTOBOOL, {0}};
error.s = numptr;
*errorptr = &error;
}
@@ -77,19 +79,19 @@ strtodnum(const char *numptr, const Error **errorptr)
// Check for any errors converting the string to a number
if (errno != 0)
{
- static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+ static Error error = {CLI_STRTOD_ERRNO, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (endptr == numptr)
{
- static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+ static Error error = {CLI_STRTONUM_EMPTY, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (*endptr != '\0')
{
- static Error error = {ERR_STRTONUM_NOT, {NULL}};
+ static Error error = {CLI_STRTONUM_NOT, {0}};
error.s = numptr;
*errorptr = &error;
}
@@ -112,19 +114,19 @@ strtofnum(const char *numptr, const Error **errorptr)
// Check for any errors converting the string to a number
if (errno != 0)
{
- static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+ static Error error = {CLI_STRTOD_ERRNO, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (endptr == numptr)
{
- static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+ static Error error = {CLI_STRTONUM_EMPTY, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (*endptr != '\0')
{
- static Error error = {ERR_STRTONUM_NOT, {NULL}};
+ static Error error = {CLI_STRTONUM_NOT, {0}};
error.s = numptr;
*errorptr = &error;
}
@@ -148,25 +150,25 @@ strtonum(const char *numptr, intmax_t minval, intmax_t
maxval, const Error **err
// Check for any errors converting the string to a number
if (errno != 0)
{
- static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+ static Error error = {CLI_STRTOI_ERRNO, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (endptr == numptr)
{
- static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+ static Error error = {CLI_STRTONUM_EMPTY, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (*endptr != '\0')
{
- static Error error = {ERR_STRTONUM_NOT, {NULL}};
+ static Error error = {CLI_STRTONUM_NOT, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (value < minval || value > maxval)
{
- static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+ static Error error = {CLI_STRTONUM_RANGE, {0}};
error.s = numptr;
*errorptr = &error;
}
@@ -189,25 +191,25 @@ strtounum(const char *numptr, uintmax_t maxval, const
Error **errorptr)
// Check for any errors converting the string to a number
if (errno != 0)
{
- static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+ static Error error = {CLI_STRTOI_ERRNO, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (endptr == numptr)
{
- static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+ static Error error = {CLI_STRTONUM_EMPTY, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (*endptr != '\0')
{
- static Error error = {ERR_STRTONUM_NOT, {NULL}};
+ static Error error = {CLI_STRTONUM_NOT, {0}};
error.s = numptr;
*errorptr = &error;
}
else if (value > maxval)
{
- static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+ static Error error = {CLI_STRTONUM_RANGE, {0}};
error.s = numptr;
*errorptr = &error;
}
@@ -225,7 +227,7 @@ xmlStartDocument(XMLReader *reader)
reader->node = reader->xml;
if (!reader->node)
{
- static Error error = {ERR_XML_INPUT, {NULL}};
+ static Error error = {CLI_XML_INPUT, {0}};
return &error;
}
@@ -249,7 +251,7 @@ xmlStartDocument(XMLReader *reader)
} while (mxmlGetType(reader->node) == MXML_OPAQUE);
}
- static Error error = {ERR_XML_GONE, {NULL}};
+ static Error error = {CLI_XML_GONE, {0}};
return reader->node ? NULL : &error;
}
@@ -268,7 +270,7 @@ xmlEndDocument(XMLReader *reader)
if (reader->node)
{
// This code path exits the program - no need to call mxmlDelete
- static Error error = {ERR_XML_LEFT, {NULL}};
+ static Error error = {CLI_XML_LEFT, {0}};
error.s = mxmlGetElement(reader->node);
return &error;
}
@@ -299,13 +301,13 @@ xmlStartComplex(XMLReader *reader, const InfosetBase
*base)
// Check whether we are walking both XML data and infoset in lockstep
if (name_from_xml && name_from_erd)
{
- static Error error = {ERR_XML_MISMATCH, {NULL}};
+ static Error error = {CLI_XML_MISMATCH, {0}};
error.s = name_from_erd;
return strcmp(name_from_xml, name_from_erd) == 0 ? NULL : &error;
}
else
{
- static Error error = {ERR_XML_GONE, {NULL}};
+ static Error error = {CLI_XML_GONE, {0}};
return &error;
}
}
@@ -385,7 +387,7 @@ xmlNumberElem(XMLReader *reader, const ERD *erd, void
*number)
return error;
default:
{
- static Error error_erd = {ERR_XML_ERD, {NULL}};
+ static Error error_erd = {CLI_XML_ERD, {0}};
error_erd.d64 = typeCode;
return &error_erd;
}
@@ -393,14 +395,14 @@ xmlNumberElem(XMLReader *reader, const ERD *erd, void
*number)
}
else
{
- static Error error = {ERR_XML_MISMATCH, {NULL}};
+ static Error error = {CLI_XML_MISMATCH, {0}};
error.s = name_from_erd;
return &error;
}
}
else
{
- static Error error = {ERR_XML_GONE, {NULL}};
+ static Error error = {CLI_XML_GONE, {0}};
return &error;
}
}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.h
index 952d39e..cc15e01 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_reader.h
@@ -18,9 +18,11 @@
#ifndef XML_READER_H
#define XML_READER_H
+// clang-format off
#include <mxml.h> // for mxml_node_t
#include <stdio.h> // for FILE
#include "infoset.h" // for VisitEventHandler, InfosetBase
+// clang-format on
// XMLReader - infoset visitor with methods to read XML
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.c
index a93df4e..f0f17eb 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.c
@@ -15,15 +15,17 @@
* limitations under the License.
*/
+// clang-format off
#include "xml_writer.h"
-#include <assert.h> // for assert
-#include <mxml.h> // for mxmlNewOpaquef, mxml_node_t, mxmlElementSetAttr,
mxmlGetOpaque, mxmlNewElement, mxmlDelete, mxmlGetElement, mxmlNewXML,
mxmlSaveFile, MXML_NO_CALLBACK
-#include <stdbool.h> // for bool
-#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t,
uint32_t, uint64_t, uint8_t
-#include <stdio.h> // for NULL
-#include <string.h> // for strcmp
-#include "errors.h" // for Error, ERR_XML_DECL, ERR_XML_ELEMENT,
ERR_XML_WRITE, LIMIT_XML_NESTING, Error::(anonymous)
-#include "stack.h" // for stack_is_empty, stack_pop, stack_push, stack_top,
stack_init
+#include <assert.h> // for assert
+#include <mxml.h> // for mxmlNewOpaquef, mxml_node_t,
mxmlElementSetAttr, mxmlGetOpaque, mxmlNewElement, mxmlDelete, mxmlGetElement,
mxmlNewXML, mxmlSaveFile, MXML_NO_CALLBACK
+#include <stdbool.h> // for bool
+#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t,
uint32_t, uint64_t, uint8_t
+#include <string.h> // for strcmp
+#include "cli_errors.h" // for CLI_XML_DECL, CLI_XML_ELEMENT, CLI_XML_WRITE,
LIMIT_XML_NESTING
+#include "errors.h" // for Error, Error::(anonymous)
+#include "stack.h" // for stack_is_empty, stack_pop, stack_push,
stack_top, stack_init
+// clang-format on
// Push new XML document on stack (note the stack is stored in a
// static array which could overflow and stop the program; it also
@@ -43,7 +45,7 @@ xmlStartDocument(XMLWriter *writer)
}
else
{
- static Error error = {ERR_XML_DECL, {NULL}};
+ static Error error = {CLI_XML_DECL, {0}};
return &error;
}
}
@@ -60,7 +62,7 @@ xmlEndDocument(XMLWriter *writer)
int status = mxmlSaveFile(xml, writer->stream, MXML_NO_CALLBACK);
if (status < 0)
{
- static Error error = {ERR_XML_WRITE, {NULL}};
+ static Error error = {CLI_XML_WRITE, {0}};
return &error;
}
mxmlDelete(xml);
@@ -192,7 +194,7 @@ xmlNumberElem(XMLWriter *writer, const ERD *erd, const void
*number)
}
else
{
- static Error error = {ERR_XML_ELEMENT, {NULL}};
+ static Error error = {CLI_XML_ELEMENT, {0}};
error.s = name;
return &error;
}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.h
index 00fe45a..c9cfaa9 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libcli/xml_writer.h
@@ -18,9 +18,11 @@
#ifndef XML_WRITER_H
#define XML_WRITER_H
+// clang-format off
#include <stdio.h> // for FILE
#include "infoset.h" // for VisitEventHandler
#include "stack.h" // for stack_t
+// clang-format on
// XMLWriter - infoset visitor with methods to output XML
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.c
index 4bd657c..23f0e7c 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.c
@@ -15,116 +15,33 @@
* limitations under the License.
*/
+// clang-format off
#include "errors.h"
#include <assert.h> // for assert
-#include <error.h> // for error
#include <inttypes.h> // for PRId64
#include <stdbool.h> // for bool, false, true
-#include <stdio.h> // for NULL, feof, ferror, FILE, size_t
-#include <stdlib.h> // for EXIT_FAILURE
+#include <stdio.h> // for fprintf, stderr, feof, ferror, FILE, stdout
+#include <stdlib.h> // for exit, EXIT_FAILURE, EXIT_SUCCESS
+// clang-format oon
-// error_message - return an internationalized error message
+// eof_or_error - get pointer to error if stream has eof or error indicator set
-static const char *
-error_message(enum ErrorCode code)
+const Error *
+eof_or_error(FILE *stream)
{
- switch (code)
+ if (feof(stream))
{
- case ERR_CHOICE_KEY:
- return "no match between choice dispatch key %" PRId64 " and any
branch key";
- case ERR_FILE_CLOSE:
- return "error closing file";
- case ERR_FILE_FLUSH:
- return "error flushing stream to file";
- case ERR_FILE_OPEN:
- return "error opening file '%s'";
- case ERR_FIXED_VALUE:
- return "value of element '%s' does not match value of its "
- "'fixed' attribute";
- case ERR_INFOSET_READ:
- return "cannot read infoset type '%s'";
- case ERR_INFOSET_WRITE:
- return "cannot write infoset type '%s'";
- case ERR_PARSE_BOOL:
- return "error parsing binary value %" PRId64 " as either true or
false";
- case ERR_STACK_EMPTY:
- return "stack empty, stopping program";
- case ERR_STACK_OVERFLOW:
- return "stack overflow, stopping program";
- case ERR_STACK_UNDERFLOW:
- return "stack underflow, stopping program";
- case ERR_STREAM_EOF:
- return "EOF in stream, stopping program";
- case ERR_STREAM_ERROR:
- return "error in stream, stopping program";
- case ERR_STRTOBOOL:
- return "error converting XML data '%s' to boolean";
- case ERR_STRTOD_ERRNO:
- return "error converting XML data '%s' to number";
- case ERR_STRTOI_ERRNO:
- return "error converting XML data '%s' to integer";
- case ERR_STRTONUM_EMPTY:
- return "found no number in XML data '%s'";
- case ERR_STRTONUM_NOT:
- return "found non-number characters in XML data '%s'";
- case ERR_STRTONUM_RANGE:
- return "number in XML data '%s' out of range";
- case ERR_XML_DECL:
- return "error making new XML declaration";
- case ERR_XML_ELEMENT:
- return "error making new XML element '%s'";
- case ERR_XML_ERD:
- return "unexpected ERD typeCode %" PRId64 " while reading XML data";
- case ERR_XML_GONE:
- return "ran out of XML data";
- case ERR_XML_INPUT:
- return "unable to read XML data from input file";
- case ERR_XML_LEFT:
- return "did not consume all of the XML data, '%s' left";
- case ERR_XML_MISMATCH:
- return "found mismatch between XML data and infoset '%s'";
- case ERR_XML_WRITE:
- return "error writing XML document";
- default:
- assert("invalid code" && 0);
- return "unrecognized error code, shouldn't happen";
+ static Error error = {ERR_STREAM_EOF, {0}};
+ return &error;
}
-}
-
-// print_maybe_stop - print a message and maybe stop the program
-
-static void
-print_maybe_stop(const Error *err, int status)
-{
- const int errnum = 0;
- const char *format = "%s";
- const char *msg = error_message(err->code);
-
- switch (err->code)
+ else if (ferror(stream))
{
- case ERR_FILE_OPEN:
- case ERR_FIXED_VALUE:
- case ERR_INFOSET_READ:
- case ERR_INFOSET_WRITE:
- case ERR_STRTOBOOL:
- case ERR_STRTOD_ERRNO:
- case ERR_STRTOI_ERRNO:
- case ERR_STRTONUM_EMPTY:
- case ERR_STRTONUM_NOT:
- case ERR_STRTONUM_RANGE:
- case ERR_XML_ELEMENT:
- case ERR_XML_LEFT:
- case ERR_XML_MISMATCH:
- error(status, errnum, msg, err->s);
- break;
- case ERR_CHOICE_KEY:
- case ERR_PARSE_BOOL:
- case ERR_XML_ERD:
- error(status, errnum, msg, err->d64);
- break;
- default:
- error(status, errnum, format, msg);
- break;
+ static Error error = {ERR_STREAM_ERROR, {0}};
+ return &error;
+ }
+ else
+ {
+ return NULL;
}
}
@@ -155,6 +72,74 @@ add_diagnostic(Diagnostics *diagnostics, const Error *error)
return false;
}
+// error_lookup - look up an internationalized error message
+
+static const ErrorLookup *
+error_lookup(uint8_t code)
+{
+ static const ErrorLookup table[ERR_ZZZ] = {
+ {ERR_CHOICE_KEY, "no match between choice dispatch key %" PRId64 " and
any branch key\n", FIELD_D64},
+ {ERR_FIXED_VALUE, "value of element '%s' does not match value of its
'fixed' attribute\n", FIELD_S},
+ {ERR_PARSE_BOOL, "error parsing binary value %" PRId64 " as either
true or false\n", FIELD_D64},
+ {ERR_STREAM_EOF, "EOF in stream, stopping program\n", FIELD_ZZZ},
+ {ERR_STREAM_ERROR, "error in stream, stopping program\n", FIELD_ZZZ},
+ };
+
+ if (code < ERR_ZZZ)
+ {
+ const ErrorLookup *lookup = &table[code];
+
+ // Double check that we looked up correct row
+ assert(code == lookup->code);
+
+ return lookup;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// print_maybe_stop - print a message and maybe stop the program
+
+static void
+print_maybe_stop(const Error *error, int status)
+{
+ const ErrorLookup *lookup = error_lookup(error->code);
+ if (!lookup && cli_error_lookup)
+ {
+ lookup = cli_error_lookup(error->code);
+ }
+ assert(lookup);
+
+ switch (lookup->field)
+ {
+ case FIELD_C:
+ fprintf(stderr, lookup->message, error->c);
+ break;
+ case FIELD_D64:
+ fprintf(stderr, lookup->message, error->d64);
+ break;
+ case FIELD_S:
+ fprintf(stderr, lookup->message, error->s);
+ break;
+ case FIELD_S_ON_STDOUT:
+ fprintf(stdout, lookup->message, error->s);
+ exit(EXIT_SUCCESS);
+ break;
+ case FIELD_ZZZ:
+ default:
+ fprintf(stderr, "%s", lookup->message);
+ break;
+ }
+
+ // Maybe stop the program
+ if (status)
+ {
+ exit(status);
+ }
+}
+
// print_diagnostics - print any validation diagnostics
void
@@ -170,7 +155,7 @@ print_diagnostics(const Diagnostics *diagnostics)
}
}
-// continue_or_exit - print and exit if an error occurred or continue otherwise
+// continue_or_exit - print and exit if any error or continue otherwise
void
continue_or_exit(const Error *error)
@@ -181,23 +166,21 @@ continue_or_exit(const Error *error)
}
}
-// eof_or_error - return an error if a stream has its eof or error indicator
set
+// check_error_lookup - call from debugger to check error lookup tables
-const Error *
-eof_or_error(FILE *stream)
+uint8_t
+check_error_lookup(void)
{
- if (feof(stream))
+ uint8_t code = 0;
+ const ErrorLookup *lookup = 0;
+ do
{
- static Error error = {ERR_STREAM_EOF, {NULL}};
- return &error;
- }
- else if (ferror(stream))
- {
- static Error error = {ERR_STREAM_ERROR, {NULL}};
- return &error;
- }
- else
- {
- return NULL;
- }
+ lookup = error_lookup(code);
+ if (!lookup && cli_error_lookup)
+ {
+ lookup = cli_error_lookup(code);
+ }
+ } while (lookup && ++code);
+
+ return code;
}
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.h
index c654181..4ea09ae 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/errors.h
@@ -18,62 +18,66 @@
#ifndef ERRORS_H
#define ERRORS_H
+// clang-format off
#include <stdbool.h> // for bool
-#include <stdint.h> // for int64_t
-#include <stdio.h> // for FILE, size_t
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint8_t, int64_t
+#include <stdio.h> // for FILE
+// clang-format on
-enum Limits
-{
- LIMIT_DIAGNOSTICS = 100, // limits how many diagnostics can accumulate
- LIMIT_NAME_LENGTH = 9999, // limits how long infoset names can become
- LIMIT_XML_NESTING = 100 // limits how deep infoset elements can nest
-};
-
-// ErrorCode - types of errors which could occur
+// ErrorCode - identifiers of libruntime errors
enum ErrorCode
{
ERR_CHOICE_KEY,
- ERR_FILE_CLOSE,
- ERR_FILE_FLUSH,
- ERR_FILE_OPEN,
ERR_FIXED_VALUE,
- ERR_INFOSET_READ,
- ERR_INFOSET_WRITE,
ERR_PARSE_BOOL,
- ERR_STACK_EMPTY,
- ERR_STACK_OVERFLOW,
- ERR_STACK_UNDERFLOW,
ERR_STREAM_EOF,
ERR_STREAM_ERROR,
- ERR_STRTOBOOL,
- ERR_STRTOD_ERRNO,
- ERR_STRTOI_ERRNO,
- ERR_STRTONUM_EMPTY,
- ERR_STRTONUM_NOT,
- ERR_STRTONUM_RANGE,
- ERR_XML_DECL,
- ERR_XML_ELEMENT,
- ERR_XML_ERD,
- ERR_XML_GONE,
- ERR_XML_INPUT,
- ERR_XML_LEFT,
- ERR_XML_MISMATCH,
- ERR_XML_WRITE
+ ERR_ZZZ,
};
+// ErrorField - identifiers of Error fields
+
+enum ErrorField
+{
+ FIELD_C,
+ FIELD_D64,
+ FIELD_S,
+ FIELD_S_ON_STDOUT,
+ FIELD_ZZZ,
+};
+
+// ErrorLookup - structure of an error lookup table row
+
+typedef struct ErrorLookup
+{
+ uint8_t code;
+ const char * message;
+ enum ErrorField field;
+} ErrorLookup;
+
// Error - specific error occuring now
typedef struct Error
{
- enum ErrorCode code;
+ uint8_t code;
union
{
- const char *s; // for %s
+ int c; // for %c
int64_t d64; // for %d64
+ const char *s; // for %s
};
} Error;
+// Limits - limits on how many elements static arrays can hold
+
+enum Limits
+{
+ LIMIT_DIAGNOSTICS = 100, // limits how many diagnostics can accumulate
+ LIMIT_NAME_LENGTH = 9999, // limits how long infoset names can become
+};
+
// Diagnostics - array of validation errors
typedef struct Diagnostics
@@ -82,25 +86,9 @@ typedef struct Diagnostics
size_t length;
} Diagnostics;
-// PState - mutable state while parsing data
+// eof_or_error - get pointer to error if stream has eof or error indicator set
-typedef struct PState
-{
- FILE * stream; // input to read data from
- size_t position; // 0-based position in stream
- Diagnostics *diagnostics; // any validation diagnostics
- const Error *error; // any error which stops program
-} PState;
-
-// UState - mutable state while unparsing infoset
-
-typedef struct UState
-{
- FILE * stream; // output to write data to
- size_t position; // 0-based position in stream
- Diagnostics *diagnostics; // any validation diagnostics
- const Error *error; // any error which stops program
-} UState;
+extern const Error *eof_or_error(FILE *stream);
// get_diagnostics - get pointer to validation diagnostics
@@ -114,13 +102,18 @@ extern bool add_diagnostic(Diagnostics *diagnostics,
const Error *error);
extern void print_diagnostics(const Diagnostics *diagnostics);
-// continue_or_exit - print and exit if an error occurred or continue otherwise
+// continue_or_exit - print and exit if any error or continue otherwise
extern void continue_or_exit(const Error *error);
-// eof_or_error - return an error if a stream has its eof or error indicator
set
+// cli_error_lookup - declare our pluggable error lookup mechanism
-extern const Error *eof_or_error(FILE *stream);
+typedef const ErrorLookup *cli_error_lookup_t(uint8_t code);
+extern cli_error_lookup_t *cli_error_lookup;
+
+// daffodil_program_version - declare our program's name and version
+
+extern const char *daffodil_program_version;
// UNUSED - suppress compiler warning about unused variable
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.c
index 107aa55..4c9b2a7 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.c
@@ -15,9 +15,11 @@
* limitations under the License.
*/
+// clang-format off
#include "infoset.h"
#include <string.h> // for memccpy
#include "errors.h" // for Error, LIMIT_NAME_LENGTH
+// clang-format on
// get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns
// attribute/value from ERD to use on XML element
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.h
index 50281b9..fbc1d0b 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/infoset.h
@@ -18,13 +18,18 @@
#ifndef INFOSET_H
#define INFOSET_H
+// clang-format off
#include <stddef.h> // for size_t
-#include "errors.h" // for Error, PState, UState
+#include <stdio.h> // for FILE
+#include "errors.h" // for Error, Diagnostics
+// clang-format on
// Prototypes needed for compilation
typedef struct ElementRuntimeData ERD;
typedef struct InfosetBase InfosetBase;
+typedef struct PState PState;
+typedef struct UState UState;
typedef struct VisitEventHandler VisitEventHandler;
typedef void (*ERDInitSelf)(InfosetBase *infoNode);
@@ -89,6 +94,26 @@ typedef struct InfosetBase
const ERD *erd;
} InfosetBase;
+// PState - mutable state while parsing data
+
+typedef struct PState
+{
+ FILE * stream; // input to read data from
+ size_t position; // 0-based position in stream
+ Diagnostics *diagnostics; // any validation diagnostics
+ const Error *error; // any error which stops program
+} PState;
+
+// UState - mutable state while unparsing infoset
+
+typedef struct UState
+{
+ FILE * stream; // output to write data to
+ size_t position; // 0-based position in stream
+ Diagnostics *diagnostics; // any validation diagnostics
+ const Error *error; // any error which stops program
+} UState;
+
// VisitEventHandler - methods to be called when walking an infoset
typedef struct VisitEventHandler
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.c
index 81b2b69..fcf21fa 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.c
@@ -15,11 +15,13 @@
* limitations under the License.
*/
+// clang-format off
#include "parsers.h"
#include <endian.h> // for be32toh, le32toh, be16toh, be64toh, le16toh,
le64toh
#include <stdbool.h> // for bool, false, true
#include <stdio.h> // for fread
-#include "errors.h" // for PState, eof_or_error, Error, ERR_PARSE_BOOL,
Error::(anonymous), add_diagnostic, get_diagnostics, ERR_FIXED_VALUE,
Diagnostics
+#include "errors.h" // for eof_or_error, Error, ERR_PARSE_BOOL,
Error::(anonymous), add_diagnostic, get_diagnostics, ERR_FIXED_VALUE,
Diagnostics
+// clang-format on
// Macros not defined by <endian.h> which we need for uniformity
@@ -65,7 +67,7 @@
}
\
else
\
{
\
- static Error error = {ERR_PARSE_BOOL, {NULL}};
\
+ static Error error = {ERR_PARSE_BOOL, {0}};
\
error.d64 = (int64_t)buffer.i_val;
\
pstate->error = &error;
\
}
\
@@ -159,7 +161,7 @@ parse_validate_fixed(bool same, const char *element, PState
*pstate)
if (!same)
{
Diagnostics *diagnostics = get_diagnostics();
- const Error error = {ERR_FIXED_VALUE, {element}};
+ const Error error = {ERR_FIXED_VALUE, {.s = element}};
add_diagnostic(diagnostics, &error);
pstate->diagnostics = diagnostics;
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.h
index 0de7de7..051a7a3 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/parsers.h
@@ -18,10 +18,12 @@
#ifndef PARSERS_H
#define PARSERS_H
+// clang-format off
#include <stdbool.h> // for bool
#include <stddef.h> // for size_t
#include <stdint.h> // for int64_t, uint32_t, int16_t, int32_t, int8_t,
uint16_t, uint64_t, uint8_t
-#include "errors.h" // for PState
+#include "infoset.h" // for PState
+// clang-format on
// Parse binary booleans, real numbers, and integers
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.c
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.c
index d64b785..279f69f 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.c
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.c
@@ -15,11 +15,13 @@
* limitations under the License.
*/
+// clang-format off
#include "unparsers.h"
#include <endian.h> // for htobe32, htole32, htobe16, htobe64, htole16,
htole64
#include <stdbool.h> // for bool
#include <stdio.h> // for fwrite
-#include "errors.h" // for UState, eof_or_error, add_diagnostic,
get_diagnostics, ERR_FIXED_VALUE, Diagnostics, Error
+#include "errors.h" // for eof_or_error, add_diagnostic, get_diagnostics,
ERR_FIXED_VALUE, Diagnostics, Error
+// clang-format on
// Macros not defined by <endian.h> which we need for uniformity
@@ -143,7 +145,7 @@ unparse_validate_fixed(bool same, const char *element,
UState *ustate)
if (!same)
{
Diagnostics *diagnostics = get_diagnostics();
- const Error error = {ERR_FIXED_VALUE, {element}};
+ const Error error = {ERR_FIXED_VALUE, {.s = element}};
add_diagnostic(diagnostics, &error);
ustate->diagnostics = diagnostics;
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.h
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.h
index 4b5f5ab..73a349c 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.h
+++
b/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/c/libruntime/unparsers.h
@@ -18,19 +18,18 @@
#ifndef UNPARSERS_H
#define UNPARSERS_H
+// clang-format off
#include <stdbool.h> // for bool
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, int16_t, int32_t, int64_t, int8_t,
uint16_t, uint64_t, uint8_t
-#include "errors.h" // for UState
+#include "infoset.h" // for UState
+// clang-format on
// Unparse binary booleans, real numbers, and integers
-extern void unparse_be_bool16(bool number, uint32_t true_rep,
- uint32_t false_rep, UState *ustate);
-extern void unparse_be_bool32(bool number, uint32_t true_rep,
- uint32_t false_rep, UState *ustate);
-extern void unparse_be_bool8(bool number, uint32_t true_rep, uint32_t
false_rep,
- UState *ustate);
+extern void unparse_be_bool16(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
+extern void unparse_be_bool32(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
+extern void unparse_be_bool8(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
extern void unparse_be_double(double number, UState *ustate);
extern void unparse_be_float(float number, UState *ustate);
@@ -45,12 +44,9 @@ extern void unparse_be_uint32(uint32_t number, UState
*ustate);
extern void unparse_be_uint64(uint64_t number, UState *ustate);
extern void unparse_be_uint8(uint8_t number, UState *ustate);
-extern void unparse_le_bool16(bool number, uint32_t true_rep,
- uint32_t false_rep, UState *ustate);
-extern void unparse_le_bool32(bool number, uint32_t true_rep,
- uint32_t false_rep, UState *ustate);
-extern void unparse_le_bool8(bool number, uint32_t true_rep, uint32_t
false_rep,
- UState *ustate);
+extern void unparse_le_bool16(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
+extern void unparse_le_bool32(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
+extern void unparse_le_bool8(bool number, uint32_t true_rep, uint32_t
false_rep, UState *ustate);
extern void unparse_le_double(double number, UState *ustate);
extern void unparse_le_float(float number, UState *ustate);
@@ -67,12 +63,10 @@ extern void unparse_le_uint8(uint8_t number, UState
*ustate);
// Unparse fill bytes until end position is reached
-extern void unparse_fill_bytes(size_t end_position, const char fill_byte,
- UState *ustate);
+extern void unparse_fill_bytes(size_t end_position, const char fill_byte,
UState *ustate);
// Validate unparsed number is same as fixed value
-extern void unparse_validate_fixed(bool same, const char *element,
- UState *ustate);
+extern void unparse_validate_fixed(bool same, const char *element, UState
*ustate);
#endif // UNPARSERS_H
diff --git
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
index b70e565..5c68924 100644
---
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
+++
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
@@ -34,7 +34,7 @@ import scala.util.Properties.isWin
/**
* We need a mutux object for exclusive access to a code block
- */
+ */
private object mutex {}
/**
@@ -54,7 +54,7 @@ class CodeGenerator(root: Root) extends DFDL.CodeGenerator {
* Writes C source files into a "c" subdirectory of the given output
directory.
* Removes the "c" subdirectory if it existed before. Returns the newly
created
* "c" subdirectory.
- */
+ */
override def generateCode(rootNS: Option[RefQName], outputDirArg: String):
os.Path = {
// Get the paths of the C resources, the output directory, and its code
subdirectory
val resources = "/org/apache/daffodil/runtime2/c"
@@ -112,15 +112,17 @@ class CodeGenerator(root: Root) extends
DFDL.CodeGenerator {
try {
// Assemble the compiler's command line arguments
val compiler = pickCompiler
- val files = os.walk(codeDir).filter(_.ext == "c")
- val libs = if (isWin) Seq("-largp", "-lmxml") else Seq("-lmxml")
+ val absFiles = os.walk(codeDir).filter(_.ext == "c")
+ val relFiles = Seq("libcli/*.c", "libruntime/*.c")
+ val libs = Seq("-lmxml")
// Run the compiler in the code directory (if we found "zig cc"
// as a compiler, it will cache previously built files in zig's
// global cache directory, not a local zig_cache directory)
if (compiler.nonEmpty) {
- val result = os.proc(compiler, "-I", codeDir/"libcli", "-I",
codeDir/"libruntime",
- files, libs, "-o", exe).call(cwd = codeDir, stderr = os.Pipe)
+ val result = os
+ .proc(compiler, "-Ilibcli", "-Ilibruntime", if (isWin) relFiles else
absFiles, libs, "-o", exe)
+ .call(cwd = codeDir, stderr = os.Pipe)
// Report any compiler output as a warning
if (result.out.text.nonEmpty || result.err.text.nonEmpty) {
@@ -146,9 +148,9 @@ class CodeGenerator(root: Root) extends DFDL.CodeGenerator {
* find any compiler from the following list:
*
* - zig cc
- * - gcc
- * - clang
* - cc
+ * - clang
+ * - gcc
*
* Returns the first compiler found as a sequence of strings in case the
* compiler is a program with a subcommand argument. Returns the empty
@@ -156,7 +158,7 @@ class CodeGenerator(root: Root) extends DFDL.CodeGenerator {
*/
lazy val pickCompiler: Seq[String] = {
val ccEnv = System.getenv("CC")
- val compilers = Seq(ccEnv, "zig cc", "gcc", "clang", "cc")
+ val compilers = Seq(ccEnv, "zig cc", "cc", "clang", "gcc")
val path = System.getenv("PATH").split(File.pathSeparatorChar)
def inPath(compiler: String): Boolean = {
(compiler != null) && {
diff --git
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
index 9c35d18..7ff7c6c 100644
---
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
+++
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
@@ -87,7 +87,7 @@ class Runtime2DataProcessor(executableFile: os.Path) extends
DFDL.DataProcessorB
val outfile = tempDir/"outfile"
try {
os.write(infile, input)
- val result = os.proc(executableFile, "parse", "-I", "xml", "-o",
outfile, infile).call(cwd = tempDir, stderr = os.Pipe)
+ val result = os.proc(executableFile, "-o", outfile, "parse",
infile).call(cwd = tempDir, stderr = os.Pipe)
if (result.out.text.isEmpty && result.err.text.isEmpty) {
val parseResult = new ParseResult(outfile, Success, infile)
parseResult
@@ -123,7 +123,7 @@ class Runtime2DataProcessor(executableFile: os.Path)
extends DFDL.DataProcessorB
val outfile = tempDir/"outfile"
try {
os.write(infile, input)
- val result = os.proc(executableFile, "unparse", "-I", "xml", "-o",
outfile, infile).call(cwd = tempDir, stderr = os.Pipe)
+ val result = os.proc(executableFile, "-o", outfile, "unparse",
infile).call(cwd = tempDir, stderr = os.Pipe)
val finalBitPos0b = os.size(outfile) * 8 // File sizes are bytes, so
must multiply to get final position in bits
os.read.stream(outfile).writeBytesTo(output)
if (result.out.text.isEmpty && result.err.text.isEmpty) {
diff --git
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
index f045f03..c814470 100644
---
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
+++
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
@@ -223,13 +223,13 @@ class CodeGeneratorState {
val erdComputation = s" &_choice_$erd"
val initStatement = s" instance->_choice = 0xFFFFFFFFFFFFFFFF;"
val initChoiceStatement =
- s""" static Error error = {ERR_CHOICE_KEY, {NULL}};
+ s""" static Error error = {ERR_CHOICE_KEY, {0}};
|
| int64_t key = rootElement->$dispatchField;
| switch (key)
| {""".stripMargin
val parseStatement =
- s""" static Error error = {ERR_CHOICE_KEY, {NULL}};
+ s""" static Error error = {ERR_CHOICE_KEY, {0}};
|
| pstate->error =
instance->_base.erd->initChoice(&instance->_base, rootElement());
| if (pstate->error) return;
@@ -237,7 +237,7 @@ class CodeGeneratorState {
| switch (instance->_choice)
| {""".stripMargin
val unparseStatement =
- s""" static Error error = {ERR_CHOICE_KEY, {NULL}};
+ s""" static Error error = {ERR_CHOICE_KEY, {0}};
|
| ustate->error =
instance->_base.erd->initChoice(&instance->_base, rootElement());
| if (ustate->error) return;
@@ -588,7 +588,7 @@ class CodeGeneratorState {
|
|// Initialize our program's name and version
|
- |const char *argp_program_version = "$program $version";
+ |const char *daffodil_program_version = "$program $version";
|
|// Declare prototypes for easier compilation
|
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.c
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.c
similarity index 92%
rename from
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.c
rename to
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.c
index f5f1f89..ab71748 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.c
+++
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.c
@@ -1,21 +1,4 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NestedUnion.h"
+#include "generated_code.h"
#include <math.h> // for NAN
#include <stdbool.h> // for bool, true, false
#include <stddef.h> // for NULL, size_t
@@ -25,7 +8,7 @@
// Initialize our program's name and version
-const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
+const char *daffodil_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
// Declare prototypes for easier compilation
@@ -334,7 +317,7 @@ data_initSelf(data *instance)
static const Error *
data_initChoice(data *instance, const NestedUnion *rootElement)
{
- static Error error = {ERR_CHOICE_KEY, {NULL}};
+ static Error error = {ERR_CHOICE_KEY, {0}};
int64_t key = rootElement->tag;
switch (key)
@@ -365,7 +348,7 @@ data_initChoice(data *instance, const NestedUnion
*rootElement)
static void
data_parseSelf(data *instance, PState *pstate)
{
- static Error error = {ERR_CHOICE_KEY, {NULL}};
+ static Error error = {ERR_CHOICE_KEY, {0}};
pstate->error = instance->_base.erd->initChoice(&instance->_base,
rootElement());
if (pstate->error) return;
@@ -391,7 +374,7 @@ data_parseSelf(data *instance, PState *pstate)
static void
data_unparseSelf(const data *instance, UState *ustate)
{
- static Error error = {ERR_CHOICE_KEY, {NULL}};
+ static Error error = {ERR_CHOICE_KEY, {0}};
ustate->error = instance->_base.erd->initChoice(&instance->_base,
rootElement());
if (ustate->error) return;
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.h
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.h
similarity index 50%
rename from
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.h
rename to
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.h
index b729693..16fdf8b 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/NestedUnion.h
+++
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/NestedUnion/generated_code.h
@@ -1,20 +1,3 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
#ifndef GENERATED_CODE_H
#define GENERATED_CODE_H
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.c
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.c
similarity index 97%
rename from
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.c
rename to
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.c
index b64e7f6..02df28a 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.c
+++
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.c
@@ -1,21 +1,4 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ex_nums.h"
+#include "generated_code.h"
#include <math.h> // for NAN
#include <stdbool.h> // for bool, true, false
#include <stddef.h> // for NULL, size_t
@@ -25,7 +8,7 @@
// Initialize our program's name and version
-const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
+const char *daffodil_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
// Declare prototypes for easier compilation
diff --git
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.h
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.h
similarity index 69%
rename from
daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.h
rename to
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.h
index 937c93c..5cbcbc0 100644
---
a/daffodil-runtime2/src/main/resources/org/apache/daffodil/runtime2/examples/ex_nums.h
+++
b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples/ex_nums/generated_code.h
@@ -1,20 +1,3 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
#ifndef GENERATED_CODE_H
#define GENERATED_CODE_H
diff --git a/project/Rat.scala b/project/Rat.scala
index 0e28845..f0f28aa 100644
--- a/project/Rat.scala
+++ b/project/Rat.scala
@@ -35,6 +35,9 @@ object Rat {
file("daffodil-cli/src/windows/banner.bmp"),
file("daffodil-cli/src/windows/dialog.bmp"),
+ // generated_code.[ch] examples
+
file("daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/examples"),
+
// test files that cannot include the Apache license without breaking tests
file("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/debugger/982"),
file("daffodil-cli/src/it/resources/org/apache/daffodil/CLI/debugger/1326"),