update mustach library
This commit is contained in:
parent
0dd0fff17d
commit
ec8ad2e3b3
1
src/templating/.gitignore
vendored
1
src/templating/.gitignore
vendored
@ -1 +1,2 @@
|
||||
test_mustach_jansson
|
||||
taler-mustach-tool
|
||||
|
@ -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
|
||||
|
@ -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.
|
14
src/templating/LICENSE.txt
Normal file
14
src/templating/LICENSE.txt
Normal 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.
|
@ -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 = \
|
||||
|
@ -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.
|
||||
|
239
src/templating/mustach-cjson.c
Normal file
239
src/templating/mustach-cjson.c
Normal 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);
|
||||
}
|
||||
|
96
src/templating/mustach-cjson.h
Normal file
96
src/templating/mustach-cjson.h
Normal 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
|
||||
|
@ -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, "<", 4);
|
||||
break;
|
||||
case '>':
|
||||
e->writecb (file, ">", 4);
|
||||
break;
|
||||
case '&':
|
||||
e->writecb (file, "&", 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
267
src/templating/mustach-json-c.c
Normal file
267
src/templating/mustach-json-c.c
Normal 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);
|
||||
}
|
||||
|
||||
|
160
src/templating/mustach-json-c.h
Normal file
160
src/templating/mustach-json-c.h
Normal 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
|
@ -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
|
||||
|
456
src/templating/mustach-wrap.c
Normal file
456
src/templating/mustach-wrap.c
Normal 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(©, 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(©, 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(©, 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, "<", 4, file); break;
|
||||
case '>': r = write(w, ">", 4, file); break;
|
||||
case '&': r = write(w, "&", 5, file); break;
|
||||
case '"': r = write(w, """, 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);
|
||||
}
|
||||
|
234
src/templating/mustach-wrap.h
Normal file
234
src/templating/mustach-wrap.h
Normal 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
|
||||
|
@ -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("<", 4, 1, file) != 1)
|
||||
return MUSTACH_ERROR_SYSTEM;
|
||||
r = fwrite("<", 4, 1, file);
|
||||
break;
|
||||
case '>':
|
||||
if (fwrite(">", 4, 1, file) != 1)
|
||||
return MUSTACH_ERROR_SYSTEM;
|
||||
r = fwrite(">", 4, 1, file);
|
||||
break;
|
||||
case '&':
|
||||
if (fwrite("&", 5, 1, file) != 1)
|
||||
return MUSTACH_ERROR_SYSTEM;
|
||||
r = fwrite("&", 5, 1, file);
|
||||
break;
|
||||
case '"':
|
||||
r = fwrite(""", 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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user