redcarpet 1.17.2 → 2.0.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.
- data/README.markdown +294 -37
- data/Rakefile +13 -15
- data/bin/redcarpet +1 -1
- data/ext/redcarpet/autolink.c +18 -19
- data/ext/redcarpet/autolink.h +7 -4
- data/ext/redcarpet/buffer.c +135 -229
- data/ext/redcarpet/buffer.h +47 -113
- data/ext/redcarpet/houdini.h +29 -0
- data/ext/redcarpet/houdini_href_e.c +108 -0
- data/ext/redcarpet/houdini_html_e.c +84 -0
- data/ext/redcarpet/html.c +181 -237
- data/ext/redcarpet/html.h +28 -14
- data/ext/redcarpet/html_blocks.h +206 -0
- data/ext/redcarpet/html_smartypants.c +99 -49
- data/ext/redcarpet/markdown.c +712 -493
- data/ext/redcarpet/markdown.h +48 -41
- data/ext/redcarpet/rc_markdown.c +137 -0
- data/ext/redcarpet/rc_render.c +434 -0
- data/ext/redcarpet/redcarpet.h +33 -0
- data/ext/redcarpet/stack.c +81 -0
- data/ext/redcarpet/stack.h +21 -0
- data/lib/redcarpet/render_man.rb +65 -0
- data/lib/redcarpet.rb +62 -101
- data/redcarpet.gemspec +18 -13
- data/test/redcarpet_test.rb +209 -122
- metadata +33 -20
- data/ext/redcarpet/array.c +0 -300
- data/ext/redcarpet/array.h +0 -147
- data/ext/redcarpet/redcarpet.c +0 -161
- data/lib/markdown.rb +0 -1
- data/test/benchmark.rb +0 -56
- data/test/benchmark.txt +0 -306
- data/test/markdown_test.rb +0 -186
data/ext/redcarpet/markdown.c
CHANGED
|
@@ -18,48 +18,58 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
#include "markdown.h"
|
|
21
|
-
#include "
|
|
21
|
+
#include "stack.h"
|
|
22
22
|
|
|
23
23
|
#include <assert.h>
|
|
24
24
|
#include <string.h>
|
|
25
|
-
//#include <strings.h> /* for strncasecmp */
|
|
26
25
|
#include <ctype.h>
|
|
27
26
|
#include <stdio.h>
|
|
28
27
|
|
|
28
|
+
#define REF_TABLE_SIZE 8
|
|
29
|
+
|
|
29
30
|
#define BUFFER_BLOCK 0
|
|
30
31
|
#define BUFFER_SPAN 1
|
|
31
32
|
|
|
32
33
|
#define MKD_LI_END 8 /* internal list flag */
|
|
33
34
|
|
|
35
|
+
#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n)
|
|
36
|
+
#define GPERF_DOWNCASE 1
|
|
37
|
+
#define GPERF_CASE_STRNCMP 1
|
|
38
|
+
#include "html_blocks.h"
|
|
39
|
+
|
|
34
40
|
/***************
|
|
35
41
|
* LOCAL TYPES *
|
|
36
42
|
***************/
|
|
37
43
|
|
|
38
|
-
/* link_ref
|
|
44
|
+
/* link_ref: reference to a link */
|
|
39
45
|
struct link_ref {
|
|
40
|
-
|
|
46
|
+
unsigned int id;
|
|
47
|
+
|
|
41
48
|
struct buf *link;
|
|
42
49
|
struct buf *title;
|
|
50
|
+
|
|
51
|
+
struct link_ref *next;
|
|
43
52
|
};
|
|
44
53
|
|
|
45
|
-
/* char_trigger
|
|
54
|
+
/* char_trigger: function pointer to render active chars */
|
|
46
55
|
/* returns the number of chars taken care of */
|
|
47
56
|
/* data is the pointer of the beginning of the span */
|
|
48
57
|
/* offset is the number of valid chars before data */
|
|
49
|
-
struct
|
|
58
|
+
struct sd_markdown;
|
|
50
59
|
typedef size_t
|
|
51
|
-
(*char_trigger)(struct buf *ob, struct
|
|
52
|
-
|
|
53
|
-
static size_t char_emphasis(struct buf *ob, struct
|
|
54
|
-
static size_t char_linebreak(struct buf *ob, struct
|
|
55
|
-
static size_t char_codespan(struct buf *ob, struct
|
|
56
|
-
static size_t char_escape(struct buf *ob, struct
|
|
57
|
-
static size_t char_entity(struct buf *ob, struct
|
|
58
|
-
static size_t char_langle_tag(struct buf *ob, struct
|
|
59
|
-
static size_t char_autolink_url(struct buf *ob, struct
|
|
60
|
-
static size_t char_autolink_email(struct buf *ob, struct
|
|
61
|
-
static size_t char_autolink_www(struct buf *ob, struct
|
|
62
|
-
static size_t char_link(struct buf *ob, struct
|
|
60
|
+
(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
61
|
+
|
|
62
|
+
static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
63
|
+
static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
64
|
+
static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
65
|
+
static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
66
|
+
static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
67
|
+
static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
68
|
+
static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
69
|
+
static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
70
|
+
static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
71
|
+
static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
72
|
+
static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
|
63
73
|
|
|
64
74
|
enum markdown_char_t {
|
|
65
75
|
MD_CHAR_NONE = 0,
|
|
@@ -72,7 +82,8 @@ enum markdown_char_t {
|
|
|
72
82
|
MD_CHAR_ENTITITY,
|
|
73
83
|
MD_CHAR_AUTOLINK_URL,
|
|
74
84
|
MD_CHAR_AUTOLINK_EMAIL,
|
|
75
|
-
MD_CHAR_AUTOLINK_WWW
|
|
85
|
+
MD_CHAR_AUTOLINK_WWW,
|
|
86
|
+
MD_CHAR_SUPERSCRIPT,
|
|
76
87
|
};
|
|
77
88
|
|
|
78
89
|
static char_trigger markdown_char_ptrs[] = {
|
|
@@ -87,84 +98,51 @@ static char_trigger markdown_char_ptrs[] = {
|
|
|
87
98
|
&char_autolink_url,
|
|
88
99
|
&char_autolink_email,
|
|
89
100
|
&char_autolink_www,
|
|
101
|
+
&char_superscript,
|
|
90
102
|
};
|
|
91
103
|
|
|
92
104
|
/* render • structure containing one particular render */
|
|
93
|
-
struct
|
|
94
|
-
struct
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
struct
|
|
105
|
+
struct sd_markdown {
|
|
106
|
+
struct sd_callbacks cb;
|
|
107
|
+
void *opaque;
|
|
108
|
+
|
|
109
|
+
struct link_ref *refs[REF_TABLE_SIZE];
|
|
110
|
+
uint8_t active_char[256];
|
|
111
|
+
struct stack work_bufs[2];
|
|
98
112
|
unsigned int ext_flags;
|
|
99
113
|
size_t max_nesting;
|
|
114
|
+
int in_link_body;
|
|
100
115
|
};
|
|
101
116
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
size_t size;
|
|
106
|
-
};
|
|
117
|
+
/***************************
|
|
118
|
+
* HELPER FUNCTIONS *
|
|
119
|
+
***************************/
|
|
107
120
|
|
|
108
121
|
static inline struct buf *
|
|
109
|
-
rndr_newbuf(struct
|
|
122
|
+
rndr_newbuf(struct sd_markdown *rndr, int type)
|
|
110
123
|
{
|
|
111
124
|
static const size_t buf_size[2] = {256, 64};
|
|
112
125
|
struct buf *work = NULL;
|
|
113
|
-
struct
|
|
126
|
+
struct stack *pool = &rndr->work_bufs[type];
|
|
114
127
|
|
|
115
|
-
if (
|
|
116
|
-
|
|
128
|
+
if (pool->size < pool->asize &&
|
|
129
|
+
pool->item[pool->size] != NULL) {
|
|
130
|
+
work = pool->item[pool->size++];
|
|
117
131
|
work->size = 0;
|
|
118
132
|
} else {
|
|
119
133
|
work = bufnew(buf_size[type]);
|
|
120
|
-
|
|
134
|
+
stack_push(pool, work);
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
return work;
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
static inline void
|
|
127
|
-
rndr_popbuf(struct
|
|
141
|
+
rndr_popbuf(struct sd_markdown *rndr, int type)
|
|
128
142
|
{
|
|
129
143
|
rndr->work_bufs[type].size--;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
|
-
/********************
|
|
133
|
-
* GLOBAL VARIABLES *
|
|
134
|
-
********************/
|
|
135
|
-
|
|
136
|
-
/* block_tags • recognised block tags, sorted by cmp_html_tag */
|
|
137
|
-
static struct html_tag block_tags[] = {
|
|
138
|
-
/*0*/ { "p", 1 },
|
|
139
|
-
{ "dl", 2 },
|
|
140
|
-
{ "h1", 2 },
|
|
141
|
-
{ "h2", 2 },
|
|
142
|
-
{ "h3", 2 },
|
|
143
|
-
{ "h4", 2 },
|
|
144
|
-
{ "h5", 2 },
|
|
145
|
-
{ "h6", 2 },
|
|
146
|
-
{ "ol", 2 },
|
|
147
|
-
{ "ul", 2 },
|
|
148
|
-
/*10*/ { "del", 3 },
|
|
149
|
-
{ "div", 3 },
|
|
150
|
-
/*12*/ { "ins", 3 },
|
|
151
|
-
{ "pre", 3 },
|
|
152
|
-
{ "form", 4 },
|
|
153
|
-
{ "math", 4 },
|
|
154
|
-
{ "table", 5 },
|
|
155
|
-
{ "iframe", 6 },
|
|
156
|
-
{ "script", 6 },
|
|
157
|
-
{ "fieldset", 8 },
|
|
158
|
-
{ "noscript", 8 },
|
|
159
|
-
{ "blockquote", 10 }
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
#define INS_TAG (block_tags + 12)
|
|
163
|
-
#define DEL_TAG (block_tags + 10)
|
|
164
|
-
|
|
165
|
-
/***************************
|
|
166
|
-
* HELPER FUNCTIONS *
|
|
167
|
-
***************************/
|
|
168
146
|
static void
|
|
169
147
|
unscape_text(struct buf *ob, struct buf *src)
|
|
170
148
|
{
|
|
@@ -185,54 +163,87 @@ unscape_text(struct buf *ob, struct buf *src)
|
|
|
185
163
|
}
|
|
186
164
|
}
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
cmp_link_ref(void *key, void *array_entry)
|
|
166
|
+
static unsigned int
|
|
167
|
+
hash_link_ref(const uint8_t *link_ref, size_t length)
|
|
191
168
|
{
|
|
192
|
-
|
|
193
|
-
|
|
169
|
+
size_t i;
|
|
170
|
+
unsigned int hash = 0;
|
|
171
|
+
|
|
172
|
+
for (i = 0; i < length; ++i)
|
|
173
|
+
hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
|
|
174
|
+
|
|
175
|
+
return hash;
|
|
194
176
|
}
|
|
195
177
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
178
|
+
static struct link_ref *
|
|
179
|
+
add_link_ref(
|
|
180
|
+
struct link_ref **references,
|
|
181
|
+
const uint8_t *name, size_t name_size)
|
|
199
182
|
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
183
|
+
struct link_ref *ref = calloc(1, sizeof(struct link_ref));
|
|
184
|
+
|
|
185
|
+
if (!ref)
|
|
186
|
+
return NULL;
|
|
187
|
+
|
|
188
|
+
ref->id = hash_link_ref(name, name_size);
|
|
189
|
+
ref->next = references[ref->id % REF_TABLE_SIZE];
|
|
190
|
+
|
|
191
|
+
references[ref->id % REF_TABLE_SIZE] = ref;
|
|
192
|
+
return ref;
|
|
203
193
|
}
|
|
204
194
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
cmp_html_tag(const void *a, const void *b)
|
|
195
|
+
static struct link_ref *
|
|
196
|
+
find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
|
|
208
197
|
{
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
198
|
+
unsigned int hash = hash_link_ref(name, length);
|
|
199
|
+
struct link_ref *ref = NULL;
|
|
200
|
+
|
|
201
|
+
ref = references[hash % REF_TABLE_SIZE];
|
|
214
202
|
|
|
203
|
+
while (ref != NULL) {
|
|
204
|
+
if (ref->id == hash)
|
|
205
|
+
return ref;
|
|
215
206
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
207
|
+
ref = ref->next;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return NULL;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static void
|
|
214
|
+
free_link_refs(struct link_ref **references)
|
|
219
215
|
{
|
|
220
|
-
size_t i
|
|
221
|
-
struct html_tag key;
|
|
216
|
+
size_t i;
|
|
222
217
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
for (i = 0; i < REF_TABLE_SIZE; ++i) {
|
|
219
|
+
struct link_ref *r = references[i];
|
|
220
|
+
struct link_ref *next;
|
|
221
|
+
|
|
222
|
+
while (r) {
|
|
223
|
+
next = r->next;
|
|
224
|
+
bufrelease(r->link);
|
|
225
|
+
bufrelease(r->title);
|
|
226
|
+
free(r);
|
|
227
|
+
r = next;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* Check whether a char is a Markdown space.
|
|
229
234
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
235
|
+
* Right now we only consider spaces the actual
|
|
236
|
+
* space and a newline: tabs and carriage returns
|
|
237
|
+
* are filtered out during the preprocessing phase.
|
|
238
|
+
*
|
|
239
|
+
* If we wanted to actually be UTF-8 compliant, we
|
|
240
|
+
* should instead extract an Unicode codepoint from
|
|
241
|
+
* this character and check for space properties.
|
|
242
|
+
*/
|
|
243
|
+
static inline int
|
|
244
|
+
_isspace(int c)
|
|
245
|
+
{
|
|
246
|
+
return c == ' ' || c == '\n';
|
|
236
247
|
}
|
|
237
248
|
|
|
238
249
|
/****************************
|
|
@@ -242,7 +253,7 @@ find_block_tag(char *data, size_t size)
|
|
|
242
253
|
/* is_mail_autolink • looks for the address part of a mail autolink and '>' */
|
|
243
254
|
/* this is less strict than the original markdown e-mail address matching */
|
|
244
255
|
static size_t
|
|
245
|
-
is_mail_autolink(
|
|
256
|
+
is_mail_autolink(uint8_t *data, size_t size)
|
|
246
257
|
{
|
|
247
258
|
size_t i = 0, nb = 0;
|
|
248
259
|
|
|
@@ -273,7 +284,7 @@ is_mail_autolink(char *data, size_t size)
|
|
|
273
284
|
|
|
274
285
|
/* tag_length • returns the length of the given tag, or 0 is it's not valid */
|
|
275
286
|
static size_t
|
|
276
|
-
tag_length(
|
|
287
|
+
tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
|
|
277
288
|
{
|
|
278
289
|
size_t i, j;
|
|
279
290
|
|
|
@@ -316,8 +327,9 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
|
|
|
316
327
|
while (i < size) {
|
|
317
328
|
if (data[i] == '\\') i += 2;
|
|
318
329
|
else if (data[i] == '>' || data[i] == '\'' ||
|
|
319
|
-
data[i] == '"' ||
|
|
320
|
-
|
|
330
|
+
data[i] == '"' || data[i] == ' ' || data[i] == '\n')
|
|
331
|
+
break;
|
|
332
|
+
else i++;
|
|
321
333
|
}
|
|
322
334
|
|
|
323
335
|
if (i >= size) return 0;
|
|
@@ -327,33 +339,33 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
|
|
|
327
339
|
}
|
|
328
340
|
|
|
329
341
|
/* looking for sometinhg looking like a tag end */
|
|
330
|
-
while (i < size && data[i] != '>') i
|
|
342
|
+
while (i < size && data[i] != '>') i++;
|
|
331
343
|
if (i >= size) return 0;
|
|
332
344
|
return i + 1;
|
|
333
345
|
}
|
|
334
346
|
|
|
335
347
|
/* parse_inline • parses inline markdown elements */
|
|
336
348
|
static void
|
|
337
|
-
parse_inline(struct buf *ob, struct
|
|
349
|
+
parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
338
350
|
{
|
|
339
351
|
size_t i = 0, end = 0;
|
|
340
|
-
|
|
341
|
-
struct buf work = { 0, 0, 0, 0
|
|
352
|
+
uint8_t action = 0;
|
|
353
|
+
struct buf work = { 0, 0, 0, 0 };
|
|
342
354
|
|
|
343
355
|
if (rndr->work_bufs[BUFFER_SPAN].size +
|
|
344
|
-
rndr->work_bufs[BUFFER_BLOCK].size >
|
|
356
|
+
rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
|
|
345
357
|
return;
|
|
346
358
|
|
|
347
359
|
while (i < size) {
|
|
348
360
|
/* copying inactive chars into the output */
|
|
349
|
-
while (end < size && (action = rndr->active_char[
|
|
361
|
+
while (end < size && (action = rndr->active_char[data[end]]) == 0) {
|
|
350
362
|
end++;
|
|
351
363
|
}
|
|
352
364
|
|
|
353
|
-
if (rndr->
|
|
365
|
+
if (rndr->cb.normal_text) {
|
|
354
366
|
work.data = data + i;
|
|
355
367
|
work.size = end - i;
|
|
356
|
-
rndr->
|
|
368
|
+
rndr->cb.normal_text(ob, &work, rndr->opaque);
|
|
357
369
|
}
|
|
358
370
|
else
|
|
359
371
|
bufput(ob, data + i, end - i);
|
|
@@ -361,78 +373,117 @@ parse_inline(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
361
373
|
if (end >= size) break;
|
|
362
374
|
i = end;
|
|
363
375
|
|
|
364
|
-
/* calling the trigger */
|
|
365
376
|
end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
|
|
366
377
|
if (!end) /* no action from the callback */
|
|
367
378
|
end = i + 1;
|
|
368
|
-
else {
|
|
379
|
+
else {
|
|
369
380
|
i += end;
|
|
370
381
|
end = i;
|
|
371
|
-
}
|
|
382
|
+
}
|
|
372
383
|
}
|
|
373
384
|
}
|
|
374
385
|
|
|
375
|
-
/* find_emph_char • looks for the next emph
|
|
386
|
+
/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */
|
|
376
387
|
static size_t
|
|
377
|
-
find_emph_char(
|
|
388
|
+
find_emph_char(uint8_t *data, size_t size, uint8_t c)
|
|
378
389
|
{
|
|
379
390
|
size_t i = 1;
|
|
380
391
|
|
|
381
392
|
while (i < size) {
|
|
382
|
-
while (i < size && data[i] != c
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (
|
|
393
|
+
while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')
|
|
394
|
+
i++;
|
|
395
|
+
|
|
396
|
+
if (i == size)
|
|
397
|
+
return 0;
|
|
398
|
+
|
|
399
|
+
if (data[i] == c)
|
|
400
|
+
return i;
|
|
386
401
|
|
|
387
402
|
/* not counting escaped chars */
|
|
388
|
-
if (i && data[i - 1] == '\\') {
|
|
403
|
+
if (i && data[i - 1] == '\\') {
|
|
404
|
+
i++; continue;
|
|
405
|
+
}
|
|
389
406
|
|
|
390
|
-
/* skipping a code span */
|
|
391
407
|
if (data[i] == '`') {
|
|
408
|
+
size_t span_nb = 0, bt;
|
|
392
409
|
size_t tmp_i = 0;
|
|
393
|
-
|
|
394
|
-
|
|
410
|
+
|
|
411
|
+
/* counting the number of opening backticks */
|
|
412
|
+
while (i < size && data[i] == '`') {
|
|
413
|
+
i++; span_nb++;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (i >= size) return 0;
|
|
417
|
+
|
|
418
|
+
/* finding the matching closing sequence */
|
|
419
|
+
bt = 0;
|
|
420
|
+
while (i < size && bt < span_nb) {
|
|
395
421
|
if (!tmp_i && data[i] == c) tmp_i = i;
|
|
396
|
-
i
|
|
397
|
-
|
|
398
|
-
|
|
422
|
+
if (data[i] == '`') bt++;
|
|
423
|
+
else bt = 0;
|
|
424
|
+
i++;
|
|
425
|
+
}
|
|
399
426
|
|
|
427
|
+
if (i >= size) return tmp_i;
|
|
428
|
+
}
|
|
400
429
|
/* skipping a link */
|
|
401
430
|
else if (data[i] == '[') {
|
|
402
431
|
size_t tmp_i = 0;
|
|
403
|
-
|
|
404
|
-
|
|
432
|
+
uint8_t cc;
|
|
433
|
+
|
|
434
|
+
i++;
|
|
405
435
|
while (i < size && data[i] != ']') {
|
|
406
436
|
if (!tmp_i && data[i] == c) tmp_i = i;
|
|
407
|
-
i
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
i
|
|
437
|
+
i++;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
i++;
|
|
441
|
+
while (i < size && (data[i] == ' ' || data[i] == '\n'))
|
|
442
|
+
i++;
|
|
443
|
+
|
|
444
|
+
if (i >= size)
|
|
445
|
+
return tmp_i;
|
|
446
|
+
|
|
447
|
+
switch (data[i]) {
|
|
448
|
+
case '[':
|
|
449
|
+
cc = ']'; break;
|
|
450
|
+
|
|
451
|
+
case '(':
|
|
452
|
+
cc = ')'; break;
|
|
453
|
+
|
|
454
|
+
default:
|
|
455
|
+
if (tmp_i)
|
|
456
|
+
return tmp_i;
|
|
457
|
+
else
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
i++;
|
|
418
462
|
while (i < size && data[i] != cc) {
|
|
419
463
|
if (!tmp_i && data[i] == c) tmp_i = i;
|
|
420
|
-
i
|
|
421
|
-
|
|
422
|
-
|
|
464
|
+
i++;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (i >= size)
|
|
468
|
+
return tmp_i;
|
|
469
|
+
|
|
470
|
+
i++;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
423
474
|
return 0;
|
|
424
475
|
}
|
|
425
476
|
|
|
426
477
|
/* parse_emph1 • parsing single emphase */
|
|
427
478
|
/* closed by a symbol not preceded by whitespace and not followed by symbol */
|
|
428
479
|
static size_t
|
|
429
|
-
parse_emph1(struct buf *ob, struct
|
|
480
|
+
parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
|
|
430
481
|
{
|
|
431
482
|
size_t i = 0, len;
|
|
432
483
|
struct buf *work = 0;
|
|
433
484
|
int r;
|
|
434
485
|
|
|
435
|
-
if (!rndr->
|
|
486
|
+
if (!rndr->cb.emphasis) return 0;
|
|
436
487
|
|
|
437
488
|
/* skipping one symbol if coming from emph3 */
|
|
438
489
|
if (size > 1 && data[0] == c && data[1] == c) i = 1;
|
|
@@ -443,21 +494,16 @@ parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c
|
|
|
443
494
|
i += len;
|
|
444
495
|
if (i >= size) return 0;
|
|
445
496
|
|
|
446
|
-
if (i
|
|
447
|
-
i += 1;
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (data[i] == c && !isspace(data[i - 1])) {
|
|
497
|
+
if (data[i] == c && !_isspace(data[i - 1])) {
|
|
452
498
|
|
|
453
499
|
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
|
|
454
|
-
if (!(i + 1 == size ||
|
|
500
|
+
if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1])))
|
|
455
501
|
continue;
|
|
456
502
|
}
|
|
457
503
|
|
|
458
504
|
work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
459
505
|
parse_inline(work, rndr, data, i);
|
|
460
|
-
r = rndr->
|
|
506
|
+
r = rndr->cb.emphasis(ob, work, rndr->opaque);
|
|
461
507
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
462
508
|
return r ? i + 1 : 0;
|
|
463
509
|
}
|
|
@@ -468,27 +514,27 @@ parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c
|
|
|
468
514
|
|
|
469
515
|
/* parse_emph2 • parsing single emphase */
|
|
470
516
|
static size_t
|
|
471
|
-
parse_emph2(struct buf *ob, struct
|
|
517
|
+
parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
|
|
472
518
|
{
|
|
473
|
-
int (*render_method)(struct buf *ob, struct buf *text, void *opaque);
|
|
519
|
+
int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);
|
|
474
520
|
size_t i = 0, len;
|
|
475
521
|
struct buf *work = 0;
|
|
476
522
|
int r;
|
|
477
523
|
|
|
478
|
-
render_method = (c == '~') ? rndr->
|
|
524
|
+
render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;
|
|
479
525
|
|
|
480
526
|
if (!render_method)
|
|
481
527
|
return 0;
|
|
482
|
-
|
|
528
|
+
|
|
483
529
|
while (i < size) {
|
|
484
530
|
len = find_emph_char(data + i, size - i, c);
|
|
485
531
|
if (!len) return 0;
|
|
486
532
|
i += len;
|
|
487
533
|
|
|
488
|
-
if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !
|
|
534
|
+
if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
|
|
489
535
|
work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
490
536
|
parse_inline(work, rndr, data, i);
|
|
491
|
-
r = render_method(ob, work, rndr->
|
|
537
|
+
r = render_method(ob, work, rndr->opaque);
|
|
492
538
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
493
539
|
return r ? i + 2 : 0;
|
|
494
540
|
}
|
|
@@ -500,7 +546,7 @@ parse_emph2(struct buf *ob, struct render *rndr, char *data, size_t size, char c
|
|
|
500
546
|
/* parse_emph3 • parsing single emphase */
|
|
501
547
|
/* finds the first closing tag, and delegates to the other emph */
|
|
502
548
|
static size_t
|
|
503
|
-
parse_emph3(struct buf *ob, struct
|
|
549
|
+
parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
|
|
504
550
|
{
|
|
505
551
|
size_t i = 0, len;
|
|
506
552
|
int r;
|
|
@@ -511,15 +557,15 @@ parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c
|
|
|
511
557
|
i += len;
|
|
512
558
|
|
|
513
559
|
/* skip whitespace preceded symbols */
|
|
514
|
-
if (data[i] != c ||
|
|
560
|
+
if (data[i] != c || _isspace(data[i - 1]))
|
|
515
561
|
continue;
|
|
516
562
|
|
|
517
|
-
if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->
|
|
563
|
+
if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {
|
|
518
564
|
/* triple symbol found */
|
|
519
565
|
struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
520
566
|
|
|
521
567
|
parse_inline(work, rndr, data, i);
|
|
522
|
-
r = rndr->
|
|
568
|
+
r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);
|
|
523
569
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
524
570
|
return r ? i + 3 : 0;
|
|
525
571
|
|
|
@@ -536,46 +582,46 @@ parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c
|
|
|
536
582
|
else return len - 1;
|
|
537
583
|
}
|
|
538
584
|
}
|
|
539
|
-
return 0;
|
|
585
|
+
return 0;
|
|
540
586
|
}
|
|
541
587
|
|
|
542
588
|
/* char_emphasis • single and double emphasis parsing */
|
|
543
589
|
static size_t
|
|
544
|
-
char_emphasis(struct buf *ob, struct
|
|
590
|
+
char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
545
591
|
{
|
|
546
|
-
|
|
592
|
+
uint8_t c = data[0];
|
|
547
593
|
size_t ret;
|
|
548
594
|
|
|
549
595
|
if (size > 2 && data[1] != c) {
|
|
550
596
|
/* whitespace cannot follow an opening emphasis;
|
|
551
597
|
* strikethrough only takes two characters '~~' */
|
|
552
|
-
if (c == '~' ||
|
|
598
|
+
if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)
|
|
553
599
|
return 0;
|
|
554
600
|
|
|
555
601
|
return ret + 1;
|
|
556
602
|
}
|
|
557
603
|
|
|
558
604
|
if (size > 3 && data[1] == c && data[2] != c) {
|
|
559
|
-
if (
|
|
605
|
+
if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)
|
|
560
606
|
return 0;
|
|
561
607
|
|
|
562
608
|
return ret + 2;
|
|
563
609
|
}
|
|
564
610
|
|
|
565
611
|
if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {
|
|
566
|
-
if (c == '~' ||
|
|
612
|
+
if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)
|
|
567
613
|
return 0;
|
|
568
614
|
|
|
569
615
|
return ret + 3;
|
|
570
616
|
}
|
|
571
617
|
|
|
572
|
-
return 0;
|
|
618
|
+
return 0;
|
|
573
619
|
}
|
|
574
620
|
|
|
575
621
|
|
|
576
622
|
/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */
|
|
577
623
|
static size_t
|
|
578
|
-
char_linebreak(struct buf *ob, struct
|
|
624
|
+
char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
579
625
|
{
|
|
580
626
|
if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
|
|
581
627
|
return 0;
|
|
@@ -584,13 +630,13 @@ char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, s
|
|
|
584
630
|
while (ob->size && ob->data[ob->size - 1] == ' ')
|
|
585
631
|
ob->size--;
|
|
586
632
|
|
|
587
|
-
return rndr->
|
|
633
|
+
return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;
|
|
588
634
|
}
|
|
589
635
|
|
|
590
636
|
|
|
591
637
|
/* char_codespan • '`' parsing a code span (assuming codespan != 0) */
|
|
592
638
|
static size_t
|
|
593
|
-
char_codespan(struct buf *ob, struct
|
|
639
|
+
char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
594
640
|
{
|
|
595
641
|
size_t end, nb = 0, i, f_begin, f_end;
|
|
596
642
|
|
|
@@ -610,20 +656,20 @@ char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, si
|
|
|
610
656
|
|
|
611
657
|
/* trimming outside whitespaces */
|
|
612
658
|
f_begin = nb;
|
|
613
|
-
while (f_begin < end &&
|
|
659
|
+
while (f_begin < end && data[f_begin] == ' ')
|
|
614
660
|
f_begin++;
|
|
615
661
|
|
|
616
662
|
f_end = end - nb;
|
|
617
|
-
while (f_end > nb &&
|
|
663
|
+
while (f_end > nb && data[f_end-1] == ' ')
|
|
618
664
|
f_end--;
|
|
619
665
|
|
|
620
666
|
/* real code span */
|
|
621
667
|
if (f_begin < f_end) {
|
|
622
|
-
struct buf work = { data + f_begin, f_end - f_begin, 0, 0
|
|
623
|
-
if (!rndr->
|
|
668
|
+
struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };
|
|
669
|
+
if (!rndr->cb.codespan(ob, &work, rndr->opaque))
|
|
624
670
|
end = 0;
|
|
625
671
|
} else {
|
|
626
|
-
if (!rndr->
|
|
672
|
+
if (!rndr->cb.codespan(ob, 0, rndr->opaque))
|
|
627
673
|
end = 0;
|
|
628
674
|
}
|
|
629
675
|
|
|
@@ -633,19 +679,19 @@ char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, si
|
|
|
633
679
|
|
|
634
680
|
/* char_escape • '\\' backslash escape */
|
|
635
681
|
static size_t
|
|
636
|
-
char_escape(struct buf *ob, struct
|
|
682
|
+
char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
637
683
|
{
|
|
638
|
-
static const char *escape_chars = "\\`*_{}[]()
|
|
639
|
-
struct buf work = { 0, 0, 0, 0
|
|
684
|
+
static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";
|
|
685
|
+
struct buf work = { 0, 0, 0, 0 };
|
|
640
686
|
|
|
641
687
|
if (size > 1) {
|
|
642
688
|
if (strchr(escape_chars, data[1]) == NULL)
|
|
643
689
|
return 0;
|
|
644
690
|
|
|
645
|
-
if (rndr->
|
|
691
|
+
if (rndr->cb.normal_text) {
|
|
646
692
|
work.data = data + 1;
|
|
647
693
|
work.size = 1;
|
|
648
|
-
rndr->
|
|
694
|
+
rndr->cb.normal_text(ob, &work, rndr->opaque);
|
|
649
695
|
}
|
|
650
696
|
else bufputc(ob, data[1]);
|
|
651
697
|
}
|
|
@@ -656,10 +702,10 @@ char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size
|
|
|
656
702
|
/* char_entity • '&' escaped when it doesn't belong to an entity */
|
|
657
703
|
/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
|
|
658
704
|
static size_t
|
|
659
|
-
char_entity(struct buf *ob, struct
|
|
705
|
+
char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
660
706
|
{
|
|
661
707
|
size_t end = 1;
|
|
662
|
-
struct buf work;
|
|
708
|
+
struct buf work = { 0, 0, 0, 0 };
|
|
663
709
|
|
|
664
710
|
if (end < size && data[end] == '#')
|
|
665
711
|
end++;
|
|
@@ -668,14 +714,14 @@ char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size
|
|
|
668
714
|
end++;
|
|
669
715
|
|
|
670
716
|
if (end < size && data[end] == ';')
|
|
671
|
-
end
|
|
717
|
+
end++; /* real entity */
|
|
672
718
|
else
|
|
673
719
|
return 0; /* lone '&' */
|
|
674
720
|
|
|
675
|
-
if (rndr->
|
|
721
|
+
if (rndr->cb.entity) {
|
|
676
722
|
work.data = data;
|
|
677
723
|
work.size = end;
|
|
678
|
-
rndr->
|
|
724
|
+
rndr->cb.entity(ob, &work, rndr->opaque);
|
|
679
725
|
}
|
|
680
726
|
else bufput(ob, data, end);
|
|
681
727
|
|
|
@@ -684,24 +730,24 @@ char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size
|
|
|
684
730
|
|
|
685
731
|
/* char_langle_tag • '<' when tags or autolinks are allowed */
|
|
686
732
|
static size_t
|
|
687
|
-
char_langle_tag(struct buf *ob, struct
|
|
733
|
+
char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
688
734
|
{
|
|
689
735
|
enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
|
|
690
736
|
size_t end = tag_length(data, size, &altype);
|
|
691
|
-
struct buf work = { data, end, 0, 0
|
|
737
|
+
struct buf work = { data, end, 0, 0 };
|
|
692
738
|
int ret = 0;
|
|
693
739
|
|
|
694
740
|
if (end > 2) {
|
|
695
|
-
if (rndr->
|
|
741
|
+
if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {
|
|
696
742
|
struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
697
743
|
work.data = data + 1;
|
|
698
744
|
work.size = end - 2;
|
|
699
745
|
unscape_text(u_link, &work);
|
|
700
|
-
ret = rndr->
|
|
746
|
+
ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);
|
|
701
747
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
702
748
|
}
|
|
703
|
-
else if (rndr->
|
|
704
|
-
ret = rndr->
|
|
749
|
+
else if (rndr->cb.raw_html_tag)
|
|
750
|
+
ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);
|
|
705
751
|
}
|
|
706
752
|
|
|
707
753
|
if (!ret) return 0;
|
|
@@ -709,23 +755,30 @@ char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset,
|
|
|
709
755
|
}
|
|
710
756
|
|
|
711
757
|
static size_t
|
|
712
|
-
char_autolink_www(struct buf *ob, struct
|
|
758
|
+
char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
713
759
|
{
|
|
714
|
-
struct buf *link, *link_url;
|
|
760
|
+
struct buf *link, *link_url, *link_text;
|
|
715
761
|
size_t link_len, rewind;
|
|
716
762
|
|
|
717
|
-
if (!rndr->
|
|
763
|
+
if (!rndr->cb.link || rndr->in_link_body)
|
|
718
764
|
return 0;
|
|
719
765
|
|
|
720
766
|
link = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
721
767
|
|
|
722
|
-
if ((link_len =
|
|
768
|
+
if ((link_len = sd_autolink__www(&rewind, link, data, offset, size)) > 0) {
|
|
723
769
|
link_url = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
724
770
|
BUFPUTSL(link_url, "http://");
|
|
725
771
|
bufput(link_url, link->data, link->size);
|
|
726
772
|
|
|
727
773
|
ob->size -= rewind;
|
|
728
|
-
|
|
774
|
+
if (rndr->cb.normal_text) {
|
|
775
|
+
link_text = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
776
|
+
rndr->cb.normal_text(link_text, link, rndr->opaque);
|
|
777
|
+
rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);
|
|
778
|
+
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
779
|
+
} else {
|
|
780
|
+
rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);
|
|
781
|
+
}
|
|
729
782
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
730
783
|
}
|
|
731
784
|
|
|
@@ -734,19 +787,19 @@ char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset
|
|
|
734
787
|
}
|
|
735
788
|
|
|
736
789
|
static size_t
|
|
737
|
-
char_autolink_email(struct buf *ob, struct
|
|
790
|
+
char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
738
791
|
{
|
|
739
792
|
struct buf *link;
|
|
740
793
|
size_t link_len, rewind;
|
|
741
794
|
|
|
742
|
-
if (!rndr->
|
|
795
|
+
if (!rndr->cb.autolink || rndr->in_link_body)
|
|
743
796
|
return 0;
|
|
744
797
|
|
|
745
798
|
link = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
746
799
|
|
|
747
|
-
if ((link_len =
|
|
800
|
+
if ((link_len = sd_autolink__email(&rewind, link, data, offset, size)) > 0) {
|
|
748
801
|
ob->size -= rewind;
|
|
749
|
-
rndr->
|
|
802
|
+
rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);
|
|
750
803
|
}
|
|
751
804
|
|
|
752
805
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
@@ -754,19 +807,19 @@ char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offs
|
|
|
754
807
|
}
|
|
755
808
|
|
|
756
809
|
static size_t
|
|
757
|
-
char_autolink_url(struct buf *ob, struct
|
|
810
|
+
char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
758
811
|
{
|
|
759
812
|
struct buf *link;
|
|
760
813
|
size_t link_len, rewind;
|
|
761
814
|
|
|
762
|
-
if (!rndr->
|
|
815
|
+
if (!rndr->cb.autolink || rndr->in_link_body)
|
|
763
816
|
return 0;
|
|
764
817
|
|
|
765
818
|
link = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
766
819
|
|
|
767
|
-
if ((link_len =
|
|
820
|
+
if ((link_len = sd_autolink__url(&rewind, link, data, offset, size)) > 0) {
|
|
768
821
|
ob->size -= rewind;
|
|
769
|
-
rndr->
|
|
822
|
+
rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);
|
|
770
823
|
}
|
|
771
824
|
|
|
772
825
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
@@ -775,7 +828,7 @@ char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset
|
|
|
775
828
|
|
|
776
829
|
/* char_link • '[': parsing a link or an image */
|
|
777
830
|
static size_t
|
|
778
|
-
char_link(struct buf *ob, struct
|
|
831
|
+
char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
779
832
|
{
|
|
780
833
|
int is_img = (offset && data[-1] == '!'), level;
|
|
781
834
|
size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
|
|
@@ -785,13 +838,14 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
785
838
|
struct buf *u_link = 0;
|
|
786
839
|
size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;
|
|
787
840
|
int text_has_nl = 0, ret = 0;
|
|
841
|
+
int in_title = 0, qtype = 0;
|
|
788
842
|
|
|
789
843
|
/* checking whether the correct renderer exists */
|
|
790
|
-
if ((is_img && !rndr->
|
|
844
|
+
if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))
|
|
791
845
|
goto cleanup;
|
|
792
846
|
|
|
793
847
|
/* looking for the matching closing bracket */
|
|
794
|
-
for (level = 1; i < size; i
|
|
848
|
+
for (level = 1; i < size; i++) {
|
|
795
849
|
if (data[i] == '\n')
|
|
796
850
|
text_has_nl = 1;
|
|
797
851
|
|
|
@@ -812,19 +866,19 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
812
866
|
goto cleanup;
|
|
813
867
|
|
|
814
868
|
txt_e = i;
|
|
815
|
-
i
|
|
869
|
+
i++;
|
|
816
870
|
|
|
817
871
|
/* skip any amount of whitespace or newline */
|
|
818
872
|
/* (this is much more laxist than original markdown syntax) */
|
|
819
|
-
while (i < size &&
|
|
873
|
+
while (i < size && _isspace(data[i]))
|
|
820
874
|
i++;
|
|
821
875
|
|
|
822
876
|
/* inline style link */
|
|
823
877
|
if (i < size && data[i] == '(') {
|
|
824
878
|
/* skipping initial whitespace */
|
|
825
|
-
i
|
|
879
|
+
i++;
|
|
826
880
|
|
|
827
|
-
while (i < size &&
|
|
881
|
+
while (i < size && _isspace(data[i]))
|
|
828
882
|
i++;
|
|
829
883
|
|
|
830
884
|
link_b = i;
|
|
@@ -833,7 +887,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
833
887
|
while (i < size) {
|
|
834
888
|
if (data[i] == '\\') i += 2;
|
|
835
889
|
else if (data[i] == ')' || data[i] == '\'' || data[i] == '"') break;
|
|
836
|
-
else i
|
|
890
|
+
else i++;
|
|
837
891
|
}
|
|
838
892
|
|
|
839
893
|
if (i >= size) goto cleanup;
|
|
@@ -841,20 +895,23 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
841
895
|
|
|
842
896
|
/* looking for title end if present */
|
|
843
897
|
if (data[i] == '\'' || data[i] == '"') {
|
|
898
|
+
qtype = data[i];
|
|
899
|
+
in_title = 1;
|
|
844
900
|
i++;
|
|
845
901
|
title_b = i;
|
|
846
902
|
|
|
847
903
|
while (i < size) {
|
|
848
904
|
if (data[i] == '\\') i += 2;
|
|
849
|
-
else if (data[i] ==
|
|
850
|
-
else i
|
|
905
|
+
else if (data[i] == qtype) {in_title = 0; i++;}
|
|
906
|
+
else if ((data[i] == ')') && !in_title) break;
|
|
907
|
+
else i++;
|
|
851
908
|
}
|
|
852
909
|
|
|
853
910
|
if (i >= size) goto cleanup;
|
|
854
911
|
|
|
855
912
|
/* skipping whitespaces after title */
|
|
856
913
|
title_e = i - 1;
|
|
857
|
-
while (title_e > title_b &&
|
|
914
|
+
while (title_e > title_b && _isspace(data[title_e]))
|
|
858
915
|
title_e--;
|
|
859
916
|
|
|
860
917
|
/* checking for closing quote presence */
|
|
@@ -865,7 +922,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
865
922
|
}
|
|
866
923
|
|
|
867
924
|
/* remove whitespace at the end of the link */
|
|
868
|
-
while (link_e > link_b &&
|
|
925
|
+
while (link_e > link_b && _isspace(data[link_e - 1]))
|
|
869
926
|
link_e--;
|
|
870
927
|
|
|
871
928
|
/* remove optional angle brackets around the link */
|
|
@@ -888,11 +945,11 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
888
945
|
|
|
889
946
|
/* reference style link */
|
|
890
947
|
else if (i < size && data[i] == '[') {
|
|
891
|
-
struct buf id = { 0, 0, 0, 0
|
|
948
|
+
struct buf id = { 0, 0, 0, 0 };
|
|
892
949
|
struct link_ref *lr;
|
|
893
950
|
|
|
894
951
|
/* looking for the id */
|
|
895
|
-
i
|
|
952
|
+
i++;
|
|
896
953
|
link_b = i;
|
|
897
954
|
while (i < size && data[i] != ']') i++;
|
|
898
955
|
if (i >= size) goto cleanup;
|
|
@@ -922,18 +979,19 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
922
979
|
id.size = link_e - link_b;
|
|
923
980
|
}
|
|
924
981
|
|
|
925
|
-
lr =
|
|
926
|
-
if (!lr)
|
|
982
|
+
lr = find_link_ref(rndr->refs, id.data, id.size);
|
|
983
|
+
if (!lr)
|
|
984
|
+
goto cleanup;
|
|
927
985
|
|
|
928
986
|
/* keeping link and title from link_ref */
|
|
929
987
|
link = lr->link;
|
|
930
988
|
title = lr->title;
|
|
931
|
-
i
|
|
989
|
+
i++;
|
|
932
990
|
}
|
|
933
991
|
|
|
934
992
|
/* shortcut reference style link */
|
|
935
993
|
else {
|
|
936
|
-
struct buf id = { 0, 0, 0, 0
|
|
994
|
+
struct buf id = { 0, 0, 0, 0 };
|
|
937
995
|
struct link_ref *lr;
|
|
938
996
|
|
|
939
997
|
/* crafting the id */
|
|
@@ -956,8 +1014,9 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
956
1014
|
}
|
|
957
1015
|
|
|
958
1016
|
/* finding the link_ref */
|
|
959
|
-
lr =
|
|
960
|
-
if (!lr)
|
|
1017
|
+
lr = find_link_ref(rndr->refs, id.data, id.size);
|
|
1018
|
+
if (!lr)
|
|
1019
|
+
goto cleanup;
|
|
961
1020
|
|
|
962
1021
|
/* keeping link and title from link_ref */
|
|
963
1022
|
link = lr->link;
|
|
@@ -970,8 +1029,15 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
970
1029
|
/* building content: img alt is escaped, link content is parsed */
|
|
971
1030
|
if (txt_e > 1) {
|
|
972
1031
|
content = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
973
|
-
if (is_img)
|
|
974
|
-
|
|
1032
|
+
if (is_img) {
|
|
1033
|
+
bufput(content, data + 1, txt_e - 1);
|
|
1034
|
+
} else {
|
|
1035
|
+
/* disable autolinking when parsing inline the
|
|
1036
|
+
* content of a link */
|
|
1037
|
+
rndr->in_link_body = 1;
|
|
1038
|
+
parse_inline(content, rndr, data + 1, txt_e - 1);
|
|
1039
|
+
rndr->in_link_body = 0;
|
|
1040
|
+
}
|
|
975
1041
|
}
|
|
976
1042
|
|
|
977
1043
|
if (link) {
|
|
@@ -984,9 +1050,9 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
|
|
|
984
1050
|
if (ob->size && ob->data[ob->size - 1] == '!')
|
|
985
1051
|
ob->size -= 1;
|
|
986
1052
|
|
|
987
|
-
ret = rndr->
|
|
1053
|
+
ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);
|
|
988
1054
|
} else {
|
|
989
|
-
ret = rndr->
|
|
1055
|
+
ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);
|
|
990
1056
|
}
|
|
991
1057
|
|
|
992
1058
|
/* cleanup */
|
|
@@ -995,7 +1061,43 @@ cleanup:
|
|
|
995
1061
|
return ret ? i : 0;
|
|
996
1062
|
}
|
|
997
1063
|
|
|
1064
|
+
static size_t
|
|
1065
|
+
char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
|
1066
|
+
{
|
|
1067
|
+
size_t sup_start, sup_len;
|
|
1068
|
+
struct buf *sup;
|
|
1069
|
+
|
|
1070
|
+
if (!rndr->cb.superscript)
|
|
1071
|
+
return 0;
|
|
1072
|
+
|
|
1073
|
+
if (size < 2)
|
|
1074
|
+
return 0;
|
|
1075
|
+
|
|
1076
|
+
if (data[1] == '(') {
|
|
1077
|
+
sup_start = sup_len = 2;
|
|
998
1078
|
|
|
1079
|
+
while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')
|
|
1080
|
+
sup_len++;
|
|
1081
|
+
|
|
1082
|
+
if (sup_len == size)
|
|
1083
|
+
return 0;
|
|
1084
|
+
} else {
|
|
1085
|
+
sup_start = sup_len = 1;
|
|
1086
|
+
|
|
1087
|
+
while (sup_len < size && !_isspace(data[sup_len]))
|
|
1088
|
+
sup_len++;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (sup_len - sup_start == 0)
|
|
1092
|
+
return (sup_start == 2) ? 3 : 0;
|
|
1093
|
+
|
|
1094
|
+
sup = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
1095
|
+
parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);
|
|
1096
|
+
rndr->cb.superscript(ob, sup, rndr->opaque);
|
|
1097
|
+
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1098
|
+
|
|
1099
|
+
return (sup_start == 2) ? sup_len + 1 : sup_len;
|
|
1100
|
+
}
|
|
999
1101
|
|
|
1000
1102
|
/*********************************
|
|
1001
1103
|
* BLOCK-LEVEL PARSING FUNCTIONS *
|
|
@@ -1003,28 +1105,31 @@ cleanup:
|
|
|
1003
1105
|
|
|
1004
1106
|
/* is_empty • returns the line length when it is empty, 0 otherwise */
|
|
1005
1107
|
static size_t
|
|
1006
|
-
is_empty(
|
|
1108
|
+
is_empty(uint8_t *data, size_t size)
|
|
1007
1109
|
{
|
|
1008
1110
|
size_t i;
|
|
1009
|
-
|
|
1010
|
-
|
|
1111
|
+
|
|
1112
|
+
for (i = 0; i < size && data[i] != '\n'; i++)
|
|
1113
|
+
if (data[i] != ' ')
|
|
1114
|
+
return 0;
|
|
1115
|
+
|
|
1011
1116
|
return i + 1;
|
|
1012
1117
|
}
|
|
1013
1118
|
|
|
1014
1119
|
/* is_hrule • returns whether a line is a horizontal rule */
|
|
1015
1120
|
static int
|
|
1016
|
-
is_hrule(
|
|
1121
|
+
is_hrule(uint8_t *data, size_t size)
|
|
1017
1122
|
{
|
|
1018
1123
|
size_t i = 0, n = 0;
|
|
1019
|
-
|
|
1124
|
+
uint8_t c;
|
|
1020
1125
|
|
|
1021
1126
|
/* skipping initial spaces */
|
|
1022
1127
|
if (size < 3) return 0;
|
|
1023
|
-
if (data[0] == ' ') { i
|
|
1024
|
-
if (data[1] == ' ') { i
|
|
1025
|
-
if (data[2] == ' ') { i
|
|
1128
|
+
if (data[0] == ' ') { i++;
|
|
1129
|
+
if (data[1] == ' ') { i++;
|
|
1130
|
+
if (data[2] == ' ') { i++; } } }
|
|
1026
1131
|
|
|
1027
|
-
/* looking at the hrule
|
|
1132
|
+
/* looking at the hrule uint8_t */
|
|
1028
1133
|
if (i + 2 >= size
|
|
1029
1134
|
|| (data[i] != '*' && data[i] != '-' && data[i] != '_'))
|
|
1030
1135
|
return 0;
|
|
@@ -1032,34 +1137,36 @@ is_hrule(char *data, size_t size)
|
|
|
1032
1137
|
|
|
1033
1138
|
/* the whole line must be the char or whitespace */
|
|
1034
1139
|
while (i < size && data[i] != '\n') {
|
|
1035
|
-
if (data[i] == c) n
|
|
1036
|
-
else if (data[i] != ' '
|
|
1140
|
+
if (data[i] == c) n++;
|
|
1141
|
+
else if (data[i] != ' ')
|
|
1037
1142
|
return 0;
|
|
1038
|
-
|
|
1143
|
+
|
|
1144
|
+
i++;
|
|
1145
|
+
}
|
|
1039
1146
|
|
|
1040
1147
|
return n >= 3;
|
|
1041
1148
|
}
|
|
1042
1149
|
|
|
1043
1150
|
/* check if a line is a code fence; return its size if it is */
|
|
1044
1151
|
static size_t
|
|
1045
|
-
is_codefence(
|
|
1152
|
+
is_codefence(uint8_t *data, size_t size, struct buf *syntax)
|
|
1046
1153
|
{
|
|
1047
1154
|
size_t i = 0, n = 0;
|
|
1048
|
-
|
|
1155
|
+
uint8_t c;
|
|
1049
1156
|
|
|
1050
1157
|
/* skipping initial spaces */
|
|
1051
1158
|
if (size < 3) return 0;
|
|
1052
|
-
if (data[0] == ' ') { i
|
|
1053
|
-
if (data[1] == ' ') { i
|
|
1054
|
-
if (data[2] == ' ') { i
|
|
1159
|
+
if (data[0] == ' ') { i++;
|
|
1160
|
+
if (data[1] == ' ') { i++;
|
|
1161
|
+
if (data[2] == ' ') { i++; } } }
|
|
1055
1162
|
|
|
1056
|
-
/* looking at the hrule
|
|
1163
|
+
/* looking at the hrule uint8_t */
|
|
1057
1164
|
if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))
|
|
1058
1165
|
return 0;
|
|
1059
1166
|
|
|
1060
1167
|
c = data[i];
|
|
1061
1168
|
|
|
1062
|
-
/* the whole line must be the
|
|
1169
|
+
/* the whole line must be the uint8_t or whitespace */
|
|
1063
1170
|
while (i < size && data[i] == c) {
|
|
1064
1171
|
n++; i++;
|
|
1065
1172
|
}
|
|
@@ -1070,7 +1177,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
|
|
|
1070
1177
|
if (syntax != NULL) {
|
|
1071
1178
|
size_t syn = 0;
|
|
1072
1179
|
|
|
1073
|
-
while (i < size &&
|
|
1180
|
+
while (i < size && data[i] == ' ')
|
|
1074
1181
|
i++;
|
|
1075
1182
|
|
|
1076
1183
|
syntax->data = data + i;
|
|
@@ -1087,16 +1194,16 @@ is_codefence(char *data, size_t size, struct buf *syntax)
|
|
|
1087
1194
|
|
|
1088
1195
|
/* strip all whitespace at the beginning and the end
|
|
1089
1196
|
* of the {} block */
|
|
1090
|
-
while (syn > 0 &&
|
|
1197
|
+
while (syn > 0 && _isspace(syntax->data[0])) {
|
|
1091
1198
|
syntax->data++; syn--;
|
|
1092
1199
|
}
|
|
1093
1200
|
|
|
1094
|
-
while (syn > 0 &&
|
|
1201
|
+
while (syn > 0 && _isspace(syntax->data[syn - 1]))
|
|
1095
1202
|
syn--;
|
|
1096
1203
|
|
|
1097
1204
|
i++;
|
|
1098
1205
|
} else {
|
|
1099
|
-
while (i < size && !
|
|
1206
|
+
while (i < size && !_isspace(data[i])) {
|
|
1100
1207
|
syn++; i++;
|
|
1101
1208
|
}
|
|
1102
1209
|
}
|
|
@@ -1105,7 +1212,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
|
|
|
1105
1212
|
}
|
|
1106
1213
|
|
|
1107
1214
|
while (i < size && data[i] != '\n') {
|
|
1108
|
-
if (!
|
|
1215
|
+
if (!_isspace(data[i]))
|
|
1109
1216
|
return 0;
|
|
1110
1217
|
|
|
1111
1218
|
i++;
|
|
@@ -1116,7 +1223,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
|
|
|
1116
1223
|
|
|
1117
1224
|
/* is_atxheader • returns whether the line is a hash-prefixed header */
|
|
1118
1225
|
static int
|
|
1119
|
-
is_atxheader(struct
|
|
1226
|
+
is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1120
1227
|
{
|
|
1121
1228
|
if (data[0] != '#')
|
|
1122
1229
|
return 0;
|
|
@@ -1127,7 +1234,7 @@ is_atxheader(struct render *rndr, char *data, size_t size)
|
|
|
1127
1234
|
while (level < size && level < 6 && data[level] == '#')
|
|
1128
1235
|
level++;
|
|
1129
1236
|
|
|
1130
|
-
if (level < size && data[level] != ' '
|
|
1237
|
+
if (level < size && data[level] != ' ')
|
|
1131
1238
|
return 0;
|
|
1132
1239
|
}
|
|
1133
1240
|
|
|
@@ -1136,92 +1243,126 @@ is_atxheader(struct render *rndr, char *data, size_t size)
|
|
|
1136
1243
|
|
|
1137
1244
|
/* is_headerline • returns whether the line is a setext-style hdr underline */
|
|
1138
1245
|
static int
|
|
1139
|
-
is_headerline(
|
|
1246
|
+
is_headerline(uint8_t *data, size_t size)
|
|
1140
1247
|
{
|
|
1141
1248
|
size_t i = 0;
|
|
1142
1249
|
|
|
1143
1250
|
/* test of level 1 header */
|
|
1144
1251
|
if (data[i] == '=') {
|
|
1145
|
-
for (i = 1; i < size && data[i] == '='; i
|
|
1146
|
-
while (i < size &&
|
|
1252
|
+
for (i = 1; i < size && data[i] == '='; i++);
|
|
1253
|
+
while (i < size && data[i] == ' ') i++;
|
|
1147
1254
|
return (i >= size || data[i] == '\n') ? 1 : 0; }
|
|
1148
1255
|
|
|
1149
1256
|
/* test of level 2 header */
|
|
1150
1257
|
if (data[i] == '-') {
|
|
1151
|
-
for (i = 1; i < size && data[i] == '-'; i
|
|
1152
|
-
while (i < size &&
|
|
1258
|
+
for (i = 1; i < size && data[i] == '-'; i++);
|
|
1259
|
+
while (i < size && data[i] == ' ') i++;
|
|
1153
1260
|
return (i >= size || data[i] == '\n') ? 2 : 0; }
|
|
1154
1261
|
|
|
1155
1262
|
return 0;
|
|
1156
1263
|
}
|
|
1157
1264
|
|
|
1265
|
+
static int
|
|
1266
|
+
is_next_headerline(uint8_t *data, size_t size)
|
|
1267
|
+
{
|
|
1268
|
+
size_t i = 0;
|
|
1269
|
+
|
|
1270
|
+
while (i < size && data[i] != '\n')
|
|
1271
|
+
i++;
|
|
1272
|
+
|
|
1273
|
+
if (++i >= size)
|
|
1274
|
+
return 0;
|
|
1275
|
+
|
|
1276
|
+
return is_headerline(data + i, size - i);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1158
1279
|
/* prefix_quote • returns blockquote prefix length */
|
|
1159
1280
|
static size_t
|
|
1160
|
-
prefix_quote(
|
|
1281
|
+
prefix_quote(uint8_t *data, size_t size)
|
|
1161
1282
|
{
|
|
1162
1283
|
size_t i = 0;
|
|
1163
|
-
if (i < size && data[i] == ' ') i
|
|
1164
|
-
if (i < size && data[i] == ' ') i
|
|
1165
|
-
if (i < size && data[i] == ' ') i
|
|
1284
|
+
if (i < size && data[i] == ' ') i++;
|
|
1285
|
+
if (i < size && data[i] == ' ') i++;
|
|
1286
|
+
if (i < size && data[i] == ' ') i++;
|
|
1287
|
+
|
|
1166
1288
|
if (i < size && data[i] == '>') {
|
|
1167
|
-
if (i + 1 < size &&
|
|
1289
|
+
if (i + 1 < size && data[i + 1] == ' ')
|
|
1168
1290
|
return i + 2;
|
|
1169
|
-
|
|
1170
|
-
|
|
1291
|
+
|
|
1292
|
+
return i + 1;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
return 0;
|
|
1171
1296
|
}
|
|
1172
1297
|
|
|
1173
1298
|
/* prefix_code • returns prefix length for block code*/
|
|
1174
1299
|
static size_t
|
|
1175
|
-
prefix_code(
|
|
1300
|
+
prefix_code(uint8_t *data, size_t size)
|
|
1176
1301
|
{
|
|
1177
|
-
if (size > 0 && data[0] == '\t') return 1;
|
|
1178
1302
|
if (size > 3 && data[0] == ' ' && data[1] == ' '
|
|
1179
|
-
|
|
1303
|
+
&& data[2] == ' ' && data[3] == ' ') return 4;
|
|
1304
|
+
|
|
1180
1305
|
return 0;
|
|
1181
1306
|
}
|
|
1182
1307
|
|
|
1183
1308
|
/* prefix_oli • returns ordered list item prefix */
|
|
1184
1309
|
static size_t
|
|
1185
|
-
prefix_oli(
|
|
1310
|
+
prefix_oli(uint8_t *data, size_t size)
|
|
1186
1311
|
{
|
|
1187
1312
|
size_t i = 0;
|
|
1188
|
-
|
|
1189
|
-
if (i < size && data[i] == ' ') i
|
|
1190
|
-
if (i < size && data[i] == ' ') i
|
|
1191
|
-
if (i
|
|
1192
|
-
|
|
1193
|
-
if (i
|
|
1194
|
-
|
|
1313
|
+
|
|
1314
|
+
if (i < size && data[i] == ' ') i++;
|
|
1315
|
+
if (i < size && data[i] == ' ') i++;
|
|
1316
|
+
if (i < size && data[i] == ' ') i++;
|
|
1317
|
+
|
|
1318
|
+
if (i >= size || data[i] < '0' || data[i] > '9')
|
|
1319
|
+
return 0;
|
|
1320
|
+
|
|
1321
|
+
while (i < size && data[i] >= '0' && data[i] <= '9')
|
|
1322
|
+
i++;
|
|
1323
|
+
|
|
1324
|
+
if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ')
|
|
1325
|
+
return 0;
|
|
1326
|
+
|
|
1327
|
+
if (is_next_headerline(data + i, size - i))
|
|
1328
|
+
return 0;
|
|
1329
|
+
|
|
1195
1330
|
return i + 2;
|
|
1196
1331
|
}
|
|
1197
1332
|
|
|
1198
1333
|
/* prefix_uli • returns ordered list item prefix */
|
|
1199
1334
|
static size_t
|
|
1200
|
-
prefix_uli(
|
|
1335
|
+
prefix_uli(uint8_t *data, size_t size)
|
|
1201
1336
|
{
|
|
1202
1337
|
size_t i = 0;
|
|
1203
|
-
|
|
1204
|
-
if (i < size && data[i] == ' ') i
|
|
1205
|
-
if (i < size && data[i] == ' ') i
|
|
1206
|
-
if (i
|
|
1207
|
-
|
|
1208
|
-
|
|
1338
|
+
|
|
1339
|
+
if (i < size && data[i] == ' ') i++;
|
|
1340
|
+
if (i < size && data[i] == ' ') i++;
|
|
1341
|
+
if (i < size && data[i] == ' ') i++;
|
|
1342
|
+
|
|
1343
|
+
if (i + 1 >= size ||
|
|
1344
|
+
(data[i] != '*' && data[i] != '+' && data[i] != '-') ||
|
|
1345
|
+
data[i + 1] != ' ')
|
|
1209
1346
|
return 0;
|
|
1347
|
+
|
|
1348
|
+
if (is_next_headerline(data + i, size - i))
|
|
1349
|
+
return 0;
|
|
1350
|
+
|
|
1210
1351
|
return i + 2;
|
|
1211
1352
|
}
|
|
1212
1353
|
|
|
1213
1354
|
|
|
1214
|
-
/* parse_block • parsing of one block, returning next
|
|
1215
|
-
static void parse_block(struct buf *ob, struct
|
|
1216
|
-
|
|
1355
|
+
/* parse_block • parsing of one block, returning next uint8_t to parse */
|
|
1356
|
+
static void parse_block(struct buf *ob, struct sd_markdown *rndr,
|
|
1357
|
+
uint8_t *data, size_t size);
|
|
1217
1358
|
|
|
1218
1359
|
|
|
1219
1360
|
/* parse_blockquote • handles parsing of a blockquote fragment */
|
|
1220
1361
|
static size_t
|
|
1221
|
-
parse_blockquote(struct buf *ob, struct
|
|
1362
|
+
parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1222
1363
|
{
|
|
1223
1364
|
size_t beg, end = 0, pre, work_size = 0;
|
|
1224
|
-
|
|
1365
|
+
uint8_t *work_data = 0;
|
|
1225
1366
|
struct buf *out = 0;
|
|
1226
1367
|
|
|
1227
1368
|
out = rndr_newbuf(rndr, BUFFER_BLOCK);
|
|
@@ -1252,22 +1393,22 @@ parse_blockquote(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1252
1393
|
}
|
|
1253
1394
|
|
|
1254
1395
|
parse_block(out, rndr, work_data, work_size);
|
|
1255
|
-
if (rndr->
|
|
1256
|
-
rndr->
|
|
1396
|
+
if (rndr->cb.blockquote)
|
|
1397
|
+
rndr->cb.blockquote(ob, out, rndr->opaque);
|
|
1257
1398
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1258
1399
|
return end;
|
|
1259
1400
|
}
|
|
1260
1401
|
|
|
1261
1402
|
static size_t
|
|
1262
|
-
parse_htmlblock(struct buf *ob, struct
|
|
1403
|
+
parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);
|
|
1263
1404
|
|
|
1264
1405
|
/* parse_blockquote • handles parsing of a regular paragraph */
|
|
1265
1406
|
static size_t
|
|
1266
|
-
parse_paragraph(struct buf *ob, struct
|
|
1407
|
+
parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1267
1408
|
{
|
|
1268
1409
|
size_t i = 0, end = 0;
|
|
1269
1410
|
int level = 0;
|
|
1270
|
-
struct buf work = { data, 0, 0, 0
|
|
1411
|
+
struct buf work = { data, 0, 0, 0 };
|
|
1271
1412
|
|
|
1272
1413
|
while (i < size) {
|
|
1273
1414
|
for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
|
|
@@ -1276,7 +1417,7 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1276
1417
|
break;
|
|
1277
1418
|
|
|
1278
1419
|
if (rndr->ext_flags & MKDEXT_LAX_HTML_BLOCKS) {
|
|
1279
|
-
if (data[i] == '<' && rndr->
|
|
1420
|
+
if (data[i] == '<' && rndr->cb.blockhtml && parse_htmlblock(ob, rndr, data + i, size - i, 0)) {
|
|
1280
1421
|
end = i;
|
|
1281
1422
|
break;
|
|
1282
1423
|
}
|
|
@@ -1297,8 +1438,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1297
1438
|
if (!level) {
|
|
1298
1439
|
struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
|
|
1299
1440
|
parse_inline(tmp, rndr, work.data, work.size);
|
|
1300
|
-
if (rndr->
|
|
1301
|
-
rndr->
|
|
1441
|
+
if (rndr->cb.paragraph)
|
|
1442
|
+
rndr->cb.paragraph(ob, tmp, rndr->opaque);
|
|
1302
1443
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1303
1444
|
} else {
|
|
1304
1445
|
struct buf *header_work;
|
|
@@ -1319,8 +1460,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1319
1460
|
struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
|
|
1320
1461
|
parse_inline(tmp, rndr, work.data, work.size);
|
|
1321
1462
|
|
|
1322
|
-
if (rndr->
|
|
1323
|
-
rndr->
|
|
1463
|
+
if (rndr->cb.paragraph)
|
|
1464
|
+
rndr->cb.paragraph(ob, tmp, rndr->opaque);
|
|
1324
1465
|
|
|
1325
1466
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1326
1467
|
work.data += beg;
|
|
@@ -1332,8 +1473,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1332
1473
|
header_work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
1333
1474
|
parse_inline(header_work, rndr, work.data, work.size);
|
|
1334
1475
|
|
|
1335
|
-
if (rndr->
|
|
1336
|
-
rndr->
|
|
1476
|
+
if (rndr->cb.header)
|
|
1477
|
+
rndr->cb.header(ob, header_work, (int)level, rndr->opaque);
|
|
1337
1478
|
|
|
1338
1479
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1339
1480
|
}
|
|
@@ -1343,11 +1484,11 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1343
1484
|
|
|
1344
1485
|
/* parse_fencedcode • handles parsing of a block-level code fragment */
|
|
1345
1486
|
static size_t
|
|
1346
|
-
parse_fencedcode(struct buf *ob, struct
|
|
1487
|
+
parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1347
1488
|
{
|
|
1348
1489
|
size_t beg, end;
|
|
1349
1490
|
struct buf *work = 0;
|
|
1350
|
-
struct buf lang = { 0, 0, 0, 0
|
|
1491
|
+
struct buf lang = { 0, 0, 0, 0 };
|
|
1351
1492
|
|
|
1352
1493
|
beg = is_codefence(data, size, &lang);
|
|
1353
1494
|
if (beg == 0) return 0;
|
|
@@ -1363,7 +1504,7 @@ parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1363
1504
|
break;
|
|
1364
1505
|
}
|
|
1365
1506
|
|
|
1366
|
-
for (end = beg + 1; end < size && data[end - 1] != '\n'; end
|
|
1507
|
+
for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
|
|
1367
1508
|
|
|
1368
1509
|
if (beg < end) {
|
|
1369
1510
|
/* verbatim copy to the working buffer,
|
|
@@ -1378,15 +1519,15 @@ parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1378
1519
|
if (work->size && work->data[work->size - 1] != '\n')
|
|
1379
1520
|
bufputc(work, '\n');
|
|
1380
1521
|
|
|
1381
|
-
if (rndr->
|
|
1382
|
-
rndr->
|
|
1522
|
+
if (rndr->cb.blockcode)
|
|
1523
|
+
rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);
|
|
1383
1524
|
|
|
1384
1525
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1385
1526
|
return beg;
|
|
1386
1527
|
}
|
|
1387
1528
|
|
|
1388
1529
|
static size_t
|
|
1389
|
-
parse_blockcode(struct buf *ob, struct
|
|
1530
|
+
parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1390
1531
|
{
|
|
1391
1532
|
size_t beg, end, pre;
|
|
1392
1533
|
struct buf *work = 0;
|
|
@@ -1419,8 +1560,8 @@ parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1419
1560
|
|
|
1420
1561
|
bufputc(work, '\n');
|
|
1421
1562
|
|
|
1422
|
-
if (rndr->
|
|
1423
|
-
rndr->
|
|
1563
|
+
if (rndr->cb.blockcode)
|
|
1564
|
+
rndr->cb.blockcode(ob, work, NULL, rndr->opaque);
|
|
1424
1565
|
|
|
1425
1566
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1426
1567
|
return beg;
|
|
@@ -1429,11 +1570,12 @@ parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1429
1570
|
/* parse_listitem • parsing of a single list item */
|
|
1430
1571
|
/* assuming initial prefix is already removed */
|
|
1431
1572
|
static size_t
|
|
1432
|
-
parse_listitem(struct buf *ob, struct
|
|
1573
|
+
parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)
|
|
1433
1574
|
{
|
|
1434
1575
|
struct buf *work = 0, *inter = 0;
|
|
1435
1576
|
size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
|
|
1436
1577
|
int in_empty = 0, has_inside_empty = 0;
|
|
1578
|
+
int has_next_uli, has_next_oli;
|
|
1437
1579
|
|
|
1438
1580
|
/* keeping track of the first indentation prefix */
|
|
1439
1581
|
while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
|
|
@@ -1479,12 +1621,21 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
|
|
|
1479
1621
|
i++;
|
|
1480
1622
|
|
|
1481
1623
|
pre = i;
|
|
1482
|
-
|
|
1624
|
+
|
|
1625
|
+
has_next_uli = prefix_uli(data + beg + i, end - beg - i);
|
|
1626
|
+
has_next_oli = prefix_oli(data + beg + i, end - beg - i);
|
|
1627
|
+
|
|
1628
|
+
/* checking for ul/ol switch */
|
|
1629
|
+
if (in_empty && (
|
|
1630
|
+
((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
|
|
1631
|
+
(!(*flags & MKD_LIST_ORDERED) && has_next_oli)
|
|
1632
|
+
)){
|
|
1633
|
+
*flags |= MKD_LI_END;
|
|
1634
|
+
break; /* the following item must have same list type */
|
|
1635
|
+
}
|
|
1483
1636
|
|
|
1484
1637
|
/* checking for a new item */
|
|
1485
|
-
if ((
|
|
1486
|
-
!is_hrule(data + beg + i, end - beg - i)) ||
|
|
1487
|
-
prefix_oli(data + beg + i, end - beg - i)) {
|
|
1638
|
+
if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
|
|
1488
1639
|
if (in_empty)
|
|
1489
1640
|
has_inside_empty = 1;
|
|
1490
1641
|
|
|
@@ -1495,7 +1646,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
|
|
|
1495
1646
|
sublist = work->size;
|
|
1496
1647
|
}
|
|
1497
1648
|
/* joining only indented stuff after empty lines */
|
|
1498
|
-
else if (in_empty && i < 4
|
|
1649
|
+
else if (in_empty && i < 4) {
|
|
1499
1650
|
*flags |= MKD_LI_END;
|
|
1500
1651
|
break;
|
|
1501
1652
|
}
|
|
@@ -1519,7 +1670,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
|
|
|
1519
1670
|
/* intermediate render of block li */
|
|
1520
1671
|
if (sublist && sublist < work->size) {
|
|
1521
1672
|
parse_block(inter, rndr, work->data, sublist);
|
|
1522
|
-
parse_block(inter, rndr, work->data + sublist, work->size - sublist);
|
|
1673
|
+
parse_block(inter, rndr, work->data + sublist, work->size - sublist);
|
|
1523
1674
|
}
|
|
1524
1675
|
else
|
|
1525
1676
|
parse_block(inter, rndr, work->data, work->size);
|
|
@@ -1534,8 +1685,8 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
|
|
|
1534
1685
|
}
|
|
1535
1686
|
|
|
1536
1687
|
/* render of li itself */
|
|
1537
|
-
if (rndr->
|
|
1538
|
-
rndr->
|
|
1688
|
+
if (rndr->cb.listitem)
|
|
1689
|
+
rndr->cb.listitem(ob, inter, *flags, rndr->opaque);
|
|
1539
1690
|
|
|
1540
1691
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1541
1692
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
@@ -1545,7 +1696,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
|
|
|
1545
1696
|
|
|
1546
1697
|
/* parse_list • parsing ordered or unordered list block */
|
|
1547
1698
|
static size_t
|
|
1548
|
-
parse_list(struct buf *ob, struct
|
|
1699
|
+
parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)
|
|
1549
1700
|
{
|
|
1550
1701
|
struct buf *work = 0;
|
|
1551
1702
|
size_t i = 0, j;
|
|
@@ -1560,15 +1711,15 @@ parse_list(struct buf *ob, struct render *rndr, char *data, size_t size, int fla
|
|
|
1560
1711
|
break;
|
|
1561
1712
|
}
|
|
1562
1713
|
|
|
1563
|
-
if (rndr->
|
|
1564
|
-
rndr->
|
|
1714
|
+
if (rndr->cb.list)
|
|
1715
|
+
rndr->cb.list(ob, work, flags, rndr->opaque);
|
|
1565
1716
|
rndr_popbuf(rndr, BUFFER_BLOCK);
|
|
1566
1717
|
return i;
|
|
1567
1718
|
}
|
|
1568
1719
|
|
|
1569
1720
|
/* parse_atxheader • parsing of atx-style headers */
|
|
1570
1721
|
static size_t
|
|
1571
|
-
parse_atxheader(struct buf *ob, struct
|
|
1722
|
+
parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1572
1723
|
{
|
|
1573
1724
|
size_t level = 0;
|
|
1574
1725
|
size_t i, end, skip;
|
|
@@ -1576,7 +1727,7 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1576
1727
|
while (level < size && level < 6 && data[level] == '#')
|
|
1577
1728
|
level++;
|
|
1578
1729
|
|
|
1579
|
-
for (i = level; i < size &&
|
|
1730
|
+
for (i = level; i < size && data[i] == ' '; i++);
|
|
1580
1731
|
|
|
1581
1732
|
for (end = i; end < size && data[end] != '\n'; end++);
|
|
1582
1733
|
skip = end;
|
|
@@ -1584,7 +1735,7 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1584
1735
|
while (end && data[end - 1] == '#')
|
|
1585
1736
|
end--;
|
|
1586
1737
|
|
|
1587
|
-
while (end &&
|
|
1738
|
+
while (end && data[end - 1] == ' ')
|
|
1588
1739
|
end--;
|
|
1589
1740
|
|
|
1590
1741
|
if (end > i) {
|
|
@@ -1592,8 +1743,8 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1592
1743
|
|
|
1593
1744
|
parse_inline(work, rndr, data + i, end - i);
|
|
1594
1745
|
|
|
1595
|
-
if (rndr->
|
|
1596
|
-
rndr->
|
|
1746
|
+
if (rndr->cb.header)
|
|
1747
|
+
rndr->cb.header(ob, work, (int)level, rndr->opaque);
|
|
1597
1748
|
|
|
1598
1749
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1599
1750
|
}
|
|
@@ -1605,20 +1756,18 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1605
1756
|
/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
|
|
1606
1757
|
/* returns the length on match, 0 otherwise */
|
|
1607
1758
|
static size_t
|
|
1608
|
-
htmlblock_end(
|
|
1759
|
+
htmlblock_end(const char *tag, size_t tag_len, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1609
1760
|
{
|
|
1610
1761
|
size_t i, w;
|
|
1611
1762
|
|
|
1612
|
-
/* assuming data[0] == '<' && data[1] == '/' already tested */
|
|
1613
|
-
|
|
1614
1763
|
/* checking if tag is a match */
|
|
1615
|
-
if (
|
|
1616
|
-
|
|
1617
|
-
|
|
1764
|
+
if (tag_len + 3 >= size ||
|
|
1765
|
+
strncasecmp((char *)data + 2, tag, tag_len) != 0 ||
|
|
1766
|
+
data[tag_len + 2] != '>')
|
|
1618
1767
|
return 0;
|
|
1619
1768
|
|
|
1620
1769
|
/* checking white lines */
|
|
1621
|
-
i =
|
|
1770
|
+
i = tag_len + 3;
|
|
1622
1771
|
w = 0;
|
|
1623
1772
|
if (i < size && (w = is_empty(data + i, size - i)) == 0)
|
|
1624
1773
|
return 0; /* non-blank after tag */
|
|
@@ -1639,16 +1788,23 @@ htmlblock_end(struct html_tag *tag, struct render *rndr, char *data, size_t size
|
|
|
1639
1788
|
|
|
1640
1789
|
/* parse_htmlblock • parsing of inline HTML block */
|
|
1641
1790
|
static size_t
|
|
1642
|
-
parse_htmlblock(struct buf *ob, struct
|
|
1791
|
+
parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)
|
|
1643
1792
|
{
|
|
1644
1793
|
size_t i, j = 0;
|
|
1645
|
-
|
|
1794
|
+
const char *curtag = NULL;
|
|
1646
1795
|
int found;
|
|
1647
|
-
struct buf work = { data, 0, 0, 0
|
|
1796
|
+
struct buf work = { data, 0, 0, 0 };
|
|
1648
1797
|
|
|
1649
1798
|
/* identification of the opening tag */
|
|
1650
|
-
if (size < 2 || data[0] != '<')
|
|
1651
|
-
|
|
1799
|
+
if (size < 2 || data[0] != '<')
|
|
1800
|
+
return 0;
|
|
1801
|
+
|
|
1802
|
+
i = 1;
|
|
1803
|
+
while (i < size && data[i] != '>' && data[i] != ' ')
|
|
1804
|
+
i++;
|
|
1805
|
+
|
|
1806
|
+
if (i < size)
|
|
1807
|
+
curtag = find_block_tag((char *)data + 1, i - 1);
|
|
1652
1808
|
|
|
1653
1809
|
/* handling of special cases */
|
|
1654
1810
|
if (!curtag) {
|
|
@@ -1667,28 +1823,28 @@ parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, in
|
|
|
1667
1823
|
|
|
1668
1824
|
if (j) {
|
|
1669
1825
|
work.size = i + j;
|
|
1670
|
-
if (do_render && rndr->
|
|
1671
|
-
rndr->
|
|
1826
|
+
if (do_render && rndr->cb.blockhtml)
|
|
1827
|
+
rndr->cb.blockhtml(ob, &work, rndr->opaque);
|
|
1672
1828
|
return work.size;
|
|
1673
|
-
}
|
|
1829
|
+
}
|
|
1674
1830
|
}
|
|
1675
1831
|
|
|
1676
1832
|
/* HR, which is the only self-closing block tag considered */
|
|
1677
1833
|
if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
|
|
1678
1834
|
i = 3;
|
|
1679
1835
|
while (i < size && data[i] != '>')
|
|
1680
|
-
i
|
|
1836
|
+
i++;
|
|
1681
1837
|
|
|
1682
1838
|
if (i + 1 < size) {
|
|
1683
|
-
i
|
|
1839
|
+
i++;
|
|
1684
1840
|
j = is_empty(data + i, size - i);
|
|
1685
1841
|
if (j) {
|
|
1686
1842
|
work.size = i + j;
|
|
1687
|
-
if (do_render && rndr->
|
|
1688
|
-
rndr->
|
|
1843
|
+
if (do_render && rndr->cb.blockhtml)
|
|
1844
|
+
rndr->cb.blockhtml(ob, &work, rndr->opaque);
|
|
1689
1845
|
return work.size;
|
|
1690
1846
|
}
|
|
1691
|
-
}
|
|
1847
|
+
}
|
|
1692
1848
|
}
|
|
1693
1849
|
|
|
1694
1850
|
/* no special case recognised */
|
|
@@ -1702,42 +1858,53 @@ parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, in
|
|
|
1702
1858
|
|
|
1703
1859
|
/* if not found, trying a second pass looking for indented match */
|
|
1704
1860
|
/* but not if tag is "ins" or "del" (following original Markdown.pl) */
|
|
1705
|
-
if (curtag !=
|
|
1861
|
+
if (strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {
|
|
1862
|
+
size_t tag_size = strlen(curtag);
|
|
1706
1863
|
i = 1;
|
|
1707
1864
|
while (i < size) {
|
|
1708
1865
|
i++;
|
|
1709
1866
|
while (i < size && !(data[i - 1] == '<' && data[i] == '/'))
|
|
1710
1867
|
i++;
|
|
1711
1868
|
|
|
1712
|
-
if (i + 2 +
|
|
1869
|
+
if (i + 2 + tag_size >= size)
|
|
1713
1870
|
break;
|
|
1714
1871
|
|
|
1715
|
-
j = htmlblock_end(curtag, rndr, data + i - 1, size - i + 1);
|
|
1872
|
+
j = htmlblock_end(curtag, tag_size, rndr, data + i - 1, size - i + 1);
|
|
1716
1873
|
|
|
1717
1874
|
if (j) {
|
|
1718
1875
|
i += j - 1;
|
|
1719
1876
|
found = 1;
|
|
1720
1877
|
break;
|
|
1721
1878
|
}
|
|
1722
|
-
}
|
|
1879
|
+
}
|
|
1723
1880
|
}
|
|
1724
1881
|
|
|
1725
1882
|
if (!found) return 0;
|
|
1726
1883
|
|
|
1727
1884
|
/* the end of the block has been found */
|
|
1728
1885
|
work.size = i;
|
|
1729
|
-
if (do_render && rndr->
|
|
1730
|
-
rndr->
|
|
1886
|
+
if (do_render && rndr->cb.blockhtml)
|
|
1887
|
+
rndr->cb.blockhtml(ob, &work, rndr->opaque);
|
|
1731
1888
|
|
|
1732
1889
|
return i;
|
|
1733
1890
|
}
|
|
1734
1891
|
|
|
1735
1892
|
static void
|
|
1736
|
-
parse_table_row(
|
|
1893
|
+
parse_table_row(
|
|
1894
|
+
struct buf *ob,
|
|
1895
|
+
struct sd_markdown *rndr,
|
|
1896
|
+
uint8_t *data,
|
|
1897
|
+
size_t size,
|
|
1898
|
+
size_t columns,
|
|
1899
|
+
int *col_data,
|
|
1900
|
+
int header_flag)
|
|
1737
1901
|
{
|
|
1738
1902
|
size_t i = 0, col;
|
|
1739
1903
|
struct buf *row_work = 0;
|
|
1740
1904
|
|
|
1905
|
+
if (!rndr->cb.table_cell || !rndr->cb.table_row)
|
|
1906
|
+
return;
|
|
1907
|
+
|
|
1741
1908
|
row_work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
1742
1909
|
|
|
1743
1910
|
if (i < size && data[i] == '|')
|
|
@@ -1749,7 +1916,7 @@ parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, si
|
|
|
1749
1916
|
|
|
1750
1917
|
cell_work = rndr_newbuf(rndr, BUFFER_SPAN);
|
|
1751
1918
|
|
|
1752
|
-
while (i < size &&
|
|
1919
|
+
while (i < size && _isspace(data[i]))
|
|
1753
1920
|
i++;
|
|
1754
1921
|
|
|
1755
1922
|
cell_start = i;
|
|
@@ -1759,31 +1926,34 @@ parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, si
|
|
|
1759
1926
|
|
|
1760
1927
|
cell_end = i - 1;
|
|
1761
1928
|
|
|
1762
|
-
while (cell_end > cell_start &&
|
|
1929
|
+
while (cell_end > cell_start && _isspace(data[cell_end]))
|
|
1763
1930
|
cell_end--;
|
|
1764
1931
|
|
|
1765
1932
|
parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
|
|
1766
|
-
|
|
1767
|
-
rndr->make.table_cell(row_work, cell_work, col_data ? col_data[col] : 0, rndr->make.opaque);
|
|
1933
|
+
rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);
|
|
1768
1934
|
|
|
1769
1935
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1770
1936
|
i++;
|
|
1771
1937
|
}
|
|
1772
1938
|
|
|
1773
1939
|
for (; col < columns; ++col) {
|
|
1774
|
-
struct buf empty_cell = {0, 0, 0, 0
|
|
1775
|
-
|
|
1776
|
-
rndr->make.table_cell(row_work, &empty_cell, col_data ? col_data[col] : 0, rndr->make.opaque);
|
|
1940
|
+
struct buf empty_cell = { 0, 0, 0, 0 };
|
|
1941
|
+
rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);
|
|
1777
1942
|
}
|
|
1778
1943
|
|
|
1779
|
-
|
|
1780
|
-
rndr->make.table_row(ob, row_work, rndr->make.opaque);
|
|
1944
|
+
rndr->cb.table_row(ob, row_work, rndr->opaque);
|
|
1781
1945
|
|
|
1782
1946
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
|
1783
1947
|
}
|
|
1784
1948
|
|
|
1785
1949
|
static size_t
|
|
1786
|
-
parse_table_header(
|
|
1950
|
+
parse_table_header(
|
|
1951
|
+
struct buf *ob,
|
|
1952
|
+
struct sd_markdown *rndr,
|
|
1953
|
+
uint8_t *data,
|
|
1954
|
+
size_t size,
|
|
1955
|
+
size_t *columns,
|
|
1956
|
+
int **column_data)
|
|
1787
1957
|
{
|
|
1788
1958
|
int pipes;
|
|
1789
1959
|
size_t i = 0, col, header_end, under_end;
|
|
@@ -1819,7 +1989,7 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
|
|
|
1819
1989
|
for (col = 0; col < *columns && i < under_end; ++col) {
|
|
1820
1990
|
size_t dashes = 0;
|
|
1821
1991
|
|
|
1822
|
-
while (i < under_end &&
|
|
1992
|
+
while (i < under_end && data[i] == ' ')
|
|
1823
1993
|
i++;
|
|
1824
1994
|
|
|
1825
1995
|
if (data[i] == ':') {
|
|
@@ -1836,7 +2006,7 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
|
|
|
1836
2006
|
dashes++;
|
|
1837
2007
|
}
|
|
1838
2008
|
|
|
1839
|
-
while (i < under_end &&
|
|
2009
|
+
while (i < under_end && data[i] == ' ')
|
|
1840
2010
|
i++;
|
|
1841
2011
|
|
|
1842
2012
|
if (i < under_end && data[i] != '|')
|
|
@@ -1851,12 +2021,23 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
|
|
|
1851
2021
|
if (col < *columns)
|
|
1852
2022
|
return 0;
|
|
1853
2023
|
|
|
1854
|
-
parse_table_row(
|
|
2024
|
+
parse_table_row(
|
|
2025
|
+
ob, rndr, data,
|
|
2026
|
+
header_end,
|
|
2027
|
+
*columns,
|
|
2028
|
+
*column_data,
|
|
2029
|
+
MKD_TABLE_HEADER
|
|
2030
|
+
);
|
|
2031
|
+
|
|
1855
2032
|
return under_end + 1;
|
|
1856
2033
|
}
|
|
1857
2034
|
|
|
1858
2035
|
static size_t
|
|
1859
|
-
parse_table(
|
|
2036
|
+
parse_table(
|
|
2037
|
+
struct buf *ob,
|
|
2038
|
+
struct sd_markdown *rndr,
|
|
2039
|
+
uint8_t *data,
|
|
2040
|
+
size_t size)
|
|
1860
2041
|
{
|
|
1861
2042
|
size_t i;
|
|
1862
2043
|
|
|
@@ -1887,12 +2068,20 @@ parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1887
2068
|
break;
|
|
1888
2069
|
}
|
|
1889
2070
|
|
|
1890
|
-
parse_table_row(
|
|
2071
|
+
parse_table_row(
|
|
2072
|
+
body_work,
|
|
2073
|
+
rndr,
|
|
2074
|
+
data + row_start,
|
|
2075
|
+
i - row_start,
|
|
2076
|
+
columns,
|
|
2077
|
+
col_data, 0
|
|
2078
|
+
);
|
|
2079
|
+
|
|
1891
2080
|
i++;
|
|
1892
2081
|
}
|
|
1893
2082
|
|
|
1894
|
-
if (rndr->
|
|
1895
|
-
rndr->
|
|
2083
|
+
if (rndr->cb.table)
|
|
2084
|
+
rndr->cb.table(ob, header_work, body_work, rndr->opaque);
|
|
1896
2085
|
}
|
|
1897
2086
|
|
|
1898
2087
|
free(col_data);
|
|
@@ -1901,16 +2090,16 @@ parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1901
2090
|
return i;
|
|
1902
2091
|
}
|
|
1903
2092
|
|
|
1904
|
-
/* parse_block • parsing of one block, returning next
|
|
2093
|
+
/* parse_block • parsing of one block, returning next uint8_t to parse */
|
|
1905
2094
|
static void
|
|
1906
|
-
parse_block(struct buf *ob, struct
|
|
2095
|
+
parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
|
1907
2096
|
{
|
|
1908
2097
|
size_t beg, end, i;
|
|
1909
|
-
|
|
2098
|
+
uint8_t *txt_data;
|
|
1910
2099
|
beg = 0;
|
|
1911
2100
|
|
|
1912
2101
|
if (rndr->work_bufs[BUFFER_SPAN].size +
|
|
1913
|
-
rndr->work_bufs[BUFFER_BLOCK].size >
|
|
2102
|
+
rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
|
|
1914
2103
|
return;
|
|
1915
2104
|
|
|
1916
2105
|
while (beg < size) {
|
|
@@ -1920,7 +2109,7 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1920
2109
|
if (is_atxheader(rndr, txt_data, end))
|
|
1921
2110
|
beg += parse_atxheader(ob, rndr, txt_data, end);
|
|
1922
2111
|
|
|
1923
|
-
else if (data[beg] == '<' && rndr->
|
|
2112
|
+
else if (data[beg] == '<' && rndr->cb.blockhtml &&
|
|
1924
2113
|
(i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)
|
|
1925
2114
|
beg += i;
|
|
1926
2115
|
|
|
@@ -1928,8 +2117,8 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1928
2117
|
beg += i;
|
|
1929
2118
|
|
|
1930
2119
|
else if (is_hrule(txt_data, end)) {
|
|
1931
|
-
if (rndr->
|
|
1932
|
-
rndr->
|
|
2120
|
+
if (rndr->cb.hrule)
|
|
2121
|
+
rndr->cb.hrule(ob, rndr->opaque);
|
|
1933
2122
|
|
|
1934
2123
|
while (beg < size && data[beg] != '\n')
|
|
1935
2124
|
beg++;
|
|
@@ -1970,7 +2159,7 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
|
1970
2159
|
|
|
1971
2160
|
/* is_ref • returns whether a line is a reference or not */
|
|
1972
2161
|
static int
|
|
1973
|
-
is_ref(
|
|
2162
|
+
is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
|
|
1974
2163
|
{
|
|
1975
2164
|
/* int n; */
|
|
1976
2165
|
size_t i = 0;
|
|
@@ -1978,8 +2167,6 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
|
|
|
1978
2167
|
size_t link_offset, link_end;
|
|
1979
2168
|
size_t title_offset, title_end;
|
|
1980
2169
|
size_t line_end;
|
|
1981
|
-
struct link_ref *lr;
|
|
1982
|
-
/* struct buf id = { 0, 0, 0, 0, 0 }; / * volatile buf for id search */
|
|
1983
2170
|
|
|
1984
2171
|
/* up to 3 optional leading spaces */
|
|
1985
2172
|
if (beg + 3 >= end) return 0;
|
|
@@ -1991,34 +2178,38 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
|
|
|
1991
2178
|
|
|
1992
2179
|
/* id part: anything but a newline between brackets */
|
|
1993
2180
|
if (data[i] != '[') return 0;
|
|
1994
|
-
i
|
|
2181
|
+
i++;
|
|
1995
2182
|
id_offset = i;
|
|
1996
2183
|
while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
|
|
1997
|
-
i
|
|
2184
|
+
i++;
|
|
1998
2185
|
if (i >= end || data[i] != ']') return 0;
|
|
1999
2186
|
id_end = i;
|
|
2000
2187
|
|
|
2001
2188
|
/* spacer: colon (space | tab)* newline? (space | tab)* */
|
|
2002
|
-
i
|
|
2189
|
+
i++;
|
|
2003
2190
|
if (i >= end || data[i] != ':') return 0;
|
|
2004
|
-
i
|
|
2005
|
-
while (i < end &&
|
|
2191
|
+
i++;
|
|
2192
|
+
while (i < end && data[i] == ' ') i++;
|
|
2006
2193
|
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
|
|
2007
|
-
i
|
|
2008
|
-
if (i < end && data[i] == '\r' && data[i - 1] == '\n') i
|
|
2009
|
-
while (i < end &&
|
|
2194
|
+
i++;
|
|
2195
|
+
if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
|
|
2196
|
+
while (i < end && data[i] == ' ') i++;
|
|
2010
2197
|
if (i >= end) return 0;
|
|
2011
2198
|
|
|
2012
2199
|
/* link: whitespace-free sequence, optionally between angle brackets */
|
|
2013
|
-
if (data[i] == '<')
|
|
2200
|
+
if (data[i] == '<')
|
|
2201
|
+
i++;
|
|
2202
|
+
|
|
2014
2203
|
link_offset = i;
|
|
2015
|
-
|
|
2016
|
-
|
|
2204
|
+
|
|
2205
|
+
while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
|
|
2206
|
+
i++;
|
|
2207
|
+
|
|
2017
2208
|
if (data[i - 1] == '>') link_end = i - 1;
|
|
2018
2209
|
else link_end = i;
|
|
2019
2210
|
|
|
2020
2211
|
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
|
|
2021
|
-
while (i < end &&
|
|
2212
|
+
while (i < end && data[i] == ' ') i++;
|
|
2022
2213
|
if (i < end && data[i] != '\n' && data[i] != '\r'
|
|
2023
2214
|
&& data[i] != '\'' && data[i] != '"' && data[i] != '(')
|
|
2024
2215
|
return 0;
|
|
@@ -2031,47 +2222,54 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
|
|
|
2031
2222
|
/* optional (space|tab)* spacer after a newline */
|
|
2032
2223
|
if (line_end) {
|
|
2033
2224
|
i = line_end + 1;
|
|
2034
|
-
while (i < end &&
|
|
2225
|
+
while (i < end && data[i] == ' ') i++; }
|
|
2035
2226
|
|
|
2036
2227
|
/* optional title: any non-newline sequence enclosed in '"()
|
|
2037
2228
|
alone on its line */
|
|
2038
2229
|
title_offset = title_end = 0;
|
|
2039
2230
|
if (i + 1 < end
|
|
2040
2231
|
&& (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
|
|
2041
|
-
i
|
|
2232
|
+
i++;
|
|
2042
2233
|
title_offset = i;
|
|
2043
2234
|
/* looking for EOL */
|
|
2044
|
-
while (i < end && data[i] != '\n' && data[i] != '\r') i
|
|
2235
|
+
while (i < end && data[i] != '\n' && data[i] != '\r') i++;
|
|
2045
2236
|
if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
|
|
2046
2237
|
title_end = i + 1;
|
|
2047
2238
|
else title_end = i;
|
|
2048
2239
|
/* stepping back */
|
|
2049
2240
|
i -= 1;
|
|
2050
|
-
while (i > title_offset &&
|
|
2241
|
+
while (i > title_offset && data[i] == ' ')
|
|
2051
2242
|
i -= 1;
|
|
2052
2243
|
if (i > title_offset
|
|
2053
2244
|
&& (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
|
|
2054
2245
|
line_end = title_end;
|
|
2055
2246
|
title_end = i; } }
|
|
2056
|
-
|
|
2247
|
+
|
|
2248
|
+
if (!line_end)
|
|
2249
|
+
return 0; /* garbage after the link */
|
|
2057
2250
|
|
|
2058
2251
|
/* a valid ref has been found, filling-in return structures */
|
|
2059
|
-
if (last)
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
bufput(
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2252
|
+
if (last)
|
|
2253
|
+
*last = line_end;
|
|
2254
|
+
|
|
2255
|
+
if (refs) {
|
|
2256
|
+
struct link_ref *ref;
|
|
2257
|
+
|
|
2258
|
+
ref = add_link_ref(refs, data + id_offset, id_end - id_offset);
|
|
2259
|
+
|
|
2260
|
+
ref->link = bufnew(link_end - link_offset);
|
|
2261
|
+
bufput(ref->link, data + link_offset, link_end - link_offset);
|
|
2262
|
+
|
|
2263
|
+
if (title_end > title_offset) {
|
|
2264
|
+
ref->title = bufnew(title_end - title_offset);
|
|
2265
|
+
bufput(ref->title, data + title_offset, title_end - title_offset);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
return 1;
|
|
2072
2270
|
}
|
|
2073
2271
|
|
|
2074
|
-
static void expand_tabs(struct buf *ob, const
|
|
2272
|
+
static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)
|
|
2075
2273
|
{
|
|
2076
2274
|
size_t i = 0, tab = 0;
|
|
2077
2275
|
|
|
@@ -2100,132 +2298,153 @@ static void expand_tabs(struct buf *ob, const char *line, size_t size)
|
|
|
2100
2298
|
* EXPORTED FUNCTIONS *
|
|
2101
2299
|
**********************/
|
|
2102
2300
|
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
struct
|
|
2108
|
-
|
|
2109
|
-
|
|
2301
|
+
struct sd_markdown *
|
|
2302
|
+
sd_markdown_new(
|
|
2303
|
+
unsigned int extensions,
|
|
2304
|
+
size_t max_nesting,
|
|
2305
|
+
const struct sd_callbacks *callbacks,
|
|
2306
|
+
void *opaque)
|
|
2307
|
+
{
|
|
2308
|
+
struct sd_markdown *md = NULL;
|
|
2110
2309
|
|
|
2111
|
-
|
|
2112
|
-
if (!rndrer)
|
|
2113
|
-
return;
|
|
2310
|
+
assert(max_nesting > 0 && callbacks);
|
|
2114
2311
|
|
|
2115
|
-
|
|
2116
|
-
if (!
|
|
2117
|
-
return;
|
|
2312
|
+
md = malloc(sizeof(struct sd_markdown));
|
|
2313
|
+
if (!md)
|
|
2314
|
+
return NULL;
|
|
2118
2315
|
|
|
2119
|
-
|
|
2120
|
-
bufgrow(text, ib->size);
|
|
2316
|
+
memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
|
|
2121
2317
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
parr_init(&rndr.work_bufs[BUFFER_BLOCK]);
|
|
2125
|
-
parr_init(&rndr.work_bufs[BUFFER_SPAN]);
|
|
2318
|
+
stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
|
|
2319
|
+
stack_init(&md->work_bufs[BUFFER_SPAN], 8);
|
|
2126
2320
|
|
|
2127
|
-
|
|
2128
|
-
rndr.active_char[i] = 0;
|
|
2321
|
+
memset(md->active_char, 0x0, 256);
|
|
2129
2322
|
|
|
2130
|
-
if (
|
|
2131
|
-
|
|
2132
|
-
|
|
2323
|
+
if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) {
|
|
2324
|
+
md->active_char['*'] = MD_CHAR_EMPHASIS;
|
|
2325
|
+
md->active_char['_'] = MD_CHAR_EMPHASIS;
|
|
2133
2326
|
if (extensions & MKDEXT_STRIKETHROUGH)
|
|
2134
|
-
|
|
2327
|
+
md->active_char['~'] = MD_CHAR_EMPHASIS;
|
|
2135
2328
|
}
|
|
2136
2329
|
|
|
2137
|
-
if (
|
|
2138
|
-
|
|
2330
|
+
if (md->cb.codespan)
|
|
2331
|
+
md->active_char['`'] = MD_CHAR_CODESPAN;
|
|
2139
2332
|
|
|
2140
|
-
if (
|
|
2141
|
-
|
|
2333
|
+
if (md->cb.linebreak)
|
|
2334
|
+
md->active_char['\n'] = MD_CHAR_LINEBREAK;
|
|
2142
2335
|
|
|
2143
|
-
if (
|
|
2144
|
-
|
|
2336
|
+
if (md->cb.image || md->cb.link)
|
|
2337
|
+
md->active_char['['] = MD_CHAR_LINK;
|
|
2145
2338
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2339
|
+
md->active_char['<'] = MD_CHAR_LANGLE;
|
|
2340
|
+
md->active_char['\\'] = MD_CHAR_ESCAPE;
|
|
2341
|
+
md->active_char['&'] = MD_CHAR_ENTITITY;
|
|
2149
2342
|
|
|
2150
2343
|
if (extensions & MKDEXT_AUTOLINK) {
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2344
|
+
md->active_char[':'] = MD_CHAR_AUTOLINK_URL;
|
|
2345
|
+
md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
|
|
2346
|
+
md->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
|
|
2154
2347
|
}
|
|
2155
2348
|
|
|
2349
|
+
if (extensions & MKDEXT_SUPERSCRIPT)
|
|
2350
|
+
md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
|
|
2351
|
+
|
|
2156
2352
|
/* Extension data */
|
|
2157
|
-
|
|
2158
|
-
|
|
2353
|
+
md->ext_flags = extensions;
|
|
2354
|
+
md->opaque = opaque;
|
|
2355
|
+
md->max_nesting = max_nesting;
|
|
2356
|
+
md->in_link_body = 0;
|
|
2357
|
+
|
|
2358
|
+
return md;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
void
|
|
2362
|
+
sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)
|
|
2363
|
+
{
|
|
2364
|
+
static const float MARKDOWN_GROW_FACTOR = 1.4f;
|
|
2365
|
+
|
|
2366
|
+
struct buf *text;
|
|
2367
|
+
size_t beg, end;
|
|
2368
|
+
|
|
2369
|
+
text = bufnew(64);
|
|
2370
|
+
if (!text)
|
|
2371
|
+
return;
|
|
2372
|
+
|
|
2373
|
+
/* Preallocate enough space for our buffer to avoid expanding while copying */
|
|
2374
|
+
bufgrow(text, doc_size);
|
|
2375
|
+
|
|
2376
|
+
/* reset the references table */
|
|
2377
|
+
memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
|
|
2159
2378
|
|
|
2160
2379
|
/* first pass: looking for references, copying everything else */
|
|
2161
2380
|
beg = 0;
|
|
2162
|
-
while (beg <
|
|
2163
|
-
if (is_ref(
|
|
2381
|
+
while (beg < doc_size) /* iterating over lines */
|
|
2382
|
+
if (is_ref(document, beg, doc_size, &end, md->refs))
|
|
2164
2383
|
beg = end;
|
|
2165
2384
|
else { /* skipping to the next line */
|
|
2166
2385
|
end = beg;
|
|
2167
|
-
while (end <
|
|
2168
|
-
end
|
|
2386
|
+
while (end < doc_size && document[end] != '\n' && document[end] != '\r')
|
|
2387
|
+
end++;
|
|
2169
2388
|
|
|
2170
2389
|
/* adding the line body if present */
|
|
2171
2390
|
if (end > beg)
|
|
2172
|
-
expand_tabs(text,
|
|
2391
|
+
expand_tabs(text, document + beg, end - beg);
|
|
2173
2392
|
|
|
2174
|
-
while (end <
|
|
2393
|
+
while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
|
|
2175
2394
|
/* add one \n per newline */
|
|
2176
|
-
if (
|
|
2395
|
+
if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
|
|
2177
2396
|
bufputc(text, '\n');
|
|
2178
|
-
end
|
|
2397
|
+
end++;
|
|
2179
2398
|
}
|
|
2180
2399
|
|
|
2181
2400
|
beg = end;
|
|
2182
2401
|
}
|
|
2183
2402
|
|
|
2184
|
-
/*
|
|
2185
|
-
|
|
2186
|
-
qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort);
|
|
2403
|
+
/* pre-grow the output buffer to minimize allocations */
|
|
2404
|
+
bufgrow(ob, text->size * MARKDOWN_GROW_FACTOR);
|
|
2187
2405
|
|
|
2188
2406
|
/* second pass: actual rendering */
|
|
2189
|
-
if (
|
|
2190
|
-
|
|
2407
|
+
if (md->cb.doc_header)
|
|
2408
|
+
md->cb.doc_header(ob, md->opaque);
|
|
2191
2409
|
|
|
2192
2410
|
if (text->size) {
|
|
2193
2411
|
/* adding a final newline if not already present */
|
|
2194
2412
|
if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
|
|
2195
2413
|
bufputc(text, '\n');
|
|
2196
2414
|
|
|
2197
|
-
parse_block(ob,
|
|
2415
|
+
parse_block(ob, md, text->data, text->size);
|
|
2198
2416
|
}
|
|
2199
2417
|
|
|
2200
|
-
if (
|
|
2201
|
-
|
|
2418
|
+
if (md->cb.doc_footer)
|
|
2419
|
+
md->cb.doc_footer(ob, md->opaque);
|
|
2202
2420
|
|
|
2203
2421
|
/* clean-up */
|
|
2204
2422
|
bufrelease(text);
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
}
|
|
2423
|
+
free_link_refs(md->refs);
|
|
2424
|
+
|
|
2425
|
+
assert(md->work_bufs[BUFFER_SPAN].size == 0);
|
|
2426
|
+
assert(md->work_bufs[BUFFER_BLOCK].size == 0);
|
|
2427
|
+
}
|
|
2211
2428
|
|
|
2212
|
-
|
|
2429
|
+
void
|
|
2430
|
+
sd_markdown_free(struct sd_markdown *md)
|
|
2431
|
+
{
|
|
2432
|
+
size_t i;
|
|
2213
2433
|
|
|
2214
|
-
|
|
2215
|
-
|
|
2434
|
+
for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i)
|
|
2435
|
+
bufrelease(md->work_bufs[BUFFER_SPAN].item[i]);
|
|
2216
2436
|
|
|
2217
|
-
for (i = 0; i < (size_t)
|
|
2218
|
-
bufrelease(
|
|
2437
|
+
for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i)
|
|
2438
|
+
bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);
|
|
2219
2439
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2440
|
+
stack_free(&md->work_bufs[BUFFER_SPAN]);
|
|
2441
|
+
stack_free(&md->work_bufs[BUFFER_BLOCK]);
|
|
2222
2442
|
|
|
2223
|
-
|
|
2224
|
-
parr_free(&rndr.work_bufs[BUFFER_BLOCK]);
|
|
2443
|
+
free(md);
|
|
2225
2444
|
}
|
|
2226
2445
|
|
|
2227
2446
|
void
|
|
2228
|
-
|
|
2447
|
+
sd_version(int *ver_major, int *ver_minor, int *ver_revision)
|
|
2229
2448
|
{
|
|
2230
2449
|
*ver_major = UPSKIRT_VER_MAJOR;
|
|
2231
2450
|
*ver_minor = UPSKIRT_VER_MINOR;
|