rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,1546 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_MUSTACHE_LOADR_H
8
+ /**
9
+ * A mustache parser using a callback systems that allows this implementation to
10
+ * be framework agnostic (i.e., can be used with any JSON library).
11
+ */
12
+ #define H_MUSTACHE_LOADR_H
13
+
14
+ #ifndef _GNU_SOURCE
15
+ #define _GNU_SOURCE
16
+ #endif
17
+
18
+ #include <ctype.h>
19
+ #include <stdint.h>
20
+ #include <stdio.h>
21
+ #include <stdlib.h>
22
+ #include <string.h>
23
+ #include <strings.h>
24
+ #include <unistd.h>
25
+
26
+ #include <errno.h>
27
+ #include <fcntl.h>
28
+ #include <sys/stat.h>
29
+ #include <sys/types.h>
30
+
31
+ #ifdef __MINGW32__
32
+ ssize_t pread(int, void*, size_t, off_t);
33
+ #endif
34
+
35
+ #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
36
+ #define __attribute__(...)
37
+ #define __has_include(...) 0
38
+ #define __has_builtin(...) 0
39
+ #define FIO_GNUC_BYPASS 1
40
+ #elif !defined(__clang__) && !defined(__has_builtin)
41
+ #define __has_builtin(...) 0
42
+ #define FIO_GNUC_BYPASS 1
43
+ #endif
44
+
45
+ #ifndef MUSTACHE_FUNC
46
+ #define MUSTACHE_FUNC static __attribute__((unused))
47
+ #endif
48
+
49
+ /* *****************************************************************************
50
+ Compile Time Behavior Flags
51
+ ***************************************************************************** */
52
+
53
+ #ifndef MUSTACHE_USE_DYNAMIC_PADDING
54
+ #define MUSTACHE_USE_DYNAMIC_PADDING 1
55
+ #endif
56
+
57
+ #ifndef MUSTACHE_FAIL_ON_MISSING_TEMPLATE
58
+ #define MUSTACHE_FAIL_ON_MISSING_TEMPLATE 1
59
+ #endif
60
+
61
+ #if !defined(MUSTACHE_NESTING_LIMIT) || !MUSTACHE_NESTING_LIMIT
62
+ #undef MUSTACHE_NESTING_LIMIT
63
+ #define MUSTACHE_NESTING_LIMIT 82
64
+ #endif
65
+
66
+ /* *****************************************************************************
67
+ Mustache API Argument types
68
+ ***************************************************************************** */
69
+
70
+ /** an opaque type for mustache template data (when caching). */
71
+ typedef struct mustache_s mustache_s;
72
+
73
+ /** Error reporting type. */
74
+ typedef enum mustache_error_en {
75
+ MUSTACHE_OK,
76
+ MUSTACHE_ERR_TOO_DEEP,
77
+ MUSTACHE_ERR_CLOSURE_MISMATCH,
78
+ MUSTACHE_ERR_FILE_NOT_FOUND,
79
+ MUSTACHE_ERR_FILE_TOO_BIG,
80
+ MUSTACHE_ERR_FILE_NAME_TOO_LONG,
81
+ MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
82
+ MUSTACHE_ERR_EMPTY_TEMPLATE,
83
+ MUSTACHE_ERR_DELIMITER_TOO_LONG,
84
+ MUSTACHE_ERR_NAME_TOO_LONG,
85
+ MUSTACHE_ERR_UNKNOWN,
86
+ MUSTACHE_ERR_USER_ERROR,
87
+ } mustache_error_en;
88
+
89
+ /** Arguments for the `mustache_load` function, used by the mustache parser. */
90
+ typedef struct {
91
+ /** The root template's file name. */
92
+ char const *filename;
93
+ /** The file name's length. */
94
+ size_t filename_len;
95
+ /** If data and data_len are set, they will be used as the file's contents. */
96
+ char const *data;
97
+ /** If data and data_len are set, they will be used as the file's contents. */
98
+ size_t data_len;
99
+ /** Parsing error reporting (can be NULL). */
100
+ mustache_error_en *err;
101
+ } mustache_load_args_s;
102
+
103
+ /* *****************************************************************************
104
+ REQUIRED: Define INCLUDE_MUSTACHE_IMPLEMENTATION only in the implementation file
105
+ ***************************************************************************** */
106
+
107
+ /**
108
+ * In non-implementation files, don't define the INCLUDE_MUSTACHE_IMPLEMENTATION
109
+ * macro.
110
+ *
111
+ * Before including the header within an implementation faile, define
112
+ * INCLUDE_MUSTACHE_IMPLEMENTATION as 1.
113
+ */
114
+ #if INCLUDE_MUSTACHE_IMPLEMENTATION
115
+
116
+ /* *****************************************************************************
117
+ Mustache API Functions and Arguments
118
+ ***************************************************************************** */
119
+
120
+ MUSTACHE_FUNC mustache_s *mustache_load(mustache_load_args_s args);
121
+
122
+ #define mustache_load(...) mustache_load((mustache_load_args_s){__VA_ARGS__})
123
+
124
+ /** free the mustache template */
125
+ inline MUSTACHE_FUNC void mustache_free(mustache_s *mustache) {
126
+ free(mustache);
127
+ }
128
+
129
+ /** Arguments for the `mustache_build` function. */
130
+ typedef struct {
131
+ /** The parsed template (an instruction collection). */
132
+ mustache_s *mustache;
133
+ /** Opaque user data (recommended for input review) - children will inherit
134
+ * the parent's udata value. Updated values will propegate to child sections
135
+ * but won't effect parent sections.
136
+ */
137
+ void *udata1;
138
+ /** Opaque user data (recommended for output handling)- children will inherit
139
+ * the parent's udata value. Updated values will propegate to child sections
140
+ * but won't effect parent sections.
141
+ */
142
+ void *udata2;
143
+ /** Formatting error reporting (can be NULL). */
144
+ mustache_error_en *err;
145
+ } mustache_build_args_s;
146
+ MUSTACHE_FUNC int mustache_build(mustache_build_args_s args);
147
+
148
+ #define mustache_build(mustache_s_ptr, ...) \
149
+ mustache_build( \
150
+ (mustache_build_args_s){.mustache = (mustache_s_ptr), __VA_ARGS__})
151
+
152
+ /* *****************************************************************************
153
+ Callbacks Types - types used by the template builder callbacks
154
+ ***************************************************************************** */
155
+
156
+ /**
157
+ * A mustache section allows the callbacks to "walk" backwards towards the root
158
+ * in search of argument data.
159
+ *
160
+ * Note that every section is allowed a separate udata value.
161
+ */
162
+ typedef struct mustache_section_s {
163
+ /** Opaque user data (recommended for input review) - children will inherit
164
+ * the parent's udata value. Updated values will propegate to child sections
165
+ * but won't effect parent sections.
166
+ */
167
+ void *udata1;
168
+ /** Opaque user data (recommended for output handling)- children will inherit
169
+ * the parent's udata value. Updated values will propegate to child sections
170
+ * but won't effect parent sections.
171
+ */
172
+ void *udata2;
173
+ } mustache_section_s;
174
+
175
+ /* *****************************************************************************
176
+ Callbacks Helpers - These functions can be called from within callbacks
177
+ ***************************************************************************** */
178
+
179
+ /**
180
+ * Returns the section's parent for nested sections, or NULL (for root section).
181
+ *
182
+ * This allows the `udata` values in the parent to be accessed and can be used,
183
+ * for example, when seeking a value (argument / keyword) within a nested data
184
+ * structure such as a Hash.
185
+ */
186
+ static inline mustache_section_s *
187
+ mustache_section_parent(mustache_section_s *section);
188
+
189
+ /**
190
+ * This helper function should be used to write text to the output
191
+ * stream form within `mustache_on_arg` or `mustache_on_section_test`.
192
+ *
193
+ * This function will call the `mustache_on_text` callback for each slice of
194
+ * text that requires padding and for escaped data.
195
+ *
196
+ * `mustache_on_text` must NEVER call this function.
197
+ */
198
+ static inline int mustache_write_text(mustache_section_s *section, char *text,
199
+ uint32_t len, uint8_t escape);
200
+
201
+ /**
202
+ * Returns the section's unparsed content as a non-NUL terminated byte array.
203
+ *
204
+ * The length of the data will be placed in the `size_t` variable pointed to by
205
+ * `p_len`. Do NOT use `strlen`, since the data isn't NUL terminated.
206
+ *
207
+ * This allows text to be accessed when a section's content is, in fact, meant
208
+ * to be passed along to a function / lambda.
209
+ */
210
+ static inline const char *mustache_section_text(mustache_section_s *section,
211
+ size_t *p_len);
212
+
213
+ /* *****************************************************************************
214
+ Client Callbacks - MUST be implemented by the including file
215
+ ***************************************************************************** */
216
+
217
+ /**
218
+ * Called when an argument name was detected in the current section.
219
+ *
220
+ * A conforming implementation will search for the named argument both in the
221
+ * existing section and all of it's parents (walking backwards towards the root)
222
+ * until a value is detected.
223
+ *
224
+ * A missing value should be treated the same as an empty string.
225
+ *
226
+ * A conforming implementation will output the named argument's value (either
227
+ * HTML escaped or not, depending on the `escape` flag) as a string.
228
+ *
229
+ * A conforming implementation will test for dot notation in the name.
230
+ *
231
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
232
+ * determine the actual string length.
233
+ */
234
+ static int mustache_on_arg(mustache_section_s *section, const char *name,
235
+ uint32_t name_len, unsigned char escape);
236
+
237
+ /**
238
+ * Called when simple template text (string) is detected.
239
+ *
240
+ * A conforming implementation will output data as a string (no escaping).
241
+ *
242
+ * NOTE: the `data` is **not** NUL terminated. Use the `data_len` data to
243
+ * determine the actual data length.
244
+ */
245
+ static int mustache_on_text(mustache_section_s *section, const char *data,
246
+ uint32_t data_len);
247
+
248
+ /**
249
+ * Called for nested sections, must return the number of objects in the new
250
+ * subsection (depending on the argument's name).
251
+ *
252
+ * Arrays should return the number of objects in the array.
253
+ *
254
+ * `true` values should return 1.
255
+ *
256
+ * `false` values should return 0.
257
+ *
258
+ * A return value of -1 will stop processing with an error.
259
+ *
260
+ * Please note, this will handle both normal and inverted sections.
261
+ *
262
+ * The `callable` value is true if the section is allowed to be a function /
263
+ * callback. If the section object represent a function / callback (lambda), the
264
+ * lambda should be called and the `mustache_on_section_test` callback should
265
+ * return 0.
266
+ *
267
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
268
+ * determine the actual string length.
269
+ *
270
+ * A conforming implementation will test for dot notation in the name.
271
+ */
272
+ static int32_t mustache_on_section_test(mustache_section_s *section,
273
+ const char *name, uint32_t name_len,
274
+ uint8_t callable);
275
+
276
+ /**
277
+ * Called when entering a nested section.
278
+ *
279
+ * `index` is a zero based index indicating the number of repetitions that
280
+ * occurred so far (same as the array index for arrays).
281
+ *
282
+ * A return value of -1 will stop processing with an error.
283
+ *
284
+ * Note: this is a good time to update the subsection's `udata` with the value
285
+ * of the array index. The `udata` will always contain the value or the parent's
286
+ * `udata`.
287
+ *
288
+ * NOTE: the `name` data is **not** NUL terminated. Use the `name_len` data to
289
+ * determine the actual string length.
290
+ *
291
+ * A conforming implementation will test for dot notation in the name.
292
+ */
293
+ static int mustache_on_section_start(mustache_section_s *section,
294
+ char const *name, uint32_t name_len,
295
+ uint32_t index);
296
+
297
+ /**
298
+ * Called for cleanup in case of error.
299
+ */
300
+ static void mustache_on_formatting_error(void *udata1, void *udata2);
301
+
302
+ /* *****************************************************************************
303
+
304
+ IMPLEMENTATION (beware: monolithic functions ahead)
305
+
306
+ ***************************************************************************** */
307
+
308
+ /* *****************************************************************************
309
+ Internal types
310
+ ***************************************************************************** */
311
+
312
+ struct mustache_s {
313
+ /* The number of instructions in the engine */
314
+ union {
315
+ void *read_only_pt; /* ensure pointer wide padding */
316
+ struct {
317
+ uint32_t intruction_count;
318
+ uint32_t data_length;
319
+ } read_only;
320
+ } u;
321
+ };
322
+
323
+ typedef struct mustache__instruction_s {
324
+ enum {
325
+ MUSTACHE_WRITE_TEXT,
326
+ MUSTACHE_WRITE_ARG,
327
+ MUSTACHE_WRITE_ARG_UNESCAPED,
328
+ MUSTACHE_SECTION_START,
329
+ MUSTACHE_SECTION_START_INV,
330
+ MUSTACHE_SECTION_END,
331
+ MUSTACHE_SECTION_GOTO,
332
+ MUSTACHE_PADDING_PUSH,
333
+ MUSTACHE_PADDING_POP,
334
+ MUSTACHE_PADDING_WRITE,
335
+ } instruction;
336
+ /** the data the instruction acts upon */
337
+ struct {
338
+ /** The length instruction block in the instruction array (for sections). */
339
+ uint32_t end;
340
+ /** The length of the (string) data. */
341
+ uint32_t len;
342
+ /** The offset from the beginning of the data segment. */
343
+ uint32_t name_pos;
344
+ /** The offset between the name (start) and content (for sections). */
345
+ uint16_t name_len;
346
+ /** The offset between the name and the content (left / right by type). */
347
+ uint16_t offset;
348
+ } data;
349
+ } mustache__instruction_s;
350
+
351
+ typedef struct {
352
+ mustache_section_s sec; /* client visible section data */
353
+ uint32_t start; /* section start instruction position */
354
+ uint32_t end; /* instruction to jump to after completion */
355
+ uint32_t index; /* zero based index for section loops */
356
+ uint32_t count; /* the number of times the section should be performed */
357
+ uint16_t frame; /* the stack frame's index (zero based) */
358
+ } mustache__section_stack_frame_s;
359
+
360
+ /*
361
+ * The stack memory is placed in a structure to allow stack unrolling and
362
+ * avoiding recursion with it's stack overflow risks.
363
+ */
364
+ typedef struct {
365
+ mustache_s *data; /* the mustache template being built */
366
+ uint32_t pos; /* the instruction postision index */
367
+ uint32_t padding; /* padding instruction position */
368
+ uint16_t index; /* the stack postision index */
369
+ mustache__section_stack_frame_s stack[MUSTACHE_NESTING_LIMIT];
370
+ } mustache__builder_stack_s;
371
+
372
+ #define MUSTACHE_DELIMITER_LENGTH_LIMIT 5
373
+
374
+ /*
375
+ * The stack memory is placed in a structure to allow stack unrolling and
376
+ * avoiding recursion with it's stack overflow risks.
377
+ */
378
+ typedef struct {
379
+ mustache_s *m;
380
+ mustache__instruction_s *i;
381
+ mustache_error_en *err;
382
+ char *data;
383
+ char *path;
384
+ uint32_t i_capa;
385
+ uint32_t data_len;
386
+ uint32_t padding;
387
+ uint16_t path_len;
388
+ uint16_t path_capa;
389
+ uint16_t index; /* stack index */
390
+ struct {
391
+ uint32_t data_start; /* template starting position (with header) */
392
+ uint32_t data_pos; /* data reading position (how much was consumed) */
393
+ uint32_t data_end; /* data ending position (for this template) */
394
+ uint16_t open_sections; /* counts open sections waiting for closure */
395
+ char del_start[MUSTACHE_DELIMITER_LENGTH_LIMIT]; /* currunt instruction
396
+ start delimiter */
397
+ char del_end[MUSTACHE_DELIMITER_LENGTH_LIMIT]; /* currunt instruction end
398
+ delimiter */
399
+ uint8_t del_start_len; /* currunt start delimiter length */
400
+ uint8_t del_end_len; /* currunt end delimiter length */
401
+ } stack[MUSTACHE_NESTING_LIMIT];
402
+ } mustache__loader_stack_s;
403
+
404
+ /* *****************************************************************************
405
+ Callbacks Helper Implementation
406
+ ***************************************************************************** */
407
+
408
+ #define MUSTACH2INSTRUCTIONS(mustache) \
409
+ ((mustache__instruction_s *)((mustache) + 1))
410
+ #define MUSTACH2DATA(mustache) \
411
+ (char *)(MUSTACH2INSTRUCTIONS((mustache)) + \
412
+ (mustache)->u.read_only.intruction_count)
413
+ #define MUSTACHE_OBJECT_OFFSET(type, member, ptr) \
414
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)(&(((type *)0)->member))))
415
+
416
+ #define MUSTACHE_ASSERT(cond, msg) \
417
+ if (!(cond)) { \
418
+ perror("FATAL ERROR: " msg); \
419
+ exit(errno); \
420
+ }
421
+ #define MUSTACHE_IGNORE_WHITESPACE(str, step) \
422
+ while (isspace(*(str))) { \
423
+ (str) += (step); \
424
+ }
425
+
426
+ /*
427
+ * used internally by some of the helpers to find convert a section pointer to
428
+ * the full builder stack data.
429
+ */
430
+ static inline mustache__builder_stack_s *
431
+ mustache___section2stack(mustache_section_s *section) {
432
+ mustache__section_stack_frame_s *f =
433
+ (mustache__section_stack_frame_s *)section;
434
+ return MUSTACHE_OBJECT_OFFSET(mustache__builder_stack_s, stack,
435
+ (f - f->frame));
436
+ }
437
+
438
+ /**
439
+ * Returns the section's parent for nested sections, or NULL (for root section).
440
+ *
441
+ * This allows the `udata` values in the parent to be accessed and can be used,
442
+ * for example, when seeking a value (argument / keyword) within a nested data
443
+ * structure such as a Hash.
444
+ */
445
+ static inline mustache_section_s *
446
+ mustache_section_parent(mustache_section_s *section) {
447
+ mustache_section_s tmp = *section;
448
+ mustache__section_stack_frame_s *f =
449
+ (mustache__section_stack_frame_s *)section;
450
+ while (f->frame) {
451
+ --f;
452
+ if (tmp.udata1 != f->sec.udata1 || tmp.udata2 != f->sec.udata2)
453
+ return &f->sec;
454
+ }
455
+ return NULL;
456
+ }
457
+
458
+ /**
459
+ * Returns the section's unparsed content as a non-NUL terminated byte array.
460
+ *
461
+ * The length of the data will be placed in the `size_t` variable pointed to by
462
+ * `p_len`. Do NOT use `strlen`, since the data isn't NUL terminated.
463
+ *
464
+ * This allows text to be accessed when a section's content is, in fact, meant
465
+ * to be passed along to a function / lambda.
466
+ */
467
+ static inline const char *mustache_section_text(mustache_section_s *section,
468
+ size_t *p_len) {
469
+ if (!section || !p_len)
470
+ goto error;
471
+ mustache__builder_stack_s *s = mustache___section2stack(section);
472
+ mustache__instruction_s *inst =
473
+ MUSTACH2INSTRUCTIONS(s->data) + s->pos; /* current instruction*/
474
+ if (inst->instruction != MUSTACHE_SECTION_START)
475
+ goto error;
476
+ const char *start =
477
+ MUSTACH2DATA(s->data) + inst->data.name_pos + inst->data.offset;
478
+ *p_len = inst->data.len;
479
+ return start;
480
+ error:
481
+ *p_len = 0;
482
+ return NULL;
483
+ }
484
+
485
+ /**
486
+ * used internally to write escaped text rather than clear text.
487
+ */
488
+ static inline int mustache__write_padding(mustache__builder_stack_s *s) {
489
+ mustache__instruction_s *const inst = MUSTACH2INSTRUCTIONS(s->data);
490
+ char *const data = MUSTACH2DATA(s->data);
491
+ for (uint32_t i = s->padding; i; i = inst[i].data.end) {
492
+ if (mustache_on_text(&s->stack[s->index].sec, data + inst[i].data.name_pos,
493
+ inst[i].data.name_len) == -1)
494
+ return -1;
495
+ }
496
+ return 0;
497
+ }
498
+
499
+ /**
500
+ * used internally to write escaped text rather than clear text.
501
+ */
502
+ static int mustache__write_escaped(mustache__builder_stack_s *s, char *text,
503
+ uint32_t len) {
504
+ #define MUSTACHE_ESCAPE_BUFFER_SIZE 4096
505
+ /** HTML ecape table, created using the following Ruby Script:
506
+
507
+ a = (0..255).to_a.map {|i| i.chr }
508
+ 100.times {|i| a[i] = "&\##{i.to_s(10)};"}
509
+ ('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
510
+ ('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
511
+ ('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
512
+ a['<'.ord] = "&lt;"
513
+ a['>'.ord] = "&gt;"
514
+ a['&'.ord] = "&amp;"
515
+ a['"'.ord] = "&quot;"
516
+ a["\'".ord] = "&apos;"
517
+ a['|'.ord] = "&\##{'|'.ord.to_s(10)};"
518
+
519
+ b = a.map {|s| s.length }
520
+ puts "static char *html_escape_strs[] = {",
521
+ a.to_s.slice(1..-2) ,"};",
522
+ "static uint8_t html_escape_len[] = {",
523
+ b.to_s.slice(1..-2),"};"
524
+ */
525
+ static const char *html_escape_strs[] = {
526
+ "&#0;", "&#1;", "&#2;", "&#3;", "&#4;", "&#5;", "&#6;", "&#7;",
527
+ "&#8;", "&#9;", "&#10;", "&#11;", "&#12;", "&#13;", "&#14;", "&#15;",
528
+ "&#16;", "&#17;", "&#18;", "&#19;", "&#20;", "&#21;", "&#22;", "&#23;",
529
+ "&#24;", "&#25;", "&#26;", "&#27;", "&#28;", "&#29;", "&#30;", "&#31;",
530
+ "&#32;", "&#33;", "&quot;", "&#35;", "&#36;", "&#37;", "&amp;", "&apos;",
531
+ "&#40;", "&#41;", "&#42;", "&#43;", "&#44;", "&#45;", "&#46;", "&#47;",
532
+ "0", "1", "2", "3", "4", "5", "6", "7",
533
+ "8", "9", "&#58;", "&#59;", "&lt;", "&#61;", "&gt;", "&#63;",
534
+ "&#64;", "A", "B", "C", "D", "E", "F", "G",
535
+ "H", "I", "J", "K", "L", "M", "N", "O",
536
+ "P", "Q", "R", "S", "T", "U", "V", "W",
537
+ "X", "Y", "Z", "&#91;", "&#92;", "&#93;", "&#94;", "&#95;",
538
+ "&#96;", "a", "b", "c", "d", "e", "f", "g",
539
+ "h", "i", "j", "k", "l", "m", "n", "o",
540
+ "p", "q", "r", "s", "t", "u", "v", "w",
541
+ "x", "y", "z", "{", "&#124;", "}", "~", "\x7F",
542
+ "\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
543
+ "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F",
544
+ "\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
545
+ "\x98", "\x99", "\x9A", "\x9B", "\x9C", "\x9D", "\x9E", "\x9F",
546
+ "\xA0", "\xA1", "\xA2", "\xA3", "\xA4", "\xA5", "\xA6", "\xA7",
547
+ "\xA8", "\xA9", "\xAA", "\xAB", "\xAC", "\xAD", "\xAE", "\xAF",
548
+ "\xB0", "\xB1", "\xB2", "\xB3", "\xB4", "\xB5", "\xB6", "\xB7",
549
+ "\xB8", "\xB9", "\xBA", "\xBB", "\xBC", "\xBD", "\xBE", "\xBF",
550
+ "\xC0", "\xC1", "\xC2", "\xC3", "\xC4", "\xC5", "\xC6", "\xC7",
551
+ "\xC8", "\xC9", "\xCA", "\xCB", "\xCC", "\xCD", "\xCE", "\xCF",
552
+ "\xD0", "\xD1", "\xD2", "\xD3", "\xD4", "\xD5", "\xD6", "\xD7",
553
+ "\xD8", "\xD9", "\xDA", "\xDB", "\xDC", "\xDD", "\xDE", "\xDF",
554
+ "\xE0", "\xE1", "\xE2", "\xE3", "\xE4", "\xE5", "\xE6", "\xE7",
555
+ "\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
556
+ "\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7",
557
+ "\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF"};
558
+ static const uint8_t html_escape_len[] = {
559
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
560
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5,
561
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 5, 4, 5, 5, 1, 1, 1, 1, 1, 1, 1,
562
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5,
563
+ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
564
+ 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
565
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
566
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
567
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
568
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
569
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
570
+ char buffer[MUSTACHE_ESCAPE_BUFFER_SIZE];
571
+ size_t pos = 0;
572
+ const char *end = text + len;
573
+ while (text < end) {
574
+ if (MUSTACHE_USE_DYNAMIC_PADDING && *text == '\n' && s->padding) {
575
+ buffer[pos++] = '\n';
576
+ buffer[pos] = 0;
577
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
578
+ return -1;
579
+ pos = 0;
580
+ if (mustache__write_padding(s) == -1)
581
+ return -1;
582
+ } else {
583
+ memcpy(buffer + pos, html_escape_strs[(uint8_t)text[0]],
584
+ html_escape_len[(uint8_t)text[0]]);
585
+ pos += html_escape_len[(uint8_t)text[0]];
586
+ if (pos >= (MUSTACHE_ESCAPE_BUFFER_SIZE - 6)) {
587
+ buffer[pos] = 0;
588
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
589
+ return -1;
590
+ pos = 0;
591
+ }
592
+ }
593
+ ++text;
594
+ }
595
+ if (pos) {
596
+ buffer[pos] = 0;
597
+ if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
598
+ return -1;
599
+ }
600
+ return 0;
601
+ #undef MUSTACHE_ESCAPE_BUFFER_SIZE
602
+ }
603
+ /**
604
+ * This helper function should be used to write text to the output
605
+ * stream (often used within the `mustache_on_arg` callback).
606
+ */
607
+ static inline int mustache_write_text(mustache_section_s *section, char *text,
608
+ uint32_t len, uint8_t escape) {
609
+ mustache__builder_stack_s *s = mustache___section2stack(section);
610
+ if (escape)
611
+ return mustache__write_escaped(s, text, len);
612
+ /* TODO */
613
+ #if MUSTACHE_USE_DYNAMIC_PADDING
614
+ char *end = memchr(text, '\n', len);
615
+ while (len && end) {
616
+ ++end;
617
+ const uint32_t slice_len = end - text;
618
+ if (mustache_on_text(&s->stack[s->index].sec, text, slice_len) == -1)
619
+ return -1;
620
+ text = end;
621
+ len -= slice_len;
622
+ end = memchr(text, '\n', len);
623
+ if (mustache__write_padding(s) == -1)
624
+ return -1;
625
+ }
626
+ if (len && mustache_on_text(&s->stack[s->index].sec, text, len) == -1)
627
+ return -1;
628
+ #else
629
+ if (mustache_on_text(&s->stack[s->index].sec, text, len) == -1)
630
+ return -1;
631
+
632
+ #endif
633
+ return 0;
634
+ }
635
+
636
+ /* *****************************************************************************
637
+ Internal Helpers
638
+ ***************************************************************************** */
639
+
640
+ /* the data segment's new length after loading the the template */
641
+ /* The data segments includes a template header: */
642
+ /* | 4 bytes template start instruction position | */
643
+ /* | 2 bytes template name | 4 bytes next template position | */
644
+ /* | template name (filename) | ...[template data]... */
645
+ /* this allows template data to be reused when repeating a template */
646
+
647
+ /** a template file data segment header */
648
+ typedef struct {
649
+ const char *filename; /* template file name */
650
+ uint32_t inst_start; /* start position for instructions */
651
+ uint32_t next; /* next template position (absolute) */
652
+ uint16_t filename_len; /* file name length */
653
+ uint16_t path_len; /* if the file is in a folder, this marks the '/' */
654
+ } mustache__data_segment_s;
655
+
656
+ /* data segment serialization, returns the number of bytes written. */
657
+ static inline size_t
658
+ mustache__data_segment_write(uint8_t *dest, mustache__data_segment_s data) {
659
+ dest[0] = 0xFF & data.inst_start;
660
+ dest[1] = 0xFF & (data.inst_start >> 1);
661
+ dest[2] = 0xFF & (data.inst_start >> 2);
662
+ dest[3] = 0xFF & (data.inst_start >> 3);
663
+ dest[4] = 0xFF & data.next;
664
+ dest[5] = 0xFF & (data.next >> 1);
665
+ dest[6] = 0xFF & (data.next >> 2);
666
+ dest[7] = 0xFF & (data.next >> 3);
667
+ dest[8] = 0xFF & data.filename_len;
668
+ dest[9] = 0xFF & (data.filename_len >> 1);
669
+ dest[10] = 0xFF & data.path_len;
670
+ dest[11] = 0xFF & (data.path_len >> 1);
671
+ if (data.filename_len)
672
+ memcpy(dest + 12, data.filename, data.filename_len);
673
+ (dest + 12)[data.filename_len] = 0;
674
+ return 13 + data.filename_len;
675
+ }
676
+
677
+ static inline size_t mustache__data_segment_length(size_t filename_len) {
678
+ return 13 + filename_len;
679
+ }
680
+
681
+ /* data segment serialization, reads data from raw stream. */
682
+ static inline mustache__data_segment_s
683
+ mustache__data_segment_read(uint8_t *data) {
684
+ mustache__data_segment_s s = {
685
+ .filename = (char *)(data + 12),
686
+ .inst_start = ((uint32_t)data[0] | ((uint32_t)data[1] << 1) |
687
+ ((uint32_t)data[2] << 2) | ((uint32_t)data[3] << 3)),
688
+ .next = ((uint32_t)data[4] | ((uint32_t)data[5] << 1) |
689
+ ((uint32_t)data[6] << 2) | ((uint32_t)data[7] << 3)),
690
+ .filename_len = ((uint16_t)data[8] | ((uint16_t)data[9] << 1)),
691
+ .path_len = ((uint16_t)data[10] | ((uint16_t)data[11] << 1)),
692
+ };
693
+ return s;
694
+ }
695
+
696
+ static inline void mustache__stand_alone_adjust(mustache__loader_stack_s *s,
697
+ uint32_t stand_alone) {
698
+ if (!stand_alone)
699
+ return;
700
+ /* adjust reading position */
701
+ s->stack[s->index].data_pos +=
702
+ 1 + (s->data[s->stack[s->index].data_pos] == '\r');
703
+ /* remove any padding from the beginning of the line */
704
+ if (s->m->u.read_only.intruction_count &&
705
+ s->i[s->m->u.read_only.intruction_count - 1].instruction ==
706
+ MUSTACHE_WRITE_TEXT) {
707
+ mustache__instruction_s *ins =
708
+ s->i + s->m->u.read_only.intruction_count - 1;
709
+ if (ins->data.name_len <= (stand_alone >> 1))
710
+ --s->m->u.read_only.intruction_count;
711
+ else
712
+ ins->data.name_len -= (stand_alone >> 1);
713
+ }
714
+ }
715
+
716
+ /* pushes an instruction to the instruction array */
717
+ static inline int mustache__instruction_push(mustache__loader_stack_s *s,
718
+ mustache__instruction_s inst) {
719
+ if (s->m->u.read_only.intruction_count >= INT32_MAX)
720
+ goto instructions_too_long;
721
+ if (s->i_capa <= s->m->u.read_only.intruction_count) {
722
+ s->m = realloc(s->m, sizeof(*s->m) + (sizeof(*s->i) * (s->i_capa + 32)));
723
+ MUSTACHE_ASSERT(s->m, "Mustache memory allocation failed");
724
+ s->i_capa += 32;
725
+ s->i = MUSTACH2INSTRUCTIONS(s->m);
726
+ }
727
+ s->i[s->m->u.read_only.intruction_count] = inst;
728
+ ++s->m->u.read_only.intruction_count;
729
+ return 0;
730
+ instructions_too_long:
731
+ *s->err = MUSTACHE_ERR_TOO_DEEP;
732
+ return -1;
733
+ }
734
+
735
+ /* pushes text and padding instruction to the instruction array */
736
+ static inline int mustache__push_text_instruction(mustache__loader_stack_s *s,
737
+ uint32_t pos, uint32_t len) {
738
+ /* always push padding instructions, in case or recursion with padding */
739
+ // if (!s->padding) {
740
+ // /* no padding, push text, as is */
741
+ // return mustache__instruction_push(
742
+ // s, (mustache__instruction_s){
743
+ // .instruction = MUSTACHE_WRITE_TEXT,
744
+ // .data = {.name_pos = pos, .name_len = len},
745
+ // });
746
+ // }
747
+ /* insert padding instruction after each new line */
748
+ for (;;) {
749
+ /* seek new line markers */
750
+ char *start = s->data + pos;
751
+ char *end = memchr(s->data + pos, '\n', len);
752
+ if (!end)
753
+ break;
754
+ /* offset marks the line's length */
755
+ const size_t offset = (end - start) + 1;
756
+ /* push text and padding instructions */
757
+ if (mustache__instruction_push(
758
+ s,
759
+ (mustache__instruction_s){
760
+ .instruction = MUSTACHE_WRITE_TEXT,
761
+ .data = {.name_pos = pos, .name_len = offset},
762
+ }) == -1 ||
763
+ mustache__instruction_push(s, (mustache__instruction_s){
764
+ .instruction = MUSTACHE_PADDING_WRITE,
765
+ }) == -1)
766
+ return -1;
767
+ pos += offset;
768
+ len -= offset;
769
+ }
770
+ /* done? */
771
+ if (!len)
772
+ return 0;
773
+ /* write any text that doesn't terminate in the EOL marker */
774
+ return mustache__instruction_push(
775
+ s, (mustache__instruction_s){
776
+ .instruction = MUSTACHE_WRITE_TEXT,
777
+ .data = {.name_pos = pos, .name_len = len},
778
+ });
779
+ }
780
+
781
+ /*
782
+ * Returns the instruction's position if the template is already existing.
783
+ *
784
+ * Returns `(uint32_t)-1` if the template is missing (not loaded, yet).
785
+ */
786
+ static inline uint32_t mustache__file_is_loaded(mustache__loader_stack_s *s,
787
+ char *name, size_t name_len) {
788
+ char *data = s->data;
789
+ const char *end = data + s->m->u.read_only.data_length;
790
+ while (data < end) {
791
+ mustache__data_segment_s seg = mustache__data_segment_read((uint8_t *)data);
792
+ if (seg.filename_len == name_len && !memcmp(seg.filename, name, name_len))
793
+ return seg.inst_start;
794
+ data += seg.next;
795
+ }
796
+ return (uint32_t)-1;
797
+ }
798
+
799
+ static inline ssize_t mustache__load_data(mustache__loader_stack_s *s,
800
+ const char *name, size_t name_len,
801
+ const char *data, size_t data_len) {
802
+ const size_t old_len = s->data_len;
803
+ if (old_len + data_len > UINT32_MAX)
804
+ goto too_long;
805
+ s->data = realloc(s->data, old_len + data_len +
806
+ mustache__data_segment_length(name_len) + 1);
807
+ MUSTACHE_ASSERT(s->data,
808
+ "failed to allocate memory for mustache template data");
809
+ size_t path_len = name_len;
810
+ while (path_len) {
811
+ --path_len;
812
+ if (name[path_len] == '/' || name[path_len] == '\\') {
813
+ ++path_len;
814
+ break;
815
+ }
816
+ }
817
+ mustache__data_segment_write(
818
+ (uint8_t *)s->data + old_len,
819
+ (mustache__data_segment_s){
820
+ .filename = name,
821
+ .filename_len = name_len,
822
+ .inst_start = s->m->u.read_only.intruction_count,
823
+ .next =
824
+ s->data_len + data_len + mustache__data_segment_length(name_len),
825
+ .path_len = path_len,
826
+ });
827
+ if (data) {
828
+ memcpy(s->data + old_len + mustache__data_segment_length(name_len), data,
829
+ data_len);
830
+ }
831
+ s->data_len += data_len + mustache__data_segment_length(name_len);
832
+ s->data[s->data_len] = 0;
833
+ s->m->u.read_only.data_length = s->data_len;
834
+
835
+ mustache__instruction_push(
836
+ s, (mustache__instruction_s){.instruction = MUSTACHE_SECTION_START});
837
+ /* advance stack frame */
838
+ ++s->index;
839
+ if (s->index >= MUSTACHE_NESTING_LIMIT)
840
+ goto too_long;
841
+ s->stack[s->index].data_pos =
842
+ old_len + mustache__data_segment_length(name_len);
843
+ s->stack[s->index].data_start = old_len;
844
+ s->stack[s->index].data_end = s->data_len;
845
+ /* reset delimiters */
846
+ s->stack[s->index].del_start_len = 2;
847
+ s->stack[s->index].del_end_len = 2;
848
+ s->stack[s->index].del_start[0] = s->stack[s->index].del_start[1] = '{';
849
+ s->stack[s->index].del_start[2] = 0;
850
+ s->stack[s->index].del_end[0] = s->stack[s->index].del_end[1] = '}';
851
+ s->stack[s->index].del_end[2] = 0;
852
+ s->stack[s->index].open_sections = 0;
853
+ return data_len;
854
+ too_long:
855
+ *s->err = MUSTACHE_ERR_TOO_DEEP;
856
+ return -1;
857
+ }
858
+
859
+ static inline ssize_t mustache__load_file(mustache__loader_stack_s *s,
860
+ const char *name, size_t name_len) {
861
+ struct stat f_data;
862
+ uint16_t i = s->index;
863
+ uint32_t old_path_len = 0;
864
+ if (!name_len) {
865
+ goto name_missing_error;
866
+ }
867
+ if (name_len >= 8192)
868
+ goto name_length_error;
869
+ /* test file names by walking the stack backwards and matching paths */
870
+ do {
871
+ mustache__data_segment_s seg;
872
+ if (s->data)
873
+ seg = mustache__data_segment_read((uint8_t *)s->data +
874
+ s->stack[i].data_start);
875
+ else
876
+ seg = (mustache__data_segment_s){
877
+ .path_len = 0,
878
+ };
879
+ /* did we test this path for the file? */
880
+ if (old_path_len && (old_path_len == seg.path_len &&
881
+ !memcmp(s->path, seg.filename, old_path_len))) {
882
+ continue;
883
+ }
884
+ old_path_len = seg.path_len;
885
+ /* make sure s->path capacity is enough. */
886
+ if (s->path_capa < seg.path_len + name_len + 10) {
887
+ s->path = realloc(s->path, seg.path_len + name_len + 10);
888
+ MUSTACHE_ASSERT(s->path,
889
+ "failed to allocate memory for mustache template data");
890
+ s->path_capa = seg.path_len + name_len + 10;
891
+ }
892
+ /* if testing local folder, there's no need to keep looping */
893
+ if (!seg.path_len) {
894
+ i = 1;
895
+ } else {
896
+ memcpy(s->path, seg.filename, seg.path_len);
897
+ }
898
+ /* copy name to path */
899
+ memcpy(s->path + seg.path_len, name, name_len);
900
+ s->path[name_len + seg.path_len] = 0;
901
+ /* test if file exists */
902
+ if (!stat(s->path, &f_data) && S_ISREG(f_data.st_mode)) {
903
+ old_path_len = name_len + seg.path_len;
904
+ goto file_found;
905
+ }
906
+ /* add default extension */
907
+ memcpy(s->path + seg.path_len + name_len, ".mustache", 9);
908
+ s->path[name_len + seg.path_len + 9] = 0;
909
+ /* test if new filename file exists */
910
+ if (!stat(s->path, &f_data) && S_ISREG(f_data.st_mode)) {
911
+ old_path_len = name_len + seg.path_len + 9;
912
+ goto file_found;
913
+ }
914
+ } while (--i);
915
+
916
+ /* test if the file is "virtual" (only true for the first template loaded) */
917
+ if (s->data) {
918
+ mustache__data_segment_s seg =
919
+ mustache__data_segment_read((uint8_t *)s->data);
920
+ if (seg.filename_len == name_len && !memcmp(seg.filename, name, name_len)) {
921
+ /* this name points to the original (root) template, and it's virtual */
922
+ if (mustache__instruction_push(
923
+ s, (mustache__instruction_s){
924
+ .instruction = MUSTACHE_SECTION_GOTO,
925
+ .data =
926
+ {
927
+ .len = 0,
928
+ .end = s->m->u.read_only.intruction_count,
929
+ },
930
+ }))
931
+ goto unknown_error;
932
+ return 0;
933
+ }
934
+ }
935
+
936
+ #if MUSTACHE_FAIL_ON_MISSING_TEMPLATE
937
+ *s->err = MUSTACHE_ERR_FILE_NOT_FOUND;
938
+ return -1;
939
+ #else
940
+ return 0;
941
+ #endif
942
+
943
+ file_found:
944
+ if (f_data.st_size >= INT32_MAX) {
945
+ goto file_too_big;
946
+ } else if (f_data.st_size == 0) {
947
+ /* empty, do nothing */
948
+ return 0;
949
+ } else {
950
+ /* test if the file was previously loaded */
951
+ uint32_t pre_existing = mustache__file_is_loaded(s, s->path, old_path_len);
952
+ if (pre_existing != (uint32_t)-1) {
953
+ if (mustache__instruction_push(
954
+ s, (mustache__instruction_s){
955
+ .instruction = MUSTACHE_SECTION_GOTO,
956
+ .data =
957
+ {
958
+ .len = pre_existing,
959
+ .end = s->m->u.read_only.intruction_count,
960
+ },
961
+ })) {
962
+ goto unknown_error;
963
+ }
964
+ return 0;
965
+ }
966
+ }
967
+ if (mustache__load_data(s, s->path, old_path_len, NULL, f_data.st_size) == -1)
968
+ goto unknown_error;
969
+ int fd = open(s->path, O_RDONLY);
970
+ if (fd == -1)
971
+ goto file_err;
972
+ if (pread(fd, s->data + s->data_len - f_data.st_size, f_data.st_size, 0) !=
973
+ f_data.st_size)
974
+ goto file_err;
975
+ close(fd);
976
+ return f_data.st_size;
977
+
978
+ name_missing_error:
979
+ *s->err = MUSTACHE_ERR_FILE_NAME_TOO_SHORT;
980
+ return -1;
981
+
982
+ name_length_error:
983
+ *s->err = MUSTACHE_ERR_FILE_NAME_TOO_LONG;
984
+ return -1;
985
+
986
+ file_too_big:
987
+ *s->err = MUSTACHE_ERR_FILE_TOO_BIG;
988
+ return -1;
989
+
990
+ file_err:
991
+ *s->err = MUSTACHE_ERR_UNKNOWN;
992
+ return -1;
993
+
994
+ unknown_error:
995
+ return -1;
996
+ }
997
+
998
+ /* *****************************************************************************
999
+ Calling the instrustion list (using the template engine)
1000
+ ***************************************************************************** */
1001
+
1002
+ /*
1003
+ * This function reviews the instructions listed at the end of the mustache_s
1004
+ * and performs any callbacks necessary.
1005
+ *
1006
+ * The `mustache_s` data is looks like this:
1007
+ *
1008
+ * - header (the `mustache_s` struct): lists the length of the instruction
1009
+ * array and data segments.
1010
+ * - Instruction array: lists all the instructions extracted from the
1011
+ * template(s) (an array of `mustache__instruction_s`).
1012
+ * - Data segment: text and data related to the instructions.
1013
+ *
1014
+ * The instructions, much like machine code, might loop or jump. This is why the
1015
+ * function keeps a stack of sorts. This allows the code to avoid recursion and
1016
+ * minimize any risk of stack overflow caused by recursive templates.
1017
+ */
1018
+ MUSTACHE_FUNC int(mustache_build)(mustache_build_args_s args) {
1019
+ mustache_error_en err_if_missing;
1020
+ if (!args.err)
1021
+ args.err = &err_if_missing;
1022
+ if (!args.mustache) {
1023
+ goto user_error;
1024
+ }
1025
+ /* extract the instruction array and data segment from the mustache_s */
1026
+ mustache__instruction_s *instructions = MUSTACH2INSTRUCTIONS(args.mustache);
1027
+ char *const data = MUSTACH2DATA(args.mustache);
1028
+ mustache__builder_stack_s s;
1029
+ s.data = args.mustache;
1030
+ s.pos = 0;
1031
+ s.index = 0;
1032
+ s.padding = 0;
1033
+ s.stack[0] = (mustache__section_stack_frame_s){
1034
+ .sec =
1035
+ {
1036
+ .udata1 = args.udata1,
1037
+ .udata2 = args.udata2,
1038
+ },
1039
+ .start = 0,
1040
+ .end = instructions[0].data.end,
1041
+ .index = 0,
1042
+ .count = 0,
1043
+ .frame = 0,
1044
+ };
1045
+ while ((uintptr_t)(instructions + s.pos) < (uintptr_t)data) {
1046
+ switch (instructions[s.pos].instruction) {
1047
+ case MUSTACHE_WRITE_TEXT:
1048
+ if (mustache_on_text(&s.stack[s.index].sec,
1049
+ data + instructions[s.pos].data.name_pos,
1050
+ instructions[s.pos].data.name_len))
1051
+ goto user_error;
1052
+ break;
1053
+ /* fallthrough */
1054
+ case MUSTACHE_WRITE_ARG:
1055
+ if (mustache_on_arg(&s.stack[s.index].sec,
1056
+ data + instructions[s.pos].data.name_pos,
1057
+ instructions[s.pos].data.name_len, 1))
1058
+ goto user_error;
1059
+ break;
1060
+ case MUSTACHE_WRITE_ARG_UNESCAPED:
1061
+ if (mustache_on_arg(&s.stack[s.index].sec,
1062
+ data + instructions[s.pos].data.name_pos,
1063
+ instructions[s.pos].data.name_len, 0))
1064
+ goto user_error;
1065
+ break;
1066
+ /* fallthrough */
1067
+ case MUSTACHE_SECTION_GOTO:
1068
+ /* fallthrough */
1069
+ case MUSTACHE_SECTION_START:
1070
+ case MUSTACHE_SECTION_START_INV:
1071
+ /* advance stack*/
1072
+ if (s.index + 1 >= MUSTACHE_NESTING_LIMIT) {
1073
+ if (args.err)
1074
+ *args.err = MUSTACHE_ERR_TOO_DEEP;
1075
+ goto error;
1076
+ }
1077
+ s.stack[s.index + 1].sec = s.stack[s.index].sec;
1078
+ ++s.index;
1079
+ s.stack[s.index].start =
1080
+ (instructions[s.pos].instruction == MUSTACHE_SECTION_GOTO
1081
+ ? instructions[s.pos].data.len
1082
+ : s.pos);
1083
+ s.stack[s.index].end = instructions[s.pos].data.end;
1084
+ s.stack[s.index].frame = s.index;
1085
+ s.stack[s.index].index = 0;
1086
+ s.stack[s.index].count = 1;
1087
+
1088
+ /* test section count */
1089
+ if (instructions[s.pos].data.name_pos) {
1090
+ /* this is a named section, it should be tested against user data */
1091
+ int32_t val = mustache_on_section_test(
1092
+ &s.stack[s.index].sec, data + instructions[s.pos].data.name_pos,
1093
+ instructions[s.pos].data.name_len,
1094
+ instructions[s.pos].instruction == MUSTACHE_SECTION_START);
1095
+ if (val == -1) {
1096
+ goto user_error;
1097
+ }
1098
+ if (instructions[s.pos].instruction == MUSTACHE_SECTION_START_INV) {
1099
+ /* invert test */
1100
+ val = (val == 0);
1101
+ }
1102
+ s.stack[s.index].count = (uint32_t)val;
1103
+ }
1104
+ /* fallthrough */
1105
+ case MUSTACHE_SECTION_END:
1106
+ /* loop section or continue */
1107
+ if (s.stack[s.index].index < s.stack[s.index].count) {
1108
+ /* repeat / start section */
1109
+ s.pos = s.stack[s.index].start;
1110
+ s.stack[s.index].sec = s.stack[s.index - 1].sec;
1111
+ /* review user callback (if it's a named section) */
1112
+ if (instructions[s.pos].data.name_pos &&
1113
+ mustache_on_section_start(&s.stack[s.index].sec,
1114
+ data + instructions[s.pos].data.name_pos,
1115
+ instructions[s.pos].data.name_len,
1116
+ s.stack[s.index].index) == -1)
1117
+ goto user_error;
1118
+ /* skip padding instructions in GOTO tags (recursive partials) */
1119
+ if (instructions[s.pos].instruction == MUSTACHE_SECTION_GOTO)
1120
+ ++s.pos;
1121
+ ++s.stack[s.index].index;
1122
+ break;
1123
+ }
1124
+ s.pos = s.stack[s.index].end;
1125
+ --s.index;
1126
+ break;
1127
+ case MUSTACHE_PADDING_PUSH:
1128
+ s.padding = s.pos;
1129
+ break;
1130
+ case MUSTACHE_PADDING_POP:
1131
+ s.padding = instructions[s.padding].data.end;
1132
+ break;
1133
+ case MUSTACHE_PADDING_WRITE:
1134
+ for (uint32_t i = s.padding; i; i = instructions[i].data.end) {
1135
+ if (mustache_on_text(&s.stack[s.index].sec,
1136
+ data + instructions[i].data.name_pos,
1137
+ instructions[i].data.name_len))
1138
+ goto user_error;
1139
+ }
1140
+ break;
1141
+ default:
1142
+ /* not a valid engine */
1143
+ fprintf(stderr, "ERROR: invalid mustache instruction set detected (wrong "
1144
+ "`mustache_s`?)\n");
1145
+ if (args.err) {
1146
+ *args.err = MUSTACHE_ERR_UNKNOWN;
1147
+ }
1148
+ goto error;
1149
+ }
1150
+ ++s.pos;
1151
+ }
1152
+ *args.err = MUSTACHE_OK;
1153
+ return 0;
1154
+ user_error:
1155
+ *args.err = MUSTACHE_ERR_USER_ERROR;
1156
+ error:
1157
+ mustache_on_formatting_error(args.udata1, args.udata2);
1158
+ return -1;
1159
+ }
1160
+
1161
+ /* *****************************************************************************
1162
+ Building the instrustion list (parsing the template)
1163
+ ***************************************************************************** */
1164
+
1165
+ /* The parsing implementation, converts a template to an instruction array */
1166
+ MUSTACHE_FUNC mustache_s *(mustache_load)(mustache_load_args_s args) {
1167
+ mustache_error_en err_if_missing;
1168
+ mustache__loader_stack_s s;
1169
+ uint8_t flag = 0;
1170
+
1171
+ if (!args.err)
1172
+ args.err = &err_if_missing;
1173
+ s.path_capa = 0;
1174
+ s.path = NULL;
1175
+ s.data = NULL;
1176
+ s.data_len = 0;
1177
+ s.i = NULL;
1178
+ s.i_capa = 32;
1179
+ s.index = 0;
1180
+ s.padding = 0;
1181
+ s.m = malloc(sizeof(*s.m) + (sizeof(*s.i) * 32));
1182
+ MUSTACHE_ASSERT(s.m, "failed to allocate memory for mustache data");
1183
+ s.m->u.read_only_pt = 0;
1184
+ s.m->u.read_only.data_length = 0;
1185
+ s.m->u.read_only.intruction_count = 0;
1186
+ s.i = MUSTACH2INSTRUCTIONS(s.m);
1187
+ s.err = args.err;
1188
+
1189
+ if (!args.filename_len && args.filename)
1190
+ args.filename_len = strlen(args.filename);
1191
+
1192
+ if (args.data) {
1193
+ if (mustache__load_data(&s, args.filename, args.filename_len, args.data,
1194
+ args.data_len) == -1) {
1195
+ goto error;
1196
+ }
1197
+ } else {
1198
+ if (mustache__load_file(&s, args.filename, args.filename_len) == -1) {
1199
+ goto error;
1200
+ }
1201
+ }
1202
+
1203
+ /* loop while there are templates to be parsed on the stack */
1204
+ while (s.index) {
1205
+ /* parsing loop */
1206
+ while (s.stack[s.index].data_pos < s.stack[s.index].data_end) {
1207
+ /* stand-alone tag flag, also containes padding length after bit 1 */
1208
+ uint32_t stand_alone = 0;
1209
+ uint32_t stand_alone_pos = 0;
1210
+ /* start parsing at current position */
1211
+ const char *start = s.data + s.stack[s.index].data_pos;
1212
+ /* find the next instruction (beg == beginning) */
1213
+ char *beg = strstr(start, s.stack[s.index].del_start);
1214
+ const char *org_beg = beg;
1215
+ if (!beg || beg >= s.data + s.stack[s.index].data_end) {
1216
+ /* no instructions left, only text */
1217
+ mustache__push_text_instruction(&s, s.stack[s.index].data_pos,
1218
+ s.stack[s.index].data_end -
1219
+ s.stack[s.index].data_pos);
1220
+ s.stack[s.index].data_pos = s.stack[s.index].data_end;
1221
+ continue;
1222
+ }
1223
+ if (beg != start) {
1224
+ /* there's text before the instruction */
1225
+ mustache__push_text_instruction(&s, s.stack[s.index].data_pos,
1226
+ (uint32_t)(uintptr_t)(beg - start));
1227
+ }
1228
+ /* move beg (reading position) after the delimiter */
1229
+ beg += s.stack[s.index].del_start_len;
1230
+ /* seek the end of the instruction delimiter */
1231
+ char *end = strstr(beg, s.stack[s.index].del_end);
1232
+ if (!end || end >= s.data + s.stack[s.index].data_end) {
1233
+ /* delimiter not closed */
1234
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1235
+ goto error;
1236
+ }
1237
+
1238
+ /* update reading position in the stack */
1239
+ s.stack[s.index].data_pos = (end - s.data) + s.stack[s.index].del_end_len;
1240
+
1241
+ /* Test for stand-alone tags */
1242
+ if (!end[s.stack[s.index].del_end_len] ||
1243
+ end[s.stack[s.index].del_end_len] == '\n' ||
1244
+ (end[s.stack[s.index].del_end_len] == '\r' &&
1245
+ end[1 + s.stack[s.index].del_end_len] == '\n')) {
1246
+ char *pad = beg - (s.stack[s.index].del_start_len + 1);
1247
+ while (pad >= start && (pad[0] == ' ' || pad[0] == '\t'))
1248
+ --pad;
1249
+ if (pad[0] == '\n' || pad[0] == 0) {
1250
+ /* Yes, this a stand-alone tag, store padding length + flag */
1251
+ ++pad;
1252
+ stand_alone_pos = pad - s.data;
1253
+ stand_alone =
1254
+ ((beg - (pad + s.stack[s.index].del_start_len)) << 1) | 1;
1255
+ }
1256
+ }
1257
+
1258
+ /* parse instruction content */
1259
+ flag = 1;
1260
+
1261
+ switch (beg[0]) {
1262
+ case '!':
1263
+ /* comment, do nothing... almost */
1264
+ mustache__stand_alone_adjust(&s, stand_alone);
1265
+ break;
1266
+
1267
+ case '=':
1268
+ /* define new seperators */
1269
+ mustache__stand_alone_adjust(&s, stand_alone);
1270
+ ++beg;
1271
+ --end;
1272
+ if (end[0] != '=') {
1273
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1274
+ goto error;
1275
+ }
1276
+ --end;
1277
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1278
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1279
+ ++end;
1280
+ {
1281
+ char *div = beg;
1282
+ while (div < end && !isspace(*div)) {
1283
+ ++div;
1284
+ }
1285
+ if (div == end || div == beg) {
1286
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1287
+ goto error;
1288
+ }
1289
+ if (div - beg >= MUSTACHE_DELIMITER_LENGTH_LIMIT) {
1290
+ *args.err = MUSTACHE_ERR_DELIMITER_TOO_LONG;
1291
+ goto error;
1292
+ }
1293
+ /* copy starting delimiter */
1294
+ s.stack[s.index].del_start_len = div - beg;
1295
+ for (size_t i = 0; i < s.stack[s.index].del_start_len; ++i) {
1296
+ s.stack[s.index].del_start[i] = beg[i];
1297
+ }
1298
+ s.stack[s.index].del_start[s.stack[s.index].del_start_len] = 0;
1299
+
1300
+ ++div;
1301
+ MUSTACHE_IGNORE_WHITESPACE(div, 1);
1302
+ if (div == end) {
1303
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1304
+ goto error;
1305
+ }
1306
+ if (end - div >= MUSTACHE_DELIMITER_LENGTH_LIMIT) {
1307
+ *args.err = MUSTACHE_ERR_DELIMITER_TOO_LONG;
1308
+ goto error;
1309
+ }
1310
+ /* copy ending delimiter */
1311
+ s.stack[s.index].del_end_len = end - div;
1312
+ for (size_t i = 0; i < s.stack[s.index].del_end_len; ++i) {
1313
+ s.stack[s.index].del_end[i] = div[i];
1314
+ }
1315
+ s.stack[s.index].del_end[s.stack[s.index].del_end_len] = 0;
1316
+ }
1317
+ break;
1318
+
1319
+ case '^':
1320
+ /* start inverted section */
1321
+ flag = 0;
1322
+ /* fallthrough */
1323
+ case '#':
1324
+ /* start section (or inverted section) */
1325
+ mustache__stand_alone_adjust(&s, stand_alone);
1326
+ ++beg;
1327
+ --end;
1328
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1329
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1330
+ ++end;
1331
+
1332
+ ++s.stack[s.index].open_sections;
1333
+ if (s.stack[s.index].open_sections >= MUSTACHE_NESTING_LIMIT) {
1334
+ *args.err = MUSTACHE_ERR_TOO_DEEP;
1335
+ goto error;
1336
+ }
1337
+ if (beg - s.data >= UINT16_MAX) {
1338
+ *args.err = MUSTACHE_ERR_NAME_TOO_LONG;
1339
+ }
1340
+ if (mustache__instruction_push(
1341
+ &s,
1342
+ (mustache__instruction_s){
1343
+ .instruction = (flag ? MUSTACHE_SECTION_START
1344
+ : MUSTACHE_SECTION_START_INV),
1345
+ .data = {
1346
+ .name_pos = beg - s.data,
1347
+ .name_len = end - beg,
1348
+ .offset = s.stack[s.index].data_pos - (beg - s.data),
1349
+ }})) {
1350
+ goto error;
1351
+ }
1352
+ break;
1353
+
1354
+ case '>':
1355
+ /* partial template - search data for loaded template or load new */
1356
+ mustache__stand_alone_adjust(&s, stand_alone);
1357
+ if ((stand_alone >> 1)) {
1358
+ /* TODO: add padding markers */
1359
+ if (mustache__instruction_push(
1360
+ &s, (mustache__instruction_s){
1361
+ .instruction = MUSTACHE_PADDING_PUSH,
1362
+ .data = {
1363
+ .name_pos = stand_alone_pos,
1364
+ .name_len = (stand_alone >> 1),
1365
+ .end = s.padding,
1366
+ }})) {
1367
+ goto error;
1368
+ }
1369
+ s.padding = s.m->u.read_only.intruction_count - 1;
1370
+ }
1371
+ ++beg;
1372
+ --end;
1373
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1374
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1375
+ ++end;
1376
+ ssize_t loaded = mustache__load_file(&s, beg, end - beg);
1377
+ if (loaded == -1)
1378
+ goto error;
1379
+ /* Add latest padding section to text */
1380
+ if ((stand_alone >> 1)) {
1381
+ if (loaded)
1382
+ mustache__instruction_push(
1383
+ &s, (mustache__instruction_s){
1384
+ .instruction = MUSTACHE_WRITE_TEXT,
1385
+ .data =
1386
+ {
1387
+ .name_pos = stand_alone_pos,
1388
+ .name_len = (stand_alone >> 1),
1389
+ },
1390
+ });
1391
+ else if (mustache__instruction_push(
1392
+ &s, (mustache__instruction_s){.instruction =
1393
+ MUSTACHE_PADDING_POP}))
1394
+ goto error;
1395
+ }
1396
+ break;
1397
+
1398
+ case '/':
1399
+ /* section end */
1400
+ mustache__stand_alone_adjust(&s, stand_alone);
1401
+ ++beg;
1402
+ --end;
1403
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1404
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1405
+ ++end;
1406
+ if (!s.stack[s.index].open_sections) {
1407
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1408
+ goto error;
1409
+ } else {
1410
+ uint32_t pos = s.m->u.read_only.intruction_count;
1411
+ uint32_t nested = 0;
1412
+ do {
1413
+ --pos;
1414
+ if (s.i[pos].instruction == MUSTACHE_SECTION_END)
1415
+ ++nested;
1416
+ else if (s.i[pos].instruction == MUSTACHE_SECTION_START ||
1417
+ s.i[pos].instruction == MUSTACHE_SECTION_START_INV) {
1418
+ if (nested) {
1419
+ --nested;
1420
+ } else {
1421
+ /* test instruction closure */
1422
+ if (s.i[pos].data.name_len != end - beg ||
1423
+ memcmp(beg, s.data + s.i[pos].data.name_pos,
1424
+ s.i[pos].data.name_len)) {
1425
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1426
+ goto error;
1427
+ }
1428
+ /* update initial instruction (do this before adding closure) */
1429
+ s.i[pos].data.end = s.m->u.read_only.intruction_count;
1430
+ s.i[pos].data.len = org_beg - (s.data + s.i[pos].data.name_pos +
1431
+ s.i[pos].data.offset);
1432
+ /* add closure instruction */
1433
+ mustache__instruction_push(
1434
+ &s, (mustache__instruction_s){.instruction =
1435
+ MUSTACHE_SECTION_END,
1436
+ .data = s.i[pos].data});
1437
+ /* update closure count */
1438
+ --s.stack[s.index].open_sections;
1439
+ /* stop loop */
1440
+ pos = 0;
1441
+ beg = NULL;
1442
+ }
1443
+ }
1444
+ } while (pos);
1445
+ if (beg) {
1446
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1447
+ goto error;
1448
+ }
1449
+ }
1450
+ break;
1451
+
1452
+ case '{':
1453
+ /* step the read position forward if the ending was '}}}' */
1454
+ if (s.data[s.stack[s.index].data_pos] == '}' &&
1455
+ s.stack[s.index].del_end[0] == '}' &&
1456
+ s.stack[s.index].del_end[s.stack[s.index].del_end_len - 1] == '}') {
1457
+ ++s.stack[s.index].data_pos;
1458
+ }
1459
+ /* fallthrough */
1460
+ case '&':
1461
+ /* unescaped variable data */
1462
+ flag = 0;
1463
+ /* fallthrough */
1464
+ case ':': /*fallthrough*/
1465
+ case '<': /*fallthrough*/
1466
+ ++beg; /*fallthrough*/
1467
+ default:
1468
+ --end;
1469
+ MUSTACHE_IGNORE_WHITESPACE(beg, 1);
1470
+ MUSTACHE_IGNORE_WHITESPACE(end, -1);
1471
+ ++end;
1472
+ mustache__instruction_push(
1473
+ &s, (mustache__instruction_s){
1474
+ .instruction = (flag ? MUSTACHE_WRITE_ARG
1475
+ : MUSTACHE_WRITE_ARG_UNESCAPED),
1476
+ .data = {.name_pos = beg - s.data, .name_len = end - beg}});
1477
+ break;
1478
+ }
1479
+ }
1480
+ /* make sure all sections were closed */
1481
+ if (s.stack[s.index].open_sections) {
1482
+ *args.err = MUSTACHE_ERR_CLOSURE_MISMATCH;
1483
+ goto error;
1484
+ }
1485
+ /* move padding from section tail to post closure (adjust for padding
1486
+ * changes) */
1487
+ flag = 0;
1488
+ if (s.m->u.read_only.intruction_count &&
1489
+ s.i[s.m->u.read_only.intruction_count - 1].instruction ==
1490
+ MUSTACHE_PADDING_WRITE) {
1491
+ --s.m->u.read_only.intruction_count;
1492
+ flag = 1;
1493
+ }
1494
+ /* mark section length */
1495
+ mustache__data_segment_s seg = mustache__data_segment_read(
1496
+ (uint8_t *)s.data + s.stack[s.index].data_start);
1497
+ s.i[seg.inst_start].data.end = s.m->u.read_only.intruction_count;
1498
+ /* add instruction closure */
1499
+ mustache__instruction_push(
1500
+ &s, (mustache__instruction_s){.instruction = MUSTACHE_SECTION_END});
1501
+ /* TODO: pop any padding (if exists) */
1502
+ if (s.padding && s.padding + 1 == seg.inst_start) {
1503
+ s.padding = s.i[s.padding].data.end;
1504
+ mustache__instruction_push(&s, (mustache__instruction_s){
1505
+ .instruction = MUSTACHE_PADDING_POP,
1506
+ });
1507
+ }
1508
+ /* complete padding switch*/
1509
+ if (flag) {
1510
+ mustache__instruction_push(&s, (mustache__instruction_s){
1511
+ .instruction = MUSTACHE_PADDING_WRITE,
1512
+ });
1513
+ flag = 0;
1514
+ }
1515
+ /* pop stack */
1516
+ --s.index;
1517
+ }
1518
+
1519
+ s.m = realloc(s.m, sizeof(*s.m) +
1520
+ (sizeof(*s.i) * s.m->u.read_only.intruction_count) +
1521
+ s.data_len);
1522
+ MUSTACHE_ASSERT(s.m,
1523
+ "failed to allocate memory for consolidated mustache data");
1524
+ memcpy(MUSTACH2DATA(s.m), s.data, s.data_len);
1525
+ free(s.data);
1526
+ free(s.path);
1527
+
1528
+ *args.err = MUSTACHE_OK;
1529
+ return s.m;
1530
+
1531
+ error:
1532
+ free(s.data);
1533
+ free(s.path);
1534
+ free(s.m);
1535
+ return NULL;
1536
+ }
1537
+
1538
+ #endif /* INCLUDE_MUSTACHE_IMPLEMENTATION */
1539
+
1540
+ #undef MUSTACHE_FUNC
1541
+ #undef MUSTACH2INSTRUCTIONS
1542
+ #undef MUSTACH2DATA
1543
+ #undef MUSTACHE_OBJECT_OFFSET
1544
+ #undef MUSTACHE_IGNORE_WHITESPACE
1545
+
1546
+ #endif /* H_MUSTACHE_LOADR_H */