iodine 0.7.11 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

@@ -71,7 +71,7 @@ Feel free to copy, use and enjoy according to the license provided.
71
71
 
72
72
  #if !defined(MUSTACHE_NESTING_LIMIT) || !MUSTACHE_NESTING_LIMIT
73
73
  #undef MUSTACHE_NESTING_LIMIT
74
- #define MUSTACHE_NESTING_LIMIT 64
74
+ #define MUSTACHE_NESTING_LIMIT 96
75
75
  #endif
76
76
 
77
77
  #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
@@ -103,7 +103,10 @@ typedef enum mustache_error_en {
103
103
  MUSTACHE_ERR_FILE_NOT_FOUND,
104
104
  MUSTACHE_ERR_FILE_TOO_BIG,
105
105
  MUSTACHE_ERR_FILE_NAME_TOO_LONG,
106
+ MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
106
107
  MUSTACHE_ERR_EMPTY_TEMPLATE,
108
+ MUSTACHE_ERR_DELIMITER_TOO_LONG,
109
+ MUSTACHE_ERR_NAME_TOO_LONG,
107
110
  MUSTACHE_ERR_UNKNOWN,
108
111
  MUSTACHE_ERR_USER_ERROR,
109
112
  } mustache_error_en;
@@ -164,7 +167,7 @@ MUSTACHE_FUNC int mustache_build(mustache_build_args_s args);
164
167
  (mustache_build_args_s){.mustache = (mustache_s_ptr), __VA_ARGS__})
165
168
 
166
169
  /* *****************************************************************************
167
- Client Callbacks - MUST be implemented by the including file
170
+ Callbacks Types - types used by the template builder callbacks
168
171
  ***************************************************************************** */
169
172
 
170
173
  /**
@@ -174,15 +177,6 @@ Client Callbacks - MUST be implemented by the including file
174
177
  * Note that every section is allowed a separate udata value.
175
178
  */
