aboutsummaryrefslogtreecommitdiff
path: root/src/templating
diff options
context:
space:
mode:
Diffstat (limited to 'src/templating')
-rw-r--r--src/templating/.gitignore1
-rw-r--r--src/templating/AUTHORS9
-rw-r--r--src/templating/LICENSE-2.0.txt202
-rw-r--r--src/templating/LICENSE.txt14
-rw-r--r--src/templating/Makefile.am10
-rw-r--r--src/templating/README.md306
-rw-r--r--src/templating/mustach-cjson.c239
-rw-r--r--src/templating/mustach-cjson.h96
-rw-r--r--src/templating/mustach-jansson.c580
-rw-r--r--src/templating/mustach-jansson.h72
-rw-r--r--src/templating/mustach-json-c.c267
-rw-r--r--src/templating/mustach-json-c.h160
-rw-r--r--src/templating/mustach-tool.c176
-rw-r--r--src/templating/mustach-wrap.c456
-rw-r--r--src/templating/mustach-wrap.h234
-rw-r--r--src/templating/mustach.c256
-rw-r--r--src/templating/mustach.h172
-rw-r--r--src/templating/templating_api.c20
18 files changed, 2370 insertions, 900 deletions
diff --git a/src/templating/.gitignore b/src/templating/.gitignore
index b2bf6ef99..ac9c37f45 100644
--- a/src/templating/.gitignore
+++ b/src/templating/.gitignore
@@ -1 +1,2 @@
test_mustach_jansson
+taler-mustach-tool
diff --git a/src/templating/AUTHORS b/src/templating/AUTHORS
index 2fcc60437..b440c24ed 100644
--- a/src/templating/AUTHORS
+++ b/src/templating/AUTHORS
@@ -4,8 +4,15 @@ Main author:
Contributors:
Abhishek Mishra
Atlas
+ Ben Beasley
+ Gabriel Zachmann
Harold L Marzan
+ Kurt Jung
Lailton Fernando Mariano
+ Lucas Ramage
+ Paramtamtam
+ RekGRpth
+ Ryan Fox
Sami Kerola
Sijmen J. Mulder
Tomasz Sieprawski
@@ -13,6 +20,7 @@ Contributors:
Packagers:
pkgsrc: Sijmen J. Mulder
alpine linux: Lucas Ramage
+ RPM & DEB: Marcus Hardt
Thanks to issue submitters:
Dante Torres
@@ -21,3 +29,4 @@ Thanks to issue submitters:
Mark Bucciarelli
Paul Wisehart
Thierry Fournier
+ SASU OKFT
diff --git a/src/templating/LICENSE-2.0.txt b/src/templating/LICENSE-2.0.txt
deleted file mode 100644
index d64569567..000000000
--- a/src/templating/LICENSE-2.0.txt
+++ /dev/null
@@ -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.
diff --git a/src/templating/LICENSE.txt b/src/templating/LICENSE.txt
new file mode 100644
index 000000000..495aeefd5
--- /dev/null
+++ b/src/templating/LICENSE.txt
@@ -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.
diff --git a/src/templating/Makefile.am b/src/templating/Makefile.am
index 2501151a8..fcdc22cd4 100644
--- a/src/templating/Makefile.am
+++ b/src/templating/Makefile.am
@@ -6,6 +6,15 @@ if USE_COVERAGE
XLIB = -lgcov
endif
+noinst_PROGRAMS = \
+ taler-mustach-tool
+
+taler_mustach_tool_SOURCES = \
+ mustach-tool.c \
+ mustach-jansson.h
+taler_mustach_tool_LDADD = \
+ libmustach.la \
+ -ljansson
lib_LTLIBRARIES = \
libtalertemplating.la
@@ -15,6 +24,7 @@ noinst_LTLIBRARIES = \
libtalertemplating_la_SOURCES = \
mustach.c mustach.h \
+ mustach-wrap.c mustach-wrap.h \
mustach-jansson.c mustach-jansson.h \
templating_api.c
libtalertemplating_la_LIBADD = \
diff --git a/src/templating/README.md b/src/templating/README.md
index a6df19f64..6699b0e89 100644
--- a/src/templating/README.md
+++ b/src/templating/README.md
@@ -1,16 +1,27 @@
-# Introduction to Mustach 0.99
+# Introduction to Mustach 1.2
`mustach` is a C implementation of the [mustache](http://mustache.github.io "main site for mustache")
template specification.
The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach).
-The best way to use mustach is to copy the files **mustach.h** and **mustach.c**
+The simpliest way to use mustach is to copy the files **mustach.h** and **mustach.c**
directly into your project and use it.
-Alternatively, make and meson files are provided for building `mustach` and
+If you are using one of the JSON libraries listed below, you can get extended feature
+by also including **mustach-wrap.h**, **mustach-wrap.c**, **mustach-XXX.h** and
+**mustach-XXX.c** in your project (see below for **XXX**)
+
+- [json-c](https://github.com/json-c/json-c): use **XXX** = **json-c**
+- [jansson](http://www.digip.org/jansson/): use **XXX** = **jansson**
+- [cJSON](https://github.com/DaveGamble/cJSON): use **XXX** = **cjson**
+
+Alternatively, make and meson files are provided for building `mustach` and
`libmustach.so` shared library.
+Since version 1.0, the makefile allows to compile and install different
+flavours. See below for details.
+
## Distributions offering mustach package
### Alpine Linux
@@ -30,6 +41,13 @@ make
See http://pkgsrc.se/devel/mustach
+## Known projects using Mustach
+
+This [wiki page](https://gitlab.com/jobol/mustach/-/wikis/projects-using-mustach)
+lists the known project that are using mustach and that kindly told it.
+
+Don't hesitate to tell us if you are interested to be listed there.
+
## Using Mustach from sources
The file **mustach.h** is the main documentation. Look at it.
@@ -38,15 +56,25 @@ The current source files are:
- **mustach.c** core implementation of mustache in C
- **mustach.h** header file for core definitions
+- **mustach-wrap.c** generic wrapper of mustach for easier integration
+- **mustach-wrap.h** header file for using mustach-wrap
- **mustach-json-c.c** tiny json wrapper of mustach using [json-c](https://github.com/json-c/json-c)
-- **mustach-json-c.h** header file for using the tiny JSON wrapper
-- **mustach-tool.c** simple tool for applying template files to a JSON file
+- **mustach-json-c.h** header file for using the tiny json-c wrapper
+- **mustach-cjson.c** tiny json wrapper of mustach using [cJSON](https://github.com/DaveGamble/cJSON)
+- **mustach-cjson.h** header file for using the tiny cJSON wrapper
+- **mustach-jansson.c** tiny json wrapper of mustach using [jansson](https://www.digip.org/jansson/)
+- **mustach-jansson.h** header file for using the tiny jansson wrapper
+- **mustach-tool.c** simple tool for applying template files to one JSON file
-The file **mustach-json-c.c** is the main example of use of **mustach** core
-and it is also a practical implementation that can be used. It uses the library
-json-c. (NOTE for Mac OS: available through homebrew).
+The file **mustach-json-c.c** is the historical example of use of **mustach** and
+**mustach-wrap** core and it is also a practical implementation that can be used.
+It uses the library json-c. (NOTE for Mac OS: available through homebrew).
-HELP REQUESTED TO GIVE EXAMPLE BASED ON OTHER LIBRARIES (ex: janson, ...).
+Since version 1.0, the project also provide integration of other JSON libraries:
+**cJSON** and **jansson**.
+
+*If you integrate a new library with* **mustach**, *your contribution will be
+welcome here*.
The tool **mustach** is build using `make`, its usage is:
@@ -57,14 +85,14 @@ It then outputs the result of applying the templates files to the JSON file.
### Portability
Some system does not provide *open_memstream*. In that case, tell your
-preferred compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
+prefered compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**.
Example:
- gcc -DNO_OPEN_MEMSTREAM
+ CFLAGS=-DNO_OPEN_MEMSTREAM make
### Integration
-The file **mustach.h** is the main documentation. Look at it.
+The files **mustach.h** and **mustach-wrap.h** are the main documentation. Look at it.
The file **mustach-json-c.c** provides a good example of integration.
@@ -76,139 +104,211 @@ If you intend to use specific escaping and/or specific output, the callbacks
of the interface **mustach_itf** that you have to implement are:
`enter`, `next`, `leave`, `get` and `emit`.
-### Extensions
-
-By default, the current implementation provides the following extensions:
+### Compilation Using Make
-#### Explicit Substitution
+Building and installing can be done using make.
-This is a core extension implemented in file **mustach.c**.
+Example:
-In somecases the name of the key used for substitution begins with a
+ $ make tool=cjson libs=none PREFIX=/usr/local DESTDIR=/ install
+ $ make tool=jsonc libs=single PREFIX=/ DESTDIR=$HOME/.local install
+
+The makefile knows following switches (\*: default):
+
+ Switch name | Values | Description
+ --------------+---------+-----------------------------------------------
+ jsonc | (unset) | Auto detection of json-c
+ | no | Don't compile for json-c
+ | yes | Compile for json-c that must exist
+ --------------+---------+-----------------------------------------------
+ cjson | (unset) | Auto detection of cJSON
+ | no | Don't compile for cJSON
+ | yes | Compile for cJSON that must exist
+ --------------+---------+-----------------------------------------------
+ jansson | (unset) | Auto detection of jansson
+ | no | Don't compile for jansson
+ | yes | Compile for jansson that must exist
+ --------------+---------+-----------------------------------------------
+ tool | (unset) | Auto detection
+ | cjson | Use cjson library
+ | jsonc | Use jsonc library
+ | jansson | Use jansson library
+ | none | Don't compile the tool
+ --------------+---------+----------------------------------------------
+ libs | (unset) | Like 'all'
+ | all | Like 'single' AND 'split'
+ | single | Only libmustach.so
+ | split | All the possible libmustach-XXX.so ...
+ | none | No library is produced
+
+The libraries that can be produced are:
+
+ Library name | Content
+ --------------------+--------------------------------------------------------
+ libmustach-core | mustach.c mustach-wrap.c
+ libmustach-cjson | mustach.c mustach-wrap.c mustach-cjson.c
+ libmustach-jsonc | mustach.c mustach-wrap.c mustach-json-c.c
+ libmustach-jansson | mustach.c mustach-wrap.c mustach-jansson.c
+ libmustach | mustach.c mustach-wrap.c mustach-{cjson,json-c,jansson}.c
+
+There is no dependencies of a library to an other. This is intended and doesn't
+hurt today because the code is small.
+
+## Extensions
+
+The current implementation provides extensions to specifications of **mustache**.
+This extensions can be activated or deactivated using flags.
+
+Here is the summary.
+
+ Flag name | Description
+ -------------------------------+------------------------------------------------
+ Mustach_With_Colon | Explicit tag substition with colon
+ Mustach_With_EmptyTag | Empty Tag Allowed
+ -------------------------------+------------------------------------------------
+ Mustach_With_Equal | Value Testing Equality
+ Mustach_With_Compare | Value Comparing
+ Mustach_With_JsonPointer | Interpret JSON Pointers
+ Mustach_With_ObjectIter | Iteration On Objects
+ Mustach_With_EscFirstCmp | Escape First Compare
+ Mustach_With_ErrorUndefined | Error when a requested tag is undefined
+ -------------------------------+------------------------------------------------
+ Mustach_With_AllExtensions | Activate all known extensions
+ Mustach_With_NoExtensions | Disable any extension
+
+For the details, see below.
+
+### Explicit Tag Substitution With Colon (Mustach_With_Colon)
+
+In somecases the name of the key used for substition begins with a
character reserved for mustach: one of `#`, `^`, `/`, `&`, `{`, `>` and `=`.
+
This extension introduces the special character `:` to explicitly
tell mustach to just substitute the value. So `:` becomes a new special
character.
-#### Value Testing and Comparing
+This is a core extension implemented in file **mustach.c**.
+
+### Empty Tag Allowed (Mustach_With_EmptyTag)
-This are a tool extension implemented in file **mustach-json-c.c**.
+When an empty tag is found, instead of automatically raising the error
+MUSTACH\_ERROR\_EMPTY\_TAG pass it.
-These extensions allows you to test the value of the selected key.
-They allow to write `key=value` (matching test) or `key=!value`
+This is a core extension implemented in file **mustach.c**.
+
+### Value Testing Equality (Mustach_With_Equal)
+
+This extension allows you to test the value of the selected key.
+It allows to write `key=value` (matching test) or `key=!value`
(not matching test) in any query.
-The specific comparison extension also allows to compare if greater,
-lesser, etc.. than a value. It allows to write `key>value`.
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Value Comparing (Mustach_With_Compare)
+
+These extension extends the extension for testing equality to also
+compare values if greater or lesser.
+Its allows to write `key>value` (greater), `key>=value` (greater or equal),
+`key<value` (lesser) and `key<=value` (lesser or equal).
It the comparator sign appears in the first column it is ignored
as if it was escaped.
-#### Access to current value
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Interpret JSON Pointers (Mustach_With_JsonPointer)
-The value of the current field can be accessed using single dot like
-in `{{#key}}{{.}}{{/key}}` that applied to `{"key":3.14}` produces `3.14`
-and `{{#array}} {{.}}{{/array}}` applied to `{"array":[1,2]}` produces
-` 1 2`.
+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.
-#### Iteration on objects
+A special escaping is used for `=`, `<`, `>` signs when
+values comparisons are enabled: `~=` gives `=` in the key.
-Using the pattern `{{#X.*}}...{{/X.*}}` it is possible to iterate on
-fields of `X`. Example: `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on
-`{"s":{"a":1,"b":true}}` produces ` a:1 b:true`. Here the single star
-`{{*}}` is replaced by the iterated key and the single dot `{{.}}` is
-replaced by its value.
+This is a wrap extension implemented in file **mustach-wrap.c**.
-### Removing Extensions
+### Iteration On Objects (Mustach_With_ObjectIter)
-When compiling mustach.c or mustach-json-c.c,
-extensions can be removed by defining macros
-using option -D.
+With this extension, using the pattern `{{#X.*}}...{{/X.*}}`
+allows to iterate on fields of `X`.
+
+Example:
-The possible macros are of 3 categories, the global,
-the mustach core specific and the mustach-json-c example
-of implementation specific.
+- `{{s.*}} {{*}}:{{.}}{{/s.*}}` applied on `{"s":{"a":1,"b":true}}` produces ` a:1 b:true`
-#### Global macros
+Here the single star `{{*}}` is replaced by the iterated key
+and the single dot `{{.}}` is replaced by its value.
-- `NO_EXTENSION_FOR_MUSTACH`
+This is a wrap extension implemented in file **mustach-wrap.c**.
- This macro disables any current or future
- extensions for the core or the example.
+### Error when a requested tag is undefined (Mustach_With_ErrorUndefined)
-#### Macros for the core mustach engine (mustach.c)
+Report the error MUSTACH_ERROR_UNDEFINED_TAG when a requested tag
+is not defined.
-- `NO_COLON_EXTENSION_FOR_MUSTACH`
+This is a wrap extension implemented in file **mustach-wrap.c**.
- This macro remove the ability to use colon (:)
- as explicit command for variable substitution.
- This extension allows to have name starting
- with one of the mustach character `:#^/&{=>`
+### Access To Current Value
-- `NO_ALLOW_EMPTY_TAG`
+*this was an extension but is now always enforced*
- Generate the error MUSTACH_ERROR_EMPTY_TAG automatically
- when an empty tag is encountered.
+The value of the current field can be accessed using single dot.
-#### Macros for the implementation example (mustach-json-c.c)
+Examples:
-- `NO_EQUAL_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 check whether
- the actual value is equal to an expected value.
- This is useful in `{{#key=val}}` or `{{^key=val}}`
- with the corresponding `{{/key=val}}`.
- It can also be used in `{{key=val}}` but this
- doesn't seem to be useful.
+This is a wrap extension implemented in file **mustach-wrap.c**.
-- `NO_COMPARE_VALUE_EXTENSION_FOR_MUSTACH`
+### Partial Data First
- This macro allows the program to compare the actual
- value with an expected value. The comparison operators
- are `=`, `>`, `<`, `>=`, `<=`. The meaning of the
- comparison numeric or alphabetic depends on the type
- of the inspected value. Also the result of the comparison
- can be inverted if the value starts with `!`.
- Example of use: `{{key>=val}}`, or `{{#key>=val}}` and
- `{{^key>=val}}` with their matching `{{/key>=val}}`.
+*this was an extension but is now always enforced*
-- `NO_USE_VALUE_ESCAPE_FIRST_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 fordids automatic escaping of coparison
- sign appearing at first column.
+By default, the order of the search is (1) as a file,
+and if not found, (2) in the current json context.
-- `NO_JSON_POINTER_EXTENSION_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.
- This macro removes the possible use of JSON pointers.
- JSON pointers are defined in IETF RFC 6901.
- If not set, any key starting with "/" is a JSON pointer.
- This implies to use the colon to introduce keys.
- So `NO_COLON_EXTENSION_FOR_MUSTACH` implies
- `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`.
- A special escaping is used for `=`, `<`, `>` signs when
- values comparisons are enabled: `~=` gives `=` in the key.
+That option is useful to keep the compatibility with
+versions of *mustach* anteriors to 1.2.0.
+
+This is a wrap extension implemented in file **mustach-wrap.c**.
+
+### Escape First Compare
+
+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
-- `NO_OBJECT_ITERATION_FOR_MUSTACH`
+The extensions can no more be removed at compile time, use
+flags to select your required extension on need.
- Disable the object iteration extension. That extension allows
- to iterate over the keys of an object. The iteration on object
- is selected by using the selector `{{#key.*}}`. In the context
- of iterating over object keys, the single key `{{*}}` returns the
- key and `{{.}}` returns the value.
+### Name of functions
-- `NO_SINGLE_DOT_EXTENSION_FOR_MUSTACH`
+Names of functions were improved. Old names remain but are obsolete
+and legacy. Their removal in far future versions is possible.
- Disable access to current object value using single dot
- like in `{{.}}`.
+The table below summarize the changes.
-- `NO_INCLUDE_PARTIAL_FALLBACK`
+ 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.
diff --git a/src/templating/mustach-cjson.c b/src/templating/mustach-cjson.c
new file mode 100644
index 000000000..48b97e7df
--- /dev/null
+++ b/src/templating/mustach-cjson.c
@@ -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);
+}
+
diff --git a/src/templating/mustach-cjson.h b/src/templating/mustach-cjson.h
new file mode 100644
index 000000000..ae0d818cb
--- /dev/null
+++ b/src/templating/mustach-cjson.h
@@ -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
+
diff --git a/src/templating/mustach-jansson.c b/src/templating/mustach-jansson.c
index c65fe2b01..3ce0e0a84 100644
--- a/src/templating/mustach-jansson.c
+++ b/src/templating/mustach-jansson.c
@@ -1,429 +1,251 @@
/*
- Copyright (C) 2020 Taler Systems SA
-
- Original license:
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+ SPDX-License-Identifier: ISC
+*/
- http://www.apache.org/licenses/LICENSE-2.0
+#define _GNU_SOURCE
- 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 <stdio.h>
+#include <string.h>
-#include "platform.h"
+#include "mustach.h"
+#include "mustach-wrap.h"
#include "mustach-jansson.h"
-struct Context
-{
- /**
- * Context object.
- */
- json_t *cont;
-
- /**
- * Current object.
- */
- json_t *obj;
-
- /**
- * Opaque object iterator.
- */
- void *iter;
-
- /**
- * Current index when iterating over an array.
- */
- unsigned int index;
-
- /**
- * Count when iterating over an array.
- */
- unsigned int count;
-
- bool is_objiter;
+struct expl {
+ json_t *root;
+ json_t *selection;
+ int depth;
+ struct {
+ json_t *cont;
+ json_t *obj;
+ void *iter;
+ int is_objiter;
+ size_t index, count;
+ } stack[MUSTACH_MAX_DEPTH];
};
-enum Bang
+static int start(void *closure)
{
- BANG_NONE,
- BANG_I18N,
- BANG_STRINGIFY,
- BANG_AMOUNT_CURRENCY,
- BANG_AMOUNT_DECIMAL,
-};
-
-struct JanssonClosure
-{
- json_t *root;
- mustach_jansson_write_cb writecb;
- int depth;
-
- /**
- * Did the last find(..) call result in an iterable?
- */
- struct Context stack[MUSTACH_MAX_DEPTH];
-
- /**
- * The last object we found should be iterated over.
- */
- bool found_iter;
-
- /**
- * Last bang we found.
- */
- enum Bang found_bang;
-
- /**
- * Language for i18n lookups.
- */
- const char *lang;
-};
-
-
-static json_t *
-walk (json_t *obj, const char *path)
-{
- char *saveptr = NULL;
- char *sp = GNUNET_strdup (path);
- char *p = sp;
- while (true)
- {
- char *tok = strtok_r (p, ".", &saveptr);
- if (tok == NULL)
- break;
- obj = json_object_get (obj, tok);
- if (obj == NULL)
- break;
- p = NULL;
- }
- GNUNET_free (sp);
- return obj;
+ struct expl *e = closure;
+ e->depth = 0;
+ e->selection = json_null();
+ e->stack[0].cont = NULL;
+ e->stack[0].obj = e->root;
+ e->stack[0].index = 0;
+ e->stack[0].count = 1;
+ return MUSTACH_OK;
}
-
-static json_t *
-find (struct JanssonClosure *e, const char *name)
-{
- json_t *obj = NULL;
- char *path = GNUNET_strdup (name);
- char *bang;
-
- bang = strchr (path, '!');
-
- e->found_bang = BANG_NONE;
-
- if (NULL != bang)
- {
- *bang = 0;
- bang++;
-
- if (0 == strcmp (bang, "i18n"))
- e->found_bang = BANG_I18N;
- else if (0 == strcmp (bang, "stringify"))
- e->found_bang = BANG_STRINGIFY;
- else if (0 == strcmp (bang, "amount_decimal"))
- e->found_bang = BANG_AMOUNT_CURRENCY;
- else if (0 == strcmp (bang, "amount_currency"))
- e->found_bang = BANG_AMOUNT_DECIMAL;
- }
-
- if (BANG_I18N == e->found_bang && NULL != e->lang)
- {
- char *aug_path;
- GNUNET_asprintf (&aug_path, "%s_i18n.%s", path, e->lang);
- obj = walk (e->stack[e->depth].obj, aug_path);
- GNUNET_free (aug_path);
- }
-
- if (NULL == obj)
- {
- obj = walk (e->stack[e->depth].obj, path);
- }
-
- GNUNET_free (path);
-
- return obj;
+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
-start (void *closure)
-{
- struct JanssonClosure *e = closure;
- e->depth = 0;
- e->stack[0].cont = NULL;
- e->stack[0].obj = e->root;
- e->stack[0].index = 0;
- e->stack[0].count = 1;
- e->lang = json_string_value (json_object_get (e->root, "$language"));
- return MUSTACH_OK;
+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
-emituw (void *closure, const char *buffer, size_t size, int escape, FILE *file)
+static int subsel(void *closure, const char *name)
{
- struct JanssonClosure *e = closure;
- if (! escape)
- e->writecb (file, buffer, size);
- else
- do
- {
- switch (*buffer)
- {
- case '<':
- e->writecb (file, "&lt;", 4);
- break;
- case '>':
- e->writecb (file, "&gt;", 4);
- break;
- case '&':
- e->writecb (file, "&amp;", 5);
- break;
- default:
- e->writecb (file, buffer, 1);
- break;
- }
- buffer++;
- }
- while(--size);
- return MUSTACH_OK;
+ struct expl *e = 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
-enter (void *closure, const char *name)
+static int next(void *closure)
{
- struct JanssonClosure *e = closure;
- json_t *o = find (e, name);
- if (++e->depth >= MUSTACH_MAX_DEPTH)
- return MUSTACH_ERROR_TOO_DEEP;
-
- if (json_is_object (o))
- {
- if (e->found_iter)
- {
- void *iter = json_object_iter (o);
- if (NULL == iter)
- {
- e->depth--;
- return 0;
- }
- e->stack[e->depth].is_objiter = 1;
- e->stack[e->depth].iter = iter;
- e->stack[e->depth].obj = json_object_iter_value (iter);
- e->stack[e->depth].cont = o;
- }
- else
- {
- e->stack[e->depth].is_objiter = 0;
- e->stack[e->depth].obj = o;
- e->stack[e->depth].cont = o;
- }
- return 1;
- }
-
- if (json_is_array (o))
- {
- unsigned int size = json_array_size (o);
- if (size == 0)
- {
- e->depth--;
- return 0;
- }
- e->stack[e->depth].count = size;
- e->stack[e->depth].cont = o;
- e->stack[e->depth].obj = json_array_get (o, 0);
- e->stack[e->depth].index = 0;
- e->stack[e->depth].is_objiter = 0;
- return 1;
- }
-
- e->depth--;
- return 0;
-}
+ struct expl *e = closure;
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
-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;
-}
+ 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;
-static int
-leave (void *closure)
-{
- struct JanssonClosure *e = closure;
- if (e->depth <= 0)
- return MUSTACH_ERROR_CLOSING;
- e->depth--;
- return 0;
+ e->stack[e->depth].obj = json_array_get(e->stack[e->depth].cont, e->stack[e->depth].index);
+ return 1;
}
-
-static void
-freecb (void *v)
+static int leave(void *closure)
{
- free (v);
-}
+ struct expl *e = closure;
+ if (e->depth <= 0)
+ return MUSTACH_ERROR_CLOSING;
-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;
+ 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;
+}
-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
+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
};
-int
-fmustach_jansson (const char *template, json_t *root, FILE *file)
+int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- return fmustach (template, &itf, &e, file);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_file(template, length, &mustach_jansson_wrap_itf, &e, flags, file);
}
-
-int
-fdmustach_jansson (const char *template, json_t *root, int fd)
+int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- return fdmustach (template, &itf, &e, fd);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_fd(template, length, &mustach_jansson_wrap_itf, &e, flags, fd);
}
-
-int
-mustach_jansson (const char *template, json_t *root, char **result,
- size_t *size)
+int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- e.writecb = NULL;
- return mustach (template, &itf, &e, result, size);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_mem(template, length, &mustach_jansson_wrap_itf, &e, flags, result, size);
}
+int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure)
+{
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_write(template, length, &mustach_jansson_wrap_itf, &e, flags, writecb, closure);
+}
-int
-umustach_jansson (const char *template, json_t *root, mustach_jansson_write_cb
- writecb, void *closure)
+int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure)
{
- struct JanssonClosure e = { 0 };
- e.root = root;
- e.writecb = writecb;
- return fmustach (template, &itfuw, &e, closure);
+ struct expl e;
+ e.root = root;
+ return mustach_wrap_emit(template, length, &mustach_jansson_wrap_itf, &e, flags, emitcb, closure);
}
+
diff --git a/src/templating/mustach-jansson.h b/src/templating/mustach-jansson.h
index 8fe989fa5..8def948e0 100644
--- a/src/templating/mustach-jansson.h
+++ b/src/templating/mustach-jansson.h
@@ -1,60 +1,60 @@
/*
- Copyright (C) 2020 Taler Systems SA
-
- Original license:
- Author: José Bollo <jose.bollo@iot.bzh>
Author: José Bollo <jobol@nonadev.net>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#ifndef _mustach_jansson_h_included_
#define _mustach_jansson_h_included_
-#include "taler_json_lib.h"
-#include "mustach.h"
+/*
+ * mustach-jansson is intended to make integration of jansson
+ * library by providing integrated functions.
+ */
+
+#include <jansson.h>
+#include "mustach-wrap.h"
/**
- * fmustach_jansson - Renders the mustache 'template' in 'file' for 'root'.
+ * Wrap interface used internally by mustach jansson functions.
+ * Can be used for overriding behaviour.
+ */
+extern const struct mustach_wrap_itf mustach_jansson_wrap_itf;
+
+/**
+ * mustach_jansson_file - Renders the mustache 'template' in 'file' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
- * \@file: the file where to write the result
+ * @file: the file where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fmustach_jansson (const char *template, json_t *root, FILE *file);
+extern int mustach_jansson_file(const char *template, size_t length, json_t *root, int flags, FILE *file);
/**
- * fmustach_jansson - Renders the mustache 'template' in 'fd' for 'root'.
+ * mustach_jansson_fd - Renders the mustache 'template' in 'fd' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @fd: the file descriptor number where to write the result
*
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fdmustach_jansson (const char *template, json_t *root, int fd);
+extern int mustach_jansson_fd(const char *template, size_t length, json_t *root, int flags, int fd);
/**
- * fmustach_jansson - Renders the mustache 'template' in 'result' for 'root'.
+ * mustach_jansson_mem - Renders the mustache 'template' in 'result' for 'root'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @result: the pointer receiving the result when 0 is returned
* @size: the size of the returned result
@@ -62,13 +62,13 @@ extern int fdmustach_jansson (const char *template, json_t *root, int fd);
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int mustach_jansson (const char *template, json_t *root, char **result,
- size_t *size);
+extern int mustach_jansson_mem(const char *template, size_t length, json_t *root, int flags, char **result, size_t *size);
/**
- * umustach_jansson - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
+ * mustach_jansson_write - Renders the mustache 'template' for 'root' to custom writer 'writecb' with 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @root: the root json object to render
* @writecb: the function that write values
* @closure: the closure for the write function
@@ -76,9 +76,21 @@ extern int mustach_jansson (const char *template, json_t *root, char **result,
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-typedef int (*mustach_jansson_write_cb)(void *closure, const char *buffer,
- size_t size);
-extern int umustach_jansson (const char *template, json_t *root,
- mustach_jansson_write_cb writecb, void *closure);
+extern int mustach_jansson_write(const char *template, size_t length, json_t *root, int flags, mustach_write_cb_t *writecb, void *closure);
+
+/**
+ * mustach_jansson_emit - Renders the mustache 'template' for 'root' to custom emiter 'emitcb' with 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @root: the root json object to render
+ * @emitcb: the function that emit values
+ * @closure: the closure for the write function
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
+ */
+extern int mustach_jansson_emit(const char *template, size_t length, json_t *root, int flags, mustach_emit_cb_t *emitcb, void *closure);
#endif
+
diff --git a/src/templating/mustach-json-c.c b/src/templating/mustach-json-c.c
new file mode 100644
index 000000000..a21a113fb
--- /dev/null
+++ b/src/templating/mustach-json-c.c
@@ -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);
+}
+
+
diff --git a/src/templating/mustach-json-c.h b/src/templating/mustach-json-c.h
new file mode 100644
index 000000000..50846c6cb
--- /dev/null
+++ b/src/templating/mustach-json-c.h
@@ -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
diff --git a/src/templating/mustach-tool.c b/src/templating/mustach-tool.c
index 364e34a84..0c8f44070 100644
--- a/src/templating/mustach-tool.c
+++ b/src/templating/mustach-tool.c
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@@ -27,7 +16,7 @@
#include <string.h>
#include <libgen.h>
-#include "mustach-json-c.h"
+#include "mustach-wrap.h"
static const size_t BLOCKSIZE = 8192;
@@ -43,16 +32,39 @@ static const char *errors[] = {
"bad unescape tag",
"invalid interface",
"item not found",
- "partial not found"
+ "partial not found",
+ "undefined tag"
};
+static const char *errmsg = 0;
+static int flags = 0;
+static FILE *output = 0;
+
static void help(char *prog)
{
- printf("usage: %s json-file mustach-templates...\n", basename(prog));
+ char *name = basename(prog);
+#define STR_INDIR(x) #x
+#define STR(x) STR_INDIR(x)
+ printf("%s version %s\n", name, STR(VERSION));
+#undef STR
+#undef STR_INDIR
+ printf(
+ "\n"
+ "USAGE:\n"
+ " %s [FLAGS] <json-file> <mustach-templates...>\n"
+ "\n"
+ "FLAGS:\n"
+ " -h, --help Prints help information\n"
+ " -s, --strict Error when a tag is undefined\n"
+ "\n"
+ "ARGS: (if a file is -, read standard input)\n"
+ " <json-file> JSON file with input data\n"
+ " <mustach-templates...> Template files to instantiate\n",
+ name);
exit(0);
}
-static char *readfile(const char *filename)
+static char *readfile(const char *filename, size_t *length)
{
int f;
struct stat s;
@@ -106,50 +118,140 @@ static char *readfile(const char *filename)
} while(rc > 0);
close(f);
+ if (length != NULL)
+ *length = pos;
result[pos] = 0;
return result;
}
+static int load_json(const char *filename);
+static int process(const char *content, size_t length);
+static void close_json();
+
int main(int ac, char **av)
{
- struct json_object *o;
- char *t;
+ char *t, *f;
char *prog = *av;
int s;
+ size_t length;
(void)ac; /* unused */
+ flags = Mustach_With_AllExtensions;
+ output = stdout;
- if (*++av) {
+ for( ++av ; av[0] && av[0][0] == '-' && av[0][1] != 0 ; av++) {
if (!strcmp(*av, "-h") || !strcmp(*av, "--help"))
help(prog);
- if (av[0][0] == '-' && !av[0][1])
- o = json_object_from_fd(0);
- else
- o = json_object_from_file(av[0]);
-#if JSON_C_VERSION_NUM >= 0x000D00
- if (json_util_get_last_err() != NULL) {
- fprintf(stderr, "Bad json: %s (file %s)\n", json_util_get_last_err(), av[0]);
- exit(1);
- }
- else
-#endif
- if (o == NULL) {
- fprintf(stderr, "Aborted: null json (file %s)\n", av[0]);
+ if (!strcmp(*av, "-s") || !strcmp(*av, "--strict"))
+ flags |= Mustach_With_ErrorUndefined;
+ }
+ if (*av) {
+ f = (av[0][0] == '-' && !av[0][1]) ? "/dev/stdin" : av[0];
+ s = load_json(f);
+ if (s < 0) {
+ fprintf(stderr, "Can't load json file %s\n", av[0]);
+ if(errmsg)
+ fprintf(stderr, " reason: %s\n", errmsg);
exit(1);
}
while(*++av) {
- t = readfile(*av);
- s = fmustach_json_c(t, o, stdout);
- if (s != 0) {
+ t = readfile(*av, &length);
+ s = process(t, length);
+ free(t);
+ if (s != MUSTACH_OK) {
s = -s;
if (s < 1 || s >= (int)(sizeof errors / sizeof * errors))
s = 0;
fprintf(stderr, "Template error %s (file %s)\n", errors[s], *av);
}
- free(t);
}
- json_object_put(o);
+ close_json();
+ }
+ return 0;
+}
+
+#define MUSTACH_TOOL_JSON_C 1
+#define MUSTACH_TOOL_JANSSON 2
+#define MUSTACH_TOOL_CJSON 3
+
+#define TOOL MUSTACH_TOOL_JANSSON
+
+#if TOOL == MUSTACH_TOOL_JSON_C
+
+#include "mustach-json-c.h"
+
+static struct json_object *o;
+static int load_json(const char *filename)
+{
+ o = json_object_from_file(filename);
+#if JSON_C_VERSION_NUM >= 0x000D00
+ errmsg = json_util_get_last_err();
+ if (errmsg != NULL)
+ return -1;
+#endif
+ if (o == NULL) {
+ errmsg = "null json";
+ return -1;
+ }
+ return 0;
+}
+static int process(const char *content, size_t length)
+{
+ return mustach_json_c_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ json_object_put(o);
+}
+
+#elif TOOL == MUSTACH_TOOL_JANSSON
+
+#include "mustach-jansson.h"
+
+static json_t *o;
+static json_error_t e;
+static int load_json(const char *filename)
+{
+ o = json_load_file(filename, JSON_DECODE_ANY, &e);
+ if (o == NULL) {
+ errmsg = e.text;
+ return -1;
}
return 0;
}
+static int process(const char *content, size_t length)
+{
+ return mustach_jansson_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ json_decref(o);
+}
+
+#elif TOOL == MUSTACH_TOOL_CJSON
+#include "mustach-cjson.h"
+
+static cJSON *o;
+static int load_json(const char *filename)
+{
+ char *t;
+ size_t length;
+
+ t = readfile(filename, &length);
+ o = t ? cJSON_ParseWithLength(t, length) : NULL;
+ free(t);
+ return -!o;
+}
+static int process(const char *content, size_t length)
+{
+ return mustach_cJSON_file(content, length, o, flags, output);
+}
+static void close_json()
+{
+ cJSON_Delete(o);
+}
+
+#else
+#error "no defined json library"
+#endif
diff --git a/src/templating/mustach-wrap.c b/src/templating/mustach-wrap.c
new file mode 100644
index 000000000..75cc9d1f6
--- /dev/null
+++ b/src/templating/mustach-wrap.c
@@ -0,0 +1,456 @@
+/*
+ Author: José Bollo <jobol@nonadev.net>
+
+ https://gitlab.com/jobol/mustach
+
+ SPDX-License-Identifier: ISC
+*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+#include <malloc.h>
+#endif
+
+#include "mustach.h"
+#include "mustach-wrap.h"
+
+#if !defined(INCLUDE_PARTIAL_EXTENSION)
+# define INCLUDE_PARTIAL_EXTENSION ".mustache"
+#endif
+
+/* global hook for partials */
+int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
+
+/* internal structure for wrapping */
+struct wrap {
+ /* original interface */
+ const struct mustach_wrap_itf *itf;
+
+ /* original closure */
+ void *closure;
+
+ /* flags */
+ int flags;
+
+ /* emiter callback */
+ mustach_emit_cb_t *emitcb;
+
+ /* write callback */
+ mustach_write_cb_t *writecb;
+};
+
+/* length given by masking with 3 */
+enum comp {
+ C_no = 0,
+ C_eq = 1,
+ C_lt = 5,
+ C_le = 6,
+ C_gt = 9,
+ C_ge = 10
+};
+
+enum sel {
+ S_none = 0,
+ S_ok = 1,
+ S_objiter = 2,
+ S_ok_or_objiter = S_ok | S_objiter
+};
+
+static enum comp getcomp(char *head, int sflags)
+{
+ return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
+ : (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
+ : (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
+ : C_no;
+}
+
+static char *keyval(char *head, int sflags, enum comp *comp)
+{
+ char *w, car, escaped;
+ enum comp k;
+
+ k = C_no;
+ w = head;
+ car = *head;
+ escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
+ while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
+ && (getcomp(head + 1, sflags) != C_no);
+ if (!escaped)
+ *w++ = car;
+ head++;
+ car = *head;
+ }
+ *w = 0;
+ *comp = k;
+ return k == C_no ? NULL : &head[k & 3];
+}
+
+static char *getkey(char **head, int sflags)
+{
+ char *result, *iter, *write, car;
+
+ car = *(iter = *head);
+ if (!car)
+ result = NULL;
+ else {
+ result = write = iter;
+ if (sflags & Mustach_With_JsonPointer)
+ {
+ while (car && car != '/') {
+ if (car == '~')
+ switch (iter[1]) {
+ case '1': car = '/'; /*@fallthrough@*/
+ case '0': iter++;
+ }
+ *write++ = car;
+ car = *++iter;
+ }
+ *write = 0;
+ while (car == '/')
+ car = *++iter;
+ }
+ else
+ {
+ while (car && car != '.') {
+ if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
+ car = *++iter;
+ *write++ = car;
+ car = *++iter;
+ }
+ *write = 0;
+ while (car == '.')
+ car = *++iter;
+ }
+ *head = iter;
+ }
+ return result;
+}
+
+static enum sel sel(struct wrap *w, const char *name)
+{
+ enum sel result;
+ int i, j, sflags, scmp;
+ char *key, *value;
+ enum comp k;
+
+ /* make a local writeable copy */
+ size_t lenname = 1 + strlen(name);
+ char buffer[lenname];
+ char *copy = buffer;
+ memcpy(copy, name, lenname);
+
+ /* check if matches json pointer selection */
+ sflags = w->flags;
+ if (sflags & Mustach_With_JsonPointer) {
+ if (copy[0] == '/')
+ copy++;
+ else
+ sflags ^= Mustach_With_JsonPointer;
+ }
+
+ /* extract the value, translate the key and get the comparator */
+ if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
+ value = keyval(copy, sflags, &k);
+ else {
+ k = C_no;
+ value = NULL;
+ }
+
+ /* case of . alone if Mustach_With_SingleDot? */
+ if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
+ /* yes, select current */
+ result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
+ else
+ {
+ /* not the single dot, extract the first key */
+ key = getkey(&copy, sflags);
+ if (key == NULL)
+ return 0;
+
+ /* select the root item */
+ if (w->itf->sel(w->closure, key))
+ result = S_ok;
+ else if (key[0] == '*'
+ && !key[1]
+ && !value
+ && !*copy
+ && (w->flags & Mustach_With_ObjectIter)
+ && w->itf->sel(w->closure, NULL))
+ result = S_ok_or_objiter;
+ else
+ result = S_none;
+ if (result == S_ok) {
+ /* iterate the selection of sub items */
+ key = getkey(&copy, sflags);
+ while(result == S_ok && key) {
+ if (w->itf->subsel(w->closure, key))
+ /* nothing */;
+ else if (key[0] == '*'
+ && !key[1]
+ && !value
+ && !*copy
+ && (w->flags & Mustach_With_ObjectIter))
+ result = S_objiter;
+ else
+ result = S_none;
+ key = getkey(&copy, sflags);
+ }
+ }
+ }
+ /* should it be compared? */
+ if (result == S_ok && value) {
+ if (!w->itf->compare)
+ result = S_none;
+ else {
+ i = value[0] == '!';
+ scmp = w->itf->compare(w->closure, &value[i]);
+ switch (k) {
+ case C_eq: j = scmp == 0; break;
+ case C_lt: j = scmp < 0; break;
+ case C_le: j = scmp <= 0; break;
+ case C_gt: j = scmp > 0; break;
+ case C_ge: j = scmp >= 0; break;
+ default: j = i; break;
+ }
+ if (i == j)
+ result = S_none;
+ }
+ }
+ return result;
+}
+
+static int start(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
+}
+
+static void stop(void *closure, int status)
+{
+ struct wrap *w = closure;
+ if (w->itf->stop)
+ w->itf->stop(w->closure, status);
+}
+
+static int write(struct wrap *w, const char *buffer, size_t size, FILE *file)
+{
+ int r;
+
+ if (w->writecb)
+ r = w->writecb(file, buffer, size);
+ else
+ r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
+ return r;
+}
+
+static int emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
+{
+ struct wrap *w = closure;
+ int r;
+ size_t s, i;
+ char car;
+
+ if (w->emitcb)
+ r = w->emitcb(file, buffer, size, escape);
+ else if (!escape)
+ r = write(w, buffer, size, file);
+ else {
+ i = 0;
+ r = MUSTACH_OK;
+ while(i < size && r == MUSTACH_OK) {
+ s = i;
+ while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
+ i++;
+ if (i != s)
+ r = write(w, &buffer[s], i - s, file);
+ if (i < size && r == MUSTACH_OK) {
+ switch(car) {
+ case '<': r = write(w, "&lt;", 4, file); break;
+ case '>': r = write(w, "&gt;", 4, file); break;
+ case '&': r = write(w, "&amp;", 5, file); break;
+ case '"': r = write(w, "&quot;", 6, file); break;
+ }
+ i++;
+ }
+ }
+ }
+ return r;
+}
+
+static int enter(void *closure, const char *name)
+{
+ struct wrap *w = closure;
+ enum sel s = sel(w, name);
+ return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
+}
+
+static int next(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->next(w->closure);
+}
+
+static int leave(void *closure)
+{
+ struct wrap *w = closure;
+ return w->itf->leave(w->closure);
+}
+
+static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
+{
+ enum sel s = sel(w, name);
+ if (!(s & S_ok))
+ return 0;
+ return w->itf->get(w->closure, sbuf, s & S_objiter);
+}
+
+static int get(void *closure, const char *name, struct mustach_sbuf *sbuf)
+{
+ struct wrap *w = closure;
+ if (getoptional(w, name, sbuf) <= 0) {
+ if (w->flags & Mustach_With_ErrorUndefined)
+ return MUSTACH_ERROR_UNDEFINED_TAG;
+ sbuf->value = "";
+ }
+ return MUSTACH_OK;
+}
+
+static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
+{
+ static char extension[] = INCLUDE_PARTIAL_EXTENSION;
+ size_t s;
+ long pos;
+ FILE *file;
+ char *path, *buffer;
+
+ /* allocate path */
+ s = strlen(name);
+ path = malloc(s + sizeof extension);
+ if (path == NULL)
+ return MUSTACH_ERROR_SYSTEM;
+
+ /* try without extension first */
+ memcpy(path, name, s + 1);
+ file = fopen(path, "r");
+ if (file == NULL) {
+ memcpy(&path[s], extension, sizeof extension);
+ file = fopen(path, "r");
+ }
+ free(path);
+
+ /* if file opened */
+ if (file == NULL)
+ return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
+
+ /* compute file size */
+ if (fseek(file, 0, SEEK_END) >= 0
+ && (pos = ftell(file)) >= 0
+ && fseek(file, 0, SEEK_SET) >= 0) {
+ /* allocate value */
+ s = (size_t)pos;
+ buffer = malloc(s + 1);
+ if (buffer != NULL) {
+ /* read value */
+ if (1 == fread(buffer, s, 1, file)) {
+ /* force zero at end */
+ sbuf->value = buffer;
+ buffer[s] = 0;
+ sbuf->freecb = free;
+ fclose(file);
+ return MUSTACH_OK;
+ }
+ free(buffer);
+ }
+ }
+ fclose(file);
+ return MUSTACH_ERROR_SYSTEM;
+}
+
+static int partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
+{
+ struct wrap *w = closure;
+ int rc;
+ if (mustach_wrap_get_partial != NULL)
+ rc = mustach_wrap_get_partial(name, sbuf);
+ else if (w->flags & Mustach_With_PartialDataFirst) {
+ if (getoptional(w, name, sbuf) > 0)
+ rc = MUSTACH_OK;
+ else
+ rc = get_partial_from_file(name, sbuf);
+ }
+ else {
+ rc = get_partial_from_file(name, sbuf);
+ if (rc != MUSTACH_OK && getoptional(w, name, sbuf) > 0)
+ rc = MUSTACH_OK;
+ }
+ if (rc != MUSTACH_OK)
+ sbuf->value = "";
+ return MUSTACH_OK;
+}
+
+const struct mustach_itf mustach_wrap_itf = {
+ .start = start,
+ .put = NULL,
+ .enter = enter,
+ .next = next,
+ .leave = leave,
+ .partial = partial,
+ .get = get,
+ .emit = emit,
+ .stop = stop
+};
+
+static void wrap_init(struct wrap *wrap, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, mustach_write_cb_t *writecb)
+{
+ if (flags & Mustach_With_Compare)
+ flags |= Mustach_With_Equal;
+ wrap->closure = closure;
+ wrap->itf = itf;
+ wrap->flags = flags;
+ wrap->emitcb = emitcb;
+ wrap->writecb = writecb;
+}
+
+int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
+}
+
+int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
+}
+
+int mustach_wrap_mem(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, char **result, size_t *size)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, NULL);
+ return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
+}
+
+int mustach_wrap_write(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_write_cb_t *writecb, void *writeclosure)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, NULL, writecb);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
+}
+
+int mustach_wrap_emit(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, mustach_emit_cb_t *emitcb, void *emitclosure)
+{
+ struct wrap w;
+ wrap_init(&w, itf, closure, flags, emitcb, NULL);
+ return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
+}
+
diff --git a/src/templating/mustach-wrap.h b/src/templating/mustach-wrap.h
new file mode 100644
index 000000000..37e6ff6cf
--- /dev/null
+++ b/src/templating/mustach-wrap.h
@@ -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
+
diff --git a/src/templating/mustach.c b/src/templating/mustach.c
index caa80dcc9..548c38224 100644
--- a/src/templating/mustach.c
+++ b/src/templating/mustach.c
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#define _GNU_SOURCE
@@ -27,19 +16,9 @@
#ifdef _WIN32
#include <malloc.h>
#endif
-#ifdef __sun
-# include <alloca.h>
-#endif
#include "mustach.h"
-#if defined(NO_EXTENSION_FOR_MUSTACH)
-# undef NO_COLON_EXTENSION_FOR_MUSTACH
-# define NO_COLON_EXTENSION_FOR_MUSTACH
-# undef NO_ALLOW_EMPTY_TAG
-# define NO_ALLOW_EMPTY_TAG
-#endif
-
struct iwrap {
int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
void *closure; /* closure for: enter, next, leave, emit, get */
@@ -51,6 +30,13 @@ struct iwrap {
int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
void *closure_partial; /* closure for partial */
+ int flags;
+};
+
+struct prefix {
+ size_t len;
+ const char *start;
+ struct prefix *prefix;
};
#if !defined(NO_OPEN_MEMSTREAM)
@@ -135,6 +121,7 @@ static inline void sbuf_reset(struct mustach_sbuf *sbuf)
sbuf->value = NULL;
sbuf->freecb = NULL;
sbuf->closure = NULL;
+ sbuf->length = 0;
}
static inline void sbuf_release(struct mustach_sbuf *sbuf)
@@ -143,38 +130,47 @@ static inline void sbuf_release(struct mustach_sbuf *sbuf)
sbuf->releasecb(sbuf->value, sbuf->closure);
}
+static inline size_t sbuf_length(struct mustach_sbuf *sbuf)
+{
+ size_t length = sbuf->length;
+ if (length == 0 && sbuf->value != NULL)
+ length = strlen(sbuf->value);
+ return length;
+}
+
static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
{
- size_t i, j;
+ size_t i, j, r;
(void)closure; /* unused */
if (!escape)
- return fwrite(buffer, size, 1, file) != 1 ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
+ return fwrite(buffer, 1, size, file) != size ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
- i = 0;
+ r = i = 0;
while (i < size) {
j = i;
- while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
+ while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
j++;
if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
return MUSTACH_ERROR_SYSTEM;
if (j < size) {
switch(buffer[j++]) {
case '<':
- if (fwrite("&lt;", 4, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&lt;", 4, 1, file);
break;
case '>':
- if (fwrite("&gt;", 4, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&gt;", 4, 1, file);
break;
case '&':
- if (fwrite("&amp;", 5, 1, file) != 1)
- return MUSTACH_ERROR_SYSTEM;
+ r = fwrite("&amp;", 5, 1, file);
+ break;
+ case '"':
+ r = fwrite("&quot;", 6, 1, file);
break;
- default: break;
}
+ if (r != 1)
+ return MUSTACH_ERROR_SYSTEM;
}
i = j;
}
@@ -191,7 +187,7 @@ static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
sbuf_reset(&sbuf);
rc = iwrap->get(iwrap->closure, name, &sbuf);
if (rc >= 0) {
- length = strlen(sbuf.value);
+ length = sbuf_length(&sbuf);
if (length)
rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
sbuf_release(&sbuf);
@@ -220,55 +216,109 @@ static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *s
if (rc == 0) {
sbuf->value = result;
sbuf->freecb = free;
+ sbuf->length = size;
}
}
}
return rc;
}
-static int process(const char *template, struct iwrap *iwrap, FILE *file, const char *opstr, const char *clstr)
+static int emitprefix(struct iwrap *iwrap, FILE *file, struct prefix *prefix)
+{
+ if (prefix->prefix) {
+ int rc = emitprefix(iwrap, file, prefix->prefix);
+ if (rc < 0)
+ return rc;
+ }
+ return prefix->len ? iwrap->emit(iwrap->closure, prefix->start, prefix->len, 0, file) : 0;
+}
+
+static int process(const char *template, size_t length, struct iwrap *iwrap, FILE *file, struct prefix *prefix)
{
struct mustach_sbuf sbuf;
- char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
- const char *beg, *term;
- struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
+ char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
+ char name[MUSTACH_MAX_LENGTH + 1], c;
+ const char *beg, *term, *end;
+ struct { const char *name, *again; size_t length; unsigned enabled: 1, entered: 1; } stack[MUSTACH_MAX_DEPTH];
size_t oplen, cllen, len, l;
- int depth, rc, enabled;
-
- enabled = 1;
- oplen = strlen(opstr);
- cllen = strlen(clstr);
- depth = 0;
- for(;;) {
- beg = strstr(template, opstr);
- if (beg == NULL) {
- /* no more mustach */
- if (enabled && template[0]) {
- rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
- if (rc < 0)
- return rc;
+ int depth, rc, enabled, stdalone;
+ struct prefix pref;
+
+ pref.prefix = prefix;
+ end = template + (length ? length : strlen(template));
+ opstr[0] = opstr[1] = '{';
+ clstr[0] = clstr[1] = '}';
+ oplen = cllen = 2;
+ stdalone = enabled = 1;
+ depth = pref.len = 0;
+ for (;;) {
+ /* search next openning delimiter */
+ for (beg = template ; ; beg++) {
+ c = beg == end ? '\n' : *beg;
+ if (c == '\n') {
+ l = (beg != end) + (size_t)(beg - template);
+ if (stdalone != 2 && enabled) {
+ if (beg != template /* don't prefix empty lines */) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ }
+ rc = iwrap->emit(iwrap->closure, template, l, 0, file);
+ if (rc < 0)
+ return rc;
+ }
+ if (beg == end) /* no more mustach */
+ return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
+ template += l;
+ stdalone = 1;
+ pref.len = 0;
+ }
+ else if (!isspace(c)) {
+ if (stdalone == 2 && enabled) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ pref.len = 0;
+ stdalone = 0;
+ }
+ if (c == *opstr && end - beg >= (ssize_t)oplen) {
+ for (l = 1 ; l < oplen && beg[l] == opstr[l] ; l++);
+ if (l == oplen)
+ break;
+ }
+ stdalone = 0;
}
- return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
- }
- if (enabled && beg != template) {
- rc = iwrap->emit(iwrap->closure, template, (size_t)(beg - template), 0, file);
- if (rc < 0)
- return rc;
}
+
+ pref.start = template;
+ pref.len = enabled ? (size_t)(beg - template) : 0;
beg += oplen;
- term = strstr(beg, clstr);
- if (term == NULL)
- return MUSTACH_ERROR_UNEXPECTED_END;
+
+ /* search next closing delimiter */
+ for (term = beg ; ; term++) {
+ if (term == end)
+ return MUSTACH_ERROR_UNEXPECTED_END;
+ if (*term == *clstr && end - term >= (ssize_t)cllen) {
+ for (l = 1 ; l < cllen && term[l] == clstr[l] ; l++);
+ if (l == cllen)
+ break;
+ }
+ }
template = term + cllen;
len = (size_t)(term - beg);
c = *beg;
switch(c) {
+ case ':':
+ stdalone = 0;
+ if (iwrap->flags & Mustach_With_Colon)
+ goto exclude_first;
+ goto get_name;
case '!':
case '=':
break;
case '{':
- for (l = 0 ; clstr[l] == '}' ; l++);
- if (clstr[l]) {
+ for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
+ if (l < cllen) {
if (!len || beg[len-1] != '}')
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
len--;
@@ -279,55 +329,63 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
c = '&';
/*@fallthrough@*/
+ case '&':
+ stdalone = 0;
+ /*@fallthrough@*/
case '^':
case '#':
case '/':
- case '&':
case '>':
-#if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
- case ':':
-#endif
- beg++; len--;
+exclude_first:
+ beg++;
+ len--;
+ goto get_name;
default:
+ stdalone = 0;
+get_name:
while (len && isspace(beg[0])) { beg++; len--; }
while (len && isspace(beg[len-1])) len--;
-#if !defined(NO_ALLOW_EMPTY_TAG)
- if (len == 0)
+ if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
return MUSTACH_ERROR_EMPTY_TAG;
-#endif
if (len > MUSTACH_MAX_LENGTH)
return MUSTACH_ERROR_TAG_TOO_LONG;
memcpy(name, beg, len);
name[len] = 0;
break;
}
+ if (stdalone)
+ stdalone = 2;
+ else if (enabled) {
+ rc = emitprefix(iwrap, file, &pref);
+ if (rc < 0)
+ return rc;
+ pref.len = 0;
+ }
switch(c) {
case '!':
/* comment */
/* nothing to do */
break;
case '=':
- /* defines separators */
+ /* defines delimiters */
if (len < 5 || beg[len - 1] != '=')
return MUSTACH_ERROR_BAD_SEPARATORS;
beg++;
len -= 2;
+ while (len && isspace(*beg))
+ beg++, len--;
+ while (len && isspace(beg[len - 1]))
+ len--;
for (l = 0; l < len && !isspace(beg[l]) ; l++);
- if (l == len)
+ if (l == len || l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
oplen = l;
- tmp = alloca(oplen + 1);
- memcpy(tmp, beg, oplen);
- tmp[oplen] = 0;
- opstr = tmp;
+ memcpy(opstr, beg, l);
while (l < len && isspace(beg[l])) l++;
- if (l == len)
+ if (l == len || len - l > MUSTACH_MAX_DELIM_LENGTH)
return MUSTACH_ERROR_BAD_SEPARATORS;
cllen = len - l;
- tmp = alloca(cllen + 1);
- memcpy(tmp, beg + l, cllen);
- tmp[cllen] = 0;
- clstr = tmp;
+ memcpy(clstr, beg + l, cllen);
break;
case '^':
case '#':
@@ -343,8 +401,8 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
stack[depth].name = beg;
stack[depth].again = template;
stack[depth].length = len;
- stack[depth].enabled = enabled;
- stack[depth].entered = rc;
+ stack[depth].enabled = enabled != 0;
+ stack[depth].entered = rc != 0;
if ((c == '#') == (rc == 0))
enabled = 0;
depth++;
@@ -370,7 +428,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
sbuf_reset(&sbuf);
rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
if (rc >= 0) {
- rc = process(sbuf.value, iwrap, file, opstr, clstr);
+ rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, file, &pref);
sbuf_release(&sbuf);
}
if (rc < 0)
@@ -389,7 +447,7 @@ static int process(const char *template, struct iwrap *iwrap, FILE *file, const
}
}
-int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
+int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file)
{
int rc;
struct iwrap iwrap;
@@ -422,17 +480,18 @@ int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE
iwrap.next = itf->next;
iwrap.leave = itf->leave;
iwrap.get = itf->get;
+ iwrap.flags = flags;
/* process */
rc = itf->start ? itf->start(closure) : 0;
if (rc == 0)
- rc = process(template, &iwrap, file, "{{", "}}");
+ rc = process(template, length, &iwrap, file, 0);
if (itf->stop)
itf->stop(closure, rc);
return rc;
}
-int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
+int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd)
{
int rc;
FILE *file;
@@ -442,13 +501,13 @@ int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int
rc = MUSTACH_ERROR_SYSTEM;
errno = ENOMEM;
} else {
- rc = fmustach(template, itf, closure, file);
+ rc = mustach_file(template, length, itf, closure, flags, file);
fclose(file);
}
return rc;
}
-int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
+int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size)
{
int rc;
FILE *file;
@@ -461,7 +520,7 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
if (file == NULL)
rc = MUSTACH_ERROR_SYSTEM;
else {
- rc = fmustach(template, itf, closure, file);
+ rc = mustach_file(template, length, itf, closure, flags, file);
if (rc < 0)
memfile_abort(file, result, size);
else
@@ -470,3 +529,18 @@ int mustach(const char *template, struct mustach_itf *itf, void *closure, char *
return rc;
}
+int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file)
+{
+ return mustach_file(template, 0, itf, closure, Mustach_With_AllExtensions, file);
+}
+
+int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd)
+{
+ return mustach_fd(template, 0, itf, closure, Mustach_With_AllExtensions, fd);
+}
+
+int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size)
+{
+ return mustach_mem(template, 0, itf, closure, Mustach_With_AllExtensions, result, size);
+}
+
diff --git a/src/templating/mustach.h b/src/templating/mustach.h
index ad952275c..8c4a43f10 100644
--- a/src/templating/mustach.h
+++ b/src/templating/mustach.h
@@ -1,20 +1,9 @@
/*
Author: José Bollo <jobol@nonadev.net>
- Author: José Bollo <jose.bollo@iot.bzh>
https://gitlab.com/jobol/mustach
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ SPDX-License-Identifier: ISC
*/
#ifndef _mustach_h_included_
@@ -25,7 +14,7 @@ struct mustach_sbuf; /* see below */
/**
* Current version of mustach and its derivates
*/
-#define MUSTACH_VERSION 99
+#define MUSTACH_VERSION 102
#define MUSTACH_VERSION_MAJOR (MUSTACH_VERSION / 100)
#define MUSTACH_VERSION_MINOR (MUSTACH_VERSION % 100)
@@ -37,20 +26,59 @@ struct mustach_sbuf; /* see below */
/**
* Maximum length of tags in mustaches {{...}}
*/
-#define MUSTACH_MAX_LENGTH 1024
+#define MUSTACH_MAX_LENGTH 4096
+
+/**
+ * Maximum length of delimitors (2 normally but extended here)
+ */
+#define MUSTACH_MAX_DELIM_LENGTH 8
/**
- * mustach_itf - interface for callbacks
+ * Flags specific to mustach core
+ */
+#define Mustach_With_NoExtensions 0
+#define Mustach_With_Colon 1
+#define Mustach_With_EmptyTag 2
+#define Mustach_With_AllExtensions 3
+
+/*
+ * Definition of error codes returned by mustach
+ */
+#define MUSTACH_OK 0
+#define MUSTACH_ERROR_SYSTEM -1
+#define MUSTACH_ERROR_UNEXPECTED_END -2
+#define MUSTACH_ERROR_EMPTY_TAG -3
+#define MUSTACH_ERROR_TAG_TOO_LONG -4
+#define MUSTACH_ERROR_BAD_SEPARATORS -5
+#define MUSTACH_ERROR_TOO_DEEP -6
+#define MUSTACH_ERROR_CLOSING -7
+#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
+#define MUSTACH_ERROR_INVALID_ITF -9
+#define MUSTACH_ERROR_ITEM_NOT_FOUND -10
+#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
+#define MUSTACH_ERROR_UNDEFINED_TAG -12
+
+/*
+ * You can use definition below for user specific error
*
- * All of this function should return a negative value to stop
- * the mustache processing. The returned negative value will be
- * then returned to the caller of mustach as is.
+ * The macro MUSTACH_ERROR_USER is involutive so for any value
+ * value = MUSTACH_ERROR_USER(MUSTACH_ERROR_USER(value))
+ */
+#define MUSTACH_ERROR_USER_BASE -100
+#define MUSTACH_ERROR_USER(x) (MUSTACH_ERROR_USER_BASE-(x))
+#define MUSTACH_IS_ERROR_USER(x) (MUSTACH_ERROR_USER(x) >= 0)
+
+/**
+ * mustach_itf - pure abstract mustach - interface for callbacks
*
* The functions enter and next should return 0 or 1.
*
* All other functions should normally return MUSTACH_OK (zero).
- * If it returns a negative value, it means an error that stop
- * the process and that is reported to the caller.
+ *
+ * If any function returns a negative value, it means an error that
+ * stop the processing and that is reported to the caller. Mustach
+ * also has its own error codes. Using the macros MUSTACH_ERROR_USER
+ * and MUSTACH_IS_ERROR_USER could help to avoid clashes.
*
* @start: If defined (can be NULL), starts the mustach processing
* of the closure, called at the very beginning before any
@@ -92,18 +120,18 @@ struct mustach_sbuf; /* see below */
* the meaning of 'FILE *file' is abstract for mustach's process and
* then you can use 'FILE*file' pass any kind of pointer (including NULL)
* to the function 'fmustach'. An example of a such behaviour is given by
- * the implementation of 'umustach_json_c'.
+ * the implementation of 'mustach_json_c_write'.
*
* @get: If defined (can be NULL), returns in 'sbuf' the value of 'name'.
* As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
* the empty string. In that later case an implementation can
* return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
- * If NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
+ * If 'get' is NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
* is returned.
*
* @stop: If defined (can be NULL), stops the mustach processing
* of the closure, called at the very end after all mustach
- * processing occurerd. The status returned by the processing
+ * processing occurered. The status returned by the processing
* is passed to the stop.
*
* The array below summarize status of callbacks:
@@ -127,7 +155,7 @@ struct mustach_sbuf; /* see below */
*
* The DUCK case runs on one leg. 'get' is not used if 'partial' is defined
* but is used for 'partial' if 'partial' is NULL. Thus for clarity, do not use
- * it that way but define 'partial' and let 'get' NULL.
+ * it that way but define 'partial' and let 'get' be NULL.
*
* The DANGEROUS case is special: it allows abstract FILE if 'partial' is defined
* but forbids abstract FILE when 'partial' is NULL.
@@ -167,6 +195,9 @@ struct mustach_itf {
* Can be NULL.
*
* @closure: The closure to use for 'releasecb'.
+ *
+ * @length: Length of the value or zero if unknown and value null terminated.
+ * To return the empty string, let it to zero and let value to NULL.
*/
struct mustach_sbuf {
const char *value;
@@ -175,45 +206,84 @@ struct mustach_sbuf {
void (*releasecb)(const char *value, void *closure);
};
void *closure;
+ size_t length;
};
-/*
- * Definition of error codes returned by mustach
+/**
+ * mustach_file - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
+ *
+ * @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface to the functions that mustach calls
+ * @closure: the closure to pass to functions called
+ * @file: the file where to write the result
+ *
+ * Returns 0 in case of success, -1 with errno set in case of system error
+ * a other negative value in case of error.
*/
-#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))
+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 'file' for 'itf' and 'closure'.
+ * mustach_fd - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
* @itf: the interface to the functions that mustach calls
* @closure: the closure to pass to functions called
- * \@file: the file 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
* 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_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd);
/**
- * fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
+ * mustach_mem - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
*
* @template: the template string to instantiate
+ * @length: length of the template or zero if unknown and template null terminated
+ * @itf: the interface to the functions that mustach calls
+ * @closure: the closure to pass to functions called
+ * @result: the pointer receiving the result when 0 is returned
+ * @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_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
@@ -221,12 +291,14 @@ extern int fmustach(const char *template, struct mustach_itf *itf, void *closure
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd);
+DEPRECATED_MUSTACH(extern int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd));
/**
- * fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
+ * OBSOLETE use mustach_mem
*
- * @template: the template string to instantiate
+ * 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
@@ -235,7 +307,7 @@ extern int fdmustach(const char *template, struct mustach_itf *itf, void *closur
* Returns 0 in case of success, -1 with errno set in case of system error
* a other negative value in case of error.
*/
-extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size);
+DEPRECATED_MUSTACH(extern int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size));
#endif
diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c
index 4bd7c5fe7..9261bde79 100644
--- a/src/templating/templating_api.c
+++ b/src/templating/templating_api.c
@@ -180,10 +180,12 @@ TALER_TEMPLATING_fill (const char *tmpl,
int eno;
if (0 !=
- (eno = mustach_jansson (tmpl,
- (json_t *) root,
- (char **) result,
- result_size)))
+ (eno = mustach_jansson_mem (tmpl,
+ 0, /* length of tmpl */
+ (json_t *) root,
+ Mustach_With_NoExtensions,
+ (char **) result,
+ result_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template with error %d\n",
@@ -237,10 +239,12 @@ TALER_TEMPLATING_build (struct MHD_Connection *connection,
GNUNET_free (static_url);
}
if (0 !=
- (eno = mustach_jansson (tmpl,
- (json_t *) root,
- &body,
- &body_size)))
+ (eno = mustach_jansson_mem (tmpl,
+ 0,
+ (json_t *) root,
+ Mustach_With_NoExtensions,
+ &body,
+ &body_size)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"mustach failed on template `%s' with error %d\n",