rmultimarkdown 4.7.1.1 → 6.2.2.1
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/LICENSE +2 -2
- data/README.md +7 -9
- data/Rakefile +33 -18
- data/ext/Makefile +261 -0
- data/ext/extconf.rb +23 -3
- data/ext/mmd/aho-corasick.c +596 -0
- data/ext/mmd/aho-corasick.h +120 -0
- data/ext/mmd/beamer.c +344 -0
- data/ext/mmd/beamer.h +72 -0
- data/ext/mmd/char.c +156 -0
- data/ext/mmd/char.h +111 -0
- data/ext/mmd/char_lookup.c +212 -0
- data/ext/mmd/critic_markup.c +330 -0
- data/ext/mmd/critic_markup.h +94 -0
- data/ext/mmd/d_string.c +402 -0
- data/ext/mmd/epub.c +563 -0
- data/ext/mmd/epub.h +69 -0
- data/ext/mmd/fodt.c +2288 -0
- data/ext/mmd/fodt.h +81 -0
- data/ext/mmd/html.c +2460 -0
- data/ext/mmd/html.h +81 -0
- data/ext/mmd/i18n.h +170 -0
- data/ext/mmd/include/d_string.h +182 -0
- data/ext/mmd/include/libMultiMarkdown.h +548 -0
- data/ext/mmd/include/token.h +233 -0
- data/ext/mmd/latex.c +2435 -0
- data/ext/mmd/latex.h +83 -0
- data/ext/mmd/lexer.c +3001 -0
- data/ext/mmd/lexer.h +75 -0
- data/ext/mmd/memoir.c +138 -0
- data/ext/mmd/memoir.h +67 -0
- data/ext/mmd/miniz.c +7557 -0
- data/ext/mmd/miniz.h +1328 -0
- data/ext/mmd/mmd.c +2798 -0
- data/ext/mmd/mmd.h +120 -0
- data/ext/mmd/object_pool.c +141 -0
- data/ext/mmd/object_pool.h +101 -0
- data/ext/mmd/opendocument-content.c +2071 -0
- data/ext/mmd/opendocument-content.h +135 -0
- data/ext/mmd/opendocument.c +981 -0
- data/ext/mmd/opendocument.h +118 -0
- data/ext/mmd/parser.c +1760 -0
- data/ext/mmd/parser.h +39 -0
- data/{MultiMarkdown-4 → ext/mmd}/rng.c +90 -49
- data/ext/mmd/scanners.c +77512 -0
- data/ext/mmd/scanners.h +101 -0
- data/ext/mmd/stack.c +142 -0
- data/ext/mmd/stack.h +113 -0
- data/ext/mmd/textbundle.c +455 -0
- data/ext/mmd/textbundle.h +115 -0
- data/ext/mmd/token.c +773 -0
- data/ext/mmd/token_pairs.c +263 -0
- data/ext/mmd/token_pairs.h +123 -0
- data/ext/mmd/transclude.c +549 -0
- data/ext/mmd/transclude.h +87 -0
- data/ext/mmd/uthash.h +1074 -0
- data/ext/mmd/uuid.c +154 -0
- data/ext/mmd/uuid.h +77 -0
- data/ext/mmd/version.h +111 -0
- data/ext/mmd/writer.c +2652 -0
- data/ext/mmd/writer.h +260 -0
- data/ext/mmd/zip.c +210 -0
- data/ext/mmd/zip.h +120 -0
- data/ext/{multi_markdown.c → ruby_multi_markdown.c} +87 -18
- data/lib/multi_markdown.bundle +0 -0
- data/lib/multi_markdown.rb +5 -8
- data/lib/multi_markdown/version.rb +1 -1
- data/rmultimarkdown.gemspec +2 -2
- data/test/{extensions_test.rb.rb → extensions_test.rb} +10 -54
- data/test/multi_markdown_test.rb +13 -0
- metadata +67 -47
- data/MultiMarkdown-4/GLibFacade.c +0 -310
- data/MultiMarkdown-4/GLibFacade.h +0 -100
- data/MultiMarkdown-4/beamer.c +0 -182
- data/MultiMarkdown-4/beamer.h +0 -11
- data/MultiMarkdown-4/critic.c +0 -111
- data/MultiMarkdown-4/critic.h +0 -15
- data/MultiMarkdown-4/glib.h +0 -11
- data/MultiMarkdown-4/html.c +0 -1117
- data/MultiMarkdown-4/html.h +0 -14
- data/MultiMarkdown-4/latex.c +0 -1217
- data/MultiMarkdown-4/latex.h +0 -16
- data/MultiMarkdown-4/libMultiMarkdown.h +0 -177
- data/MultiMarkdown-4/lyx.c +0 -2265
- data/MultiMarkdown-4/lyx.h +0 -37
- data/MultiMarkdown-4/lyxbeamer.c +0 -265
- data/MultiMarkdown-4/lyxbeamer.h +0 -11
- data/MultiMarkdown-4/memoir.c +0 -80
- data/MultiMarkdown-4/memoir.h +0 -10
- data/MultiMarkdown-4/multimarkdown.c +0 -518
- data/MultiMarkdown-4/odf.c +0 -1222
- data/MultiMarkdown-4/odf.h +0 -18
- data/MultiMarkdown-4/opml.c +0 -189
- data/MultiMarkdown-4/opml.h +0 -15
- data/MultiMarkdown-4/parse_utilities.c +0 -884
- data/MultiMarkdown-4/parser.c +0 -16656
- data/MultiMarkdown-4/parser.h +0 -188
- data/MultiMarkdown-4/rtf.c +0 -665
- data/MultiMarkdown-4/rtf.h +0 -17
- data/MultiMarkdown-4/strtok.c +0 -56
- data/MultiMarkdown-4/strtok.h +0 -9
- data/MultiMarkdown-4/text.c +0 -53
- data/MultiMarkdown-4/text.h +0 -11
- data/MultiMarkdown-4/toc.c +0 -142
- data/MultiMarkdown-4/toc.h +0 -15
- data/MultiMarkdown-4/transclude.c +0 -307
- data/MultiMarkdown-4/transclude.h +0 -28
- data/MultiMarkdown-4/writer.c +0 -731
- data/MultiMarkdown-4/writer.h +0 -38
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
|
|
3
|
+
MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
|
|
4
|
+
|
|
5
|
+
@file token_pairs.c
|
|
6
|
+
|
|
7
|
+
@brief Allow for pairing certain tokens together (e.g. '[' and ']') to create
|
|
8
|
+
more meaningful token trees.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@author Fletcher T. Penney
|
|
12
|
+
@bug
|
|
13
|
+
|
|
14
|
+
**/
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
|
|
18
|
+
Copyright © 2016 - 2017 Fletcher T. Penney.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
The `MultiMarkdown 6` project is released under the MIT License..
|
|
22
|
+
|
|
23
|
+
GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
|
|
24
|
+
|
|
25
|
+
https://github.com/fletcher/MultiMarkdown-4/
|
|
26
|
+
|
|
27
|
+
MMD 4 is released under both the MIT License and GPL.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
CuTest is released under the zlib/libpng license. See CuTest.c for the text
|
|
31
|
+
of the license.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## The MIT License ##
|
|
35
|
+
|
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
37
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
38
|
+
in the Software without restriction, including without limitation the rights
|
|
39
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
40
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
41
|
+
furnished to do so, subject to the following conditions:
|
|
42
|
+
|
|
43
|
+
The above copyright notice and this permission notice shall be included in
|
|
44
|
+
all copies or substantial portions of the Software.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
47
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
48
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
49
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
50
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
51
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
52
|
+
THE SOFTWARE.
|
|
53
|
+
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
#include <stdbool.h>
|
|
57
|
+
#include <stdio.h>
|
|
58
|
+
#include <stdlib.h>
|
|
59
|
+
#include <string.h>
|
|
60
|
+
|
|
61
|
+
#include "stack.h"
|
|
62
|
+
#include "token.h"
|
|
63
|
+
#include "token_pairs.h"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/// Create a new token pair engine
|
|
67
|
+
token_pair_engine * token_pair_engine_new(void) {
|
|
68
|
+
token_pair_engine * e = malloc(sizeof(token_pair_engine));
|
|
69
|
+
|
|
70
|
+
if (e) {
|
|
71
|
+
unsigned short empty[kMaxTokenTypes] = {0};
|
|
72
|
+
unsigned short empty2[kMaxTokenTypes][kMaxTokenTypes] = {{0}};
|
|
73
|
+
|
|
74
|
+
memcpy(e->can_open_pair, empty, sizeof(unsigned short) * kMaxTokenTypes);
|
|
75
|
+
memcpy(e->can_close_pair, empty, sizeof(unsigned short) * kMaxTokenTypes);
|
|
76
|
+
|
|
77
|
+
memcpy(e->pair_type, empty2, sizeof(unsigned short) * kMaxTokenTypes * kMaxTokenTypes);
|
|
78
|
+
|
|
79
|
+
memcpy(e->empty_allowed, empty, sizeof(unsigned short) * kMaxTokenTypes);
|
|
80
|
+
memcpy(e->match_len, empty, sizeof(unsigned short) * kMaxTokenTypes);
|
|
81
|
+
memcpy(e->should_prune, empty, sizeof(unsigned short) * kMaxTokenTypes);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return e;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/// Free existing token pair engine
|
|
89
|
+
void token_pair_engine_free(token_pair_engine * e) {
|
|
90
|
+
if (e == NULL) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
free(e);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/// Add a new pairing configuration to a token pair engine
|
|
99
|
+
void token_pair_engine_add_pairing(token_pair_engine * e, unsigned short open_type, unsigned short close_type,
|
|
100
|
+
unsigned short pair_type, int options) {
|
|
101
|
+
// \todo: This needs to be more sophisticated
|
|
102
|
+
e->can_open_pair[open_type] = 1;
|
|
103
|
+
e->can_close_pair[close_type] = 1;
|
|
104
|
+
(e->pair_type)[open_type][close_type] = pair_type;
|
|
105
|
+
|
|
106
|
+
if (options & PAIRING_ALLOW_EMPTY) {
|
|
107
|
+
e->empty_allowed[pair_type] = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (options & PAIRING_MATCH_LENGTH) {
|
|
111
|
+
e->match_len[pair_type] = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (options & PAIRING_PRUNE_MATCH) {
|
|
115
|
+
e->should_prune[pair_type] = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
/// Mate opener and closer together
|
|
122
|
+
void token_pair_mate(token * a, token * b) {
|
|
123
|
+
if (a == NULL | b == NULL) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
a->mate = b;
|
|
128
|
+
a->unmatched = false;
|
|
129
|
+
|
|
130
|
+
b->mate = a;
|
|
131
|
+
b->unmatched = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
/// Search a token's childen for matching pairs
|
|
136
|
+
void token_pairs_match_pairs_inside_token(token * parent, token_pair_engine * e, stack * s, unsigned short depth) {
|
|
137
|
+
|
|
138
|
+
// Avoid stack overflow in "pathologic" input
|
|
139
|
+
if (depth == kMaxPairRecursiveDepth) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Walk the child chain
|
|
144
|
+
token * walker = parent->child;
|
|
145
|
+
|
|
146
|
+
// Counter
|
|
147
|
+
size_t start_counter = s->size;
|
|
148
|
+
size_t i; // We're sharing one stack, so any opener earlier than this belongs to a parent
|
|
149
|
+
|
|
150
|
+
token * peek;
|
|
151
|
+
unsigned short pair_type;
|
|
152
|
+
|
|
153
|
+
unsigned int opener_count[kMaxTokenTypes] = {0}; // Keep track of which token types are on the stack
|
|
154
|
+
|
|
155
|
+
while (walker != NULL) {
|
|
156
|
+
|
|
157
|
+
if (walker->child) {
|
|
158
|
+
token_pairs_match_pairs_inside_token(walker, e, s, depth + 1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Is this a closer?
|
|
162
|
+
if (walker->can_close && e->can_close_pair[walker->type] && walker->unmatched ) {
|
|
163
|
+
i = s->size;
|
|
164
|
+
|
|
165
|
+
// Do we even have a valid opener in the stack?
|
|
166
|
+
// It's only worth checking if the stack is beyond a certain size
|
|
167
|
+
if (i > start_counter + kLargeStackThreshold) {
|
|
168
|
+
for (int j = 0; j < kMaxTokenTypes; ++j) {
|
|
169
|
+
if (opener_count[j]) {
|
|
170
|
+
if (e->pair_type[j][walker->type]) {
|
|
171
|
+
goto close;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// No opener available for this as closer
|
|
177
|
+
goto open;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
close:
|
|
181
|
+
|
|
182
|
+
// Find matching opener for this closer
|
|
183
|
+
while (i > start_counter) {
|
|
184
|
+
peek = stack_peek_index(s, i - 1);
|
|
185
|
+
|
|
186
|
+
pair_type = e->pair_type[peek->type][walker->type];
|
|
187
|
+
|
|
188
|
+
if (pair_type) {
|
|
189
|
+
if (!e->empty_allowed[pair_type]) {
|
|
190
|
+
// Make sure they aren't consecutive tokens
|
|
191
|
+
if ((peek->next == walker) &&
|
|
192
|
+
(peek->start + peek->len == walker->start)) {
|
|
193
|
+
// i--;
|
|
194
|
+
i = start_counter; // In this situation, we can't use this token as a closer
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (e->match_len[pair_type]) {
|
|
200
|
+
// Lengths must match
|
|
201
|
+
if (peek->len != walker->len) {
|
|
202
|
+
i--;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
token_pair_mate(peek, walker);
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
// Clear portion of stack between opener and closer as they are now unavailable for mating
|
|
211
|
+
while (s->size > (i - 1)) {
|
|
212
|
+
peek = stack_pop(s);
|
|
213
|
+
opener_count[peek->type]--;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#ifndef NDEBUG
|
|
217
|
+
fprintf(stderr, "stack now sized %lu\n", s->size);
|
|
218
|
+
#endif
|
|
219
|
+
// Prune matched section
|
|
220
|
+
|
|
221
|
+
if (e->should_prune[pair_type]) {
|
|
222
|
+
if (peek->prev == NULL) {
|
|
223
|
+
walker = token_prune_graft(peek, walker, e->pair_type[peek->type][walker->type]);
|
|
224
|
+
parent->child = walker;
|
|
225
|
+
} else {
|
|
226
|
+
walker = token_prune_graft(peek, walker, e->pair_type[peek->type][walker->type]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#ifndef NDEBUG
|
|
234
|
+
else {
|
|
235
|
+
fprintf(stderr, "token type %d failed to match stack element\n", walker->type);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#endif
|
|
239
|
+
i--;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
open:
|
|
244
|
+
|
|
245
|
+
// Is this an opener?
|
|
246
|
+
if (walker->can_open && e->can_open_pair[walker->type] && walker->unmatched) {
|
|
247
|
+
stack_push(s, walker);
|
|
248
|
+
opener_count[walker->type]++;
|
|
249
|
+
#ifndef NDEBUG
|
|
250
|
+
fprintf(stderr, "push token type %d to stack (%lu elements)\n", walker->type, s->size);
|
|
251
|
+
#endif
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
walker = walker->next;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#ifndef NDEBUG
|
|
258
|
+
fprintf(stderr, "token stack has %lu elements (of %lu)\n", s->size, s->capacity);
|
|
259
|
+
#endif
|
|
260
|
+
|
|
261
|
+
// Remove unused tokens from stack and return to parent
|
|
262
|
+
s->size = start_counter;
|
|
263
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
|
|
3
|
+
MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
|
|
4
|
+
|
|
5
|
+
@file token_pairs.h
|
|
6
|
+
|
|
7
|
+
@brief Allow for pairing certain tokens together (e.g. '[' and ']') to create
|
|
8
|
+
more meaningful token trees.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@author Fletcher T. Penney
|
|
12
|
+
@bug
|
|
13
|
+
|
|
14
|
+
**/
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
|
|
18
|
+
Copyright © 2016 - 2017 Fletcher T. Penney.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
The `MultiMarkdown 6` project is released under the MIT License..
|
|
22
|
+
|
|
23
|
+
GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
|
|
24
|
+
|
|
25
|
+
https://github.com/fletcher/MultiMarkdown-4/
|
|
26
|
+
|
|
27
|
+
MMD 4 is released under both the MIT License and GPL.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
CuTest is released under the zlib/libpng license. See CuTest.c for the text
|
|
31
|
+
of the license.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## The MIT License ##
|
|
35
|
+
|
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
37
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
38
|
+
in the Software without restriction, including without limitation the rights
|
|
39
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
40
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
41
|
+
furnished to do so, subject to the following conditions:
|
|
42
|
+
|
|
43
|
+
The above copyright notice and this permission notice shall be included in
|
|
44
|
+
all copies or substantial portions of the Software.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
47
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
48
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
49
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
50
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
51
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
52
|
+
THE SOFTWARE.
|
|
53
|
+
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#ifndef TOKEN_PAIRS_MULTIMARKDOWN_H
|
|
58
|
+
#define TOKEN_PAIRS_MULTIMARKDOWN_H
|
|
59
|
+
|
|
60
|
+
#include "stack.h"
|
|
61
|
+
#include "token.h"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
#ifdef TEST
|
|
65
|
+
#include "CuTest.h"
|
|
66
|
+
#endif
|
|
67
|
+
|
|
68
|
+
#define kMaxTokenTypes 230 //!< This needs to be larger than the largest token type being used
|
|
69
|
+
#define kLargeStackThreshold 1000 //!< Avoid unnecessary searches of large stacks
|
|
70
|
+
#define kMaxPairRecursiveDepth 1000 //!< Maximum recursion depth to traverse when pairing tokens -- to prevent stack overflow with "pathologic" input
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/// Store information about which tokens can be paired, and what actions to take when
|
|
74
|
+
/// pairing them.
|
|
75
|
+
struct token_pair_engine {
|
|
76
|
+
unsigned short can_open_pair[kMaxTokenTypes]; //!< Can token type open a pair?
|
|
77
|
+
unsigned short can_close_pair[kMaxTokenTypes]; //!< Can token type close a pair?
|
|
78
|
+
|
|
79
|
+
unsigned short pair_type[kMaxTokenTypes][kMaxTokenTypes]; //!< Which pair are we forming?
|
|
80
|
+
|
|
81
|
+
unsigned short empty_allowed[kMaxTokenTypes]; //!< Is this pair type allowed to be empty?
|
|
82
|
+
unsigned short match_len[kMaxTokenTypes]; //!< Does this pair type require matched lengths of openers/closers?
|
|
83
|
+
unsigned short should_prune[kMaxTokenTypes]; //!< Does this pair type need to be pruned to a child token chain?
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
typedef struct token_pair_engine token_pair_engine;
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/// Flags for token pair options
|
|
90
|
+
enum pairings_options {
|
|
91
|
+
PAIRING_ALLOW_EMPTY = 1 << 0, //!< Allow consecutive tokens to match with each other
|
|
92
|
+
PAIRING_MATCH_LENGTH = 1 << 1, //!< Require that opening/closing tokens be same length
|
|
93
|
+
PAIRING_PRUNE_MATCH = 1 << 2, //!< Move the matched sub-chain into a child chain
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
/// Create a new token pair engine
|
|
98
|
+
token_pair_engine * token_pair_engine_new(void);
|
|
99
|
+
|
|
100
|
+
/// Free existing token pair engine
|
|
101
|
+
void token_pair_engine_free(
|
|
102
|
+
token_pair_engine * e //!< Token pair engine to be freed
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
/// Add a new pairing configuration to a token pair engine
|
|
106
|
+
void token_pair_engine_add_pairing(
|
|
107
|
+
token_pair_engine * e, //!< Token pair engine to add to
|
|
108
|
+
unsigned short open_type, //!< Token type for opener
|
|
109
|
+
unsigned short close_type, //!< Token type for closer
|
|
110
|
+
unsigned short pair_type, //!< Token type for pairing
|
|
111
|
+
int options //!< Token pair options to use
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
/// Search a token's childen for matching pairs
|
|
115
|
+
void token_pairs_match_pairs_inside_token(
|
|
116
|
+
token * parent, //!< Which tokens should we search for pairs
|
|
117
|
+
token_pair_engine * e, //!< Token pair engine to be used for matching
|
|
118
|
+
stack * s, //!< Pointer to a stack to use for pairing tokens
|
|
119
|
+
unsigned short depth //!< Keep track of recursion depth
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
#endif
|
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/**
|
|
2
|
+
|
|
3
|
+
MultiMarkdown 6 -- Lightweight markup processor to produce HTML, LaTeX, and more.
|
|
4
|
+
|
|
5
|
+
@file transclude.c
|
|
6
|
+
|
|
7
|
+
@brief
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@author Fletcher T. Penney
|
|
11
|
+
@bug
|
|
12
|
+
|
|
13
|
+
**/
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
|
|
17
|
+
Copyright © 2016 - 2017 Fletcher T. Penney.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
The `MultiMarkdown 6` project is released under the MIT License..
|
|
21
|
+
|
|
22
|
+
GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:
|
|
23
|
+
|
|
24
|
+
https://github.com/fletcher/MultiMarkdown-4/
|
|
25
|
+
|
|
26
|
+
MMD 4 is released under both the MIT License and GPL.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
CuTest is released under the zlib/libpng license. See CuTest.c for the text
|
|
30
|
+
of the license.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## The MIT License ##
|
|
34
|
+
|
|
35
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
36
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
37
|
+
in the Software without restriction, including without limitation the rights
|
|
38
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
39
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
40
|
+
furnished to do so, subject to the following conditions:
|
|
41
|
+
|
|
42
|
+
The above copyright notice and this permission notice shall be included in
|
|
43
|
+
all copies or substantial portions of the Software.
|
|
44
|
+
|
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
46
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
47
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
48
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
49
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
50
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
51
|
+
THE SOFTWARE.
|
|
52
|
+
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
#include <stdio.h>
|
|
56
|
+
#include <string.h>
|
|
57
|
+
|
|
58
|
+
#include "d_string.h"
|
|
59
|
+
#include "libMultiMarkdown.h"
|
|
60
|
+
#include "transclude.h"
|
|
61
|
+
|
|
62
|
+
#if defined(__WIN32)
|
|
63
|
+
#include <windows.h>
|
|
64
|
+
#endif
|
|
65
|
+
|
|
66
|
+
#define kBUFFERSIZE 4096 // How many bytes to read at a time
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
/// strndup not available on all platforms
|
|
70
|
+
static char * my_strndup(const char * source, size_t n) {
|
|
71
|
+
if (source == NULL) {
|
|
72
|
+
return NULL;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
size_t len = 0;
|
|
76
|
+
char * result;
|
|
77
|
+
const char * test = source;
|
|
78
|
+
|
|
79
|
+
// strlen is too slow if strlen(source) >> n
|
|
80
|
+
for (len = 0; len < n; ++len) {
|
|
81
|
+
if (test == '\0') {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
test++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
result = malloc(len + 1);
|
|
89
|
+
|
|
90
|
+
if (result) {
|
|
91
|
+
memcpy(result, source, len);
|
|
92
|
+
result[len] = '\0';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/// strdup() not available on all platforms
|
|
100
|
+
static char * my_strdup(const char * source) {
|
|
101
|
+
char * result = malloc(strlen(source) + 1);
|
|
102
|
+
|
|
103
|
+
if (result) {
|
|
104
|
+
strcpy(result, source);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/// Windows can use either `\` or `/` as a separator -- thanks to t-beckmann on github
|
|
112
|
+
/// for suggesting a fix for this.
|
|
113
|
+
bool is_separator(char c) {
|
|
114
|
+
#if defined(__WIN32)
|
|
115
|
+
return c == '\\' || c == '/';
|
|
116
|
+
#else
|
|
117
|
+
return c == '/';
|
|
118
|
+
#endif
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
#ifdef TEST
|
|
123
|
+
void Test_is_separator(CuTest* tc) {
|
|
124
|
+
char * test = "a/\\";
|
|
125
|
+
|
|
126
|
+
#if defined(__WIN32)
|
|
127
|
+
CuAssertIntEquals(tc, false, is_separator(test[0]));
|
|
128
|
+
CuAssertIntEquals(tc, true, is_separator(test[1]));
|
|
129
|
+
CuAssertIntEquals(tc, true, is_separator(test[2]));
|
|
130
|
+
#else
|
|
131
|
+
CuAssertIntEquals(tc, false, is_separator(test[0]));
|
|
132
|
+
CuAssertIntEquals(tc, true, is_separator(test[1]));
|
|
133
|
+
CuAssertIntEquals(tc, false, is_separator(test[2]));
|
|
134
|
+
#endif
|
|
135
|
+
}
|
|
136
|
+
#endif
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
void add_trailing_sep(DString * path) {
|
|
140
|
+
#if defined(__WIN32)
|
|
141
|
+
char sep = '\\';
|
|
142
|
+
#else
|
|
143
|
+
char sep = '/';
|
|
144
|
+
#endif
|
|
145
|
+
|
|
146
|
+
// Ensure that folder ends in separator
|
|
147
|
+
if (!is_separator(path->str[path->currentStringLength - 1])) {
|
|
148
|
+
d_string_append_c(path, sep);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Combine directory and base filename to create a full path */
|
|
153
|
+
char * path_from_dir_base(const char * dir, const char * base) {
|
|
154
|
+
if (!dir && !base) {
|
|
155
|
+
return NULL;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
DString * path = NULL;
|
|
160
|
+
char * result = NULL;
|
|
161
|
+
|
|
162
|
+
if ((base != NULL) && (is_separator(base[0]))) {
|
|
163
|
+
// We have an absolute path
|
|
164
|
+
path = d_string_new(base);
|
|
165
|
+
} else {
|
|
166
|
+
// We have a directory and relative path
|
|
167
|
+
path = d_string_new(dir);
|
|
168
|
+
|
|
169
|
+
// Ensure that folder ends in separator
|
|
170
|
+
add_trailing_sep(path);
|
|
171
|
+
|
|
172
|
+
// Append filename (if present)
|
|
173
|
+
if (base) {
|
|
174
|
+
d_string_append(path, base);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
result = path->str;
|
|
179
|
+
d_string_free(path, false);
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
#ifdef TEST
|
|
186
|
+
void Test_path_from_dir_base(CuTest* tc) {
|
|
187
|
+
char dir[10] = "/foo";
|
|
188
|
+
char base[10] = "bar";
|
|
189
|
+
|
|
190
|
+
char * path = path_from_dir_base(dir, base);
|
|
191
|
+
|
|
192
|
+
#if defined(__WIN32)
|
|
193
|
+
CuAssertStrEquals(tc, "/foo\\bar", path);
|
|
194
|
+
#else
|
|
195
|
+
CuAssertStrEquals(tc, "/foo/bar", path);
|
|
196
|
+
#endif
|
|
197
|
+
|
|
198
|
+
free(path);
|
|
199
|
+
strcpy(base, "/bar");
|
|
200
|
+
|
|
201
|
+
path = path_from_dir_base(dir, base);
|
|
202
|
+
|
|
203
|
+
CuAssertStrEquals(tc, "/bar", path);
|
|
204
|
+
|
|
205
|
+
free(path);
|
|
206
|
+
|
|
207
|
+
path = path_from_dir_base(NULL, NULL);
|
|
208
|
+
CuAssertStrEquals(tc, NULL, path);
|
|
209
|
+
}
|
|
210
|
+
#endif
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
/// Separate filename and directory from a full path
|
|
214
|
+
///
|
|
215
|
+
/// See http://stackoverflow.com/questions/1575278/function-to-split-a-filepath-into-path-and-file
|
|
216
|
+
void split_path_file(char ** dir, char ** file, const char * path) {
|
|
217
|
+
const char * slash = path, * next;
|
|
218
|
+
|
|
219
|
+
#if defined(__WIN32)
|
|
220
|
+
const char sep[] = "\\/"; // Windows allows either variant
|
|
221
|
+
#else
|
|
222
|
+
const char sep[] = "/";
|
|
223
|
+
#endif
|
|
224
|
+
|
|
225
|
+
while ((next = strpbrk(slash + 1, sep))) {
|
|
226
|
+
slash = next;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (path != slash) {
|
|
230
|
+
slash++;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
*dir = my_strndup(path, slash - path);
|
|
234
|
+
*file = my_strdup(slash);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
#ifdef TEST
|
|
238
|
+
void Test_split_path_file(CuTest* tc) {
|
|
239
|
+
char * dir, * file;
|
|
240
|
+
|
|
241
|
+
char * path = "/foo/bar.txt";
|
|
242
|
+
split_path_file(&dir, &file, path);
|
|
243
|
+
|
|
244
|
+
CuAssertStrEquals(tc, "/foo/", dir);
|
|
245
|
+
CuAssertStrEquals(tc, "bar.txt", file);
|
|
246
|
+
|
|
247
|
+
path = "\\foo\\bar.txt";
|
|
248
|
+
split_path_file(&dir, &file, path);
|
|
249
|
+
|
|
250
|
+
#if defined(__WIN32)
|
|
251
|
+
CuAssertStrEquals(tc, "\\foo\\", dir);
|
|
252
|
+
CuAssertStrEquals(tc, "bar.txt", file);
|
|
253
|
+
#else
|
|
254
|
+
CuAssertStrEquals(tc, "", dir);
|
|
255
|
+
CuAssertStrEquals(tc, "\\foo\\bar.txt", file);
|
|
256
|
+
#endif
|
|
257
|
+
}
|
|
258
|
+
#endif
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
DString * scan_file(const char * fname) {
|
|
262
|
+
/* Read from a file and return a DString *
|
|
263
|
+
`buffer` will need to be freed elsewhere */
|
|
264
|
+
|
|
265
|
+
char chunk[kBUFFERSIZE];
|
|
266
|
+
size_t bytes;
|
|
267
|
+
|
|
268
|
+
FILE * file;
|
|
269
|
+
|
|
270
|
+
#if defined(__WIN32)
|
|
271
|
+
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
|
|
272
|
+
wchar_t wstr[wchars_num];
|
|
273
|
+
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wstr, wchars_num);
|
|
274
|
+
|
|
275
|
+
if ((file = _wfopen(wstr, L"rb")) == NULL) {
|
|
276
|
+
#else
|
|
277
|
+
|
|
278
|
+
if ((file = fopen(fname, "r")) == NULL ) {
|
|
279
|
+
#endif
|
|
280
|
+
|
|
281
|
+
return NULL;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
DString * buffer = d_string_new("");
|
|
285
|
+
|
|
286
|
+
while ((bytes = fread(chunk, 1, kBUFFERSIZE, file)) > 0) {
|
|
287
|
+
d_string_append_c_array(buffer, chunk, bytes);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fclose(file);
|
|
291
|
+
|
|
292
|
+
return buffer;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
/// Recursively transclude source text, given a search directory.
|
|
297
|
+
/// Track files to prevent infinite recursive loops
|
|
298
|
+
void mmd_transclude_source(DString * source, const char * search_path, const char * source_path, short format, stack * parsed, stack * manifest) {
|
|
299
|
+
DString * file_path;
|
|
300
|
+
DString * buffer;
|
|
301
|
+
|
|
302
|
+
// Ensure search_folder is tidied up
|
|
303
|
+
char * search_folder = path_from_dir_base(search_path, NULL);
|
|
304
|
+
char * source_folder;
|
|
305
|
+
char * source_file;
|
|
306
|
+
|
|
307
|
+
split_path_file(&source_folder, &source_file, source_path);
|
|
308
|
+
|
|
309
|
+
char * start, * stop;
|
|
310
|
+
char text[1100];
|
|
311
|
+
|
|
312
|
+
char * temp;
|
|
313
|
+
|
|
314
|
+
size_t offset = 0;
|
|
315
|
+
size_t last_match;
|
|
316
|
+
|
|
317
|
+
mmd_engine * e = mmd_engine_create_with_dstring(source, EXT_TRANSCLUDE);
|
|
318
|
+
|
|
319
|
+
if (mmd_engine_has_metadata(e, &offset)) {
|
|
320
|
+
|
|
321
|
+
temp = mmd_engine_metavalue_for_key(e, "transclude base");
|
|
322
|
+
|
|
323
|
+
if (temp) {
|
|
324
|
+
// The new file overrides the search path
|
|
325
|
+
free(search_folder);
|
|
326
|
+
|
|
327
|
+
// Calculate new search path relative to source document
|
|
328
|
+
search_folder = path_from_dir_base(source_folder, temp);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
free(source_folder);
|
|
333
|
+
free(source_file);
|
|
334
|
+
|
|
335
|
+
mmd_engine_free(e, false);
|
|
336
|
+
|
|
337
|
+
if (search_folder == NULL) {
|
|
338
|
+
// We don't have anywhere to search, so nothing to do
|
|
339
|
+
goto exit;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Make sure we use a parse tree for children
|
|
343
|
+
stack * parse_stack = parsed;
|
|
344
|
+
|
|
345
|
+
if (parsed == NULL) {
|
|
346
|
+
// Create temporary stack
|
|
347
|
+
parse_stack = stack_new(0);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Remember where we currently are in the stack
|
|
351
|
+
size_t stack_depth = parse_stack->size;
|
|
352
|
+
|
|
353
|
+
// Iterate through source text, looking for `{{foo}}`
|
|
354
|
+
|
|
355
|
+
start = strstr(&source->str[offset], "{{");
|
|
356
|
+
|
|
357
|
+
while (start != NULL) {
|
|
358
|
+
stop = strstr(start, "}}");
|
|
359
|
+
|
|
360
|
+
if (stop == NULL) {
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Remember insertion point
|
|
365
|
+
last_match = start - source->str;
|
|
366
|
+
|
|
367
|
+
// Ensure we have a reasonable match -- cap at 1000 characters
|
|
368
|
+
if (stop - start < 1000) {
|
|
369
|
+
// Grab text
|
|
370
|
+
strncpy(text, start + 2, stop - start - 2);
|
|
371
|
+
text[stop - start - 2] = '\0';
|
|
372
|
+
|
|
373
|
+
// Is this just {{TOC}}
|
|
374
|
+
if (strcmp("TOC", text) == 0) {
|
|
375
|
+
start = strstr(stop, "{{");
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Is this an absolute path or relative path?
|
|
380
|
+
if (is_separator(text[0])) {
|
|
381
|
+
// Absolute path
|
|
382
|
+
file_path = d_string_new(text);
|
|
383
|
+
} else {
|
|
384
|
+
// Relative path
|
|
385
|
+
file_path = d_string_new(search_folder);
|
|
386
|
+
|
|
387
|
+
// Ensure that search_folder ends in separator
|
|
388
|
+
add_trailing_sep(file_path);
|
|
389
|
+
|
|
390
|
+
d_string_append(file_path, text);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Adjust file wildcard extension for output format
|
|
394
|
+
// e.g. `foo.*`
|
|
395
|
+
if ((format != FORMAT_MMD) && strncmp(&text[stop - start - 4], ".*", 2) == 0) {
|
|
396
|
+
// Trim '.*'
|
|
397
|
+
d_string_erase(file_path, file_path->currentStringLength - 2, 2);
|
|
398
|
+
|
|
399
|
+
switch (format) {
|
|
400
|
+
case FORMAT_HTML:
|
|
401
|
+
d_string_append(file_path, ".html");
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
case FORMAT_LATEX:
|
|
405
|
+
case FORMAT_BEAMER:
|
|
406
|
+
case FORMAT_MEMOIR:
|
|
407
|
+
d_string_append(file_path, ".tex");
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
case FORMAT_FODT:
|
|
411
|
+
d_string_append(file_path, ".fodt");
|
|
412
|
+
break;
|
|
413
|
+
|
|
414
|
+
default:
|
|
415
|
+
d_string_append(file_path, ".txt");
|
|
416
|
+
break;
|
|
417
|
+
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Prevent infinite recursive loops
|
|
422
|
+
for (int i = 0; i < stack_depth; ++i) {
|
|
423
|
+
temp = stack_peek_index(parse_stack, i);
|
|
424
|
+
|
|
425
|
+
if (strcmp(file_path->str, temp) == 0) {
|
|
426
|
+
// We have parsed this file already, don't recurse infinitely
|
|
427
|
+
last_match += 2;
|
|
428
|
+
goto finish_file;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Add this file to stack
|
|
433
|
+
stack_push(parse_stack, file_path->str);
|
|
434
|
+
|
|
435
|
+
// Add file to the manifest?
|
|
436
|
+
if (manifest) {
|
|
437
|
+
bool add = true;
|
|
438
|
+
|
|
439
|
+
for (int i = 0; i < manifest->size; ++i) {
|
|
440
|
+
temp = stack_peek_index(manifest, i);
|
|
441
|
+
|
|
442
|
+
if (strcmp(file_path->str, temp) == 0) {
|
|
443
|
+
// Already on manifest, don't duplicate
|
|
444
|
+
add = false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Add path to manifest
|
|
449
|
+
if (add) {
|
|
450
|
+
stack_push(manifest, my_strdup(file_path->str));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Read the file
|
|
455
|
+
buffer = scan_file(file_path->str);
|
|
456
|
+
|
|
457
|
+
// Substitue buffer for transclusion token
|
|
458
|
+
if (buffer) {
|
|
459
|
+
// Erase transclusion token from current source
|
|
460
|
+
d_string_erase(source, start - source->str, 2 + stop - start);
|
|
461
|
+
|
|
462
|
+
// Recursively check this file for transclusions
|
|
463
|
+
mmd_transclude_source(buffer, search_folder, file_path->str, format, parse_stack, manifest);
|
|
464
|
+
|
|
465
|
+
// Strip metadata from buffer now that we have parsed it
|
|
466
|
+
e = mmd_engine_create_with_dstring(buffer, EXT_TRANSCLUDE);
|
|
467
|
+
|
|
468
|
+
if (mmd_engine_has_metadata(e, &offset)) {
|
|
469
|
+
d_string_erase(buffer, 0, offset);
|
|
470
|
+
} else {
|
|
471
|
+
// Do we need to strip BOM?
|
|
472
|
+
if (strncmp(buffer->str, "\xef\xbb\xbf", 3) == 0) {
|
|
473
|
+
d_string_erase(buffer, 0, 3);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
mmd_engine_free(e, false);
|
|
478
|
+
|
|
479
|
+
// Insert file text -- this may cause d_string to reallocate the
|
|
480
|
+
// character buffer, meaning start/stop are no longer valid
|
|
481
|
+
d_string_insert(source, start - source->str, buffer->str);
|
|
482
|
+
|
|
483
|
+
// Shift search point
|
|
484
|
+
last_match += buffer->currentStringLength;
|
|
485
|
+
|
|
486
|
+
d_string_free(buffer, true);
|
|
487
|
+
} else {
|
|
488
|
+
// Skip over marker
|
|
489
|
+
last_match += 2;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Remove this file from stack
|
|
493
|
+
stack_pop(parse_stack);
|
|
494
|
+
|
|
495
|
+
finish_file:
|
|
496
|
+
d_string_free(file_path, true);
|
|
497
|
+
|
|
498
|
+
} else {
|
|
499
|
+
// Match was too long to be reasonable file name
|
|
500
|
+
// Skip over marker
|
|
501
|
+
last_match += 2;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
start = strstr(source->str + last_match, "{{");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
exit:
|
|
508
|
+
|
|
509
|
+
if (parsed == NULL) {
|
|
510
|
+
// Free temp stack
|
|
511
|
+
stack_free(parse_stack);
|
|
512
|
+
} else {
|
|
513
|
+
// Reset stack depth
|
|
514
|
+
parse_stack->size = stack_depth;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
free(search_folder);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
/// If MMD Header metadata used, insert it into appropriate place
|
|
523
|
+
void mmd_prepend_mmd_header(DString * source) {
|
|
524
|
+
size_t end;
|
|
525
|
+
|
|
526
|
+
if (mmd_d_string_has_metadata(source, &end)) {
|
|
527
|
+
char * meta = mmd_d_string_metavalue_for_key(source, "mmdheader");
|
|
528
|
+
|
|
529
|
+
if (meta) {
|
|
530
|
+
d_string_insert(source, end, "\n\n");
|
|
531
|
+
d_string_insert(source, end + 2, meta);
|
|
532
|
+
free(meta);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
/// If MMD Footer metadata used, insert it into appropriate place
|
|
539
|
+
void mmd_append_mmd_footer(DString * source) {
|
|
540
|
+
char * meta = mmd_d_string_metavalue_for_key(source, "mmdfooter");
|
|
541
|
+
|
|
542
|
+
if (meta) {
|
|
543
|
+
d_string_append(source, "\n\n");
|
|
544
|
+
d_string_append(source, meta);
|
|
545
|
+
|
|
546
|
+
free(meta);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|