Add libcyaml
This commit is contained in:
		
							parent
							
								
									3f1e47b054
								
							
						
					
					
						commit
						286eca58d6
					
				
							
								
								
									
										1
									
								
								libs/libcyaml/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								libs/libcyaml/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
build/
 | 
			
		||||
							
								
								
									
										114
									
								
								libs/libcyaml/CHANGES.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								libs/libcyaml/CHANGES.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,114 @@
 | 
			
		||||
LibCYAML: Change Log
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.4.1
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Log valid enum values on error parsing enums.
 | 
			
		||||
  * Split out storage of scaler values to client data structure.
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Updated code coverage CI to latest action versions.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.4.0
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Reject numerical values with trailing junk.
 | 
			
		||||
  * New `CYAML_CFG_IGNORED_KEY_WARNING` to generate warnings when mapping
 | 
			
		||||
    keys are ignored either explicitly by the `CYAML_IGNORE` type or implicitly
 | 
			
		||||
    by the `CYAML_CFG_IGNORE_UNKNOWN_KEYS` config setting.
 | 
			
		||||
* **Buildsystem**:
 | 
			
		||||
  * Avoid using GNU `-D` install flag.
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Improved API documentation for `CYAML_SEQUENCE_FIXED`.
 | 
			
		||||
  * Minor optimisation for host endian detection for older GCCs.
 | 
			
		||||
  * Minor CI updates.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.3.1
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Fixed value out-of-range detection limits for signed integers.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.3.0
 | 
			
		||||
 | 
			
		||||
* **Saving**:
 | 
			
		||||
  * New flags allow control over scalar output style.
 | 
			
		||||
    - For example to force single or double quote style.
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Buildsystem changes to allow use of CPPFLAGS from the environment.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.2.1
 | 
			
		||||
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Support for dynamic library build on Mac OS X.
 | 
			
		||||
  * Ordered designated initialisers in public header for C++ compatibility.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.2.0
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Allow mappings with zero fields in the schema.
 | 
			
		||||
  * Improved logging of errors.
 | 
			
		||||
  * `CYAML_BOOL` type now treats "Off" as false.
 | 
			
		||||
  * Allow loading of float values that overflow or underflow unless
 | 
			
		||||
    `CYAML_FLAG_STRICT` set.
 | 
			
		||||
  * Added line and column numbers to backtraces.
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Update tests to handle libyaml 0.2.5 output format change.
 | 
			
		||||
  * Buildsystem improvements.
 | 
			
		||||
  * Made public header C++ compatible.
 | 
			
		||||
  * Test runner supports running individual tests.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.1.0
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Significantly optimised handling of aliases and anchors.
 | 
			
		||||
  * Fixed handling of duplicate mapping keys.
 | 
			
		||||
* **Saving**:
 | 
			
		||||
  * Increased precision for double precision floating point values.
 | 
			
		||||
* **General**:
 | 
			
		||||
  * Fixed data handling on big endian systems.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.0.2
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Fixed invalid read on error path for bitfield handling.
 | 
			
		||||
* **Buildsystem**:
 | 
			
		||||
  * Fixed to link against libraries after listing objects.
 | 
			
		||||
  * Added `check` target as alias for `test`.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.0.1
 | 
			
		||||
 | 
			
		||||
* **Loading**:
 | 
			
		||||
  * Fixed mapping and sequence values with `CYAML_FLAG_POINTER_NULL`.
 | 
			
		||||
* **Buildsystem**:
 | 
			
		||||
  * Installation: Explicitly create leading directories.
 | 
			
		||||
 | 
			
		||||
No changes are required for client applications to upgrade.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## LibCYAML v1.0.0
 | 
			
		||||
 | 
			
		||||
* Initial release.
 | 
			
		||||
							
								
								
									
										14
									
								
								libs/libcyaml/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								libs/libcyaml/LICENSE
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
Copyright (c) 2017-2021, Michael Drake
 | 
			
		||||
 | 
			
		||||
Permission to use, copy, modify, and/or distribute this software for any purpose
 | 
			
		||||
with or without fee is hereby granted, provided that the above copyright notice
 | 
			
		||||
and this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 | 
			
		||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 | 
			
		||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 | 
			
		||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 | 
			
		||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | 
			
		||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 | 
			
		||||
THIS SOFTWARE.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										209
									
								
								libs/libcyaml/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								libs/libcyaml/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,209 @@
 | 
			
		||||
# SPDX-License-Identifier: ISC
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2017-2023 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 | 
			
		||||
# CYAML's versioning is <MAJOR>.<MINOR>.<PATCH>[-DEVEL]
 | 
			
		||||
# The main branch will usually be DEVEL.  The release process is:
 | 
			
		||||
# 0. Create branch for release.
 | 
			
		||||
# 1. Update the CHANGES.md file.
 | 
			
		||||
# 2. Set MAJOR, MINOR, and PATCH versions appropriately for changes.
 | 
			
		||||
# 3. Create PR, review, and merge to main.
 | 
			
		||||
# 4. Set VESION_DEVEL to 0, commit to main.
 | 
			
		||||
# 5. Tag the release: `git tag -a vN.N.N -m "libcyaml N.N.N"`
 | 
			
		||||
# 6. Set VESION_DEVEL to 1, commit to main.
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 4
 | 
			
		||||
VERSION_PATCH = 1
 | 
			
		||||
VERSION_DEVEL = 0
 | 
			
		||||
VERSION_STR = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
 | 
			
		||||
 | 
			
		||||
# Default variant depends on whether it's a development build.
 | 
			
		||||
ifeq ($(VERSION_DEVEL), 1)
 | 
			
		||||
	VARIANT = debug
 | 
			
		||||
else
 | 
			
		||||
	VARIANT = release
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Unfortunately ASan is incompatible with valgrind, so we have a special
 | 
			
		||||
# variant for running with sanitisers.
 | 
			
		||||
VALID_VARIANTS := release debug san
 | 
			
		||||
ifneq ($(filter $(VARIANT),$(VALID_VARIANTS)),)
 | 
			
		||||
else
 | 
			
		||||
$(error VARIANT must be 'debug' (default), 'san', or 'release')
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
UNAME_S := $(shell uname -s)
 | 
			
		||||
 | 
			
		||||
LIB_NAME = libcyaml
 | 
			
		||||
LIB_PKGCON = $(LIB_NAME).pc
 | 
			
		||||
LIB_STATIC = $(LIB_NAME).a
 | 
			
		||||
LIB_SHARED = $(LIB_NAME).so
 | 
			
		||||
LIB_SH_MAJ = $(LIB_SHARED).$(VERSION_MAJOR)
 | 
			
		||||
LIB_SH_VER = $(LIB_SHARED).$(VERSION_STR)
 | 
			
		||||
 | 
			
		||||
.IMPLICIT =
 | 
			
		||||
 | 
			
		||||
PREFIX ?= /usr/local
 | 
			
		||||
LIBDIR ?= lib
 | 
			
		||||
INCLUDEDIR ?= include
 | 
			
		||||
 | 
			
		||||
Q ?= @
 | 
			
		||||
 | 
			
		||||
CC ?= gcc
 | 
			
		||||
AR ?= ar
 | 
			
		||||
MKDIR =	mkdir -p
 | 
			
		||||
INSTALL ?= install -c
 | 
			
		||||
VALGRIND = valgrind --leak-check=full --track-origins=yes
 | 
			
		||||
 | 
			
		||||
VERSION_FLAGS = -DVERSION_MAJOR=$(VERSION_MAJOR) \
 | 
			
		||||
                -DVERSION_MINOR=$(VERSION_MINOR) \
 | 
			
		||||
                -DVERSION_PATCH=$(VERSION_PATCH) \
 | 
			
		||||
                -DVERSION_DEVEL=$(VERSION_DEVEL)
 | 
			
		||||
 | 
			
		||||
LIBYAML = yaml-0.1
 | 
			
		||||
LIBYAML_CFLAGS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --cflags $(LIBYAML)),)
 | 
			
		||||
LIBYAML_LIBS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --libs $(LIBYAML)),-lyaml)
 | 
			
		||||
 | 
			
		||||
INCLUDE = -I include
 | 
			
		||||
CPPFLAGS += $(VERSION_FLAGS) -MMD -MP
 | 
			
		||||
CFLAGS += $(INCLUDE) $(LIBYAML_CFLAGS)
 | 
			
		||||
CFLAGS += -std=c11 -Wall -Wextra -pedantic \
 | 
			
		||||
		-Wconversion -Wwrite-strings -Wcast-align -Wpointer-arith \
 | 
			
		||||
		-Winit-self -Wshadow -Wstrict-prototypes -Wmissing-prototypes \
 | 
			
		||||
		-Wredundant-decls -Wundef -Wvla -Wdeclaration-after-statement
 | 
			
		||||
LDFLAGS += $(LIBYAML_LIBS)
 | 
			
		||||
LDFLAGS_SHARED = -Wl,-soname=$(LIB_SH_MAJ) -shared
 | 
			
		||||
 | 
			
		||||
ifeq ($(VARIANT), debug)
 | 
			
		||||
	CFLAGS += -O0 -g
 | 
			
		||||
else ifeq ($(VARIANT), san)
 | 
			
		||||
	CFLAGS += -O0 -g -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
 | 
			
		||||
	LDFLAGS += -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
 | 
			
		||||
else
 | 
			
		||||
	CFLAGS += -O2 -DNDEBUG
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq ($(filter coverage,$(MAKECMDGOALS)),)
 | 
			
		||||
	BUILDDIR = build/coverage/$(VARIANT)
 | 
			
		||||
	CFLAGS_COV = --coverage -DNDEBUG
 | 
			
		||||
	LDFLAGS_COV = --coverage
 | 
			
		||||
else
 | 
			
		||||
	BUILDDIR = build/$(VARIANT)
 | 
			
		||||
	CFLAGS_COV =
 | 
			
		||||
	LDFLAGS_COV =
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
BUILDDIR_SHARED = $(BUILDDIR)/shared
 | 
			
		||||
BUILDDIR_STATIC = $(BUILDDIR)/static
 | 
			
		||||
 | 
			
		||||
LIB_SRC_FILES = mem.c free.c load.c save.c util.c utf8.c
 | 
			
		||||
LIB_SRC := $(addprefix src/,$(LIB_SRC_FILES))
 | 
			
		||||
LIB_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(LIB_SRC)))
 | 
			
		||||
LIB_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(LIB_SRC)))
 | 
			
		||||
LIB_OBJ_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_OBJ))
 | 
			
		||||
LIB_OBJ_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_OBJ))
 | 
			
		||||
LIB_DEP_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_DEP))
 | 
			
		||||
LIB_DEP_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_DEP))
 | 
			
		||||
 | 
			
		||||
LIB_PATH = LD_LIBRARY_PATH=$(BUILDDIR)
 | 
			
		||||
 | 
			
		||||
ifeq ($(UNAME_S),Darwin)
 | 
			
		||||
	LIB_SHARED = $(LIB_NAME).dylib
 | 
			
		||||
	LIB_SH_MAJ = $(LIB_NAME).$(VERSION_MAJOR).dylib
 | 
			
		||||
	LIB_SH_VER = $(LIB_NAME).$(VERSION_STR).dylib
 | 
			
		||||
	LDFLAGS_SHARED = -dynamiclib -install_name "$(LIB_SH_MAJ)" -current_version $(VERSION_STR)
 | 
			
		||||
	LIB_PATH = DYLD_FALLBACK_LIBRARY_PATH=$(BUILDDIR)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
TEST_SRC_FILES = units/free.c units/load.c units/test.c units/util.c \
 | 
			
		||||
		units/errs.c units/file.c units/save.c units/utf8.c
 | 
			
		||||
TEST_SRC := $(addprefix test/,$(TEST_SRC_FILES))
 | 
			
		||||
TEST_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(TEST_SRC)))
 | 
			
		||||
TEST_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(TEST_SRC)))
 | 
			
		||||
 | 
			
		||||
TEST_BINS = \
 | 
			
		||||
		$(BUILDDIR)/test/units/cyaml-shared \
 | 
			
		||||
		$(BUILDDIR)/test/units/cyaml-static
 | 
			
		||||
 | 
			
		||||
all: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) examples
 | 
			
		||||
 | 
			
		||||
coverage: test-verbose
 | 
			
		||||
	$(Q)$(MKDIR) $(BUILDDIR)
 | 
			
		||||
	$(Q)gcovr -e 'test/.*' -r .
 | 
			
		||||
	$(Q)gcovr -e 'test/.*' -x -o build/coverage.xml -r .
 | 
			
		||||
	$(Q)gcovr -e 'test/.*' --html --html-details -o build/coverage.html -r .
 | 
			
		||||
 | 
			
		||||
test test-quiet test-verbose test-debug: $(TEST_BINS)
 | 
			
		||||
	$(Q)for i in $(^); do $(LIB_PATH) $$i $(subst test,,$(subst test-,--,$@)) "$(TESTLIST)" || exit; done
 | 
			
		||||
 | 
			
		||||
valgrind valgrind-quiet valgrind-verbose valgrind-debug: $(TEST_BINS)
 | 
			
		||||
	$(Q)for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i $(subst valgrind,,$(subst valgrind-,--,$@)) "$(TESTLIST)" || exit; done
 | 
			
		||||
 | 
			
		||||
check: test
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/$(LIB_PKGCON): $(LIB_PKGCON).in
 | 
			
		||||
	sed \
 | 
			
		||||
		-e 's#PREFIX#$(PREFIX)#' \
 | 
			
		||||
		-e 's#LIBDIR#$(LIBDIR)#' \
 | 
			
		||||
		-e 's#INCLUDEDIR#$(INCLUDEDIR)#' \
 | 
			
		||||
		-e 's#VERSION#$(VERSION_STR)#' \
 | 
			
		||||
		$(LIB_PKGCON).in >$(BUILDDIR)/$(LIB_PKGCON)
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/$(LIB_STATIC): $(LIB_OBJ_STATIC)
 | 
			
		||||
	$(AR) -rcs $@ $^
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/$(LIB_SH_MAJ): $(LIB_OBJ_SHARED)
 | 
			
		||||
	$(CC) -o $@ $^ $(LDFLAGS) $(LDFLAGS_COV) $(LDFLAGS_SHARED)
 | 
			
		||||
 | 
			
		||||
$(LIB_OBJ_STATIC): $(BUILDDIR_STATIC)/%.o : %.c
 | 
			
		||||
	$(Q)$(MKDIR) $(dir $@)
 | 
			
		||||
	$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(LIB_OBJ_SHARED): $(BUILDDIR_SHARED)/%.o : %.c
 | 
			
		||||
	$(Q)$(MKDIR) $(dir $@)
 | 
			
		||||
	$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(CFLAGS_COV) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
docs:
 | 
			
		||||
	$(MKDIR) build/docs/api
 | 
			
		||||
	$(MKDIR) build/docs/devel
 | 
			
		||||
	doxygen docs/api.doxygen.conf
 | 
			
		||||
	doxygen docs/devel.doxygen.conf
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf build/
 | 
			
		||||
 | 
			
		||||
