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