update mustach library

This commit is contained in:
Christian Grothoff 2023-05-11 01:16:53 +02:00
parent 0dd0fff17d
commit ec8ad2e3b3
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
18 changed files with 2390 additions and 920 deletions

View File

@ -1 +1,2 @@
test_mustach_jansson
taler-mustach-tool

View File

@ -4,8 +4,15 @@ Main author:
Contributors:
Abhishek Mishra
Atlas
Ben Beasley
Gabriel Zachmann
Harold L Marzan
Kurt Jung
Lailton Fernando Mariano
Lucas Ramage
Paramtamtam
RekGRpth
Ryan Fox
Sami Kerola
Sijmen J. Mulder
Tomasz Sieprawski
@ -13,6 +20,7 @@ Contributors:
Packagers:
pkgsrc: Sijmen J. Mulder
alpine linux: Lucas Ramage
RPM & DEB: Marcus Hardt
Thanks to issue submitters:
Dante Torres
@ -21,3 +29,4 @@ Thanks to issue submitters:
Mark Bucciarelli
Paul Wisehart
Thierry Fournier
SASU OKFT

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,14 @@
Copyright (c) 2017-2020 by José Bollo
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 ISC DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL ISC 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.

View File

@ -6,6 +6,15 @@ if USE_COVERAGE
XLIB = -lgcov
endif
noinst_PROGRAMS = \
taler-mustach-tool
taler_mustach_tool_SOURCES = \
mustach-tool.c \
mustach-jansson.h
taler_mustach_tool_LDADD = \
libmustach.la \
-ljansson
lib_LTLIBRARIES = \
libtalertemplating.la
@ -15,6 +24,7 @@ noinst_LTLIBRARIES = \
libtalertemplating_la_SOURCES = \
mustach.c mustach.h \
mustach-wrap.c mustach-wrap.h \
mustach-jansson.c mustach-jansson.h \
templating_api.c
libtalertemplating_la_LIBADD = \

View File

