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("<", 4, 1, file); 165 break; 166 case '>': 167 r = fwrite(">", 4, 1, file); 168 break; 169 case '&': 170 r = fwrite("&", 5, 1, file); 171 break; 172 case '"': 173 r = fwrite(""", 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