exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mustach.c (13073B)


      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 #include <errno.h>
     17 #include <ctype.h>
     18 #ifdef _WIN32
     19 #include <malloc.h>
     20 #endif
     21 
     22 #include "mustach.h"
     23 
     24 struct iwrap {
     25 	int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
     26 	void *closure; /* closure for: enter, next, leave, emit, get */
     27 	int (*put)(void *closure, const char *name, int escape, FILE *file);
     28 	void *closure_put; /* closure for put */
     29 	int (*enter)(void *closure, const char *name);
     30 	int (*next)(void *closure);
     31 	int (*leave)(void *closure);
     32 	int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
     33 	int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
     34 	void *closure_partial; /* closure for partial */
     35 	FILE *file;
     36 	int flags;
     37 	int nesting;
     38 };
     39 
     40 struct prefix {
     41 	size_t len;
     42 	const char *start;
     43 	struct prefix *prefix;
     44 };
     45 
     46 #if !defined(NO_OPEN_MEMSTREAM)
     47 static FILE *memfile_open(char **buffer, size_t *size)
     48 {
     49 	return open_memstream(buffer, size);
     50 }
     51 static void memfile_abort(FILE *file, char **buffer, size_t *size)
     52 {
     53 	fclose(file);
     54 	free(*buffer);
     55 	*buffer = NULL;
     56 	*size = 0;
     57 }
     58 static int memfile_close(FILE *file, char **buffer, size_t *size)
     59 {
     60 	int rc;
     61 
     62 	/* adds terminating null */
     63 	rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
     64 	fclose(file);
     65 	if (rc == 0)
     66 		/* removes terminating null of the length */
     67 		(*size)--;
     68 	else {
     69 		free(*buffer);
     70 		*buffer = NULL;
     71 		*size = 0;
     72 	}
     73 	return rc;
     74 }
     75 #else
     76 static FILE *memfile_open(char **buffer, size_t *size)
     77 {
     78 	/*
     79 	 * We can't provide *buffer and *size as open_memstream does but
     80 	 * at least clear them so the caller won't get bad data.
     81 	 */
     82 	*buffer = NULL;
     83 	*size = 0;
     84 
     85 	return tmpfile();
     86 }
     87 static void memfile_abort(FILE *file, char **buffer, size_t *size)
     88 {
     89 	fclose(file);
     90 	*buffer = NULL;
     91 	*size = 0;
     92 }
     93 static int memfile_close(FILE *file, char **buffer, size_t *size)
     94 {
     95 	int rc;
     96 	size_t s;
     97 	char *b;
     98 
     99 	s = (size_t)ftell(file);
    100 	b = malloc(s + 1);
    101 	if (b == NULL) {
    102 		rc = MUSTACH_ERROR_SYSTEM;
    103 		errno = ENOMEM;
    104 		s = 0;
    105 	} else {
    106 		rewind(file);
    107 		if (1 == fread(b, s, 1, file)) {
    108 			rc = 0;
    109 			b[s] = 0;
    110 		} else {
    111 			rc = MUSTACH_ERROR_SYSTEM;
    112 			free(b);
    113 			b = NULL;
    114 			s = 0;
    115 		}
    116 	}
    117 	*buffer = b;
    118 	*size = s;
    119 	return rc;
    120 }
    121 #endif
    122 
    123 static inline void sbuf_reset(struct mustach_sbuf *sbuf)
    124 {
    125 	sbuf->value = NULL;
    126 	sbuf->freecb = NULL;
    127 	sbuf->closure = NULL;
    128 	sbuf->length = 0;
    129 }
    130 
    131 static inline void sbuf_release(struct mustach_sbuf *sbuf)
    132 {
    133 	if (sbuf->releasecb)
    134 		sbuf->releasecb(sbuf->value, sbuf->closure);
    135 }
    136 
    137 static inline size_t sbuf_length(struct mustach_sbuf *sbuf)
    138 {
    139 	size_t length = sbuf->length;
    140 	if (length == 0 && sbuf->value != NULL)
    141 		length = strlen(sbuf->value);
    142 	return length;
    143 }
    144 
    145 static int iwrap_emit(void *closure, const char *buffer, size_t size, int escape, FILE *file)
    146 {
    147 	size_t i, j, r;
    148 
    149 	(void)closure; /* unused */
    150 
    151 	if (!escape)
    152 		return fwrite(buffer, 1, size, file) != size ? MUSTACH_ERROR_SYSTEM : MUSTACH_OK;
    153 
    154 	r = i = 0;
    155 	while (i < size) {
    156 		j = i;
    157 		while (j < size && buffer[j] != '<' && buffer[j] != '>' && buffer[j] != '&' && buffer[j] != '"')
    158 			j++;
    159 		if (j != i && fwrite(&buffer[i], j - i, 1, file) != 1)
    160 			return MUSTACH_ERROR_SYSTEM;
    161 		if (j < size) {
    162 			switch(buffer[j++]) {
    163 			case '<':
    164 				r = fwrite("&lt;", 4, 1, file);
    165 				break;
    166 			case '>':
    167 				r = fwrite("&gt;", 4, 1, file);
    168 				break;
    169 			case '&':
    170 				r = fwrite("&amp;", 5, 1, file);
    171 				break;
    172 			case '"':
    173 				r = fwrite("&quot;", 6, 1, file);
    174 				break;
    175 			}
    176 			if (r != 1)
    177 				return MUSTACH_ERROR_SYSTEM;
    178 		}
    179 		i = j;
    180 	}
    181 	return MUSTACH_OK;
    182 }
    183 
    184 static int iwrap_put(void *closure, const char *name, int escape, FILE *file)
    185 {
    186 	struct iwrap *iwrap = closure;
    187 	int rc;
    188 	struct mustach_sbuf sbuf;
    189 	size_t length;
    190 
    191 	sbuf_reset(&sbuf);
    192 	rc = iwrap->get(iwrap->closure, name, &sbuf);
    193 	if (rc >= 0) {
    194 		length = sbuf_length(&sbuf);
    195 		if (length)
    196 			rc = iwrap->emit(iwrap->closure, sbuf.value, length, escape, file);
    197 		sbuf_release(&sbuf);
    198 	}
    199 	return rc;
    200 }
    201 
    202 static int iwrap_partial(void *closure, const char *name, struct mustach_sbuf *sbuf)
    203 {
    204 	struct iwrap *iwrap = closure;
    205 	int rc;
    206 	FILE *file;
    207 	size_t size;
    208 	char *result;
    209 
    210 	result = NULL;
    211 	file = memfile_open(&result, &size);
    212 	if (file == NULL)
    213 		rc = MUSTACH_ERROR_SYSTEM;
    214 	else {
    215 		rc = iwrap->put(iwrap->closure_put, name, 0, file);
    216 		if (rc < 0)
    217 			memfile_abort(file, &result, &size);
    218 		else {
    219 			rc = memfile_close(file, &result, &size);
    220 			if (rc == 0) {
    221 				sbuf->value = result;
    222 				sbuf->freecb = free;
    223 				sbuf->length = size;
    224 			}
    225 		}
    226 	}
    227 	return rc;
    228 }
    229 
    230 static int emitprefix(struct iwrap *iwrap, struct prefix *prefix)
    231 {
    232 	if (prefix->prefix) {
    233 		int rc = emitprefix(iwrap, prefix->prefix);
    234 		if (rc < 0)
    235 			return rc;
    236 	}
    237 	return prefix->len ? iwrap->emit(iwrap->closure, prefix->start, prefix->len, 0, iwrap->file) : 0;
    238 }
    239 
    240 static int process(const char *template, size_t length, struct iwrap *iwrap, struct prefix *prefix)
    241 {
    242 	struct mustach_sbuf sbuf;
    243 	char opstr[MUSTACH_MAX_DELIM_LENGTH], clstr[MUSTACH_MAX_DELIM_LENGTH];
    244 	char name[MUSTACH_MAX_LENGTH + 1], c;
    245 	const char *beg, *term, *end;
    246 	struct { const char *name, *again; size_t length; unsigned enabled: 1, entered: 1; } stack[MUSTACH_MAX_DEPTH];
    247 	size_t oplen, cllen, len, l;
    248 	int depth, rc, enabled, stdalone;
    249 	struct prefix pref;
    250 
    251 	pref.prefix = prefix;
    252 	end = template + (length ? length : strlen(template));
    253 	opstr[0] = opstr[1] = '{';
    254 	clstr[0] = clstr[1] = '}';
    255 	oplen = cllen = 2;
    256 	stdalone = enabled = 1;
    257 	depth = pref.len = 0;
    258 	for (;;) {
    259 		/* search next openning delimiter */
    260 		for (beg = template ; ; beg++) {
    261 			c = beg == end ? '\n' : *beg;
    262 			if (c == '\n') {
    263 				l = (beg != end) + (size_t)(beg - template);
    264 				if (stdalone != 2 && enabled) {
    265 					if (beg != template /* don't prefix empty lines */) {
    266 						rc = emitprefix(iwrap, &pref);
    267 						if (rc < 0)
    268 							return rc;
    269 					}
    270 					rc = iwrap->emit(iwrap->closure, template, l, 0, iwrap->file);
    271 					if (rc < 0)
    272 						return rc;
    273 				}
    274 				if (beg == end) /* no more mustach */
    275 					return depth ? MUSTACH_ERROR_UNEXPECTED_END : MUSTACH_OK;
    276 				template += l;
    277 				stdalone = 1;
    278 				pref.len = 0;
    279 				pref.prefix = prefix;
    280 			}
    281 			else if (!isspace(c)) {
    282 				if (stdalone == 2 && enabled) {
    283 					rc = emitprefix(iwrap, &pref);
    284 					if (rc < 0)
    285 						return rc;
    286 					pref.len = 0;
    287 					stdalone = 0;
    288 					pref.prefix = NULL;
    289 				}
    290 				if (c == *opstr && end - beg >= (ssize_t)oplen) {
    291 					for (l = 1 ; l < oplen && beg[l] == opstr[l] ; l++);
    292 					if (l == oplen)
    293 						break;
    294 				}
    295 				stdalone = 0;
    296 			}
    297 		}
    298 
    299 		pref.start = template;
    300 		pref.len = enabled ? (size_t)(beg - template) : 0;
    301 		beg += oplen;
    302 
    303 		/* search next closing delimiter */
    304 		for (term = beg ; ; term++) {
    305 			if (term == end)
    306 				return MUSTACH_ERROR_UNEXPECTED_END;
    307 			if (*term == *clstr && end - term >= (ssize_t)cllen) {
    308 				for (l = 1 ; l < cllen && term[l] == clstr[l] ; l++);
    309 				if (l == cllen)
    310 					break;
    311 			}
    312 		}
    313 		template = term + cllen;
    314 		len = (size_t)(term - beg);
    315 		c = *beg;
    316 		switch(c) {
    317 		case ':':
    318 			stdalone = 0;
    319 			if (iwrap->flags & Mustach_With_Colon)
    320 				goto exclude_first;
    321 			goto get_name;
    322 		case '!':
    323 		case '=':
    324 			break;
    325 		case '{':
    326 			for (l = 0 ; l < cllen && clstr[l] == '}' ; l++);
    327 			if (l < cllen) {
    328 				if (!len || beg[len-1] != '}')
    329 					return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
    330 				len--;
    331 			} else {
    332 				if (term[l] != '}')
    333 					return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
    334 				template++;
    335 			}
    336 			c = '&';
    337 			/*@fallthrough@*/
    338 		case '&':
    339 			stdalone = 0;
    340 			/*@fallthrough@*/
    341 		case '^':
    342 		case '#':
    343 		case '/':
    344 		case '>':
    345 exclude_first:
    346 			beg++;
    347 			len--;
    348 			goto get_name;
    349 		default:
    350 			stdalone = 0;
    351 get_name:
    352 			while (len && isspace(beg[0])) { beg++; len--; }
    353 			while (len && isspace(beg[len-1])) len--;
    354 			if (len == 0 && !(iwrap->flags & Mustach_With_EmptyTag))
    355 				return MUSTACH_ERROR_EMPTY_TAG;
    356 			if (len > MUSTACH_MAX_LENGTH)
    357 				return MUSTACH_ERROR_TAG_TOO_LONG;
    358 			memcpy(name, beg, len);
    359 			name[len] = 0;
    360 			break;
    361 		}
    362 		if (stdalone)
    363 			stdalone = 2;
    364 		else if (enabled) {
    365 			rc = emitprefix(iwrap, &pref);
    366 			if (rc < 0)
    367 				return rc;
    368 			pref.len = 0;
    369 			pref.prefix = NULL;
    370 		}
    371 		switch(c) {
    372 		case '!':
    373 			/* comment */
    374 			/* nothing to do */
    375 			break;
    376 		case '=':
    377 			/* defines delimiters */
    378 			if (len < 5 || beg[len - 1] != '=')
    379 				return MUSTACH_ERROR_BAD_SEPARATORS;
    380 			beg++;
    381 			len -= 2;
    382 			while (len && isspace(*beg))
    383 				beg++, len--;
    384 			while (len && isspace(beg[len - 1]))
    385 				len--;
    386 			for (l = 0; l < len && !isspace(beg[l]) ; l++);
    387 			if (l == len || l > MUSTACH_MAX_DELIM_LENGTH)
    388 				return MUSTACH_ERROR_BAD_SEPARATORS;
    389 			oplen = l;
    390 			memcpy(opstr, beg, l);
    391 			while (l < len && isspace(beg[l])) l++;
    392 			if (l == len || len - l > MUSTACH_MAX_DELIM_LENGTH)
    393 				return MUSTACH_ERROR_BAD_SEPARATORS;
    394 			cllen = len - l;
    395 			memcpy(clstr, beg + l, cllen);
    396 			break;
    397 		case '^':
    398 		case '#':
    399 			/* begin section */
    400 			if (depth == MUSTACH_MAX_DEPTH)
    401 				return MUSTACH_ERROR_TOO_DEEP;
    402 			rc = enabled;
    403 			if (rc) {
    404 				rc = iwrap->enter(iwrap->closure, name);
    405 				if (rc < 0)
    406 					return rc;
    407 			}
    408 			stack[depth].name = beg;
    409 			stack[depth].again = template;
    410 			stack[depth].length = len;
    411 			stack[depth].enabled = enabled != 0;
    412 			stack[depth].entered = rc != 0;
    413 			if ((c == '#') == (rc == 0))
    414 				enabled = 0;
    415 			depth++;
    416 			break;
    417 		case '/':
    418 			/* end section */
    419 			if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
    420 				return MUSTACH_ERROR_CLOSING;
    421 			rc = enabled && stack[depth].entered ? iwrap->next(iwrap->closure) : 0;
    422 			if (rc < 0)
    423 				return rc;
    424 			if (rc) {
    425 				template = stack[depth++].again;
    426 			} else {
    427 				enabled = stack[depth].enabled;
    428 				if (enabled && stack[depth].entered)
    429 					iwrap->leave(iwrap->closure);
    430 			}
    431 			break;
    432 		case '>':
    433 			/* partials */
    434 			if (enabled) {
    435 				if (iwrap->nesting >= MUSTACH_MAX_NESTING)
    436 					rc = MUSTACH_ERROR_TOO_MUCH_NESTING;
    437 				else {
    438 					sbuf_reset(&sbuf);
    439 					rc = iwrap->partial(iwrap->closure_partial, name, &sbuf);
    440 					if (rc >= 0) {
    441 						iwrap->nesting++;
    442 						rc = process(sbuf.value, sbuf_length(&sbuf), iwrap, &pref);
    443 						sbuf_release(&sbuf);
    444 						iwrap->nesting--;
    445 					}
    446 				}
    447 				if (rc < 0)
    448 					return rc;
    449 			}
    450 			break;
    451 		default:
    452 			/* replacement */
    453 			if (enabled) {
    454 				rc = iwrap->put(iwrap->closure_put, name, c != '&', iwrap->file);
    455 				if (rc < 0)
    456 					return rc;
    457 			}
    458 			break;
    459 		}
    460 	}
    461 }
    462 
    463 int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file)
    464 {
    465 	int rc;
    466 	struct iwrap iwrap;
    467 
    468 	/* check validity */
    469 	if (!itf->enter || !itf->next || !itf->leave || (!itf->put && !itf->get))
    470 		return MUSTACH_ERROR_INVALID_ITF;
    471 
    472 	/* init wrap structure */
    473 	iwrap.closure = closure;
    474 	if (itf->put) {
    475 		iwrap.put = itf->put;
    476 		iwrap.closure_put = closure;
    477 	} else {
    478 		iwrap.put = iwrap_put;
    479 		iwrap.closure_put = &iwrap;
    480 	}
    481 	if (itf->partial) {
    482 		iwrap.partial = itf->partial;
    483 		iwrap.closure_partial = closure;
    484 	} else if (itf->get) {
    485 		iwrap.partial = itf->get;
    486 		iwrap.closure_partial = closure;
    487 	} else {
    488 		iwrap.partial = iwrap_partial;
    489 		iwrap.closure_partial = &iwrap;
    490 	}
    491 	iwrap.emit = itf->emit ? itf->emit : iwrap_emit;
    492 	iwrap.enter = itf->enter;
    493 	iwrap.next = itf->next;
    494 	iwrap.leave = itf->leave;
    495 	iwrap.get = itf->get;
    496 	iwrap.file = file;
    497 	iwrap.flags = flags;
    498 	iwrap.nesting = 0;
    499 
    500 	/* process */
    501 	rc = itf->start ? itf->start(closure) : 0;
    502 	if (rc == 0)
    503 		rc = process(template, length, &iwrap, NULL);
    504 	if (itf->stop)
    505 		itf->stop(closure, rc);
    506 	return rc;
    507 }
    508 
    509 int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd)
    510 {
    511 	int rc;
    512 	FILE *file;
    513 
    514 	file = fdopen(fd, "w");
    515 	if (file == NULL) {
    516 		rc = MUSTACH_ERROR_SYSTEM;
    517 		errno = ENOMEM;
    518 	} else {
    519 		rc = mustach_file(template, length, itf, closure, flags, file);
    520 		fclose(file);
    521 	}
    522 	return rc;
    523 }
    524 
    525 int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size)
    526 {
    527 	int rc;
    528 	FILE *file;
    529 	size_t s;
    530 
    531 	*result = NULL;
    532 	if (size == NULL)
    533 		size = &s;
    534 	file = memfile_open(result, size);
    535 	if (file == NULL)
    536 		rc = MUSTACH_ERROR_SYSTEM;
    537 	else {
    538 		rc = mustach_file(template, length, itf, closure, flags, file);
    539 		if (rc < 0)
    540 			memfile_abort(file, result, size);
    541 		else
    542 			rc = memfile_close(file, result, size);
    543 	}
    544 	return rc;
    545 }
    546 
    547 int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file)
    548 {
    549 	return mustach_file(template, 0, itf, closure, Mustach_With_AllExtensions, file);
    550 }
    551 
    552 int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd)
    553 {
    554 	return mustach_fd(template, 0, itf, closure, Mustach_With_AllExtensions, fd);
    555 }
    556 
    557 int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size)
    558 {
    559 	return mustach_mem(template, 0, itf, closure, Mustach_With_AllExtensions, result, size);
    560 }
    561