install: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) $(BUILDDIR)/$(LIB_PKGCON)
 | 
			
		||||
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR)
 | 
			
		||||
	$(INSTALL) $(BUILDDIR)/$(LIB_SH_MAJ) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_SH_VER)
 | 
			
		||||
	(cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SH_MAJ) || { rm -f $(LIB_SH_MAJ) && ln -s $(LIB_SH_VER) $(LIB_SH_MAJ); }; })
 | 
			
		||||
	(cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SHARED) || { rm -f $(LIB_SHARED) && ln -s $(LIB_SH_VER) $(LIB_SHARED); }; })
 | 
			
		||||
	$(INSTALL) $(BUILDDIR)/$(LIB_STATIC) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC)
 | 
			
		||||
	chmod 644 $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC)
 | 
			
		||||
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml
 | 
			
		||||
	$(INSTALL) -m 644 include/cyaml/* $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml
 | 
			
		||||
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig
 | 
			
		||||
	$(INSTALL) -m 644 $(BUILDDIR)/$(LIB_PKGCON) $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig/$(LIB_PKGCON)
 | 
			
		||||
 | 
			
		||||
examples: $(BUILDDIR)/planner $(BUILDDIR)/numerical
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/planner: examples/planner/main.c $(BUILDDIR)/$(LIB_STATIC)
 | 
			
		||||
	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/numerical: examples/numerical/main.c $(BUILDDIR)/$(LIB_STATIC)
 | 
			
		||||
	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
-include $(LIB_DEP_SHARED) $(LIB_DEP_STATIC) $(TEST_DEP)
 | 
			
		||||
 | 
			
		||||
.PHONY: all test test-quiet test-verbose test-debug \
 | 
			
		||||
		valgrind valgrind-quiet valgrind-verbose valgrind-debug \
 | 
			
		||||
		clean coverage docs install examples check
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/test/units/cyaml-static: $(TEST_OBJ) $(BUILDDIR)/$(LIB_STATIC)
 | 
			
		||||
	$(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
$(BUILDDIR)/test/units/cyaml-shared: $(TEST_OBJ) $(BUILDDIR)/$(LIB_SH_MAJ)
 | 
			
		||||
	$(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS)
 | 
			
		||||
 | 
			
		||||
$(TEST_OBJ): $(BUILDDIR)/%.o : %.c
 | 
			
		||||
	$(Q)$(MKDIR) $(dir $@)
 | 
			
		||||
	$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $<
 | 
			
		||||
							
								
								
									
										112
									
								
								libs/libcyaml/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								libs/libcyaml/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,112 @@
 | 
			
		||||
LibCYAML: Schema-based YAML parsing and serialisation
 | 
			
		||||
=====================================================
 | 
			
		||||
 | 
			
		||||
[](https://github.com/tlsa/libcyaml/actions) [](https://github.com/tlsa/libcyaml/actions/workflows/static-analysis.yaml) [](https://codecov.io/gh/tlsa/libcyaml)
 | 
			
		||||
 | 
			
		||||
LibCYAML is a C library for reading and writing structured YAML documents.
 | 
			
		||||
It is written in ISO C11 and licensed under the ISC licence.
 | 
			
		||||
 | 
			
		||||
Overview
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
The fundamental idea behind CYAML is to allow applications to construct
 | 
			
		||||
schemas which describe both the permissible structure of the YAML documents
 | 
			
		||||
to read/write, and the C data structure(s) in which the loaded data is
 | 
			
		||||
arranged in memory.
 | 
			
		||||
 | 
			
		||||
### Goals
 | 
			
		||||
 | 
			
		||||
* Make it easy to load YAML into client's custom C data structures.
 | 
			
		||||
* Good compromise between flexibility and simplicity.
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
* Load, Save and Free functions.
 | 
			
		||||
* Reads and writes arbitrary client data structures.
 | 
			
		||||
* Schema-driven, allowing control over permitted YAML, for example:
 | 
			
		||||
    - Required / optional mapping fields.
 | 
			
		||||
    - Allowed / disallowed values.
 | 
			
		||||
    - Minimum / maximum sequence entry count.
 | 
			
		||||
    - etc...
 | 
			
		||||
* Enumerations and flag words.
 | 
			
		||||
* YAML backtraces make it simple for users to fix their YAML to
 | 
			
		||||
  conform to your schema.
 | 
			
		||||
* Uses standard [`libyaml`](https://github.com/yaml/libyaml) library for
 | 
			
		||||
  low-level YAML read / write.
 | 
			
		||||
* Support for YAML aliases and anchors.
 | 
			
		||||
 | 
			
		||||
Building
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
To build the library, run:
 | 
			
		||||
 | 
			
		||||
    make
 | 
			
		||||
 | 
			
		||||
You can control the optimisation and building of asserts by setting
 | 
			
		||||
the build variant:
 | 
			
		||||
 | 
			
		||||
    make VARIANT=debug
 | 
			
		||||
    make VARIANT=release
 | 
			
		||||
 | 
			
		||||
Another debug build variant which is built with address sanitiser (incompatible
 | 
			
		||||
with valgrind) can be built with:
 | 
			
		||||
 | 
			
		||||
    make VARIANT=san
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
To install a release version of the library, run:
 | 
			
		||||
 | 
			
		||||
    make install VARIANT=release
 | 
			
		||||
 | 
			
		||||
It will install to the PREFIX `/usr/local` by default, and it will use
 | 
			
		||||
DESTDIR and PREFIX from the environment if set.
 | 
			
		||||
 | 
			
		||||
Testing
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
To run the tests, run any of the following, which generate various
 | 
			
		||||
levels of output verbosity (optionally setting `VARIANT=release`, or
 | 
			
		||||
`VARIANT=san`):
 | 
			
		||||
 | 
			
		||||
    make test
 | 
			
		||||
    make test-quiet
 | 
			
		||||
    make test-verbose
 | 
			
		||||
    make test-debug
 | 
			
		||||
 | 
			
		||||
To run the tests under `valgrind`, a similar set of targets is available:
 | 
			
		||||
 | 
			
		||||
    make valgrind
 | 
			
		||||
    make valgrind-quiet
 | 
			
		||||
    make valgrind-verbose
 | 
			
		||||
    make valgrind-debug
 | 
			
		||||
 | 
			
		||||
To run a single test or a subset of tests, use the `TESTLIST` variable, which
 | 
			
		||||
expects a space and/or comma separated list of test names:
 | 
			
		||||
 | 
			
		||||
    make test-debug TESTLIST=test_load_mapping_without_any_fields
 | 
			
		||||
    make valgrind-debug TESTLIST="test_load_no_log test_util_state_invalid"
 | 
			
		||||
 | 
			
		||||
To generate a test coverage report, `gcovr` is required:
 | 
			
		||||
 | 
			
		||||
    make coverage
 | 
			
		||||
 | 
			
		||||
Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
To generate both public API documentation, and documentation of CYAML's
 | 
			
		||||
internals, `doxygen` is required:
 | 
			
		||||
 | 
			
		||||
    make docs
 | 
			
		||||
 | 
			
		||||
Alternatively, the read the API documentation directly from the
 | 
			
		||||
[cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h)
 | 
			
		||||
header file.
 | 
			
		||||
 | 
			
		||||
There is also a [tutorial](docs/guide.md).
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
In addition to the documentation, you can study the [examples](examples/).
 | 
			
		||||
							
								
								
									
										15
									
								
								libs/libcyaml/docs/api.doxygen.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								libs/libcyaml/docs/api.doxygen.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
# SPDX-License-Identifier: ISC
 | 
			
		||||
 | 
			
		||||
# This generates documentation for developers using libcyaml.
 | 
			
		||||
 | 
			
		||||
PROJECT_NAME           = "CYAML"
 | 
			
		||||
OUTPUT_DIRECTORY       = "build/docs/api"
 | 
			
		||||
OPTIMIZE_OUTPUT_FOR_C  = YES
 | 
			
		||||
EXTRACT_STATIC         = NO
 | 
			
		||||
MARKDOWN_SUPPORT       = YES
 | 
			
		||||
INPUT                  = include README.md docs/guide.md
 | 
			
		||||
RECURSIVE              = YES
 | 
			
		||||
USE_MDFILE_AS_MAINPAGE = README.md
 | 
			
		||||
GENERATE_HTML          = YES
 | 
			
		||||
GENERATE_LATEX         = NO
 | 
			
		||||
QUIET                  = YES
 | 
			
		||||
							
								
								
									
										15
									
								
								libs/libcyaml/docs/devel.doxygen.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								libs/libcyaml/docs/devel.doxygen.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
# SPDX-License-Identifier: ISC
 | 
			
		||||
 | 
			
		||||
# This generates documentation for developers developing libcyaml.
 | 
			
		||||
 | 
			
		||||
PROJECT_NAME           = "CYAML Internals"
 | 
			
		||||
OUTPUT_DIRECTORY       = "build/docs/devel"
 | 
			
		||||
OPTIMIZE_OUTPUT_FOR_C  = YES
 | 
			
		||||
EXTRACT_STATIC         = YES
 | 
			
		||||
MARKDOWN_SUPPORT       = YES
 | 
			
		||||
INPUT                  = include src README.md docs/guide.md
 | 
			
		||||
RECURSIVE              = YES
 | 
			
		||||
USE_MDFILE_AS_MAINPAGE = README.md
 | 
			
		||||
GENERATE_HTML          = YES
 | 
			
		||||
GENERATE_LATEX         = NO
 | 
			
		||||
QUIET                  = YES
 | 
			
		||||
							
								
								
									
										160
									
								
								libs/libcyaml/docs/guide.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								libs/libcyaml/docs/guide.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,160 @@
 | 
			
		||||
LibCYAML: Tutorial
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
This document is intended for C developers wishing to make use of
 | 
			
		||||
[LibCYAML](https://github.com/tlsa/libcyaml).
 | 
			
		||||
 | 
			
		||||
Overview
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
If you want to use LibCYAML you'll need to have two things:
 | 
			
		||||
 | 
			
		||||
1. A consistent structure to the sort of YAML you want to load/save.
 | 
			
		||||
2. Some C data structure to load/save to/from.
 | 
			
		||||
 | 
			
		||||
LibCYAML's aim is to make this as simple as possible for the programmer.
 | 
			
		||||
However, LibCYAML knows nothing about either your data structure or the
 | 
			
		||||
"shape" of the YAML you want to load.  You provide this information by
 | 
			
		||||
defining "schemas", and passing them to LibCYAML.
 | 
			
		||||
 | 
			
		||||
> **Note**: If you need to handle arbitrary "free-form" YAML (e.g. for a tool
 | 
			
		||||
> to convert between YAML and JSON), then LibCYAML would not be much help.
 | 
			
		||||
> In such a case, I'd recommend using [libyaml](https://github.com/yaml/libyaml)
 | 
			
		||||
> directly.
 | 
			
		||||
 | 
			
		||||
A simple example: loading YAML
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
Let's say you want to load the following YAML document:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
name: Fibonacci
 | 
			
		||||
data:
 | 
			
		||||
  - 1
 | 
			
		||||
  - 1
 | 
			
		||||
  - 2
 | 
			
		||||
  - 3
 | 
			
		||||
  - 5
 | 
			
		||||
  - 8
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And you want to load it into the following C data structure:
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
struct numbers {
 | 
			
		||||
	char *name;
 | 
			
		||||
	int *data;
 | 
			
		||||
	unsigned data_count;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then we need to define a CYAML schema to describe these to LibCYAML.
 | 
			
		||||
 | 
			
		||||
> **Note**: Use the doxygen API documentation, or else the documentation in
 | 
			
		||||
> [cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h)
 | 
			
		||||
> in conjunction with this guide.
 | 
			
		||||
 | 
			
		||||
At the top level of the YAML is a mapping with two fields, "name" and
 | 
			
		||||
"data". 
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
name:
 | 
			
		||||
data:
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The first field is just a simple scalar value (it's neither
 | 
			
		||||
a mapping nor a sequence).  The second field has a sequence value.
 | 
			
		||||
 | 
			
		||||
We'll start by defining the CYAML schema for the "data" sequence,
 | 
			
		||||
since since that's the "deepest" non-scalar type.  The reason for
 | 
			
		||||
starting here will become clear later.
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
/* CYAML value schema for entries of the data sequence. */
 | 
			
		||||
