LCOV - code coverage report
Current view: top level - templating - mustach.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 130 244 53.3 %
Date: 2022-08-25 06:15:09 Functions: 9 12 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  Author: José Bollo <jobol@nonadev.net>
       3             :  Author: José Bollo <jose.bollo@iot.bzh>
       4             : 
       5             :  https://gitlab.com/jobol/mustach
       6             : 
       7             :  Licensed under the Apache License, Version 2.0 (the "License");
       8             :  you may not use this file except in compliance with the License.
       9             :  You may obtain a copy of the License at
      10             : 
      11             :      http://www.apache.org/licenses/LICENSE-2.0
      12             : 
      13             :  Unless required by applicable law or agreed to in writing, software
      14             :  distributed under the License is distributed on an "AS IS" BASIS,
      15             :  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16             :  See the License for the specific language governing permissions and
      17             :  limitations under the License.
      18             : */
      19             : 
      20             : #define _GNU_SOURCE
      21             : 
      22             : #include <stdlib.h>
      23             : #include <stdio.h>
      24             : #include <string.h>
      25             : #include <errno.h>
      26             : #include <ctype.h>
      27             : #ifdef _WIN32
      28             : #include <malloc.h>
      29             : #endif
      30             : #ifdef __sun
      31             : # include <alloca.h>
      32             : #endif
      33             : 
      34             : #include "mustach.h"
      35             : 
      36             : #if defined(NO_EXTENSION_FOR_MUSTACH)
      37             : # undef  NO_COLON_EXTENSION_FOR_MUSTACH
      38             : # define NO_COLON_EXTENSION_FOR_MUSTACH
      39             : # undef  NO_ALLOW_EMPTY_TAG
      40             : # define NO_ALLOW_EMPTY_TAG
      41             : #endif
      42             : 
      43             : struct iwrap {
      44             :         int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
      45             :         void *closure; /* closure for: enter, next, leave, emit, get */
      46             :         int (*put)(void *closure, const char *name, int escape, FILE *file);
      47             :         void *closure_put; /* closure for put */
      48             :         int (*enter)(void *closure, const char *name);
      49             :         int (*next)(void *closure);
      50             :         int (*leave)(void *closure);
      51             :         int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
      52             :         int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
      53             :         void *closure_partial; /* closure for partial */
      54             : };
      55             : 
      56             : #if !defined(NO_OPEN_MEMSTREAM)
      57          12 : static FILE *memfile_open(char **buffer, size_t *size)
      58             : {
      59          12 :         return open_memstream(buffer, size);
      60             : }
      61           0 : static void memfile_abort(FILE *file, char **buffer, size_t *size)
      62             : {
      63           0 :         fclose(file);
      64           0 :         free(*buffer);
      65           0 :         *buffer = NULL;
      66           0 :         *size = 0;
      67           0 : }
      68          12 : static int memfile_close(FILE *file, char **buffer, size_t *size)
      69             : {
      70             :         int rc;
      71             : 
      72             :         /* adds terminating null */
      73          12 :         rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
      74          12 :         fclose(file);
      75          12 :         if (rc == 0)
      76             :                 /* removes terminating null of the length */
      77          12 :                 (*size)--;
      78             :         else {
      79           0 :                 free(*buffer);
      80           0 :                 *buffer = NULL;
      81           0 :                 *size = 0;
      82             :         }
      83          12 :         return rc;
      84             : }
      85             : #else
      86             : static FILE *memfile_open(char **buffer, size_t *size)
      87             : {
      88             :         /*
      89             :          * We can't provide *buffer and *size as open_memstream does but
      90             :          * at least clear them so the caller won't get bad data.
      91             :          */
      92             :         *buffer = NULL;
      93             :         *size = 0;
      94             : 
      95             :         return tmpfile();
      96             : }
      97             : static void memfile_abort(FILE *file, char **buffer, size_t *size)
      98             : {
      99             :         fclose(file);
     100             :         *buffer = NULL;
     101             :         *size = 0;
     102             : }
     103             : static int memfile_close(FILE *file, char **buffer, size_t *size)
     104             : {
     105             :         int rc;
     106             :         size_t s;
     107             :         char *b;
     108             : 
     109             :         s = (size_t)ftell(file);
     110             :         b = malloc(s + 1);
     111             :         if (b == NULL) {
     112             :                 rc = MUSTACH_ERROR_SYSTEM;
     113             :                 errno = ENOMEM;
     114             :                 s = 0;
     115             :         } else {
     116             :                 rewind(file);
     117             :                 if (1 == fread(b, s, 1, file)) {
     118             :                         rc = 0;
     119             :                         b[s] = 0;
     120             :                 } else {
     121             :                         rc = MUSTACH_ERROR_SYSTEM;
     122             :                         free(b);
     123             :                         b = NULL;
     124             :                         s = 0;
     125             :                 }
     126             :         }
     127             :         *buffer = b;
     128             :         *size = s;
     129             :         return rc;
     130             : }
     131             : #endif
     132             : 
     133          14 : static inline void sbuf_reset(struct mustach_sbuf *sbuf)
     134             : {
     135          14 :         sbuf->value = NULL;
     136          14 :         sbuf->freecb = NULL;
     137          14 :         sbuf->closure = NULL;
     138          14 : }
     139             : 
     140          14 : static inline void sbuf_release(struct mustach_sbuf *sbuf)
     141             : {
     142          14 :         if (sbuf->releasecb)
     143           2 :                 sbuf->releasecb(sbuf->value, sbuf->closure);
     144          14 : }
     145             : 
     146          27 : static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
     147             : {
     148             :         size_t i, j;
     149             : 
     150             :         (void)closure; /* unused */
     151             : 
     152          27 :         if (!escape)
     153          14 :                 return fwrite(buffer, size, 1, file) != 1 ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
     154             : 
     155          13 :         i = 0;
     156          26 :         while (i < size) {
     157          13 :                 j = i;
     158          93 :                 while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&')
     159          80 :                         j++;
     160          13 :                 if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
     161           0 :                         return MUSTACH_ERROR_SYSTEM;
     162          13 :                 if (j < size) {
     163           0 :                         switch(buffer[j++]) {
     164           0 :                         case '<':
     165           0 :                                 if (fwrite("&lt;", 4, 1, file) != 1)
     166           0 :                                         return MUSTACH_ERROR_SYSTEM;
     167           0 :                                 break;
     168           0 :                         case '>':
     169           0 :                                 if (fwrite("&gt;", 4, 1, file) != 1)
     170           0 :                                         return MUSTACH_ERROR_SYSTEM;
     171           0 :                                 break;
     172           0 :                         case '&':
     173           0 :                                 if (fwrite("&amp;", 5, 1, file) != 1)
     174           0 :                                         return MUSTACH_ERROR_SYSTEM;
     175           0 :                                 break;
     176           0 :                         default: break;
     177             :                         }
     178          13 :                 }
     179          13 :                 i = j;
     180             :         }
     181          13 :         return MUSTACH_OK;
     182             : }
     183             : 
     184          14 : static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
     185             : {
     186          14 :         struct iwrap *iwrap = closure;
     187             :         int rc;
     188             :         struct mustach_sbuf sbuf;
     189             :         size_t length;
     190             : 
     191          14 :         sbuf_reset(&sbuf);
     192          14 :         rc = iwrap->get(iwrap->closure, name, &sbuf);
     193          14 :         if (rc >= 0) {
     194          14 :                 length = strlen(sbuf.value);
     195          14 :                 if (length)
     196          13 :                         rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
     197          14 :                 sbuf_release(&sbuf);
     198             :         }
     199          14 :         return rc;
     200             : }
     201             : 
     202           0 : static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
     203             : {
     204           0 :         struct iwrap *iwrap = closure;
     205             :         int rc;
     206             :         FILE *file;
     207             :         size_t size;
     208             :         char *result;
     209             : 
     210           0 :         result = NULL;
     211           0 :         file = memfile_open(&result, &size);
     212           0 :         if (file == NULL)
     213           0 :                 rc = MUSTACH_ERROR_SYSTEM;
     214             :         else {
     215           0 :                 rc = iwrap->put(iwrap->closure_put, name, 0, file);
     216           0 :                 if (rc < 0)
     217           0 :                         memfile_abort(file, &result, &size);
     218             :                 else {
     219           0 :                         rc = memfile_close(file, &result, &size);
     220           0 :                         if (rc == 0) {
     221           0 :                                 sbuf->value = result;
     222           0 :                                 sbuf->freecb = free;
     223             :                         }
     224             :                 }
     225             :         }
     226           0 :         return rc;
     227             : }
     228             : 
     229          12 : static int process(const char *template, struct iwrap *iwrap, FILE *file, const char *opstr, const char *clstr)
     230             : {
     231             :         struct mustach_sbuf sbuf;
     232             :         char name[MUSTACH_MAX_LENGTH + 1], c, *tmp;
     233             :         const char *beg, *term;
     234             :         struct { const char *name, *again; size_t length; int enabled, entered; } stack[MUSTACH_MAX_DEPTH];
     235             :         size_t oplen, cllen, len, l;
     236             :         int depth, rc, enabled;
     237             : 
     238          12 :         enabled = 1;
     239          12 :         oplen = strlen(opstr);
     240          12 :         cllen = strlen(clstr);
     241          12 :         depth = 0;
     242             :         for(;;) {
     243          21 :                 beg = strstr(template, opstr);
     244          33 :                 if (beg == NULL) {
     245             :                         /* no more mustach */
     246          12 :                         if (enabled && template[0]) {
     247           1 :                                 rc = iwrap->emit(iwrap->closure, template, strlen(template), 0, file);
     248           1 :                                 if (rc < 0)
     249           0 :                                         return rc;
     250             :                         }
     251          12 :                         return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
     252             :                 }
     253          21 :                 if (enabled && beg != template) {
     254          13 :                         rc = iwrap->emit(iwrap->closure, template, (size_t)(beg - template), 0, file);
     255          13 :                         if (rc < 0)
     256           0 :                                 return rc;
     257             :                 }
     258          21 :                 beg += oplen;
     259          21 :                 term = strstr(beg, clstr);
     260          21 :                 if (term == NULL)
     261           0 :                         return MUSTACH_ERROR_UNEXPECTED_END;
     262          21 :                 template = term + cllen;
     263          21 :                 len = (size_t)(term - beg);
     264          21 :                 c = *beg;
     265          21 :                 switch(c) {
     266           0 :                 case '!':
     267             :                 case '=':
     268           0 :                         break;
     269           0 :                 case '{':
     270           0 :                         for (l = 0 ; clstr[l] == '}' ; l++);
     271           0 :                         if (clstr[l]) {
     272           0 :                                 if (!len || beg[len-1] != '}')
     273           0 :                                         return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
     274           0 :                                 len--;
     275             :                         } else {
     276           0 :                                 if (term[l] != '}')
     277           0 :                                         return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
     278           0 :                                 template++;
     279             :                         }
     280           0 :                         c = '&';
     281             :                         /*@fallthrough@*/
     282           7 :                 case '^':
     283             :                 case '#':
     284             :                 case '/':
     285             :                 case '&':
     286             :                 case '>':
     287             : #if !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
     288             :                 case ':':
     289             : #endif
     290           7 :                         beg++; len--;
     291          21 :                 default:
     292          42 :                         while (len && isspace(beg[0])) { beg++; len--; }
     293          42 :                         while (len && isspace(beg[len-1])) len--;
     294             : #if !defined(NO_ALLOW_EMPTY_TAG)
     295          21 :                         if (len == 0)
     296           0 :                                 return MUSTACH_ERROR_EMPTY_TAG;
     297             : #endif
     298          21 :                         if (len > MUSTACH_MAX_LENGTH)
     299           0 :                                 return MUSTACH_ERROR_TAG_TOO_LONG;
     300          21 :                         memcpy(name, beg, len);
     301          21 :                         name[len] = 0;
     302          21 :                         break;
     303             :                 }
     304          21 :                 switch(c) {
     305           0 :                 case '!':
     306             :                         /* comment */
     307             :                         /* nothing to do */
     308           0 :                         break;
     309           0 :                 case '=':
     310             :                         /* defines separators */
     311           0 :                         if (len < 5 || beg[len - 1] != '=')
     312           0 :                                 return MUSTACH_ERROR_BAD_SEPARATORS;
     313           0 :                         beg++;
     314           0 :                         len -= 2;
     315           0 :                         for (l = 0; l < len && !isspace(beg[l]) ; l++);
     316           0 :                         if (l == len)
     317           0 :                                 return MUSTACH_ERROR_BAD_SEPARATORS;
     318           0 :                         oplen = l;
     319           0 :                         tmp = alloca(oplen + 1);
     320           0 :                         memcpy(tmp, beg, oplen);
     321           0 :                         tmp[oplen] = 0;
     322           0 :                         opstr = tmp;
     323           0 :                         while (l < len && isspace(beg[l])) l++;
     324           0 :                         if (l == len)
     325           0 :                                 return MUSTACH_ERROR_BAD_SEPARATORS;
     326           0 :                         cllen = len - l;
     327           0 :                         tmp = alloca(cllen + 1);
     328           0 :                         memcpy(tmp, beg + l, cllen);
     329           0 :                         tmp[cllen] = 0;
     330           0 :                         clstr = tmp;
     331           0 :                         break;
     332           3 :                 case '^':
     333             :                 case '#':
     334             :                         /* begin section */
     335           3 :                         if (depth == MUSTACH_MAX_DEPTH)
     336           0 :                                 return MUSTACH_ERROR_TOO_DEEP;
     337           3 :                         rc = enabled;
     338           3 :                         if (rc) {
     339           3 :                                 rc = iwrap->enter(iwrap->closure, name);
     340           3 :                                 if (rc < 0)
     341           0 :                                         return rc;
     342             :                         }
     343           3 :                         stack[depth].name = beg;
     344           3 :                         stack[depth].again = template;
     345           3 :                         stack[depth].length = len;
     346           3 :                         stack[depth].enabled = enabled;
     347           3 :                         stack[depth].entered = rc;
     348           3 :                         if ((c == '#') == (rc == 0))
     349           0 :                                 enabled = 0;
     350           3 :                         depth++;
     351           3 :                         break;
     352           4 :                 case '/':
     353             :                         /* end section */
     354           4 :                         if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
     355           0 :                                 return MUSTACH_ERROR_CLOSING;
     356           4 :                         rc = enabled && stack[depth].entered ? iwrap->next(iwrap->closure) : 0;
     357           4 :                         if (rc < 0)
     358           0 :                                 return rc;
     359           4 :                         if (rc) {
     360           1 :                                 template = stack[depth++].again;
     361             :                         } else {
     362           3 :                                 enabled = stack[depth].enabled;
     363           3 :                                 if (enabled && stack[depth].entered)
     364           2 :                                         iwrap->leave(iwrap->closure);
     365             :                         }
     366           4 :                         break;
     367           0 :                 case '>':
     368             :                         /* partials */
     369           0 :                         if (enabled) {
     370           0 :                                 sbuf_reset(&sbuf);
     371           0 :                                 rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
     372           0 :                                 if (rc >= 0) {
     373           0 :                                         rc = process(sbuf.value, iwrap, file, opstr, clstr);
     374           0 :                                         sbuf_release(&sbuf);
     375             :                                 }
     376           0 :                                 if (rc < 0)
     377           0 :                                         return rc;
     378             :                         }
     379           0 :                         break;
     380          14 :                 default:
     381             :                         /* replacement */
     382          14 :                         if (enabled) {
     383          14 :                                 rc = iwrap->put(iwrap->closure_put, name, c != '&', file);
     384          14 :                                 if (rc < 0)
     385           0 :                                         return rc;
     386             :                         }
     387          14 :                         break;
     388             :                 }
     389             :         }
     390             : }
     391             : 
     392          12 : int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
     393             : {
     394             :         int rc;
     395             :         struct iwrap iwrap;
     396             : 
     397             :         /* check validity */
     398          12 :         if (!itf->enter || !itf->next || !itf->leave || (!itf->put && !itf->get))
     399           0 :                 return MUSTACH_ERROR_INVALID_ITF;
     400             : 
     401             :         /* init wrap structure */
     402          12 :         iwrap.closure = closure;
     403          12 :         if (itf->put) {
     404           0 :                 iwrap.put = itf->put;
     405           0 :                 iwrap.closure_put = closure;
     406             :         } else {
     407          12 :                 iwrap.put = iwrap_put;
     408          12 :                 iwrap.closure_put = &iwrap;
     409             :         }
     410          12 :         if (itf->partial) {
     411           0 :                 iwrap.partial = itf->partial;
     412           0 :                 iwrap.closure_partial = closure;
     413          12 :         } else if (itf->get) {
     414          12 :                 iwrap.partial = itf->get;
     415          12 :                 iwrap.closure_partial = closure;
     416             :         } else {
     417           0 :                 iwrap.partial = iwrap_partial;
     418           0 :                 iwrap.closure_partial = &iwrap;
     419             :         }
     420          12 :         iwrap.emit = itf->emit ? itf->emit : iwrap_emit;
     421          12 :         iwrap.enter = itf->enter;
     422          12 :         iwrap.next = itf->next;
     423          12 :         iwrap.leave = itf->leave;
     424          12 :         iwrap.get = itf->get;
     425             : 
     426             :         /* process */
     427          12 :         rc = itf->start ? itf->start(closure) : 0;
     428          12 :         if (rc == 0)
     429          12 :                 rc = process(template, &iwrap, file, "{{", "}}");
     430          12 :         if (itf->stop)
     431           0 :                 itf->stop(closure, rc);
     432          12 :         return rc;
     433             : }
     434             : 
     435           0 : int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
     436             : {
     437             :         int rc;
     438             :         FILE *file;
     439             : 
     440           0 :         file = fdopen(fd, "w");
     441           0 :         if (file == NULL) {
     442           0 :                 rc = MUSTACH_ERROR_SYSTEM;
     443           0 :                 errno = ENOMEM;
     444             :         } else {
     445           0 :                 rc = fmustach(template, itf, closure, file);
     446           0 :                 fclose(file);
     447             :         }
     448           0 :         return rc;
     449             : }
     450             : 
     451          12 : int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
     452             : {
     453             :         int rc;
     454             :         FILE *file;
     455             :         size_t s;
     456             : 
     457          12 :         *result = NULL;
     458          12 :         if (size == NULL)
     459           0 :                 size = &s;
     460          12 :         file = memfile_open(result, size);
     461          12 :         if (file == NULL)
     462           0 :                 rc = MUSTACH_ERROR_SYSTEM;
     463             :         else {
     464          12 :                 rc = fmustach(template, itf, closure, file);
     465          12 :                 if (rc < 0)
     466           0 :                         memfile_abort(file, result, size);
     467             :                 else
     468          12 :                         rc = memfile_close(file, result, size);
     469             :         }
     470          12 :         return rc;
     471             : }
     472             : 

Generated by: LCOV version 1.14