iodine 0.7.60 → 0.8.0
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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
|
@@ -1,1546 +0,0 @@
|
|
|
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] = "<"
|
|
513
|
-
a['>'.ord] = ">"
|
|
514
|
-
a['&'.ord] = "&"
|
|
515
|
-
a['"'.ord] = """
|
|
516
|
-
a["\'".ord] = "'"
|
|
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
|
-
"�", "", "", "", "", "", "", "",
|
|
527
|
-
"", "	", " ", "", "", " ", "", "",
|
|
528
|
-
"", "", "", "", "", "", "", "",
|
|
529
|
-
"", "", "", "", "", "", "", "",
|
|
530
|
-
" ", "!", """, "#", "$", "%", "&", "'",
|
|
531
|
-
"(", ")", "*", "+", ",", "-", ".", "/",
|
|
532
|
-
"0", "1", "2", "3", "4", "5", "6", "7",
|
|
533
|
-
"8", "9", ":", ";", "<", "=", ">", "?",
|
|
534
|
-
"@", "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", "[", "\", "]", "^", "_",
|
|
538
|
-
"`", "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", "{", "|", "}", "~", "\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 */
|