redcarpet 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of redcarpet might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/Gemfile +11 -0
- data/README.markdown +43 -25
- data/Rakefile +4 -1
- data/bin/redcarpet +8 -3
- data/ext/redcarpet/autolink.h +2 -2
- data/ext/redcarpet/buffer.c +2 -31
- data/ext/redcarpet/buffer.h +2 -8
- data/ext/redcarpet/houdini.h +0 -8
- data/ext/redcarpet/houdini_html_e.c +3 -8
- data/ext/redcarpet/html.c +124 -33
- data/ext/redcarpet/html.h +4 -4
- data/ext/redcarpet/markdown.c +345 -22
- data/ext/redcarpet/markdown.h +10 -13
- data/ext/redcarpet/rc_markdown.c +6 -0
- data/ext/redcarpet/rc_render.c +49 -5
- data/ext/redcarpet/stack.c +0 -19
- data/ext/redcarpet/stack.h +0 -3
- data/lib/redcarpet.rb +8 -8
- data/lib/redcarpet/render_strip.rb +4 -2
- data/redcarpet.gemspec +4 -4
- data/test/html_render_test.rb +88 -4
- data/test/html_toc_render_test.rb +55 -0
- data/test/markdown_test.rb +43 -6
- data/test/smarty_html_test.rb +15 -0
- metadata +10 -48
data/ext/redcarpet/html.h
CHANGED
@@ -14,8 +14,8 @@
|
|
14
14
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
15
15
|
*/
|
16
16
|
|
17
|
-
#ifndef
|
18
|
-
#define
|
17
|
+
#ifndef HTML_H__
|
18
|
+
#define HTML_H__
|
19
19
|
|
20
20
|
#include "markdown.h"
|
21
21
|
#include "buffer.h"
|
@@ -27,9 +27,9 @@ extern "C" {
|
|
27
27
|
|
28
28
|
struct html_renderopt {
|
29
29
|
struct {
|
30
|
-
int header_count;
|
31
30
|
int current_level;
|
32
31
|
int level_offset;
|
32
|
+
int nesting_level;
|
33
33
|
} toc_data;
|
34
34
|
|
35
35
|
unsigned int flags;
|
@@ -65,7 +65,7 @@ extern void
|
|
65
65
|
sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);
|
66
66
|
|
67
67
|
extern void
|
68
|
-
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr);
|
68
|
+
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, int nesting_level);
|
69
69
|
|
70
70
|
extern void
|
71
71
|
sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size);
|
data/ext/redcarpet/markdown.c
CHANGED
@@ -55,6 +55,29 @@ struct link_ref {
|
|
55
55
|
struct link_ref *next;
|
56
56
|
};
|
57
57
|
|
58
|
+
/* footnote_ref: reference to a footnote */
|
59
|
+
struct footnote_ref {
|
60
|
+
unsigned int id;
|
61
|
+
|
62
|
+
int is_used;
|
63
|
+
unsigned int num;
|
64
|
+
|
65
|
+
struct buf *contents;
|
66
|
+
};
|
67
|
+
|
68
|
+
/* footnote_item: an item in a footnote_list */
|
69
|
+
struct footnote_item {
|
70
|
+
struct footnote_ref *ref;
|
71
|
+
struct footnote_item *next;
|
72
|
+
};
|
73
|
+
|
74
|
+
/* footnote_list: linked list of footnote_item */
|
75
|
+
struct footnote_list {
|
76
|
+
unsigned int count;
|
77
|
+
struct footnote_item *head;
|
78
|
+
struct footnote_item *tail;
|
79
|
+
};
|
80
|
+
|
58
81
|
/* char_trigger: function pointer to render active chars */
|
59
82
|
/* returns the number of chars taken care of */
|
60
83
|
/* data is the pointer of the beginning of the span */
|
@@ -66,6 +89,7 @@ typedef size_t
|
|
66
89
|
static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
67
90
|
static size_t char_underline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
68
91
|
static size_t char_highlight(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
92
|
+
static size_t char_quote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
69
93
|
static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
70
94
|
static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
71
95
|
static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
|
@@ -90,6 +114,7 @@ enum markdown_char_t {
|
|
90
114
|
MD_CHAR_AUTOLINK_EMAIL,
|
91
115
|
MD_CHAR_AUTOLINK_WWW,
|
92
116
|
MD_CHAR_SUPERSCRIPT,
|
117
|
+
MD_CHAR_QUOTE
|
93
118
|
};
|
94
119
|
|
95
120
|
static char_trigger markdown_char_ptrs[] = {
|
@@ -105,6 +130,7 @@ static char_trigger markdown_char_ptrs[] = {
|
|
105
130
|
&char_autolink_email,
|
106
131
|
&char_autolink_www,
|
107
132
|
&char_superscript,
|
133
|
+
&char_quote
|
108
134
|
};
|
109
135
|
|
110
136
|
/* render • structure containing one particular render */
|
@@ -113,6 +139,8 @@ struct sd_markdown {
|
|
113
139
|
void *opaque;
|
114
140
|
|
115
141
|
struct link_ref *refs[REF_TABLE_SIZE];
|
142
|
+
struct footnote_list footnotes_found;
|
143
|
+
struct footnote_list footnotes_used;
|
116
144
|
uint8_t active_char[256];
|
117
145
|
struct stack work_bufs[2];
|
118
146
|
unsigned int ext_flags;
|
@@ -235,6 +263,77 @@ free_link_refs(struct link_ref **references)
|
|
235
263
|
}
|
236
264
|
}
|
237
265
|
|
266
|
+
static struct footnote_ref *
|
267
|
+
create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size)
|
268
|
+
{
|
269
|
+
struct footnote_ref *ref = calloc(1, sizeof(struct footnote_ref));
|
270
|
+
if (!ref)
|
271
|
+
return NULL;
|
272
|
+
|
273
|
+
ref->id = hash_link_ref(name, name_size);
|
274
|
+
|
275
|
+
return ref;
|
276
|
+
}
|
277
|
+
|
278
|
+
static int
|
279
|
+
add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref)
|
280
|
+
{
|
281
|
+
struct footnote_item *item = calloc(1, sizeof(struct footnote_item));
|
282
|
+
if (!item)
|
283
|
+
return 0;
|
284
|
+
item->ref = ref;
|
285
|
+
|
286
|
+
if (list->head == NULL) {
|
287
|
+
list->head = list->tail = item;
|
288
|
+
} else {
|
289
|
+
list->tail->next = item;
|
290
|
+
list->tail = item;
|
291
|
+
}
|
292
|
+
list->count++;
|
293
|
+
|
294
|
+
return 1;
|
295
|
+
}
|
296
|
+
|
297
|
+
static struct footnote_ref *
|
298
|
+
find_footnote_ref(struct footnote_list *list, uint8_t *name, size_t length)
|
299
|
+
{
|
300
|
+
unsigned int hash = hash_link_ref(name, length);
|
301
|
+
struct footnote_item *item = NULL;
|
302
|
+
|
303
|
+
item = list->head;
|
304
|
+
|
305
|
+
while (item != NULL) {
|
306
|
+
if (item->ref->id == hash)
|
307
|
+
return item->ref;
|
308
|
+
item = item->next;
|
309
|
+
}
|
310
|
+
|
311
|
+
return NULL;
|
312
|
+
}
|
313
|
+
|
314
|
+
static void
|
315
|
+
free_footnote_ref(struct footnote_ref *ref)
|
316
|
+
{
|
317
|
+
bufrelease(ref->contents);
|
318
|
+
free(ref);
|
319
|
+
}
|
320
|
+
|
321
|
+
static void
|
322
|
+
free_footnote_list(struct footnote_list *list, int free_refs)
|
323
|
+
{
|
324
|
+
struct footnote_item *item = list->head;
|
325
|
+
struct footnote_item *next;
|
326
|
+
|
327
|
+
while (item) {
|
328
|
+
next = item->next;
|
329
|
+
if (free_refs)
|
330
|
+
free_footnote_ref(item->ref);
|
331
|
+
free(item);
|
332
|
+
item = next;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
|
336
|
+
|
238
337
|
/*
|
239
338
|
* Check whether a char is a Markdown space.
|
240
339
|
|
@@ -691,6 +790,48 @@ char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t of
|
|
691
790
|
return end;
|
692
791
|
}
|
693
792
|
|
793
|
+
/* char_quote • '"' parsing a quote */
|
794
|
+
static size_t
|
795
|
+
char_quote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
|
796
|
+
{
|
797
|
+
size_t end, nq = 0, i, f_begin, f_end;
|
798
|
+
|
799
|
+
/* counting the number of quotes in the delimiter */
|
800
|
+
while (nq < size && data[nq] == '"')
|
801
|
+
nq++;
|
802
|
+
|
803
|
+
/* finding the next delimiter */
|
804
|
+
i = 0;
|
805
|
+
for (end = nq; end < size && i < nq; end++) {
|
806
|
+
if (data[end] == '"') i++;
|
807
|
+
else i = 0;
|
808
|
+
}
|
809
|
+
|
810
|
+
if (i < nq && end >= size)
|
811
|
+
return 0; /* no matching delimiter */
|
812
|
+
|
813
|
+
/* trimming outside whitespaces */
|
814
|
+
f_begin = nq;
|
815
|
+
while (f_begin < end && data[f_begin] == ' ')
|
816
|
+
f_begin++;
|
817
|
+
|
818
|
+
f_end = end - nq;
|
819
|
+
while (f_end > nq && data[f_end-1] == ' ')
|
820
|
+
f_end--;
|
821
|
+
|
822
|
+
/* real quote */
|
823
|
+
if (f_begin < f_end) {
|
824
|
+
struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };
|
825
|
+
if (!rndr->cb.quote(ob, &work, rndr->opaque))
|
826
|
+
end = 0;
|
827
|
+
} else {
|
828
|
+
if (!rndr->cb.quote(ob, 0, rndr->opaque))
|
829
|
+
end = 0;
|
830
|
+
}
|
831
|
+
|
832
|
+
return end;
|
833
|
+
}
|
834
|
+
|
694
835
|
|
695
836
|
/* char_escape • '\\' backslash escape */
|
696
837
|
static size_t
|
@@ -834,7 +975,7 @@ char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_
|
|
834
975
|
|
835
976
|
link = rndr_newbuf(rndr, BUFFER_SPAN);
|
836
977
|
|
837
|
-
if ((link_len = sd_autolink__url(&rewind, link, data, offset, size,
|
978
|
+
if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, SD_AUTOLINK_SHORT_DOMAINS)) > 0) {
|
838
979
|
ob->size -= rewind;
|
839
980
|
rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);
|
840
981
|
}
|
@@ -885,6 +1026,34 @@ char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset
|
|
885
1026
|
txt_e = i;
|
886
1027
|
i++;
|
887
1028
|
|
1029
|
+
/* footnote link */
|
1030
|
+
if (rndr->ext_flags & MKDEXT_FOOTNOTES && data[1] == '^') {
|
1031
|
+
if (txt_e < 3)
|
1032
|
+
goto cleanup;
|
1033
|
+
|
1034
|
+
struct buf id = { 0, 0, 0, 0 };
|
1035
|
+
struct footnote_ref *fr;
|
1036
|
+
|
1037
|
+
id.data = data + 2;
|
1038
|
+
id.size = txt_e - 2;
|
1039
|
+
|
1040
|
+
fr = find_footnote_ref(&rndr->footnotes_found, id.data, id.size);
|
1041
|
+
|
1042
|
+
/* mark footnote used */
|
1043
|
+
if (fr && !fr->is_used) {
|
1044
|
+
if(!add_footnote_ref(&rndr->footnotes_used, fr))
|
1045
|
+
goto cleanup;
|
1046
|
+
fr->is_used = 1;
|
1047
|
+
fr->num = rndr->footnotes_used.count;
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
/* render */
|
1051
|
+
if (fr && rndr->cb.footnote_ref)
|
1052
|
+
ret = rndr->cb.footnote_ref(ob, fr->num, rndr->opaque);
|
1053
|
+
|
1054
|
+
goto cleanup;
|
1055
|
+
}
|
1056
|
+
|
888
1057
|
/* skip any amount of whitespace or newline */
|
889
1058
|
/* (this is much more laxist than original markdown syntax) */
|
890
1059
|
while (i < size && _isspace(data[i]))
|
@@ -1131,7 +1300,7 @@ char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1131
1300
|
|
1132
1301
|
/* is_empty • returns the line length when it is empty, 0 otherwise */
|
1133
1302
|
static size_t
|
1134
|
-
is_empty(uint8_t *data, size_t size)
|
1303
|
+
is_empty(const uint8_t *data, size_t size)
|
1135
1304
|
{
|
1136
1305
|
size_t i;
|
1137
1306
|
|
@@ -1475,7 +1644,7 @@ parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1475
1644
|
* let's check to see if there's some kind of block starting
|
1476
1645
|
* here
|
1477
1646
|
*/
|
1478
|
-
if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !
|
1647
|
+
if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalpha(data[i])) {
|
1479
1648
|
if (prefix_oli(data + i, size - i) ||
|
1480
1649
|
prefix_uli(data + i, size - i)) {
|
1481
1650
|
end = i;
|
@@ -1543,7 +1712,7 @@ parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1543
1712
|
parse_inline(header_work, rndr, work.data, work.size);
|
1544
1713
|
|
1545
1714
|
if (rndr->cb.header)
|
1546
|
-
rndr->cb.header(ob, header_work, (int)level, rndr->opaque);
|
1715
|
+
rndr->cb.header(ob, header_work, (int)level, header_anchor(header_work), rndr->opaque);
|
1547
1716
|
|
1548
1717
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
1549
1718
|
}
|
@@ -1724,10 +1893,8 @@ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t s
|
|
1724
1893
|
if (!sublist)
|
1725
1894
|
sublist = work->size;
|
1726
1895
|
}
|
1727
|
-
/* joining only indented stuff after empty lines
|
1728
|
-
|
1729
|
-
* to continue a list */
|
1730
|
-
else if (in_empty && pre == 0) {
|
1896
|
+
/* joining only indented stuff after empty lines */
|
1897
|
+
else if (in_empty && i < 4 && data[beg] != '\t') {
|
1731
1898
|
*flags |= MKD_LI_END;
|
1732
1899
|
break;
|
1733
1900
|
}
|
@@ -1825,7 +1992,7 @@ parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1825
1992
|
parse_inline(work, rndr, data + i, end - i);
|
1826
1993
|
|
1827
1994
|
if (rndr->cb.header)
|
1828
|
-
rndr->cb.header(ob, work, (int)level, rndr->opaque);
|
1995
|
+
rndr->cb.header(ob, work, (int)level, header_anchor(work), rndr->opaque);
|
1829
1996
|
|
1830
1997
|
rndr_popbuf(rndr, BUFFER_SPAN);
|
1831
1998
|
}
|
@@ -1833,6 +2000,44 @@ parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1833
2000
|
return skip;
|
1834
2001
|
}
|
1835
2002
|
|
2003
|
+
/* parse_footnote_def • parse a single footnote definition */
|
2004
|
+
static void
|
2005
|
+
parse_footnote_def(struct buf *ob, struct sd_markdown *rndr, unsigned int num, uint8_t *data, size_t size)
|
2006
|
+
{
|
2007
|
+
struct buf *work = 0;
|
2008
|
+
work = rndr_newbuf(rndr, BUFFER_SPAN);
|
2009
|
+
|
2010
|
+
parse_block(work, rndr, data, size);
|
2011
|
+
|
2012
|
+
if (rndr->cb.footnote_def)
|
2013
|
+
rndr->cb.footnote_def(ob, work, num, rndr->opaque);
|
2014
|
+
rndr_popbuf(rndr, BUFFER_SPAN);
|
2015
|
+
}
|
2016
|
+
|
2017
|
+
/* parse_footnote_list • render the contents of the footnotes */
|
2018
|
+
static void
|
2019
|
+
parse_footnote_list(struct buf *ob, struct sd_markdown *rndr, struct footnote_list *footnotes)
|
2020
|
+
{
|
2021
|
+
struct buf *work = 0;
|
2022
|
+
struct footnote_item *item;
|
2023
|
+
struct footnote_ref *ref;
|
2024
|
+
|
2025
|
+
if (footnotes->count == 0)
|
2026
|
+
return;
|
2027
|
+
|
2028
|
+
work = rndr_newbuf(rndr, BUFFER_BLOCK);
|
2029
|
+
|
2030
|
+
item = footnotes->head;
|
2031
|
+
while (item) {
|
2032
|
+
ref = item->ref;
|
2033
|
+
parse_footnote_def(work, rndr, ref->num, ref->contents->data, ref->contents->size);
|
2034
|
+
item = item->next;
|
2035
|
+
}
|
2036
|
+
|
2037
|
+
if (rndr->cb.footnotes)
|
2038
|
+
rndr->cb.footnotes(ob, work, rndr->opaque);
|
2039
|
+
rndr_popbuf(rndr, BUFFER_BLOCK);
|
2040
|
+
}
|
1836
2041
|
|
1837
2042
|
/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
|
1838
2043
|
/* returns the length on match, 0 otherwise */
|
@@ -2264,6 +2469,111 @@ parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size
|
|
2264
2469
|
* REFERENCE PARSING *
|
2265
2470
|
*********************/
|
2266
2471
|
|
2472
|
+
/* is_footnote • returns whether a line is a footnote definition or not */
|
2473
|
+
static int
|
2474
|
+
is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct footnote_list *list)
|
2475
|
+
{
|
2476
|
+
size_t i = 0;
|
2477
|
+
struct buf *contents = 0;
|
2478
|
+
size_t ind = 0;
|
2479
|
+
int in_empty = 0;
|
2480
|
+
size_t start = 0;
|
2481
|
+
|
2482
|
+
size_t id_offset, id_end;
|
2483
|
+
|
2484
|
+
/* up to 3 optional leading spaces */
|
2485
|
+
if (beg + 3 >= end) return 0;
|
2486
|
+
if (data[beg] == ' ') { i = 1;
|
2487
|
+
if (data[beg + 1] == ' ') { i = 2;
|
2488
|
+
if (data[beg + 2] == ' ') { i = 3;
|
2489
|
+
if (data[beg + 3] == ' ') return 0; } } }
|
2490
|
+
i += beg;
|
2491
|
+
|
2492
|
+
/* id part: caret followed by anything between brackets */
|
2493
|
+
if (data[i] != '[') return 0;
|
2494
|
+
i++;
|
2495
|
+
if (i >= end || data[i] != '^') return 0;
|
2496
|
+
i++;
|
2497
|
+
id_offset = i;
|
2498
|
+
while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
|
2499
|
+
i++;
|
2500
|
+
if (i >= end || data[i] != ']') return 0;
|
2501
|
+
id_end = i;
|
2502
|
+
|
2503
|
+
/* spacer: colon (space | tab)* newline? (space | tab)* */
|
2504
|
+
i++;
|
2505
|
+
if (i >= end || data[i] != ':') return 0;
|
2506
|
+
i++;
|
2507
|
+
|
2508
|
+
/* getting content buffer */
|
2509
|
+
contents = bufnew(64);
|
2510
|
+
|
2511
|
+
start = i;
|
2512
|
+
|
2513
|
+
/* process lines similiar to a list item */
|
2514
|
+
while (i < end) {
|
2515
|
+
while (i < end && data[i] != '\n' && data[i] != '\r') i++;
|
2516
|
+
|
2517
|
+
/* process an empty line */
|
2518
|
+
if (is_empty(data + start, i - start)) {
|
2519
|
+
in_empty = 1;
|
2520
|
+
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
|
2521
|
+
i++;
|
2522
|
+
if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++;
|
2523
|
+
}
|
2524
|
+
start = i;
|
2525
|
+
continue;
|
2526
|
+
}
|
2527
|
+
|
2528
|
+
/* calculating the indentation */
|
2529
|
+
ind = 0;
|
2530
|
+
while (ind < 4 && start + ind < end && data[start + ind] == ' ')
|
2531
|
+
ind++;
|
2532
|
+
|
2533
|
+
/* joining only indented stuff after empty lines;
|
2534
|
+
* note that now we only require 1 space of indentation
|
2535
|
+
* to continue, just like lists */
|
2536
|
+
if (ind == 0) {
|
2537
|
+
if (start == id_end + 2 && data[start] == '\t') {}
|
2538
|
+
else break;
|
2539
|
+
}
|
2540
|
+
else if (in_empty) {
|
2541
|
+
bufputc(contents, '\n');
|
2542
|
+
}
|
2543
|
+
|
2544
|
+
in_empty = 0;
|
2545
|
+
|
2546
|
+
/* adding the line into the content buffer */
|
2547
|
+
bufput(contents, data + start + ind, i - start - ind);
|
2548
|
+
/* add carriage return */
|
2549
|
+
if (i < end) {
|
2550
|
+
bufput(contents, "\n", 1);
|
2551
|
+
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
|
2552
|
+
i++;
|
2553
|
+
if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++;
|
2554
|
+
}
|
2555
|
+
}
|
2556
|
+
start = i;
|
2557
|
+
}
|
2558
|
+
|
2559
|
+
if (last)
|
2560
|
+
*last = start;
|
2561
|
+
|
2562
|
+
if (list) {
|
2563
|
+
struct footnote_ref *ref;
|
2564
|
+
ref = create_footnote_ref(list, data + id_offset, id_end - id_offset);
|
2565
|
+
if (!ref)
|
2566
|
+
return 0;
|
2567
|
+
if (!add_footnote_ref(list, ref)) {
|
2568
|
+
free_footnote_ref(ref);
|
2569
|
+
return 0;
|
2570
|
+
}
|
2571
|
+
ref->contents = contents;
|
2572
|
+
}
|
2573
|
+
|
2574
|
+
return 1;
|
2575
|
+
}
|
2576
|
+
|
2267
2577
|
/* is_ref • returns whether a line is a reference or not */
|
2268
2578
|
static int
|
2269
2579
|
is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
|
@@ -2296,11 +2606,11 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2296
2606
|
i++;
|
2297
2607
|
if (i >= end || data[i] != ':') return 0;
|
2298
2608
|
i++;
|
2299
|
-
while (i < end && data[i]
|
2609
|
+
while (i < end && strchr("\t ", data[i])) i++;
|
2300
2610
|
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
|
2301
2611
|
i++;
|
2302
2612
|
if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
|
2303
|
-
while (i < end && data[i]
|
2613
|
+
while (i < end && strchr("\t ", data[i])) i++;
|
2304
2614
|
if (i >= end) return 0;
|
2305
2615
|
|
2306
2616
|
/* link: whitespace-free sequence, optionally between angle brackets */
|
@@ -2316,7 +2626,7 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2316
2626
|
else link_end = i;
|
2317
2627
|
|
2318
2628
|
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
|
2319
|
-
while (i < end && data[i]
|
2629
|
+
while (i < end && strchr("\t ", data[i])) i++;
|
2320
2630
|
if (i < end && data[i] != '\n' && data[i] != '\r'
|
2321
2631
|
&& data[i] != '\'' && data[i] != '"' && data[i] != '(')
|
2322
2632
|
return 0;
|
@@ -2329,7 +2639,7 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2329
2639
|
/* optional (space|tab)* spacer after a newline */
|
2330
2640
|
if (line_end) {
|
2331
2641
|
i = line_end + 1;
|
2332
|
-
while (i < end && data[i]
|
2642
|
+
while (i < end && strchr("\t ", data[i])) i++; }
|
2333
2643
|
|
2334
2644
|
/* optional title: any non-newline sequence enclosed in '"()
|
2335
2645
|
alone on its line */
|
@@ -2460,6 +2770,9 @@ sd_markdown_new(
|
|
2460
2770
|
if (extensions & MKDEXT_SUPERSCRIPT)
|
2461
2771
|
md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
|
2462
2772
|
|
2773
|
+
if (extensions & MKDEXT_QUOTE)
|
2774
|
+
md->active_char['"'] = MD_CHAR_QUOTE;
|
2775
|
+
|
2463
2776
|
/* Extension data */
|
2464
2777
|
md->ext_flags = extensions;
|
2465
2778
|
md->opaque = opaque;
|
@@ -2488,6 +2801,14 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2488
2801
|
/* reset the references table */
|
2489
2802
|
memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
|
2490
2803
|
|
2804
|
+
int footnotes_enabled = md->ext_flags & MKDEXT_FOOTNOTES;
|
2805
|
+
|
2806
|
+
/* reset the footnotes lists */
|
2807
|
+
if (footnotes_enabled) {
|
2808
|
+
memset(&md->footnotes_found, 0x0, sizeof(md->footnotes_found));
|
2809
|
+
memset(&md->footnotes_used, 0x0, sizeof(md->footnotes_used));
|
2810
|
+
}
|
2811
|
+
|
2491
2812
|
/* first pass: looking for references, copying everything else */
|
2492
2813
|
beg = 0;
|
2493
2814
|
|
@@ -2497,7 +2818,9 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2497
2818
|
beg += 3;
|
2498
2819
|
|
2499
2820
|
while (beg < doc_size) /* iterating over lines */
|
2500
|
-
if (
|
2821
|
+
if (footnotes_enabled && is_footnote(document, beg, doc_size, &end, &md->footnotes_found))
|
2822
|
+
beg = end;
|
2823
|
+
else if (is_ref(document, beg, doc_size, &end, md->refs))
|
2501
2824
|
beg = end;
|
2502
2825
|
else { /* skipping to the next line */
|
2503
2826
|
end = beg;
|
@@ -2533,12 +2856,20 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2533
2856
|
parse_block(ob, md, text->data, text->size);
|
2534
2857
|
}
|
2535
2858
|
|
2859
|
+
/* footnotes */
|
2860
|
+
if (footnotes_enabled)
|
2861
|
+
parse_footnote_list(ob, md, &md->footnotes_used);
|
2862
|
+
|
2536
2863
|
if (md->cb.doc_footer)
|
2537
2864
|
md->cb.doc_footer(ob, md->opaque);
|
2538
2865
|
|
2539
2866
|
/* clean-up */
|
2540
2867
|
bufrelease(text);
|
2541
2868
|
free_link_refs(md->refs);
|
2869
|
+
if (footnotes_enabled) {
|
2870
|
+
free_footnote_list(&md->footnotes_found, 1);
|
2871
|
+
free_footnote_list(&md->footnotes_used, 0);
|
2872
|
+
}
|
2542
2873
|
|
2543
2874
|
assert(md->work_bufs[BUFFER_SPAN].size == 0);
|
2544
2875
|
assert(md->work_bufs[BUFFER_BLOCK].size == 0);
|
@@ -2560,11 +2891,3 @@ sd_markdown_free(struct sd_markdown *md)
|
|
2560
2891
|
|
2561
2892
|
free(md);
|
2562
2893
|
}
|
2563
|
-
|
2564
|
-
void
|
2565
|
-
sd_version(int *ver_major, int *ver_minor, int *ver_revision)
|
2566
|
-
{
|
2567
|
-
*ver_major = SUNDOWN_VER_MAJOR;
|
2568
|
-
*ver_minor = SUNDOWN_VER_MINOR;
|
2569
|
-
*ver_revision = SUNDOWN_VER_REVISION;
|
2570
|
-
}
|