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
|
test_mustach_jansson
|
||||||
|
taler-mustach-tool
|
||||||
|
@ -4,8 +4,15 @@ Main author:
|
|||||||
Contributors:
|
Contributors:
|
||||||
Abhishek Mishra
|
Abhishek Mishra
|
||||||
Atlas
|
Atlas
|
||||||
|
Ben Beasley
|
||||||
|
Gabriel Zachmann
|
||||||
Harold L Marzan
|
Harold L Marzan
|
||||||
|
Kurt Jung
|
||||||
Lailton Fernando Mariano
|
Lailton Fernando Mariano
|
||||||
|
Lucas Ramage
|
||||||
|
Paramtamtam
|
||||||
|
RekGRpth
|
||||||
|
Ryan Fox
|
||||||
Sami Kerola
|
Sami Kerola
|
||||||
Sijmen J. Mulder
|
Sijmen J. Mulder
|
||||||
Tomasz Sieprawski
|
Tomasz Sieprawski
|
||||||
@ -13,6 +20,7 @@ Contributors:
|
|||||||
Packagers:
|
Packagers:
|
||||||
pkgsrc: Sijmen J. Mulder
|
pkgsrc: Sijmen J. Mulder
|
||||||
alpine linux: Lucas Ramage
|
alpine linux: Lucas Ramage
|
||||||
|
RPM & DEB: Marcus Hardt
|
||||||
|
|
||||||
Thanks to issue submitters:
|
Thanks to issue submitters:
|
||||||
Dante Torres
|
Dante Torres
|
||||||
@ -21,3 +29,4 @@ Thanks to issue submitters:
|
|||||||
Mark Bucciarelli
|
Mark Bucciarelli
|
||||||
Paul Wisehart
|
Paul Wisehart
|
||||||
Thierry Fournier
|
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
|
XLIB = -lgcov
|
||||||
endif
|
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 = \
|
lib_LTLIBRARIES = \
|
||||||
libtalertemplating.la
|
libtalertemplating.la
|
||||||
@ -15,6 +24,7 @@ noinst_LTLIBRARIES = \
|
|||||||
|
|
||||||
libtalertemplating_la_SOURCES = \
|
libtalertemplating_la_SOURCES = \
|
||||||
mustach.c mustach.h \
|
mustach.c mustach.h \
|
||||||
|
mustach-wrap.c mustach-wrap.h \
|
||||||
mustach-jansson.c mustach-jansson.h \
|
mustach-jansson.c mustach-jansson.h \
|
||||||
templating_api.c
|
templating_api.c
|
||||||
libtalertemplating_la_LIBADD = \
|
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")
|
`mustach` is a C implementation of the [mustache](http://mustache.github.io "main site for mustache")
|
||||||
template specification.
|
template specification.
|
||||||
|
|
||||||
The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach).
|
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.
|
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.
|
`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
|
## Distributions offering mustach package
|
||||||
|
|
||||||
### Alpine Linux
|
### Alpine Linux
|
||||||
@ -30,6 +41,13 @@ make
|
|||||||
|
|
||||||
See http://pkgsrc.se/devel/mustach
|
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
|
## Using Mustach from sources
|
||||||
|
|
||||||
The file **mustach.h** is the main documentation. Look at it.
|
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.c** core implementation of mustache in C
|
||||||
- **mustach.h** header file for core definitions
|
- **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.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-json-c.h** header file for using the tiny json-c wrapper
|
||||||
- **mustach-tool.c** simple tool for applying template files to a JSON file
|
- **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
|
The file **mustach-json-c.c** is the historical example of use of **mustach** and
|
||||||
and it is also a practical implementation that can be used. It uses the library
|
**mustach-wrap** core and it is also a practical implementation that can be used.
|
||||||
json-c. (NOTE for Mac OS: available through homebrew).
|
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:
|
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
|
### Portability
|
||||||
|
|
||||||
Some system does not provide *open_memstream*. In that case, tell your
|
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:
|
Example:
|
||||||
|
|
||||||
gcc -DNO_OPEN_MEMSTREAM
|
CFLAGS=-DNO_OPEN_MEMSTREAM make
|
||||||
|
|
||||||
### Integration
|
### 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.
|
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:
|
of the interface **mustach_itf** that you have to implement are:
|
||||||
`enter`, `next`, `leave`, `get` and `emit`.
|
`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 `=`.
|
character reserved for mustach: one of `#`, `^`, `/`, `&`, `{`, `>` and `=`.
|
||||||
|
|
||||||
This extension introduces the special character `:` to explicitly
|
This extension introduces the special character `:` to explicitly
|
||||||
tell mustach to just substitute the value. So `:` becomes a new special
|
tell mustach to just substitute the value. So `:` becomes a new special
|
||||||
character.
|
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.
|
When an empty tag is found, instead of automatically raising the error
|
||||||
They allow to write `key=value` (matching test) or `key=!value`
|
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.
|
(not matching test) in any query.
|
||||||
|
|
||||||
The specific comparison extension also allows to compare if greater,
|
This is a wrap extension implemented in file **mustach-wrap.c**.
|
||||||
lesser, etc.. than a value. It allows to write `key>value`.
|
|
||||||
|
### 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
|
It the comparator sign appears in the first column it is ignored
|
||||||
as if it was escaped.
|
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
|
### Interpret JSON Pointers (Mustach_With_JsonPointer)
|
||||||
in `{{#key}}{{.}}{{/key}}` that applied to `{"key":3.14}` produces `3.14`
|
|
||||||
and `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces
|
|
||||||
` 1 2`.
|
|
||||||
|
|
||||||
#### 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
|
A special escaping is used for `=`, `<`, `>` signs when
|
||||||
fields of `X`. Example: `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on
|
values comparisons are enabled: `~=` gives `=` in the key.
|
||||||
`{"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.
|
|
||||||
|
|
||||||
### Removing Extensions
|
This is a wrap extension implemented in file **mustach-wrap.c**.
|
||||||
|
|
||||||
When compiling mustach.c or mustach-json-c.c,
|
### Iteration On Objects (Mustach_With_ObjectIter)
|
||||||
extensions can be removed by defining macros
|
|
||||||
using option -D.
|
|
||||||
|
|
||||||
The possible macros are of 3 categories, the global,
|
With this extension, using the pattern `{{#X.*}}...{{/X.*}}`
|
||||||
the mustach core specific and the mustach-json-c example
|
allows to iterate on fields of `X`.
|
||||||
of implementation specific.
|
|
||||||
|
|
||||||
#### 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
|
Here the single star `{{*}}` is replaced by the iterated key
|
||||||
extensions for the core or the example.
|
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 (:)
|
Report the error MUSTACH_ERROR_UNDEFINED_TAG when a requested tag
|
||||||
as explicit command for variable substitution.
|
is not defined.
|
||||||
This extension allows to have name starting
|
|
||||||
with one of the mustach character `:#^/&{=>`
|
|
||||||
|
|
||||||
- `NO_ALLOW_EMPTY_TAG`
|
This is a wrap extension implemented in file **mustach-wrap.c**.
|
||||||
|
|
||||||
Generate the error MUSTACH_ERROR_EMPTY_TAG automatically
|
### Access To Current Value
|
||||||
when an empty tag is encountered.
|
|
||||||
|
|
||||||
#### 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
|
Examples:
|
||||||
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.
|
|
||||||
|
|
||||||
- `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
|
This is a wrap extension implemented in file **mustach-wrap.c**.
|
||||||
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}}`.
|
|
||||||
|
|
||||||
- `NO_USE_VALUE_ESCAPE_FIRST_EXTENSION_FOR_MUSTACH`
|
### Partial Data First
|
||||||
|
|
||||||
This macro fordids automatic escaping of coparison
|
*this was an extension but is now always enforced*
|
||||||
sign appearing at first column.
|
|
||||||
|
|
||||||
- `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.
|
By default, the order of the search is (1) as a file,
|
||||||
JSON pointers are defined in IETF RFC 6901.
|
and if not found, (2) in the current json context.
|
||||||
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.
|
|
||||||
|
|
||||||
- `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
|
That option is useful to keep the compatibility with
|
||||||
to iterate over the keys of an object. The iteration on object
|
versions of *mustach* anteriors to 1.2.0.
|
||||||
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.
|
|
||||||
|
|
||||||
- `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
|
### Escape First Compare
|
||||||
like in `{{.}}`.
|
|
||||||
|
|
||||||
- `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 <jobol@nonadev.net>
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
https://gitlab.com/jobol/mustach
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
SPDX-License-Identifier: ISC
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "platform.h"
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "mustach.h"
|
||||||
|
#include "mustach-wrap.h"
|
||||||
#include "mustach-jansson.h"
|
#include "mustach-jansson.h"
|
||||||
|
|
||||||
struct Context
|
struct expl {
|
||||||
{
|
json_t *root;
|
||||||
/**
|
json_t *selection;
|
||||||
* Context object.
|
int depth;
|
||||||
*/
|
struct {
|
||||||
json_t *cont;
|
json_t *cont;
|
||||||
|
json_t *obj;
|
||||||
/**
|
void *iter;
|
||||||
* Current object.
|
int is_objiter;
|
||||||
*/
|
size_t index, count;
|
||||||
json_t *obj;
|
} stack[MUSTACH_MAX_DEPTH];
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Bang
|
static int start(void *closure)
|
||||||
{
|
{
|
||||||
BANG_NONE,
|
struct expl *e = closure;
|
||||||
BANG_I18N,
|
e->depth = 0;
|
||||||
BANG_STRINGIFY,
|
e->selection = json_null();
|
||||||
BANG_AMOUNT_CURRENCY,
|
e->stack[0].cont = NULL;
|
||||||
BANG_AMOUNT_DECIMAL,
|
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;
|
struct expl e;
|
||||||
mustach_jansson_write_cb writecb;
|
e.root = root;
|
||||||
int depth;
|
return mustach_wrap_file(template, length, &mustach_jansson_wrap_itf, &e, flags, file);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd)
|
||||||
static json_t *
|
|
||||||
find (struct JanssonClosure *e, const char *name)
|
|
||||||
{
|
{
|
||||||
json_t *obj = NULL;
|
struct expl e;
|
||||||
char *path = GNUNET_strdup (name);
|
e.root = root;
|
||||||
char *bang;
|
return mustach_wrap_fd(template, length, &mustach_jansson_wrap_itf, &e, flags, fd);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size)
|
||||||
static int
|
|
||||||
start (void *closure)
|
|
||||||
{
|
{
|
||||||
struct JanssonClosure *e = closure;
|
struct expl e;
|
||||||
e->depth = 0;
|
e.root = root;
|
||||||
e->stack[0].cont = NULL;
|
return mustach_wrap_mem(template, length, &mustach_jansson_wrap_itf, &e, flags, result, size);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure)
|
||||||
static int
|
|
||||||
emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
|
|
||||||
{
|
{
|
||||||
struct JanssonClosure *e = closure;
|
struct expl e;
|
||||||
if (! escape)
|
e.root = root;
|
||||||
e->writecb (file, buffer, size);
|
return mustach_wrap_write(template, length, &mustach_jansson_wrap_itf, &e, flags, writecb, closure);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
|
||||||
static int
|
|
||||||
enter (void *closure, const char *name)
|
|
||||||
{
|
{
|
||||||
struct JanssonClosure *e = closure;
|
struct expl e;
|
||||||
json_t *o = find (e, name);
|
e.root = root;
|
||||||
if (++e->depth >= MUSTACH_MAX_DEPTH)
|
return mustach_wrap_emit(template, length, &mustach_jansson_wrap_itf, &e, flags, emitcb, closure);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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>
|
Author: José Bollo <jobol@nonadev.net>
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
https://gitlab.com/jobol/mustach
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
SPDX-License-Identifier: ISC
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _mustach_jansson_h_included_
|
#ifndef _mustach_jansson_h_included_
|
||||||
#define _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
|
* @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
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of 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
|
* @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
|
* @root: the root json object to render
|
||||||
* @fd: the file descriptor number where to write the result
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of 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
|
* @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
|
* @root: the root json object to render
|
||||||
* @result: the pointer receiving the result when 0 is returned
|
* @result: the pointer receiving the result when 0 is returned
|
||||||
* @size: the size of the returned result
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of error.
|
* a other negative value in case of error.
|
||||||
*/
|
*/
|
||||||
extern int mustach_jansson (const char *template, json_t *root, char **result,
|
extern int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size);
|
||||||
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
|
* @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
|
* @root: the root json object to render
|
||||||
* @writecb: the function that write values
|
* @writecb: the function that write values
|
||||||
* @closure: the closure for the write function
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of error.
|
* a other negative value in case of error.
|
||||||
*/
|
*/
|
||||||
typedef int (*mustach_jansson_write_cb)(void *closure, const char *buffer,
|
extern int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure);
|
||||||
size_t size);
|
|
||||||
extern int umustach_jansson (const char *template, json_t *root,
|
/**
|
||||||
mustach_jansson_write_cb 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
|
#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 <jobol@nonadev.net>
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
https://gitlab.com/jobol/mustach
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
SPDX-License-Identifier: ISC
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -27,7 +16,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "mustach-json-c.h"
|
#include "mustach-wrap.h"
|
||||||
|
|
||||||
static const size_t BLOCKSIZE = 8192;
|
static const size_t BLOCKSIZE = 8192;
|
||||||
|
|
||||||
@ -43,16 +32,39 @@ static const char *errors[] = {
|
|||||||
"bad unescape tag",
|
"bad unescape tag",
|
||||||
"invalid interface",
|
"invalid interface",
|
||||||
"item not found",
|
"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)
|
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);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *readfile(const char *filename)
|
static char *readfile(const char *filename, size_t *length)
|
||||||
{
|
{
|
||||||
int f;
|
int f;
|
||||||
struct stat s;
|
struct stat s;
|
||||||
@ -106,50 +118,140 @@ static char *readfile(const char *filename)
|
|||||||
} while(rc > 0);
|
} while(rc > 0);
|
||||||
|
|
||||||
close(f);
|
close(f);
|
||||||
|
if (length != NULL)
|
||||||
|
*length = pos;
|
||||||
result[pos] = 0;
|
result[pos] = 0;
|
||||||
return result;
|
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)
|
int main(int ac, char **av)
|
||||||
{
|
{
|
||||||
struct json_object *o;
|
char *t, *f;
|
||||||
char *t;
|
|
||||||
char *prog = *av;
|
char *prog = *av;
|
||||||
int s;
|
int s;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
(void)ac; /* unused */
|
(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"))
|
if (!strcmp(*av, "-h") || !strcmp(*av, "--help"))
|
||||||
help(prog);
|
help(prog);
|
||||||
if (av[0][0] == '-' && !av[0][1])
|
if (!strcmp(*av, "-s") || !strcmp(*av, "--strict"))
|
||||||
o = json_object_from_fd(0);
|
flags |= Mustach_With_ErrorUndefined;
|
||||||
else
|
}
|
||||||
o = json_object_from_file(av[0]);
|
if (*av) {
|
||||||
#if JSON_C_VERSION_NUM >= 0x000D00
|
f = (av[0][0] == '-' && !av[0][1]) ? "/dev/stdin" : av[0];
|
||||||
if (json_util_get_last_err() != NULL) {
|
s = load_json(f);
|
||||||
fprintf(stderr, "Bad json: %s (file %s)\n", json_util_get_last_err(), av[0]);
|
if (s < 0) {
|
||||||
exit(1);
|
fprintf(stderr, "Can't load json file %s\n", av[0]);
|
||||||
}
|
if(errmsg)
|
||||||
else
|
fprintf(stderr, " reason: %s\n", errmsg);
|
||||||
#endif
|
|
||||||
if (o == NULL) {
|
|
||||||
fprintf(stderr, "Aborted: null json (file %s)\n", av[0]);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
while(*++av) {
|
while(*++av) {
|
||||||
t = readfile(*av);
|
t = readfile(*av, &length);
|
||||||
s = fmustach_json_c(t, o, stdout);
|
s = process(t, length);
|
||||||
if (s != 0) {
|
free(t);
|
||||||
|
if (s != MUSTACH_OK) {
|
||||||
s = -s;
|
s = -s;
|
||||||
if (s < 1 || s >= (int)(sizeof errors / sizeof * errors))
|
if (s < 1 || s >= (int)(sizeof errors / sizeof * errors))
|
||||||
s = 0;
|
s = 0;
|
||||||
fprintf(stderr, "Template error %s (file %s)\n", errors[s], *av);
|
fprintf(stderr, "Template error %s (file %s)\n", errors[s], *av);
|
||||||
}
|
}
|
||||||
free(t);
|
|
||||||
}
|
}
|
||||||
json_object_put(o);
|
close_json();
|
||||||
}
|
}
|
||||||
return 0;
|
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 <jobol@nonadev.net>
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
https://gitlab.com/jobol/mustach
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
SPDX-License-Identifier: ISC
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -27,19 +16,9 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef __sun
|
|
||||||
# include <alloca.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mustach.h"
|
#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 {
|
struct iwrap {
|
||||||
int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
|
int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
|
||||||
void *closure; /* closure for: enter, next, leave, emit, get */
|
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 (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
|
||||||
int (*partial)(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 */
|
void *closure_partial; /* closure for partial */
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct prefix {
|
||||||
|
size_t len;
|
||||||
|
const char *start;
|
||||||
|
struct prefix *prefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if !defined(NO_OPEN_MEMSTREAM)
|
#if !defined(NO_OPEN_MEMSTREAM)
|
||||||
@ -135,6 +121,7 @@ static inline void sbuf_reset(struct mustach_sbuf *sbuf)
|
|||||||
sbuf->value = NULL;
|
sbuf->value = NULL;
|
||||||
sbuf->freecb = NULL;
|
sbuf->freecb = NULL;
|
||||||
sbuf->closure = NULL;
|
sbuf->closure = NULL;
|
||||||
|
sbuf->length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sbuf_release(struct mustach_sbuf *sbuf)
|
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);
|
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)
|
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 */
|
(void)closure; /* unused */
|
||||||
|
|
||||||
if (!escape)
|
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) {
|
while (i < size) {
|
||||||
j = i;
|
j = i;
|
||||||
while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
|
while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
|
||||||
j++;
|
j++;
|
||||||
if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
|
if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
|
||||||
return MUSTACH_ERROR_SYSTEM;
|
return MUSTACH_ERROR_SYSTEM;
|
||||||
if (j < size) {
|
if (j < size) {
|
||||||
switch(buffer[j++]) {
|
switch(buffer[j++]) {
|
||||||
case '<':
|
case '<':
|
||||||
if (fwrite("<", 4, 1, file) != 1)
|
r = fwrite("<", 4, 1, file);
|
||||||
return MUSTACH_ERROR_SYSTEM;
|
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
if (fwrite(">", 4, 1, file) != 1)
|
r = fwrite(">", 4, 1, file);
|
||||||
return MUSTACH_ERROR_SYSTEM;
|
|
||||||
break;
|
break;
|
||||||
case '&':
|
case '&':
|
||||||
if (fwrite("&", 5, 1, file) != 1)
|
r = fwrite("&", 5, 1, file);
|
||||||
return MUSTACH_ERROR_SYSTEM;
|
break;
|
||||||
|
case '"':
|
||||||
|
r = fwrite(""", 6, 1, file);
|
||||||
break;
|
break;
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
|
if (r != 1)
|
||||||
|
return MUSTACH_ERROR_SYSTEM;
|
||||||
}
|
}
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
@ -191,7 +187,7 @@ static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
|
|||||||
sbuf_reset(&sbuf);
|
sbuf_reset(&sbuf);
|
||||||
rc = iwrap->get(iwrap->closure, name, &sbuf);
|
rc = iwrap->get(iwrap->closure, name, &sbuf);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
length = strlen(sbuf.value);
|
length = sbuf_length(&sbuf);
|
||||||
if (length)
|
if (length)
|
||||||
rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
|
rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
|
||||||
sbuf_release(&sbuf);
|
sbuf_release(&sbuf);
|
||||||
@ -220,55 +216,109 @@ static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *s
|
|||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
sbuf->value = result;
|
sbuf->value = result;
|
||||||
sbuf->freecb = free;
|
sbuf->freecb = free;
|
||||||
|
sbuf->length = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
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;
|
struct mustach_sbuf sbuf;
|
||||||
char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
|
char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
|
||||||
const char *beg, *term;
|
char name[MUSTACH_MAX_LENGTH + 1], c;
|
||||||
struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
|
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;
|
size_t oplen, cllen, len, l;
|
||||||
int depth, rc, enabled;
|
int depth, rc, enabled, stdalone;
|
||||||
|
struct prefix pref;
|
||||||
|
|
||||||
enabled = 1;
|
pref.prefix = prefix;
|
||||||
oplen = strlen(opstr);
|
end = template + (length ? length : strlen(template));
|
||||||
cllen = strlen(clstr);
|
opstr[0] = opstr[1] = '{';
|
||||||
depth = 0;
|
clstr[0] = clstr[1] = '}';
|
||||||
for(;;) {
|
oplen = cllen = 2;
|
||||||
beg = strstr(template, opstr);
|
stdalone = enabled = 1;
|
||||||
if (beg == NULL) {
|
depth = pref.len = 0;
|
||||||
/* no more mustach */
|
for (;;) {
|
||||||
if (enabled && template[0]) {
|
/* search next openning delimiter */
|
||||||
rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
|
for (beg = template ; ; beg++) {
|
||||||
if (rc < 0)
|
c = beg == end ? '\n' : *beg;
|
||||||
return rc;
|
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;
|
beg += oplen;
|
||||||
term = strstr(beg, clstr);
|
|
||||||
if (term == NULL)
|
/* search next closing delimiter */
|
||||||
return MUSTACH_ERROR_UNEXPECTED_END;
|
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;
|
template = term + cllen;
|
||||||
len = (size_t)(term - beg);
|
len = (size_t)(term - beg);
|
||||||
c = *beg;
|
c = *beg;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
case ':':
|
||||||
|
stdalone = 0;
|
||||||
|
if (iwrap->flags & Mustach_With_Colon)
|
||||||
|
goto exclude_first;
|
||||||
|
goto get_name;
|
||||||
case '!':
|
case '!':
|
||||||
case '=':
|
case '=':
|
||||||
break;
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
for (l = 0 ; clstr[l] == '}' ; l++);
|
for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
|
||||||
if (clstr[l]) {
|
if (l < cllen) {
|
||||||
if (!len || beg[len-1] != '}')
|
if (!len || beg[len-1] != '}')
|
||||||
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
|
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
|
||||||
len--;
|
len--;
|
||||||
@ -279,55 +329,63 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
|
|||||||
}
|
}
|
||||||
c = '&';
|
c = '&';
|
||||||
/*@fallthrough@*/
|
/*@fallthrough@*/
|
||||||
|
case '&':
|
||||||
|
stdalone = 0;
|
||||||
|
/*@fallthrough@*/
|
||||||
case '^':
|
case '^':
|
||||||
case '#':
|
case '#':
|
||||||
case '/':
|
case '/':
|
||||||
case '&':
|
|
||||||
case '>':
|
case '>':
|
||||||
#if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
|
exclude_first:
|
||||||
case ':':
|
beg++;
|
||||||
#endif
|
len--;
|
||||||
beg++; len--;
|
goto get_name;
|
||||||
default:
|
default:
|
||||||
|
stdalone = 0;
|
||||||
|
get_name:
|
||||||
while (len && isspace(beg[0])) { beg++; len--; }
|
while (len && isspace(beg[0])) { beg++; len--; }
|
||||||
while (len && isspace(beg[len-1])) len--;
|
while (len && isspace(beg[len-1])) len--;
|
||||||
#if !defined(NO_ALLOW_EMPTY_TAG)
|
if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
|
||||||
if (len == 0)
|
|
||||||
return MUSTACH_ERROR_EMPTY_TAG;
|
return MUSTACH_ERROR_EMPTY_TAG;
|
||||||
#endif
|
|
||||||
if (len > MUSTACH_MAX_LENGTH)
|
if (len > MUSTACH_MAX_LENGTH)
|
||||||
return MUSTACH_ERROR_TAG_TOO_LONG;
|
return MUSTACH_ERROR_TAG_TOO_LONG;
|
||||||
memcpy(name, beg, len);
|
memcpy(name, beg, len);
|
||||||
name[len] = 0;
|
name[len] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (stdalone)
|
||||||
|
stdalone = 2;
|
||||||
|
else if (enabled) {
|
||||||
|
rc = emitprefix(iwrap, file, &pref);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
pref.len = 0;
|
||||||
|
}
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case '!':
|
case '!':
|
||||||
/* comment */
|
/* comment */
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
/* defines separators */
|
/* defines delimiters */
|
||||||
if (len < 5 || beg[len - 1] != '=')
|
if (len < 5 || beg[len - 1] != '=')
|
||||||
return MUSTACH_ERROR_BAD_SEPARATORS;
|
return MUSTACH_ERROR_BAD_SEPARATORS;
|
||||||
beg++;
|
beg++;
|
||||||
len -= 2;
|
len -= 2;
|
||||||
|
while (len && isspace(*beg))
|
||||||
|
beg++, len--;
|
||||||
|
while (len && isspace(beg[len - 1]))
|
||||||
|
len--;
|
||||||
for (l = 0; l < len && !isspace(beg[l]) ; l++);
|
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;
|
return MUSTACH_ERROR_BAD_SEPARATORS;
|
||||||
oplen = l;
|
oplen = l;
|
||||||
tmp = alloca(oplen + 1);
|
memcpy(opstr, beg, l);
|
||||||
memcpy(tmp, beg, oplen);
|
|
||||||
tmp[oplen] = 0;
|
|
||||||
opstr = tmp;
|
|
||||||
while (l < len && isspace(beg[l])) 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;
|
return MUSTACH_ERROR_BAD_SEPARATORS;
|
||||||
cllen = len - l;
|
cllen = len - l;
|
||||||
tmp = alloca(cllen + 1);
|
memcpy(clstr, beg + l, cllen);
|
||||||
memcpy(tmp, beg + l, cllen);
|
|
||||||
tmp[cllen] = 0;
|
|
||||||
clstr = tmp;
|
|
||||||
break;
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
case '#':
|
case '#':
|
||||||
@ -343,8 +401,8 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
|
|||||||
stack[depth].name = beg;
|
stack[depth].name = beg;
|
||||||
stack[depth].again = template;
|
stack[depth].again = template;
|
||||||
stack[depth].length = len;
|
stack[depth].length = len;
|
||||||
stack[depth].enabled = enabled;
|
stack[depth].enabled = enabled != 0;
|
||||||
stack[depth].entered = rc;
|
stack[depth].entered = rc != 0;
|
||||||
if ((c == '#') == (rc == 0))
|
if ((c == '#') == (rc == 0))
|
||||||
enabled = 0;
|
enabled = 0;
|
||||||
depth++;
|
depth++;
|
||||||
@ -370,7 +428,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
|
|||||||
sbuf_reset(&sbuf);
|
sbuf_reset(&sbuf);
|
||||||
rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
|
rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
rc = process(sbuf.value, iwrap, file, opstr, clstr);
|
rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, file, &pref);
|
||||||
sbuf_release(&sbuf);
|
sbuf_release(&sbuf);
|
||||||
}
|
}
|
||||||
if (rc < 0)
|
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;
|
int rc;
|
||||||
struct iwrap iwrap;
|
struct iwrap iwrap;
|
||||||
@ -422,17 +480,18 @@ int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE
|
|||||||
iwrap.next = itf->next;
|
iwrap.next = itf->next;
|
||||||
iwrap.leave = itf->leave;
|
iwrap.leave = itf->leave;
|
||||||
iwrap.get = itf->get;
|
iwrap.get = itf->get;
|
||||||
|
iwrap.flags = flags;
|
||||||
|
|
||||||
/* process */
|
/* process */
|
||||||
rc = itf->start ? itf->start(closure) : 0;
|
rc = itf->start ? itf->start(closure) : 0;
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
rc = process(template, &iwrap, file, "{{", "}}");
|
rc = process(template, length, &iwrap, file, 0);
|
||||||
if (itf->stop)
|
if (itf->stop)
|
||||||
itf->stop(closure, rc);
|
itf->stop(closure, rc);
|
||||||
return 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;
|
int rc;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
@ -442,13 +501,13 @@ int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int
|
|||||||
rc = MUSTACH_ERROR_SYSTEM;
|
rc = MUSTACH_ERROR_SYSTEM;
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
rc = fmustach(template, itf, closure, file);
|
rc = mustach_file(template, length, itf, closure, flags, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
return rc;
|
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;
|
int rc;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
@ -461,7 +520,7 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
|
|||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
rc = MUSTACH_ERROR_SYSTEM;
|
rc = MUSTACH_ERROR_SYSTEM;
|
||||||
else {
|
else {
|
||||||
rc = fmustach(template, itf, closure, file);
|
rc = mustach_file(template, length, itf, closure, flags, file);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
memfile_abort(file, result, size);
|
memfile_abort(file, result, size);
|
||||||
else
|
else
|
||||||
@ -470,3 +529,18 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
|
|||||||
return rc;
|
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 <jobol@nonadev.net>
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
https://gitlab.com/jobol/mustach
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
SPDX-License-Identifier: ISC
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _mustach_h_included_
|
#ifndef _mustach_h_included_
|
||||||
@ -25,7 +14,7 @@ struct mustach_sbuf; /* see below */
|
|||||||
/**
|
/**
|
||||||
* Current version of mustach and its derivates
|
* 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_MAJOR (MUSTACH_VERSION / 100)
|
||||||
#define MUSTACH_VERSION_MINOR (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 {{...}}
|
* 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 macro MUSTACH_ERROR_USER is involutive so for any value
|
||||||
* the mustache processing. The returned negative value will be
|
* value = MUSTACH_ERROR_USER(MUSTACH_ERROR_USER(value))
|
||||||
* then returned to the caller of mustach as is.
|
*/
|
||||||
|
#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.
|
* The functions enter and next should return 0 or 1.
|
||||||
*
|
*
|
||||||
* All other functions should normally return MUSTACH_OK (zero).
|
* 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
|
* @start: If defined (can be NULL), starts the mustach processing
|
||||||
* of the closure, called at the very beginning before any
|
* 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
|
* 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)
|
* 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
|
* 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'.
|
* @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
|
* As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
|
||||||
* the empty string. In that later case an implementation can
|
* the empty string. In that later case an implementation can
|
||||||
* return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
|
* 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.
|
* is returned.
|
||||||
*
|
*
|
||||||
* @stop: If defined (can be NULL), stops the mustach processing
|
* @stop: If defined (can be NULL), stops the mustach processing
|
||||||
* of the closure, called at the very end after all mustach
|
* 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.
|
* is passed to the stop.
|
||||||
*
|
*
|
||||||
* The array below summarize status of callbacks:
|
* 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
|
* 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
|
* 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
|
* The DANGEROUS case is special: it allows abstract FILE if 'partial' is defined
|
||||||
* but forbids abstract FILE when 'partial' is NULL.
|
* but forbids abstract FILE when 'partial' is NULL.
|
||||||
@ -167,6 +195,9 @@ struct mustach_itf {
|
|||||||
* Can be NULL.
|
* Can be NULL.
|
||||||
*
|
*
|
||||||
* @closure: The closure to use for 'releasecb'.
|
* @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 {
|
struct mustach_sbuf {
|
||||||
const char *value;
|
const char *value;
|
||||||
@ -175,45 +206,28 @@ struct mustach_sbuf {
|
|||||||
void (*releasecb)(const char *value, void *closure);
|
void (*releasecb)(const char *value, void *closure);
|
||||||
};
|
};
|
||||||
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
|
* @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
|
* @itf: the interface to the functions that mustach calls
|
||||||
* @closure: the closure to pass to functions called
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of 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
|
* @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
|
* @itf: the interface to the functions that mustach calls
|
||||||
* @closure: the closure to pass to functions called
|
* @closure: the closure to pass to functions called
|
||||||
* @fd: the file descriptor number where to write the result
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of 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
|
* @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
|
* @itf: the interface to the functions that mustach calls
|
||||||
* @closure: the closure to pass to functions called
|
* @closure: the closure to pass to functions called
|
||||||
* @result: the pointer receiving the result when 0 is returned
|
* @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
|
* Returns 0 in case of success, -1 with errno set in case of system error
|
||||||
* a other negative value in case of 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
|
#endif
|
||||||
|
|
||||||
|
@ -180,10 +180,12 @@ TALER_TEMPLATING_fill (const char *tmpl,
|
|||||||
int eno;
|
int eno;
|
||||||
|
|
||||||
if (0 !=
|
if (0 !=
|
||||||
(eno = mustach_jansson (tmpl,
|
(eno = mustach_jansson_mem (tmpl,
|
||||||
(json_t *) root,
|
0, /* length of tmpl */
|
||||||
(char **) result,
|
(json_t *) root,
|
||||||
result_size)))
|
Mustach_With_NoExtensions,
|
||||||
|
(char **) result,
|
||||||
|
result_size)))
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"mustach failed on template with error %d\n",
|
"mustach failed on template with error %d\n",
|
||||||
@ -237,10 +239,12 @@ TALER_TEMPLATING_build (struct MHD_Connection *connection,
|
|||||||
GNUNET_free (static_url);
|
GNUNET_free (static_url);
|
||||||
}
|
}
|
||||||
if (0 !=
|
if (0 !=
|
||||||
(eno = mustach_jansson (tmpl,
|
(eno = mustach_jansson_mem (tmpl,
|
||||||
(json_t *) root,
|
0,
|
||||||
&body,
|
(json_t *) root,
|
||||||
&body_size)))
|
Mustach_With_NoExtensions,
|
||||||
|
&body,
|
||||||
|
&body_size)))
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"mustach failed on template `%s' with error %d\n",
|
"mustach failed on template `%s' with error %d\n",
|
||||||
|
Loading…
Reference in New Issue
Block a user