redcarpet 1.0.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.

data/ext/markdown.h ADDED
@@ -0,0 +1,124 @@
1
+ /* markdown.h - generic markdown parser */
2
+
3
+ /*
4
+ * Copyright (c) 2009, Natacha Porté
5
+ *
6
+ * Permission to use, copy, modify, and distribute this software for any
7
+ * purpose with or without fee is hereby granted, provided that the above
8
+ * copyright notice and this permission notice appear in all copies.
9
+ *
10
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
+ */
18
+
19
+ #ifndef LITHIUM_MARKDOWN_H
20
+ #define LITHIUM_MARKDOWN_H
21
+
22
+ #include "buffer.h"
23
+
24
+ /* Require a blank newline after HTML tags */
25
+ #define UPSKIRT_NEWLINE_AFTER_TAGS
26
+
27
+ /********************
28
+ * TYPE DEFINITIONS *
29
+ ********************/
30
+
31
+ /* mkd_autolink • type of autolink */
32
+ enum mkd_autolink {
33
+ MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/
34
+ MKDA_NORMAL, /* normal http/http/ftp/etc link */
35
+ MKDA_EXPLICIT_EMAIL, /* e-mail link with explit mailto: */
36
+ MKDA_IMPLICIT_EMAIL /* e-mail link without mailto: */
37
+ };
38
+
39
+ struct mkd_renderopt {
40
+ void *opaque;
41
+ unsigned int flags;
42
+ };
43
+
44
+ struct mkd_parseropt {
45
+ unsigned int flags;
46
+ int recursion_depth;
47
+ };
48
+
49
+ /* mkd_renderer • functions for rendering parsed data */
50
+ struct mkd_renderer {
51
+ /* block level callbacks - NULL skips the block */
52
+ void (*blockcode)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
53
+ void (*blockquote)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
54
+ void (*blockhtml)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
55
+ void (*header)(struct buf *ob, struct buf *text, int level, struct mkd_renderopt *opaque);
56
+ void (*hrule)(struct buf *ob, struct mkd_renderopt *opaque);
57
+ void (*list)(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *opaque);
58
+ void (*listitem)(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *opaque);
59
+ void (*paragraph)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
60
+
61
+ /* span level callbacks - NULL or return 0 prints the span verbatim */
62
+ int (*autolink)(struct buf *ob, struct buf *link, enum mkd_autolink type, struct mkd_renderopt *opaque);
63
+ int (*codespan)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
64
+ int (*double_emphasis)(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *opaque);
65
+ int (*emphasis)(struct buf *ob, struct buf *text, char c,struct mkd_renderopt *opaque);
66
+ int (*image)(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, struct mkd_renderopt *opaque);
67
+ int (*linebreak)(struct buf *ob, struct mkd_renderopt *opaque);
68
+ int (*link)(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, struct mkd_renderopt *opaque);
69
+ int (*raw_html_tag)(struct buf *ob, struct buf *tag, struct mkd_renderopt *opaque);
70
+ int (*triple_emphasis)(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *opaque);
71
+
72
+ /* low level callbacks - NULL copies input directly into the output */
73
+ void (*entity)(struct buf *ob, struct buf *entity, struct mkd_renderopt *opaque);
74
+ void (*normal_text)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
75
+
76
+ /* renderer data */
77
+ const char *emph_chars; /* chars that trigger emphasis rendering */
78
+
79
+ struct mkd_renderopt render_options;
80
+ struct mkd_parseropt parser_options;
81
+ };
82
+
83
+
84
+
85
+ /*********
86
+ * FLAGS *
87
+ *********/
88
+
89
+ /* list/listitem flags */
90
+ #define MKD_LIST_ORDERED 1
91
+ #define MKD_LI_BLOCK 2 /* <li> containing block data */
92
+
93
+ typedef enum {
94
+ RENDER_SKIP_HTML = (1 << 0),
95
+ RENDER_SKIP_STYLE = (1 << 1),
96
+ RENDER_SKIP_IMAGES = (1 << 2),
97
+ RENDER_SKIP_LINKS = (1 << 3),
98
+ RENDER_SMARTYPANTS = (1 << 4),
99
+ RENDER_EXPAND_TABS = (1 << 5),
100
+ RENDER_AUTOLINK = (1 << 6),
101
+ RENDER_SAFELINK = (1 << 7),
102
+ } render_mode;
103
+
104
+ typedef enum {
105
+ PARSER_STRICT = (1 << 0),
106
+ } parser_mode;
107
+
108
+ /**********************
109
+ * EXPORTED FUNCTIONS *
110
+ **********************/
111
+
112
+ /* markdown • parses the input buffer and renders it into the output buffer */
113
+ void
114
+ markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr);
115
+
116
+ void
117
+ init_renderer(struct mkd_renderer *renderer,
118
+ unsigned int render_flags, void *opaque,
119
+ unsigned int parser_flags, int recursion_depth);
120
+
121
+
122
+ #endif /* ndef LITHIUM_MARKDOWN_H */
123
+
124
+ /* vim: set filetype=c: */
data/ext/redcarpet.c ADDED
@@ -0,0 +1,86 @@
1
+ #include <stdio.h>
2
+ #include "ruby.h"
3
+
4
+ #include "markdown.h"
5
+
6
+ static VALUE rb_cRedcarpet;
7
+
8
+ static void rb_redcarpet__setup_render(VALUE ruby_obj, struct mkd_renderer *rnd)
9
+ {
10
+ unsigned int render_flags = RENDER_EXPAND_TABS;
11
+ unsigned int parser_flags = 0;
12
+
13
+ /* smart */
14
+ if (rb_funcall(ruby_obj, rb_intern("smart"), 0) == Qtrue)
15
+ render_flags |= RENDER_SMARTYPANTS;
16
+
17
+ /* filter_html */
18
+ if (rb_funcall(ruby_obj, rb_intern("filter_html"), 0) == Qtrue)
19
+ render_flags |= RENDER_SKIP_HTML;
20
+
21
+ /* no_image */
22
+ if (rb_funcall(ruby_obj, rb_intern("no_image"), 0) == Qtrue)
23
+ render_flags |= RENDER_SKIP_IMAGES;
24
+
25
+ /* no_links */
26
+ if (rb_funcall(ruby_obj, rb_intern("no_links"), 0) == Qtrue)
27
+ render_flags |= RENDER_SKIP_LINKS;
28
+
29
+ /* filter_style */
30
+ if (rb_funcall(ruby_obj, rb_intern("filter_styles"), 0) == Qtrue)
31
+ render_flags |= RENDER_SKIP_STYLE;
32
+
33
+ /* autolink */
34
+ if (rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
35
+ render_flags |= RENDER_AUTOLINK;
36
+
37
+ /* safelink */
38
+ if (rb_funcall(ruby_obj, rb_intern("safelink"), 0) == Qtrue)
39
+ render_flags |= RENDER_SAFELINK;
40
+
41
+
42
+ /* parser - strict */
43
+ if (rb_funcall(ruby_obj, rb_intern("strict"), 0) == Qtrue)
44
+ parser_flags |= PARSER_STRICT;
45
+
46
+
47
+ init_renderer(rnd, render_flags, NULL, parser_flags, 16);
48
+ }
49
+
50
+ static VALUE rb_redcarpet_to_html(int argc, VALUE *argv, VALUE self)
51
+ {
52
+ VALUE text = rb_funcall(self, rb_intern("text"), 0);
53
+ VALUE result;
54
+
55
+ struct buf input_buf, *output_buf;
56
+ struct mkd_renderer redcarpet_render;
57
+
58
+ Check_Type(text, T_STRING);
59
+
60
+ memset(&input_buf, 0x0, sizeof(struct buf));
61
+ input_buf.data = RSTRING_PTR(text);
62
+ input_buf.size = RSTRING_LEN(text);
63
+
64
+ output_buf = bufnew(64);
65
+
66
+ rb_redcarpet__setup_render(self, &redcarpet_render);
67
+ markdown(output_buf, &input_buf, &redcarpet_render);
68
+
69
+ result = rb_str_new(output_buf->data, output_buf->size);
70
+ bufrelease(output_buf);
71
+
72
+ /* force the input encoding */
73
+ if (rb_respond_to(text, rb_intern("encoding"))) {
74
+ VALUE encoding = rb_funcall(text, rb_intern("encoding"), 0);
75
+ rb_funcall(result, rb_intern("force_encoding"), 1, encoding);
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ void Init_redcarpet()
82
+ {
83
+ rb_cRedcarpet = rb_define_class("Redcarpet", rb_cObject);
84
+ rb_define_method(rb_cRedcarpet, "to_html", rb_redcarpet_to_html, -1);
85
+ }
86
+
data/ext/render.c ADDED
@@ -0,0 +1,499 @@
1
+ /*
2
+ * Copyright (c) 2011, Vicent Marti
3
+ * Copyright (c) 2009, Natacha Porté
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
+
20
+ #include <strings.h>
21
+ #include <stdlib.h>
22
+
23
+
24
+ static int is_safe_link(const char *link, size_t link_len)
25
+ {
26
+ static const size_t valid_uris_count = 4;
27
+ static const char *valid_uris[] = {
28
+ "http:", "https:", "ftp:", "mailto:"
29
+ };
30
+
31
+ size_t i;
32
+
33
+ for (i = 0; i < valid_uris_count; ++i) {
34
+ size_t len = strlen(valid_uris[i]);
35
+
36
+ if (link_len > len && memcmp(link, valid_uris[i], len) == 0)
37
+ return 1;
38
+ }
39
+
40
+ return 0;
41
+ }
42
+
43
+ static int
44
+ put_scaped_char(struct buf *ob, char c)
45
+ {
46
+ switch (c) {
47
+ case '<': BUFPUTSL(ob, "&lt;"); return 1;
48
+ case '>': BUFPUTSL(ob, "&gt;"); return 1;
49
+ case '&': BUFPUTSL(ob, "&amp;"); return 1;
50
+ case '"': BUFPUTSL(ob, "&quot;"); return 1;
51
+ default: return 0;
52
+ }
53
+ }
54
+
55
+
56
+ /* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
57
+ static void
58
+ lus_attr_escape(struct buf *ob, const char *src, size_t size) {
59
+ size_t i = 0, org;
60
+ while (i < size) {
61
+ /* copying directly unescaped characters */
62
+ org = i;
63
+ while (i < size && src[i] != '<' && src[i] != '>'
64
+ && src[i] != '&' && src[i] != '"')
65
+ i += 1;
66
+ if (i > org) bufput(ob, src + org, i - org);
67
+
68
+ /* escaping */
69
+ if (i >= size) break;
70
+
71
+ put_scaped_char(ob, src[i]);
72
+ i++;
73
+ }
74
+ }
75
+
76
+ static int
77
+ is_html_tag(struct buf *tag, const char *tagname)
78
+ {
79
+ size_t i;
80
+
81
+ for (i = 0; i < tag->size; ++i) {
82
+ if (tagname[i] == '>')
83
+ break;
84
+
85
+ if (tag->data[i] != tagname[i])
86
+ return 0;
87
+ }
88
+
89
+ return (i == tag->size || isspace(tag->data[i]) || tag->data[i] == '>');
90
+ }
91
+
92
+ /********************
93
+ * GENERIC RENDERER *
94
+ ********************/
95
+
96
+ static void
97
+ rndr_autolink2(struct buf *ob, const char *link, size_t link_size, enum mkd_autolink type)
98
+ {
99
+ BUFPUTSL(ob, "<a href=\"");
100
+ if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:");
101
+ lus_attr_escape(ob, link, link_size);
102
+ BUFPUTSL(ob, "\">");
103
+ if (type == MKDA_EXPLICIT_EMAIL && link_size > 7)
104
+ lus_attr_escape(ob, link + 7, link_size - 7);
105
+ else lus_attr_escape(ob, link, link_size);
106
+ BUFPUTSL(ob, "</a>");
107
+ }
108
+
109
+ static int
110
+ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, struct mkd_renderopt *options)
111
+ {
112
+ if (!link || !link->size)
113
+ return 0;
114
+
115
+ if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
116
+ return 0;
117
+
118
+ rndr_autolink2(ob, link->data, link->size, type);
119
+ return 1;
120
+ }
121
+
122
+ static void
123
+ rndr_blockcode(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
124
+ if (ob->size) bufputc(ob, '\n');
125
+ BUFPUTSL(ob, "<pre><code>");
126
+ if (text) lus_attr_escape(ob, text->data, text->size);
127
+ BUFPUTSL(ob, "</code></pre>\n");
128
+ }
129
+
130
+ static void
131
+ rndr_blockquote(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
132
+ BUFPUTSL(ob, "<blockquote>\n");
133
+ if (text) bufput(ob, text->data, text->size);
134
+ BUFPUTSL(ob, "</blockquote>");
135
+ }
136
+
137
+ static int
138
+ rndr_codespan(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
139
+ BUFPUTSL(ob, "<code>");
140
+ if (text) lus_attr_escape(ob, text->data, text->size);
141
+ BUFPUTSL(ob, "</code>");
142
+ return 1;
143
+ }
144
+
145
+ static int
146
+ rndr_double_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
147
+ if (!text || !text->size) return 0;
148
+ BUFPUTSL(ob, "<strong>");
149
+ bufput(ob, text->data, text->size);
150
+ BUFPUTSL(ob, "</strong>");
151
+ return 1;
152
+ }
153
+
154
+ static int
155
+ rndr_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
156
+ if (!text || !text->size) return 0;
157
+ BUFPUTSL(ob, "<em>");
158
+ if (text) bufput(ob, text->data, text->size);
159
+ BUFPUTSL(ob, "</em>");
160
+ return 1;
161
+ }
162
+
163
+ static void
164
+ rndr_header(struct buf *ob, struct buf *text, int level, struct mkd_renderopt *options) {
165
+ if (ob->size) bufputc(ob, '\n');
166
+ bufprintf(ob, "<h%d>", level);
167
+ if (text) bufput(ob, text->data, text->size);
168
+ bufprintf(ob, "</h%d>\n", level);
169
+ }
170
+
171
+ static int
172
+ rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, struct mkd_renderopt *options)
173
+ {
174
+ if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
175
+ return 0;
176
+
177
+ BUFPUTSL(ob, "<a href=\"");
178
+ if (link && link->size) lus_attr_escape(ob, link->data, link->size);
179
+ if (title && title->size) {
180
+ BUFPUTSL(ob, "\" title=\"");
181
+ lus_attr_escape(ob, title->data, title->size); }
182
+ BUFPUTSL(ob, "\">");
183
+ if (content && content->size) bufput(ob, content->data, content->size);
184
+ BUFPUTSL(ob, "</a>");
185
+ return 1;
186
+ }
187
+
188
+ static void
189
+ rndr_list(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) {
190
+ if (ob->size) bufputc(ob, '\n');
191
+ bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
192
+ if (text) bufput(ob, text->data, text->size);
193
+ bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
194
+ }
195
+
196
+ static void
197
+ rndr_listitem(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) {
198
+ if (ob->size) bufputc(ob, '\n');
199
+ BUFPUTSL(ob, "<li>\n");
200
+ if (text) {
201
+ while (text->size && text->data[text->size - 1] == '\n')
202
+ text->size -= 1;
203
+ bufput(ob, text->data, text->size); }
204
+ BUFPUTSL(ob, "</li>\n");
205
+ }
206
+
207
+ static void
208
+ rndr_paragraph(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
209
+ size_t i = 0;
210
+
211
+ if (ob->size) bufputc(ob, '\n');
212
+
213
+ if (!text || !text->size)
214
+ return;
215
+
216
+ while (i < text->size && isspace(text->data[i])) i++;
217
+
218
+ if (i < text->size) {
219
+ BUFPUTSL(ob, "<p>");
220
+ bufput(ob, &text->data[i], text->size - i);
221
+ BUFPUTSL(ob, "</p>\n");
222
+ }
223
+ }
224
+
225
+ static void
226
+ rndr_raw_block(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
227
+ size_t org, sz;
228
+ if (!text) return;
229
+ sz = text->size;
230
+ while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1;
231
+ org = 0;
232
+ while (org < sz && text->data[org] == '\n') org += 1;
233
+ if (org >= sz) return;
234
+ if (ob->size) bufputc(ob, '\n');
235
+ bufput(ob, text->data + org, sz - org);
236
+ bufputc(ob, '\n');
237
+ }
238
+
239
+ static int
240
+ rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
241
+ if (!text || !text->size) return 0;
242
+ BUFPUTSL(ob, "<strong><em>");
243
+ bufput(ob, text->data, text->size);
244
+ BUFPUTSL(ob, "</em></strong>");
245
+ return 1;
246
+ }
247
+
248
+
249
+ /**********************
250
+ * XHTML 1.0 RENDERER *
251
+ **********************/
252
+
253
+ static void
254
+ rndr_hrule(struct buf *ob, struct mkd_renderopt *options) {
255
+ if (ob->size) bufputc(ob, '\n');
256
+ BUFPUTSL(ob, "<hr />\n");
257
+ }
258
+
259
+ static int
260
+ rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, struct mkd_renderopt *options) {
261
+ if (!link || !link->size) return 0;
262
+ BUFPUTSL(ob, "<img src=\"");
263
+ lus_attr_escape(ob, link->data, link->size);
264
+ BUFPUTSL(ob, "\" alt=\"");
265
+ if (alt && alt->size)
266
+ lus_attr_escape(ob, alt->data, alt->size);
267
+ if (title && title->size) {
268
+ BUFPUTSL(ob, "\" title=\"");
269
+ lus_attr_escape(ob, title->data, title->size); }
270
+ BUFPUTSL(ob, "\" />");
271
+ return 1;
272
+ }
273
+
274
+ static int
275
+ rndr_linebreak(struct buf *ob, struct mkd_renderopt *options) {
276
+ BUFPUTSL(ob, "<br />\n");
277
+ return 1;
278
+ }
279
+
280
+ static int
281
+ rndr_raw_html(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
282
+ int escape_html = 0;
283
+
284
+ if (options->flags & RENDER_SKIP_HTML)
285
+ escape_html = 1;
286
+
287
+ else if ((options->flags & RENDER_SKIP_STYLE) != 0 && is_html_tag(text, "<style>"))
288
+ escape_html = 1;
289
+
290
+ else if ((options->flags & RENDER_SKIP_LINKS) != 0 && is_html_tag(text, "<a>"))
291
+ escape_html = 1;
292
+
293
+ else if ((options->flags & RENDER_SKIP_IMAGES) != 0 && is_html_tag(text, "<img>"))
294
+ escape_html = 1;
295
+
296
+
297
+ if (escape_html)
298
+ lus_attr_escape(ob, text->data, text->size);
299
+ else
300
+ bufput(ob, text->data, text->size);
301
+
302
+ return 1;
303
+ }
304
+
305
+
306
+ static struct {
307
+ char c0;
308
+ const char *pattern;
309
+ const char *entity;
310
+ int skip;
311
+ } smartypants_subs[] = {
312
+ { '\'', "'s>", "&rsquo;", 0 },
313
+ { '\'', "'t>", "&rsquo;", 0 },
314
+ { '\'', "'re>", "&rsquo;", 0 },
315
+ { '\'', "'ll>", "&rsquo;", 0 },
316
+ { '\'', "'ve>", "&rsquo;", 0 },
317
+ { '\'', "'m>", "&rsquo;", 0 },
318
+ { '\'', "'d>", "&rsquo;", 0 },
319
+ { '-', "--", "&mdash;", 1 },
320
+ { '-', "<->", "&ndash;", 0 },
321
+ { '.', "...", "&hellip;", 2 },
322
+ { '.', ". . .", "&hellip;", 4 },
323
+ { '(', "(c)", "&copy;", 2 },
324
+ { '(', "(r)", "&reg;", 2 },
325
+ { '(', "(tm)", "&trade;", 3 },
326
+ { '3', "<3/4>", "&frac34;", 2 },
327
+ { '3', "<3/4ths>", "&frac34;", 2 },
328
+ { '1', "<1/2>", "&frac12;", 2 },
329
+ { '1', "<1/4>", "&frac14;", 2 },
330
+ { '1', "<1/4th>", "&frac14;", 2 },
331
+ { '&', "&#0;", 0, 3 },
332
+ };
333
+
334
+ static const char *smartypants_squotes[] = {"&lsquo;", "&rsquo;"};
335
+ static const char *smartypants_dquotes[] = {"&ldquo;", "&rdquo;"};
336
+
337
+ #define SUBS_COUNT (sizeof(smartypants_subs) / sizeof(smartypants_subs[0]))
338
+
339
+ static int
340
+ word_boundary(char c)
341
+ {
342
+ return isspace(c) || ispunct(c);
343
+ }
344
+
345
+ static int
346
+ smartypants_cmpsub(const struct buf *buf, size_t start, const char *prefix)
347
+ {
348
+ size_t i;
349
+
350
+ if (prefix[0] == '<') {
351
+ if (start == 0 || !word_boundary(buf->data[start - 1]))
352
+ return 0;
353
+
354
+ prefix++;
355
+ }
356
+
357
+ for (i = start; i < buf->size; ++i) {
358
+ char c, p;
359
+
360
+ c = tolower(buf->data[i]);
361
+ p = *prefix++;
362
+
363
+ if (p == 0)
364
+ return 1;
365
+
366
+ if (p == '>')
367
+ return word_boundary(c);
368
+
369
+ if (c != p)
370
+ return 0;
371
+ }
372
+
373
+ return (*prefix == '>');
374
+ }
375
+
376
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
377
+ */
378
+ static void
379
+ smartypants_and_autolink(struct buf *ob, struct buf *text, unsigned int flags)
380
+ {
381
+ size_t i;
382
+ int open_single = 0, open_double = 0;
383
+
384
+ int autolink = (flags & RENDER_AUTOLINK);
385
+ int smartypants = (flags & RENDER_SMARTYPANTS);
386
+
387
+ for (i = 0; i < text->size; ++i) {
388
+ size_t sub;
389
+ char c = text->data[i];
390
+
391
+ if (autolink && is_safe_link(text->data + i, text->size - i)) {
392
+ size_t j = i;
393
+
394
+ while (j < text->size && !isspace(text->data[j])) j++;
395
+
396
+ rndr_autolink2(ob, &text->data[i], j - i, MKDA_NORMAL);
397
+ i = j;
398
+ continue;
399
+ }
400
+
401
+ if (smartypants) {
402
+ for (sub = 0; sub < SUBS_COUNT; ++sub) {
403
+ if (c == smartypants_subs[sub].c0 &&
404
+ smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
405
+
406
+ if (smartypants_subs[sub].entity)
407
+ bufputs(ob, smartypants_subs[sub].entity);
408
+
409
+ i += smartypants_subs[sub].skip;
410
+ break;
411
+ }
412
+ }
413
+
414
+ if (sub < SUBS_COUNT)
415
+ continue;
416
+
417
+ switch (c) {
418
+ case '\"':
419
+ bufputs(ob, smartypants_dquotes[open_double]);
420
+ open_double = !open_double;
421
+ continue;
422
+
423
+ case '\'':
424
+ bufputs(ob, smartypants_squotes[open_single]);
425
+ open_single = !open_single;
426
+ continue;
427
+
428
+ /* TODO: advanced quotes like `` and '' */
429
+ }
430
+ }
431
+
432
+ if (!put_scaped_char(ob, c))
433
+ bufputc(ob, c);
434
+ }
435
+ }
436
+
437
+ static void
438
+ rndr_normal_text(struct buf *ob, struct buf *text, struct mkd_renderopt *options)
439
+ {
440
+ if (!text)
441
+ return;
442
+
443
+ if (options->flags & RENDER_SMARTYPANTS || options->flags & RENDER_AUTOLINK)
444
+ smartypants_and_autolink(ob, text, options->flags);
445
+ else
446
+ lus_attr_escape(ob, text->data, text->size);
447
+ }
448
+
449
+ void
450
+ init_renderer(struct mkd_renderer *renderer,
451
+ unsigned int render_flags, void *opaque,
452
+ unsigned int parser_flags, int recursion_depth)
453
+ {
454
+ static const struct mkd_renderer renderer_default = {
455
+ rndr_blockcode,
456
+ rndr_blockquote,
457
+ rndr_raw_block,
458
+ rndr_header,
459
+ rndr_hrule,
460
+ rndr_list,
461
+ rndr_listitem,
462
+ rndr_paragraph,
463
+
464
+ rndr_autolink,
465
+ rndr_codespan,
466
+ rndr_double_emphasis,
467
+ rndr_emphasis,
468
+ rndr_image,
469
+ rndr_linebreak,
470
+ rndr_link,
471
+ rndr_raw_html,
472
+ rndr_triple_emphasis,
473
+
474
+ NULL,
475
+ rndr_normal_text,
476
+
477
+ "*_",
478
+
479
+ { NULL, 0 },
480
+ { 0, 0 },
481
+ };
482
+
483
+ memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
484
+
485
+ if (render_flags & RENDER_SKIP_IMAGES)
486
+ renderer->image = NULL;
487
+
488
+ if (render_flags & RENDER_SKIP_LINKS) {
489
+ renderer->link = NULL;
490
+ renderer->autolink = NULL;
491
+ }
492
+
493
+ renderer->parser_options.recursion_depth = recursion_depth;
494
+ renderer->parser_options.flags = parser_flags;
495
+
496
+ renderer->render_options.opaque = opaque;
497
+ renderer->render_options.flags = render_flags;
498
+ }
499
+