LCOV - code coverage report
Current view: top level - templating - mustach-wrap.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 51.9 % 231 120
Test Date: 2025-12-28 14:06:02 Functions: 71.4 % 21 15

            Line data    Source code
       1              : /*
       2              :  Author: José Bollo <jobol@nonadev.net>
       3              : 
       4              :  https://gitlab.com/jobol/mustach
       5              : 
       6              :  SPDX-License-Identifier: ISC
       7              : */
       8              : 
       9              : #ifndef _GNU_SOURCE
      10              : #define _GNU_SOURCE
      11              : #endif
      12              : 
      13              : #include <stdlib.h>
      14              : #include <stdio.h>
      15              : #include <string.h>
      16              : #ifdef _WIN32
      17              : #include <malloc.h>
      18              : #endif
      19              : 
      20              : #include "mustach.h"
      21              : #include "mustach-wrap.h"
      22              : 
      23              : /*
      24              : * It was stated that allowing to include files
      25              : * through template is not safe when the mustache
      26              : * template is open to any value because it could
      27              : * create leaks (example: {{>/etc/passwd}}).
      28              : */
      29              : #if MUSTACH_SAFE
      30              : # undef MUSTACH_LOAD_TEMPLATE
      31              : #elif !defined(MUSTACH_LOAD_TEMPLATE)
      32              : # define MUSTACH_LOAD_TEMPLATE 1
      33              : #endif
      34              : 
      35              : #if !defined(INCLUDE_PARTIAL_EXTENSION)
      36              : # define INCLUDE_PARTIAL_EXTENSION ".mustache"
      37              : #endif
      38              : 
      39              : /* global hook for partials */
      40              : int (*mustach_wrap_get_partial)(const char *name, struct mustach_sbuf *sbuf) = NULL;
      41              : 
      42              : /* internal structure for wrapping */
      43              : struct wrap {
      44              :         /* original interface */
      45              :         const struct mustach_wrap_itf *itf;
      46              : 
      47              :         /* original closure */
      48              :         void *closure;
      49              : 
      50              :         /* flags */
      51              :         int flags;
      52              : 
      53              :         /* emiter callback */
      54              :         mustach_emit_cb_t *emitcb;
      55              : 
      56              :         /* write callback */
      57              :         mustach_write_cb_t *writecb;
      58              : };
      59              : 
      60              : /* length given by masking with 3 */
      61              : enum comp {
      62              :         C_no = 0,
      63              :         C_eq = 1,
      64              :         C_lt = 5,
      65              :         C_le = 6,
      66              :         C_gt = 9,
      67              :         C_ge = 10
      68              : };
      69              : 
      70              : enum sel {
      71              :         S_none = 0,
      72              :         S_ok = 1,
      73              :         S_objiter = 2,
      74              :         S_ok_or_objiter = S_ok | S_objiter
      75              : };
      76              : 
      77           27 : static enum comp getcomp(char *head, int sflags)
      78              : {
      79            0 :         return (head[0] == '=' && (sflags & Mustach_With_Equal)) ? C_eq
      80           54 :                 : (head[0] == '<' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_le : C_lt)
      81           27 :                 : (head[0] == '>' && (sflags & Mustach_With_Compare)) ? (head[1] == '=' ? C_ge : C_gt)
      82              :                 : C_no;
      83              : }
      84              : 
      85           10 : static char *keyval(char *head, int sflags, enum comp *comp)
      86              : {
      87              :         char *w, car, escaped;
      88              :         enum comp k;
      89              : 
      90           10 :         k = C_no;
      91           10 :         w = head;
      92           10 :         car = *head;
      93           10 :         escaped = (sflags & Mustach_With_EscFirstCmp) && (getcomp(head, sflags) != C_no);
      94           27 :         while (car && (escaped || (k = getcomp(head, sflags)) == C_no)) {
      95           17 :                 if (escaped)
      96            0 :                         escaped = 0;
      97              :                 else
      98           17 :                         escaped = ((sflags & Mustach_With_JsonPointer) ? car == '~' : car == '\\')
      99           17 :                             && (getcomp(head + 1, sflags) != C_no);
     100           17 :                 if (!escaped)
     101           17 :                         *w++ = car;
     102           17 :                 head++;
     103           17 :                 car = *head;
     104              :         }
     105           10 :         *w = 0;
     106           10 :         *comp = k;
     107           10 :         return k == C_no ? NULL : &head[k & 3];
     108              : }
     109              : 
     110           16 : static char *getkey(char **head, int sflags)
     111              : {
     112              :         char *result, *iter, *write, car;
     113              : 
     114           16 :         car = *(iter = *head);
     115           16 :         if (!car)
     116            7 :                 result = NULL;
     117              :         else {
     118            9 :                 result = write = iter;
     119            9 :                 if (sflags & Mustach_With_JsonPointer)
     120              :                 {
     121            0 :                         while (car && car != '/') {
     122            0 :                                 if (car == '~')
     123            0 :                                         switch (iter[1]) {
     124            0 :                                         case '1': car = '/'; /*@fallthrough@*/
     125            0 :                                         case '0': iter++;
     126              :                                         }
     127            0 :                                 *write++ = car;
     128            0 :                                 car = *++iter;
     129              :                         }
     130            0 :                         *write = 0;
     131            0 :                         while (car == '/')
     132            0 :                                 car = *++iter;
     133              :                 }
     134              :                 else
     135              :                 {
     136           23 :                         while (car && car != '.') {
     137           14 :                                 if (car == '\\' && (iter[1] == '.' || iter[1] == '\\'))
     138            0 :                                         car = *++iter;
     139           14 :                                 *write++ = car;
     140           14 :                                 car = *++iter;
     141              :                         }
     142            9 :                         *write = 0;
     143           10 :                         while (car == '.')
     144            1 :                                 car = *++iter;
     145              :                 }
     146            9 :                 *head = iter;
     147              :         }
     148           16 :         return result;
     149              : }
     150              : 
     151           10 : static enum sel sel(struct wrap *w, const char *name)
     152           10 : {
     153              :         enum sel result;
     154              :         int i, j, sflags, scmp;
     155              :         char *key, *value;
     156              :         enum comp k;
     157              : 
     158              :         /* make a local writeable copy */
     159           10 :         size_t lenname = 1 + strlen(name);
     160           10 :         char buffer[lenname];
     161           10 :         char *copy = buffer;
     162           10 :         memcpy(copy, name, lenname);
     163              : 
     164              :         /* check if matches json pointer selection */
     165           10 :         sflags = w->flags;
     166           10 :         if (sflags & Mustach_With_JsonPointer) {
     167           10 :                 if (copy[0] == '/')
     168            0 :                         copy++;
     169              :                 else
     170           10 :                         sflags ^= Mustach_With_JsonPointer;
     171              :         }
     172              : 
     173              :         /* extract the value, translate the key and get the comparator */
     174           10 :         if (sflags & (Mustach_With_Equal | Mustach_With_Compare))
     175           10 :                 value = keyval(copy, sflags, &k);
     176              :         else {
     177            0 :                 k = C_no;
     178            0 :                 value = NULL;
     179              :         }
     180              : 
     181              :         /* case of . alone if Mustach_With_SingleDot? */
     182           10 :         if (copy[0] == '.' && copy[1] == 0 /*&& (sflags & Mustach_With_SingleDot)*/)
     183              :                 /* yes, select current */
     184            2 :                 result = w->itf->sel(w->closure, NULL) ? S_ok : S_none;
     185              :         else
     186              :         {
     187              :                 /* not the single dot, extract the first key */
     188            8 :                 key = getkey(&copy, sflags);
     189            8 :                 if (key == NULL)
     190            0 :                         return 0;
     191              : 
     192              :                 /* select the root item */
     193            8 :                 if (w->itf->sel(w->closure, key))
     194            7 :                         result = S_ok;
     195            1 :                 else if (key[0] == '*'
     196            0 :                       && !key[1]
     197            0 :                       && !value
     198            0 :                       && !*copy
     199            0 :                       && (w->flags & Mustach_With_ObjectIter)
     200            0 :                       && w->itf->sel(w->closure, NULL))
     201            0 :                         result = S_ok_or_objiter;
     202              :                 else
     203            1 :                         result = S_none;
     204            8 :                 if (result == S_ok) {
     205              :                         /* iterate the selection of sub items */
     206            7 :                         key = getkey(&copy, sflags);
     207            8 :                         while(result == S_ok && key) {
     208            1 :                                 if (w->itf->subsel(w->closure, key))
     209              :                                         /* nothing */;
     210            0 :                                 else if (key[0] == '*'
     211            0 :                                       && !key[1]
     212            0 :                                       && !value
     213            0 :                                       && !*copy
     214            0 :                                       && (w->flags & Mustach_With_ObjectIter))
     215            0 :                                         result = S_objiter;
     216              :                                 else
     217            0 :                                         result = S_none;
     218            1 :                                 key = getkey(&copy, sflags);
     219              :                         }
     220              :                 }
     221              :         }
     222              :         /* should it be compared? */
     223           10 :         if (result == S_ok && value) {
     224            0 :                 if (!w->itf->compare)
     225            0 :                         result = S_none;
     226              :                 else {
     227            0 :                         i = value[0] == '!';
     228            0 :                         scmp = w->itf->compare(w->closure, &value[i]);
     229            0 :                         switch (k) {
     230            0 :                         case C_eq: j = scmp == 0; break;
     231            0 :                         case C_lt: j = scmp < 0; break;
     232            0 :                         case C_le: j = scmp <= 0; break;
     233            0 :                         case C_gt: j = scmp > 0; break;
     234            0 :                         case C_ge: j = scmp >= 0; break;
     235            0 :                         default: j = i; break;
     236              :                         }
     237            0 :                         if (i == j)
     238            0 :                                 result = S_none;
     239              :                 }
     240              :         }
     241           10 :         return result;
     242              : }
     243              : 
     244            6 : static int start_callback(void *closure)
     245              : {
     246            6 :         struct wrap *w = closure;
     247            6 :         return w->itf->start ? w->itf->start(w->closure) : MUSTACH_OK;
     248              : }
     249              : 
     250            6 : static void stop_callback(void *closure, int status)
     251              : {
     252            6 :         struct wrap *w = closure;
     253            6 :         if (w->itf->stop)
     254            0 :                 w->itf->stop(w->closure, status);
     255            6 : }
     256              : 
     257           18 : static int emit(struct wrap *w, const char *buffer, size_t size, FILE *file)
     258              : {
     259              :         int r;
     260              : 
     261           18 :         if (w->writecb)
     262            0 :                 r = w->writecb(file, buffer, size);
     263              :         else
     264           18 :                 r = fwrite(buffer, 1, size, file) == size ? MUSTACH_OK : MUSTACH_ERROR_SYSTEM;
     265           18 :         return r;
     266              : }
     267              : 
     268           18 : static int emit_callback(void *closure, const char *buffer, size_t size, int escape, FILE *file)
     269              : {
     270           18 :         struct wrap *w = closure;
     271              :         int r;
     272              :         size_t s, i;
     273              :         char car;
     274              : 
     275           18 :         if (w->emitcb)
     276            0 :                 r = w->emitcb(file, buffer, size, escape);
     277           18 :         else if (!escape)
     278           12 :                 r = emit(w, buffer, size, file);
     279              :         else {
     280            6 :                 i = 0;
     281            6 :                 r = MUSTACH_OK;
     282           12 :                 while(i < size && r == MUSTACH_OK) {
     283            6 :                         s = i;
     284           27 :                         while (i < size && (car = buffer[i]) != '<' && car != '>' && car != '&' && car != '"')
     285           21 :                                 i++;
     286            6 :                         if (i != s)
     287            6 :                                 r = emit(w, &buffer[s], i - s, file);
     288            6 :                         if (i < size && r == MUSTACH_OK) {
     289            0 :                                 switch(car) {
     290            0 :                                 case '<': r = emit(w, "&lt;", 4, file); break;
     291            0 :                                 case '>': r = emit(w, "&gt;", 4, file); break;
     292            0 :                                 case '&': r = emit(w, "&amp;", 5, file); break;
     293            0 :                                 case '"': r = emit(w, "&quot;", 6, file); break;
     294              :                                 }
     295            0 :                                 i++;
     296              :                         }
     297              :                 }
     298              :         }
     299           18 :         return r;
     300              : }
     301              : 
     302            3 : static int enter_callback(void *closure, const char *name)
     303              : {
     304            3 :         struct wrap *w = closure;
     305            3 :         enum sel s = sel(w, name);
     306            3 :         return s == S_none ? 0 : w->itf->enter(w->closure, s & S_objiter);
     307              : }
     308              : 
     309            3 : static int next_callback(void *closure)
     310              : {
     311            3 :         struct wrap *w = closure;
     312            3 :         return w->itf->next(w->closure);
     313              : }
     314              : 
     315            2 : static int leave_callback(void *closure)
     316              : {
     317            2 :         struct wrap *w = closure;
     318            2 :         return w->itf->leave(w->closure);
     319              : }
     320              : 
     321            7 : static int getoptional(struct wrap *w, const char *name, struct mustach_sbuf *sbuf)
     322              : {
     323            7 :         enum sel s = sel(w, name);
     324            7 :         if (!(s & S_ok))
     325            1 :                 return 0;
     326            6 :         return w->itf->get(w->closure, sbuf, s & S_objiter);
     327              : }
     328              : 
     329            7 : static int get_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
     330              : {
     331            7 :         struct wrap *w = closure;
     332            7 :         if (getoptional(w, name, sbuf) <= 0) {
     333            1 :                 if (w->flags & Mustach_With_ErrorUndefined)
     334            0 :                         return MUSTACH_ERROR_UNDEFINED_TAG;
     335            1 :                 sbuf->value = "";
     336              :         }
     337            7 :         return MUSTACH_OK;
     338              : }
     339              : 
     340              : #if MUSTACH_LOAD_TEMPLATE
     341            0 : static int get_partial_from_file(const char *name, struct mustach_sbuf *sbuf)
     342              : {
     343              :         static char extension[] = INCLUDE_PARTIAL_EXTENSION;
     344              :         size_t s;
     345              :         long pos;
     346              :         FILE *file;
     347              :         char *path, *buffer;
     348              : 
     349              :         /* allocate path */
     350            0 :         s = strlen(name);
     351            0 :         path = malloc(s + sizeof extension);
     352            0 :         if (path == NULL)
     353            0 :                 return MUSTACH_ERROR_SYSTEM;
     354              : 
     355              :         /* try without extension first */
     356            0 :         memcpy(path, name, s + 1);
     357            0 :         file = fopen(path, "r");
     358            0 :         if (file == NULL) {
     359            0 :                 memcpy(&path[s], extension, sizeof extension);
     360            0 :                 file = fopen(path, "r");
     361              :         }
     362            0 :         free(path);
     363              : 
     364              :         /* if file opened */
     365            0 :         if (file == NULL)
     366            0 :                 return MUSTACH_ERROR_PARTIAL_NOT_FOUND;
     367              : 
     368              :         /* compute file size */
     369            0 :         if (fseek(file, 0, SEEK_END) >= 0
     370            0 :          && (pos = ftell(file)) >= 0
     371            0 :          && fseek(file, 0, SEEK_SET) >= 0) {
     372              :                 /* allocate value */
     373            0 :                 s = (size_t)pos;
     374            0 :                 buffer = malloc(s + 1);
     375            0 :                 if (buffer != NULL) {
     376              :                         /* read value */
     377            0 :                         if (1 == fread(buffer, s, 1, file)) {
     378              :                                 /* force zero at end */
     379            0 :                                 sbuf->value = buffer;
     380            0 :                                 buffer[s] = 0;
     381            0 :                                 sbuf->freecb = free;
     382            0 :                                 fclose(file);
     383            0 :                                 return MUSTACH_OK;
     384              :                         }
     385            0 :                         free(buffer);
     386              :                 }
     387              :         }
     388            0 :         fclose(file);
     389            0 :         return MUSTACH_ERROR_SYSTEM;
     390              : }
     391              : #endif
     392              : 
     393            0 : static int partial_callback(void *closure, const char *name, struct mustach_sbuf *sbuf)
     394              : {
     395            0 :         struct wrap *w = closure;
     396              :         int rc;
     397            0 :         if (mustach_wrap_get_partial != NULL) {
     398            0 :                 rc = mustach_wrap_get_partial(name, sbuf);
     399            0 :                 if (rc != MUSTACH_ERROR_PARTIAL_NOT_FOUND) {
     400            0 :                         if (rc != MUSTACH_OK)
     401            0 :                                 sbuf->value = "";
     402            0 :                         return rc;
     403              :                 }
     404              :         }
     405              : #if MUSTACH_LOAD_TEMPLATE
     406            0 :         if (w->flags & Mustach_With_PartialDataFirst) {
     407            0 :                 if (getoptional(w, name, sbuf) > 0)
     408            0 :                         rc = MUSTACH_OK;
     409              :                 else
     410            0 :                         rc = get_partial_from_file(name, sbuf);
     411              :         }
     412              :         else {
     413            0 :                 rc = get_partial_from_file(name, sbuf);
     414            0 :                 if (rc != MUSTACH_OK &&  getoptional(w, name, sbuf) > 0)
     415            0 :                         rc = MUSTACH_OK;
     416              :         }
     417              : #else
     418              :         rc = getoptional(w, name, sbuf) > 0 ?  MUSTACH_OK : MUSTACH_ERROR_PARTIAL_NOT_FOUND;
     419              : #endif
     420            0 :         if (rc != MUSTACH_OK)
     421            0 :                 sbuf->value = "";
     422            0 :         return MUSTACH_OK;
     423              : }
     424              : 
     425              : const struct mustach_itf mustach_wrap_itf = {
     426              :         .start = start_callback,
     427              :         .put = NULL,
     428              :         .enter = enter_callback,
     429              :         .next = next_callback,
     430              :         .leave = leave_callback,
     431              :         .partial = partial_callback,
     432              :         .get = get_callback,
     433              :         .emit = emit_callback,
     434              :         .stop = stop_callback
     435              : };
     436              : 
     437            6 : 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)
     438              : {
     439            6 :         if (flags & Mustach_With_Compare)
     440            6 :                 flags |= Mustach_With_Equal;
     441            6 :         wrap->closure = closure;
     442            6 :         wrap->itf = itf;
     443            6 :         wrap->flags = flags;
     444            6 :         wrap->emitcb = emitcb;
     445            6 :         wrap->writecb = writecb;
     446            6 : }
     447              : 
     448            0 : int mustach_wrap_file(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, FILE *file)
     449              : {
     450              :         struct wrap w;
     451            0 :         wrap_init(&w, itf, closure, flags, NULL, NULL);
     452            0 :         return mustach_file(template, length, &mustach_wrap_itf, &w, flags, file);
     453              : }
     454              : 
     455            0 : int mustach_wrap_fd(const char *template, size_t length, const struct mustach_wrap_itf *itf, void *closure, int flags, int fd)
     456              : {
     457              :         struct wrap w;
     458            0 :         wrap_init(&w, itf, closure, flags, NULL, NULL);
     459            0 :         return mustach_fd(template, length, &mustach_wrap_itf, &w, flags, fd);
     460              : }
     461              : 
     462            6 : 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)
     463              : {
     464              :         struct wrap w;
     465            6 :         wrap_init(&w, itf, closure, flags, NULL, NULL);
     466            6 :         return mustach_mem(template, length, &mustach_wrap_itf, &w, flags, result, size);
     467              : }
     468              : 
     469            0 : 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)
     470              : {
     471              :         struct wrap w;
     472            0 :         wrap_init(&w, itf, closure, flags, NULL, writecb);
     473            0 :         return mustach_file(template, length, &mustach_wrap_itf, &w, flags, writeclosure);
     474              : }
     475              : 
     476            0 : 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)
     477              : {
     478              :         struct wrap w;
     479            0 :         wrap_init(&w, itf, closure, flags, emitcb, NULL);
     480            0 :         return mustach_file(template, length, &mustach_wrap_itf, &w, flags, emitclosure);
     481              : }
     482              : 
        

Generated by: LCOV version 2.0-1