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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/ext/iodine/fio.c +341 -154
- data/ext/iodine/fio.h +71 -33
- data/ext/iodine/fio_cli.c +25 -24
- data/ext/iodine/fiobj.h +4 -0
- data/ext/iodine/{fiobj4sock.h → fiobj4fio.h} +0 -0
- data/ext/iodine/fiobj_mustache.c +77 -54
- data/ext/iodine/fiobj_mustache.h +21 -3
- data/ext/iodine/http.c +19 -0
- data/ext/iodine/http.h +5 -1
- data/ext/iodine/http_internal.h +0 -2
- data/ext/iodine/iodine_connection.c +1 -1
- data/ext/iodine/iodine_http.c +17 -9
- data/ext/iodine/iodine_mustache.c +174 -91
- data/ext/iodine/mustache_parser.h +772 -695
- data/ext/iodine/redis_engine.c +0 -1
- data/lib/iodine/mustache.rb +4 -4
- data/lib/iodine/version.rb +1 -1
- metadata +4 -4
@@ -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
|
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
|
-
|
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
|
293
|
-
uint32_t
|
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
|
-
*
|
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 *
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
354
|
-
|
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(&
|
362
|
-
data + pos
|
363
|
-
|
364
|
-
|
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
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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(&
|
382
|
-
data + pos
|
383
|
-
|
384
|
-
|
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
|
390
|
-
case MUSTACHE_SECTION_START:
|
391
|
-
|
392
|
-
|
393
|
-
|
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(§ion_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
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
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
|
-
|
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(§ion_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
|
-
|
457
|
-
if (
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
if (
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
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
|
-
|
496
|
-
|
497
|
-
|
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(§ion_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
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
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.
|
802
|
-
|
803
|
-
|
804
|
-
|
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
|
-
|
841
|
-
|
927
|
+
if (mustache__load_file(&s, args.filename, args.filename_len)) {
|
928
|
+
goto error;
|
929
|
+
}
|
842
930
|
}
|
843
931
|
|
844
|
-
|
845
|
-
while (
|
846
|
-
/*
|
847
|
-
while (
|
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
|
-
|
937
|
+
const char *start = s.data + s.stack[s.index].data_pos;
|
851
938
|
/* find the next instruction (beg == beginning) */
|
852
|
-
|
853
|
-
|
854
|
-
if (!beg || beg >= data +
|
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
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
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
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
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
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
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
|
-
|
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
|
-
|
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
|
-
/*
|
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
|
-
|
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
|
-
|
920
|
-
while (div < end && !isspace(*
|
1001
|
+
char *div = beg;
|
1002
|
+
while (div < end && !isspace(*div)) {
|
921
1003
|
++div;
|
922
1004
|
}
|
923
|
-
if (div == end) {
|
924
|
-
|
925
|
-
|
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
|
-
|
930
|
-
|
931
|
-
|
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
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
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
|
-
|
947
|
-
|
948
|
-
end
|
949
|
-
|
950
|
-
|
951
|
-
|
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
|
-
|
974
|
-
|
1076
|
+
MUSTACHE_IGNORE_WHITESPACE(beg, 1);
|
1077
|
+
MUSTACHE_IGNORE_WHITESPACE(end, -1);
|
975
1078
|
++end;
|
976
|
-
end
|
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
|
-
|
1010
|
-
|
1011
|
-
end
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
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 (
|
1036
|
-
|
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
|
-
|
1049
|
-
|
1050
|
-
end
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
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
|
-
/*
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
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
|
-
|
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
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
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(
|
1113
|
-
free(
|
1114
|
-
free(
|
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 */
|