176
179
  typedef struct mustache_section_s {
177
- /**
178
- * READ ONLY. The parent section (when nesting), if any.
179
- *
180
- * This is important for accessing the parent's `udata` values when searching
181
- * for an argument's value.
182
- *
183
- * The root's parent is NULL.
184
- */
185
- struct mustache_section_s *parent;
186
180
  /** Opaque user data (recommended for input review) - children will inherit
187
181
  * the parent's udata value. Updated values will propegate to child sections
188
182
  * but won't effect parent sections.
@@ -195,6 +189,36 @@ typedef struct mustache_section_s {
195
189
  void *udata2;
196
190
  } mustache_section_s;
197
191
 
192
+ /* *****************************************************************************
193
+ Callbacks Helpers - These functions can be called from within callbacks
194
+ ***************************************************************************** */
195
+
196
+ /**
197
+ * Returns the section's parent for nested sections, or NULL (for root section).
198
+ *
199
+ * This allows the `udata` values in the parent to be accessed and can be used,
200
+ * for example, when seeking a value (argument / keyword) within a nested data
201
+ * structure such as a Hash.
202
+ */
203
+ static inline mustache_section_s *
204
+ mustache_section_parent(mustache_section_s *section);
205
+
206
+ /**
207
+ * Returns the section's unparsed content as a non-NUL terminated byte array.
208
+ *
209
+ * The length of the data will be placed in the `size_t` variable pointed to by
210
+ * `p_len`. Do NOT use `strlen`, since the data isn't NUL terminated.
211
+ *
212
+ * This allows text to be accessed when a section's content is, in fact, meant
213
+ * to be passed along to a function / lambda.
214
+ */
215
+ static inline const char *mustache_section_text(mustache_section_s *section,
216
+ size_t *p_len);
217
+
218
+ /* *****************************************************************************
219
+ Client Callbacks - MUST be implemented by the including file
220
+ ***************************************************************************** */
221
+
198
222
  /**
199
223
  * Called when an argument name was detected in the current section.
200
224
  *
@@ -206,6 +230,9 @@ typedef struct mustache_section_s {
206
230
  *
207
231
  * A conforming implementation will output the named argument's value (either
208
232
  * HTML escaped or not, depending on the `escape` flag) as a string.
233
+ *
234
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
235
+ * determine the actual string length.
209
236
  */
210
237
  static int mustache_on_arg(mustache_section_s *section, const char *name,
211
238
  uint32_t name_len, unsigned char escape);
@@ -214,6 +241,9 @@ static int mustache_on_arg(mustache_section_s *section, const char *name,
214
241
  * Called when simple template text (string) is detected.
215
242
  *
216
243
  * A conforming implementation will output data as a string (no escaping).
244
+ *
245
+ * NOTE: the `data` is **not** NUL terminated. Use the `data_len` data to
246
+ * determine the actual data length.
217
247
  */
218
248
  static int mustache_on_text(mustache_section_s *section, const char *data,
219
249
  uint32_t data_len);
@@ -231,9 +261,18 @@ static int mustache_on_text(mustache_section_s *section, const char *data,
231
261
  * A return value of -1 will stop processing with an error.
232
262
  *
233
263
  * Please note, this will handle both normal and inverted sections.
264
+ *
265
+ * The `callable` value is true if the section is allowed to be a function /
266
+ * callback. If the section object represent a function / callback (lambda), the
267
+ * lambda should be called and the `mustache_on_section_test` callback should
268
+ * return 0.
269
+ *
270
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
271
+ * determine the actual string length.
234
272
  */
235
273
  static int32_t mustache_on_section_test(mustache_section_s *section,
236
- const char *name, uint32_t name_len);
274
+ const char *name, uint32_t name_len,
275
+ uint8_t callable);
237
276
 
238
277
  /**
239
278
  * Called when entering a nested section.
@@ -246,6 +285,9 @@ static int32_t mustache_on_section_test(mustache_section_s *section,
246
285
  * Note: this is a good time to update the subsection's `udata` with the value
247
286
  * of the array index. The `udata` will always contain the value or the parent's
248
287
  * `udata`.
288
+ *
289
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
290
+ * determine the actual string length.
249
291
  */
250
292
  static int mustache_on_section_start(mustache_section_s *section,
251
293
  char const *name, uint32_t name_len,
@@ -289,13 +331,421 @@ typedef struct mustache__instruction_s {
289
331
  } instruction;
290
332
  /** the data the instruction acts upon */
291
333
  struct {
292
- /** The offset from the beginning of the data segment. */
293
- uint32_t start;
294
- /** The length of the data. */
334
+ /** The length instruction block in the instruction array (for sections). */
335
+ uint32_t end;
336
+ /** The length of the (string) data. */
295
337
  uint32_t len;
338
+ /** The offset from the beginning of the data segment. */
339
+ uint32_t name_pos;
340
+ /** The offset between the name (start) and content (for sections). */
341
+ uint16_t name_len;
342
+ /** The offset between the name and the content (left / right by type). */
343
+ uint16_t offset;
296
344
  } data;
297
345
  } mustache__instruction_s;
298
346
 
347
+ typedef struct {
348
+ mustache_section_s sec; /* client visible section data */
349
+ uint32_t start; /* section start instruction position */
350
+ uint32_t end; /* instruction to jump to after completion */
351
+ uint32_t index; /* zero based index for section loops */
352
+ uint32_t count; /* the number of times the section should be performed */
353
+ uint16_t frame; /* the stack frame's index (zero based) */
354
+ } mustache__section_stack_frame_s;
355
+
356
+ typedef struct {
357
+ mustache_s *data; /* the mustache template being built */
358
+ uint32_t pos; /* the instruction postision index */
359
+ uint16_t index; /* the stack postision index */
360
+ mustache__section_stack_frame_s stack[MUSTACHE_NESTING_LIMIT];
361
+ } mustache__builder_stack_s;
362
+
363
+ #define MUSTACHE_DELIMITER_LENGTH_LIMIT 11
364
+
365
+ typedef struct {
366
+ mustache_s *m;
367
+ mustache__instruction_s *i;
368
+ mustache_error_en *err;
369
+ char *data;
370
+ char *path;
371
+ uint32_t i_capa;
372
+ uint32_t data_len;
373
+ uint16_t path_len;
374
+ uint16_t path_capa;
375
+ uint16_t index; /* stack index */
376
+ struct {
377
+ uint32_t data_start; /* template starting position (with header) */
378
+ uint32_t data_pos; /* data reading position (how much was consumed) */
379
+ uint32_t data_end; /* data ending position (for this template) */
380
+ uint16_t open_sections; /* counts open sections waiting for closure */
381
+ char del_start[MUSTACHE_DELIMITER_LENGTH_LIMIT]; /* currunt instruction
382
+ start delimiter */
383
+ char del_end[MUSTACHE_DELIMITER_LENGTH_LIMIT]; /* currunt instruction end
384
+ delimiter */
385
+ uint8_t del_start_len; /* currunt start delimiter length */
386
+ uint8_t del_end_len; /* currunt end delimiter length */
387
+ } stack[MUSTACHE_NESTING_LIMIT];
388
+ } mustache__loader_stack_s;
389
+
390
+ /* *****************************************************************************
391
+ Callbacks Helper Implementation
392
+ ***************************************************************************** */
393
+
394
+ #define MUSTACH2INSTRUCTIONS(mustache) \
395
+ ((mustache__instruction_s *)((mustache) + 1))
396
+ #define MUSTACH2DATA(mustache) \
397
+ (char *)(MUSTACH2INSTRUCTIONS((mustache)) + \
398
+ (mustache)->u.read_only.intruction_count)
399
+ #define MUSTACHE_OBJECT_OFFSET(type, member, ptr) \
400
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)(&(((type *)0)->member))))
401
+
402
+ #define MUSTACHE_ASSERT(cond, msg) \
403
+ if (!(cond)) { \
404
+ perror("FATAL ERROR: " msg); \
405
+ exit(errno); \
406
+ }
407
+ #define MUSTACHE_IGNORE_WHITESPACE(str, step) \
408
+ while (isspace(*(str))) { \
409
+ (str) += (step); \
410
+ }
411
+
412
+ /**
413
+ * Returns the section's parent for nested sections, or NULL (for root section).
414
+ *
415
+ * This allows the `udata` values in the parent to be accessed and can be used,
416
+ * for example, when seeking a value (argument / keyword) within a nested data
417
+ * structure such as a Hash.
418
+ */
419
+ static inline mustache_section_s *
420
+ mustache_section_parent(mustache_section_s *section) {
421
+ mustache_section_s tmp = *section;
422
+ mustache__section_stack_frame_s *f =
423
+ (mustache__section_stack_frame_s *)section;
424
+ while (f->frame) {
425
+ --f;
426
+ if (tmp.udata1 != f->sec.udata1 || tmp.udata2 != f->sec.udata2)
427
+ return &f->sec;
428
+ }
429
+ return NULL;
430
+ }
431
+
432
+ /**
433
+ * Returns the section's unparsed content as a non-NUL terminated byte array.
434
+ *
435
+ * The length of the data will be placed in the `size_t` variable pointed to by
436
+ * `p_len`. Do NOT use `strlen`, since the data isn't NUL terminated.
437
+ *
438
+ * This allows text to be accessed when a section's content is, in fact, meant
439
+ * to be passed along to a function / lambda.
440
+ */
441
+ static inline const char *mustache_section_text(mustache_section_s *section,
442
+ size_t *p_len) {
443
+ if (!section || !p_len)
444
+ goto error;
445
+ mustache__section_stack_frame_s *f =
446
+ (mustache__section_stack_frame_s *)section;
447
+ mustache__builder_stack_s *s =
448
+ MUSTACHE_OBJECT_OFFSET(mustache__builder_stack_s, stack, (f - f->frame));
449
+ mustache__instruction_s *inst =
450
+ MUSTACH2INSTRUCTIONS(s->data) + s->pos; /* current instruction*/
451
+ if (inst->instruction != MUSTACHE_SECTION_START)
452
+ goto error;
453
+ const char *start =
454
+ MUSTACH2DATA(s->data) + inst->data.name_pos + inst->data.offset;
455
+ *p_len = inst->data.len;
456
+ return start;
457
+ error:
458
+ *p_len = 0;
459
+ return NULL;
460
+ }
461
+
462
+ /* *****************************************************************************
463
+ Internal Helpers
464
+ ***************************************************************************** */
465
+
466
+ /* the data segment's new length after loading the the template */
467
+ /* The data segments includes a template header: */
468
+ /* | 4 bytes template start instruction position | */
469
+ /* | 2 bytes template name | 4 bytes next template position | */
470
+ /* | template name (filename) | ...[template data]... */
471
+ /* this allows template data to be reused when repeating a template */
472
+
473
+ /** a template file data segment header */
474
+ typedef struct {
475
+ const char *filename; /* template file name */
476
+ uint32_t inst_start; /* start position for instructions */
477
+ uint32_t next; /* next template position (absolute) */
478
+ uint16_t filename_len; /* file name length */
479
+ uint16_t path_len; /* if the file is in a folder, this marks the '/' */
480
+ } mustache__data_segment_s;
481
+
482
+ /* writes the data to dest, returns the number of bytes written. */
483
+ static inline size_t
484
+ mustache__data_segment_write(uint8_t *dest, mustache__data_segment_s data) {
485
+ dest[0] = 0xFF & data.inst_start;
486
+ dest[1] = 0xFF & (data.inst_start >> 1);
487
+ dest[2] = 0xFF & (data.inst_start >> 2);
488
+ dest[3] = 0xFF & (data.inst_start >> 3);
489
+ dest[4] = 0xFF & data.next;
490
+ dest[5] = 0xFF & (data.next >> 1);
491
+ dest[6] = 0xFF & (data.next >> 2);
492
+ dest[7] = 0xFF & (data.next >> 3);
493
+ dest[8] = 0xFF & data.filename_len;
494
+ dest[9] = 0xFF & (data.filename_len >> 1);
495
+ dest[10] = 0xFF & data.path_len;
496
+ dest[11] = 0xFF & (data.path_len >> 1);
497
+ if (data.filename_len)
498
+ memcpy(dest + 12, data.filename, data.filename_len);
499
+ (dest + 12)[data.filename_len] = 0;
500
+ return 13 + data.filename_len;
501
+ }
502
+
503
+ static inline size_t mustache__data_segment_length(size_t filename_len) {
504
+ return 13 + filename_len;
505
+ }
506
+ static inline mustache__data_segment_s
507
+ mustache__data_segment_read(uint8_t *data) {
508
+ mustache__data_segment_s s = {
509
+ .filename = (char *)(data + 12),
510
+ .inst_start = ((uint32_t)data[0] | ((uint32_t)data[1] << 1) |
511
+ ((uint32_t)data[2] << 2) | ((uint32_t)data[3] << 3)),
512
+ .next = ((uint32_t)data[4] | ((uint32_t)data[5] << 1) |
513
+ ((uint32_t)data[6] << 2) | ((uint32_t)data[7] << 3)),
514
+ .filename_len = ((uint16_t)data[8] | ((uint16_t)data[9] << 1)),
515
+ .path_len = ((uint16_t)data[10] | ((uint16_t)data[11] << 1)),
516
+ };
517
+ return s;
518
+ }
519
+
520
+ /* pushes an instruction to the instruction array */
521
+ static inline int mustache__instruction_push(mustache__loader_stack_s *s,
522
+ mustache__instruction_s inst) {
523
+ if (s->m->u.read_only.intruction_count >= INT32_MAX)
524
+ goto instructions_too_long;
525
+ if (s->i_capa <= s->m->u.read_only.intruction_count) {
526
+ s->m = realloc(s->m, sizeof(*s->m) + (sizeof(*s->i) * (s->i_capa + 32)));
527
+ MUSTACHE_ASSERT(s->m, "Mustache memory allocation failed");
528
+ s->i_capa += 32;
529
+ s->i = MUSTACH2INSTRUCTIONS(s->m);
530
+ }
531
+ s->i[s->m->u.read_only.intruction_count] = inst;
532
+ ++s->m->u.read_only.intruction_count;
533
+ return 0;
534
+ instructions_too_long:
535
+ *s->err = MUSTACHE_ERR_TOO_DEEP;
536
+ return -1;
537
+ }
538
+
539
+ /*
540
+ * Returns the instruction's position if the template is already existing.
541
+ *
542
+ * Returns `(uint32_t)-1` if the template is missing (not loaded, yet).
543
+ */
544
+ static inline uint32_t mustache__file_is_loaded(mustache__loader_stack_s *s,
545
+ char *name, size_t name_len) {
546
+ char *data = s->data;
547
+ const char *end = data + s->m->u.read_only.data_length;
548
+ while (data < end) {
549
+ mustache__data_segment_s seg = mustache__data_segment_read((uint8_t *)data);
550
+ if (seg.filename_len == name_len && !memcmp(seg.filename, name, name_len))
551
+ return seg.inst_start;
552
+ data += seg.next;
553
+ }
554
+ return (uint32_t)-1;
555
+ }
556
+
557
+ static inline int mustache__load_data(mustache__loader_stack_s *s,
558
+ const char *name, size_t name_len,
559
+ const char *data, size_t data_len) {
560
+ const size_t old_len = s->data_len;
561
+ if (old_len + data_len > UINT32_MAX)
562
+ goto too_long;
563
+ s->data = realloc(s->data, old_len + data_len +
564
+ mustache__data_segment_length(name_len) + 1);
565
+ MUSTACHE_ASSERT(s->data,
566
+ "failed to allocate memory for mustache template data");
567
+ size_t path_len = name_len;
568
+ while (path_len) {
569
+ --path_len;
570
+ if (name[path_len] == '/' || name[path_len] == '\\') {
571
+ ++path_len;
572
+ break;
573
+ }
574
+ }
575
+ mustache__data_segment_write(
576
+ (uint8_t *)s->data + old_len,
577
+ (mustache__data_segment_s){
578
+ .filename = name,
579
+ .filename_len = name_len,
580
+ .inst_start = s->m->u.read_only.intruction_count,
581
+ .next =
582
+ s->data_len + data_len + mustache__data_segment_length(name_len),
583
+ .path_len = path_len,
584
+ });
585
+ if (data) {
586
+ memcpy(s->data + old_len + mustache__data_segment_length(name_len), data,
587
+ data_len);
588
+ }
589
+ s->data_len += data_len + mustache__data_segment_length(name_len);
590
+ s->data[s->data_len] = 0;
591
+ s->m->u.read_only.data_length = s->data_len;
592
+
593
+ mustache__instruction_push(
594
+ s, (mustache__instruction_s){.instruction = MUSTACHE_SECTION_START});
595
+ /* advance stack frame */
596
+ ++s->index;
597
+ if (s->index >= MUSTACHE_NESTING_LIMIT)
598
+ goto too_long;
599
+ s->stack[s->index].data_pos =
600
+ old_len + mustache__data_segment_length(name_len);
601
+ s->stack[s->index].data_start = old_len;
602
+ s->stack[s->index].data_end = s->data_len;
603
+ /* reset delimiters */
604
+ s->stack[s->index].del_start_len = 2;
605
+ s->stack[s->index].del_end_len = 2;
606
+ s->stack[s->index].del_start[0] = s->stack[s->index].del_start[1] = '{';
607
+ s->stack[s->index].del_start[2] = 0;
608
+ s->stack[s->index].del_end[0] = s->stack[s->index].del_end[1] = '}';
609
+ s->stack[s->index].del_end[2] = 0;
610
+ s->stack[s->index].open_sections = 0;
611
+ return 0;
612
+ too_long:
613
+ *s->err = MUSTACHE_ERR_TOO_DEEP;
614
+ return -1;
615
+ }
616
+
617
+ static inline int mustache__load_file(mustache__loader_stack_s *s,
618
+ const char *name, size_t name_len) {
619
+ struct stat f_data;
620
+ uint16_t i = s->index;
621
+ uint32_t old_path_len = 0;
622
+ if (!name_len) {
623
+ goto name_missing_error;
624
+ }
625
+ if (name_len >= 8192)
626
+ goto name_length_error;
627
+ /* test file names by walking the stack backwards and matching paths */
628
+ do {
629
+ mustache__data_segment_s seg;
630
+ if (s->data)
631
+ seg = mustache__data_segment_read((uint8_t *)s->data +
632
+ s->stack[i].data_start);
633
+ else
634
+ seg = (mustache__data_segment_s){
635
+ .path_len = 0,
636
+ };
637
+ /* did we test this path for the file? */
638
+ if (old_path_len && (old_path_len == seg.path_len &&
639
+ !memcmp(s->path, seg.filename, old_path_len))) {
640
+ continue;
641
+ }
642
+ old_path_len = seg.path_len;
643
+ /* make sure s->path capacity is enough. */
644
+ if (s->path_capa < seg.path_len + name_len + 10) {
645
+ s->path = realloc(s->path, seg.path_len + name_len + 10);
646
+ MUSTACHE_ASSERT(s->path,
647
+ "failed to allocate memory for mustache template data");
648
+ s->path_capa = seg.path_len + name_len + 10;
649
+ }
650
+ /* if testing local folder, there's no need to keep looping */
651
+ if (!seg.path_len) {
652
+ i = 1;
653
+ } else {
654
+ memcpy(s->path, seg.filename, seg.path_len);
655
+ }
656
+ /* copy name to path */
657
+ memcpy(s->path + seg.path_len, name, name_len);
658
+ s->path[name_len + seg.path_len] = 0;
659
+ /* test if file exists */
660
+ if (!stat(s->path, &f_data) && S_ISREG(f_data.st_mode)) {
661
+ old_path_len = name_len + seg.path_len;
662
+ goto file_found;
663
+ }
664
+ /* add default extension */
665
+ memcpy(s->path + seg.path_len + name_len, ".mustache", 9);
666
+ s->path[name_len + seg.path_len + 9] = 0;
667
+ /* test if new filename file exists */
668
+ if (!stat(s->path, &f_data) && S_ISREG(f_data.st_mode)) {
669
+ old_path_len = name_len + seg.path_len + 9;
670
+ goto file_found;
671
+ }
672
+ } while (--i);
673
+
674
+ /* test if the file is "virtual" (only true for the first template loaded) */
675
+ if (s->data) {
676
+ mustache__data_segment_s seg =
677
+ mustache__data_segment_read((uint8_t *)s->data);
678
+ if (seg.filename_len == name_len && !memcmp(seg.filename, name, name_len)) {
679
+ /* this name points to the original (root) template, and it's virtual */
680
+ if (mustache__instruction_push(
681
+ s, (mustache__instruction_s){
682
+ .instruction = MUSTACHE_SECTION_GOTO,
683
+ .data =
684
+ {
685
+ .len = 0,
686
+ .end = s->m->u.read_only.intruction_count,
687
+ },
688
+ }))
689
+ goto unknown_error;
690
+ return 0;
691
+ }
692
+ }
693
+
694
+ *s->err = MUSTACHE_ERR_FILE_NOT_FOUND;
695
+ return -1;
696
+
697
+ file_found:
698
+ if (f_data.st_size >= INT32_MAX) {
699
+ goto file_too_big;
700
+ } else {
701
+ /* test if the file was previously loaded */
702
+ uint32_t pre_existing = mustache__file_is_loaded(s, s->path, old_path_len);
703
+ if (pre_existing != (uint32_t)-1) {
704
+ if (mustache__instruction_push(
705
+ s, (mustache__instruction_s){
706
+ .instruction = MUSTACHE_SECTION_GOTO,
707
+ .data =
708
+ {
709
+ .len = pre_existing,
710
+ .end = s->m->u.read_only.intruction_count,
711
+ },
712
+ })) {
713
+ goto unknown_error;
714
+ }
715
+ return 0;
716
+ }
717
+ }
718
+ if (mustache__load_data(s, s->path, old_path_len, NULL, f_data.st_size))
719
+ goto unknown_error;
720
+ int fd = open(s->path, O_RDONLY);
721
+ if (fd == -1)
722
+ goto file_err;
723
+ if (pread(fd, s->data + s->data_len - f_data.st_size, f_data.st_size, 0) !=
724
+ f_data.st_size)
725
+ goto file_err;
726
+ close(fd);
727
+ return 0;
728
+
729
+ name_missing_error:
730
+ *s->err = MUSTACHE_ERR_FILE_NAME_TOO_SHORT;
731
+ return -1;
732
+
733
+ name_length_error:
734
+ *s->err = MUSTACHE_ERR_FILE_NAME_TOO_LONG;
735
+ return -1;
736
+
737
+ file_too_big:
738
+ *s->err = MUSTACHE_ERR_FILE_TOO_BIG;
739
+ return -1;
740
+
741
+ file_err:
742
+ *s->err = MUSTACHE_ERR_UNKNOWN;
743
+ return -1;
744
+
745
+ unknown_error:
746
+ return -1;
747
+ }
748
+
299
749
  /* *****************************************************************************
300
750
  Calling the instrustion list (using the template engine)
301
751
  ***************************************************************************** */