static const cyaml_schema_value_t data_entry = {
 | 
			
		||||
	CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Here we're making a `cyaml_schema_value_t` for the entries in the
 | 
			
		||||
sequence.  There are various `CYAML_VALUE_{TYPE}` macros to assist with
 | 
			
		||||
this.  Here we're using `CYAML_VALUE_INT`, because the value is a signed
 | 
			
		||||
integer.  The parameters passed to the macro are `enum cyaml_flag`, and
 | 
			
		||||
the C data type of the value.
 | 
			
		||||
 | 
			
		||||
Now we can write the schema for the mapping.  First we'll construct
 | 
			
		||||
an array of `cyaml_schema_field_t` entries that describe each
 | 
			
		||||
field in the mapping.
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
/* CYAML mapping schema fields array for the top level mapping. */
 | 
			
		||||
static const cyaml_schema_field_t top_mapping_schema[] = {
 | 
			
		||||
	CYAML_FIELD_STRING_PTR(
 | 
			
		||||
		"name", CYAML_FLAG_POINTER, struct numbers, name, 0, CYAML_UNLIMITED),
 | 
			
		||||
	CYAML_FIELD_SEQUENCE(
 | 
			
		||||
		"data", CYAML_FLAG_POINTER, struct numbers, data, &data_entry, 0, CYAML_UNLIMITED),
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
There are `CYAML_FIELD_{TYPE}` helper macros to construct the mapping field
 | 
			
		||||
entries.  The array must be terminated by a `CYAML_FIELD_END` entry.
 | 
			
		||||
The helper macro parameters are specific to each `CYAML_FIELD_{TYPE}` macro.
 | 
			
		||||
 | 
			
		||||
The entry for the name field is of type string pointer.  You can consult the
 | 
			
		||||
documentation for the `CYAML_FIELD_{TYPE}` macros to see what the parameters
 | 
			
		||||
mean.
 | 
			
		||||
 | 
			
		||||
> **Note**: The field for the sequence takes a pointer to the sequence entry
 | 
			
		||||
> data type that we defined earlier as `data_entry`.
 | 
			
		||||
 | 
			
		||||
Finally we can define the schema for the top level value that gets passed to
 | 
			
		||||
the LibCYAML.
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
/* CYAML value schema for the top level mapping. */
 | 
			
		||||
static const cyaml_schema_value_t top_schema = {
 | 
			
		||||
	CYAML_VALUE_MAPPING(
 | 
			
		||||
		CYAML_FLAG_POINTER, struct numbers, top_mapping_schema),
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this case our top level value is a mapping type.  One of the parameters
 | 
			
		||||
needed for mappings is the array of field definitions.  In this case we're
 | 
			
		||||
passing the `top_mapping_schema` that we defined above.
 | 
			
		||||
 | 
			
		||||
```c
 | 
			
		||||
/* Create our CYAML configuration. */
 | 
			
		||||
static const cyaml_config_t config = {
 | 
			
		||||
	.log_fn = cyaml_log,            /* Use the default logging function. */
 | 
			
		||||
	.mem_fn = cyaml_mem,            /* Use the default memory allocator. */
 | 
			
		||||
	.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Where to store the loaded data */
 | 
			
		||||
struct numbers *n;
 | 
			
		||||
 | 
			
		||||
/* Load the file into n */
 | 
			
		||||
cyaml_err_t err = cyaml_load_file(argv[ARG_PATH_IN], &config,
 | 
			
		||||
		&top_schema, (cyaml_data_t **)&n, NULL);
 | 
			
		||||
if (err != CYAML_OK) {
 | 
			
		||||
	/* Handle error */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Use the data. */
 | 
			
		||||
printf("%s:\n", n->name);
 | 
			
		||||
for (unsigned i = 0; i < n->data_count; i++) {
 | 
			
		||||
	printf("  - %i\n", n->data[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free the data */
 | 
			
		||||
err = cyaml_free(&config, &top_schema, n, 0);
 | 
			
		||||
if (err != CYAML_OK) {
 | 
			
		||||
	/* Handle error */
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And that's it, the YAML is loaded into the custom C data structure.
 | 
			
		||||
 | 
			
		||||
You can find the code for this in the "numerical" example in the
 | 
			
		||||
[examples](../examples) directory.
 | 
			
		||||
							
								
								
									
										7
									
								
								libs/libcyaml/examples/numerical/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								libs/libcyaml/examples/numerical/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
Simple LibCYAML example
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
This has a simple YAML document for the Fibonacci sequence, and an example
 | 
			
		||||
LibCYAML binding to load/free the data from C.
 | 
			
		||||
 | 
			
		||||
The [tutorial](../../docs/guide.md) explains how this works.
 | 
			
		||||
							
								
								
									
										8
									
								
								libs/libcyaml/examples/numerical/fibonacci.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								libs/libcyaml/examples/numerical/fibonacci.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
name: Fibonacci
 | 
			
		||||
data:
 | 
			
		||||
  - 1
 | 
			
		||||
  - 1
 | 
			
		||||
  - 2
 | 
			
		||||
  - 3
 | 
			
		||||
  - 5
 | 
			
		||||
  - 8
 | 
			
		||||
							
								
								
									
										107
									
								
								libs/libcyaml/examples/numerical/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								libs/libcyaml/examples/numerical/main.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,107 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * C data structure for storing a project plan.
 | 
			
		||||
 *
 | 
			
		||||
 * This is what we want to load the YAML into.
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Structure for storing numerical sequence */
 | 
			
		||||
struct numbers {
 | 
			
		||||
	char *name;
 | 
			
		||||
	int *data;
 | 
			
		||||
	unsigned data_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * CYAML schema to tell libcyaml about both expected YAML and data structure.
 | 
			
		||||
 *
 | 
			
		||||
 * (Our CYAML schema is just a bunch of static const data.)
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* CYAML value schema for entries of the data sequence. */
 | 
			
		||||
static const cyaml_schema_value_t data_entry = {
 | 
			
		||||
	CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* CYAML mapping schema fields array for the top level mapping. */
 | 
			
		||||
static const cyaml_schema_field_t top_mapping_schema[] = {
 | 
			
		||||
	CYAML_FIELD_STRING_PTR("name", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct numbers, name,
 | 
			
		||||
			0, CYAML_UNLIMITED),
 | 
			
		||||
	CYAML_FIELD_SEQUENCE("data", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct numbers, data, &data_entry,
 | 
			
		||||
			0, CYAML_UNLIMITED),
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* CYAML value schema for the top level mapping. */
 | 
			
		||||
static const cyaml_schema_value_t top_schema = {
 | 
			
		||||
	CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
			struct numbers, top_mapping_schema),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * Actual code to load and save YAML doc using libcyaml.
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Our CYAML config.
 | 
			
		||||
 *
 | 
			
		||||
 * If you want to change it between calls, don't make it const.
 | 
			
		||||
 *
 | 
			
		||||
 * Here we have a very basic config.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_config_t config = {
 | 
			
		||||
	.log_fn = cyaml_log,            /* Use the default logging function. */
 | 
			
		||||
	.mem_fn = cyaml_mem,            /* Use the default memory allocator. */
 | 
			
		||||
	.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Main entry point from OS. */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	struct numbers *n;
 | 
			
		||||
	enum {
 | 
			
		||||
		ARG_PROG_NAME,
 | 
			
		||||
		ARG_PATH_IN,
 | 
			
		||||
		ARG__COUNT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* Handle args */
 | 
			
		||||
	if (argc != ARG__COUNT) {
 | 
			
		||||
		fprintf(stderr, "Usage:\n");
 | 
			
		||||
		fprintf(stderr, "  %s <INPUT>\n", argv[ARG_PROG_NAME]);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Load input file. */
 | 
			
		||||
	err = cyaml_load_file(argv[ARG_PATH_IN], &config,
 | 
			
		||||
			&top_schema, (cyaml_data_t **)&n, NULL);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use the data. */
 | 
			
		||||
	printf("%s:\n", n->name);
 | 
			
		||||
	for (unsigned i = 0; i < n->data_count; i++) {
 | 
			
		||||
		printf("  - %i\n", n->data[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Free the data */
 | 
			
		||||
	cyaml_free(&config, &top_schema, n, 0);
 | 
			
		||||
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								libs/libcyaml/examples/planner/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								libs/libcyaml/examples/planner/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
Simple LibCYAML example
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
This has a simple YAML document for project planning, and an example
 | 
			
		||||
LibCYAML binding to load/modify/save/free the data from C.
 | 
			
		||||
 | 
			
		||||
The C source code is heavliy documented to help explain how to
 | 
			
		||||
write a CYAML schema data structure.
 | 
			
		||||
							
								
								
									
										468
									
								
								libs/libcyaml/examples/planner/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								libs/libcyaml/examples/planner/main.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,468 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * C data structure for storing a project plan.
 | 
			
		||||
 *
 | 
			
		||||
 * This is what we want to load the YAML into.
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Enumeration of months of the year */
 | 
			
		||||
enum months {
 | 
			
		||||
	MONTH_JAN = 1,
 | 
			
		||||
	MONTH_FEB,
 | 
			
		||||
	MONTH_MAR,
 | 
			
		||||
	MONTH_APR,
 | 
			
		||||
	MONTH_MAY,
 | 
			
		||||
	MONTH_JUN,
 | 
			
		||||
	MONTH_JUL,
 | 
			
		||||
	MONTH_AUG,
 | 
			
		||||
	MONTH_SEP,
 | 
			
		||||
	MONTH_OCT,
 | 
			
		||||
	MONTH_NOV,
 | 
			
		||||
	MONTH_DEC
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Structure for storing dates */
 | 
			
		||||
struct date {
 | 
			
		||||
	uint8_t day;
 | 
			
		||||
	enum months month;
 | 
			
		||||
	uint16_t year;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Structure for storing durations */
 | 
			
		||||
struct duration {
 | 
			
		||||
	uint8_t hours;
 | 
			
		||||
	unsigned days;
 | 
			
		||||
	unsigned weeks;
 | 
			
		||||
	unsigned years;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Enumeration of task flags */
 | 
			
		||||
enum task_flags {
 | 
			
		||||
	FLAGS_NONE          = 0,
 | 
			
		||||
	FLAGS_IMPORTANT     = (1 << 0),
 | 
			
		||||
	FLAGS_ENGINEERING   = (1 << 1),
 | 
			
		||||
	FLAGS_DOCUMENTATION = (1 << 2),
 | 
			
		||||
	FLAGS_MANAGEMENT    = (1 << 3),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Structure for storing a task */
 | 
			
		||||
struct task {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	enum task_flags flags;
 | 
			
		||||
	struct duration estimate;
 | 
			
		||||
 | 
			
		||||
	const char **depends;
 | 
			
		||||
	unsigned depends_count;
 | 
			
		||||
 | 
			
		||||
	const char **people;
 | 
			
		||||
	unsigned n_people;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Top-level structure for storing a plan */
 | 
			
		||||
struct plan {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	struct date *start;
 | 
			
		||||
 | 
			
		||||
	const char **people;
 | 
			
		||||
	unsigned n_people;
 | 
			
		||||
 | 
			
		||||
	struct task *tasks;
 | 
			
		||||
	uint64_t tasks_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * CYAML schema to tell libcyaml about both expected YAML and data structure.
 | 
			
		||||
 *
 | 
			
		||||
 * (Our CYAML schema is just a bunch of static const data.)
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Mapping from "task_flags" strings to enum values for schema. */
 | 
			
		||||
static const cyaml_strval_t task_flags_strings[] = {
 | 
			
		||||
	{ "None",          FLAGS_NONE          },
 | 
			
		||||
	{ "Important",     FLAGS_IMPORTANT     },
 | 
			
		||||
	{ "Engineering",   FLAGS_ENGINEERING   },
 | 
			
		||||
	{ "Documentation", FLAGS_DOCUMENTATION },
 | 
			
		||||
	{ "Management",    FLAGS_MANAGEMENT    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Mapping from "month" strings to flag values for schema. */
 | 
			
		||||
static const cyaml_strval_t month_strings[] = {
 | 
			
		||||
	{ "January",   MONTH_JAN },
 | 
			
		||||
	{ "February",  MONTH_FEB },
 | 
			
		||||
	{ "March",     MONTH_MAR },
 | 
			
		||||
	{ "April",     MONTH_APR },
 | 
			
		||||
	{ "May",       MONTH_MAY },
 | 
			
		||||
	{ "June",      MONTH_JUN },
 | 
			
		||||
	{ "July",      MONTH_JUL },
 | 
			
		||||
	{ "August",    MONTH_AUG },
 | 
			
		||||
	{ "September", MONTH_SEP },
 | 
			
		||||
	{ "October",   MONTH_OCT },
 | 
			
		||||
	{ "November",  MONTH_NOV },
 | 
			
		||||
	{ "December",  MONTH_DEC },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Schema for string pointer values (used in sequences of strings). */
 | 
			
		||||
static const cyaml_schema_value_t string_ptr_schema = {
 | 
			
		||||
	CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The duration mapping's field definitions schema is an array.
 | 
			
		||||
 *
 | 
			
		||||
 * All the field entries will refer to their parent mapping's structure,
 | 
			
		||||
 * in this case, `struct duration`.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_field_t duration_fields_schema[] = {
 | 
			
		||||
	/* The fields here are all optional unsigned integers.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Note that if an optional field is unset in the YAML, its value
 | 
			
		||||
	 * will be zero in the C data structure.
 | 
			
		||||
	 *
 | 
			
		||||
	 * In all cases here, the YAML mapping key name (first parameter to
 | 
			
		||||
	 * the macros) matches the name of the associated member of the
 | 
			
		||||
	 * `duration` structure (fourth parameter).
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"hours", CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct duration, hours),
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"days", CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct duration, days),
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"weeks", CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct duration, weeks),
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"years", CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct duration, years),
 | 
			
		||||
 | 
			
		||||
	/* The field array must be terminated by an entry with a NULL key.
 | 
			
		||||
	 * Here we use the CYAML_FIELD_END macro for that. */
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The date mapping's field definitions schema is an array.
 | 
			
		||||
 *
 | 
			
		||||
 * All the field entries will refer to their parent mapping's structure,
 | 
			
		||||
 * in this case, `struct date`.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_field_t date_fields_schema[] = {
 | 
			
		||||
	/* The "day" and "year" fields are just normal UNIT CYAML types.
 | 
			
		||||
	 * See the comments for duration_fields_schema for details.
 | 
			
		||||
	 * The only difference is neither of these are optional.
 | 
			
		||||
	 * Note: The order of the fields in this array doesn't matter.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"day", CYAML_FLAG_DEFAULT,
 | 
			
		||||
			struct date, day),
 | 
			
		||||
 | 
			
		||||
	CYAML_FIELD_UINT(
 | 
			
		||||
			"year", CYAML_FLAG_DEFAULT,
 | 
			
		||||
			struct date, year),
 | 
			
		||||
 | 
			
		||||
	/* The month field is an enum.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "month".
 | 
			
		||||
	 * C structure member for this key: "month".
 | 
			
		||||
	 * Array mapping strings to values: month_strings
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is ENUM, so an array of cyaml_strval_t must be
 | 
			
		||||
	 * provided to map from string to values.
 | 
			
		||||
	 * Note that we're not setting the strict flag here so both strings and
 | 
			
		||||
	 * numbers are accepted in the YAML.  (For example, both "4" and "April"
 | 
			
		||||
	 * would be accepted.)
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_ENUM(
 | 
			
		||||
			"month", CYAML_FLAG_DEFAULT,
 | 
			
		||||
			struct date, month, month_strings,
 | 
			
		||||
			CYAML_ARRAY_LEN(month_strings)),
 | 
			
		||||
 | 
			
		||||
	/* The field array must be terminated by an entry with a NULL key.
 | 
			
		||||
	 * Here we use the CYAML_FIELD_END macro for that. */
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The task mapping's field definitions schema is an array.
 | 
			
		||||
 *
 | 
			
		||||
 * All the field entries will refer to their parent mapping's structure,
 | 
			
		||||
 * in this case, `struct task`.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_field_t task_fields_schema[] = {
 | 
			
		||||
	/* The first field in the mapping is a task name.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "name".
 | 
			
		||||
	 * C structure member for this key: "name".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is string pointer, and we have no minimum or maximum
 | 
			
		||||
	 * string length constraints.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_STRING_PTR(
 | 
			
		||||
			"name", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct task, name, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* The flags field is a flags value.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "flags".
 | 
			
		||||
	 * C structure member for this key: "flags".
 | 
			
		||||
	 * Array mapping strings to values: task_flags_strings
 | 
			
		||||
	 *
 | 
			
		||||
	 * In the YAML a CYAML flags value should be a sequence of scalars.
 | 
			
		||||
	 * The values of each set scalar is looked up the in array of
 | 
			
		||||
	 * string/value mappings, and the values are bitwise ORed together.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Note that we're setting the strict flag here so only strings
 | 
			
		||||
	 * present in task_flags_strings are allowed, and numbers are not.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We make the field optional so when there are no flags set, the field
 | 
			
		||||
	 * can be omitted from the YAML.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_FLAGS(
 | 
			
		||||
			"flags", CYAML_FLAG_OPTIONAL | CYAML_FLAG_STRICT,
 | 
			
		||||
			struct task, flags, task_flags_strings,
 | 
			
		||||
			CYAML_ARRAY_LEN(task_flags_strings)),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the task estimate.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "estimate".
 | 
			
		||||
	 * C structure member for this key: "estimate".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a mapping.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a mapping type, the schema for its mapping's fields must
 | 
			
		||||
	 * be provided too.  In this case, it's `duration_fields_schema`.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_MAPPING(
 | 
			
		||||
			"estimate", CYAML_FLAG_DEFAULT,
 | 
			
		||||
			struct task, estimate, duration_fields_schema),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the tasks that this task depends on.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "depends".
 | 
			
		||||
	 * C structure member for this key: "depends".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a sequence.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a sequence type, the value schema for its entries must
 | 
			
		||||
	 * be provided too.  In this case, it's string_ptr_schema.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's not a sequence of a fixed-length, we must tell CYAML
 | 
			
		||||
	 * where the sequence entry count is to be stored.  In this case, it
 | 
			
		||||
	 * goes in the "depends_count" C structure member in `struct task`.
 | 
			
		||||
	 * Since this is "depends" with the "_count" postfix, we can use
 | 
			
		||||
	 * the following macro, which assumes a postfix of "_count" in the
 | 
			
		||||
	 * struct member name.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_SEQUENCE(
 | 
			
		||||
			"depends", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct task, depends,
 | 
			
		||||
			&string_ptr_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the task people.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "people".
 | 
			
		||||
	 * C structure member for this key: "people".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a sequence.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a sequence type, the value schema for its entries must
 | 
			
		||||
	 * be provided too.  In this case, it's string_ptr_schema.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's not a sequence of a fixed-length, we must tell CYAML
 | 
			
		||||
	 * where the sequence entry count is to be stored.  In this case, it
 | 
			
		||||
	 * goes in the "n_people" C structure member in `struct plan`.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_SEQUENCE_COUNT(
 | 
			
		||||
			"people", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
 | 
			
		||||
			struct task, people, n_people,
 | 
			
		||||
			&string_ptr_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* The field array must be terminated by an entry with a NULL key.
 | 
			
		||||
	 * Here we use the CYAML_FIELD_END macro for that. */
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The value for a task is a mapping.
 | 
			
		||||
 *
 | 
			
		||||
 * Its fields are defined in task_fields_schema.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_value_t task_schema = {
 | 
			
		||||
	CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
 | 
			
		||||
			struct task, task_fields_schema),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The plan mapping's field definitions schema is an array.
 | 
			
		||||
 *
 | 
			
		||||
 * All the field entries will refer to their parent mapping's structure,
 | 
			
		||||
 * in this case, `struct plan`.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_field_t plan_fields_schema[] = {
 | 
			
		||||
	/* The first field in the mapping is a project name.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "project".
 | 
			
		||||
	 * C structure member for this key: "name".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is string pointer, and we have no minimum or maximum
 | 
			
		||||
	 * string length constraints.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_STRING_PTR(
 | 
			
		||||
			"project", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct plan, name, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the project start date.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "start".
 | 
			
		||||
	 * C structure member for this key: "start".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a mapping pointer.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a mapping type, the schema for its mapping's fields must
 | 
			
		||||
	 * be provided too.  In this case, it's `date_fields_schema`.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_MAPPING_PTR(
 | 
			
		||||
			"start", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct plan, start, date_fields_schema),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the project people.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "people".
 | 
			
		||||
	 * C structure member for this key: "people".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a sequence.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a sequence type, the value schema for its entries must
 | 
			
		||||
	 * be provided too.  In this case, it's string_ptr_schema.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's not a sequence of a fixed-length, we must tell CYAML
 | 
			
		||||
	 * where the sequence entry count is to be stored.  In this case, it
 | 
			
		||||
	 * goes in the "n_people" C structure member in `struct plan`.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_SEQUENCE_COUNT(
 | 
			
		||||
			"people", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct plan, people, n_people,
 | 
			
		||||
			&string_ptr_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* The next field is the project tasks.
 | 
			
		||||
	 *
 | 
			
		||||
	 * YAML key: "tasks".
 | 
			
		||||
	 * C structure member for this key: "tasks".
 | 
			
		||||
	 *
 | 
			
		||||
	 * Its CYAML type is a sequence.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's a sequence type, the value schema for its entries must
 | 
			
		||||
	 * be provided too.  In this case, it's task_schema.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Since it's not a sequence of a fixed-length, we must tell CYAML
 | 
			
		||||
	 * where the sequence entry count is to be stored.  In this case, it
 | 
			
		||||
	 * goes in the "tasks_count" C structure member in `struct plan`.
 | 
			
		||||
	 * Since this is "tasks" with the "_count" postfix, we can use
 | 
			
		||||
	 * the following macro, which assumes a postfix of "_count" in the
 | 
			
		||||
	 * struct member name.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_SEQUENCE(
 | 
			
		||||
			"tasks", CYAML_FLAG_POINTER,
 | 
			
		||||
			struct plan, tasks,
 | 
			
		||||
			&task_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
 | 
			
		||||
	/* If the YAML contains a field that our program is not interested in
 | 
			
		||||
	 * we can ignore it, so the value of the field will not be loaded.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Note that unless the OPTIONAL flag is set, the ignored field must
 | 
			
		||||
	 * be present.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Another way of handling this would be to use the config flag
 | 
			
		||||
	 * to ignore unknown keys.  This config is passed to libcyaml
 | 
			
		||||
	 * separately from the schema.
 | 
			
		||||
	 */
 | 
			
		||||
	CYAML_FIELD_IGNORE("irrelevant", CYAML_FLAG_OPTIONAL),
 | 
			
		||||
 | 
			
		||||
	/* The field array must be terminated by an entry with a NULL key.
 | 
			
		||||
	 * Here we use the CYAML_FIELD_END macro for that. */
 | 
			
		||||
	CYAML_FIELD_END
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Top-level schema.  The top level value for the plan is a mapping.
 | 
			
		||||
 *
 | 
			
		||||
 * Its fields are defined in plan_fields_schema.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_schema_value_t plan_schema = {
 | 
			
		||||
	CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
			struct plan, plan_fields_schema),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * Actual code to load and save YAML doc using libcyaml.
 | 
			
		||||
 ******************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Our CYAML config.
 | 
			
		||||
 *
 | 
			
		||||
 * If you want to change it between calls, don't make it const.
 | 
			
		||||
 *
 | 
			
		||||
 * Here we have a very basic config.
 | 
			
		||||
 */
 | 
			
		||||
static const cyaml_config_t config = {
 | 
			
		||||
	.log_fn = cyaml_log,            /* Use the default logging function. */
 | 
			
		||||
	.mem_fn = cyaml_mem,            /* Use the default memory allocator. */
 | 
			
		||||
	.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Main entry point from OS. */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	struct plan *plan;
 | 
			
		||||
	enum {
 | 
			
		||||
		ARG_PROG_NAME,
 | 
			
		||||
		ARG_PATH_IN,
 | 
			
		||||
		ARG_PATH_OUT,
 | 
			
		||||
		ARG__COUNT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* Handle args */
 | 
			
		||||
	if (argc != ARG__COUNT) {
 | 
			
		||||
		fprintf(stderr, "Usage:\n");
 | 
			
		||||
		fprintf(stderr, "  %s <INPUT> <OUTPUT>\n", argv[ARG_PROG_NAME]);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Load input file. */
 | 
			
		||||
	err = cyaml_load_file(argv[ARG_PATH_IN], &config,
 | 
			
		||||
			&plan_schema, (void **) &plan, NULL);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use the data. */
 | 
			
		||||
	printf("Project: %s\n", plan->name);
 | 
			
		||||
	for (unsigned i = 0; i < plan->tasks_count; i++) {
 | 
			
		||||
		printf("%u. %s\n", i + 1, plan->tasks[i].name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Modify the data */
 | 
			
		||||
	plan->tasks[0].estimate.days += 3;
 | 
			
		||||
	plan->tasks[0].estimate.weeks += 1;
 | 
			
		||||
 | 
			
		||||
	/* Save data to new YAML file. */
 | 
			
		||||
	err = cyaml_save_file(argv[ARG_PATH_OUT], &config,
 | 
			
		||||
			&plan_schema, plan, 0);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
 | 
			
		||||
		cyaml_free(&config, &plan_schema, plan, 0);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Free the data */
 | 
			
		||||
	cyaml_free(&config, &plan_schema, plan, 0);
 | 
			
		||||
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								libs/libcyaml/examples/planner/project.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								libs/libcyaml/examples/planner/project.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
project: Write new browser layout engine.
 | 
			
		||||
start:
 | 
			
		||||
  day: 1
 | 
			
		||||
  month: June
 | 
			
		||||
  year: 2018
 | 
			
		||||
people:
 | 
			
		||||
  - Stephen
 | 
			
		||||
  - Neil
 | 
			
		||||
  - Alex
 | 
			
		||||
irrelevant:
 | 
			
		||||
  - details:
 | 
			
		||||
      - the app doesn't need this stuff
 | 
			
		||||
      - so it should be able to ignore it
 | 
			
		||||
 | 
			
		||||
tasks:
 | 
			
		||||
  - name: Read the HTML and CSS specs.
 | 
			
		||||
    flags:
 | 
			
		||||
      - Important
 | 
			
		||||
    estimate:
 | 
			
		||||
      weeks: 2
 | 
			
		||||
    people:
 | 
			
		||||
      - Stephen
 | 
			
		||||
      - Neil
 | 
			
		||||
 | 
			
		||||
  - name: Think of name for library.
 | 
			
		||||
    estimate:
 | 
			
		||||
      hours: 1
 | 
			
		||||
 | 
			
		||||
  - name: Create project repo.
 | 
			
		||||
    estimate:
 | 
			
		||||
      hours: 1
 | 
			
		||||
    depends:
 | 
			
		||||
      - Think of name for library.
 | 
			
		||||
    people:
 | 
			
		||||
      - Alex
 | 
			
		||||
 | 
			
		||||
  - name: Initial design of library API.
 | 
			
		||||
    flags:
 | 
			
		||||
      - Important
 | 
			
		||||
      - Engineering
 | 
			
		||||
    depends:
 | 
			
		||||
      - Read the HTML and CSS specs.
 | 
			
		||||
      - Create project repo.
 | 
			
		||||
    estimate:
 | 
			
		||||
      days: 1
 | 
			
		||||
 | 
			
		||||
  - name: Plan the initial implementation
 | 
			
		||||
    flags:
 | 
			
		||||
      - Engineering
 | 
			
		||||
      - Documentation
 | 
			
		||||
      - Management
 | 
			
		||||
    depends:
 | 
			
		||||
      - Initial design of library API.
 | 
			
		||||
    estimate:
 | 
			
		||||
      days: 6
 | 
			
		||||
    people:
 | 
			
		||||
      - Stephen
 | 
			
		||||
      - Alex
 | 
			
		||||
							
								
								
									
										1696
									
								
								libs/libcyaml/include/cyaml/cyaml.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1696
									
								
								libs/libcyaml/include/cyaml/cyaml.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								libs/libcyaml/libcyaml.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								libs/libcyaml/libcyaml.pc.in
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
prefix=PREFIX
 | 
			
		||||
exec_prefix=${prefix}
 | 
			
		||||
libdir=${exec_prefix}/LIBDIR
 | 
			
		||||
includedir=${prefix}/INCLUDEDIR
 | 
			
		||||
 | 
			
		||||
Name: libcyaml
 | 
			
		||||
Description: Schema-based YAML parsing and serialisation
 | 
			
		||||
Version: VERSION
 | 
			
		||||
Libs: -L${libdir} -lcyaml -lyaml
 | 
			
		||||
Cflags: -I${includedir}
 | 
			
		||||
							
								
								
									
										126
									
								
								libs/libcyaml/src/data.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								libs/libcyaml/src/data.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML functions for manipulating client data structures.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CYAML_DATA_H
 | 
			
		||||
#define CYAML_DATA_H
 | 
			
		||||
 | 
			
		||||
#include "cyaml/cyaml.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Write a value of up to eight bytes to data_target.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  value       The value to write.
 | 
			
		||||
 * \param[in]  entry_size  The number of bytes of value to write.
 | 
			
		||||
 * \param[in]  data_tgt    The address to write to.
 | 
			
		||||
 * \return \ref CYAML_OK on success, or appropriate error code otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static inline cyaml_err_t cyaml_data_write(
 | 
			
		||||
		uint64_t value,
 | 
			
		||||
		uint64_t entry_size,
 | 
			
		||||
		uint8_t *data_tgt)
 | 
			
		||||
{
 | 
			
		||||
	const uint8_t *value_bytes = (uint8_t *)&value;
 | 
			
		||||
 | 
			
		||||
	if (entry_size == 0 || entry_size > sizeof(value)) {
 | 
			
		||||
		return CYAML_ERR_INVALID_DATA_SIZE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cyaml__host_is_big_endian()) {
 | 
			
		||||
		value_bytes += sizeof(value) - entry_size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(data_tgt, value_bytes, entry_size);
 | 
			
		||||
 | 
			
		||||
	return CYAML_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Write a pointer to data.
 | 
			
		||||
 *
 | 
			
		||||
 * This is a wrapper for \ref cyaml_data_write that does a compile time
 | 
			
		||||
 * assertion on the pointer size, so it can never return a runtime error.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  ptr         The pointer address to write.
 | 
			
		||||
 * \param[in]  data_target The address to write to.
 | 
			
		||||
 */
 | 
			
		||||
static inline void cyaml_data_write_pointer(
 | 
			
		||||
		const void *ptr,
 | 
			
		||||
		uint8_t *data_target)
 | 
			
		||||
{
 | 
			
		||||
	/* Refuse to build on platforms where sizeof pointer would
 | 
			
		||||
	 * lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */
 | 
			
		||||
	static_assert(sizeof(char *) >  0, "Incompatible pointer size.");
 | 
			
		||||
	static_assert(sizeof(char *) <= sizeof(uint64_t),
 | 
			
		||||
			"Incompatible pointer size.");
 | 
			
		||||
 | 
			
		||||
	CYAML_UNUSED(cyaml_data_write((uint64_t)ptr, sizeof(ptr), data_target));
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read a value of up to eight bytes from data.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  entry_size  The number of bytes to read.
 | 
			
		||||
 * \param[in]  data        The address to read from.
 | 
			
		||||
 * \param[out] error_out   Returns the error code.  \ref CYAML_OK on success,
 | 
			
		||||
 *                         or appropriate error otherwise.
 | 
			
		||||
 * \return On success, returns the value read from data.
 | 
			
		||||
 *         On failure, returns 0.
 | 
			
		||||
 */
 | 
			
		||||
static inline uint64_t cyaml_data_read(
 | 
			
		||||
		uint64_t entry_size,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		cyaml_err_t *error_out)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t ret = 0;
 | 
			
		||||
	uint8_t *ret_bytes = (uint8_t *)&ret;
 | 
			
		||||
 | 
			
		||||
	if (entry_size == 0 || entry_size > sizeof(ret)) {
 | 
			
		||||
		*error_out = CYAML_ERR_INVALID_DATA_SIZE;
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cyaml__host_is_big_endian()) {
 | 
			
		||||
		ret_bytes += sizeof(ret) - entry_size;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(ret_bytes, data, entry_size);
 | 
			
		||||
 | 
			
		||||
	*error_out = CYAML_OK;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read a pointer from data.
 | 
			
		||||
 *
 | 
			
		||||
 * This is a wrapper for \ref cyaml_data_read that does a compile time
 | 
			
		||||
 * assertion on the pointer size, so it can never return a runtime error.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  data        The address to read from.
 | 
			
		||||
 * \return Returns the value read from data.
 | 
			
		||||
 */
 | 
			
		||||
static inline uint8_t * cyaml_data_read_pointer(
 | 
			
		||||
		const uint8_t *data)
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
 | 
			
		||||
	/* Refuse to build on platforms where sizeof pointer would
 | 
			
		||||
	 * lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */
 | 
			
		||||
	static_assert(sizeof(char *) >  0, "Incompatible pointer size.");
 | 
			
		||||
	static_assert(sizeof(char *) <= sizeof(uint64_t),
 | 
			
		||||
			"Incompatible pointer size.");
 | 
			
		||||
 | 
			
		||||
	return (void *)cyaml_data_read(sizeof(char *), data, &err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										161
									
								
								libs/libcyaml/src/free.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								libs/libcyaml/src/free.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2019 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief Free data structures created by the CYAML load functions.
 | 
			
		||||
 *
 | 
			
		||||
 * As described in the public API for \ref cyaml_free(), it is preferable for
 | 
			
		||||
 * clients to write their own free routines, tailored for their data structure.
 | 
			
		||||
 *
 | 
			
		||||
 * Recursion and stack usage
 | 
			
		||||
 * -------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * This generic CYAML free routine is implemented using recursion, rather
 | 
			
		||||
 * than iteration with a heap-allocated stack.  This is because recursion
 | 
			
		||||
 * seems less bad than allocating within the free code, and the stack-cost
 | 
			
		||||
 * of these functions isn't huge.  The maximum recursion depth is of course
 | 
			
		||||
 * bound by the schema, however schemas for recursively nesting data structures
 | 
			
		||||
 * are unbound, e.g. for a data tree structure.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "data.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "mem.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal function for freeing a CYAML-parsed data structure.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  cfg     The client's CYAML library config.
 | 
			
		||||
 * \param[in]  schema  The schema describing how to free `data`.
 | 
			
		||||
 * \param[in]  data    The data structure to be freed.
 | 
			
		||||
 * \param[in]  count   If data is of type \ref CYAML_SEQUENCE, this is the
 | 
			
		||||
 *                     number of entries in the sequence.
 | 
			
		||||
 */
 | 
			
		||||
static void cyaml__free_value(
 | 
			
		||||
		const cyaml_config_t *cfg,
 | 
			
		||||
		const cyaml_schema_value_t *schema,
 | 
			
		||||
		uint8_t * data,
 | 
			
		||||
		uint64_t count);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal function for freeing a CYAML-parsed sequence.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  cfg              The client's CYAML library config.
 | 
			
		||||
 * \param[in]  sequence_schema  The schema describing how to free `data`.
 | 
			
		||||
 * \param[in]  data             The data structure to be freed.
 | 
			
		||||
 * \param[in]  count            The sequence's entry count.
 | 
			
		||||
 */
 | 
			
		||||
static void cyaml__free_sequence(
 | 
			
		||||
		const cyaml_config_t *cfg,
 | 
			
		||||
		const cyaml_schema_value_t *sequence_schema,
 | 
			
		||||
		uint8_t * const data,
 | 
			
		||||
		uint64_t count)
 | 
			
		||||
{
 | 
			
		||||
	const cyaml_schema_value_t *schema = sequence_schema->sequence.entry;
 | 
			
		||||
	uint32_t data_size = schema->data_size;
 | 
			
		||||
 | 
			
		||||
	cyaml__log(cfg, CYAML_LOG_DEBUG,
 | 
			
		||||
			"Free: Freeing sequence with count: %u\n", count);
 | 
			
		||||
 | 
			
		||||
	if (schema->flags & CYAML_FLAG_POINTER) {
 | 
			
		||||
		data_size = sizeof(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < count; i++) {
 | 
			
		||||
		cyaml__log(cfg, CYAML_LOG_DEBUG,
 | 
			
		||||
				"Free: Freeing sequence entry: %u\n", i);
 | 
			
		||||
		cyaml__free_value(cfg, schema, data + data_size * i, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal function for freeing a CYAML-parsed mapping.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  cfg             The client's CYAML library config.
 | 
			
		||||
 * \param[in]  mapping_schema  The schema describing how to free `data`.
 | 
			
		||||
 * \param[in]  data            The data structure to be freed.
 | 
			
		||||
 */
 | 
			
		||||
static void cyaml__free_mapping(
 | 
			
		||||
		const cyaml_config_t *cfg,
 | 
			
		||||
		const cyaml_schema_value_t *mapping_schema,
 | 
			
		||||
		uint8_t * const data)
 | 
			
		||||
{
 | 
			
		||||
	const cyaml_schema_field_t *schema = mapping_schema->mapping.fields;
 | 
			
		||||
 | 
			
		||||
	while (schema->key != NULL) {
 | 
			
		||||
		uint64_t count = 0;
 | 
			
		||||
		cyaml__log(cfg, CYAML_LOG_DEBUG,
 | 
			
		||||
				"Free: Freeing key: %s (at offset: %u)\n",
 | 
			
		||||
				schema->key, (unsigned)schema->data_offset);
 | 
			
		||||
		if (schema->value.type == CYAML_SEQUENCE) {
 | 
			
		||||
			cyaml_err_t err;
 | 
			
		||||
			count = cyaml_data_read(schema->count_size,
 | 
			
		||||
					data + schema->count_offset, &err);
 | 
			
		||||
			if (err != CYAML_OK) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		cyaml__free_value(cfg, &schema->value,
 | 
			
		||||
				data + schema->data_offset, count);
 | 
			
		||||
		schema++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is documented at the forward declaration above. */
 | 
			
		||||
static void cyaml__free_value(
 | 
			
		||||
		const cyaml_config_t *cfg,
 | 
			
		||||
		const cyaml_schema_value_t *schema,
 | 
			
		||||
		uint8_t * data,
 | 
			
		||||
		uint64_t count)
 | 
			
		||||
{
 | 
			
		||||
	if (schema->flags & CYAML_FLAG_POINTER) {
 | 
			
		||||
		data = cyaml_data_read_pointer(data);
 | 
			
		||||
		if (data == NULL) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (schema->type == CYAML_MAPPING) {
 | 
			
		||||
		cyaml__free_mapping(cfg, schema, data);
 | 
			
		||||
	} else if (schema->type == CYAML_SEQUENCE ||
 | 
			
		||||
	           schema->type == CYAML_SEQUENCE_FIXED) {
 | 
			
		||||
		if (schema->type == CYAML_SEQUENCE_FIXED) {
 | 
			
		||||
			count = schema->sequence.max;
 | 
			
		||||
		}
 | 
			
		||||
		cyaml__free_sequence(cfg, schema, data, count);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (schema->flags & CYAML_FLAG_POINTER) {
 | 
			
		||||
		cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing: %p\n", data);
 | 
			
		||||
		cyaml__free(cfg, data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in include/cyaml/cyaml.h */
 | 
			
		||||
cyaml_err_t cyaml_free(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		const cyaml_schema_value_t *schema,
 | 
			
		||||
		cyaml_data_t *data,
 | 
			
		||||
		unsigned seq_count)
 | 
			
		||||
{
 | 
			
		||||
	if (config == NULL) {
 | 
			
		||||
		return CYAML_ERR_BAD_PARAM_NULL_CONFIG;
 | 
			
		||||
	}
 | 
			
		||||
	if (config->mem_fn == NULL) {
 | 
			
		||||
		return CYAML_ERR_BAD_CONFIG_NULL_MEMFN;
 | 
			
		||||
	}
 | 
			
		||||
	if (schema == NULL) {
 | 
			
		||||
		return CYAML_ERR_BAD_PARAM_NULL_SCHEMA;
 | 
			
		||||
	}
 | 
			
		||||
	cyaml__log(config, CYAML_LOG_DEBUG, "Free: Top level data: %p\n", data);
 | 
			
		||||
	cyaml__free_value(config, schema, (void *)&data, seq_count);
 | 
			
		||||
	return CYAML_OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2751
									
								
								libs/libcyaml/src/load.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2751
									
								
								libs/libcyaml/src/load.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										35
									
								
								libs/libcyaml/src/mem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								libs/libcyaml/src/mem.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML memory allocation handling.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "mem.h"
 | 
			
		||||
 | 
			
		||||
/** Macro to squash unused variable compiler warnings. */
 | 
			
		||||
#define CYAML_UNUSED(_x) ((void)(_x))
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in include/cyaml/cyaml.h */
 | 
			
		||||
void * cyaml_mem(
 | 
			
		||||
		void *ctx,
 | 
			
		||||
		void *ptr,
 | 
			
		||||
		size_t size)
 | 
			
		||||
{
 | 
			
		||||
	CYAML_UNUSED(ctx);
 | 
			
		||||
 | 
			
		||||
	if (size == 0) {
 | 
			
		||||
		free(ptr);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return realloc(ptr, size);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								libs/libcyaml/src/mem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								libs/libcyaml/src/mem.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018-2020 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML memory allocation handling.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CYAML_MEM_H
 | 
			
		||||
#define CYAML_MEM_H
 | 
			
		||||
 | 
			
		||||
#include "cyaml/cyaml.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper for freeing using the client's choice of allocator routine.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config  The CYAML client config.
 | 
			
		||||
 * \param[in]  ptr     Pointer to allocation to free.
 | 
			
		||||
 */
 | 
			
		||||
static inline void cyaml__free(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		void *ptr)
 | 
			
		||||
{
 | 
			
		||||
	config->mem_fn(config->mem_ctx, ptr, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper for new allocations using the client's choice of allocator routine.
 | 
			
		||||
 *
 | 
			
		||||
 * \note On failure, any existing allocation is still owned by the caller, and
 | 
			
		||||
 *       they are responsible for freeing it.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config        The CYAML client config.
 | 
			
		||||
 * \param[in]  ptr           The existing allocation or NULL.
 | 
			
		||||
 * \param[in]  current_size  Size of the current allocation.  (Only needed if
 | 
			
		||||
 *                           `clean != false`).
 | 
			
		||||
 * \param[in]  new_size      The number of bytes to resize allocation to.
 | 
			
		||||
 * \param[in]  clean         Only applies if `new_size > current_size`.
 | 
			
		||||
 *                           If `false`, the new memory is uninitialised,
 | 
			
		||||
 *                           if `true`, the new memory is initialised to zero.
 | 
			
		||||
 * \return Pointer to allocation on success, or `NULL` on failure.
 | 
			
		||||
 */
 | 
			
		||||
static inline void * cyaml__realloc(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		void *ptr,
 | 
			
		||||
		size_t current_size,
 | 
			
		||||
		size_t new_size,
 | 
			
		||||
		bool clean)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t *temp = config->mem_fn(config->mem_ctx, ptr, new_size);
 | 
			
		||||
	if (temp == NULL) {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (clean && (new_size > current_size)) {
 | 
			
		||||
		memset(temp + current_size, 0, new_size - current_size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper for new allocations using the client's choice of allocator routine.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config  The CYAML client config.
 | 
			
		||||
 * \param[in]  size    The number of bytes to allocate.
 | 
			
		||||
 * \param[in]  clean   If `false`, the memory is uninitialised, if `true`,
 | 
			
		||||
 *                     the memory is initialised to zero.
 | 
			
		||||
 * \return Pointer to allocation on success, or `NULL` on failure.
 | 
			
		||||
 */
 | 
			
		||||
static inline void * cyaml__alloc(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		size_t size,
 | 
			
		||||
		bool clean)
 | 
			
		||||
{
 | 
			
		||||
	return cyaml__realloc(config, NULL, 0, size, clean);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper for string duplication using the client's choice of allocator routine.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config   The CYAML client config.
 | 
			
		||||
 * \param[in]  str      The string to duplicate.
 | 
			
		||||
 * \param[in]  len_out  If non-NULL, updated to length of string on success.
 | 
			
		||||
 * \return Pointer to new string on success, or `NULL` on failure.
 | 
			
		||||
 */
 | 
			
		||||
static inline char * cyaml__strdup(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		const char *str,
 | 
			
		||||
		size_t *len_out)
 | 
			
		||||
{
 | 
			
		||||
	size_t len = strlen(str) + 1;
 | 
			
		||||
	char *dup = cyaml__alloc(config, len, false);
 | 
			
		||||
	if (dup == NULL) {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(dup, str, len);
 | 
			
		||||
 | 
			
		||||
	if (len_out != NULL) {
 | 
			
		||||
		*len_out = len - 1;
 | 
			
		||||
	}
 | 
			
		||||
	return dup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1516
									
								
								libs/libcyaml/src/save.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1516
									
								
								libs/libcyaml/src/save.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										259
									
								
								libs/libcyaml/src/utf8.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								libs/libcyaml/src/utf8.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,259 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018-2019 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML functions for handling utf8 text.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include "utf8.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get expected byte-length of UTF8 character.
 | 
			
		||||
 *
 | 
			
		||||
 * Finds the number of bytes expected for the UTF8 sequence starting with
 | 
			
		||||
 * the given byte.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  b  First byte of UTF8 sequence.
 | 
			
		||||
 * \return the byte width of the character or 0 if invalid.
 | 
			
		||||
 */
 | 
			
		||||
static inline unsigned cyaml_utf8_char_len(uint8_t b)
 | 
			
		||||
{
 | 
			
		||||
	if (!(b & 0x80)) {
 | 
			
		||||
		return 1;
 | 
			
		||||
	} else switch (b >> 3) {
 | 
			
		||||
		case 0x18:
 | 
			
		||||
		case 0x19:
 | 
			
		||||
		case 0x1a:
 | 
			
		||||
		case 0x1b:
 | 
			
		||||
			return 2;
 | 
			
		||||
		case 0x1c:
 | 
			
		||||
		case 0x1d:
 | 
			
		||||
			return 3;
 | 
			
		||||
		case 0x1e:
 | 
			
		||||
			return 4;
 | 
			
		||||
	}
 | 
			
		||||
	return 0; /* Invalid */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in utf8.h. */
 | 
			
		||||
unsigned cyaml_utf8_get_codepoint(
 | 
			
		||||
		const uint8_t *s,
 | 
			
		||||
		unsigned *len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned c = 0;
 | 
			
		||||
	bool sf = false;
 | 
			
		||||
 | 
			
		||||
	if (*len == 1) {
 | 
			
		||||
		return s[0];
 | 
			
		||||
	} else if ((*len > 1) && (*len <= 4)) {
 | 
			
		||||
		/* Compose first byte into codepoint. */
 | 
			
		||||
		c |= (s[0] & ((1u << (7 - *len)) - 1u)) << ((*len - 1) * 6);
 | 
			
		||||
 | 
			
		||||
		/* Handle continuation bytes. */
 | 
			
		||||
		for (unsigned i = 1; i < *len; i++) {
 | 
			
		||||
			/* Check continuation byte begins with 0b10xxxxxx. */
 | 
			
		||||
			if ((s[i] & 0xc0) != 0x80) {
 | 
			
		||||
				/* Need to shorten length so we don't consume
 | 
			
		||||
				 * this byte with this invalid character. */
 | 
			
		||||
				*len -= i;
 | 
			
		||||
				goto invalid;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Compose continuation byte into codepoint. */
 | 
			
		||||
			c |= (0x3fu & s[i]) << ((*len - i - 1) * 6);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Non-shortest forms are forbidden.
 | 
			
		||||
	 *
 | 
			
		||||
	 * The following table shows the bits available for each
 | 
			
		||||
	 * byte sequence length, as well as the bit-delta to the
 | 
			
		||||
	 * shorter sequence.
 | 
			
		||||
	 *
 | 
			
		||||
	 * | Bytes | Bits | Bit delta | Data                    |
 | 
			
		||||
	 * | ----- | ---- | --------- | ----------------------- |
 | 
			
		||||
	 * | 1     |  7   | N/A       | 0xxxxxxx                |
 | 
			
		||||
	 * | 2     |  11  | 4         | 110xxxxx + 10xxxxxx x 1 |
 | 
			
		||||
	 * | 3     |  16  | 5         | 1110xxxx + 10xxxxxx x 2 |
 | 
			
		||||
	 * | 4     |  21  | 5         | 11110xxx + 10xxxxxx x 3 |
 | 
			
		||||
	 *
 | 
			
		||||
	 * So here we check that the top "bit-delta" bits are not all
 | 
			
		||||
	 * clear for the byte length,
 | 
			
		||||
	 */
 | 
			
		||||
	switch (*len) {
 | 
			
		||||
	case 2: sf = (c & (((1 << 4) - 1) << (11 - 4))) != 0; break;
 | 
			
		||||
	case 3: sf = (c & (((1 << 5) - 1) << (16 - 5))) != 0; break;
 | 
			
		||||
	case 4: sf = (c & (((1 << 5) - 1) << (21 - 5))) != 0; break;
 | 
			
		||||
	default: goto invalid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!sf) {
 | 
			
		||||
		/* Codepoint representation was not shortest-form. */
 | 
			
		||||
		goto invalid;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c;
 | 
			
		||||
 | 
			
		||||
invalid:
 | 
			
		||||
	return 0xfffd; /* REPLACEMENT CHARACTER */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a Unicode codepoint to lower case.
 | 
			
		||||
 *
 | 
			
		||||
 * \note This only handles some of the Unicode blocks.
 | 
			
		||||
 *       (Currently the Latin ones.)
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  c  Codepoint to convert to lower-case, if applicable.
 | 
			
		||||
 * \return the lower-cased codepoint.
 | 
			
		||||
 */
 | 
			
		||||
static unsigned cyaml_utf8_to_lower(unsigned c)
 | 
			
		||||
{
 | 
			
		||||
	if (((c >= 0x0041) && (c <= 0x005a)) /* Basic Latin */        ||
 | 
			
		||||
	    ((c >= 0x00c0) && (c <= 0x00d6)) /* Latin-1 Supplement */ ||
 | 
			
		||||
	    ((c >= 0x00d8) && (c <= 0x00de)) /* Latin-1 Supplement */ ) {
 | 
			
		||||
		return c + 32u;
 | 
			
		||||
 | 
			
		||||
	} else if (((c >= 0x0100) && (c <= 0x012f)) /* Latin Extended-A */ ||
 | 
			
		||||
	           ((c >= 0x0132) && (c <= 0x0137)) /* Latin Extended-A */ ||
 | 
			
		||||
	           ((c >= 0x014a) && (c <= 0x0177)) /* Latin Extended-A */ ||
 | 
			
		||||
	           ((c >= 0x0182) && (c <= 0x0185)) /* Latin Extended-B */ ||
 | 
			
		||||
	           ((c >= 0x01a0) && (c <= 0x01a5)) /* Latin Extended-B */ ||
 | 
			
		||||
	           ((c >= 0x01de) && (c <= 0x01ef)) /* Latin Extended-B */ ||
 | 
			
		||||
	           ((c >= 0x01f8) && (c <= 0x021f)) /* Latin Extended-B */ ||
 | 
			
		||||
	           ((c >= 0x0222) && (c <= 0x0233)) /* Latin Extended-B */ ||
 | 
			
		||||
	           ((c >= 0x0246) && (c <= 0x024f)) /* Latin Extended-B */ ) {
 | 
			
		||||
		return c & ~0x1u;
 | 
			
		||||
 | 
			
		||||
	} else if (((c >= 0x0139) && (c <= 0x0148) /* Latin Extended-A */) ||
 | 
			
		||||
	           ((c >= 0x0179) && (c <= 0x017e) /* Latin Extended-A */) ||
 | 
			
		||||
	           ((c >= 0x01b3) && (c <= 0x01b6) /* Latin Extended-B */) ||
 | 
			
		||||
	           ((c >= 0x01cd) && (c <= 0x01dc) /* Latin Extended-B */)) {
 | 
			
		||||
		return (c + 1) & ~0x1u;
 | 
			
		||||
 | 
			
		||||
	} else switch (c) {
 | 
			
		||||
		case 0x0178: return 0x00ff; /* Latin Extended-A */
 | 
			
		||||
		case 0x0187: return 0x0188; /* Latin Extended-B */
 | 
			
		||||
		case 0x018b: return 0x018c; /* Latin Extended-B */
 | 
			
		||||
		case 0x018e: return 0x01dd; /* Latin Extended-B */
 | 
			
		||||
		case 0x0191: return 0x0192; /* Latin Extended-B */
 | 
			
		||||
		case 0x0198: return 0x0199; /* Latin Extended-B */
 | 
			
		||||
		case 0x01a7: return 0x01a8; /* Latin Extended-B */
 | 
			
		||||
		case 0x01ac: return 0x01ad; /* Latin Extended-B */
 | 
			
		||||
		case 0x01af: return 0x01b0; /* Latin Extended-B */
 | 
			
		||||
		case 0x01b7: return 0x0292; /* Latin Extended-B */
 | 
			
		||||
		case 0x01b8: return 0x01b9; /* Latin Extended-B */
 | 
			
		||||
		case 0x01bc: return 0x01bd; /* Latin Extended-B */
 | 
			
		||||
		case 0x01c4: return 0x01c6; /* Latin Extended-B */
 | 
			
		||||
		case 0x01c5: return 0x01c6; /* Latin Extended-B */
 | 
			
		||||
		case 0x01c7: return 0x01c9; /* Latin Extended-B */
 | 
			
		||||
		case 0x01c8: return 0x01c9; /* Latin Extended-B */
 | 
			
		||||
		case 0x01ca: return 0x01cc; /* Latin Extended-B */
 | 
			
		||||
		case 0x01cb: return 0x01cc; /* Latin Extended-B */
 | 
			
		||||
		case 0x01f1: return 0x01f3; /* Latin Extended-B */
 | 
			
		||||
		case 0x01f2: return 0x01f3; /* Latin Extended-B */
 | 
			
		||||
		case 0x01f4: return 0x01f5; /* Latin Extended-B */
 | 
			
		||||
		case 0x01f7: return 0x01bf; /* Latin Extended-B */
 | 
			
		||||
		case 0x0220: return 0x019e; /* Latin Extended-B */
 | 
			
		||||
		case 0x023b: return 0x023c; /* Latin Extended-B */
 | 
			
		||||
		case 0x023d: return 0x019a; /* Latin Extended-B */
 | 
			
		||||
		case 0x0241: return 0x0242; /* Latin Extended-B */
 | 
			
		||||
		case 0x0243: return 0x0180; /* Latin Extended-B */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find the difference between two codepoints.
 | 
			
		||||
 *
 | 
			
		||||
 * \param a  First codepoint.
 | 
			
		||||
 * \param b  Second codepoint.
 | 
			
		||||
 * \return the difference.
 | 
			
		||||
 */
 | 
			
		||||
static inline int cyaml_utf8_difference(unsigned a, unsigned b)
 | 
			
		||||
{
 | 
			
		||||
	return (((int)a) - ((int)b));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in utf8.h. */
 | 
			
		||||
int cyaml_utf8_casecmp(
 | 
			
		||||
		const void * const str1,
 | 
			
		||||
		const void * const str2)
 | 
			
		||||
{
 | 
			
		||||
	const uint8_t *s1 = str1;
 | 
			
		||||
	const uint8_t *s2 = str2;
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		unsigned len1;
 | 
			
		||||
		unsigned len2;
 | 
			
		||||
		unsigned cmp1;
 | 
			
		||||
		unsigned cmp2;
 | 
			
		||||
 | 
			
		||||
		/* Check for end of strings. */
 | 
			
		||||
		if ((*s1 == 0) && (*s2 == 0)) {
 | 
			
		||||
			return 0; /* Both strings ended; match. */
 | 
			
		||||
 | 
			
		||||
		} else if (*s1 == 0) {
 | 
			
		||||
			return 1; /* String 1 has ended. */
 | 
			
		||||
 | 
			
		||||
		} else if (*s2 == 0) {
 | 
			
		||||
			return -1;/* String 2 has ended. */
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Get byte lengths of these characters. */
 | 
			
		||||
		len1 = cyaml_utf8_char_len(*s1);
 | 
			
		||||
		len2 = cyaml_utf8_char_len(*s2);
 | 
			
		||||
 | 
			
		||||
		/* Compare values. */
 | 
			
		||||
		if ((len1 == 1) && (len2 == 1)) {
 | 
			
		||||
			/* Common case: Both strings have ASCII values. */
 | 
			
		||||
			if (*s1 != *s2) {
 | 
			
		||||
				/* They're different; need to lower case. */
 | 
			
		||||
				cmp1 = ((*s1 >= 'A') && (*s1 <= 'Z')) ?
 | 
			
		||||
						(*s1 + 32u) : *s1;
 | 
			
		||||
				cmp2 = ((*s2 >= 'A') && (*s2 <= 'Z')) ?
 | 
			
		||||
						(*s2 + 32u) : *s2;
 | 
			
		||||
				if (cmp1 != cmp2) {
 | 
			
		||||
					return cyaml_utf8_difference(
 | 
			
		||||
							cmp1, cmp2);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if ((len1 != 0) && (len2 != 0)) {
 | 
			
		||||
			/* Neither string has invalid sequence;
 | 
			
		||||
			 * convert to UCS4 for comparison. */
 | 
			
		||||
			cmp1 = cyaml_utf8_get_codepoint(s1, &len1);
 | 
			
		||||
			cmp2 = cyaml_utf8_get_codepoint(s2, &len2);
 | 
			
		||||
 | 
			
		||||
			if (cmp1 != cmp2) {
 | 
			
		||||
				/* They're different; need to lower case. */
 | 
			
		||||
				cmp1 = cyaml_utf8_to_lower(cmp1);
 | 
			
		||||
				cmp2 = cyaml_utf8_to_lower(cmp2);
 | 
			
		||||
				if (cmp1 != cmp2) {
 | 
			
		||||
					return cyaml_utf8_difference(
 | 
			
		||||
							cmp1, cmp2);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if (len1 | len2) {
 | 
			
		||||
				/* One of the strings has invalid sequence. */
 | 
			
		||||
				return cyaml_utf8_difference(len1, len2);
 | 
			
		||||
			} else {
 | 
			
		||||
				/* Both strings have an invalid sequence. */
 | 
			
		||||
				len1 = len2 = 1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Advance each string by their current character length. */
 | 
			
		||||
		s1 += len1;
 | 
			
		||||
		s2 += len2;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								libs/libcyaml/src/utf8.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								libs/libcyaml/src/utf8.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML functions for handling utf8 text.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CYAML_UTF8_H
 | 
			
		||||
#define CYAML_UTF8_H
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get a codepoint from the input string.
 | 
			
		||||
 *
 | 
			
		||||
 * Caller must provide the expected length given the first input byte.
 | 
			
		||||
 *
 | 
			
		||||
 * If a multi-byte character contains an invalid continuation byte, the
 | 
			
		||||
 * character length will be updated on exit to the number of bytes consumed,
 | 
			
		||||
 * and the replacement character, U+FFFD will be returned.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]      s    String to read first codepoint from.
 | 
			
		||||
 * \param[in,out]  len  Expected length of first character, updated on exit.
 | 
			
		||||
 * \return The codepoint or `0xfffd` if character is invalid.
 | 
			
		||||
 */
 | 
			
		||||
unsigned cyaml_utf8_get_codepoint(
 | 
			
		||||
		const uint8_t *s,
 | 
			
		||||
		unsigned *len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Case insensitive comparason.
 | 
			
		||||
 *
 | 
			
		||||
 * \note This has some limitations and only performs case insensitive
 | 
			
		||||
 *       comparason over some sectons of unicode.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  str1  First string to be compared.
 | 
			
		||||
 * \param[in]  str2  Second string to be compared.
 | 
			
		||||
 * \return 0 if and only if strings are equal.
 | 
			
		||||
 */
 | 
			
		||||
int cyaml_utf8_casecmp(
 | 
			
		||||
		const void * const str1,
 | 
			
		||||
		const void * const str2);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										122
									
								
								libs/libcyaml/src/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								libs/libcyaml/src/util.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,122 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2019 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief Utility functions.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
/** Flag that indicates a release in \ref cyaml_version. */
 | 
			
		||||
#define CYAML_RELEASE_FLAG (1u << 31)
 | 
			
		||||
 | 
			
		||||
/** Stringification helper macro. */
 | 
			
		||||
#define CYAML_STR_HELPER(_x) #_x
 | 
			
		||||
 | 
			
		||||
/** Stringification macro. */
 | 
			
		||||
#define CYAML_STR(_x) CYAML_STR_HELPER(_x)
 | 
			
		||||
 | 
			
		||||
/* Version depends on whether we're a development build. */
 | 
			
		||||
#if VERSION_DEVEL
 | 
			
		||||
	/** Version string is composed from components in Makefile. */
 | 
			
		||||
	#define CYAML_VERSION_STR \
 | 
			
		||||
			CYAML_STR(VERSION_MAJOR) "." \
 | 
			
		||||
			CYAML_STR(VERSION_MINOR) "." \
 | 
			
		||||
			CYAML_STR(VERSION_PATCH) "-DEVEL"
 | 
			
		||||
 | 
			
		||||
	/* Exported constant, documented in include/cyaml/cyaml.h */
 | 
			
		||||
	const uint32_t cyaml_version =
 | 
			
		||||
			((VERSION_MAJOR << 16) |
 | 
			
		||||
			 (VERSION_MINOR <<  8) |
 | 
			
		||||
			 (VERSION_PATCH <<  0));
 | 
			
		||||
#else
 | 
			
		||||
	/** Version string is composed from components in Makefile. */
 | 
			
		||||
	#define CYAML_VERSION_STR \
 | 
			
		||||
			CYAML_STR(VERSION_MAJOR) "." \
 | 
			
		||||
			CYAML_STR(VERSION_MINOR) "." \
 | 
			
		||||
			CYAML_STR(VERSION_PATCH)
 | 
			
		||||
 | 
			
		||||
	/* Exported constant, documented in include/cyaml/cyaml.h */
 | 
			
		||||
	const uint32_t cyaml_version =
 | 
			
		||||
			((VERSION_MAJOR << 16) |
 | 
			
		||||
			 (VERSION_MINOR <<  8) |
 | 
			
		||||
			 (VERSION_PATCH <<  0) |
 | 
			
		||||
			 CYAML_RELEASE_FLAG);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Exported constant, documented in include/cyaml/cyaml.h */
 | 
			
		||||
const char *cyaml_version_str = CYAML_VERSION_STR;
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in include/cyaml/cyaml.h */
 | 
			
		||||
void cyaml_log(
 | 
			
		||||
		cyaml_log_t level,
 | 
			
		||||
		void *ctx,
 | 
			
		||||
		const char *fmt,
 | 
			
		||||
		va_list args)
 | 
			
		||||
{
 | 
			
		||||
	static const char * const strings[] = {
 | 
			
		||||
		[CYAML_LOG_DEBUG]   = "DEBUG",
 | 
			
		||||
		[CYAML_LOG_INFO]    = "INFO",
 | 
			
		||||
		[CYAML_LOG_NOTICE]  = "NOTICE",
 | 
			
		||||
		[CYAML_LOG_WARNING] = "WARNING",
 | 
			
		||||
		[CYAML_LOG_ERROR]   = "ERROR",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	CYAML_UNUSED(ctx);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "libcyaml: %7.7s: ", strings[level]);
 | 
			
		||||
	vfprintf(stderr, fmt, args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Exported function, documented in include/cyaml/cyaml.h */
 | 
			
		||||
const char * cyaml_strerror(
 | 
			
		||||
		cyaml_err_t err)
 | 
			
		||||
{
 | 
			
		||||
	static const char * const strings[CYAML_ERR__COUNT] = {
 | 
			
		||||
		[CYAML_OK]                        = "Success",
 | 
			
		||||
		[CYAML_ERR_OOM]                   = "Memory allocation failed",
 | 
			
		||||
		[CYAML_ERR_ALIAS]                 = "YAML alias unsupported",
 | 
			
		||||
		[CYAML_ERR_FILE_OPEN]             = "Could not open file",
 | 
			
		||||
		[CYAML_ERR_INVALID_KEY]           = "Invalid key",
 | 
			
		||||
		[CYAML_ERR_INVALID_VALUE]         = "Invalid value",
 | 
			
		||||
		[CYAML_ERR_INVALID_ALIAS]         = "No anchor found for alias",
 | 
			
		||||
		[CYAML_ERR_INTERNAL_ERROR]        = "Internal error",
 | 
			
		||||
		[CYAML_ERR_UNEXPECTED_EVENT]      = "Unexpected event",
 | 
			
		||||
		[CYAML_ERR_STRING_LENGTH_MIN]     = "String length too short",
 | 
			
		||||
		[CYAML_ERR_STRING_LENGTH_MAX]     = "String length too long",
 | 
			
		||||
		[CYAML_ERR_INVALID_DATA_SIZE]     = "Data size must be 0 < X <= 8 bytes",
 | 
			
		||||
		[CYAML_ERR_TOP_LEVEL_NON_PTR]     = "Top-level schema value must be pointer",
 | 
			
		||||
		[CYAML_ERR_BAD_TYPE_IN_SCHEMA]    = "Schema contains invalid type",
 | 
			
		||||
		[CYAML_ERR_BAD_MIN_MAX_SCHEMA]    = "Bad schema: min exceeds max",
 | 
			
		||||
		[CYAML_ERR_BAD_PARAM_SEQ_COUNT]   = "Bad parameter: seq_count",
 | 
			
		||||
		[CYAML_ERR_BAD_PARAM_NULL_DATA]   = "Bad parameter: NULL data",
 | 
			
		||||
		[CYAML_ERR_BAD_BITVAL_IN_SCHEMA]  = "Bit value beyond bitfield size",
 | 
			
		||||
		[CYAML_ERR_SEQUENCE_ENTRIES_MIN]  = "Sequence with too few entries",
 | 
			
		||||
		[CYAML_ERR_SEQUENCE_ENTRIES_MAX]  = "Sequence with too many entries",
 | 
			
		||||
		[CYAML_ERR_SEQUENCE_FIXED_COUNT]  = "Sequence fixed has unequal min max",
 | 
			
		||||
		[CYAML_ERR_SEQUENCE_IN_SEQUENCE]  = "Non-fixed sequence in sequence",
 | 
			
		||||
		[CYAML_ERR_MAPPING_FIELD_MISSING] = "Missing required mapping field",
 | 
			
		||||
		[CYAML_ERR_BAD_CONFIG_NULL_MEMFN] = "Bad config: NULL mem function",
 | 
			
		||||
		[CYAML_ERR_BAD_PARAM_NULL_CONFIG] = "Bad parameter: NULL config",
 | 
			
		||||
		[CYAML_ERR_BAD_PARAM_NULL_SCHEMA] = "Bad parameter: NULL schema",
 | 
			
		||||
		[CYAML_ERR_LIBYAML_EMITTER_INIT]  = "libyaml emitter init failed",
 | 
			
		||||
		[CYAML_ERR_LIBYAML_PARSER_INIT]   = "libyaml parser init failed",
 | 
			
		||||
		[CYAML_ERR_LIBYAML_EVENT_INIT]    = "libyaml event init failed",
 | 
			
		||||
		[CYAML_ERR_LIBYAML_EMITTER]       = "libyaml emitter error",
 | 
			
		||||
		[CYAML_ERR_LIBYAML_PARSER]        = "libyaml parser error",
 | 
			
		||||
	};
 | 
			
		||||
	if ((unsigned)err >= CYAML_ERR__COUNT) {
 | 
			
		||||
		return "Invalid error code";
 | 
			
		||||
	}
 | 
			
		||||
	assert(strings[err] != NULL);
 | 
			
		||||
	return strings[err];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										200
									
								
								libs/libcyaml/src/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								libs/libcyaml/src/util.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,200 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \file
 | 
			
		||||
 * \brief CYAML common utility functions.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef CYAML_UTIL_H
 | 
			
		||||
#define CYAML_UTIL_H
 | 
			
		||||
 | 
			
		||||
#include "cyaml/cyaml.h"
 | 
			
		||||
#include "utf8.h"
 | 
			
		||||
 | 
			
		||||
/** Macro to squash unused variable compiler warnings. */
 | 
			
		||||
#define CYAML_UNUSED(_x) ((void)(_x))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check whether the host is little endian.
 | 
			
		||||
 *
 | 
			
		||||
 * Checks whether least significant bit is in the first byte of a `uint16_t`.
 | 
			
		||||
 *
 | 
			
		||||
 * \return true if host is little endian.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool cyaml__host_is_little_endian(void)
 | 
			
		||||
{
 | 
			
		||||
	const uint16_t test = 1;
 | 
			
		||||
 | 
			
		||||
	return ((const uint8_t *) &test)[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check whether the host is big endian.
 | 
			
		||||
 *
 | 
			
		||||
 * \return true if host is big endian.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool cyaml__host_is_big_endian(void)
 | 
			
		||||
{
 | 
			
		||||
	return !cyaml__host_is_little_endian();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** CYAML bitfield type. */
 | 
			
		||||
typedef uint32_t cyaml_bitfield_t;
 | 
			
		||||
 | 
			
		||||
/** Number of bits in \ref cyaml_bitfield_t. */
 | 
			
		||||
#define CYAML_BITFIELD_BITS (sizeof(cyaml_bitfield_t) * CHAR_BIT)
 | 
			
		||||
 | 
			
		||||
/** CYAML state machine states. */
 | 
			
		||||
enum cyaml_state_e {
 | 
			
		||||
	CYAML_STATE_START,        /**< Initial state. */
 | 
			
		||||
	CYAML_STATE_IN_STREAM,    /**< In a stream. */
 | 
			
		||||
	CYAML_STATE_IN_DOC,       /**< In a document. */
 | 
			
		||||
	CYAML_STATE_IN_MAP_KEY,   /**< In a mapping. */
 | 
			
		||||
	CYAML_STATE_IN_MAP_VALUE, /**< In a mapping. */
 | 
			
		||||
	CYAML_STATE_IN_SEQUENCE,  /**< In a sequence. */
 | 
			
		||||
	CYAML_STATE__COUNT,       /**< Count of states, **not a valid
 | 
			
		||||
	                               state itself**. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a CYAML state into a human readable string.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  state  The state to convert.
 | 
			
		||||
 * \return String representing state.
 | 
			
		||||
 */
 | 
			
		||||
static inline const char * cyaml__state_to_str(enum cyaml_state_e state)
 | 
			
		||||
{
 | 
			
		||||
	static const char * const strings[CYAML_STATE__COUNT] = {
 | 
			
		||||
		[CYAML_STATE_START]        = "start",
 | 
			
		||||
		[CYAML_STATE_IN_STREAM]    = "in stream",
 | 
			
		||||
		[CYAML_STATE_IN_DOC]       = "in doc",
 | 
			
		||||
		[CYAML_STATE_IN_MAP_KEY]   = "in mapping (key)",
 | 
			
		||||
		[CYAML_STATE_IN_MAP_VALUE] = "in mapping (value)",
 | 
			
		||||
		[CYAML_STATE_IN_SEQUENCE]  = "in sequence",
 | 
			
		||||
	};
 | 
			
		||||
	if ((unsigned)state >= CYAML_STATE__COUNT) {
 | 
			
		||||
		return "<invalid>";
 | 
			
		||||
	}
 | 
			
		||||
	return strings[state];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a CYAML type into a human readable string.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  type  The state to convert.
 | 
			
		||||
 * \return String representing state.
 | 
			
		||||
 */
 | 
			
		||||
static inline const char * cyaml__type_to_str(cyaml_type_e type)
 | 
			
		||||
{
 | 
			
		||||
	static const char * const strings[CYAML__TYPE_COUNT] = {
 | 
			
		||||
		[CYAML_INT]            = "INT",
 | 
			
		||||
		[CYAML_UINT]           = "UINT",
 | 
			
		||||
		[CYAML_BOOL]           = "BOOL",
 | 
			
		||||
		[CYAML_ENUM]           = "ENUM",
 | 
			
		||||
		[CYAML_FLAGS]          = "FLAGS",
 | 
			
		||||
		[CYAML_FLOAT]          = "FLOAT",
 | 
			
		||||
		[CYAML_STRING]         = "STRING",
 | 
			
		||||
		[CYAML_MAPPING]        = "MAPPING",
 | 
			
		||||
		[CYAML_BITFIELD]       = "BITFIELD",
 | 
			
		||||
		[CYAML_SEQUENCE]       = "SEQUENCE",
 | 
			
		||||
		[CYAML_SEQUENCE_FIXED] = "SEQUENCE_FIXED",
 | 
			
		||||
		[CYAML_IGNORE]         = "IGNORE",
 | 
			
		||||
	};
 | 
			
		||||
	if ((unsigned)type >= CYAML__TYPE_COUNT) {
 | 
			
		||||
		return "<invalid>";
 | 
			
		||||
	}
 | 
			
		||||
	return strings[type];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Log to client's logging function, if provided.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in] cfg    CYAML client config structure.
 | 
			
		||||
 * \param[in] level  Log level of message to log.
 | 
			
		||||
 * \param[in] fmt    Format string for message to log.
 | 
			
		||||
 * \param[in] ...    Additional arguments used by fmt.
 | 
			
		||||
 */
 | 
			
		||||
static inline void cyaml__log(
 | 
			
		||||
		const cyaml_config_t *cfg,
 | 
			
		||||
		cyaml_log_t level,
 | 
			
		||||
		const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	if (level >= cfg->log_level && cfg->log_fn != NULL) {
 | 
			
		||||
		va_list args;
 | 
			
		||||
		va_start(args, fmt);
 | 
			
		||||
		cfg->log_fn(level, cfg->log_ctx, fmt, args);
 | 
			
		||||
		va_end(args);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check if comparason should be case sensitive.
 | 
			
		||||
 *
 | 
			
		||||
 * As described in the API, schema flags take priority over config flags.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config  Client's CYAML configuration structure.
 | 
			
		||||
 * \param[in]  schema  The CYAML schema for the value to be compared.
 | 
			
		||||
 * \return Whether to use case-sensitive comparason.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool cyaml__is_case_sensitive(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		const cyaml_schema_value_t *schema)
 | 
			
		||||
{
 | 
			
		||||
	if (schema->flags & CYAML_FLAG_CASE_INSENSITIVE) {
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	} else if (schema->flags & CYAML_FLAG_CASE_SENSITIVE) {
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	} else if (config->flags & CYAML_CFG_CASE_INSENSITIVE) {
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Compare two strings.
 | 
			
		||||
 *
 | 
			
		||||
 * Depending on the client's configuration, and the value's schema,
 | 
			
		||||
 * this will do either a case-sensitive or case-insensitive comparason.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  config  Client's CYAML configuration structure.
 | 
			
		||||
 * \param[in]  schema  The CYAML schema for the value to be compared.
 | 
			
		||||
 * \param[in]  str1    First string to be compared.
 | 
			
		||||
 * \param[in]  str2    Second string to be compared.
 | 
			
		||||
 * \return 0 if and only if strings are equal.
 | 
			
		||||
 */
 | 
			
		||||
static inline int cyaml__strcmp(
 | 
			
		||||
		const cyaml_config_t *config,
 | 
			
		||||
		const cyaml_schema_value_t *schema,
 | 
			
		||||
		const void * const str1,
 | 
			
		||||
		const void * const str2)
 | 
			
		||||
{
 | 
			
		||||
	if (cyaml__is_case_sensitive(config, schema)) {
 | 
			
		||||
		return strcmp(str1, str2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cyaml_utf8_casecmp(str1, str2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check of all the bits of a mask are set in a cyaml value flag word.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in] flags  The value flags to test.
 | 
			
		||||
 * \param[in] mask   Mask of the bits to test for in flags.
 | 
			
		||||
 * \return true if all bits of mask are set in flags.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool cyaml__flag_check_all(
 | 
			
		||||
		enum cyaml_flag flags,
 | 
			
		||||
		enum cyaml_flag mask)
 | 
			
		||||
{
 | 
			
		||||
	return ((flags & mask) == mask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										19
									
								
								libs/libcyaml/test/data/basic.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								libs/libcyaml/test/data/basic.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
animals:
 | 
			
		||||
  - kind: cat
 | 
			
		||||
    sounds:
 | 
			
		||||
      - meow
 | 
			
		||||
      - purr
 | 
			
		||||
  - kind: hippo
 | 
			
		||||
    sounds:
 | 
			
		||||
      - wheeze
 | 
			
		||||
      - grunt
 | 
			
		||||
      - roar
 | 
			
		||||
  - kind: snake
 | 
			
		||||
    sounds:
 | 
			
		||||
      - hiss
 | 
			
		||||
cakes:
 | 
			
		||||
  - salted caramel cake
 | 
			
		||||
  - lemon drizzle cake
 | 
			
		||||
  - victoria sponge
 | 
			
		||||
  - carrot cake
 | 
			
		||||
  - yule log
 | 
			
		||||
							
								
								
									
										7371
									
								
								libs/libcyaml/test/units/errs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7371
									
								
								libs/libcyaml/test/units/errs.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										448
									
								
								libs/libcyaml/test/units/file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										448
									
								
								libs/libcyaml/test/units/file.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,448 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
#include "ttest.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unit test context data.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct test_data {
 | 
			
		||||
	cyaml_data_t **data;
 | 
			
		||||
	unsigned *seq_count;
 | 
			
		||||
	const struct cyaml_config *config;
 | 
			
		||||
	const struct cyaml_schema_value *schema;
 | 
			
		||||
} test_data_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Common clean up function to free data loaded by tests.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  data  The unit test context data.
 | 
			
		||||
 */
 | 
			
		||||
static void cyaml_cleanup(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct test_data *td = data;
 | 
			
		||||
	unsigned seq_count = 0;
 | 
			
		||||
 | 
			
		||||
	if (td->seq_count != NULL) {
 | 
			
		||||
		seq_count = *(td->seq_count);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (td->data != NULL) {
 | 
			
		||||
		cyaml_free(td->config, td->schema, *(td->data), seq_count);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test loading a non-existent file.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_load_bad_path(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		char *cakes;
 | 
			
		||||
	} *data_tgt = NULL;
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.data = (cyaml_data_t **) &data_tgt,
 | 
			
		||||
		.config = config,
 | 
			
		||||
		.schema = &top_schema,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_load_file("/cyaml/path/shouldn't/exist.yaml",
 | 
			
		||||
			config, &top_schema, (cyaml_data_t **) &data_tgt, NULL);
 | 
			
		||||
	if (err != CYAML_ERR_FILE_OPEN) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test loading a non-existent file.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_save_bad_path(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		char *cakes;
 | 
			
		||||
	} *data = NULL;
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.data = NULL,
 | 
			
		||||
		.config = config,
 | 
			
		||||
		.schema = &top_schema,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_save_file("/cyaml/path/shouldn't/exist.yaml",
 | 
			
		||||
			config, &top_schema, data, 0);
 | 
			
		||||
	if (err != CYAML_ERR_FILE_OPEN) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test loading the basic YAML file.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_load_basic(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	struct animal {
 | 
			
		||||
		char *kind;
 | 
			
		||||
		char **sounds;
 | 
			
		||||
		unsigned sounds_count;
 | 
			
		||||
	};
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		struct animal *animals;
 | 
			
		||||
		unsigned animals_count;
 | 
			
		||||
		char **cakes;
 | 
			
		||||
		unsigned cakes_count;
 | 
			
		||||
	} *data_tgt = NULL;
 | 
			
		||||
	static const struct cyaml_schema_value sounds_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field animal_mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, kind, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, sounds,
 | 
			
		||||
				&sounds_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value animals_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
 | 
			
		||||
				struct animal, animal_mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value cakes_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, animals,
 | 
			
		||||
				&animals_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, cakes,
 | 
			
		||||
				&cakes_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.data = (cyaml_data_t **) &data_tgt,
 | 
			
		||||
		.config = config,
 | 
			
		||||
		.schema = &top_schema,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
 | 
			
		||||
			(cyaml_data_t **) &data_tgt, NULL);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test loading and then saving the basic YAML file.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_load_save_basic(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	struct animal {
 | 
			
		||||
		char *kind;
 | 
			
		||||
		char **sounds;
 | 
			
		||||
		unsigned sounds_count;
 | 
			
		||||
	};
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		struct animal *animals;
 | 
			
		||||
		unsigned animals_count;
 | 
			
		||||
		char **cakes;
 | 
			
		||||
		unsigned cakes_count;
 | 
			
		||||
	} *data_tgt = NULL;
 | 
			
		||||
	static const struct cyaml_schema_value sounds_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field animal_mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, kind, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, sounds,
 | 
			
		||||
				&sounds_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value animals_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
 | 
			
		||||
				struct animal, animal_mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value cakes_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, animals,
 | 
			
		||||
				&animals_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, cakes,
 | 
			
		||||
				&cakes_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.data = (cyaml_data_t **) &data_tgt,
 | 
			
		||||
		.config = config,
 | 
			
		||||
		.schema = &top_schema,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
 | 
			
		||||
			(cyaml_data_t **) &data_tgt, NULL);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_save_file("build/load_save.yaml", config, &top_schema,
 | 
			
		||||
				data_tgt, 0);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test loading the basic YAML file, with a mismatching schema.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_load_basic_invalid(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	struct animal {
 | 
			
		||||
		char *kind;
 | 
			
		||||
		int *sounds;
 | 
			
		||||
		unsigned sounds_count;
 | 
			
		||||
	};
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		struct animal *animals;
 | 
			
		||||
		unsigned animals_count;
 | 
			
		||||
		char **cakes;
 | 
			
		||||
		unsigned cakes_count;
 | 
			
		||||
	} *data_tgt = NULL;
 | 
			
		||||
	static const struct cyaml_schema_value sounds_entry_schema = {
 | 
			
		||||
		/* The data has a string, but we're expecting int here. */
 | 
			
		||||
		CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field animal_mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, kind, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct animal, sounds,
 | 
			
		||||
				&sounds_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value animals_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
 | 
			
		||||
				struct animal, animal_mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value cakes_entry_schema = {
 | 
			
		||||
		CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, animals,
 | 
			
		||||
				&animals_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, cakes,
 | 
			
		||||
				&cakes_entry_schema, 0, CYAML_UNLIMITED),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.data = (cyaml_data_t **) &data_tgt,
 | 
			
		||||
		.config = config,
 | 
			
		||||
		.schema = &top_schema,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
 | 
			
		||||
			(cyaml_data_t **) &data_tgt, NULL);
 | 
			
		||||
	if (err != CYAML_ERR_INVALID_VALUE) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test saving to a file when an erro occurs.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_file_save_basic_invalid(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	static const struct target_struct {
 | 
			
		||||
		int value;
 | 
			
		||||
	} data = {
 | 
			
		||||
		.value = 9,
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		{
 | 
			
		||||
			.key = "key",
 | 
			
		||||
			.value = {
 | 
			
		||||
				.type = CYAML_INT,
 | 
			
		||||
				.flags = CYAML_FLAG_DEFAULT,
 | 
			
		||||
				.data_size = 0,
 | 
			
		||||
			},
 | 
			
		||||
			.data_offset = offsetof(struct target_struct, value),
 | 
			
		||||
		},
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	test_data_t td = {
 | 
			
		||||
		.config = config,
 | 
			
		||||
	};
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cyaml_save_file("build/save.yaml", config, &top_schema, &data, 0);
 | 
			
		||||
	if (err != CYAML_ERR_INVALID_DATA_SIZE) {
 | 
			
		||||
		return ttest_fail(&tc, cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Run the YAML file tests.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  rc         The ttest report context.
 | 
			
		||||
 * \param[in]  log_level  CYAML log level.
 | 
			
		||||
 * \param[in]  log_fn     CYAML logging function, or NULL.
 | 
			
		||||
 * \return true iff all unit tests pass, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool file_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn)
 | 
			
		||||
{
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
	cyaml_config_t config = {
 | 
			
		||||
		.log_fn = log_fn,
 | 
			
		||||
		.mem_fn = cyaml_mem,
 | 
			
		||||
		.log_level = log_level,
 | 
			
		||||
		.flags = CYAML_CFG_DEFAULT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "File loading tests");
 | 
			
		||||
 | 
			
		||||
	pass &= test_file_load_basic(rc, &config);
 | 
			
		||||
	pass &= test_file_load_save_basic(rc, &config);
 | 
			
		||||
 | 
			
		||||
	/* Since we expect loads of error logging for these tests,
 | 
			
		||||
	 * suppress log output if required log level is greater
 | 
			
		||||
	 * than \ref CYAML_LOG_INFO.
 | 
			
		||||
	 */
 | 
			
		||||
	if (log_level > CYAML_LOG_INFO) {
 | 
			
		||||
		config.log_fn = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pass &= test_file_load_bad_path(rc, &config);
 | 
			
		||||
	pass &= test_file_save_bad_path(rc, &config);
 | 
			
		||||
	pass &= test_file_load_basic_invalid(rc, &config);
 | 
			
		||||
	pass &= test_file_save_basic_invalid(rc, &config);
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										161
									
								
								libs/libcyaml/test/units/free.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								libs/libcyaml/test/units/free.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
#include "ttest.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
/** Macro to squash unused variable compiler warnings. */
 | 
			
		||||
#define UNUSED(_x) ((void)(_x))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml_free with NULL data.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_free_null_data(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	struct target_struct {
 | 
			
		||||
		int test_value_int;
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_field mapping_schema[] = {
 | 
			
		||||
		CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT,
 | 
			
		||||
				struct target_struct, test_value_int),
 | 
			
		||||
		CYAML_FIELD_END
 | 
			
		||||
	};
 | 
			
		||||
	static const struct cyaml_schema_value top_schema = {
 | 
			
		||||
		CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
 | 
			
		||||
				struct target_struct, mapping_schema),
 | 
			
		||||
	};
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	err = cyaml_free(config, &top_schema, NULL, 0);
 | 
			
		||||
	if (err != CYAML_OK) {
 | 
			
		||||
		return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml_free with NULL config.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_free_null_config(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	UNUSED(config);
 | 
			
		||||
 | 
			
		||||
	err = cyaml_free(NULL, NULL, NULL, 0);
 | 
			
		||||
	if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) {
 | 
			
		||||
		return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml_free with NULL memory allocation function.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_free_null_mem_fn(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	cyaml_config_t cfg = *config;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	cfg.mem_fn = NULL;
 | 
			
		||||
 | 
			
		||||
	err = cyaml_free(&cfg, NULL, NULL, 0);
 | 
			
		||||
	if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) {
 | 
			
		||||
		return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml_free with NULL schema.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_free_null_schema(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	cyaml_err_t err;
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	err = cyaml_free(config, NULL, NULL, 0);
 | 
			
		||||
	if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) {
 | 
			
		||||
		return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Run the CYAML freeing unit tests.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  rc         The ttest report context.
 | 
			
		||||
 * \param[in]  log_level  CYAML log level.
 | 
			
		||||
 * \param[in]  log_fn     CYAML logging function, or NULL.
 | 
			
		||||
 * \return true iff all unit tests pass, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool free_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn)
 | 
			
		||||
{
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
	cyaml_config_t config = {
 | 
			
		||||
		.log_fn = log_fn,
 | 
			
		||||
		.mem_fn = cyaml_mem,
 | 
			
		||||
		.log_level = log_level,
 | 
			
		||||
		.flags = CYAML_CFG_DEFAULT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "Free tests");
 | 
			
		||||
 | 
			
		||||
	pass &= test_free_null_data(rc, &config);
 | 
			
		||||
	pass &= test_free_null_mem_fn(rc, &config);
 | 
			
		||||
	pass &= test_free_null_config(rc, &config);
 | 
			
		||||
	pass &= test_free_null_schema(rc, &config);
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7725
									
								
								libs/libcyaml/test/units/load.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7725
									
								
								libs/libcyaml/test/units/load.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										4530
									
								
								libs/libcyaml/test/units/save.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4530
									
								
								libs/libcyaml/test/units/save.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										85
									
								
								libs/libcyaml/test/units/test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								libs/libcyaml/test/units/test.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
#include "ttest.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Print program usage
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  prog_name  Name of program.
 | 
			
		||||
 */
 | 
			
		||||
static void usage(const char *prog_name)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "Usage: %s [-q|-v|-d]\n", prog_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main entry point from OS.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  argc  Argument count.
 | 
			
		||||
 * \param[in]  argv  Vector of string arguments.
 | 
			
		||||
 * \return Program return code.
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
	bool quiet = false;
 | 
			
		||||
	ttest_report_ctx_t rc;
 | 
			
		||||
	const char *test_list = NULL;
 | 
			
		||||
	cyaml_log_fn_t log_fn = cyaml_log;
 | 
			
		||||
	cyaml_log_t log_level = CYAML_LOG_ERROR;
 | 
			
		||||
	enum {
 | 
			
		||||
		ARG_PROG_NAME,
 | 
			
		||||
		ARG_VERBOSE,
 | 
			
		||||
		ARG_TEST_LIST,
 | 
			
		||||
		ARG__COUNT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (argc > ARG__COUNT) {
 | 
			
		||||
		usage(argv[ARG_PROG_NAME]);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (int i = 1; i < argc; i++) {
 | 
			
		||||
		if ((strcmp(argv[i], "-q") == 0) ||
 | 
			
		||||
		    (strcmp(argv[i], "--quiet") == 0)) {
 | 
			
		||||
			quiet = true;
 | 
			
		||||
			log_fn = NULL;
 | 
			
		||||
		} else if ((strcmp(argv[i], "-v") == 0) ||
 | 
			
		||||
		           (strcmp(argv[i], "--verbose") == 0)) {
 | 
			
		||||
			log_level = CYAML_LOG_INFO;
 | 
			
		||||
		} else if ((strcmp(argv[i], "-d") == 0) ||
 | 
			
		||||
		           (strcmp(argv[i], "--debug") == 0)) {
 | 
			
		||||
			log_level = CYAML_LOG_DEBUG;
 | 
			
		||||
		} else {
 | 
			
		||||
			test_list = argv[i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = ttest_init(test_list, quiet);
 | 
			
		||||
 | 
			
		||||
	pass &= utf8_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= util_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= free_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= load_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= errs_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= file_tests(&rc, log_level, log_fn);
 | 
			
		||||
	pass &= save_tests(&rc, log_level, log_fn);
 | 
			
		||||
 | 
			
		||||
	ttest_report(&rc);
 | 
			
		||||
 | 
			
		||||
	return (pass) ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								libs/libcyaml/test/units/test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								libs/libcyaml/test/units/test.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TEST_H
 | 
			
		||||
#define TEST_H
 | 
			
		||||
 | 
			
		||||
/** In load.c */
 | 
			
		||||
extern bool load_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In file.c */
 | 
			
		||||
extern bool file_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In free.c */
 | 
			
		||||
extern bool free_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In utf8.c */
 | 
			
		||||
extern bool utf8_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In util.c */
 | 
			
		||||
extern bool util_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In errs.c */
 | 
			
		||||
extern bool errs_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
/** In save.c */
 | 
			
		||||
extern bool save_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										291
									
								
								libs/libcyaml/test/units/ttest.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								libs/libcyaml/test/units/ttest.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2013-2019 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TTEST_H
 | 
			
		||||
#define TTEST_H
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cleanup client callback.
 | 
			
		||||
 *
 | 
			
		||||
 * The caller passes this callback function to ttest_start, and it is called
 | 
			
		||||
 * in whichever tlsa-test function finishes the test (ttest_pass, ttest_fail,
 | 
			
		||||
 * or ttest_todo).
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  Client data to clean up.
 | 
			
		||||
 */
 | 
			
		||||
typedef void (*ttest_cleanup_fn)(void *data);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal tlsa-test report context.
 | 
			
		||||
 *
 | 
			
		||||
 * Clients should not touch this directly, it is only exposed in this
 | 
			
		||||
 * header because tlsa-test is arranged to be header-only, for convenience
 | 
			
		||||
 * of utilisation.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct ttest_report_ctx {
 | 
			
		||||
	/** Space/comma separated list of tests to run. */
 | 
			
		||||
	const char *test_list;
 | 
			
		||||
	size_t test_list_len;
 | 
			
		||||
 | 
			
		||||
	bool quiet;      /**< Whether to print only the report summary. */
 | 
			
		||||
	unsigned tests;  /**< Number of tests started. */
 | 
			
		||||
	unsigned todo;   /**< Number of tests marked as unimplemented. */
 | 
			
		||||
	unsigned passed; /**< Number of tests passed. */
 | 
			
		||||
} ttest_report_ctx_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal tlsa-test test context.
 | 
			
		||||
 *
 | 
			
		||||
 * Clients should not touch this directly, it is only exposed in this
 | 
			
		||||
 * header because tlsa-test is arranged to be header-only, for convenience
 | 
			
		||||
 * of utilisation.
 | 
			
		||||
 */
 | 
			
		||||
typedef struct ttest_ctx {
 | 
			
		||||
	ttest_report_ctx_t *report; /**< The tlsa-test report context. */
 | 
			
		||||
	const char *name;           /**< The unit test name. */
 | 
			
		||||
	ttest_cleanup_fn cleanup;   /**< Client's unit test cleanup function. */
 | 
			
		||||
	void *cleanup_data;         /**< Client's unit test cleanup context. */
 | 
			
		||||
} ttest_ctx_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialise a tlsa-test report context.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  test_list  Space/comma separated list of tests to run.
 | 
			
		||||
 * \param[in]  quiet      Whether report should be a minimal summary.
 | 
			
		||||
 * \return initialised tlsa-test report context.
 | 
			
		||||
 */
 | 
			
		||||
static inline ttest_report_ctx_t ttest_init(
 | 
			
		||||
		const char *test_list,
 | 
			
		||||
		bool quiet)
 | 
			
		||||
{
 | 
			
		||||
	ttest_report_ctx_t rc = {
 | 
			
		||||
		.test_list = test_list,
 | 
			
		||||
		.test_list_len = 0,
 | 
			
		||||
		.quiet = quiet,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (test_list != NULL) {
 | 
			
		||||
		rc.test_list_len = strlen(test_list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determine whether test of given name should be run.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The tlsa-test report context.
 | 
			
		||||
 * \param[in]  name    The name of the test to consider.
 | 
			
		||||
 * \return true if test should be run, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ttest__run_test(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const char *name)
 | 
			
		||||
{
 | 
			
		||||
	size_t len;
 | 
			
		||||
	size_t pos = 0;
 | 
			
		||||
	size_t name_len;
 | 
			
		||||
 | 
			
		||||
	if (report->test_list == NULL || report->test_list_len == 0) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name_len = strlen(name);
 | 
			
		||||
	while (pos < report->test_list_len) {
 | 
			
		||||
		/* Skip commas and spaces. */
 | 
			
		||||
		pos += strspn(report->test_list + pos, ", ");
 | 
			
		||||
		len = strcspn(report->test_list + pos, ", ");
 | 
			
		||||
		if (len == name_len) {
 | 
			
		||||
			if (memcmp(report->test_list + pos, name, len) == 0) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		pos += len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Start a unit test.
 | 
			
		||||
 *
 | 
			
		||||
 * The when complete, the test must be competed by calling either ttest_pass,
 | 
			
		||||
 * ttest_fail, or ttest_todo.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report        The tlsa-test report context.
 | 
			
		||||
 * \param[in]  name          Name for this unit test.
 | 
			
		||||
 * \param[in]  cleanup       Cleanup function to call on test completion.
 | 
			
		||||
 * \param[in]  cleanup_data  Pointer to client cleanup context.
 | 
			
		||||
 * \param[out] test_ctx_out  Returns the unit test context on success.
 | 
			
		||||
 *                           The test context must be passed to the test
 | 
			
		||||
 *                           completion function.
 | 
			
		||||
 * \return true if the test should be started, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ttest_start(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const char *name,
 | 
			
		||||
		ttest_cleanup_fn cleanup,
 | 
			
		||||
		void *cleanup_data,
 | 
			
		||||
		ttest_ctx_t *test_ctx_out)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc = {
 | 
			
		||||
		.name = name,
 | 
			
		||||
		.report = report,
 | 
			
		||||
		.cleanup = cleanup,
 | 
			
		||||
		.cleanup_data = cleanup_data,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (!ttest__run_test(report, name)) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	report->tests++;
 | 
			
		||||
 | 
			
		||||
	*test_ctx_out = tc;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Pass a unit test.
 | 
			
		||||
 *
 | 
			
		||||
 * This function competes a unit test.  It should be called when a test passes.
 | 
			
		||||
 * This function always returns true.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  tc  Unit test context, returned by ttest_start.
 | 
			
		||||
 * \return true.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ttest_pass(
 | 
			
		||||
		const ttest_ctx_t *tc)
 | 
			
		||||
{
 | 
			
		||||
	assert(tc != NULL);
 | 
			
		||||
	assert(tc->report != NULL);
 | 
			
		||||
 | 
			
		||||
	tc->report->passed++;
 | 
			
		||||
 | 
			
		||||
	if (tc->cleanup != NULL) {
 | 
			
		||||
		tc->cleanup(tc->cleanup_data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tc->report->quiet == false) {
 | 
			
		||||
		fprintf(stderr, "  PASS: %s\n", tc->name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fail a unit test.
 | 
			
		||||
 *
 | 
			
		||||
 * This function competes a unit test.  It should be called when a test fails.
 | 
			
		||||
 * This function always returns false.  Prints the test result.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  tc      Unit test context, returned by ttest_start.
 | 
			
		||||
 * \param[in]  reason  Format string to explain reason for test failure.
 | 
			
		||||
 * \param[in]  ...     Additional arguments for formatted rendering.
 | 
			
		||||
 * \return false.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ttest_fail(
 | 
			
		||||
		const ttest_ctx_t *tc,
 | 
			
		||||
		const char *reason, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list args;
 | 
			
		||||
 | 
			
		||||
	assert(tc != NULL);
 | 
			
		||||
	assert(tc->report != NULL);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "  FAIL: %s (", tc->name);
 | 
			
		||||
	va_start(args, reason);
 | 
			
		||||
	vfprintf(stderr, reason, args);
 | 
			
		||||
	va_end(args);
 | 
			
		||||
	fprintf(stderr, ")\n");
 | 
			
		||||
 | 
			
		||||
	/* Cleanup after printing result, in case `reason` refers to cleaned up
 | 
			
		||||
	 * memory. */
 | 
			
		||||
	if (tc->cleanup != NULL) {
 | 
			
		||||
		tc->cleanup(tc->cleanup_data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Make a unit test as unimplemented.
 | 
			
		||||
 *
 | 
			
		||||
 * This function competes a unit test.  Should be called on unimplemented tests.
 | 
			
		||||
 * This function always returns true.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  tc  Unit test context, returned by ttest_start.
 | 
			
		||||
 * \return true.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ttest_todo(
 | 
			
		||||
		const ttest_ctx_t *tc)
 | 
			
		||||
{
 | 
			
		||||
	assert(tc != NULL);
 | 
			
		||||
	assert(tc->report != NULL);
 | 
			
		||||
 | 
			
		||||
	tc->report->todo++;
 | 
			
		||||
 | 
			
		||||
	if (tc->cleanup != NULL) {
 | 
			
		||||
		tc->cleanup(tc->cleanup_data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tc->report->quiet == false) {
 | 
			
		||||
		fprintf(stderr, "  TODO: %s\n", tc->name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Print a visual divider in the test output.
 | 
			
		||||
 */
 | 
			
		||||
static inline void ttest_divider(void)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "========================================"
 | 
			
		||||
			"========================================\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Print a test heading.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  tr       The tlsa-test report context.
 | 
			
		||||
 * \param[in]  heading  The heading to print.
 | 
			
		||||
 */
 | 
			
		||||
static inline void ttest_heading(
 | 
			
		||||
		const ttest_report_ctx_t *tr,
 | 
			
		||||
		const char *heading)
 | 
			
		||||
{
 | 
			
		||||
	if (tr->test_list == NULL || tr->test_list_len == 0) {
 | 
			
		||||
		if (!tr->quiet) {
 | 
			
		||||
			ttest_divider();
 | 
			
		||||
			fprintf(stderr, "TEST: %s\n", heading);
 | 
			
		||||
			ttest_divider();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Print the test report summary.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  tr  The tlsa-test report context.
 | 
			
		||||
 */
 | 
			
		||||
static inline void ttest_report(
 | 
			
		||||
		const ttest_report_ctx_t *tr)
 | 
			
		||||
{
 | 
			
		||||
	ttest_divider();
 | 
			
		||||
	if (tr->todo > 0) {
 | 
			
		||||
		fprintf(stderr, "TODO: %u test%s unimplemented.\n",
 | 
			
		||||
				tr->todo, (tr->todo > 1) ? "s" : "");
 | 
			
		||||
	}
 | 
			
		||||
	fprintf(stderr, "%s: %u of %u tests passed.\n",
 | 
			
		||||
			(tr->passed == tr->tests - tr->todo) ? "PASS" : "FAIL",
 | 
			
		||||
			tr->passed, tr->tests - tr->todo);
 | 
			
		||||
	ttest_divider();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										280
									
								
								libs/libcyaml/test/units/utf8.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								libs/libcyaml/test/units/utf8.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,280 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
#include "../../src/utf8.h"
 | 
			
		||||
 | 
			
		||||
#include "ttest.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
/** Helper macro to squash unused variable warnings. */
 | 
			
		||||
#define UNUSED(_x) ((void)(_x))
 | 
			
		||||
 | 
			
		||||
/** Helper macro to get the length of string string literals. */
 | 
			
		||||
#define SLEN(_s) (CYAML_ARRAY_LEN(_s) - 1)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test utf-8 decoding.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_utf8_get_codepoint(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	static const struct tests {
 | 
			
		||||
		unsigned c;
 | 
			
		||||
		const char *s;
 | 
			
		||||
		unsigned l;
 | 
			
		||||
	} t[] = {
 | 
			
		||||
		{ 0xfffd, "\ufffd", SLEN("\ufffd") },
 | 
			
		||||
		{ 0xfffd, "\xC1\x9C", SLEN("\xC1\x9C") },
 | 
			
		||||
		{ 0x1f638, u8"😸", SLEN(u8"😸") },
 | 
			
		||||
		{ 0xfffd, u8"😸", 0 },
 | 
			
		||||
		{ 0xfffd, u8"😸", 5 },
 | 
			
		||||
	};
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CYAML_ARRAY_LEN(t); i++) {
 | 
			
		||||
		unsigned l;
 | 
			
		||||
		unsigned c;
 | 
			
		||||
		ttest_ctx_t tc;
 | 
			
		||||
		char name[sizeof(__func__) + 32];
 | 
			
		||||
		sprintf(name, "%s_%u", __func__, i);
 | 
			
		||||
 | 
			
		||||
		if (!ttest_start(report, name, NULL, NULL, &tc)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		l = t[i].l;
 | 
			
		||||
		c = cyaml_utf8_get_codepoint((uint8_t *)t[i].s, &l);
 | 
			
		||||
		if (c != t[i].c) {
 | 
			
		||||
			pass &= ttest_fail(&tc, "Incorrect codepoint for %s "
 | 
			
		||||
					"(expecting %4.4x, got %4.4x)",
 | 
			
		||||
					t[i].s, t[i].c, c);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pass &= ttest_pass(&tc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test comparing the same strings.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_utf8_strcmp_same(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	const char *strings[] = {
 | 
			
		||||
		"Simple",
 | 
			
		||||
		"test",
 | 
			
		||||
		"This is a LONGER string, if you see what I mean.",
 | 
			
		||||
		"29087   lsdkfj  </,.{}'#\"|@>",
 | 
			
		||||
		u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß",
 | 
			
		||||
		u8"àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
 | 
			
		||||
		u8"¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿",
 | 
			
		||||
		"\xc3\0",
 | 
			
		||||
		u8"αβγδε",
 | 
			
		||||
		u8"¯\\_(ツ)_/¯",
 | 
			
		||||
		"\xfa",
 | 
			
		||||
		u8"😸",
 | 
			
		||||
	};
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CYAML_ARRAY_LEN(strings); i++) {
 | 
			
		||||
		ttest_ctx_t tc;
 | 
			
		||||
		char name[sizeof(__func__) + 32];
 | 
			
		||||
		sprintf(name, "%s_%u", __func__, i);
 | 
			
		||||
 | 
			
		||||
		if (!ttest_start(report, name, NULL, NULL, &tc)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cyaml_utf8_casecmp(strings[i], strings[i]) != 0) {
 | 
			
		||||
			pass &= ttest_fail(&tc, "Failed to match: %s",
 | 
			
		||||
					strings[i]);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pass &= ttest_pass(&tc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test comparing strings that match.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_utf8_strcmp_matches(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	static const struct string_pairs {
 | 
			
		||||
		const char *a;
 | 
			
		||||
		const char *b;
 | 
			
		||||
	} pairs[] = {
 | 
			
		||||
		{ "", "" },
 | 
			
		||||
		{ "This is a TEST", "this is A test" },
 | 
			
		||||
		{ u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ", u8"àáâãäåæçèéêëìíîïðñòóôõö" },
 | 
			
		||||
		{ u8"ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞ", u8"āăąćĉċčďđēĕėęěĝğ" },
 | 
			
		||||
		{ u8"ijĵķĸĺļľ", u8"IJĴĶĸĹĻĽ" },
 | 
			
		||||
		{ u8"ŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶ", u8"ŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷ" },
 | 
			
		||||
		{ u8"űųŵŷŸźżž", u8"űųŵŷÿŹŻŽ" },
 | 
			
		||||
		{ u8"ƂƄ ơƣƥ", u8"ƃƅ ƠƢƤ" },
 | 
			
		||||
		{ u8"ǞǠǢǤǦǨǪǬǮ", u8"ǟǡǣǥǧǩǫǭǯ" },
 | 
			
		||||
		{ u8"ǸǺǼǾȀȂȄȆȈȊȌȎȐȒȔȖȘȚȜȞ", u8"ǹǻǽǿȁȃȅȇȉȋȍȏȑȓȕȗșțȝȟ" },
 | 
			
		||||
		{ u8"ȢȤȦȨȪȬȮȰȲ", u8"ȣȥȧȩȫȭȯȱȳ" },
 | 
			
		||||
		{ u8"ɇɉɋɍɏ", u8"ɆɈɊɌɎ" },
 | 
			
		||||
		{ u8"ƯźżžƳƵ", u8"ưŹŻŽƴƶ" },
 | 
			
		||||
		{ u8"ǍǏǑǓǕǗǙǛ", u8"ǎǐǒǔǖǘǚǜ" },
 | 
			
		||||
		{ u8"\u0178", u8"\u00ff" },
 | 
			
		||||
		{ u8"\u0187", u8"\u0188" },
 | 
			
		||||
		{ u8"\u018b", u8"\u018c" },
 | 
			
		||||
		{ u8"\u018e", u8"\u01dd" },
 | 
			
		||||
		{ u8"\u0191", u8"\u0192" },
 | 
			
		||||
		{ u8"\u0198", u8"\u0199" },
 | 
			
		||||
		{ u8"\u01a7", u8"\u01a8" },
 | 
			
		||||
		{ u8"\u01ac", u8"\u01ad" },
 | 
			
		||||
		{ u8"\u01af", u8"\u01b0" },
 | 
			
		||||
		{ u8"\u01b7", u8"\u0292" },
 | 
			
		||||
		{ u8"\u01b8", u8"\u01b9" },
 | 
			
		||||
		{ u8"\u01bc", u8"\u01bd" },
 | 
			
		||||
		{ u8"\u01c4", u8"\u01c6" },
 | 
			
		||||
		{ u8"\u01c5", u8"\u01c6" },
 | 
			
		||||
		{ u8"\u01c7", u8"\u01c9" },
 | 
			
		||||
		{ u8"\u01c8", u8"\u01c9" },
 | 
			
		||||
		{ u8"\u01ca", u8"\u01cc" },
 | 
			
		||||
		{ u8"\u01cb", u8"\u01cc" },
 | 
			
		||||
		{ u8"\u01f1", u8"\u01f3" },
 | 
			
		||||
		{ u8"\u01f2", u8"\u01f3" },
 | 
			
		||||
		{ u8"\u01f4", u8"\u01f5" },
 | 
			
		||||
		{ u8"\u01f7", u8"\u01bf" },
 | 
			
		||||
		{ u8"\u0220", u8"\u019e" },
 | 
			
		||||
		{ u8"\u023b", u8"\u023c" },
 | 
			
		||||
		{ u8"\u023d", u8"\u019a" },
 | 
			
		||||
		{ u8"\u0241", u8"\u0242" },
 | 
			
		||||
		{ u8"\u0243", u8"\u0180" },
 | 
			
		||||
		{ "\xF0\x9F\x98\xB8", "\xF0\x9F\x98\xB8" },
 | 
			
		||||
		{ "\xF0\x00\x98\xB8", "\xF0\x00\x98\xB8" },
 | 
			
		||||
		{ "\xF0\x9F\x00\xB8", "\xF0\x9F\x00\xB8" },
 | 
			
		||||
		{ "\xF0\x9F\x98\x00", "\xF0\x9F\x98\x00" },
 | 
			
		||||
		{ "\xE2\x9F\x9A", "\xE2\x9F\x9A" },
 | 
			
		||||
		{ "\xE2\x00\x9A", "\xE2\x00\x9A" },
 | 
			
		||||
		{ "\xE2\x9F\x00", "\xE2\x9F\x00" },
 | 
			
		||||
		{ "A\xc2""C", "A\xc2""C" },
 | 
			
		||||
		{ "A\xc2""C", u8"A\ufffdC" },
 | 
			
		||||
		{ u8"A\ufffdC", "A\xc2""C" },
 | 
			
		||||
	};
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) {
 | 
			
		||||
		ttest_ctx_t tc;
 | 
			
		||||
		char name[sizeof(__func__) + 32];
 | 
			
		||||
		sprintf(name, "%s_%u", __func__, i);
 | 
			
		||||
 | 
			
		||||
		if (!ttest_start(report, name, NULL, NULL, &tc)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) != 0) {
 | 
			
		||||
			pass &= ttest_fail(&tc, "Failed to match strings: "
 | 
			
		||||
					"%s and %s", pairs[i].a, pairs[i].b);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pass &= ttest_pass(&tc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test comparing strings that match.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_utf8_strcmp_mismatches(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	static const struct string_pairs {
 | 
			
		||||
		const char *a;
 | 
			
		||||
		const char *b;
 | 
			
		||||
	} pairs[] = {
 | 
			
		||||
		{ "Invalid", "\xfa" },
 | 
			
		||||
		{ "Cat", u8"😸" },
 | 
			
		||||
		{ "cat", u8"😸" },
 | 
			
		||||
		{ "1 cat", u8"😸" },
 | 
			
		||||
		{ "[cat]", u8"😸" },
 | 
			
		||||
		{ "Ü cat", u8"😸" },
 | 
			
		||||
		{ "Ü cat", u8"😸" },
 | 
			
		||||
		{ "\\", "\xC1\x9C" },
 | 
			
		||||
	};
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) {
 | 
			
		||||
		ttest_ctx_t tc;
 | 
			
		||||
		char name[sizeof(__func__) + 32];
 | 
			
		||||
		sprintf(name, "%s_%u", __func__, i);
 | 
			
		||||
 | 
			
		||||
		if (!ttest_start(report, name, NULL, NULL, &tc)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) == 0) {
 | 
			
		||||
			pass &= ttest_fail(&tc, "Failed to detect mismatch: "
 | 
			
		||||
					"%s and %s", pairs[i].a, pairs[i].b);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pass &= ttest_pass(&tc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Run the CYAML util unit tests.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  rc         The ttest report context.
 | 
			
		||||
 * \param[in]  log_level  CYAML log level.
 | 
			
		||||
 * \param[in]  log_fn     CYAML logging function, or NULL.
 | 
			
		||||
 * \return true iff all unit tests pass, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool utf8_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn)
 | 
			
		||||
{
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
 | 
			
		||||
	UNUSED(log_level);
 | 
			
		||||
	UNUSED(log_fn);
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "UTF-8 tests: Codepoint composition");
 | 
			
		||||
 | 
			
		||||
	pass &= test_utf8_get_codepoint(rc);
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "UTF-8 tests: String comparison");
 | 
			
		||||
 | 
			
		||||
	pass &= test_utf8_strcmp_same(rc);
 | 
			
		||||
	pass &= test_utf8_strcmp_matches(rc);
 | 
			
		||||
	pass &= test_utf8_strcmp_mismatches(rc);
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										300
									
								
								libs/libcyaml/test/units/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								libs/libcyaml/test/units/util.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,300 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: ISC
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <cyaml/cyaml.h>
 | 
			
		||||
 | 
			
		||||
#include "../../src/mem.h"
 | 
			
		||||
#include "../../src/util.h"
 | 
			
		||||
 | 
			
		||||
#include "ttest.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml memory functions.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_memory_funcs(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
	unsigned char *mem, *tmp;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	/* Create test allocation */
 | 
			
		||||
	mem = cyaml__alloc(config, 0xff, true);
 | 
			
		||||
	if (mem == NULL) {
 | 
			
		||||
		return ttest_fail(&tc, "Memory allocation failed.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check allocation was zeroed. */
 | 
			
		||||
	for (unsigned i = 0; i < 0x7f; i++) {
 | 
			
		||||
		if (mem[i] != 0) {
 | 
			
		||||
			return ttest_fail(&tc, "Allocation not cleaned.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set our own known values */
 | 
			
		||||
	for (unsigned i = 0; i < 0xff; i++) {
 | 
			
		||||
		mem[i] = 0xff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Shrink allocation */
 | 
			
		||||
	tmp = cyaml__realloc(config, mem, 0xff, 0x7f, true);
 | 
			
		||||
	if (tmp == NULL) {
 | 
			
		||||
		return ttest_fail(&tc, "Allocation shrink failed.");
 | 
			
		||||
	}
 | 
			
		||||
	mem = tmp;
 | 
			
		||||
 | 
			
		||||
	/* Check for our own values. */
 | 
			
		||||
	for (unsigned i = 0; i < 0x7f; i++) {
 | 
			
		||||
		if (mem[i] != 0xff) {
 | 
			
		||||
			return ttest_fail(&tc, "Data trampled by realloc.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Free test allocation. */
 | 
			
		||||
	cyaml__free(config, mem);
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml memory functions.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_strdup(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
	const char *orig = "Hello!";
 | 
			
		||||
	char *copy;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	/* Create test allocation */
 | 
			
		||||
	copy = cyaml__strdup(config, orig, NULL);
 | 
			
		||||
	if (copy == NULL) {
 | 
			
		||||
		return ttest_fail(&tc, "Memory allocation failed.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Confirm string duplication. */
 | 
			
		||||
	if (strcmp(orig, copy) != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "String not copied correctly.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Free test allocation. */
 | 
			
		||||
	cyaml__free(config, copy);
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test cyaml memory functions.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \param[in]  config  The CYAML config to use for the test.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_strdup_len(
 | 
			
		||||
		ttest_report_ctx_t *report,
 | 
			
		||||
		const cyaml_config_t *config)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
	const char *orig = "Hello!";
 | 
			
		||||
	char *copy;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	/* Create test allocation */
 | 
			
		||||
	copy = cyaml__strdup(config, orig, &len);
 | 
			
		||||
	if (copy == NULL) {
 | 
			
		||||
		return ttest_fail(&tc, "Memory allocation failed.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Confirm string duplication. */
 | 
			
		||||
	if (strcmp(orig, copy) != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "String not copied correctly.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Confirm string length. */
 | 
			
		||||
	if (strlen(orig) != len) {
 | 
			
		||||
		return ttest_fail(&tc, "Bad string length.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Free test allocation. */
 | 
			
		||||
	cyaml__free(config, copy);
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test invalid state machine state.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_state_invalid(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT),
 | 
			
		||||
			"<invalid>") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "Wrong string for invalid state.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT + 1),
 | 
			
		||||
			"<invalid>") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "Wrong string for invalid state.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test CYAML_OK error code.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_err_success_zero(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	if (CYAML_OK != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "CYAML_OK value not zero");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test valid cyaml_strerror strings.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_err_strings_valid(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	if (cyaml_strerror(CYAML_OK) == NULL) {
 | 
			
		||||
		return ttest_fail(&tc, "CYAML_OK string is NULL");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml_strerror(CYAML_OK), "Success") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "CYAML_OK string not 'Success'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 1; i <= CYAML_ERR__COUNT; i++) {
 | 
			
		||||
		if (cyaml_strerror(i) == NULL) {
 | 
			
		||||
			return ttest_fail(&tc, "Error code %u string is NULL",
 | 
			
		||||
					i);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (unsigned j = 0; j < i; j++) {
 | 
			
		||||
			if (strcmp(cyaml_strerror(j), cyaml_strerror(i)) == 0) {
 | 
			
		||||
				return ttest_fail(&tc, "Two error codes "
 | 
			
		||||
						"have same string (%u and %u)",
 | 
			
		||||
						i, j);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test invalid cyaml_strerror strings.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  report  The test report context.
 | 
			
		||||
 * \return true if test passes, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static bool test_util_err_strings_invalid(
 | 
			
		||||
		ttest_report_ctx_t *report)
 | 
			
		||||
{
 | 
			
		||||
	ttest_ctx_t tc;
 | 
			
		||||
 | 
			
		||||
	if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml_strerror(CYAML_ERR__COUNT),
 | 
			
		||||
			"Invalid error code") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "CYAML_ERR__COUNT string not "
 | 
			
		||||
				"'Invalid error code'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml_strerror(CYAML_ERR__COUNT + 1),
 | 
			
		||||
			"Invalid error code") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "CYAML_ERR__COUNT + 1 string not "
 | 
			
		||||
				"'Invalid error code'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cyaml_strerror((cyaml_err_t)-1), "Invalid error code") != 0) {
 | 
			
		||||
		return ttest_fail(&tc, "-1 string not 'Invalid error code'");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ttest_pass(&tc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Run the CYAML util unit tests.
 | 
			
		||||
 *
 | 
			
		||||
 * \param[in]  rc         The ttest report context.
 | 
			
		||||
 * \param[in]  log_level  CYAML log level.
 | 
			
		||||
 * \param[in]  log_fn     CYAML logging function, or NULL.
 | 
			
		||||
 * \return true iff all unit tests pass, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool util_tests(
 | 
			
		||||
		ttest_report_ctx_t *rc,
 | 
			
		||||
		cyaml_log_t log_level,
 | 
			
		||||
		cyaml_log_fn_t log_fn)
 | 
			
		||||
{
 | 
			
		||||
	bool pass = true;
 | 
			
		||||
	cyaml_config_t config = {
 | 
			
		||||
		.log_fn = log_fn,
 | 
			
		||||
		.mem_fn = cyaml_mem,
 | 
			
		||||
		.log_level = log_level,
 | 
			
		||||
		.flags = CYAML_CFG_DEFAULT,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "Memory utility functions");
 | 
			
		||||
 | 
			
		||||
	pass &= test_util_strdup(rc, &config);
 | 
			
		||||
	pass &= test_util_strdup_len(rc, &config);
 | 
			
		||||
	pass &= test_util_memory_funcs(rc, &config);
 | 
			
		||||
 | 
			
		||||
	ttest_heading(rc, "Error code tests");
 | 
			
		||||
 | 
			
		||||
	pass &= test_util_state_invalid(rc);
 | 
			
		||||
	pass &= test_util_err_success_zero(rc);
 | 
			
		||||
	pass &= test_util_err_strings_valid(rc);
 | 
			
		||||
	pass &= test_util_err_strings_invalid(rc);
 | 
			
		||||
 | 
			
		||||
	return pass;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								objs/libcyaml.so
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								objs/libcyaml.so
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
			
		||||
/home/joe/Development/Ennix/objs/libcyaml.so.1
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								objs/libcyaml.so.1
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								objs/libcyaml.so.1
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user