redcarpet 1.5.0 → 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 +312 -20
- data/Rakefile +31 -36
- data/bin/redcarpet +1 -1
- data/ext/redcarpet/autolink.c +260 -0
- data/ext/{xhtml.h → redcarpet/autolink.h} +17 -24
- data/ext/redcarpet/buffer.c +229 -0
- data/ext/redcarpet/buffer.h +88 -0
- 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 +603 -0
- data/ext/redcarpet/html.h +67 -0
- data/ext/redcarpet/html_blocks.h +206 -0
- data/ext/redcarpet/html_smartypants.c +385 -0
- data/ext/redcarpet/markdown.c +2454 -0
- data/ext/redcarpet/markdown.h +130 -0
- 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 +65 -72
- data/redcarpet.gemspec +29 -20
- data/test/redcarpet_test.rb +232 -88
- metadata +45 -29
- data/ext/array.c +0 -300
- data/ext/array.h +0 -148
- data/ext/buffer.c +0 -319
- data/ext/buffer.h +0 -145
- data/ext/markdown.c +0 -2076
- data/ext/markdown.h +0 -107
- data/ext/redcarpet.c +0 -140
- data/ext/xhtml.c +0 -779
- 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/{extconf.rb → redcarpet/extconf.rb} +0 -0
data/ext/xhtml.c
DELETED
|
@@ -1,779 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2009, Natacha Porté
|
|
3
|
-
* Copyright (c) 2011, Vicent Marti
|
|
4
|
-
*
|
|
5
|
-
* Permission to use, copy, modify, and distribute this software for any
|
|
6
|
-
* purpose with or without fee is hereby granted, provided that the above
|
|
7
|
-
* copyright notice and this permission notice appear in all copies.
|
|
8
|
-
*
|
|
9
|
-
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
-
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
-
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
-
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
-
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
-
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
-
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
#include "markdown.h"
|
|
19
|
-
#include "xhtml.h"
|
|
20
|
-
|
|
21
|
-
#include <strings.h>
|
|
22
|
-
#include <stdlib.h>
|
|
23
|
-
#include <stdio.h>
|
|
24
|
-
|
|
25
|
-
struct xhtml_renderopt {
|
|
26
|
-
struct {
|
|
27
|
-
int header_count;
|
|
28
|
-
int current_level;
|
|
29
|
-
} toc_data;
|
|
30
|
-
|
|
31
|
-
unsigned int flags;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
static int
|
|
35
|
-
is_safe_link(const char *link, size_t link_len)
|
|
36
|
-
{
|
|
37
|
-
static const size_t valid_uris_count = 4;
|
|
38
|
-
static const char *valid_uris[] = {
|
|
39
|
-
"http:", "https:", "ftp:", "mailto:"
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
size_t i;
|
|
43
|
-
|
|
44
|
-
for (i = 0; i < valid_uris_count; ++i) {
|
|
45
|
-
size_t len = strlen(valid_uris[i]);
|
|
46
|
-
|
|
47
|
-
if (link_len > len && memcmp(link, valid_uris[i], len) == 0)
|
|
48
|
-
return 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static inline int
|
|
55
|
-
put_scaped_char(struct buf *ob, char c)
|
|
56
|
-
{
|
|
57
|
-
switch (c) {
|
|
58
|
-
case '<': BUFPUTSL(ob, "<"); return 1;
|
|
59
|
-
case '>': BUFPUTSL(ob, ">"); return 1;
|
|
60
|
-
case '&': BUFPUTSL(ob, "&"); return 1;
|
|
61
|
-
case '"': BUFPUTSL(ob, """); return 1;
|
|
62
|
-
default: return 0;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
|
|
67
|
-
static void
|
|
68
|
-
lus_attr_escape(struct buf *ob, const char *src, size_t size)
|
|
69
|
-
{
|
|
70
|
-
size_t i = 0, org;
|
|
71
|
-
while (i < size) {
|
|
72
|
-
/* copying directly unescaped characters */
|
|
73
|
-
org = i;
|
|
74
|
-
while (i < size && src[i] != '<' && src[i] != '>'
|
|
75
|
-
&& src[i] != '&' && src[i] != '"')
|
|
76
|
-
i += 1;
|
|
77
|
-
if (i > org) bufput(ob, src + org, i - org);
|
|
78
|
-
|
|
79
|
-
/* escaping */
|
|
80
|
-
if (i >= size) break;
|
|
81
|
-
|
|
82
|
-
put_scaped_char(ob, src[i]);
|
|
83
|
-
i++;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
static int
|
|
88
|
-
is_html_tag(struct buf *tag, const char *tagname)
|
|
89
|
-
{
|
|
90
|
-
size_t i;
|
|
91
|
-
|
|
92
|
-
for (i = 0; i < tag->size; ++i) {
|
|
93
|
-
if (tagname[i] == '>')
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
if (tag->data[i] != tagname[i])
|
|
97
|
-
return 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return (i == tag->size || isspace(tag->data[i]) || tag->data[i] == '>');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/********************
|
|
104
|
-
* GENERIC RENDERER *
|
|
105
|
-
********************/
|
|
106
|
-
|
|
107
|
-
static void
|
|
108
|
-
rndr_autolink2(struct buf *ob, const char *link, size_t link_size, enum mkd_autolink type)
|
|
109
|
-
{
|
|
110
|
-
BUFPUTSL(ob, "<a href=\"");
|
|
111
|
-
if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:");
|
|
112
|
-
lus_attr_escape(ob, link, link_size);
|
|
113
|
-
BUFPUTSL(ob, "\">");
|
|
114
|
-
if (type == MKDA_EXPLICIT_EMAIL && link_size > 7)
|
|
115
|
-
lus_attr_escape(ob, link + 7, link_size - 7);
|
|
116
|
-
else lus_attr_escape(ob, link, link_size);
|
|
117
|
-
BUFPUTSL(ob, "</a>");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
static int
|
|
121
|
-
rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque)
|
|
122
|
-
{
|
|
123
|
-
struct xhtml_renderopt *options = opaque;
|
|
124
|
-
|
|
125
|
-
if (!link || !link->size)
|
|
126
|
-
return 0;
|
|
127
|
-
|
|
128
|
-
if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
|
|
129
|
-
return 0;
|
|
130
|
-
|
|
131
|
-
rndr_autolink2(ob, link->data, link->size, type);
|
|
132
|
-
return 1;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
static void
|
|
136
|
-
rndr_blockcode(struct buf *ob, struct buf *text, void *opaque)
|
|
137
|
-
{
|
|
138
|
-
if (ob->size) bufputc(ob, '\n');
|
|
139
|
-
BUFPUTSL(ob, "<pre><code>");
|
|
140
|
-
if (text) lus_attr_escape(ob, text->data, text->size);
|
|
141
|
-
BUFPUTSL(ob, "</code></pre>\n");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
static void
|
|
145
|
-
rndr_blockquote(struct buf *ob, struct buf *text, void *opaque)
|
|
146
|
-
{
|
|
147
|
-
BUFPUTSL(ob, "<blockquote>\n");
|
|
148
|
-
if (text) bufput(ob, text->data, text->size);
|
|
149
|
-
BUFPUTSL(ob, "</blockquote>");
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
static int
|
|
153
|
-
rndr_codespan(struct buf *ob, struct buf *text, void *opaque)
|
|
154
|
-
{
|
|
155
|
-
BUFPUTSL(ob, "<code>");
|
|
156
|
-
if (text) lus_attr_escape(ob, text->data, text->size);
|
|
157
|
-
BUFPUTSL(ob, "</code>");
|
|
158
|
-
return 1;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
static int
|
|
162
|
-
rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque)
|
|
163
|
-
{
|
|
164
|
-
struct xhtml_renderopt *options = opaque;
|
|
165
|
-
|
|
166
|
-
if (!text || !text->size)
|
|
167
|
-
return 0;
|
|
168
|
-
|
|
169
|
-
if (c == '~') {
|
|
170
|
-
if (options->flags & XHTML_SKIP_STRIKETHROUGH)
|
|
171
|
-
return 0;
|
|
172
|
-
|
|
173
|
-
BUFPUTSL(ob, "<span style=\"text-decoration:line-through;\">");
|
|
174
|
-
bufput(ob, text->data, text->size);
|
|
175
|
-
BUFPUTSL(ob, "</span>");
|
|
176
|
-
} else {
|
|
177
|
-
BUFPUTSL(ob, "<strong>");
|
|
178
|
-
bufput(ob, text->data, text->size);
|
|
179
|
-
BUFPUTSL(ob, "</strong>");
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return 1;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
static int
|
|
186
|
-
rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque)
|
|
187
|
-
{
|
|
188
|
-
if (!text || !text->size || c == '~') return 0;
|
|
189
|
-
BUFPUTSL(ob, "<em>");
|
|
190
|
-
if (text) bufput(ob, text->data, text->size);
|
|
191
|
-
BUFPUTSL(ob, "</em>");
|
|
192
|
-
return 1;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
static void
|
|
196
|
-
rndr_header(struct buf *ob, struct buf *text, int level, void *opaque)
|
|
197
|
-
{
|
|
198
|
-
struct xhtml_renderopt *options = opaque;
|
|
199
|
-
|
|
200
|
-
if (ob->size)
|
|
201
|
-
bufputc(ob, '\n');
|
|
202
|
-
|
|
203
|
-
if (options->flags & XHTML_TOC) {
|
|
204
|
-
bufprintf(ob, "<a name=\"toc_%d\"></a>", options->toc_data.header_count++);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
bufprintf(ob, "<h%d>", level);
|
|
208
|
-
if (text) bufput(ob, text->data, text->size);
|
|
209
|
-
bufprintf(ob, "</h%d>\n", level);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
static int
|
|
213
|
-
rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque)
|
|
214
|
-
{
|
|
215
|
-
struct xhtml_renderopt *options = opaque;
|
|
216
|
-
|
|
217
|
-
if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
|
|
218
|
-
return 0;
|
|
219
|
-
|
|
220
|
-
BUFPUTSL(ob, "<a href=\"");
|
|
221
|
-
if (link && link->size) lus_attr_escape(ob, link->data, link->size);
|
|
222
|
-
if (title && title->size) {
|
|
223
|
-
BUFPUTSL(ob, "\" title=\"");
|
|
224
|
-
lus_attr_escape(ob, title->data, title->size); }
|
|
225
|
-
BUFPUTSL(ob, "\">");
|
|
226
|
-
if (content && content->size) bufput(ob, content->data, content->size);
|
|
227
|
-
BUFPUTSL(ob, "</a>");
|
|
228
|
-
return 1;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
static void
|
|
232
|
-
rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque)
|
|
233
|
-
{
|
|
234
|
-
if (ob->size) bufputc(ob, '\n');
|
|
235
|
-
bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
|
|
236
|
-
if (text) bufput(ob, text->data, text->size);
|
|
237
|
-
bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
static void
|
|
241
|
-
rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque)
|
|
242
|
-
{
|
|
243
|
-
BUFPUTSL(ob, "<li>");
|
|
244
|
-
if (text) {
|
|
245
|
-
while (text->size && text->data[text->size - 1] == '\n')
|
|
246
|
-
text->size -= 1;
|
|
247
|
-
bufput(ob, text->data, text->size); }
|
|
248
|
-
BUFPUTSL(ob, "</li>\n");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
static void
|
|
252
|
-
rndr_paragraph(struct buf *ob, struct buf *text, void *opaque)
|
|
253
|
-
{
|
|
254
|
-
size_t i = 0;
|
|
255
|
-
|
|
256
|
-
if (ob->size) bufputc(ob, '\n');
|
|
257
|
-
|
|
258
|
-
if (!text || !text->size)
|
|
259
|
-
return;
|
|
260
|
-
|
|
261
|
-
while (i < text->size && isspace(text->data[i])) i++;
|
|
262
|
-
|
|
263
|
-
if (i < text->size) {
|
|
264
|
-
BUFPUTSL(ob, "<p>");
|
|
265
|
-
bufput(ob, &text->data[i], text->size - i);
|
|
266
|
-
BUFPUTSL(ob, "</p>\n");
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
static void
|
|
271
|
-
rndr_raw_block(struct buf *ob, struct buf *text, void *opaque)
|
|
272
|
-
{
|
|
273
|
-
size_t org, sz;
|
|
274
|
-
if (!text) return;
|
|
275
|
-
sz = text->size;
|
|
276
|
-
while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1;
|
|
277
|
-
org = 0;
|
|
278
|
-
while (org < sz && text->data[org] == '\n') org += 1;
|
|
279
|
-
if (org >= sz) return;
|
|
280
|
-
if (ob->size) bufputc(ob, '\n');
|
|
281
|
-
bufput(ob, text->data + org, sz - org);
|
|
282
|
-
bufputc(ob, '\n');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
static int
|
|
286
|
-
rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque)
|
|
287
|
-
{
|
|
288
|
-
if (!text || !text->size) return 0;
|
|
289
|
-
BUFPUTSL(ob, "<strong><em>");
|
|
290
|
-
bufput(ob, text->data, text->size);
|
|
291
|
-
BUFPUTSL(ob, "</em></strong>");
|
|
292
|
-
return 1;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
/**********************
|
|
297
|
-
* XHTML 1.0 RENDERER *
|
|
298
|
-
**********************/
|
|
299
|
-
|
|
300
|
-
static void
|
|
301
|
-
rndr_hrule(struct buf *ob, void *opaque)
|
|
302
|
-
{
|
|
303
|
-
if (ob->size) bufputc(ob, '\n');
|
|
304
|
-
BUFPUTSL(ob, "<hr />\n");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
static int
|
|
308
|
-
rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, void *opaque)
|
|
309
|
-
{
|
|
310
|
-
if (!link || !link->size) return 0;
|
|
311
|
-
BUFPUTSL(ob, "<img src=\"");
|
|
312
|
-
lus_attr_escape(ob, link->data, link->size);
|
|
313
|
-
BUFPUTSL(ob, "\" alt=\"");
|
|
314
|
-
if (alt && alt->size)
|
|
315
|
-
lus_attr_escape(ob, alt->data, alt->size);
|
|
316
|
-
if (title && title->size) {
|
|
317
|
-
BUFPUTSL(ob, "\" title=\"");
|
|
318
|
-
lus_attr_escape(ob, title->data, title->size); }
|
|
319
|
-
BUFPUTSL(ob, "\" />");
|
|
320
|
-
return 1;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
static int
|
|
324
|
-
rndr_linebreak(struct buf *ob, void *opaque)
|
|
325
|
-
{
|
|
326
|
-
BUFPUTSL(ob, "<br />\n");
|
|
327
|
-
return 1;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
static int
|
|
331
|
-
rndr_raw_html(struct buf *ob, struct buf *text, void *opaque)
|
|
332
|
-
{
|
|
333
|
-
struct xhtml_renderopt *options = opaque;
|
|
334
|
-
int escape_html = 0;
|
|
335
|
-
|
|
336
|
-
if (options->flags & XHTML_SKIP_HTML)
|
|
337
|
-
escape_html = 1;
|
|
338
|
-
|
|
339
|
-
else if ((options->flags & XHTML_SKIP_STYLE) != 0 && is_html_tag(text, "<style>"))
|
|
340
|
-
escape_html = 1;
|
|
341
|
-
|
|
342
|
-
else if ((options->flags & XHTML_SKIP_LINKS) != 0 && is_html_tag(text, "<a>"))
|
|
343
|
-
escape_html = 1;
|
|
344
|
-
|
|
345
|
-
else if ((options->flags & XHTML_SKIP_IMAGES) != 0 && is_html_tag(text, "<img>"))
|
|
346
|
-
escape_html = 1;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (escape_html)
|
|
350
|
-
lus_attr_escape(ob, text->data, text->size);
|
|
351
|
-
else
|
|
352
|
-
bufput(ob, text->data, text->size);
|
|
353
|
-
|
|
354
|
-
return 1;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
static void
|
|
358
|
-
rndr_table(struct buf *ob, struct buf *header, struct buf *body, void *opaque)
|
|
359
|
-
{
|
|
360
|
-
if (ob->size) bufputc(ob, '\n');
|
|
361
|
-
BUFPUTSL(ob, "<table><thead>\n");
|
|
362
|
-
if (header)
|
|
363
|
-
bufput(ob, header->data, header->size);
|
|
364
|
-
BUFPUTSL(ob, "\n</thead><tbody>\n");
|
|
365
|
-
if (body)
|
|
366
|
-
bufput(ob, body->data, body->size);
|
|
367
|
-
BUFPUTSL(ob, "\n</tbody></table>");
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
static void
|
|
371
|
-
rndr_tablerow(struct buf *ob, struct buf *text, void *opaque)
|
|
372
|
-
{
|
|
373
|
-
if (ob->size) bufputc(ob, '\n');
|
|
374
|
-
BUFPUTSL(ob, "<tr>\n");
|
|
375
|
-
if (text)
|
|
376
|
-
bufput(ob, text->data, text->size);
|
|
377
|
-
BUFPUTSL(ob, "\n</tr>");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
static void
|
|
381
|
-
rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
|
|
382
|
-
{
|
|
383
|
-
if (ob->size) bufputc(ob, '\n');
|
|
384
|
-
switch (align) {
|
|
385
|
-
case MKD_TABLE_ALIGN_L:
|
|
386
|
-
BUFPUTSL(ob, "<td align=\"left\">");
|
|
387
|
-
break;
|
|
388
|
-
|
|
389
|
-
case MKD_TABLE_ALIGN_R:
|
|
390
|
-
BUFPUTSL(ob, "<td align=\"right\">");
|
|
391
|
-
break;
|
|
392
|
-
|
|
393
|
-
case MKD_TABLE_ALIGN_CENTER:
|
|
394
|
-
BUFPUTSL(ob, "<td align=\"center\">");
|
|
395
|
-
break;
|
|
396
|
-
|
|
397
|
-
default:
|
|
398
|
-
BUFPUTSL(ob, "<td>");
|
|
399
|
-
break;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (text)
|
|
403
|
-
bufput(ob, text->data, text->size);
|
|
404
|
-
BUFPUTSL(ob, "</td>");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
static struct {
|
|
408
|
-
char c0;
|
|
409
|
-
const char *pattern;
|
|
410
|
-
const char *entity;
|
|
411
|
-
int skip;
|
|
412
|
-
} smartypants_subs[] = {
|
|
413
|
-
{ '\'', "'s>", "’", 0 },
|
|
414
|
-
{ '\'', "'t>", "’", 0 },
|
|
415
|
-
{ '\'', "'re>", "’", 0 },
|
|
416
|
-
{ '\'', "'ll>", "’", 0 },
|
|
417
|
-
{ '\'', "'ve>", "’", 0 },
|
|
418
|
-
{ '\'', "'m>", "’", 0 },
|
|
419
|
-
{ '\'', "'d>", "’", 0 },
|
|
420
|
-
{ '-', "--", "—", 1 },
|
|
421
|
-
{ '-', "<->", "–", 0 },
|
|
422
|
-
{ '.', "...", "…", 2 },
|
|
423
|
-
{ '.', ". . .", "…", 4 },
|
|
424
|
-
{ '(', "(c)", "©", 2 },
|
|
425
|
-
{ '(', "(r)", "®", 2 },
|
|
426
|
-
{ '(', "(tm)", "™", 3 },
|
|
427
|
-
{ '3', "<3/4>", "¾", 2 },
|
|
428
|
-
{ '3', "<3/4ths>", "¾", 2 },
|
|
429
|
-
{ '1', "<1/2>", "½", 2 },
|
|
430
|
-
{ '1', "<1/4>", "¼", 2 },
|
|
431
|
-
{ '1', "<1/4th>", "¼", 2 },
|
|
432
|
-
{ '&', "�", 0, 3 },
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
#define SUBS_COUNT (sizeof(smartypants_subs) / sizeof(smartypants_subs[0]))
|
|
436
|
-
|
|
437
|
-
static inline
|
|
438
|
-
word_boundary(char c)
|
|
439
|
-
{
|
|
440
|
-
return isspace(c) || ispunct(c);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
static int
|
|
444
|
-
smartypants_cmpsub(const struct buf *buf, size_t start, const char *prefix)
|
|
445
|
-
{
|
|
446
|
-
size_t i;
|
|
447
|
-
|
|
448
|
-
if (prefix[0] == '<') {
|
|
449
|
-
if (start == 0 || !word_boundary(buf->data[start - 1]))
|
|
450
|
-
return 0;
|
|
451
|
-
|
|
452
|
-
prefix++;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
for (i = start; i < buf->size; ++i) {
|
|
456
|
-
char c, p;
|
|
457
|
-
|
|
458
|
-
c = tolower(buf->data[i]);
|
|
459
|
-
p = *prefix++;
|
|
460
|
-
|
|
461
|
-
if (p == 0)
|
|
462
|
-
return 1;
|
|
463
|
-
|
|
464
|
-
if (p == '>')
|
|
465
|
-
return word_boundary(c);
|
|
466
|
-
|
|
467
|
-
if (c != p)
|
|
468
|
-
return 0;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return (*prefix == '>');
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
static int
|
|
475
|
-
smartypants_quotes(struct buf *ob, struct buf *text, size_t i, int is_open)
|
|
476
|
-
{
|
|
477
|
-
char ent[8];
|
|
478
|
-
|
|
479
|
-
if (is_open && i + 1 < text->size && !word_boundary(text->data[i + 1]))
|
|
480
|
-
return 0;
|
|
481
|
-
|
|
482
|
-
if (!is_open && i > 0 && !word_boundary(text->data[i - 1]))
|
|
483
|
-
return 0;
|
|
484
|
-
|
|
485
|
-
snprintf(ent, sizeof(ent), "&%c%cquo;",
|
|
486
|
-
is_open ? 'r' : 'l',
|
|
487
|
-
text->data[i] == '\'' ? 's' : 'd');
|
|
488
|
-
|
|
489
|
-
bufputs(ob, ent);
|
|
490
|
-
return 1;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
static void
|
|
494
|
-
rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
|
|
495
|
-
{
|
|
496
|
-
size_t i;
|
|
497
|
-
int open_single = 0, open_double = 0, open_tag = 0;
|
|
498
|
-
struct xhtml_renderopt *options = opaque;
|
|
499
|
-
|
|
500
|
-
int autolink = (options->flags & XHTML_AUTOLINK);
|
|
501
|
-
int smartypants = (options->flags & XHTML_SMARTYPANTS);
|
|
502
|
-
|
|
503
|
-
if (!text)
|
|
504
|
-
return;
|
|
505
|
-
|
|
506
|
-
if (!autolink && !smartypants) {
|
|
507
|
-
lus_attr_escape(ob, text->data, text->size);
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
for (i = 0; i < text->size; ++i) {
|
|
512
|
-
size_t sub;
|
|
513
|
-
char c = text->data[i];
|
|
514
|
-
|
|
515
|
-
/*
|
|
516
|
-
* Autolinking
|
|
517
|
-
*/
|
|
518
|
-
if (autolink) {
|
|
519
|
-
/* Autolinking is not standarized in the Markdown spec.
|
|
520
|
-
* We only check for links immediately after a space */
|
|
521
|
-
if ((i == 0 || isspace(text->data[i - 1])) &&
|
|
522
|
-
is_safe_link(text->data + i, text->size - i)) {
|
|
523
|
-
size_t j = i;
|
|
524
|
-
|
|
525
|
-
while (j < text->size && !isspace(text->data[j]))
|
|
526
|
-
j++;
|
|
527
|
-
|
|
528
|
-
rndr_autolink2(ob, &text->data[i], j - i, MKDA_NORMAL);
|
|
529
|
-
i = j - 1;
|
|
530
|
-
continue;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/*
|
|
535
|
-
* Smartypants subsitutions
|
|
536
|
-
*/
|
|
537
|
-
if (smartypants) {
|
|
538
|
-
for (sub = 0; sub < SUBS_COUNT; ++sub) {
|
|
539
|
-
if (c == smartypants_subs[sub].c0 &&
|
|
540
|
-
smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
|
|
541
|
-
|
|
542
|
-
if (smartypants_subs[sub].entity)
|
|
543
|
-
bufputs(ob, smartypants_subs[sub].entity);
|
|
544
|
-
|
|
545
|
-
i += smartypants_subs[sub].skip;
|
|
546
|
-
break;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (sub < SUBS_COUNT)
|
|
551
|
-
continue;
|
|
552
|
-
|
|
553
|
-
switch (c) {
|
|
554
|
-
case '<':
|
|
555
|
-
open_tag = 1;
|
|
556
|
-
break;
|
|
557
|
-
|
|
558
|
-
case '>':
|
|
559
|
-
open_tag = 0;
|
|
560
|
-
break;
|
|
561
|
-
|
|
562
|
-
#if 0
|
|
563
|
-
/*
|
|
564
|
-
* FIXME: this is bongos.
|
|
565
|
-
*
|
|
566
|
-
* The markdown spec defines that code blocks can be delimited
|
|
567
|
-
* by more than one backtick, e.g.
|
|
568
|
-
*
|
|
569
|
-
* ``There is a literal backtick (`) here.``
|
|
570
|
-
* <p><code>There is a literal backtick (`) here.</code></p>
|
|
571
|
-
*
|
|
572
|
-
* Obviously, there's no way to differentiate between the start
|
|
573
|
-
* of a code block and the start of a quoted string for smartypants
|
|
574
|
-
*
|
|
575
|
-
* Look at this piece of Python code:
|
|
576
|
-
*
|
|
577
|
-
* ``result = ''.join(['this', 'is', 'bongos'])``
|
|
578
|
-
*
|
|
579
|
-
* This MD expression is clearly ambiguous since it can be parsed as:
|
|
580
|
-
*
|
|
581
|
-
* <p>“result = ”.join ...</p>
|
|
582
|
-
*
|
|
583
|
-
* Or also as:
|
|
584
|
-
*
|
|
585
|
-
* <p><code>result = ''.join(['this', 'is', 'bongos'])</code></p>
|
|
586
|
-
*
|
|
587
|
-
* Fuck everything about this. This is temporarily disabled, because at GitHub
|
|
588
|
-
* it's probably smarter to prioritize code blocks than pretty cutesy punctuation.
|
|
589
|
-
*
|
|
590
|
-
* The equivalent closing tag for the (``), ('') has also been disabled, because
|
|
591
|
-
* it makes no sense to have closing tags without opening tags.
|
|
592
|
-
*/
|
|
593
|
-
case '`':
|
|
594
|
-
if (open_tag == 0) {
|
|
595
|
-
if (i + 1 < text->size && text->data[i + 1] == '`') {
|
|
596
|
-
BUFPUTSL(ob, "“"); i++;
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
break;
|
|
601
|
-
#endif
|
|
602
|
-
|
|
603
|
-
case '\"':
|
|
604
|
-
if (open_tag == 0) {
|
|
605
|
-
if (smartypants_quotes(ob, text, i, open_double)) {
|
|
606
|
-
open_double = !open_double;
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
break;
|
|
611
|
-
|
|
612
|
-
case '\'':
|
|
613
|
-
if (open_tag == 0) {
|
|
614
|
-
|
|
615
|
-
#if 0 /* temporarily disabled, see previous comment */
|
|
616
|
-
if (i + 1 < text->size && text->data[i + 1] == '\'') {
|
|
617
|
-
BUFPUTSL(ob, "”"); i++;
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
#endif
|
|
621
|
-
|
|
622
|
-
if (smartypants_quotes(ob, text, i, open_single)) {
|
|
623
|
-
open_single = !open_single;
|
|
624
|
-
continue;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
break;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/*
|
|
632
|
-
* Copy raw character
|
|
633
|
-
*/
|
|
634
|
-
if (!put_scaped_char(ob, c))
|
|
635
|
-
bufputc(ob, c);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
static void
|
|
640
|
-
toc_header(struct buf *ob, struct buf *text, int level, void *opaque)
|
|
641
|
-
{
|
|
642
|
-
struct xhtml_renderopt *options = opaque;
|
|
643
|
-
|
|
644
|
-
if (level > options->toc_data.current_level) {
|
|
645
|
-
if (level > 1)
|
|
646
|
-
BUFPUTSL(ob, "<li>");
|
|
647
|
-
BUFPUTSL(ob, "<ul>\n");
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
if (level < options->toc_data.current_level) {
|
|
651
|
-
BUFPUTSL(ob, "</ul>");
|
|
652
|
-
if (options->toc_data.current_level > 1)
|
|
653
|
-
BUFPUTSL(ob, "</li>\n");
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
options->toc_data.current_level = level;
|
|
657
|
-
|
|
658
|
-
bufprintf(ob, "<li><a href=\"#toc_%d\">", options->toc_data.header_count++);
|
|
659
|
-
if (text)
|
|
660
|
-
bufput(ob, text->data, text->size);
|
|
661
|
-
BUFPUTSL(ob, "</a></li>\n");
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
static void
|
|
665
|
-
toc_finalize(struct buf *ob, void *opaque)
|
|
666
|
-
{
|
|
667
|
-
struct xhtml_renderopt *options = opaque;
|
|
668
|
-
|
|
669
|
-
while (options->toc_data.current_level > 1) {
|
|
670
|
-
BUFPUTSL(ob, "</ul></li>\n");
|
|
671
|
-
options->toc_data.current_level--;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (options->toc_data.current_level)
|
|
675
|
-
BUFPUTSL(ob, "</ul>\n");
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
void
|
|
679
|
-
init_toc_renderer(struct mkd_renderer *renderer)
|
|
680
|
-
{
|
|
681
|
-
static const struct mkd_renderer toc_render = {
|
|
682
|
-
NULL,
|
|
683
|
-
NULL,
|
|
684
|
-
NULL,
|
|
685
|
-
toc_header,
|
|
686
|
-
NULL,
|
|
687
|
-
NULL,
|
|
688
|
-
NULL,
|
|
689
|
-
NULL,
|
|
690
|
-
NULL,
|
|
691
|
-
NULL,
|
|
692
|
-
NULL,
|
|
693
|
-
|
|
694
|
-
rndr_autolink,
|
|
695
|
-
rndr_codespan,
|
|
696
|
-
rndr_double_emphasis,
|
|
697
|
-
rndr_emphasis,
|
|
698
|
-
rndr_image,
|
|
699
|
-
rndr_linebreak,
|
|
700
|
-
rndr_link,
|
|
701
|
-
rndr_raw_html,
|
|
702
|
-
rndr_triple_emphasis,
|
|
703
|
-
|
|
704
|
-
NULL,
|
|
705
|
-
NULL,
|
|
706
|
-
|
|
707
|
-
NULL,
|
|
708
|
-
toc_finalize,
|
|
709
|
-
|
|
710
|
-
"*-~",
|
|
711
|
-
NULL
|
|
712
|
-
};
|
|
713
|
-
|
|
714
|
-
struct xhtml_renderopt *options;
|
|
715
|
-
options = calloc(1, sizeof(struct xhtml_renderopt));
|
|
716
|
-
options->flags = XHTML_TOC;
|
|
717
|
-
|
|
718
|
-
memcpy(renderer, &toc_render, sizeof(struct mkd_renderer));
|
|
719
|
-
renderer->opaque = options;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
void
|
|
723
|
-
init_xhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
|
|
724
|
-
{
|
|
725
|
-
static const struct mkd_renderer renderer_default = {
|
|
726
|
-
rndr_blockcode,
|
|
727
|
-
rndr_blockquote,
|
|
728
|
-
rndr_raw_block,
|
|
729
|
-
rndr_header,
|
|
730
|
-
rndr_hrule,
|
|
731
|
-
rndr_list,
|
|
732
|
-
rndr_listitem,
|
|
733
|
-
rndr_paragraph,
|
|
734
|
-
rndr_table,
|
|
735
|
-
rndr_tablerow,
|
|
736
|
-
rndr_tablecell,
|
|
737
|
-
|
|
738
|
-
rndr_autolink,
|
|
739
|
-
rndr_codespan,
|
|
740
|
-
rndr_double_emphasis,
|
|
741
|
-
rndr_emphasis,
|
|
742
|
-
rndr_image,
|
|
743
|
-
rndr_linebreak,
|
|
744
|
-
rndr_link,
|
|
745
|
-
rndr_raw_html,
|
|
746
|
-
rndr_triple_emphasis,
|
|
747
|
-
|
|
748
|
-
NULL,
|
|
749
|
-
rndr_normal_text,
|
|
750
|
-
|
|
751
|
-
NULL,
|
|
752
|
-
NULL,
|
|
753
|
-
|
|
754
|
-
"*_~",
|
|
755
|
-
NULL
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
struct xhtml_renderopt *options;
|
|
759
|
-
options = calloc(1, sizeof(struct xhtml_renderopt));
|
|
760
|
-
options->flags = render_flags;
|
|
761
|
-
|
|
762
|
-
memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
|
|
763
|
-
renderer->opaque = options;
|
|
764
|
-
|
|
765
|
-
if (render_flags & XHTML_SKIP_IMAGES)
|
|
766
|
-
renderer->image = NULL;
|
|
767
|
-
|
|
768
|
-
if (render_flags & XHTML_SKIP_LINKS) {
|
|
769
|
-
renderer->link = NULL;
|
|
770
|
-
renderer->autolink = NULL;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
void
|
|
775
|
-
free_renderer(struct mkd_renderer *renderer)
|
|
776
|
-
{
|
|
777
|
-
free(renderer->opaque);
|
|
778
|
-
}
|
|
779
|
-
|