gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated (0dd0fff1 -> 7899bc56)


From: gnunet
Subject: [taler-exchange] branch master updated (0dd0fff1 -> 7899bc56)
Date: Thu, 11 May 2023 01:18:27 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a change to branch master
in repository exchange.

    from 0dd0fff1 -typo
     new ec8ad2e3 update mustach library
     new 7899bc56 externalize persona JSON conversion logic, expand with file 
download

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/kyclogic/Makefile.am                           |   6 +-
 src/kyclogic/kyclogic-persona.conf                 |   4 +
 src/kyclogic/plugin_kyclogic_persona.c             | 374 ++++++-------
 .../taler-exchange-kyc-persona-converter.sh        |  54 ++
 src/templating/.gitignore                          |   1 +
 src/templating/AUTHORS                             |   9 +
 src/templating/LICENSE-2.0.txt                     | 202 -------
 src/templating/LICENSE.txt                         |  14 +
 src/templating/Makefile.am                         |  10 +
 src/templating/README.md                           | 306 +++++++----
 src/templating/mustach-cjson.c                     | 239 +++++++++
 src/templating/mustach-cjson.h                     |  96 ++++
 src/templating/mustach-jansson.c                   | 580 +++++++--------------
 src/templating/mustach-jansson.h                   |  72 +--
 src/templating/mustach-json-c.c                    | 267 ++++++++++
 src/templating/mustach-json-c.h                    | 160 ++++++
 src/templating/mustach-tool.c                      | 176 +++++--
 src/templating/mustach-wrap.c                      | 456 ++++++++++++++++
 src/templating/mustach-wrap.h                      | 234 +++++++++
 src/templating/mustach.c                           | 256 +++++----
 src/templating/mustach.h                           | 172 ++++--
 src/templating/templating_api.c                    |  20 +-
 src/util/.gitignore                                |   1 +
 23 files changed, 2605 insertions(+), 1104 deletions(-)
 create mode 100755 src/kyclogic/taler-exchange-kyc-persona-converter.sh
 delete mode 100644 src/templating/LICENSE-2.0.txt
 create mode 100644 src/templating/LICENSE.txt
 create mode 100644 src/templating/mustach-cjson.c
 create mode 100644 src/templating/mustach-cjson.h
 create mode 100644 src/templating/mustach-json-c.c
 create mode 100644 src/templating/mustach-json-c.h
 create mode 100644 src/templating/mustach-wrap.c
 create mode 100644 src/templating/mustach-wrap.h

diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am
index 858331f3..20430a4e 100644
--- a/src/kyclogic/Makefile.am
+++ b/src/kyclogic/Makefile.am
@@ -16,7 +16,11 @@ pkgcfg_DATA = \
 
 EXTRA_DIST = \
   $(pkgcfg_DATA) \
-  sample.conf
+  sample.conf \
+  persona-sample-reply.json
+
+bin_SCRIPTS = \
+  taler-exchange-kyc-persona-converter.sh
 
 lib_LTLIBRARIES = \
   libtalerkyclogic.la
diff --git a/src/kyclogic/kyclogic-persona.conf 
b/src/kyclogic/kyclogic-persona.conf
index 7f02bf49..2d52a9ee 100644
--- a/src/kyclogic/kyclogic-persona.conf
+++ b/src/kyclogic/kyclogic-persona.conf
@@ -29,6 +29,10 @@ KYC_PERSONA_SUBDOMAIN = taler
 # Authentication token to use.
 KYC_PERSONA_AUTH_TOKEN = persona_sandbox_42
 
+# Program that converts Persona KYC data into the
+# GNU Taler format.
+KYC_PERSONA_CONVERTER_HELPER = taler-exchange-kyc-persona-converter.sh
+
 # Form to use.
 KYC_PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
 
diff --git a/src/kyclogic/plugin_kyclogic_persona.c 
b/src/kyclogic/plugin_kyclogic_persona.c
index 4f01ae40..35ab9ded 100644
--- a/src/kyclogic/plugin_kyclogic_persona.c
+++ b/src/kyclogic/plugin_kyclogic_persona.c
@@ -111,6 +111,12 @@ struct TALER_KYCLOGIC_ProviderDetails
    */
   char *subdomain;
 
+  /**
+   * Name of the program we use to convert outputs
+   * from Persona into our JSON inputs.
+   */
+  char *conversion_binary;
+
   /**
    * Where to redirect the client upon completion.
    */
