commit
f8eb024c1a
36 changed files with 25901 additions and 0 deletions
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2019 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of Zrythm |
||||
# |
||||
# Zrythm is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Affero General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Zrythm is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Affero General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Affero General Public License |
||||
# along with Zrythm. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
# Build files |
||||
build |
||||
builddir |
||||
_build |
||||
|
||||
# -- C++ |
||||
# Prerequisites |
||||
*.d |
||||
|
||||
# Compiled Object files |
||||
*.slo |
||||
*.lo |
||||
*.o |
||||
*.obj |
||||
**/*/*.so* |
||||
|
||||
# Precompiled Headers |
||||
*.gch |
||||
*.pch |
||||
|
||||
# Compiled Dynamic libraries |
||||
*.so |
||||
*.dylib |
||||
build/*.dll |
||||
|
||||
# Fortran module files |
||||
*.mod |
||||
*.smod |
||||
|
||||
# Compiled Static libraries |
||||
*.lai |
||||
*.la |
||||
*.a |
||||
*.lib |
||||
|
||||
# Executables |
||||
*.exe |
||||
*.out |
||||
*.app |
||||
|
||||
# -- Vim |
||||
# Swap |
||||
[._]*.s[a-v][a-z] |
||||
[._]*.sw[a-p] |
||||
[._]s[a-rt-v][a-z] |
||||
[._]ss[a-gi-z] |
||||
[._]sw[a-p] |
||||
.vimrc |
||||
|
||||
# Session |
||||
Session.vim |
||||
|
||||
# Temporary |
||||
.netrwhist |
||||
*~ |
||||
*.ui# |
||||
**/.deps |
||||
|
||||
# Auto-generated tag files |
||||
tags |
||||
# Persistent undo |
||||
[._]*.un~ |
||||
|
||||
# Backup files |
||||
*.bak |
||||
|
||||
# Valgrind |
||||
vgcore.* |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2017-2018, 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. |
||||
|
@ -0,0 +1,195 @@
@@ -0,0 +1,195 @@
|
||||
# SPDX-License-Identifier: ISC
|
||||
#
|
||||
# Copyright (C) 2017-2020 Michael Drake <tlsa@netsurf-browser.org>
|
||||
|
||||
# CYAML's versioning is <MAJOR>.<MINOR>.<PATCH>[-DEVEL]
|
||||
# Master branch will always be DEVEL. The release process will be to make
|
||||
# the release branch, set VESION_DEVEL to 0, and tag the release.
|
||||
VERSION_MAJOR = 1 |
||||
VERSION_MINOR = 0 |
||||
VERSION_PATCH = 0 |
||||
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 |
||||
|
||||
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 |
||||
|
||||
CC ?= gcc |
||||
AR ?= ar |
||||
MKDIR = mkdir -p |
||||
INSTALL ?= install -c -D |
||||
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 |
||||
CFLAGS += $(INCLUDE) $(VERSION_FLAGS) $(LIBYAML_CFLAGS) |
||||
CFLAGS += -std=c11 -Wall -Wextra -pedantic -Wconversion |
||||
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_OBJ_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_OBJ)) |
||||
LIB_OBJ_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_OBJ)) |
||||
|
||||
LIB_PATH = LD_LIBRARY_PATH=$(BUILDDIR) |
||||
|
||||
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_BINS = \
|
||||
$(BUILDDIR)/test/units/cyaml-shared \
|
||||
$(BUILDDIR)/test/units/cyaml-static |
||||
|
||||
all: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) examples |
||||
|
||||
coverage: test-verbose |
||||
@$(MKDIR) $(BUILDDIR) |
||||
@gcovr -e 'test/.*' -r . |
||||
@gcovr -e 'test/.*' -x -o build/coverage.xml -r . |
||||
@gcovr -e 'test/.*' --html --html-details -o build/coverage.html -r . |
||||
|
||||
test: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $$i || exit; done |
||||
|
||||
test-quiet: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $$i -q || exit; done |
||||
|
||||
test-verbose: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $$i -v || exit; done |
||||
|
||||
test-debug: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $$i -d || exit; done |
||||
|
||||
valgrind: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i || exit; done |
||||
|
||||
valgrind-quiet: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i -q || exit; done |
||||
|
||||
valgrind-verbose: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i -v || exit; done |
||||
|
||||
valgrind-debug: $(TEST_BINS) |
||||
@for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i -d || exit; done |
||||
|
||||
$(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) $(LDFLAGS) $(LDFLAGS_COV) $(LDFLAGS_SHARED) -o $@ $^ |
||||
|
||||
$(LIB_OBJ_STATIC): $(BUILDDIR_STATIC)/%.o : %.c |
||||
@$(MKDIR) $(BUILDDIR_STATIC)/src |
||||
$(CC) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $< |
||||
|
||||
$(LIB_OBJ_SHARED): $(BUILDDIR_SHARED)/%.o : %.c |
||||
@$(MKDIR) $(BUILDDIR_SHARED)/src |
||||
$(CC) $(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) $(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) -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) $(CFLAGS) -o $@ $^ $(LDFLAGS) |
||||
|
||||
$(BUILDDIR)/numerical: examples/numerical/main.c $(BUILDDIR)/$(LIB_STATIC) |
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) |
||||
|
||||
.PHONY: all test test-quiet test-verbose test-debug \ |
||||
valgrind valgrind-quiet valgrind-verbose valgrind-debug \
|
||||
clean coverage docs install examples |
||||
|
||||
$(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 |
||||
@$(MKDIR) $(BUILDDIR)/test/units |
||||
$(CC) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $< |
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
LibCYAML: Schema-based YAML parsing and serialisation |
||||
===================================================== |
||||
|
||||
[](https://github.com/tlsa/libcyaml/actions) [](https://codecov.io/gh/tlsa/libcyaml) [](https://lgtm.com/projects/g/tlsa/libcyaml/alerts/) |
||||
|
||||
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 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/master/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/). |
@ -0,0 +1,15 @@
@@ -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 |
@ -0,0 +1,15 @@
@@ -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 |
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
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/master/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". The 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_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ |
||||
.log_fn = cyaml_log, /* Use the default logging function. */ |
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */ |
||||
}; |
||||
|
||||
/* Where to store the loaded data */ |
||||
struct numbers *n; |
||||
|
||||
/* Load the file into p */ |
||||
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. |
@ -0,0 +1,7 @@
@@ -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. |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
name: Fibonacci |
||||
data: |
||||
- 1 |
||||
- 1 |
||||
- 2 |
||||
- 3 |
||||
- 5 |
||||
- 8 |
@ -0,0 +1,107 @@
@@ -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_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ |
||||
.log_fn = cyaml_log, /* Use the default logging function. */ |
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */ |
||||
}; |
||||
|
||||
/* 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; |
||||
} |
@ -0,0 +1,8 @@
@@ -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. |
@ -0,0 +1,468 @@
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* 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. |
||||
******************************************************************************/ |
||||
|
||||
/* 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_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ |
||||
.log_fn = cyaml_log, /* Use the default logging function. */ |
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */ |
||||
}; |
||||
|
||||
/* 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; |
||||
} |
@ -0,0 +1,58 @@
@@ -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 |
@ -0,0 +1,10 @@
@@ -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} |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2019-2020 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of Zrythm |
||||
# |
||||
# Zrythm is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Affero General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# Zrythm is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Affero General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Affero General Public License |
||||
# along with Zrythm. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
project ( |
||||
'zrythm-cyaml', ['c'], |
||||
version: '1.0.0', |
||||
license: 'ISC', |
||||
default_options: [ |
||||
'warning_level=1', |
||||
'buildtype=debug', |
||||
'c_std=gnu11', |
||||
], |
||||
) |
||||
|
||||
cc = meson.get_compiler ('c') |
||||
|
||||
cargs = [ |
||||
'-DVERSION_MAJOR=1', |
||||
'-DVERSION_MINOR=0', |
||||
'-DVERSION_PATCH=0', |
||||
] |
||||
|
||||
yaml_dep = dependency('yaml-0.1') |
||||
|
||||
zrythm_cyaml_inc = [ |
||||
include_directories ('include'), |
||||
] |
||||
zrythm_cyaml = static_library ( |
||||
'cyaml', |
||||
sources: [ |
||||
join_paths ('src', 'mem.c'), |
||||
join_paths ('src', 'save.c'), |
||||
join_paths ('src', 'free.c'), |
||||
join_paths ('src', 'load.c'), |
||||
join_paths ('src', 'utf8.c'), |
||||
join_paths ('src', 'util.c'), |
||||
], |
||||
c_args: cargs, |
||||
dependencies: yaml_dep, |
||||
include_directories: zrythm_cyaml_inc, |
||||
) |
||||
|
||||
zrythm_cyaml_dep = declare_dependency ( |
||||
include_directories: 'include', |
||||
link_with: zrythm_cyaml) |
@ -0,0 +1,142 @@
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC |
||||
* |
||||
* Copyright (C) 2017 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) |
||||
{ |
||||
if (entry_size == 0) { |
||||
return CYAML_ERR_INVALID_DATA_SIZE; |
||||
} |
||||
|
||||
data_tgt += entry_size - 1; |
||||
|
||||
switch (entry_size) { |
||||
case 8: *data_tgt-- = (uint8_t)(value >> 56) & 0xff; /* Fall through. */ |
||||
case 7: *data_tgt-- = (uint8_t)(value >> 48) & 0xff; /* Fall through. */ |
||||
case 6: *data_tgt-- = (uint8_t)(value >> 40) & 0xff; /* Fall through. */ |
||||
case 5: *data_tgt-- = (uint8_t)(value >> 32) & 0xff; /* Fall through. */ |
||||
case 4: *data_tgt-- = (uint8_t)(value >> 24) & 0xff; /* Fall through. */ |
||||
case 3: *data_tgt-- = (uint8_t)(value >> 16) & 0xff; /* Fall through. */ |
||||
case 2: *data_tgt-- = (uint8_t)(value >> 8) & 0xff; /* Fall through. */ |
||||
case 1: *data_tgt-- = (uint8_t)(value >> 0) & 0xff; |
||||
break; |
||||
default: |
||||
return CYAML_ERR_INVALID_DATA_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. */ |
||||
cyaml_static_assert(sizeof(char *) > 0); |
||||
cyaml_static_assert(sizeof(char *) <= sizeof(uint64_t)); |
||||
|
||||
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; |
||||
|
||||
if (entry_size == 0) { |
||||
*error_out = CYAML_ERR_INVALID_DATA_SIZE; |
||||
return ret; |
||||
} |
||||
|
||||
data += entry_size - 1; |
||||
|
||||
switch (entry_size) { |
||||
case 8: ret |= ((uint64_t)(*data-- & 0xff)) << 56; /* Fall through. */ |
||||
case 7: ret |= ((uint64_t)(*data-- & 0xff)) << 48; /* Fall through. */ |
||||
case 6: ret |= ((uint64_t)(*data-- & 0xff)) << 40; /* Fall through. */ |
||||
case 5: ret |= ((uint64_t)(*data-- & 0xff)) << 32; /* Fall through. */ |
||||
case 4: ret |= ((uint64_t)(*data-- & 0xff)) << 24; /* Fall through. */ |
||||
case 3: ret |= ((uint64_t)(*data-- & 0xff)) << 16; /* Fall through. */ |
||||
case 2: ret |= ((uint64_t)(*data-- & 0xff)) << 8; /* Fall through. */ |
||||
case 1: ret |= ((uint64_t)(*data-- & 0xff)) << 0; |
||||
break; |
||||
default: |
||||
*error_out = CYAML_ERR_INVALID_DATA_SIZE; |
||||
return ret; |
||||
} |
||||
|
||||
*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. */ |
||||
cyaml_static_assert(sizeof(char *) > 0); |
||||
cyaml_static_assert(sizeof(char *) <= sizeof(uint64_t)); |
||||
|
||||
return (void *)cyaml_data_read(sizeof(char *), data, &err); |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC |
||||
* |
||||
* Copyright (C) 2017-2018 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, |
||||