@@ -313,215 +763,112 @@ Calling the instrustion list (using the template engine)
313
763
  * - Data segment: text and data related to the instructions.
314
764
  *
315
765
  * The instructions, much like machine code, might loop or jump. This is why the
316
- * functions keep a stack of sorts. This allows the code to avoid recursion and
766
+ * function keeps a stack of sorts. This allows the code to avoid recursion and
317
767
  * minimize any risk of stack overflow caused by recursive templates.
318
- *
319
- * Note:
320
- *
321
- * For text and argument instructions, the mustache__instruction_s.data.start
322
- * and mustache__instruction_s.data.len mark the beginning of the text/argument
323
- * and it's name.
324
- *
325
- * However, for MUSTACHE_SECTION_START instructions, data.len marks position for
326
- * the complementing MUSTACHE_SECTION_END instruction, allowing for easy jumps
327
- * in cases where a section is skipped or in cases of a recursive template.
328
768
  */
329
769
  MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
770
+ mustache_error_en err_if_missing;
771
+ if (!args.err)
772
+ args.err = &err_if_missing;
773
+ if (!args.mustache) {
774
+ goto user_error;
775
+ }
330
776
  /* extract the instruction array and data segment from the mustache_s */
331
- mustache__instruction_s *pos =
332
- (mustache__instruction_s *)(sizeof(*args.mustache) +
333
- (uintptr_t)args.mustache);
334
- mustache__instruction_s *const start = pos;
335
- mustache__instruction_s *const end =
336
- pos + args.mustache->u.read_only.intruction_count;
337
- char *const data = (char *const)end;
338
-
339
- /* prepare a pre-allocated stack space to flatten recursion needs */
340
- struct {
341
- mustache_section_s sec; /* client visible section data */
342
- uint32_t start; /* section start instruction position */
343
- uint32_t end; /* instruction to jump to after completion */
344
- uint32_t index; /* zero based index forr section loops */
345
- uint32_t count; /* the number of times the section should be performed */
346
- } section_stack[MUSTACHE_NESTING_LIMIT];
347
-
348
- /* first section (section 0) data */
349
- section_stack[0].sec = (mustache_section_s){
350
- .udata1 = args.udata1,
351
- .udata2 = args.udata2,
777
+ mustache__instruction_s *instructions = MUSTACH2INSTRUCTIONS(args.mustache);
778
+ char *const data = MUSTACH2DATA(args.mustache);
779
+ mustache__builder_stack_s s;
780
+ s.data = args.mustache;
781
+ s.pos = 0;
782
+ s.index = 0;
783
+ s.stack[0] = (mustache__section_stack_frame_s){
784
+ .sec =
785
+ {
786
+ .udata1 = args.udata1,
787
+ .udata2 = args.udata2,
788
+ },
789
+ .start = 0,
790
+ .end = instructions[0].data.end,
791
+ .index = 0,
792
+ .count = 0,
793
+ .frame = 0,
352
794
  };
353
- section_stack[0].end = 0;
354
- uint32_t nesting_pos = 0;
355
-
356
- /* run through the instruction list and persorm each instruction */
357
- while (pos < end) {
358
- switch (pos->instruction) {
359
-
795
+ while ((uintptr_t)(instructions + s.pos) < (uintptr_t)data) {
796
+ switch (instructions[s.pos].instruction) {
360
797
  case MUSTACHE_WRITE_TEXT:
361
- if (mustache_on_text(&section_stack[nesting_pos].sec,
362
- data + pos->data.start, pos->data.len) == -1) {
363
- if (args.err) {
364
- *args.err = MUSTACHE_ERR_USER_ERROR;
365
- }
366
- goto error;
367
- }
798
+ if (mustache_on_text(&s.stack[s.index].sec,
799
+ data + instructions[s.pos].data.name_pos,
800
+ instructions[s.pos].data.name_len))
801
+ goto user_error;
368
802
  break;
369
-
370
- case MUSTACHE_WRITE_ARG:
371
- if (mustache_on_arg(&section_stack[nesting_pos].sec,
372
- data + pos->data.start, pos->data.len, 1) == -1) {
373
- if (args.err) {
374
- *args.err = MUSTACHE_ERR_USER_ERROR;
375
- }
376
- goto error;
377
- }
803
+ case MUSTACHE_WRITE_ARG: /* overflow */
804
+ if (mustache_on_arg(&s.stack[s.index].sec,
805
+ data + instructions[s.pos].data.name_pos,
806
+ instructions[s.pos].data.name_len, 1))
807
+ goto user_error;
378
808
  break;
379
-
380
809
  case MUSTACHE_WRITE_ARG_UNESCAPED:
381
- if (mustache_on_arg(&section_stack[nesting_pos].sec,
382
- data + pos->data.start, pos->data.len, 0) == -1) {
383
- if (args.err) {
384
- *args.err = MUSTACHE_ERR_USER_ERROR;
385
- }
386
- goto error;
387
- }
810
+ if (mustache_on_arg(&s.stack[s.index].sec,
811
+ data + instructions[s.pos].data.name_pos,
812
+ instructions[s.pos].data.name_len, 0))
813
+ goto user_error;
388
814
  break;
389
- case MUSTACHE_SECTION_START_INV: /* overfloaw*/
390
- case MUSTACHE_SECTION_START: {
391
- /* starting a new section, increased nesting & review */
392
- if (nesting_pos + 1 == MUSTACHE_NESTING_LIMIT) {
393
- if (args.err) {
815
+ case MUSTACHE_SECTION_GOTO: /* overflow */
816
+ case MUSTACHE_SECTION_START: /* overflow */
817
+ case MUSTACHE_SECTION_START_INV:
818
+ /* advance stack*/
819
+ if (s.index + 1 >= MUSTACHE_NESTING_LIMIT) {
820
+ if (args.err)
394
821
  *args.err = MUSTACHE_ERR_TOO_DEEP;
395
- }
396
- goto error;
397
- }
398
- section_stack[nesting_pos + 1].sec = section_stack[nesting_pos].sec;
399
- ++nesting_pos;
400
-
401
- /* find the end of the section */
402
- mustache__instruction_s *section_end = start + pos->data.len;
403
-
404
- /* test for template (partial) section (nameless) */
405
- if (pos->data.start == 0) {
406
- section_stack[nesting_pos].end = section_end - start;
407
- section_stack[nesting_pos].start = pos - start;
408
- section_stack[nesting_pos].index = 1;
409
- section_stack[nesting_pos].count = 0;
410
- break;
411
- }
412
-
413
- /* test for user abort signal and cycle value */
414
- int32_t val = mustache_on_section_test(&section_stack[nesting_pos].sec,
415
- data + pos->data.start,
416
- strlen(data + pos->data.start));
417
- if (val == -1) {
418
- if (args.err) {
419
- *args.err = MUSTACHE_ERR_USER_ERROR;
420
- }
421
822
  goto error;
422
823
  }
423
- if (pos->instruction == MUSTACHE_SECTION_START_INV) {
424
- if (val == 0) {
425
- /* perform once for inverted sections */
426
- val = 1;
427
- } else {
428
- /* or don't perform */
429
- val = 0;
824
+ s.stack[s.index + 1].sec = s.stack[s.index].sec;
825
+ ++s.index;
826
+ s.stack[s.index].start =
827
+ (instructions[s.pos].instruction == MUSTACHE_SECTION_GOTO
828
+ ? instructions[s.pos].data.len
829
+ : s.pos);
830
+ s.stack[s.index].end = instructions[s.pos].data.end;
831
+ s.stack[s.index].frame = s.index;
832
+ s.stack[s.index].index = 0;
833
+ s.stack[s.index].count = 1;
834
+
835
+ /* test section count */
836
+ if (instructions[s.pos].data.name_pos) {
837
+ /* this is a named section, it should be tested against user data */
838
+ int32_t val = mustache_on_section_test(
839
+ &s.stack[s.index].sec, data + instructions[s.pos].data.name_pos,
840
+ instructions[s.pos].data.name_len,
841
+ instructions[s.pos].instruction == MUSTACHE_SECTION_START);
842
+ if (val == -1) {
843
+ goto user_error;
430
844
  }
431
- }
432
-
433
- if (val == 0) {
434
- --nesting_pos;
435
- pos = section_end;
436
- } else {
437
- /* save start/end positions and index counter */
438
- section_stack[nesting_pos].end = section_end - start;
439
- section_stack[nesting_pos].start = pos - start;
440
- section_stack[nesting_pos].index = val;
441
- section_stack[nesting_pos].count = 0;
442
- if (mustache_on_section_start(&section_stack[nesting_pos].sec,
443
- data + pos->data.start,
444
- strlen(data + pos->data.start),
445
- section_stack[nesting_pos].count) == -1) {
446
- if (args.err) {
447
- *args.err = MUSTACHE_ERR_USER_ERROR;
448
- }
449
- goto error;
845
+ if (instructions[s.pos].instruction == MUSTACHE_SECTION_START_INV) {
846
+ /* invert test */
847
+ val = (val == 0);
450
848
  }
849
+ s.stack[s.index].count = (uint32_t)val;
451
850
  }
452
- break;
453
- }
454
851
 
852
+ /* overflow */
455
853
  case MUSTACHE_SECTION_END:
456
- ++section_stack[nesting_pos].count;
457
- if (section_stack[nesting_pos].index > section_stack[nesting_pos].count) {
458
- pos = start + section_stack[nesting_pos].start;
459
- if (nesting_pos) { /* revert to old udata values */
460
- section_stack[nesting_pos].sec = section_stack[nesting_pos - 1].sec;
461
- }
462
- if (mustache_on_section_start(&section_stack[nesting_pos].sec,
463
- data + pos->data.start,
464
- strlen(data + pos->data.start),
465
- section_stack[nesting_pos].count)) {
466
- if (args.err) {
467
- *args.err = MUSTACHE_ERR_USER_ERROR;
468
- }
469
- goto error;
470
- }
471
- } else {
472
- pos = start + section_stack[nesting_pos].end; /* in case of recursion */
473
- --nesting_pos;
474
- }
475
- break;
476
-
477
- case MUSTACHE_SECTION_GOTO: {
478
- /* used to handle recursive sections and re-occuring partials. */
479
- if (nesting_pos + 1 == MUSTACHE_NESTING_LIMIT) {
480
- if (args.err) {
481
- *args.err = MUSTACHE_ERR_TOO_DEEP;
482
- }
483
- goto error;
484
- }
485
- section_stack[nesting_pos + 1].sec = section_stack[nesting_pos].sec;
486
- ++nesting_pos;
487
- if (start[pos->data.len].data.start == 0) {
488
- section_stack[nesting_pos].end = pos - start;
489
- section_stack[nesting_pos].index = 1;
490
- section_stack[nesting_pos].count = 0;
491
- section_stack[nesting_pos].start = pos->data.len;
492
- pos = start + pos->data.len;
854
+ /* loop section or continue */
855
+ if (s.stack[s.index].index < s.stack[s.index].count) {
856
+ /* repeat / start section */
857
+ s.pos = s.stack[s.index].start;
858
+ s.stack[s.index].sec = s.stack[s.index - 1].sec;
859
+ /* review user callback (if it's a named section) */
860
+ if (instructions[s.pos].data.name_pos &&
861
+ mustache_on_section_start(&s.stack[s.index].sec,
862
+ data + instructions[s.pos].data.name_pos,
863
+ instructions[s.pos].data.name_len,
864
+ s.stack[s.index].index) == -1)
865
+ goto user_error;
866
+ ++s.stack[s.index].index;
493
867
  break;
494
868
  }
495
- int32_t val = mustache_on_section_test(
496
- &section_stack[nesting_pos].sec,
497
- data + start[pos->data.len].data.start,
498
- strlen(data + start[pos->data.len].data.start));
499
- if (val == -1) {
500
- if (args.err) {
501
- *args.err = MUSTACHE_ERR_USER_ERROR;
502
- }
503
- goto error;
504
- }
505
- if (val == 0) {
506
- --nesting_pos;
507
- } else {
508
- /* save start/end positions and index counter */
509
- section_stack[nesting_pos].end = pos - start;
510
- section_stack[nesting_pos].index = val;
511
- section_stack[nesting_pos].count = 0;
512
- section_stack[nesting_pos].start = pos->data.len;
513
- pos = start + pos->data.len;
514
- if (mustache_on_section_start(&section_stack[nesting_pos].sec,
515
- data + pos->data.start,
516
- strlen(data + pos->data.start),
517
- section_stack[nesting_pos].count) == -1) {
518
- if (args.err) {
519
- *args.err = MUSTACHE_ERR_USER_ERROR;
520
- }
521
- goto error;
522
- }
523
- }
524
- } break;
869
+ s.pos = s.stack[s.index].end;
870
+ --s.index;
871
+ break;
525
872
  default:
526
873
  /* not a valid engine */
527
874
  fprintf(stderr, "ERROR: invalid mustache instruction set detected (wrong "
@@ -531,10 +878,12 @@ MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
531
878
  }
532
879
  goto error;
533
880
  }
534
- ++pos;
881
+ ++s.pos;
535
882
  }
536
-
883
+ *args.err = MUSTACHE_OK;
537
884
  return 0;
885
+ user_error:
886
+ *args.err = MUSTACHE_ERR_USER_ERROR;
538
887
  error:
539
888
  mustache_on_formatting_error(args.udata1, args.udata2);
540
889
  return -1;
@@ -546,360 +895,91 @@ Building the instrustion list (parsing the template)
546
895
 
547
896
  /* The parsing implementation, converts a template to an instruction array */
548
897
  MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
549
- /* Make sure the args string length is set and prepare the path name */
550
- char *path = NULL;
551
- uint32_t path_capa = 0;
552
- uint32_t path_len = 0;
553
- if (args.filename && !args.filename_len) {
898
+ mustache_error_en err_if_missing;
899
+ mustache__loader_stack_s s;
900
+
901
+ if (!args.err)
902
+ args.err = &err_if_missing;
903
+ s.path_capa = 0;
904
+ s.path = NULL;
905
+ s.data = NULL;
906
+ s.data_len = 0;
907
+ s.i = NULL;
908
+ s.i_capa = 32;
909
+ s.index = 0;
910
+ s.m = malloc(sizeof(*s.m) + (sizeof(*s.i) * 32));
911
+ MUSTACHE_ASSERT(s.m, "failed to allocate memory for mustache data");
912
+ s.m->u.read_only_pt = 0;
913
+ s.m->u.read_only.data_length = 0;
914
+ s.m->u.read_only.intruction_count = 0;
915
+ s.i = MUSTACH2INSTRUCTIONS(s.m);
916
+ s.err = args.err;
917
+
918
+ if (!args.filename_len && args.filename)
554
919
  args.filename_len = strlen(args.filename);
555
- }
556
-
557
- /* copy the path data (and resolve) into writable memory */
558
- if (args.filename && args.filename[0] == '~' && args.filename[1] == '/' &&
559
- getenv("HOME")) {
560
- const char *home = getenv("HOME");
561
- path_len = strlen(home);
562
- path_capa =
563
- path_len + 1 + args.filename_len + 1 + 9 + 1; /* + file extension */
564
- path = malloc(path_capa);
565
- if (!path) {
566
- perror("FATAL ERROR: couldn't allocate memory for path resolution");
567
- exit(errno);
568
- }
569
- memcpy(path, home, path_len);
570
- if (path[path_len - 1] != '/') {
571
- path[path_len++] = '/';
572
- }
573
- memcpy(path + path_len, args.filename + 2, args.filename_len);
574
- args.filename_len += path_len;
575
- args.filename = path;
576
- }
577
-
578
- /*
579
- * We need a dynamic array to hold the list of instructions...
580
- * We might as well use the same memory structure as the final product, saving
581
- * us an allocation and a copy at the end.
582
- *
583
- * Allocation starts with 32 instructions.
584
- */
585
- struct {
586
- mustache_s head; /* instruction array capacity and length */
587
- mustache__instruction_s ary[]; /* the instruction array */
588
- } *instructions =
589
- malloc(sizeof(*instructions) + (32 * sizeof(mustache__instruction_s)));
590
- if (!instructions) {
591
- perror(
592
- "FATAL ERROR: couldn't allocate memory for mustache template parsing");
593
- exit(errno);
594
- }
595
- /* initialize dynamic array */
596
- instructions->head.u.read_only.intruction_count = 0;
597
- instructions->head.u.read_only.data_length = 32;
598
- uint32_t data_len = 0;
599
- uint8_t *data = NULL;
600
-
601
- /* We define a dynamic array handling macro, using 32 instruction chunks */
602
- #define PUSH_INSTRUCTION(...) \
603
- do { \
604
- if (instructions->head.u.read_only.intruction_count == \
605
- instructions->head.u.read_only.data_length) { \
606
- instructions->head.u.read_only.data_length += 32; \
607
- instructions = realloc(instructions, \
608
- sizeof(*instructions) + \
609
- (instructions->head.u.read_only.data_length * \
610
- sizeof(mustache__instruction_s))); \
611
- if (!instructions) { \
612
- perror("FATAL ERROR: couldn't reallocate memory for mustache " \
613
- "template path"); \
614
- exit(errno); \
615
- } \
616
- } \
617
- instructions->ary[instructions->head.u.read_only.intruction_count++] = \
618
- (mustache__instruction_s){__VA_ARGS__}; \
619
- } while (0);
620
-
621
- /* a limited local template stack to manage template data "jumps" */
622
- /* Note: templates can be recursive. */
623
- int32_t stack_pos = 0;
624
- struct {
625
- uint8_t *delimiter_start; /* currunt instruction start delimiter */
626
- uint8_t *delimiter_end; /* currunt instruction end delimiter */
627
- uint32_t data_start; /* template starting position (with header) */
628
- uint32_t data_pos; /* data reading position (how much was consumed) */
629
- uint32_t data_end; /* data ending position (for this template) */
630
- uint16_t del_start_len; /* delimiter length (start) */
631
- uint16_t del_end_len; /* delimiter length (end) */
632
- } template_stack[MUSTACHE_NESTING_LIMIT];
633
- template_stack[0].data_start = 0;
634
- template_stack[0].data_pos = 0;
635
- template_stack[0].data_end = 0;
636
- template_stack[0].delimiter_start = (uint8_t *)"{{";
637
- template_stack[0].delimiter_end = (uint8_t *)"}}";
638
- template_stack[0].del_start_len = 2;
639
- template_stack[0].del_end_len = 2;
640
-
641
- /* a section data stack, allowing us to safely mark section closures */
642
- int32_t section_depth = 0;
643
- struct {
644
- /* section name, for closure validation */
645
- struct {
646
- uint32_t start;
647
- uint32_t len;
648
- } name;
649
- /* position for the section start instruction */
650
- uint32_t instruction_pos;
651
- } section_stack[MUSTACHE_NESTING_LIMIT];
652
-
653
- #define SECTION2FILENAME() (data + template_stack[stack_pos].data_start + 10)
654
- #define SECTION2FLEN() \
655
- ((((uint8_t *)data + template_stack[stack_pos].data_start + 4)[0] << 1) | \
656
- (((uint8_t *)data + template_stack[stack_pos].data_start + 4)[1]))
657
-
658
- /* append a filename to the path, managing the C string memory and length */
659
- #define PATH2FULL(folder, folder_len, filename, filename_len) \
660
- do { \
661
- if (path_capa < (filename_len) + (folder_len) + 9 + 1) { \
662
- path_capa = (filename_len) + (folder_len) + 9 + 1; \
663
- path = realloc(path, path_capa); \
664
- if (!path) { \
665
- perror("FATAL ERROR: couldn't allocate memory for path resolution"); \
666
- exit(errno); \
667
- } \
668
- } \
669
- if (path != (char *)(folder) && (folder_len) && (filename)[0] != '/') { \
670
- memcpy(path, (folder), (folder_len)); \
671
- path_len = (folder_len); \
672
- } else { \
673
- path_len = 0; \
674
- } \
675
- if (path != (char *)(filename)) \
676
- memcpy(path + path_len, (filename), (filename_len)); \
677
- path_len += (filename_len); \
678
- path[path_len] = 0; \
679
- } while (0);
680
-
681
- /* append a filename to the path, managing the C string memory and length */
682
- #define PATH_WITH_EXT() \
683
- do { \
684
- memcpy(path + path_len, ".mustache", 9); \
685
- path[path_len + 9] = 0; /* keep path_len the same */ \
686
- } while (0);
687
-
688
- /* We define a dynamic template loading macro to manage memory details */
689
- #define LOAD_TEMPLATE(root, root_len, filename, filname_len) \
690
- do { \
691
- /* find root filename's path start */ \
692
- int32_t root_len_tmp = (root_len); \
693
- while (root_len_tmp && (((char *)(root))[root_len_tmp - 1] != '/' || \
694
- (root_len_tmp > 1 && \
695
- ((char *)(root))[root_len_tmp - 2] == '\\'))) { \
696
- --root_len_tmp; \
697
- } \
698
- if ((filname_len) + root_len_tmp >= ((uint32_t)1 << 16)) { \
699
- *args.err = MUSTACHE_ERR_FILE_NAME_TOO_LONG; \
700
- goto error; \
701
- } \
702
- PATH2FULL((root), root_len_tmp, (filename), (filname_len)); \
703
- struct stat f_data; \
704
- { \
705
- /* test file name with and without the .mustache extension */ \
706
- int stat_result = stat(path, &f_data); \
707
- if (stat_result == -1) { \
708
- PATH_WITH_EXT(); \
709
- stat_result = stat(path, &f_data); \
710
- } \
711
- if (stat_result == -1) { \
712
- if (args.err) { \
713
- *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
714
- } \
715
- goto error; \
716
- } \
717
- } \
718
- if (f_data.st_size >= ((uint32_t)1 << 24)) { \
719
- *args.err = MUSTACHE_ERR_FILE_TOO_BIG; \
720
- goto error; \
721
- } \
722
- /* the data segment's new length after loading the the template */ \
723
- /* The data segments includes a template header: */ \
724
- /* | 4 bytes template start instruction position | */ \
725
- /* | 2 bytes template name | 4 bytes next template position | */ \
726
- /* | template name (filename) | ...[template data]... */ \
727
- /* this allows template data to be reused when repeating a template */ \
728
- const uint32_t new_len = \
729
- data_len + 4 + 2 + 4 + path_len + f_data.st_size + 1; \
730
- /* reallocate memory */ \
731
- data = realloc(data, new_len); \
732
- if (!data) { \
733
- perror("FATAL ERROR: couldn't reallocate memory for mustache " \
734
- "data segment"); \
735
- exit(errno); \
736
- } \
737
- /* save instruction position length into template header */ \
738
- data[data_len + 0] = \
739
- (instructions->head.u.read_only.intruction_count >> 3) & 0xFF; \
740
- data[data_len + 1] = \
741
- (instructions->head.u.read_only.intruction_count >> 2) & 0xFF; \
742
- data[data_len + 2] = \
743
- (instructions->head.u.read_only.intruction_count >> 1) & 0xFF; \
744
- data[data_len + 3] = \
745
- (instructions->head.u.read_only.intruction_count) & 0xFF; \
746
- /* Add section start marker (to support recursion or repeated partials) */ \
747
- PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_START); \
748
- /* save filename length */ \
749
- data[data_len + 4 + 0] = (path_len >> 1) & 0xFF; \
750
- data[data_len + 4 + 1] = path_len & 0xFF; \
751
- /* save data length ("next" pointer) */ \
752
- data[data_len + 4 + 2 + 0] = ((uint32_t)new_len >> 3) & 0xFF; \
753
- data[data_len + 4 + 2 + 1] = ((uint32_t)new_len >> 2) & 0xFF; \
754
- data[data_len + 4 + 2 + 2] = ((uint32_t)new_len >> 1) & 0xFF; \
755
- data[data_len + 4 + 2 + 3] = ((uint32_t)new_len) & 0xFF; \
756
- /* copy filename */ \
757
- memcpy(data + data_len + 4 + 2 + 4, path, path_len); \
758
- /* open file and dump it into the data segment after the new header */ \
759
- int fd = open(path, O_RDONLY); \
760
- if (fd == -1) { \
761
- if (args.err) { \
762
- *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
763
- } \
764
- goto error; \
765
- } \
766
- if (pread(fd, (data + data_len + 4 + 2 + 4 + path_len), f_data.st_size, \
767
- 0) != (ssize_t)f_data.st_size) { \
768
- if (args.err) { \
769
- *args.err = MUSTACHE_ERR_FILE_NOT_FOUND; \
770
- } \
771
- close(fd); \
772
- goto error; \
773
- } \
774
- if (stack_pos + 1 == MUSTACHE_NESTING_LIMIT) { \
775
- if (args.err) { \
776
- *args.err = MUSTACHE_ERR_TOO_DEEP; \
777
- } \
778
- close(fd); \
779
- goto error; \
780
- } \
781
- close(fd); \
782
- /* increase the data stack pointer and setup new stack frame */ \
783
- ++stack_pos; \
784
- template_stack[stack_pos].data_start = data_len; \
785
- template_stack[stack_pos].data_pos = data_len + 4 + 3 + 3 + path_len; \
786
- template_stack[stack_pos].data_end = new_len - 1; \
787
- template_stack[stack_pos].delimiter_start = (uint8_t *)"{{"; \
788
- template_stack[stack_pos].delimiter_end = (uint8_t *)"}}"; \
789
- template_stack[stack_pos].del_start_len = 2; \
790
- template_stack[stack_pos].del_end_len = 2; \
791
- /* update new data segment length and add NUL marker */ \
792
- data_len = new_len; \
793
- data[new_len - 1] = 0; \
794
- } while (0);
795
-
796
- #define IGNORE_WHITESPACE(str, step) \
797
- while (isspace(*(str))) { \
798
- (str) += (step); \
799
- }
800
920
 
801
- if (args.data_len) {
802
- /* allocate data segment */
803
- data_len = 4 + 2 + 4 + args.data_len + args.filename_len + 1;
804
- data = malloc(data_len);
805
- if (!data) {
806
- perror("FATAL ERROR: couldn't reallocate memory for mustache "
807
- "data segment");
808
- exit(errno);
921
+ if (args.data) {
922
+ if (mustache__load_data(&s, args.filename, args.filename_len, args.data,
923
+ args.data_len)) {
924
+ goto error;
809
925
  }
810
- /* save instruction position length into template header */
811
- data[0] = (instructions->head.u.read_only.intruction_count >> 3) & 0xFF;
812
- data[1] = (instructions->head.u.read_only.intruction_count >> 2) & 0xFF;
813
- data[2] = (instructions->head.u.read_only.intruction_count >> 1) & 0xFF;
814
- data[3] = (instructions->head.u.read_only.intruction_count) & 0xFF;
815
- /* Add section start marker (to support recursion or repeated partials) */
816
- PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_START);
817
- /* save filename length */
818
- data[4 + 0] = (args.filename_len >> 1) & 0xFF;
819
- data[4 + 1] = args.filename_len & 0xFF;
820
- /* save data length ("next" pointer) */
821
- data[4 + 2 + 0] = ((uint32_t)data_len >> 3) & 0xFF;
822
- data[4 + 2 + 1] = ((uint32_t)data_len >> 2) & 0xFF;
823
- data[4 + 2 + 2] = ((uint32_t)data_len >> 1) & 0xFF;
824
- data[4 + 2 + 3] = ((uint32_t)data_len) & 0xFF;
825
- /* copy filename */
826
- if (args.filename && args.filename_len)
827
- memcpy(data + 4 + 2 + 4, args.filename, args.filename_len);
828
- /* copy data */
829
- memcpy(data + 4 + 2 + 4 + args.filename_len, args.data, args.data_len);
830
- ++stack_pos;
831
- template_stack[stack_pos].data_start = 0;
832
- template_stack[stack_pos].data_pos = 4 + 3 + 3 + args.filename_len;
833
- template_stack[stack_pos].data_end = data_len - 1;
834
- template_stack[stack_pos].delimiter_start = (uint8_t *)"{{";
835
- template_stack[stack_pos].delimiter_end = (uint8_t *)"}}";
836
- template_stack[stack_pos].del_start_len = 2;
837
- template_stack[stack_pos].del_end_len = 2;
838
- data[data_len - 1] = 0;
839
926
  } else {
840
- /* Our first template to load is the root template */
841
- LOAD_TEMPLATE(path, 0, args.filename, args.filename_len);
927
+ if (mustache__load_file(&s, args.filename, args.filename_len)) {
928
+ goto error;
929
+ }
842
930
  }
843
931
 
844
- /*** As long as the stack has templated to parse - parse the template ***/
845
- while (stack_pos) {
846
- /* test reading position against template ending and parse */
847
- while (template_stack[stack_pos].data_pos <
848
- template_stack[stack_pos].data_end) {
932
+ /* loop while there are templates to be parsed on the stack */
933
+ while (s.index) {
934
+ /* parsing loop */
935
+ while (s.stack[s.index].data_pos < s.stack[s.index].data_end) {
849
936
  /* start parsing at current position */
850
- uint8_t *const start = data + template_stack[stack_pos].data_pos;
937
+ const char *start = s.data + s.stack[s.index].data_pos;
851
938
  /* find the next instruction (beg == beginning) */
852
- uint8_t *beg = (uint8_t *)strstr(
853
- (char *)start, (char *)template_stack[stack_pos].delimiter_start);
854
- if (!beg || beg >= data + template_stack[stack_pos].data_end) {
939
+ char *beg = strstr(start, s.stack[s.index].del_start);
940
+ const char *org_beg = beg;
941
+ if (!beg || beg >= s.data + s.stack[s.index].data_end) {
855
942
  /* no instructions left, only text */
856
- PUSH_INSTRUCTION(.instruction = MUSTACHE_WRITE_TEXT,
857
- .data = {
858
- .start = template_stack[stack_pos].data_pos,
859
- .len = template_stack[stack_pos].data_end -
860
- template_stack[stack_pos].data_pos,
861
- });
862
- template_stack[stack_pos].data_pos = template_stack[stack_pos].data_end;
943
+ mustache__instruction_push(
944
+ &s, (mustache__instruction_s){
945
+ .instruction = MUSTACHE_WRITE_TEXT,
946
+ .data =
947
+ {
948
+ .name_pos = s.stack[s.index].data_pos,
949
+ .name_len = s.stack[s.index].data_end -
950
+ s.stack[s.index].data_pos,
951
+ },
952
+ });
953
+ s.stack[s.index].data_pos = s.stack[s.index].data_end;
863
954
  continue;
864
955
  }
865
- if (beg + template_stack[stack_pos].del_start_len >=
866
- data + template_stack[stack_pos].data_end) {
867
- /* overshot... ending the template with a delimiter...*/
868
- if (args.err) {
869
- *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
870
- }
871
- goto error;
956
+ if (beg != start) {
957
+ /* there's text before the instruction */
958
+ mustache__instruction_push(
959
+ &s, (mustache__instruction_s){
960
+ .instruction = MUSTACHE_WRITE_TEXT,
961
+ .data =
962
+ {
963
+ .name_pos = s.stack[s.index].data_pos,
964
+ .name_len = beg - start,
965
+ },
966
+ });
872
967
  }
873
- beg[0] = 0; /* mark the end of any text segment or string, just in case */
874
- /* find the ending of the instruction */
875
- uint8_t *end = (uint8_t *)strstr(
876
- (char *)beg + template_stack[stack_pos].del_start_len,
877
- (char *)template_stack[stack_pos].delimiter_end);
878
- if (!end || end >= data + template_stack[stack_pos].data_end) {
968
+ /* move beg (reading position) after the delimiter */
969
+ beg += s.stack[s.index].del_start_len;
970
+ /* seek the end of the instruction delimiter */
971
+ char *end = strstr(beg, s.stack[s.index].del_end);
972
+ if (!end || end >= s.data + s.stack[s.index].data_end) {
879
973
  /* delimiter not closed */
880
- if (args.err) {
881
- *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
882
- }
974
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
883
975
  goto error;
884
976
  }
885
- /* text before instruction? add text instruction */
886
- if (beg != data + template_stack[stack_pos].data_pos) {
887
- PUSH_INSTRUCTION(.instruction = MUSTACHE_WRITE_TEXT,
888
- .data = {
889
- .start = template_stack[stack_pos].data_pos,
890
- .len = beg -
891
- (data + template_stack[stack_pos].data_pos),
892
- });
893
- }
894
977
  /* update reading position in the stack */
895
- template_stack[stack_pos].data_pos =
896
- (end - data) + template_stack[stack_pos].del_end_len;
897
-
898
- /* move the beginning marker the the instruction's content */
899
- beg += template_stack[stack_pos].del_start_len;
978
+ s.stack[s.index].data_pos = (end - s.data) + s.stack[s.index].del_end_len;
900
979
 
901
- /* review template instruction (the {{tag}}) */
980
+ /* parse instruction content */
902
981
  uint8_t escape_str = 1;
982
+
903
983
  switch (beg[0]) {
904
984
  case '!':
905
985
  /* comment, do nothing */
@@ -910,130 +990,155 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
910
990
  ++beg;
911
991
  --end;
912
992
  if (end[0] != '=') {
913
- if (args.err) {
914
- *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
915
- }
993
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
916
994
  goto error;
917
995
  }
996
+ --end;
997
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
998
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
999
+ ++end;
918
1000
  {
919
- uint8_t *div = beg;
920
- while (div < end && !isspace(*(char *)div)) {
1001
+ char *div = beg;
1002
+ while (div < end && !isspace(*div)) {
921
1003
  ++div;
922
1004
  }
923
- if (div == end) {
924
- if (args.err) {
925
- *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
926
- }
1005
+ if (div == end || div == beg) {
1006
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1007
+ goto error;
1008
+ }
1009
+ if (div - beg >= MUSTACHE_DELIMITER_LENGTH_LIMIT) {
1010
+ *args.err = MUSTACHE_ERR_DELIMITER_TOO_LONG;
927
1011
  goto error;
928
1012
  }
929
- template_stack[stack_pos].delimiter_start = beg;
930
- template_stack[stack_pos].del_start_len = div - beg;
931
- div[0] = 0;
1013
+ /* copy starting delimiter */
1014
+ s.stack[s.index].del_start_len = div - beg;
1015
+ for (size_t i = 0; i < s.stack[s.index].del_start_len; ++i) {
1016
+ s.stack[s.index].del_start[i] = beg[i];
1017
+ }
1018
+ s.stack[s.index].del_start[s.stack[s.index].del_start_len] = 0;
1019
+
932
1020
  ++div;
933
- IGNORE_WHITESPACE(div, 1);
934
- template_stack[stack_pos].delimiter_end = div;
935
- template_stack[stack_pos].del_end_len = end - div;
936
- end[0] = 0;
1021
+ MUSTACHE_IGNORE_WHITESPACE(div, 1);
1022
+ if (div == end) {
1023
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1024
+ goto error;
1025
+ }
1026
+ if (end - div >= MUSTACHE_DELIMITER_LENGTH_LIMIT) {
1027
+ *args.err = MUSTACHE_ERR_DELIMITER_TOO_LONG;
1028
+ goto error;
1029
+ }
1030
+ /* copy ending delimiter */
1031
+ s.stack[s.index].del_end_len = end - div;
1032
+ for (size_t i = 0; i < s.stack[s.index].del_end_len; ++i) {
1033
+ s.stack[s.index].del_end[i] = div[i];
1034
+ }
1035
+ s.stack[s.index].del_end[s.stack[s.index].del_end_len] = 0;
937
1036
  }
938
1037
  break;
939
1038
 
940
1039
  case '^': /*overflow*/
1040
+ /* start inverted section */
941
1041
  escape_str = 0;
942
1042
  case '#':
943
1043
  /* start section (or inverted section) */
944
1044
  ++beg;
945
1045
  --end;
946
- IGNORE_WHITESPACE(beg, 1);
947
- IGNORE_WHITESPACE(end, -1);
948
- end[1] = 0;
949
- if (section_depth >= MUSTACHE_NESTING_LIMIT) {
950
- if (args.err) {
951
- *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
952
- }
1046
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1047
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1048
+ ++end;
1049
+
1050
+ ++s.stack[s.index].open_sections;
1051
+ if (s.stack[s.index].open_sections >= MUSTACHE_NESTING_LIMIT) {
1052
+ *args.err = MUSTACHE_ERR_TOO_DEEP;
1053
+ goto error;
1054
+ }
1055
+ if (beg - s.data >= UINT16_MAX) {
1056
+ *args.err = MUSTACHE_ERR_NAME_TOO_LONG;
1057
+ }
1058
+ if (mustache__instruction_push(
1059
+ &s,
1060
+ (mustache__instruction_s){
1061
+ .instruction = (escape_str ? MUSTACHE_SECTION_START
1062
+ : MUSTACHE_SECTION_START_INV),
1063
+ .data = {
1064
+ .name_pos = beg - s.data,
1065
+ .name_len = end - beg,
1066
+ .offset = s.stack[s.index].data_pos - (beg - s.data),
1067
+ }})) {
953
1068
  goto error;
954
1069
  }
955
- section_stack[section_depth].instruction_pos =
956
- instructions->head.u.read_only.intruction_count;
957
- section_stack[section_depth].name.start = beg - data;
958
- section_stack[section_depth].name.len = (end - beg) + 1;
959
- ++section_depth;
960
- PUSH_INSTRUCTION(.instruction =
961
- (escape_str ? MUSTACHE_SECTION_START
962
- : MUSTACHE_SECTION_START_INV),
963
- .data = {
964
- .start = beg - data,
965
- .len = (end - beg) + 1,
966
- });
967
1070
  break;
968
1071
 
969
1072
  case '>':
970
1073
  /* partial template - search data for loaded template or load new */
971
1074
  ++beg;
972
1075
  --end;
973
- IGNORE_WHITESPACE(beg, 1);
974
- IGNORE_WHITESPACE(end, -1);
1076
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1077
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
975
1078
  ++end;
976
- end[0] = 0;
977
- {
978
- uint8_t *loaded = data;
979
- uint8_t *const data_end = data + data_len;
980
- while (loaded < data_end) {
981
- uint32_t const fn_len =
982
- ((loaded[4] & 0xFF) << 1) | (loaded[5] & 0xFF);
983
- if (fn_len != end - beg || memcmp(beg, loaded + 10, end - beg)) {
984
- uint32_t const next_offset =
985
- ((loaded[6] & 0xFF) << 3) | ((loaded[7] & 0xFF) << 2) |
986
- ((loaded[8] & 0xFF) << 1) | (loaded[9] & 0xFF);
987
- loaded = data + next_offset;
988
- continue;
989
- }
990
- uint32_t const section_start =
991
- ((loaded[0] & 0xFF) << 3) | ((loaded[1] & 0xFF) << 2) |
992
- ((loaded[2] & 0xFF) << 1) | (loaded[3] & 0xFF);
993
- PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_GOTO,
994
- .data = {
995
- .len = section_start,
996
- });
997
- break;
998
- }
999
- if (loaded >= data_end) {
1000
- LOAD_TEMPLATE(SECTION2FILENAME(), SECTION2FLEN(), beg, (end - beg));
1001
- }
1002
- }
1079
+ if (mustache__load_file(&s, beg, end - beg))
1080
+ goto error;
1003
1081
  break;
1004
1082
 
1005
1083
  case '/':
1006
1084
  /* section end */
1007
1085
  ++beg;
1008
1086
  --end;
1009
- IGNORE_WHITESPACE(beg, 1);
1010
- IGNORE_WHITESPACE(end, -1);
1011
- end[1] = 0;
1012
- --section_depth;
1013
- if (!(section_depth + 1) ||
1014
- (end - beg) + 1 != section_stack[section_depth].name.len ||
1015
- memcmp(beg, data + section_stack[section_depth].name.start,
1016
- section_stack[section_depth].name.len)) {
1017
- if (args.err) {
1087
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1088
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1089
+ ++end;
1090
+ if (!s.stack[s.index].open_sections) {
1091
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1092
+ goto error;
1093
+ } else {
1094
+ uint32_t pos = s.m->u.read_only.intruction_count;
1095
+ uint32_t nested = 0;
1096
+ do {
1097
+ --pos;
1098
+ if (s.i[pos].instruction == MUSTACHE_SECTION_END)
1099
+ ++nested;
1100
+ else if (s.i[pos].instruction == MUSTACHE_SECTION_START ||
1101
+ s.i[pos].instruction == MUSTACHE_SECTION_START_INV) {
1102
+ if (nested) {
1103
+ --nested;
1104
+ } else {
1105
+ /* test instruction closure */
1106
+ if (s.i[pos].data.name_len != end - beg ||
1107
+ memcmp(beg, s.data + s.i[pos].data.name_pos,
1108
+ s.i[pos].data.name_len)) {
1109
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1110
+ goto error;
1111
+ }
1112
+ /* update initial instruction (do this before adding closure) */
1113
+ s.i[pos].data.end = s.m->u.read_only.intruction_count;
1114
+ s.i[pos].data.len = org_beg - (s.data + s.i[pos].data.name_pos +
1115
+ s.i[pos].data.offset);
1116
+ /* add closure instruction */
1117
+ mustache__instruction_push(
1118
+ &s, (mustache__instruction_s){.instruction =
1119
+ MUSTACHE_SECTION_END,
1120
+ .data = s.i[pos].data});
1121
+ /* update closure count */
1122
+ --s.stack[s.index].open_sections;
1123
+ /* stop loop */
1124
+ pos = 0;
1125
+ beg = NULL;
1126
+ }
1127
+ }
1128
+ } while (pos);
1129
+ if (beg) {
1018
1130
  *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1131
+ goto error;
1019
1132
  }
1020
- goto error;
1021
1133
  }
1022
- /* update the section_start instruction with the ending's location */
1023
- instructions->ary[section_stack[section_depth].instruction_pos]
1024
- .data.len = instructions->head.u.read_only.intruction_count;
1025
- /* push sction end instruction */
1026
- PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_END,
1027
- .data = {
1028
- .len =
1029
- section_stack[section_depth].instruction_pos,
1030
- });
1031
1134
  break;
1032
1135
 
1033
1136
  case '{':
1034
1137
  /* step the read position forward if the ending was '}}}' */
1035
- if ((data + template_stack[stack_pos].data_pos)[0] == '}') {
1036
- ++template_stack[stack_pos].data_pos;
1138
+ if (s.data[s.stack[s.index].data_pos] == '}' &&
1139
+ s.stack[s.index].del_end[0] == '}' &&
1140
+ s.stack[s.index].del_end[s.stack[s.index].del_end_len - 1] == '}') {
1141
+ ++s.stack[s.index].data_pos;
1037
1142
  }
1038
1143
  /*overflow*/
1039
1144
  case '&': /*overflow*/
@@ -1045,85 +1150,57 @@ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1045
1150
  ++beg; /*overflow*/
1046
1151
  default:
1047
1152
  --end;
1048
- IGNORE_WHITESPACE(beg, 1);
1049
- IGNORE_WHITESPACE(end, -1);
1050
- end[1] = 0;
1051
- PUSH_INSTRUCTION(.instruction =
1052
- (escape_str ? MUSTACHE_WRITE_ARG
1053
- : MUSTACHE_WRITE_ARG_UNESCAPED),
1054
- .data = {
1055
- .start = beg - data,
1056
- .len = (end - beg) + 1,
1057
- });
1153
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1154
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1155
+ ++end;
1156
+ mustache__instruction_push(
1157
+ &s, (mustache__instruction_s){
1158
+ .instruction = (escape_str ? MUSTACHE_WRITE_ARG
1159
+ : MUSTACHE_WRITE_ARG_UNESCAPED),
1160
+ .data = {.name_pos = beg - s.data, .name_len = end - beg}});
1058
1161
  break;
1059
1162
  }
1060
1163
  }
1061
- /* templates are treated as sections, allowing for recursion using "goto" */
1062
- /* update the template's section_start instruction with the end position */
1063
- uint32_t const section_start =
1064
- ((data[template_stack[stack_pos].data_start + 0] & 0xFF) << 3) |
1065
- ((data[template_stack[stack_pos].data_start + 1] & 0xFF) << 2) |
1066
- ((data[template_stack[stack_pos].data_start + 2] & 0xFF) << 1) |
1067
- (data[template_stack[stack_pos].data_start + 3] & 0xFF);
1068
- instructions->ary[section_start].data.len =
1069
- instructions->head.u.read_only.intruction_count;
1070
- /* add section end instructiomn for the template section */
1071
- PUSH_INSTRUCTION(.instruction = MUSTACHE_SECTION_END,
1072
- .data.len = section_start);
1073
- /* pop the stack frame */
1074
- --stack_pos;
1075
- }
1076
- /*** done parsing ***/
1077
-
1078
- /* is the template empty?*/
1079
- if (!instructions->head.u.read_only.intruction_count) {
1080
- if (args.err) {
1081
- *args.err = MUSTACHE_ERR_EMPTY_TEMPLATE;
1164
+ /* make sure all sections were closed */
1165
+ if (s.stack[s.index].open_sections) {
1166
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1167
+ goto error;
1082
1168
  }
1083
- goto error;
1169
+ /* add instruction closure */
1170
+ mustache__data_segment_s seg = mustache__data_segment_read(
1171
+ (uint8_t *)s.data + s.stack[s.index].data_start);
1172
+ s.i[seg.inst_start].data.end = s.m->u.read_only.intruction_count;
1173
+ mustache__instruction_push(
1174
+ &s, (mustache__instruction_s){.instruction = MUSTACHE_SECTION_END});
1175
+ /* pop stack */
1176
+ --s.index;
1084
1177
  }
1085
1178
 
1086
- /* We're done making up the instruction list, time to finalize the product */
1087
- /* Make room for the String data at the end of the instruction array */
1088
- instructions = realloc(instructions,
1089
- sizeof(*instructions) +
1090
- (instructions->head.u.read_only.intruction_count *
1091
- sizeof(mustache__instruction_s)) +
1092
- data_len);
1093
- if (!instructions) {
1094
- perror("FATAL ERROR: couldn't reallocate memory for mustache "
1095
- "template finalization");
1096
- exit(errno);
1097
- }
1098
- /* Copy the data segment to the end of the instruction array */
1099
- instructions->head.u.read_only.data_length = data_len;
1100
- memcpy((void *)((uintptr_t)(instructions + 1) +
1101
- (instructions->head.u.read_only.intruction_count *
1102
- sizeof(mustache__instruction_s))),
1103
- data, data_len);
1104
- /* Cleanup, set error code and return. */
1105
- free(data);
1106
- free(path);
1107
- if (args.err) {
1108
- *args.err = MUSTACHE_OK;
1109
- }
1110
- return &instructions->head;
1179
+ s.m = realloc(s.m, sizeof(*s.m) +
1180
+ (sizeof(*s.i) * s.m->u.read_only.intruction_count) +
1181
+ s.data_len);
1182
+ MUSTACHE_ASSERT(s.m,
1183
+ "failed to allocate memory for consolidated mustache data");
1184
+ memcpy(MUSTACH2DATA(s.m), s.data, s.data_len);
1185
+ free(s.data);
1186
+ free(s.path);
1187
+
1188
+ *args.err = MUSTACHE_OK;
1189
+ return s.m;
1190
+
1111
1191
  error:
1112
- free(instructions);
1113
- free(data);
1114
- free(path);
1192
+ free(s.data);
1193
+ free(s.path);
1194
+ free(s.m);
1115
1195
  return NULL;
1116
-
1117
- #undef PATH2FULL
1118
- #undef PATH_WITH_EXT
1119
- #undef SECTION2FILENAME
1120
- #undef SECTION2FLEN
1121
- #undef LOAD_TEMPLATE
1122
- #undef PUSH_INSTRUCTION
1123
- #undef IGNORE_WHITESPACE
1124
1196
  }
1125
1197
 
1126
1198
  #endif /* INCLUDE_MUSTACHE_IMPLEMENTATION */
1127
1199
 
1128
1200
  #undef MUSTACHE_FUNC
1201
+ #undef MUSTACH2INSTRUCTIONS
1202
+ #undef MUSTACH2DATA
1203
+ #undef MUSTACHE_OBJECT_OFFSET
1204
+ #undef MUSTACHE_IGNORE_WHITESPACE
1205
+
1129
1206
  #endif /* H_MUSTACHE_LOADR_H */