@@ -230,6 +236,12 @@ struct TALER_KYCLOGIC_ProofHandle
    */
   char *url;
 
+  /**
+   * Handle to an external process that converts the
+   * Persona response to our internal format.
+   */
+  struct TALER_JSON_ExternalConversion *ec;
+
   /**
    * Hash of the payto:// URI we are checking the KYC for.
    */
@@ -246,6 +258,11 @@ struct TALER_KYCLOGIC_ProofHandle
    */
   char *provider_user_id;
 
+  /**
+   * Account ID from the service.
+   */
+  char *account_id;
+
   /**
    * Inquiry ID at the provider.
    */
@@ -294,6 +311,11 @@ struct TALER_KYCLOGIC_WebhookHandle
    */
   char *inquiry_id;
 
+  /**
+   * Account ID from the service.
+   */
+  char *account_id;
+
   /**
    * URL of the cURL request.
    */
@@ -315,6 +337,12 @@ struct TALER_KYCLOGIC_WebhookHandle
    */
   const char *template_id;
 
+  /**
+   * Handle to an external process that converts the
+   * Persona response to our internal format.
+   */
+  struct TALER_JSON_ExternalConversion *ec;
+
   /**
    * Our account ID.
    */
@@ -344,6 +372,7 @@ persona_unload_configuration (struct 
TALER_KYCLOGIC_ProviderDetails *pd)
   GNUNET_free (pd->auth_token);
   GNUNET_free (pd->template_id);
   GNUNET_free (pd->subdomain);
+  GNUNET_free (pd->conversion_binary);
   GNUNET_free (pd->salt);
   GNUNET_free (pd->section);
   GNUNET_free (pd->post_kyc_redirect_url);
@@ -418,6 +447,18 @@ persona_load_configuration (void *cls,
     persona_unload_configuration (pd);
     return NULL;
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (ps->cfg,
+                                             provider_section_name,
+                                             "KYC_PERSONA_CONVERTER_HELPER",
+                                             &pd->conversion_binary))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               provider_section_name,
+                               "KYC_PERSONA_CONVERTER_HELPER");
+    persona_unload_configuration (pd);
+    return NULL;
+  }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
                                              provider_section_name,
@@ -838,8 +879,14 @@ persona_proof_cancel (struct TALER_KYCLOGIC_ProofHandle 
*ph)
     GNUNET_CURL_job_cancel (ph->job);
     ph->job = NULL;
   }
+  if (NULL != ph->ec)
+  {
+    TALER_JSON_external_conversion_stop (ph->ec);
+    ph->ec = NULL;
+  }
   GNUNET_free (ph->url);
   GNUNET_free (ph->provider_user_id);
+  GNUNET_free (ph->account_id);
   GNUNET_free (ph->inquiry_id);
   GNUNET_free (ph);
 }
@@ -922,161 +969,6 @@ proof_reply_error (struct TALER_KYCLOGIC_ProofHandle *ph,
 }
 
 