@ -1,16 +1,27 @@
# Introduction to Mustach 0.99
# Introduction to Mustach 1.2
`mustach` is a C implementation of the [mustache](http://mustache.github.io "main site for mustache")
template specification.
The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach).
The best way to use mustach is to copy the files **mustach.h** and **mustach.c**
The simpliest way to use mustach is to copy the files **mustach.h** and **mustach.c**
directly into your project and use it.
Alternatively, make and meson files are provided for building `mustach` and
If you are using one of the JSON libraries listed below, you can get extended feature
by also including **mustach-wrap.h**, **mustach-wrap.c**, **mustach-XXX.h** and
**mustach-XXX.c** in your project (see below for **XXX**)
- [json-c](https://github.com/json-c/json-c): use **XXX** = **json-c**
- [jansson](http://www.digip.org/jansson/): use **XXX** = **jansson**
- [cJSON](https://github.com/DaveGamble/cJSON): use **XXX** = **cjson**
Alternatively, make and meson files are provided for building `mustach` and
`libmustach.so` shared library.
Since version 1.0, the makefile allows to compile and install different
flavours. See below for details.
## Distributions offering mustach package
### Alpine Linux
@ -30,6 +41,13 @@ make
See http://pkgsrc.se/devel/mustach
## Known projects using Mustach
This [wiki page](https://gitlab.com/jobol/mustach/-/wikis/projects-using-mustach)
lists the known project that are using mustach and that kindly told it.
Don't hesitate to tell us if you are interested to be listed there.
## Using Mustach from sources
The file **mustach.h** is the main documentation. Look at it.
@ -38,15 +56,25 @@ The current source files are:
- **mustach.c** core implementation of mustache in C
- **mustach.h** header file for core definitions
- **mustach-wrap.c** generic wrapper of mustach for easier integration
- **mustach-wrap.h** header file for using mustach-wrap
- **mustach-json-c.c** tiny json wrapper of mustach using [json-c](https://github.com/json-c/json-c)
- **mustach-json-c.h** header file for using the tiny JSON wrapper
- **mustach-tool.c** simple tool for applying template files to a JSON file
- **mustach-json-c.h** header file for using the tiny json-c wrapper
- **mustach-cjson.c** tiny json wrapper of mustach using [cJSON](https://github.com/DaveGamble/cJSON)
- **mustach-cjson.h** header file for using the tiny cJSON wrapper
- **mustach-jansson.c** tiny json wrapper of mustach using [jansson](https://www.digip.org/jansson/)
- **mustach-jansson.h** header file for using the tiny jansson wrapper
- **mustach-tool.c** simple tool for applying template files to one JSON file
The file **mustach-json-c.c** is the main example of use of **mustach** core
and it is also a practical implementation that can be used. It uses the library
json-c. (NOTE for Mac OS: available through homebrew).
The file **mustach-json-c.c** is the historical example of use of **mustach** and
**mustach-wrap** core and it is also a practical implementation that can be used.
It uses the library json-c. (NOTE for Mac OS: available through homebrew).
HELP REQUESTED TO GIVE EXAMPLE BASED ON OTHER LIBRARIES (ex: janson, ...).
Since version 1.0, the project also provide integration of other JSON libraries:
**cJSON** and **jansson**.
*If you integrate a new library with* **mustach**, *your contribution will be
welcome here*.
The tool **mustach** is build using `make`, its usage is:
@ -57,14 +85,14 @@ It then outputs the result of applying the templates files to the JSON file.
### Portability
Some system does not provide *open_memstream*. In that case, tell your
preferred compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
prefered compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
Example:
gcc -DNO_OPEN_MEMSTREAM
CFLAGS=-DNO_OPEN_MEMSTREAM make
### Integration
The file **mustach.h** is the main documentation. Look at it.
The files **mustach.h** and **mustach-wrap.h** are the main documentation. Look at it.
The file **mustach-json-c.c** provides a good example of integration.
@ -76,139 +104,211 @@ If you intend to use specific escaping and/or specific output, the callbacks
of the interface **mustach_itf** that you have to implement are:
`enter`, `next`, `leave`, `get` and `emit`.
### Extensions
### Compilation Using Make
By default, the current implementation provides the following extensions:
Building and installing can be done using make.
#### Explicit Substitution
Example:
This is a core extension implemented in file **mustach.c**.
$ make tool=cjson libs=none PREFIX=/usr/local DESTDIR=/ install
$ make tool=jsonc libs=single PREFIX=/ DESTDIR=$HOME/.local install
In somecases the name of the key used for substitution begins with a
The makefile knows following switches (\*: default):
Switch name | Values | Description
--------------+---------+-----------------------------------------------
jsonc | (unset) | Auto detection of json-c
| no | Don't compile for json-c
| yes | Compile for json-c that must exist
--------------+---------+-----------------------------------------------
cjson | (unset) | Auto detection of cJSON
| no | Don't compile for cJSON
| yes | Compile for cJSON that must exist
--------------+---------+-----------------------------------------------
jansson | (unset) | Auto detection of jansson
| no | Don't compile for jansson
| yes | Compile for jansson that must exist
--------------+---------+-----------------------------------------------
tool | (unset) | Auto detection
| cjson | Use cjson library
| jsonc | Use jsonc library
| jansson | Use jansson library
| none | Don't compile the tool
--------------+---------+----------------------------------------------
libs | (unset) | Like 'all'
| all | Like 'single' AND 'split'
| single | Only libmustach.so
| split | All the possible libmustach-XXX.so ...
| none | No library is produced
The libraries that can be produced are:
Library name | Content
--------------------+--------------------------------------------------------
libmustach-core | mustach.c mustach-wrap.c
libmustach-cjson | mustach.c mustach-wrap.c mustach-cjson.c
libmustach-jsonc | mustach.c mustach-wrap.c mustach-json-c.c
libmustach-jansson | mustach.c mustach-wrap.c mustach-jansson.c
libmustach | mustach.c mustach-wrap.c mustach-{cjson,json-c,jansson}.c
There is no dependencies of a library to an other. This is intended and doesn't
hurt today because the code is small.
## Extensions
The current implementation provides extensions to specifications of **mustache**.
This extensions can be activated or deactivated using flags.
Here is the summary.
Flag name | Description
-------------------------------+------------------------------------------------
Mustach_With_Colon | Explicit tag substition with colon
Mustach_With_EmptyTag | Empty Tag Allowed
-------------------------------+------------------------------------------------
Mustach_With_Equal | Value Testing Equality
Mustach_With_Compare | Value Comparing
Mustach_With_JsonPointer | Interpret JSON Pointers
Mustach_With_ObjectIter | Iteration On Objects
Mustach_With_EscFirstCmp | Escape First Compare
Mustach_With_ErrorUndefined | Error when a requested tag is undefined
-------------------------------+------------------------------------------------
Mustach_With_AllExtensions | Activate all known extensions
Mustach_With_NoExtensions | Disable any extension
For the details, see below.
### Explicit Tag Substitution With Colon (Mustach_With_Colon)
In somecases the name of the key used for substition begins with a
character reserved for mustach: one of `#`, `^`, `/`, `&`, `{`, `>` and `=`.
This extension introduces the special character `:` to explicitly
tell mustach to just substitute the value. So `:` becomes a new special
character.
#### Value Testing and Comparing
This is a core extension implemented in file **mustach.c**.
This are a tool extension implemented in file **mustach-json-c.c**.
### Empty Tag Allowed (Mustach_With_EmptyTag)
These extensions allows you to test the value of the selected key.
They allow to write `key=value` (matching test) or `key=!value`
When an empty tag is found, instead of automatically raising the error
MUSTACH\_ERROR\_EMPTY\_TAG pass it.
This is a core extension implemented in file **mustach.c**.
### Value Testing Equality (Mustach_With_Equal)
This extension allows you to test the value of the selected key.
It allows to write `key=value` (matching test) or `key=!value`
(not matching test) in any query.
The specific comparison extension also allows to compare if greater,
lesser, etc.. than a value. It allows to write `key>value`.
This is a wrap extension implemented in file **mustach-wrap.c**.
### Value Comparing (Mustach_With_Compare)
These extension extends the extension for testing equality to also
compare values if greater or lesser.
Its allows to write `key>value` (greater), `key>=value` (greater or equal),
`key<value` (lesser) and `key<=value` (lesser or equal).
It the comparator sign appears in the first column it is ignored
as if it was escaped.
#### Access to current value
This is a wrap extension implemented in file **mustach-wrap.c**.
The value of the current field can be accessed using single dot like
in `{{#key}}{{.}}{{/key}}` that applied to `{"key":3.14}` produces `3.14`
and `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces
` 1 2`.
### Interpret JSON Pointers (Mustach_With_JsonPointer)
#### Iteration on objects
This extension allows to use JSON pointers as defined in IETF RFC 6901.
If active, any key starting with "/" is a JSON pointer.
This implies to use the colon to introduce JSON keys.
Using the pattern `{{#X.*}}...{{/X.*}}` it is possible to iterate on
fields of `X`. Example: `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on
`{"s":{"a":1,"b":true}}` produces ` a:1 b:true`. Here the single star
`{{*}}` is replaced by the iterated key and the single dot `{{.}}` is
replaced by its value.
A special escaping is used for `=`, `<`, `>` signs when
values comparisons are enabled: `~=` gives `=` in the key.
### Removing Extensions
This is a wrap extension implemented in file **mustach-wrap.c**.
When compiling mustach.c or mustach-json-c.c,
extensions can be removed by defining macros
using option -D.
### Iteration On Objects (Mustach_With_ObjectIter)
The possible macros are of 3 categories, the global,
the mustach core specific and the mustach-json-c example
of implementation specific.
With this extension, using the pattern `{{#X.*}}...{{/X.*}}`
allows to iterate on fields of `X`.
#### Global macros
Example:
- `NO_EXTENSION_FOR_MUSTACH`
- `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on `{"s":{"a":1,"b":true}}` produces ` a:1 b:true`
This macro disables any current or future
extensions for the core or the example.
Here the single star `{{*}}` is replaced by the iterated key
and the single dot `{{.}}` is replaced by its value.
#### Macros for the core mustach engine (mustach.c)
This is a wrap extension implemented in file **mustach-wrap.c**.
- `NO_COLON_EXTENSION_FOR_MUSTACH`
### Error when a requested tag is undefined (Mustach_With_ErrorUndefined)
This macro remove the ability to use colon (:)
as explicit command for variable substitution.
This extension allows to have name starting
with one of the mustach character `:#^/&{=>`
Report the error MUSTACH_ERROR_UNDEFINED_TAG when a requested tag
is not defined.
- `NO_ALLOW_EMPTY_TAG`
This is a wrap extension implemented in file **mustach-wrap.c**.
Generate the error MUSTACH_ERROR_EMPTY_TAG automatically
when an empty tag is encountered.
### Access To Current Value
#### Macros for the implementation example (mustach-json-c.c)
*this was an extension but is now always enforced*
- `NO_EQUAL_VALUE_EXTENSION_FOR_MUSTACH`
The value of the current field can be accessed using single dot.
This macro allows the program to check whether
the actual value is equal to an expected value.
This is useful in `{{#key=val}}` or `{{^key=val}}`
with the corresponding `{{/key=val}}`.
It can also be used in `{{key=val}}` but this
doesn't seem to be useful.
Examples:
- `NO_COMPARE_VALUE_EXTENSION_FOR_MUSTACH`
- `{{#key}}{{.}}{{/key}}` applied to `{"key":3.14}` produces `3.14`
- `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces ` 1 2`.
This macro allows the program to compare the actual
value with an expected value. The comparison operators
are `=`, `>`, `<`, `>=`, `<=`. The meaning of the
comparison numeric or alphabetic depends on the type
of the inspected value. Also the result of the comparison
can be inverted if the value starts with `!`.
Example of use: `{{key>=val}}`, or `{{#key>=val}}` and
`{{^key>=val}}` with their matching `{{/key>=val}}`.
This is a wrap extension implemented in file **mustach-wrap.c**.
- `NO_USE_VALUE_ESCAPE_FIRST_EXTENSION_FOR_MUSTACH`
### Partial Data First
This macro fordids automatic escaping of coparison
sign appearing at first column.
*this was an extension but is now always enforced*
- `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`
The default resolution for partial pattern like `{{> name}}`
is to search for `name` in the current json context and
as a file named `name` or if not found `name.mustache`.
This macro removes the possible use of JSON pointers.
JSON pointers are defined in IETF RFC 6901.
If not set, any key starting with "/" is a JSON pointer.
This implies to use the colon to introduce keys.
So `NO_COLON_EXTENSION_FOR_MUSTACH` implies
`NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`.
A special escaping is used for `=`, `<`, `>` signs when
values comparisons are enabled: `~=` gives `=` in the key.
By default, the order of the search is (1) as a file,
and if not found, (2) in the current json context.
- `NO_OBJECT_ITERATION_FOR_MUSTACH`
When this option is set, the order is reverted and content
of partial is search (1) in the current json context,
and if not found, (2) as a file.
Disable the object iteration extension. That extension allows
to iterate over the keys of an object. The iteration on object
is selected by using the selector `{{#key.*}}`. In the context
of iterating over object keys, the single key `{{*}}` returns the
key and `{{.}}` returns the value.
That option is useful to keep the compatibility with
versions of *mustach* anteriors to 1.2.0.
- `NO_SINGLE_DOT_EXTENSION_FOR_MUSTACH`
This is a wrap extension implemented in file **mustach-wrap.c**.
Disable access to current object value using single dot
like in `{{.}}`.
### Escape First Compare
- `NO_INCLUDE_PARTIAL_FALLBACK`
This extension automatically escapes comparisons appears as
first characters.
This is a wrap extension implemented in file **mustach-wrap.c**.
## Difference with version 0.99 and previous
### Extensions
The extensions can no more be removed at compile time, use
flags to select your required extension on need.
### Name of functions
Names of functions were improved. Old names remain but are obsolete
and legacy. Their removal in far future versions is possible.
The table below summarize the changes.
legacy name | name since version 1.0.0
------------------+-----------------------
fmustach | mustach_file
fdmustach | mustach_fd
mustach | mustach_mem
fmustach_json_c | mustach_json_c_file
fdmustach_json_c | mustach_json_c_fd
mustach_json_c | mustach_json_c_mem
mustach_json_c | mustach_json_c_write
Disable include of file by partial pattern like `{{> name}}`.
By default if a such pattern is found, **mustach** search
for `name` in the current json context. This what is done
historically and when `NO_INCLUDE_PARTIAL_FALLBACK` is defined.
When `NO_INCLUDE_PARTIAL_FALLBACK` is defined, if the value is
found in the json context, the files `name` and `name.mustache`
are searched in that order and the first file found is used
as partial content. The macro `INCLUDE_PARTIAL_EXTENSION` can
be use for changing the extension added.

View File

@ -0,0 +1,239 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mustach.h"
#include "mustach-wrap.h"
#include "mustach-cjson.h"
struct expl {
cJSON null;
cJSON *root;
cJSON *selection;
int depth;
struct {
cJSON *cont;
cJSON *obj;
cJSON *next;
int is_objiter;
} stack[MUSTACH_MAX_DEPTH];
};
static int start(void *closure)
{
struct expl *e = closure;
e->depth = 0;
memset(&e->null, 0, sizeof e->null);
e->null.type = cJSON_NULL;
e->selection = &e->null;
e->stack[0].cont = NULL;
e->stack[0].obj = e->root;
return MUSTACH_OK;
}
static int compare(void *closure, const char *value)
{
struct expl *e = closure;
cJSON *o = e->selection;
double d;
if (cJSON_IsNumber(o)) {
d = o->valuedouble - atof(value);
return d < 0 ? -1 : d > 0 ? 1 : 0;
} else if (cJSON_IsString(o)) {
return strcmp(o->valuestring, value);
} else if (cJSON_IsTrue(o)) {
return strcmp("true", value);
} else if (cJSON_IsFalse(o)) {
return strcmp("false", value);
} else if (cJSON_IsNull(o)) {
return strcmp("null", value);
} else {
return 1;
}
}
static int sel(void *closure, const char *name)
{
struct expl *e = closure;
cJSON *o;
int i, r;
if (name == NULL) {
o = e->stack[e->depth].obj;
r = 1;
} else {
i = e->depth;
while (i >= 0 && !(o = cJSON_GetObjectItemCaseSensitive(e->stack[i].obj, name)))
i--;
if (i >= 0)
r = 1;
else {
o = &e->null;
r = 0;
}
}
e->selection = o;
return r;
}
static int subsel(void *closure, const char *name)
{
struct expl *e = closure;
cJSON *o;
int r;
o = cJSON_GetObjectItemCaseSensitive(e->selection, name);
r = o != NULL;
if (r)
e->selection = o;
return r;
}
static int enter(void *closure, int objiter)
{
struct expl *e = closure;
cJSON *o;
if (++e->depth >= MUSTACH_MAX_DEPTH)
return MUSTACH_ERROR_TOO_DEEP;
o = e->selection;
e->stack[e->depth].is_objiter = 0;
if (objiter) {
if (! cJSON_IsObject(o))
goto not_entering;
if (o->child == NULL)
goto not_entering;
e->stack[e->depth].obj = o->child;
e->stack[e->depth].next = o->child->next;
e->stack[e->depth].cont = o;
e->stack[e->depth].is_objiter = 1;
} else if (cJSON_IsArray(o)) {
if (o->child == NULL)
goto not_entering;
e->stack[e->depth].obj = o->child;
e->stack[e->depth].next = o->child->next;
e->stack[e->depth].cont = o;
} else if ((cJSON_IsObject(o) && o->child == NULL) || (! cJSON_IsFalse(o) && ! cJSON_IsNull(o))) {
e->stack[e->depth].obj = o;
e->stack[e->depth].cont = NULL;
e->stack[e->depth].next = NULL;
} else
goto not_entering;
return 1;
not_entering:
e->depth--;
return 0;
}
static int next(void *closure)
{
struct expl *e = closure;
cJSON *o;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
o = e->stack[e->depth].next;
if (o == NULL)
return 0;
e->stack[e->depth].obj = o;
e->stack[e->depth].next = o->next;
return 1;
}
static int leave(void *closure)
{
struct expl *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
e->depth--;
return 0;
}
static int get(void *closure, struct mustach_sbuf *sbuf, int key)
{
struct expl *e = closure;
const char *s;
if (key) {
s = e->stack[e->depth].is_objiter
? e->stack[e->depth].obj->string
: "";
}
else if (cJSON_IsString(e->selection))
s = e->selection->valuestring;
else if (cJSON_IsNull(e->selection))
s = "";
else {
s = cJSON_PrintUnformatted(e->selection);
if (s == NULL)
return MUSTACH_ERROR_SYSTEM;
sbuf->freecb = cJSON_free;
}
sbuf->value = s;
return 1;
}
const struct mustach_wrap_itf mustach_cJSON_wrap_itf = {
.start = start,
.stop = NULL,
.compare = compare,
.sel = sel,
.subsel = subsel,
.enter = enter,
.next = next,
.leave = leave,
.get = get
};
int mustach_cJSON_file(const char *template, size_t length, cJSON *root, int flags, FILE *file)
{
struct expl e;
e.root = root;
return mustach_wrap_file(template, length, &mustach_cJSON_wrap_itf, &e, flags, file);
}
int mustach_cJSON_fd(const char *template, size_t length, cJSON *root, int flags, int fd)
{
struct expl e;
e.root = root;
return mustach_wrap_fd(template, length, &mustach_cJSON_wrap_itf, &e, flags, fd);
}
int mustach_cJSON_mem(const char *template, size_t length, cJSON *root, int flags, char **result, size_t *size)
{
struct expl e;
e.root = root;
return mustach_wrap_mem(template, length, &mustach_cJSON_wrap_itf, &e, flags, result, size);
}
int mustach_cJSON_write(const char *template, size_t length, cJSON *root, int flags, mustach_write_cb_t *writecb, void *closure)
{
struct expl e;
e.root = root;
return mustach_wrap_write(template, length, &mustach_cJSON_wrap_itf, &e, flags, writecb, closure);
}
int mustach_cJSON_emit(const char *template, size_t length, cJSON *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
{
struct expl e;
e.root = root;
return mustach_wrap_emit(template, length, &mustach_cJSON_wrap_itf, &e, flags, emitcb, closure);
}

View File

@ -0,0 +1,96 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#ifndef _mustach_cJSON_h_included_
#define _mustach_cJSON_h_included_
/*
* mustach-json-c is intended to make integration of cJSON
* library by providing integrated functions.
*/
#include <cjson/cJSON.h>
#include "mustach-wrap.h"
/**
* Wrap interface used internally by mustach cJSON functions.
* Can be used for overriding behaviour.
*/
extern const struct mustach_wrap_itf mustach_cJSON_wrap_itf;
/**
* mustach_cJSON_file - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_cJSON_file(const char *template, size_t length, cJSON *root, int flags, FILE *file);
/**
* mustach_cJSON_fd - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_cJSON_fd(const char *template, size_t length, cJSON *root, int flags, int fd);
/**
* mustach_cJSON_mem - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_cJSON_mem(const char *template, size_t length, cJSON *root, int flags, char **result, size_t *size);
/**
* mustach_cJSON_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_cJSON_write(const char *template, size_t length, cJSON *root, int flags, mustach_write_cb_t *writecb, void *closure);
/**
* mustach_cJSON_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @emitcb: the function that emit values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_cJSON_emit(const char *template, size_t length, cJSON *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
#endif

View File

@ -1,429 +1,251 @@
/*
Copyright (C) 2020 Taler Systems SA
Original license:
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: ISC
*/
#include "platform.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include "mustach.h"
#include "mustach-wrap.h"
#include "mustach-jansson.h"
struct Context
{
/**
* Context object.
*/
json_t *cont;
/**
* Current object.
*/
json_t *obj;
/**
* Opaque object iterator.
*/
void *iter;
/**
* Current index when iterating over an array.
*/
unsigned int index;
/**
* Count when iterating over an array.
*/
unsigned int count;
bool is_objiter;
struct expl {
json_t *root;
json_t *selection;
int depth;
struct {
json_t *cont;
json_t *obj;
void *iter;
int is_objiter;
size_t index, count;
} stack[MUSTACH_MAX_DEPTH];
};
enum Bang
static int start(void *closure)
{
BANG_NONE,
BANG_I18N,
BANG_STRINGIFY,
BANG_AMOUNT_CURRENCY,
BANG_AMOUNT_DECIMAL,
struct expl *e = closure;
e->depth = 0;
e->selection = json_null();
e->stack[0].cont = NULL;
e->stack[0].obj = e->root;
e->stack[0].index = 0;
e->stack[0].count = 1;
return MUSTACH_OK;
}
static int compare(void *closure, const char *value)
{
struct expl *e = closure;
json_t *o = e->selection;
double d;
json_int_t i;
switch (json_typeof(o)) {
case JSON_REAL:
d = json_number_value(o) - atof(value);
return d < 0 ? -1 : d > 0 ? 1 : 0;
case JSON_INTEGER:
i = (json_int_t)json_integer_value(o) - (json_int_t)atoll(value);
return i < 0 ? -1 : i > 0 ? 1 : 0;
case JSON_STRING:
return strcmp(json_string_value(o), value);
case JSON_TRUE:
return strcmp("true", value);
case JSON_FALSE:
return strcmp("false", value);
case JSON_NULL:
return strcmp("null", value);
default:
return 1;
}
}
static int sel(void *closure, const char *name)
{
struct expl *e = closure;
json_t *o;
int i, r;
if (name == NULL) {
o = e->stack[e->depth].obj;
r = 1;
} else {
i = e->depth;
while (i >= 0 && !(o = json_object_get(e->stack[i].obj, name)))
i--;
if (i >= 0)
r = 1;
else {
o = json_null();
r = 0;
}
}
e->selection = o;
return r;
}
static int subsel(void *closure, const char *name)
{
struct expl *e = closure;
json_t *o;
int r;
o = json_object_get(e->selection, name);
r = o != NULL;
if (r)
e->selection = o;
return r;
}
static int enter(void *closure, int objiter)
{
struct expl *e = closure;
json_t *o;
if (++e->depth >= MUSTACH_MAX_DEPTH)
return MUSTACH_ERROR_TOO_DEEP;
o = e->selection;
e->stack[e->depth].is_objiter = 0;
if (objiter) {
if (!json_is_object(o))
goto not_entering;
e->stack[e->depth].iter = json_object_iter(o);
if (e->stack[e->depth].iter == NULL)
goto not_entering;
e->stack[e->depth].obj = json_object_iter_value(e->stack[e->depth].iter);
e->stack[e->depth].cont = o;
e->stack[e->depth].is_objiter = 1;
} else if (json_is_array(o)) {
e->stack[e->depth].count = json_array_size(o);
if (e->stack[e->depth].count == 0)
goto not_entering;
e->stack[e->depth].cont = o;
e->stack[e->depth].obj = json_array_get(o, 0);
e->stack[e->depth].index = 0;
} else if ((json_is_object(o) && json_object_size(0)) || (!json_is_false(o) && !json_is_null(o))) {
e->stack[e->depth].count = 1;
e->stack[e->depth].cont = NULL;
e->stack[e->depth].obj = o;
e->stack[e->depth].index = 0;
} else
goto not_entering;
return 1;
not_entering:
e->depth--;
return 0;
}
static int next(void *closure)
{
struct expl *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
if (e->stack[e->depth].is_objiter) {
e->stack[e->depth].iter = json_object_iter_next(e->stack[e->depth].cont, e->stack[e->depth].iter);
if (e->stack[e->depth].iter == NULL)
return 0;
e->stack[e->depth].obj = json_object_iter_value(e->stack[e->depth].iter);
return 1;
}
e->stack[e->depth].index++;
if (e->stack[e->depth].index >= e->stack[e->depth].count)
return 0;
e->stack[e->depth].obj = json_array_get(e->stack[e->depth].cont, e->stack[e->depth].index);
return 1;
}
static int leave(void *closure)
{
struct expl *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
e->depth--;
return 0;
}
static int get(void *closure, struct mustach_sbuf *sbuf, int key)
{
struct expl *e = closure;
const char *s;
if (key) {
s = e->stack[e->depth].is_objiter
? json_object_iter_key(e->stack[e->depth].iter)
: "";
}
else if (json_is_string(e->selection))
s = json_string_value(e->selection);
else if (json_is_null(e->selection))
s = "";
else {
s = json_dumps(e->selection, JSON_ENCODE_ANY | JSON_COMPACT);
if (s == NULL)
return MUSTACH_ERROR_SYSTEM;
sbuf->freecb = free;
}
sbuf->value = s;
return 1;
}
const struct mustach_wrap_itf mustach_jansson_wrap_itf = {
.start = start,
.stop = NULL,
.compare = compare,
.sel = sel,
.subsel = subsel,
.enter = enter,
.next = next,
.leave = leave,
.get = get
};
struct JanssonClosure
int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file)
{
json_t *root;
mustach_jansson_write_cb writecb;
int depth;
/**
* Did the last find(..) call result in an iterable?
*/
struct Context stack[MUSTACH_MAX_DEPTH];
/**
* The last object we found should be iterated over.
*/
bool found_iter;
/**
* Last bang we found.
*/
enum Bang found_bang;
/**
* Language for i18n lookups.
*/
const char *lang;
};
static json_t *
walk (json_t *obj, const char *path)
{
char *saveptr = NULL;
char *sp = GNUNET_strdup (path);
char *p = sp;
while (true)
{
char *tok = strtok_r (p, ".", &saveptr);
if (tok == NULL)
break;
obj = json_object_get (obj, tok);
if (obj == NULL)
break;
p = NULL;
}
GNUNET_free (sp);
return obj;
struct expl e;
e.root = root;
return mustach_wrap_file(template, length, &mustach_jansson_wrap_itf, &e, flags, file);
}
static json_t *
find (struct JanssonClosure *e, const char *name)
int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd)
{
json_t *obj = NULL;
char *path = GNUNET_strdup (name);
char *bang;
bang = strchr (path, '!');
e->found_bang = BANG_NONE;
if (NULL != bang)
{
*bang = 0;
bang++;
if (0 == strcmp (bang, "i18n"))
e->found_bang = BANG_I18N;
else if (0 == strcmp (bang, "stringify"))
e->found_bang = BANG_STRINGIFY;
else if (0 == strcmp (bang, "amount_decimal"))
e->found_bang = BANG_AMOUNT_CURRENCY;
else if (0 == strcmp (bang, "amount_currency"))
e->found_bang = BANG_AMOUNT_DECIMAL;
}
if (BANG_I18N == e->found_bang && NULL != e->lang)
{
char *aug_path;
GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang);
obj = walk (e->stack[e->depth].obj, aug_path);
GNUNET_free (aug_path);
}
if (NULL == obj)
{
obj = walk (e->stack[e->depth].obj, path);
}
GNUNET_free (path);
return obj;
struct expl e;
e.root = root;
return mustach_wrap_fd(template, length, &mustach_jansson_wrap_itf, &e, flags, fd);
}
static int
start (void *closure)
int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size)
{
struct JanssonClosure *e = closure;
e->depth = 0;
e->stack[0].cont = NULL;
e->stack[0].obj = e->root;
e->stack[0].index = 0;
e->stack[0].count = 1;
e->lang = json_string_value (json_object_get (e->root, "$language"));
return MUSTACH_OK;
struct expl e;
e.root = root;
return mustach_wrap_mem(template, length, &mustach_jansson_wrap_itf, &e, flags, result, size);
}
static int
emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure)
{
struct JanssonClosure *e = closure;
if (! escape)
e->writecb (file, buffer, size);
else
do
{
switch (*buffer)
{
case '<':
e->writecb (file, "&lt;", 4);
break;
case '>':
e->writecb (file, "&gt;", 4);
break;
case '&':
e->writecb (file, "&amp;", 5);
break;
default:
e->writecb (file, buffer, 1);
break;
}
buffer++;
}
while(--size);
return MUSTACH_OK;
struct expl e;
e.root = root;
return mustach_wrap_write(template, length, &mustach_jansson_wrap_itf, &e, flags, writecb, closure);
}
static int
enter (void *closure, const char *name)
int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
{
struct JanssonClosure *e = closure;
json_t *o = find (e, name);
if (++e->depth >= MUSTACH_MAX_DEPTH)
return MUSTACH_ERROR_TOO_DEEP;
if (json_is_object (o))
{
if (e->found_iter)
{
void *iter = json_object_iter (o);
if (NULL == iter)
{
e->depth--;
return 0;
}
e->stack[e->depth].is_objiter = 1;
e->stack[e->depth].iter = iter;
e->stack[e->depth].obj = json_object_iter_value (iter);
e->stack[e->depth].cont = o;
}
else
{
e->stack[e->depth].is_objiter = 0;
e->stack[e->depth].obj = o;
e->stack[e->depth].cont = o;
}
return 1;
}
if (json_is_array (o))
{
unsigned int size = json_array_size (o);
if (size == 0)
{
e->depth--;
return 0;
}
e->stack[e->depth].count = size;
e->stack[e->depth].cont = o;
e->stack[e->depth].obj = json_array_get (o, 0);
e->stack[e->depth].index = 0;
e->stack[e->depth].is_objiter = 0;
return 1;
}
e->depth--;
return 0;
struct expl e;
e.root = root;
return mustach_wrap_emit(template, length, &mustach_jansson_wrap_itf, &e, flags, emitcb, closure);
}
static int
next (void *closure)
{
struct JanssonClosure *e = closure;
struct Context *ctx;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
ctx = &e->stack[e->depth];
if (ctx->is_objiter)
{
ctx->iter = json_object_iter_next (ctx->obj, ctx->iter);
if (NULL == ctx->iter)
return 0;
ctx->obj = json_object_iter_value (ctx->iter);
return 1;
}
ctx->index++;
if (ctx->index >= ctx->count)
return 0;
ctx->obj = json_array_get (ctx->cont, ctx->index);
return 1;
}
static int
leave (void *closure)
{
struct JanssonClosure *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
e->depth--;
return 0;
}
static void
freecb (void *v)
{
free (v);
}
static int
get (void *closure, const char *name, struct mustach_sbuf *sbuf)
{
struct JanssonClosure *e = closure;
json_t *obj;
if ( (0 == strcmp (name, "*") ) &&
(e->stack[e->depth].is_objiter) )
{
sbuf->value = json_object_iter_key (e->stack[e->depth].iter);
return MUSTACH_OK;
}
obj = find (e, name);
if (NULL != obj)
{
switch (e->found_bang)
{
case BANG_I18N:
case BANG_NONE:
{
const char *s = json_string_value (obj);
if (NULL != s)
{
sbuf->value = s;
return MUSTACH_OK;
}
}
break;
case BANG_STRINGIFY:
sbuf->value = json_dumps (obj, JSON_INDENT (2));
sbuf->freecb = freecb;
return MUSTACH_OK;
case BANG_AMOUNT_DECIMAL:
{
char *s;
char *c;
if (! json_is_string (obj))
break;
s = GNUNET_strdup (json_string_value (obj));
c = strchr (s, ':');
if (NULL != c)
*c = 0;
sbuf->value = s;
sbuf->freecb = freecb;
return MUSTACH_OK;
}
break;
case BANG_AMOUNT_CURRENCY:
{
const char *s;
if (! json_is_string (obj))
break;
s = json_string_value (obj);
s = strchr (s, ':');
if (NULL == s)
break;
sbuf->value = s + 1;
return MUSTACH_OK;
}
break;
default:
break;
}
}
sbuf->value = "";
return MUSTACH_OK;
}
static struct mustach_itf itf = {
.start = start,
.put = NULL,
.enter = enter,
.next = next,
.leave = leave,
.partial = NULL,
.get = get,
.emit = NULL,
.stop = NULL
};
static struct mustach_itf itfuw = {
.start = start,
.put = NULL,
.enter = enter,
.next = next,
.leave = leave,
.partial = NULL,
.get = get,
.emit = emituw,
.stop = NULL
};
int
fmustach_jansson (const char *template, json_t *root, FILE *file)
{
struct JanssonClosure e = { 0 };
e.root = root;
return fmustach (template, &itf, &e, file);
}
int
fdmustach_jansson (const char *template, json_t *root, int fd)
{
struct JanssonClosure e = { 0 };
e.root = root;
return fdmustach (template, &itf, &e, fd);
}
int
mustach_jansson (const char *template, json_t *root, char **result,
size_t *size)
{
struct JanssonClosure e = { 0 };
e.root = root;
e.writecb = NULL;
return mustach (template, &itf, &e, result, size);
}
int
umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb
writecb, void *closure)
{
struct JanssonClosure e = { 0 };
e.root = root;
e.writecb = writecb;
return fmustach (template, &itfuw, &e, closure);
}

View File

@ -1,60 +1,60 @@
/*
Copyright (C) 2020 Taler Systems SA
Original license:
Author: José Bollo <jose.bollo@iot.bzh>
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: ISC
*/
#ifndef _mustach_jansson_h_included_
#define _mustach_jansson_h_included_
#include "taler_json_lib.h"
#include "mustach.h"
/*
* mustach-jansson is intended to make integration of jansson
* library by providing integrated functions.
*/
#include <jansson.h>
#include "mustach-wrap.h"
/**
* fmustach_jansson - Renders the mustache 'template' in 'file' for 'root'.
* Wrap interface used internally by mustach jansson functions.
* Can be used for overriding behaviour.
*/
extern const struct mustach_wrap_itf mustach_jansson_wrap_itf;
/**
* mustach_jansson_file - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* \@file: the file where to write the result
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fmustach_jansson (const char *template, json_t *root, FILE *file);
extern int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file);
/**
* fmustach_jansson - Renders the mustache 'template' in 'fd' for 'root'.
* mustach_jansson_fd - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fdmustach_jansson (const char *template, json_t *root, int fd);
extern int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd);
/**
* fmustach_jansson - Renders the mustache 'template' in 'result' for 'root'.
* mustach_jansson_mem - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
@ -62,13 +62,13 @@ extern int fdmustach_jansson (const char *template, json_t *root, int fd);
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_jansson (const char *template, json_t *root, char **result,
size_t *size);
extern int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size);
/**
* umustach_jansson - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
* mustach_jansson_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
@ -76,9 +76,21 @@ extern int mustach_jansson (const char *template, json_t *root, char **result,
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
typedef int (*mustach_jansson_write_cb)(void *closure, const char *buffer,
size_t size);
extern int umustach_jansson (const char *template, json_t *root,
mustach_jansson_write_cb writecb, void *closure);
extern int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure);
/**
* mustach_jansson_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @emitcb: the function that emit values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
#endif

View File

@ -0,0 +1,267 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include "mustach.h"
#include "mustach-wrap.h"
#include "mustach-json-c.h"
struct expl {
struct json_object *root;
struct json_object *selection;
int depth;
struct {
struct json_object *cont;
struct json_object *obj;
struct json_object_iterator iter;
struct json_object_iterator enditer;
int is_objiter;
int index, count;
} stack[MUSTACH_MAX_DEPTH];
};
static int start(void *closure)
{
struct expl *e = closure;
e->depth = 0;
e->selection = NULL;
e->stack[0].cont = NULL;
e->stack[0].obj = e->root;
e->stack[0].index = 0;
e->stack[0].count = 1;
return MUSTACH_OK;
}
static int compare(void *closure, const char *value)
{
struct expl *e = closure;
struct json_object *o = e->selection;
double d;
int64_t i;
switch (json_object_get_type(o)) {
case json_type_double:
d = json_object_get_double(o) - atof(value);
return d < 0 ? -1 : d > 0 ? 1 : 0;
case json_type_int:
i = json_object_get_int64(o) - (int64_t)atoll(value);
return i < 0 ? -1 : i > 0 ? 1 : 0;
default:
return strcmp(json_object_get_string(o), value);
}
}
static int sel(void *closure, const char *name)
{
struct expl *e = closure;
struct json_object *o;
int i, r;
if (name == NULL) {
o = e->stack[e->depth].obj;
r = 1;
} else {
i = e->depth;
while (i >= 0 && !json_object_object_get_ex(e->stack[i].obj, name, &o))
i--;
if (i >= 0)
r = 1;
else {
o = NULL;
r = 0;
}
}
e->selection = o;
return r;
}
static int subsel(void *closure, const char *name)
{
struct expl *e = closure;
struct json_object *o;
int r;
r = json_object_object_get_ex(e->selection, name, &o);
if (r)
e->selection = o;
return r;
}
static int enter(void *closure, int objiter)
{
struct expl *e = closure;
struct json_object *o;
if (++e->depth >= MUSTACH_MAX_DEPTH)
return MUSTACH_ERROR_TOO_DEEP;
o = e->selection;
e->stack[e->depth].is_objiter = 0;
if (objiter) {
if (!json_object_is_type(o, json_type_object))
goto not_entering;
e->stack[e->depth].iter = json_object_iter_begin(o);
e->stack[e->depth].enditer = json_object_iter_end(o);
if (json_object_iter_equal(&e->stack[e->depth].iter, &e->stack[e->depth].enditer))
goto not_entering;
e->stack[e->depth].obj = json_object_iter_peek_value(&e->stack[e->depth].iter);
e->stack[e->depth].cont = o;
e->stack[e->depth].is_objiter = 1;
} else if (json_object_is_type(o, json_type_array)) {
e->stack[e->depth].count = json_object_array_length(o);
if (e->stack[e->depth].count == 0)
goto not_entering;
e->stack[e->depth].cont = o;
e->stack[e->depth].obj = json_object_array_get_idx(o, 0);
e->stack[e->depth].index = 0;
} else if (json_object_is_type(o, json_type_object) || json_object_get_boolean(o)) {
e->stack[e->depth].count = 1;
e->stack[e->depth].cont = NULL;
e->stack[e->depth].obj = o;
e->stack[e->depth].index = 0;
} else
goto not_entering;
return 1;
not_entering:
e->depth--;
return 0;
}
static int next(void *closure)
{
struct expl *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
if (e->stack[e->depth].is_objiter) {
json_object_iter_next(&e->stack[e->depth].iter);
if (json_object_iter_equal(&e->stack[e->depth].iter, &e->stack[e->depth].enditer))
return 0;
e->stack[e->depth].obj = json_object_iter_peek_value(&e->stack[e->depth].iter);
return 1;
}
e->stack[e->depth].index++;
if (e->stack[e->depth].index >= e->stack[e->depth].count)
return 0;
e->stack[e->depth].obj = json_object_array_get_idx(e->stack[e->depth].cont, e->stack[e->depth].index);
return 1;
}
static int leave(void *closure)
{
struct expl *e = closure;
if (e->depth <= 0)
return MUSTACH_ERROR_CLOSING;
e->depth--;
return 0;
}
static int get(void *closure, struct mustach_sbuf *sbuf, int key)
{
struct expl *e = closure;
const char *s;
if (key)
s = e->stack[e->depth].is_objiter
? json_object_iter_peek_name(&e->stack[e->depth].iter)
: "";
else
switch (json_object_get_type(e->selection)) {
case json_type_string:
s = json_object_get_string(e->selection);
break;
case json_type_null:
s = "";
break;
default:
s = json_object_to_json_string_ext(e->selection, 0);
break;
}
sbuf->value = s;
return 1;
}
const struct mustach_wrap_itf mustach_json_c_wrap_itf = {
.start = start,
.stop = NULL,
.compare = compare,
.sel = sel,
.subsel = subsel,
.enter = enter,
.next = next,
.leave = leave,
.get = get
};
int mustach_json_c_file(const char *template, size_t length, struct json_object *root, int flags, FILE *file)
{
struct expl e;
e.root = root;
return mustach_wrap_file(template, length, &mustach_json_c_wrap_itf, &e, flags, file);
}
int mustach_json_c_fd(const char *template, size_t length, struct json_object *root, int flags, int fd)
{
struct expl e;
e.root = root;
return mustach_wrap_fd(template, length, &mustach_json_c_wrap_itf, &e, flags, fd);
}
int mustach_json_c_mem(const char *template, size_t length, struct json_object *root, int flags, char **result, size_t *size)
{
struct expl e;
e.root = root;
return mustach_wrap_mem(template, length, &mustach_json_c_wrap_itf, &e, flags, result, size);
}
int mustach_json_c_write(const char *template, size_t length, struct json_object *root, int flags, mustach_write_cb_t *writecb, void *closure)
{
struct expl e;
e.root = root;
return mustach_wrap_write(template, length, &mustach_json_c_wrap_itf, &e, flags, writecb, closure);
}
int mustach_json_c_emit(const char *template, size_t length, struct json_object *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
{
struct expl e;
e.root = root;
return mustach_wrap_emit(template, length, &mustach_json_c_wrap_itf, &e, flags, emitcb, closure);
}
int fmustach_json_c(const char *template, struct json_object *root, FILE *file)
{
return mustach_json_c_file(template, 0, root, -1, file);
}
int fdmustach_json_c(const char *template, struct json_object *root, int fd)
{
return mustach_json_c_fd(template, 0, root, -1, fd);
}
int mustach_json_c(const char *template, struct json_object *root, char **result, size_t *size)
{
return mustach_json_c_mem(template, 0, root, -1, result, size);
}
int umustach_json_c(const char *template, struct json_object *root, mustach_write_cb_t *writecb, void *closure)
{
return mustach_json_c_write(template, 0, root, -1, writecb, closure);
}

View File

@ -0,0 +1,160 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#ifndef _mustach_json_c_h_included_
#define _mustach_json_c_h_included_
/*
* mustach-json-c is intended to make integration of json-c
* library by providing integrated functions.
*/
#include <json-c/json.h>
#include "mustach-wrap.h"
/**
* Wrap interface used internally by mustach json-c functions.
* Can be used for overriding behaviour.
*/
extern const struct mustach_wrap_itf mustach_json_c_wrap_itf;
/**
* mustach_json_c_file - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_json_c_file(const char *template, size_t length, struct json_object *root, int flags, FILE *file);
/**
* mustach_json_c_fd - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_json_c_fd(const char *template, size_t length, struct json_object *root, int flags, int fd);
/**
* mustach_json_c_mem - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_json_c_mem(const char *template, size_t length, struct json_object *root, int flags, char **result, size_t *size);
/**
* mustach_json_c_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_json_c_write(const char *template, size_t length, struct json_object *root, int flags, mustach_write_cb_t *writecb, void *closure);
/**
* mustach_json_c_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @emitcb: the function that emit values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_json_c_emit(const char *template, size_t length, struct json_object *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
/***************************************************************************
* compatibility with version before 1.0
*/
/**
* OBSOLETE use mustach_json_c_file
*
* fmustach_json_c - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
* @root: the root json object to render
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int fmustach_json_c(const char *template, struct json_object *root, FILE *file));
/**
* OBSOLETE use mustach_json_c_fd
*
* fdmustach_json_c - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int fdmustach_json_c(const char *template, struct json_object *root, int fd));
/**
* OBSOLETE use mustach_json_c_mem
*
* mustach_json_c - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int mustach_json_c(const char *template, struct json_object *root, char **result, size_t *size));
/**
* OBSOLETE use mustach_json_c_write
*
* umustach_json_c - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
typedef mustach_write_cb_t *mustach_json_write_cb;
DEPRECATED_MUSTACH(extern int umustach_json_c(const char *template, struct json_object *root, mustach_write_cb_t *writecb, void *closure));
#endif

View File

@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@ -27,7 +16,7 @@
#include <string.h>
#include <libgen.h>
#include "mustach-json-c.h"
#include "mustach-wrap.h"
static const size_t BLOCKSIZE = 8192;
@ -43,16 +32,39 @@ static const char *errors[] = {
"bad unescape tag",
"invalid interface",
"item not found",
"partial not found"
"partial not found",
"undefined tag"
};
static const char *errmsg = 0;
static int flags = 0;
static FILE *output = 0;
static void help(char *prog)
{
printf("usage: %s json-file mustach-templates...\n", basename(prog));
char *name = basename(prog);
#define STR_INDIR(x) #x
#define STR(x) STR_INDIR(x)
printf("%s version %s\n", name, STR(VERSION));
#undef STR
#undef STR_INDIR
printf(
"\n"
"USAGE:\n"
" %s [FLAGS] <json-file> <mustach-templates...>\n"
"\n"
"FLAGS:\n"
" -h, --help Prints help information\n"
" -s, --strict Error when a tag is undefined\n"
"\n"
"ARGS: (if a file is -, read standard input)\n"
" <json-file> JSON file with input data\n"
" <mustach-templates...> Template files to instantiate\n",
name);
exit(0);
}
static char *readfile(const char *filename)
static char *readfile(const char *filename, size_t *length)
{
int f;
struct stat s;
@ -106,50 +118,140 @@ static char *readfile(const char *filename)
} while(rc > 0);
close(f);
if (length != NULL)
*length = pos;
result[pos] = 0;
return result;
}
static int load_json(const char *filename);
static int process(const char *content, size_t length);
static void close_json();
int main(int ac, char **av)
{
struct json_object *o;
char *t;
char *t, *f;
char *prog = *av;
int s;
size_t length;
(void)ac; /* unused */
flags = Mustach_With_AllExtensions;
output = stdout;
if (*++av) {
for( ++av ; av[0] && av[0][0] == '-' && av[0][1] != 0 ; av++) {
if (!strcmp(*av, "-h") || !strcmp(*av, "--help"))
help(prog);
if (av[0][0] == '-' && !av[0][1])
o = json_object_from_fd(0);
else
o = json_object_from_file(av[0]);
#if JSON_C_VERSION_NUM >= 0x000D00
if (json_util_get_last_err() != NULL) {
fprintf(stderr, "Bad json: %s (file %s)\n", json_util_get_last_err(), av[0]);
exit(1);
}
else
#endif
if (o == NULL) {
fprintf(stderr, "Aborted: null json (file %s)\n", av[0]);
if (!strcmp(*av, "-s") || !strcmp(*av, "--strict"))
flags |= Mustach_With_ErrorUndefined;
}
if (*av) {
f = (av[0][0] == '-' && !av[0][1]) ? "/dev/stdin" : av[0];
s = load_json(f);
if (s < 0) {
fprintf(stderr, "Can't load json file %s\n", av[0]);
if(errmsg)
fprintf(stderr, " reason: %s\n", errmsg);
exit(1);
}
while(*++av) {
t = readfile(*av);
s = fmustach_json_c(t, o, stdout);
if (s != 0) {
t = readfile(*av, &length);
s = process(t, length);
free(t);
if (s != MUSTACH_OK) {
s = -s;
if (s < 1 || s >= (int)(sizeof errors / sizeof * errors))
s = 0;
fprintf(stderr, "Template error %s (file %s)\n", errors[s], *av);
}
free(t);
}
json_object_put(o);
close_json();
}
return 0;
}
#define MUSTACH_TOOL_JSON_C 1
#define MUSTACH_TOOL_JANSSON 2
#define MUSTACH_TOOL_CJSON 3
#define TOOL MUSTACH_TOOL_JANSSON
#if TOOL == MUSTACH_TOOL_JSON_C
#include "mustach-json-c.h"
static struct json_object *o;
static int load_json(const char *filename)
{
o = json_object_from_file(filename);
#if JSON_C_VERSION_NUM >= 0x000D00
errmsg = json_util_get_last_err();
if (errmsg != NULL)
return -1;
#endif
if (o == NULL) {
errmsg = "null json";
return -1;
}
return 0;
}
static int process(const char *content, size_t length)
{
return mustach_json_c_file(content, length, o, flags, output);
}
static void close_json()
{
json_object_put(o);
}
#elif TOOL == MUSTACH_TOOL_JANSSON
#include "mustach-jansson.h"
static json_t *o;
static json_error_t e;
static int load_json(const char *filename)
{
o = json_load_file(filename, JSON_DECODE_ANY, &e);
if (o == NULL) {
errmsg = e.text;
return -1;
}
return 0;
}
static int process(const char *content, size_t length)
{
return mustach_jansson_file(content, length, o, flags, output);
}
static void close_json()
{
json_decref(o);
}
#elif TOOL == MUSTACH_TOOL_CJSON
#include "mustach-cjson.h"
static cJSON *o;
static int load_json(const char *filename)
{
char *t;
size_t length;
t = readfile(filename, &length);
o = t ? cJSON_ParseWithLength(t, length) : NULL;
free(t);
return -!o;
}
static int process(const char *content, size_t length)
{
return mustach_cJSON_file(content, length, o, flags, output);
}
static void close_json()
{
cJSON_Delete(o);
}
#else
#error "no defined json library"
#endif

View File

@ -0,0 +1,456 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <malloc.h>
#endif
#include "mustach.h"
#include "mustach-wrap.h"
#if !defined(INCLUDE_PARTIAL_EXTENSION)
# define INCLUDE_PARTIAL_EXTENSION ".mustache"
#endif
/* global hook for partials */
int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
/* internal structure for wrapping */
struct wrap {
/* original interface */
const struct mustach_wrap_itf *itf;
/* original closure */
void *closure;
/* flags */
int flags;
/* emiter callback */
mustach_emit_cb_t *emitcb;
/* write callback */
mustach_write_cb_t *writecb;
};
/* length given by masking with 3 */
enum comp {
C_no = 0,
C_eq = 1,
C_lt = 5,
C_le = 6,
C_gt = 9,
C_ge = 10
};
enum sel {
S_none = 0,
S_ok = 1,
S_objiter = 2,
S_ok_or_objiter = S_ok | S_objiter
};
static enum comp getcomp(char *head, int sflags)
{
return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
: (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
: (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
: C_no;
}
static char *keyval(char *head, int sflags, enum comp *comp)
{
char *w, car, escaped;
enum comp k;
k = C_no;
w = head;
car = *head;
escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
if (escaped)
escaped = 0;
else
escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
&& (getcomp(head + 1, sflags) != C_no);
if (!escaped)
*w++ = car;
head++;
car = *head;
}
*w = 0;
*comp = k;
return k == C_no ? NULL : &head[k & 3];
}
static char *getkey(char **head, int sflags)
{
char *result, *iter, *write, car;
car = *(iter = *head);
if (!car)
result = NULL;
else {
result = write = iter;
if (sflags & Mustach_With_JsonPointer)
{
while (car && car != '/') {
if (car == '~')
switch (iter[1]) {
case '1': car = '/'; /*@fallthrough@*/
case '0': iter++;
}
*write++ = car;
car = *++iter;
}
*write = 0;
while (car == '/')
car = *++iter;
}
else
{
while (car && car != '.') {
if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
car = *++iter;
*write++ = car;
car = *++iter;
}
*write = 0;
while (car == '.')
car = *++iter;
}
*head = iter;
}
return result;
}
static enum sel sel(struct wrap *w, const char *name)
{
enum sel result;
int i, j, sflags, scmp;
char *key, *value;
enum comp k;
/* make a local writeable copy */
size_t lenname = 1 + strlen(name);
char buffer[lenname];
char *copy = buffer;
memcpy(copy, name, lenname);
/* check if matches json pointer selection */
sflags = w->flags;
if (sflags & Mustach_With_JsonPointer) {
if (copy[0] == '/')
copy++;
else
sflags ^= Mustach_With_JsonPointer;
}
/* extract the value, translate the key and get the comparator */
if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
value = keyval(copy, sflags, &k);
else {
k = C_no;
value = NULL;
}
/* case of . alone if Mustach_With_SingleDot? */
if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
/* yes, select current */
result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
else
{
/* not the single dot, extract the first key */
key = getkey(&copy, sflags);
if (key == NULL)
return 0;
/* select the root item */
if (w->itf->sel(w->closure, key))
result = S_ok;
else if (key[0] == '*'
&& !key[1]
&& !value
&& !*copy
&& (w->flags & Mustach_With_ObjectIter)
&& w->itf->sel(w->closure, NULL))
result = S_ok_or_objiter;
else
result = S_none;
if (result == S_ok) {
/* iterate the selection of sub items */
key = getkey(&copy, sflags);
while(result == S_ok && key) {
if (w->itf->subsel(w->closure, key))
/* nothing */;
else if (key[0] == '*'
&& !key[1]
&& !value
&& !*copy
&& (w->flags & Mustach_With_ObjectIter))
result = S_objiter;
else
result = S_none;
key = getkey(&copy, sflags);
}
}
}
/* should it be compared? */
if (result == S_ok && value) {
if (!w->itf->compare)
result = S_none;
else {
i = value[0] == '!';
scmp = w->itf->compare(w->closure, &value[i]);
switch (k) {
case C_eq: j = scmp == 0; break;
case C_lt: j = scmp < 0; break;
case C_le: j = scmp <= 0; break;
case C_gt: j = scmp > 0; break;
case C_ge: j = scmp >= 0; break;
default: j = i; break;
}
if (i == j)
result = S_none;
}
}
return result;
}
static int start(void *closure)
{
struct wrap *w = closure;
return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
}
static void stop(void *closure, int status)
{
struct wrap *w = closure;
if (w->itf->stop)
w->itf->stop(w->closure, status);
}
static int write(struct wrap *w, const char *buffer, size_t size, FILE *file)
{
int r;
if (w->writecb)
r = w->writecb(file, buffer, size);
else
r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
return r;
}
static int emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
{
struct wrap *w = closure;
int r;
size_t s, i;
char car;
if (w->emitcb)
r = w->emitcb(file, buffer, size, escape);
else if (!escape)
r = write(w, buffer, size, file);
else {
i = 0;
r = MUSTACH_OK;
while(i < size && r == MUSTACH_OK) {
s = i;
while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
i++;
if (i != s)
r = write(w, &buffer[s], i - s, file);
if (i < size && r == MUSTACH_OK) {
switch(car) {
case '<': r = write(w, "&lt;", 4, file); break;
case '>': r = write(w, "&gt;", 4, file); break;
case '&': r = write(w, "&amp;", 5, file); break;
case '"': r = write(w, "&quot;", 6, file); break;
}
i++;
}
}
}
return r;
}
static int enter(void *closure, const char *name)
{
struct wrap *w = closure;
enum sel s = sel(w, name);
return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
}
static int next(void *closure)
{
struct wrap *w = closure;
return w->itf->next(w->closure);
}
static int leave(void *closure)
{
struct wrap *w = closure;
return w->itf->leave(w->closure);
}
static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
{
enum sel s = sel(w, name);
if (!(s & S_ok))
return 0;
return w->itf->get(w->closure, sbuf, s & S_objiter);
}
static int get(void *closure, const char *name, struct mustach_sbuf *sbuf)
{
struct wrap *w = closure;
if (getoptional(w, name, sbuf) <= 0) {
if (w->flags & Mustach_With_ErrorUndefined)
return MUSTACH_ERROR_UNDEFINED_TAG;
sbuf->value = "";
}
return MUSTACH_OK;
}
static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
{
static char extension[] = INCLUDE_PARTIAL_EXTENSION;
size_t s;
long pos;
FILE *file;
char *path, *buffer;
/* allocate path */
s = strlen(name);
path = malloc(s + sizeof extension);
if (path == NULL)
return MUSTACH_ERROR_SYSTEM;
/* try without extension first */
memcpy(path, name, s + 1);
file = fopen(path, "r");
if (file == NULL) {
memcpy(&path[s], extension, sizeof extension);
file = fopen(path, "r");
}
free(path);
/* if file opened */
if (file == NULL)
return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
/* compute file size */
if (fseek(file, 0, SEEK_END) >= 0
&& (pos = ftell(file)) >= 0
&& fseek(file, 0, SEEK_SET) >= 0) {
/* allocate value */
s = (size_t)pos;
buffer = malloc(s + 1);
if (buffer != NULL) {
/* read value */
if (1 == fread(buffer, s, 1, file)) {
/* force zero at end */
sbuf->value = buffer;
buffer[s] = 0;
sbuf->freecb = free;
fclose(file);
return MUSTACH_OK;
}
free(buffer);
}
}
fclose(file);
return MUSTACH_ERROR_SYSTEM;
}
static int partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
{
struct wrap *w = closure;
int rc;
if (mustach_wrap_get_partial != NULL)
rc = mustach_wrap_get_partial(name, sbuf);
else if (w->flags & Mustach_With_PartialDataFirst) {
if (getoptional(w, name, sbuf) > 0)
rc = MUSTACH_OK;
else
rc = get_partial_from_file(name, sbuf);
}
else {
rc = get_partial_from_file(name, sbuf);
if (rc != MUSTACH_OK && getoptional(w, name, sbuf) > 0)
rc = MUSTACH_OK;
}
if (rc != MUSTACH_OK)
sbuf->value = "";
return MUSTACH_OK;
}
const struct mustach_itf mustach_wrap_itf = {
.start = start,
.put = NULL,
.enter = enter,
.next = next,
.leave = leave,
.partial = partial,
.get = get,
.emit = emit,
.stop = stop
};
static void wrap_init(struct wrap *wrap, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, mustach_write_cb_t *writecb)
{
if (flags & Mustach_With_Compare)
flags |= Mustach_With_Equal;
wrap->closure = closure;
wrap->itf = itf;
wrap->flags = flags;
wrap->emitcb = emitcb;
wrap->writecb = writecb;
}
int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
{
struct wrap w;
wrap_init(&w, itf, closure, flags, NULL, NULL);
return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
}
int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
{
struct wrap w;
wrap_init(&w, itf, closure, flags, NULL, NULL);
return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
}
int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size)
{
struct wrap w;
wrap_init(&w, itf, closure, flags, NULL, NULL);
return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
}
int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure)
{
struct wrap w;
wrap_init(&w, itf, closure, flags, NULL, writecb);
return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
}
int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure)
{
struct wrap w;
wrap_init(&w, itf, closure, flags, emitcb, NULL);
return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
}

View File

@ -0,0 +1,234 @@
/*
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
SPDX-License-Identifier: ISC
*/
#ifndef _mustach_wrap_h_included_
#define _mustach_wrap_h_included_
/*
* mustach-wrap is intended to make integration of JSON
* libraries easier by wrapping mustach extensions in a
* single place.
*
* As before, using mustach and only mustach is possible
* (by using only mustach.h) but does not implement high
* level features coming with extensions implemented by
* this high level wrapper.
*/
#include "mustach.h"
/*
* Definition of the writing callbacks for mustach functions
* producing output to callbacks.
*
* Two callback types are defined:
*
* @mustach_write_cb_t:
*
* callback receiving the escaped data to be written as 3 parameters:
*
* 1. the 'closure', the same given to the wmustach_... function
* 2. a pointer to a 'buffer' containing the characters to be written
* 3. the size in bytes of the data pointed by 'buffer'
*
* @mustach_emit_cb_t:
*
* callback receiving the data to be written and a flag indicating
* if escaping should be done or not as 4 parameters:
*
* 1. the 'closure', the same given to the emustach_... function
* 2. a pointer to a 'buffer' containing the characters to be written
* 3. the size in bytes of the data pointed by 'buffer'
* 4. a boolean indicating if 'escape' should be done
*/
#ifndef _mustach_output_callbacks_defined_
#define _mustach_output_callbacks_defined_
typedef int mustach_write_cb_t(void *closure, const char *buffer, size_t size);
typedef int mustach_emit_cb_t(void *closure, const char *buffer, size_t size, int escape);
#endif
/**
* Flags specific to mustach wrap
*/
#define Mustach_With_SingleDot 4 /* obsolete, always set */
#define Mustach_With_Equal 8
#define Mustach_With_Compare 16
#define Mustach_With_JsonPointer 32
#define Mustach_With_ObjectIter 64
#define Mustach_With_IncPartial 128 /* obsolete, always set */
#define Mustach_With_EscFirstCmp 256
#define Mustach_With_PartialDataFirst 512
#define Mustach_With_ErrorUndefined 1024
#undef Mustach_With_AllExtensions
#define Mustach_With_AllExtensions 1023 /* don't include ErrorUndefined */
/**
* mustach_wrap_itf - high level wrap of mustach - interface for callbacks
*
* The functions sel, subsel, enter and next should return 0 or 1.
*
* All other functions should normally return MUSTACH_OK (zero).
*
* If any function returns a negative value, it means an error that
* stop the processing and that is reported to the caller. Mustach
* also has its own error codes. Using the macros MUSTACH_ERROR_USER
* and MUSTACH_IS_ERROR_USER could help to avoid clashes.
*
* @start: If defined (can be NULL), starts the mustach processing
* of the closure, called at the very beginning before any
* mustach processing occurs.
*
* @stop: If defined (can be NULL), stops the mustach processing
* of the closure, called at the very end after all mustach
* processing occurered. The status returned by the processing
* is passed to the stop.
*
* @compare: If defined (can be NULL), compares the value of the
* currently selected item with the given value and returns
* a negative value if current value is lesser, a positive
* value if the current value is greater or zero when
* values are equals.
* If 'compare' is NULL, any comparison in mustach
* is going to fails.
*
* @sel: Selects the item of the given 'name'. If 'name' is NULL
* Selects the current item. Returns 1 if the selection is
* effective or else 0 if the selection failed.
*
* @subsel: Selects from the currently selected object the value of
* the field of given name. Returns 1 if the selection is
* effective or else 0 if the selection failed.
*
* @enter: Enters the section of 'name' if possible.
* Musts return 1 if entered or 0 if not entered.
* When 1 is returned, the function 'leave' will always be called.
* Conversely 'leave' is never called when enter returns 0 or
* a negative value.
* When 1 is returned, the function must activate the first
* item of the section.
*
* @next: Activates the next item of the section if it exists.
* Musts return 1 when the next item is activated.
* Musts return 0 when there is no item to activate.
*
* @leave: Leaves the last entered section
*
* @get: Returns in 'sbuf' the value of the current selection if 'key'
* is zero. Otherwise, when 'key' is not zero, return in 'sbuf'
* the name of key of the current selection, or if no such key
* exists, the empty string. Must return 1 if possible or
* 0 when not possible or an error code.
*/
struct mustach_wrap_itf {
int (*start)(void *closure);
void (*stop)(void *closure, int status);
int (*compare)(void *closure, const char *value);
int (*sel)(void *closure, const char *name);
int (*subsel)(void *closure, const char *name);
int (*enter)(void *closure, int objiter);
int (*next)(void *closure);
int (*leave)(void *closure);
int (*get)(void *closure, struct mustach_sbuf *sbuf, int key);
};
/**
* Mustach interface used internally by mustach wrapper functions.
* Can be used for overriding behaviour.
*/
extern const struct mustach_itf mustach_wrap_itf;
/**
* Global hook for providing partials. When set to a not NULL value, the pointed
* function replaces the default behaviour and is called to provide the partial
* of the given 'name' in 'sbuf'.
* The function must return MUSTACH_OK when it filled 'sbuf' with value of partial
* or must return an error code if it failed.
*/
extern int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf);
/**
* mustach_wrap_file - Renders the mustache 'template' in 'file' for an abstract
* wrapper of interface 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface of the abstract wrapper
* @closure: the closure of the abstract wrapper
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file);
/**
* mustach_wrap_fd - Renders the mustache 'template' in 'fd' for an abstract
* wrapper of interface 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface of the abstract wrapper
* @closure: the closure of the abstract wrapper
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd);
/**
* mustach_wrap_mem - Renders the mustache 'template' in 'result' for an abstract
* wrapper of interface 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface of the abstract wrapper
* @closure: the closure of the abstract wrapper
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size);
/**
* mustach_wrap_write - Renders the mustache 'template' for an abstract
* wrapper of interface 'itf' and 'closure' to custom writer
* 'writecb' with 'writeclosure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface of the abstract wrapper
* @closure: the closure of the abstract wrapper
* @writecb: the function that write values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure);
/**
* mustach_wrap_emit - Renders the mustache 'template' for an abstract
* wrapper of interface 'itf' and 'closure' to custom emiter 'emitcb'
* with 'emitclosure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface of the abstract wrapper
* @closure: the closure of the abstract wrapper
* @emitcb: the function that emit values
* @closure: the closure for the write function
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure);
#endif

View File

@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@ -27,19 +16,9 @@
#ifdef _WIN32
#include <malloc.h>
#endif
#ifdef __sun
# include <alloca.h>
#endif
#include "mustach.h"
#if defined(NO_EXTENSION_FOR_MUSTACH)
# undef NO_COLON_EXTENSION_FOR_MUSTACH
# define NO_COLON_EXTENSION_FOR_MUSTACH
# undef NO_ALLOW_EMPTY_TAG
# define NO_ALLOW_EMPTY_TAG
#endif
struct iwrap {
int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
void *closure; /* closure for: enter, next, leave, emit, get */
@ -51,6 +30,13 @@ struct iwrap {
int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
void *closure_partial; /* closure for partial */
int flags;
};
struct prefix {
size_t len;
const char *start;
struct prefix *prefix;
};
#if !defined(NO_OPEN_MEMSTREAM)
@ -135,6 +121,7 @@ static inline void sbuf_reset(struct mustach_sbuf *sbuf)
sbuf->value = NULL;
sbuf->freecb = NULL;
sbuf->closure = NULL;
sbuf->length = 0;
}
static inline void sbuf_release(struct mustach_sbuf *sbuf)
@ -143,38 +130,47 @@ static inline void sbuf_release(struct mustach_sbuf *sbuf)
sbuf->releasecb(sbuf->value, sbuf->closure);
}
static inline size_t sbuf_length(struct mustach_sbuf *sbuf)
{
size_t length = sbuf->length;
if (length == 0 && sbuf->value != NULL)
length = strlen(sbuf->value);
return length;
}
static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
{
size_t i, j;
size_t i, j, r;
(void)closure; /* unused */
if (!escape)
return fwrite(buffer, size, 1, file) != 1 ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
return fwrite(buffer, 1, size, file) != size ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
i = 0;
r = i = 0;
while (i < size) {
j = i;
while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
j++;
if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
if (j < size) {
switch(buffer[j++]) {
case '<':
if (fwrite("&lt;", 4, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
r = fwrite("&lt;", 4, 1, file);
break;
case '>':
if (fwrite("&gt;", 4, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
r = fwrite("&gt;", 4, 1, file);
break;
case '&':
if (fwrite("&amp;", 5, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
r = fwrite("&amp;", 5, 1, file);
break;
case '"':
r = fwrite("&quot;", 6, 1, file);
break;
default: break;
}
if (r != 1)
return MUSTACH_ERROR_SYSTEM;
}
i = j;
}
@ -191,7 +187,7 @@ static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
sbuf_reset(&sbuf);
rc = iwrap->get(iwrap->closure, name, &sbuf);
if (rc >= 0) {
length = strlen(sbuf.value);
length = sbuf_length(&sbuf);
if (length)
rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
sbuf_release(&sbuf);
@ -220,55 +216,109 @@ static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *s
if (rc == 0) {
sbuf->value = result;
sbuf->freecb = free;
sbuf->length = size;
}
}
}
return rc;
}
static int process(const char *template, struct iwrap *iwrap, FILE *file, const char *opstr, const char *clstr)
static int emitprefix(struct iwrap *iwrap, FILE *file, struct prefix *prefix)
{
if (prefix->prefix) {
int rc = emitprefix(iwrap, file, prefix->prefix);
if (rc < 0)
return rc;
}
return prefix->len ? iwrap->emit(iwrap->closure, prefix->start, prefix->len, 0, file) : 0;
}
static int process(const char *template, size_t length, struct iwrap *iwrap, FILE *file, struct prefix *prefix)
{
struct mustach_sbuf sbuf;
char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
const char *beg, *term;
struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
char name[MUSTACH_MAX_LENGTH + 1], c;
const char *beg, *term, *end;
struct { const char *name, *again; size_t length; unsigned enabled: 1, entered: 1; } stack[MUSTACH_MAX_DEPTH];
size_t oplen, cllen, len, l;
int depth, rc, enabled;
int depth, rc, enabled, stdalone;
struct prefix pref;
enabled = 1;
oplen = strlen(opstr);
cllen = strlen(clstr);
depth = 0;
for(;;) {
beg = strstr(template, opstr);
if (beg == NULL) {
/* no more mustach */
if (enabled && template[0]) {
rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
if (rc < 0)
return rc;
pref.prefix = prefix;
end = template + (length ? length : strlen(template));
opstr[0] = opstr[1] = '{';
clstr[0] = clstr[1] = '}';
oplen = cllen = 2;
stdalone = enabled = 1;
depth = pref.len = 0;
for (;;) {
/* search next openning delimiter */
for (beg = template ; ; beg++) {
c = beg == end ? '\n' : *beg;
if (c == '\n') {
l = (beg != end) + (size_t)(beg - template);
if (stdalone != 2 && enabled) {
if (beg != template /* don't prefix empty lines */) {
rc = emitprefix(iwrap, file, &pref);
if (rc < 0)
return rc;
}
rc = iwrap->emit(iwrap->closure, template, l, 0, file);
if (rc < 0)
return rc;
}
if (beg == end) /* no more mustach */
return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
template += l;
stdalone = 1;
pref.len = 0;
}
else if (!isspace(c)) {
if (stdalone == 2 && enabled) {
rc = emitprefix(iwrap, file, &pref);
if (rc < 0)
return rc;
pref.len = 0;
stdalone = 0;
}
if (c == *opstr && end - beg >= (ssize_t)oplen) {
for (l = 1 ; l < oplen && beg[l] == opstr[l] ; l++);
if (l == oplen)
break;
}
stdalone = 0;
}
return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
}
if (enabled && beg != template) {
rc = iwrap->emit(iwrap->closure, template, (size_t)(beg - template), 0, file);
if (rc < 0)
return rc;
}
pref.start = template;
pref.len = enabled ? (size_t)(beg - template) : 0;
beg += oplen;
term = strstr(beg, clstr);
if (term == NULL)
return MUSTACH_ERROR_UNEXPECTED_END;
/* search next closing delimiter */
for (term = beg ; ; term++) {
if (term == end)
return MUSTACH_ERROR_UNEXPECTED_END;
if (*term == *clstr && end - term >= (ssize_t)cllen) {
for (l = 1 ; l < cllen && term[l] == clstr[l] ; l++);
if (l == cllen)
break;
}
}
template = term + cllen;
len = (size_t)(term - beg);
c = *beg;
switch(c) {
case ':':
stdalone = 0;
if (iwrap->flags & Mustach_With_Colon)
goto exclude_first;
goto get_name;
case '!':
case '=':
break;
case '{':
for (l = 0 ; clstr[l] == '}' ; l++);
if (clstr[l]) {
for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
if (l < cllen) {
if (!len || beg[len-1] != '}')
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
len--;
@ -279,55 +329,63 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
c = '&';
/*@fallthrough@*/
case '&':
stdalone = 0;
/*@fallthrough@*/
case '^':
case '#':
case '/':
case '&':
case '>':
#if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
case ':':
#endif
beg++; len--;
exclude_first:
beg++;
len--;
goto get_name;
default:
stdalone = 0;
get_name:
while (len && isspace(beg[0])) { beg++; len--; }
while (len && isspace(beg[len-1])) len--;
#if !defined(NO_ALLOW_EMPTY_TAG)
if (len == 0)
if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
return MUSTACH_ERROR_EMPTY_TAG;
#endif
if (len > MUSTACH_MAX_LENGTH)
return MUSTACH_ERROR_TAG_TOO_LONG;
memcpy(name, beg, len);
name[len] = 0;
break;
}
if (stdalone)
stdalone = 2;
else if (enabled) {
rc = emitprefix(iwrap, file, &pref);
if (rc < 0)
return rc;
pref.len = 0;
}
switch(c) {
case '!':
/* comment */
/* nothing to do */
break;
case '=':
/* defines separators */
/* defines delimiters */
if (len < 5 || beg[len - 1] != '=')
return MUSTACH_ERROR_BAD_SEPARATORS;
beg++;
len -= 2;
while (len && isspace(*beg))
beg++, len--;
while (len && isspace(beg[len - 1]))
len--;
for (l = 0; l < len && !isspace(beg[l]) ; l++);
if (l == len)
if (l == len || l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
oplen = l;
tmp = alloca(oplen + 1);
memcpy(tmp, beg, oplen);
tmp[oplen] = 0;
opstr = tmp;
memcpy(opstr, beg, l);
while (l < len && isspace(beg[l])) l++;
if (l == len)
if (l == len || len - l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
cllen = len - l;
tmp = alloca(cllen + 1);
memcpy(tmp, beg + l, cllen);
tmp[cllen] = 0;
clstr = tmp;
memcpy(clstr, beg + l, cllen);
break;
case '^':
case '#':
@ -343,8 +401,8 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
stack[depth].name = beg;
stack[depth].again = template;
stack[depth].length = len;
stack[depth].enabled = enabled;
stack[depth].entered = rc;
stack[depth].enabled = enabled != 0;
stack[depth].entered = rc != 0;
if ((c == '#') == (rc == 0))
enabled = 0;
depth++;
@ -370,7 +428,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
sbuf_reset(&sbuf);
rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
if (rc >= 0) {
rc = process(sbuf.value, iwrap, file, opstr, clstr);
rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, file, &pref);
sbuf_release(&sbuf);
}
if (rc < 0)
@ -389,7 +447,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
}
int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file)
{
int rc;
struct iwrap iwrap;
@ -422,17 +480,18 @@ int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE
iwrap.next = itf->next;
iwrap.leave = itf->leave;
iwrap.get = itf->get;
iwrap.flags = flags;
/* process */
rc = itf->start ? itf->start(closure) : 0;
if (rc == 0)
rc = process(template, &iwrap, file, "{{", "}}");
rc = process(template, length, &iwrap, file, 0);
if (itf->stop)
itf->stop(closure, rc);
return rc;
}
int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd)
{
int rc;
FILE *file;
@ -442,13 +501,13 @@ int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int
rc = MUSTACH_ERROR_SYSTEM;
errno = ENOMEM;
} else {
rc = fmustach(template, itf, closure, file);
rc = mustach_file(template, length, itf, closure, flags, file);
fclose(file);
}
return rc;
}
int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size)
{
int rc;
FILE *file;
@ -461,7 +520,7 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
if (file == NULL)
rc = MUSTACH_ERROR_SYSTEM;
else {
rc = fmustach(template, itf, closure, file);
rc = mustach_file(template, length, itf, closure, flags, file);
if (rc < 0)
memfile_abort(file, result, size);
else
@ -470,3 +529,18 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
return rc;
}
int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file)
{
return mustach_file(template, 0, itf, closure, Mustach_With_AllExtensions, file);
}
int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd)
{
return mustach_fd(template, 0, itf, closure, Mustach_With_AllExtensions, fd);
}
int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size)
{
return mustach_mem(template, 0, itf, closure, Mustach_With_AllExtensions, result, size);
}

View File

@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: ISC
*/
#ifndef _mustach_h_included_
@ -25,7 +14,7 @@ struct mustach_sbuf; /* see below */
/**
* Current version of mustach and its derivates
*/
#define MUSTACH_VERSION 99
#define MUSTACH_VERSION 102
#define MUSTACH_VERSION_MAJOR (MUSTACH_VERSION / 100)
#define MUSTACH_VERSION_MINOR (MUSTACH_VERSION % 100)
@ -37,20 +26,59 @@ struct mustach_sbuf; /* see below */
/**
* Maximum length of tags in mustaches {{...}}
*/
#define MUSTACH_MAX_LENGTH 1024
#define MUSTACH_MAX_LENGTH 4096
/**
* mustach_itf - interface for callbacks
* Maximum length of delimitors (2 normally but extended here)
*/
#define MUSTACH_MAX_DELIM_LENGTH 8
/**
* Flags specific to mustach core
*/
#define Mustach_With_NoExtensions 0
#define Mustach_With_Colon 1
#define Mustach_With_EmptyTag 2
#define Mustach_With_AllExtensions 3
/*
* Definition of error codes returned by mustach
*/
#define MUSTACH_OK 0
#define MUSTACH_ERROR_SYSTEM -1
#define MUSTACH_ERROR_UNEXPECTED_END -2
#define MUSTACH_ERROR_EMPTY_TAG -3
#define MUSTACH_ERROR_TAG_TOO_LONG -4
#define MUSTACH_ERROR_BAD_SEPARATORS -5
#define MUSTACH_ERROR_TOO_DEEP -6
#define MUSTACH_ERROR_CLOSING -7
#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
#define MUSTACH_ERROR_INVALID_ITF -9
#define MUSTACH_ERROR_ITEM_NOT_FOUND -10
#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
#define MUSTACH_ERROR_UNDEFINED_TAG -12
/*
* You can use definition below for user specific error
*
* All of this function should return a negative value to stop
* the mustache processing. The returned negative value will be
* then returned to the caller of mustach as is.
* The macro MUSTACH_ERROR_USER is involutive so for any value
* value = MUSTACH_ERROR_USER(MUSTACH_ERROR_USER(value))
*/
#define MUSTACH_ERROR_USER_BASE -100
#define MUSTACH_ERROR_USER(x) (MUSTACH_ERROR_USER_BASE-(x))
#define MUSTACH_IS_ERROR_USER(x) (MUSTACH_ERROR_USER(x) >= 0)
/**
* mustach_itf - pure abstract mustach - interface for callbacks
*
* The functions enter and next should return 0 or 1.
*
* All other functions should normally return MUSTACH_OK (zero).
* If it returns a negative value, it means an error that stop
* the process and that is reported to the caller.
*
* If any function returns a negative value, it means an error that
* stop the processing and that is reported to the caller. Mustach
* also has its own error codes. Using the macros MUSTACH_ERROR_USER
* and MUSTACH_IS_ERROR_USER could help to avoid clashes.
*
* @start: If defined (can be NULL), starts the mustach processing
* of the closure, called at the very beginning before any
@ -92,18 +120,18 @@ struct mustach_sbuf; /* see below */
* the meaning of 'FILE *file' is abstract for mustach's process and
* then you can use 'FILE*file' pass any kind of pointer (including NULL)
* to the function 'fmustach'. An example of a such behaviour is given by
* the implementation of 'umustach_json_c'.
* the implementation of 'mustach_json_c_write'.
*
* @get: If defined (can be NULL), returns in 'sbuf' the value of 'name'.
* As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
* the empty string. In that later case an implementation can
* return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
* If NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
* If 'get' is NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
* is returned.
*
* @stop: If defined (can be NULL), stops the mustach processing
* of the closure, called at the very end after all mustach
* processing occurerd. The status returned by the processing
* processing occurered. The status returned by the processing
* is passed to the stop.
*
* The array below summarize status of callbacks:
@ -127,7 +155,7 @@ struct mustach_sbuf; /* see below */
*
* The DUCK case runs on one leg. 'get' is not used if 'partial' is defined
* but is used for 'partial' if 'partial' is NULL. Thus for clarity, do not use
* it that way but define 'partial' and let 'get' NULL.
* it that way but define 'partial' and let 'get' be NULL.
*
* The DANGEROUS case is special: it allows abstract FILE if 'partial' is defined
* but forbids abstract FILE when 'partial' is NULL.
@ -167,6 +195,9 @@ struct mustach_itf {
* Can be NULL.
*
* @closure: The closure to use for 'releasecb'.
*
* @length: Length of the value or zero if unknown and value null terminated.
* To return the empty string, let it to zero and let value to NULL.
*/
struct mustach_sbuf {
const char *value;
@ -175,45 +206,28 @@ struct mustach_sbuf {
void (*releasecb)(const char *value, void *closure);
};
void *closure;
size_t length;
};
/*
* Definition of error codes returned by mustach
*/
#define MUSTACH_OK 0
#define MUSTACH_ERROR_SYSTEM -1
#define MUSTACH_ERROR_UNEXPECTED_END -2
#define MUSTACH_ERROR_EMPTY_TAG -3
#define MUSTACH_ERROR_TAG_TOO_LONG -4
#define MUSTACH_ERROR_BAD_SEPARATORS -5
#define MUSTACH_ERROR_TOO_DEEP -6
#define MUSTACH_ERROR_CLOSING -7
#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
#define MUSTACH_ERROR_INVALID_ITF -9
#define MUSTACH_ERROR_ITEM_NOT_FOUND -10
#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
/* You can use definition below for user specific error */
#define MUSTACH_ERROR_USER_BASE -100
#define MUSTACH_ERROR_USER(x) (MUSTACH_ERROR_USER_BASE-(x))
/**
* fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
* mustach_file - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* \@file: the file where to write the result
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file);
extern int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file);
/**
* fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
* mustach_fd - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @fd: the file descriptor number where to write the result
@ -221,12 +235,13 @@ extern int fmustach(const char *template, struct mustach_itf *itf, void *closure
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd);
extern int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd);
/**
* fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
* mustach_mem - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
* @length: length of the template or zero if unknown and template null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @result: the pointer receiving the result when 0 is returned
@ -235,7 +250,64 @@ extern int fdmustach(const char *template, struct mustach_itf *itf, void *closur
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size);
extern int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size);
/***************************************************************************
* compatibility with version before 1.0
*/
#ifdef __GNUC__
#define DEPRECATED_MUSTACH(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED_MUSTACH(func) __declspec(deprecated) func
#elif !defined(DEPRECATED_MUSTACH)
#pragma message("WARNING: You need to implement DEPRECATED_MUSTACH for this compiler")
#define DEPRECATED_MUSTACH(func) func
#endif
/**
* OBSOLETE use mustach_file
*
* fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
*
* @template: the template string to instantiate, null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file));
/**
* OBSOLETE use mustach_fd
*
* fdmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
*
* @template: the template string to instantiate, null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd));
/**
* OBSOLETE use mustach_mem
*
* mustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
*
* @template: the template string to instantiate, null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
DEPRECATED_MUSTACH(extern int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size));
#endif

View File

@ -180,10 +180,12 @@ TALER_TEMPLATING_fill (const char *tmpl,
int eno;
if (0 !=
(eno = mustach_jansson (tmpl,
(json_t *) root,
(char **) result,
result_size)))
(eno = mustach_jansson_mem (tmpl,
0, /* length of tmpl */
(json_t *) root,
Mustach_With_NoExtensions,
(char **) result,
result_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template with error %d\n",
@ -237,10 +239,12 @@ TALER_TEMPLATING_build (struct MHD_Connection *connection,
GNUNET_free (static_url);
}
if (0 !=
(eno = mustach_jansson (tmpl,
(json_t *) root,
&body,
&body_size)))
(eno = mustach_jansson_mem (tmpl,
0,
(json_t *) root,
Mustach_With_NoExtensions,
&body,
&body_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template `%s' with error %d\n",