-/**
- * Convert KYC attribute data from Persona response.
- *
- * @param attr json array with Persona attribute data
- * @return KYC attribute data
- */
-static json_t *
-convert_attributes (const json_t *attr)
-{
-  const char *country_code = NULL;
-  const char *name_first = NULL;
-  const char *name_middle = NULL;
-  const char *name_last = NULL;
-  const char *address_street_1 = NULL;
-  const char *address_street_2 = NULL;
-  const char *address_city = NULL;
-  const char *address_postal_code = NULL;
-  const char *birthdate = NULL;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("country-code",
-                               &country_code),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("name-first",
-                               &name_first),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("name-middle",
-                               &name_middle),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("name-last",
-                               &name_last),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("address-street-1",
-                               &address_street_1),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("address-street-2",
-                               &address_street_2),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("address-city",
-                               &address_city),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("address-postal-code",
-                               &address_postal_code),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("birthdate",
-                               &birthdate),
-      NULL),
-    GNUNET_JSON_spec_end ()
-  };
-  json_t *ret;
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (attr,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break (0);
-    json_dumpf (attr,
-                stderr,
-                JSON_INDENT (2));
-    return NULL;
-  }
-  {
-    char *name = NULL;
-    char *street = NULL;
-    char *city = NULL;
-
-    if ( (NULL != name_last) ||
-         (NULL != name_first) ||
-         (NULL != name_middle) )
-    {
-      GNUNET_asprintf (&name,
-                       "%s, %s %s",
-                       (NULL != name_last)
-                       ? name_last
-                       : "",
-                       (NULL != name_first)
-                       ? name_first
-                       : "",
-                       (NULL != name_middle)
-                       ? name_middle
-                       : "");
-    }
-    if ( (NULL != address_city) ||
-         (NULL != address_postal_code) )
-    {
-      GNUNET_asprintf (&city,
-                       "%s%s%s %s",
-                       (NULL != country_code)
-                       ? country_code
-                       : "",
-                       (NULL != country_code)
-                       ? "-"
-                       : "",
-                       (NULL != address_postal_code)
-                       ? address_postal_code
-                       : "",
-                       (NULL != address_city)
-                       ? address_city
-                       : "");
-    }
-    if ( (NULL != address_street_1) ||
-         (NULL != address_street_2) )
-    {
-      GNUNET_asprintf (&street,
-                       "%s%s%s",
-                       (NULL != address_street_1)
-                       ? address_street_1
-                       : "",
-                       ( (NULL != address_street_1) &&
-                         (NULL != address_street_2) )
-                       ? "\n"
-                       : "",
-                       (NULL != address_street_2)
-                       ? address_street_2
-                       : "");
-    }
-    ret = GNUNET_JSON_PACK (
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string (
-          TALER_ATTRIBUTE_BIRTHDATE,
-          birthdate)),
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string (
-          TALER_ATTRIBUTE_FULL_NAME,
-          name)),
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string (
-          TALER_ATTRIBUTE_ADDRESS_STREET,
-          street)),
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string (
-          TALER_ATTRIBUTE_ADDRESS_CITY,
-          city)),
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string (
-          TALER_ATTRIBUTE_RESIDENCES,
-          country_code))
-      );
-    GNUNET_free (street);
-    GNUNET_free (city);
-    GNUNET_free (name);
-  }
-  return ret;
-}
-
-
 /**
  * Return a response for the @a ph request indicating a
  * protocol violation by the Persona server.
@@ -1115,6 +1007,86 @@ return_invalid_response (struct 
TALER_KYCLOGIC_ProofHandle *ph,
 }
 
 
+/**
+ * Start the external conversion helper.
+ *
+ * @param pd configuration details
+ * @param attr attributes to give to the helper
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle for the helper
+ */
+static struct TALER_JSON_ExternalConversion *
+start_conversion (const struct TALER_KYCLOGIC_ProviderDetails *pd,
+                  const json_t *attr,
+                  TALER_JSON_JsonCallback cb,
+                  void *cb_cls)
+{
+  return TALER_JSON_external_conversion_start (
+    attr,
+    cb,
+    cb_cls,
+    pd->conversion_binary,
+    pd->conversion_binary,
+    "-a",
+    pd->auth_token,
+    NULL
+    );
+}
+
+
+/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure with a `struct TALER_KYCLOGIC_ProofHandle *`
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param result some JSON result, NULL if we failed to get an JSON output
+ */
+static void
+proof_post_conversion_cb (void *cls,
+                          enum GNUNET_OS_ProcessStatusType status_type,
+                          unsigned long code,
+                          const json_t *attr)
+{
+  struct TALER_KYCLOGIC_ProofHandle *ph = cls;
+  struct MHD_Response *resp;
+  struct GNUNET_TIME_Absolute expiration;
+
+  ph->ec = NULL;
+  if ( (NULL == attr) ||
+       (0 != code) )
+  {
+    GNUNET_break_op (0);
+    return_invalid_response (ph,
+                             MHD_HTTP_OK,
+                             ph->inquiry_id,
+                             "converter",
+                             NULL);
+    persona_proof_cancel (ph);
+    return;
+  }
+  expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
+  resp = MHD_create_response_from_buffer (0,
+                                          "",
+                                          MHD_RESPMEM_PERSISTENT);
+  GNUNET_break (MHD_YES ==
+                MHD_add_response_header (resp,
+                                         MHD_HTTP_HEADER_LOCATION,
+                                         ph->pd->post_kyc_redirect_url));
+  TALER_MHD_add_global_headers (resp);
+  ph->cb (ph->cb_cls,
+          TALER_KYCLOGIC_STATUS_SUCCESS,
+          ph->account_id,
+          ph->inquiry_id,
+          expiration,
+          attr,
+          MHD_HTTP_SEE_OTHER,
+          resp);
+  persona_proof_cancel (ph);
+}
+
+
 /**
  * Function called when we're done processing the
  * HTTP "/api/v1/inquiries/{inquiry-id}" request.
@@ -1283,46 +1255,15 @@ handle_proof_finished (void *cls,
                                    data);
           break;
         }
-
-        {
-          struct MHD_Response *resp;
-          struct GNUNET_TIME_Absolute expiration;
-          json_t *attr;
-
-          attr = convert_attributes (attributes);
-          if (NULL == attr)
-          {
-            GNUNET_break_op (0);
-            return_invalid_response (ph,
-                                     response_code,
-                                     inquiry_id,
-                                     "data-relationships-account-data-id",
-                                     data);
-            break;
-          }
-          expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
-          resp = MHD_create_response_from_buffer (0,
-                                                  "",
-                                                  MHD_RESPMEM_PERSISTENT);
-          GNUNET_break (MHD_YES ==
-                        MHD_add_response_header (resp,
-                                                 MHD_HTTP_HEADER_LOCATION,
-                                                 
ph->pd->post_kyc_redirect_url));
-          TALER_MHD_add_global_headers (resp);
-          ph->cb (ph->cb_cls,
-                  TALER_KYCLOGIC_STATUS_SUCCESS,
-                  account_id,
-                  inquiry_id,
-                  expiration,
-                  attr,
-                  MHD_HTTP_SEE_OTHER,
-                  resp);
-          json_decref (attr);
-        }
+        ph->account_id = GNUNET_strdup (account_id);
+        ph->ec = start_conversion (ph->pd,
+                                   j,
+                                   &proof_post_conversion_cb,
+                                   ph);
         GNUNET_JSON_parse_free (ispec);
       }
       GNUNET_JSON_parse_free (spec);
-      break;
+      return; /* continued in proof_post_conversion_cb */
     }
   case MHD_HTTP_BAD_REQUEST:
   case MHD_HTTP_NOT_FOUND:
@@ -1580,6 +1521,12 @@ persona_webhook_cancel (struct 
TALER_KYCLOGIC_WebhookHandle *wh)
     GNUNET_CURL_job_cancel (wh->job);
     wh->job = NULL;
   }
+  if (NULL != wh->ec)
+  {
+    TALER_JSON_external_conversion_stop (wh->ec);
+    wh->ec = NULL;
+  }
+  GNUNET_free (wh->account_id);
   GNUNET_free (wh->inquiry_id);
   GNUNET_free (wh->url);
   GNUNET_free (wh);
@@ -1650,6 +1597,32 @@ webhook_reply_error (struct TALER_KYCLOGIC_WebhookHandle 
*wh,
 }
 
 
+/**
+ * Type of a callback that receives a JSON @a result.
+ *
+ * @param cls closure with a `struct TALER_KYCLOGIC_WebhookHandle *`
+ * @param status_type how did the process die
+ * @param code termination status code from the process
+ * @param result some JSON result, NULL if we failed to get an JSON output
+ */
+static void
+webhook_post_conversion_cb (void *cls,
+                            enum GNUNET_OS_ProcessStatusType status_type,
+                            unsigned long code,
+                            const json_t *attr)
+{
+  struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
+
+  wh->ec = NULL;
+  webhook_generic_reply (wh,
+                         TALER_KYCLOGIC_STATUS_SUCCESS,
+                         wh->account_id,
+                         wh->inquiry_id,
+                         attr,
+                         MHD_HTTP_OK);
+}
+
+
 /**
  * Function called when we're done processing the
  * HTTP "/api/v1/inquiries/{inquiry_id}" request.
@@ -1723,7 +1696,6 @@ handle_webhook_finished (void *cls,
             NULL),
           GNUNET_JSON_spec_end ()
         };
-        json_t *attr;
 
         if (GNUNET_OK !=
             GNUNET_JSON_parse (attributes,
@@ -1807,19 +1779,15 @@ handle_webhook_finished (void *cls,
                                MHD_HTTP_BAD_GATEWAY);
           break;
         }
-
-        attr = convert_attributes (attributes);
-        webhook_generic_reply (wh,
-                               TALER_KYCLOGIC_STATUS_SUCCESS,
-                               account_id,
-                               inquiry_id,
-                               attr,
-                               MHD_HTTP_OK);
-        json_decref (attr);
+        wh->account_id = GNUNET_strdup (account_id);
+        wh->ec = start_conversion (wh->pd,
+                                   j,
+                                   &webhook_post_conversion_cb,
+                                   wh);
         GNUNET_JSON_parse_free (ispec);
       }
       GNUNET_JSON_parse_free (spec);
-      break;
+      return; /* continued in webhook_post_conversion_cb */
     }
   case MHD_HTTP_BAD_REQUEST:
   case MHD_HTTP_NOT_FOUND:
diff --git a/src/kyclogic/taler-exchange-kyc-persona-converter.sh 
b/src/kyclogic/taler-exchange-kyc-persona-converter.sh
new file mode 100755
index 00000000..a5d4d03a
--- /dev/null
+++ b/src/kyclogic/taler-exchange-kyc-persona-converter.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+# This file is in the public domain.
+#
+# This code converts (some of) the JSON output from Persona into the GNU Taler
+# specific KYC attribute data (again in JSON format).  We may need to download
+# and inline file data in the process, for authorization pass "-a" with the
+# respective bearer token.
+#
+
+# Die if anything goes wrong.
+set -eu
+
+# Parse command-line options
+while getopts ':a:' OPTION; do
+    case "$OPTION" in
+        a)
+            TOKEN="$OPTARG"
+            ;;
+        ?)
+        echo "Unrecognized command line option"
+        exit 1
+        ;;
+    esac
+done
+
+
+# First, extract everything from stdin.
+J=$(jq 
'{"first":.data.attributes."name-first","middle":.data.attributes."name-middle","last":.data.attributes."name-last","cc":.data.attributes.fields."address-country-code".value,"birthdate":.data.attributes.birthdate,"city":.data.attributes."address-city","postcode":.data.attributes."address-postal-code","street-1":.data.attributes."address-street-1","street-2":.data.attributes."address-street-2","address-subdivision":.data.attributes."address-subdivision","identification-number":.dat
 [...]
+
+
+# Next, combine some fields into larger values.
+FULLNAME=$(echo "$J" | jq -r '[.first,.middle,.last]|join(" ")')
+STREET=$(echo $J | jq -r '[."street-1",."street-2"]|join(" ")')
+CITY=$(echo $J | jq -r '[.postcode,.city,."address-subdivision,.cc"]|join(" 
")')
+
+# Download and base32-encode the photo
+PHOTO_URL=$(echo "$J" | jq -r '.photo')
+PHOTO_FILE=$(mktemp -t tmp.XXXXXXXXXX)
+if [ -z "${TOKEN:-}" ]
+then
+    wget -q --output-document=- "$PHOTO_URL" | gnunet-base32 > ${PHOTO_FILE}
+else
+    wget -q --output-document=- --header "Authorization: Bearer $TOKEN" 
"$PHOTO_URL" | gnunet-base32  > ${PHOTO_FILE}
+fi
+
+# Combine into final result.
+echo "$J" | jq \
+   --arg full_name "${FULLNAME}" \
+   --arg street "${STREET}" \
+   --arg city "${CITY}" \
+   --rawfile photo "${PHOTO_FILE}" \
+   
'{$full_name,$street,$city,"birthdate":.birthdate,"residences":.cc,"identification_number":."identification-number",$photo}'
+
+exit 0
diff --git a/src/templating/.gitignore b/src/templating/.gitignore
index b2bf6ef9..ac9c37f4 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 2fcc6043..b440c24e 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 d6456956..00000000
--- 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 00000000..495aeefd
--- /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 2501151a..fcdc22cd 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 a6df19f6..6699b0e8 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 00000000..48b97e7d
--- /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 00000000..ae0d818c
--- /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 c65fe2b0..3ce0e0a8 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 8fe989fa..8def948e 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 00000000..a21a113f
--- /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 00000000..50846c6c
--- /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 364e34a8..0c8f4407 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 00000000..75cc9d1f
--- /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 00000000..37e6ff6c
--- /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 caa80dcc..548c3822 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 ad952275..8c4a43f1 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 4bd7c5fe..9261bde7 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",
diff --git a/src/util/.gitignore b/src/util/.gitignore
index c5f8c76d..d79786ec 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -9,3 +9,4 @@ test_helper_cs
 test_helper_cs_home/
 test_helper_eddsa
 test_helper_eddsa_home/
+test_conversion

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]