tight-redcarpet 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Copyright (c) 2008, Natacha Porté
3
+ * Copyright (c) 2011, Vicent Martí
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
+ #ifndef BUFFER_H__
19
+ #define BUFFER_H__
20
+
21
+ #include <stddef.h>
22
+ #include <stdarg.h>
23
+ #include <stdint.h>
24
+
25
+ #ifdef __cplusplus
26
+ extern "C" {
27
+ #endif
28
+
29
+ #if defined(_MSC_VER)
30
+ #define __attribute__(x)
31
+ #define inline
32
+ #endif
33
+
34
+ typedef enum {
35
+ BUF_OK = 0,
36
+ BUF_ENOMEM = -1,
37
+ } buferror_t;
38
+
39
+ /* struct buf: character array buffer */
40
+ struct buf {
41
+ uint8_t *data; /* actual character data */
42
+ size_t size; /* size of the string */
43
+ size_t asize; /* allocated size (0 = volatile buffer) */
44
+ size_t unit; /* reallocation unit size (0 = read-only buffer) */
45
+ };
46
+
47
+ /* BUFPUTSL: optimized bufputs of a string literal */
48
+ #define BUFPUTSL(output, literal) \
49
+ bufput(output, literal, sizeof literal - 1)
50
+
51
+ /* bufgrow: increasing the allocated size to the given value */
52
+ int bufgrow(struct buf *, size_t);
53
+
54
+ /* bufnew: allocation of a new buffer */
55
+ struct buf *bufnew(size_t) __attribute__ ((malloc));
56
+
57
+ /* bufnullterm: NUL-termination of the string array (making a C-string) */
58
+ const char *bufcstr(struct buf *);
59
+
60
+ /* bufprefix: compare the beginning of a buffer with a string */
61
+ int bufprefix(const struct buf *buf, const char *prefix);
62
+
63
+ /* bufput: appends raw data to a buffer */
64
+ void bufput(struct buf *, const void *, size_t);
65
+
66
+ /* bufputs: appends a NUL-terminated string to a buffer */
67
+ void bufputs(struct buf *, const char *);
68
+
69
+ /* bufputc: appends a single char to a buffer */
70
+ void bufputc(struct buf *, int);
71
+
72
+ /* bufrelease: decrease the reference count and free the buffer if needed */
73
+ void bufrelease(struct buf *);
74
+
75
+ /* bufprintf: formatted printing to a buffer */
76
+ void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
77
+
78
+ #ifdef __cplusplus
79
+ }
80
+ #endif
81
+
82
+ #endif
83
+
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS << ' -fvisibility=hidden'
4
+
5
+ dir_config('redcarpet')
6
+ create_makefile('redcarpet')
@@ -0,0 +1,29 @@
1
+ #ifndef HOUDINI_H__
2
+ #define HOUDINI_H__
3
+
4
+ #include "buffer.h"
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ #ifdef HOUDINI_USE_LOCALE
11
+ # define _isxdigit(c) isxdigit(c)
12
+ # define _isdigit(c) isdigit(c)
13
+ #else
14
+ /*
15
+ * Helper _isdigit methods -- do not trust the current locale
16
+ * */
17
+ # define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)
18
+ # define _isdigit(c) ((c) >= '0' && (c) <= '9')
19
+ #endif
20
+
21
+ extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size);
22
+ extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure);
23
+ extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size);
24
+
25
+ #ifdef __cplusplus
26
+ }
27
+ #endif
28
+
29
+ #endif
@@ -0,0 +1,108 @@
1
+ #include <assert.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+
5
+ #include "houdini.h"
6
+
7
+ #define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10)
8
+
9
+ /*
10
+ * The following characters will not be escaped:
11
+ *
12
+ * -_.+!*'(),%#@?=;:/,+&$ alphanum
13
+ *
14
+ * Note that this character set is the addition of:
15
+ *
16
+ * - The characters which are safe to be in an URL
17
+ * - The characters which are *not* safe to be in
18
+ * an URL because they are RESERVED characters.
19
+ *
20
+ * We asume (lazily) that any RESERVED char that
21
+ * appears inside an URL is actually meant to
22
+ * have its native function (i.e. as an URL
23
+ * component/separator) and hence needs no escaping.
24
+ *
25
+ * There are two exceptions: the chacters & (amp)
26
+ * and ' (single quote) do not appear in the table.
27
+ * They are meant to appear in the URL as components,
28
+ * yet they require special HTML-entity escaping
29
+ * to generate valid HTML markup.
30
+ *
31
+ * All other characters will be escaped to %XX.
32
+ *
33
+ */
34
+ static const char HREF_SAFE[] = {
35
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
+ 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
38
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
39
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
41
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
42
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
43
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51
+ };
52
+
53
+ void
54
+ houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)
55
+ {
56
+ static const char hex_chars[] = "0123456789ABCDEF";
57
+ size_t i = 0, org;
58
+ char hex_str[3];
59
+
60
+ bufgrow(ob, ESCAPE_GROW_FACTOR(size));
61
+ hex_str[0] = '%';
62
+
63
+ while (i < size) {
64
+ org = i;
65
+ while (i < size && HREF_SAFE[src[i]] != 0)
66
+ i++;
67
+
68
+ if (i > org)
69
+ bufput(ob, src + org, i - org);
70
+
71
+ /* escaping */
72
+ if (i >= size)
73
+ break;
74
+
75
+ switch (src[i]) {
76
+ /* amp appears all the time in URLs, but needs
77
+ * HTML-entity escaping to be inside an href */
78
+ case '&':
79
+ BUFPUTSL(ob, "&amp;");
80
+ break;
81
+
82
+ /* the single quote is a valid URL character
83
+ * according to the standard; it needs HTML
84
+ * entity escaping too */
85
+ case '\'':
86
+ BUFPUTSL(ob, "&#x27;");
87
+ break;
88
+
89
+ /* the space can be escaped to %20 or a plus
90
+ * sign. we're going with the generic escape
91
+ * for now. the plus thing is more commonly seen
92
+ * when building GET strings */
93
+ #if 0
94
+ case ' ':
95
+ bufputc(ob, '+');
96
+ break;
97
+ #endif
98
+
99
+ /* every other character goes with a %XX escaping */
100
+ default:
101
+ hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
102
+ hex_str[2] = hex_chars[src[i] & 0xF];
103
+ bufput(ob, hex_str, 3);
104
+ }
105
+
106
+ i++;
107
+ }
108
+ }
@@ -0,0 +1,83 @@
1
+ #include <assert.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+
5
+ #include "houdini.h"
6
+
7
+ #define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */
8
+
9
+ /**
10
+ * According to the OWASP rules:
11
+ *
12
+ * & --> &amp;
13
+ * < --> &lt;
14
+ * > --> &gt;
15
+ * " --> &quot;
16
+ * ' --> &#x27; &apos; is not recommended
17
+ * / --> &#x2F; forward slash is included as it helps end an HTML entity
18
+ *
19
+ */
20
+ static const char HTML_ESCAPE_TABLE[] = {
21
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23
+ 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
24
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
25
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
+ };
38
+
39
+ static const char *HTML_ESCAPES[] = {
40
+ "",
41
+ "&quot;",
42
+ "&amp;",
43
+ "&#39;",
44
+ "&#47;",
45
+ "&lt;",
46
+ "&gt;"
47
+ };
48
+
49
+ void
50
+ houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure)
51
+ {
52
+ size_t i = 0, org, esc = 0;
53
+
54
+ bufgrow(ob, ESCAPE_GROW_FACTOR(size));
55
+
56
+ while (i < size) {
57
+ org = i;
58
+ while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
59
+ i++;
60
+
61
+ if (i > org)
62
+ bufput(ob, src + org, i - org);
63
+
64
+ /* escaping */
65
+ if (i >= size)
66
+ break;
67
+
68
+ /* The forward slash is only escaped in secure mode */
69
+ if ( ( src[i] == '/' || src[i] == '\'' || src[i] == '"' ) && !secure) {
70
+ bufputc(ob, src[i]);
71
+ } else {
72
+ bufputs(ob, HTML_ESCAPES[esc]);
73
+ }
74
+ i++;
75
+ }
76
+ }
77
+
78
+ void
79
+ houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)
80
+ {
81
+ houdini_escape_html0(ob, src, size, 1);
82
+ }
83
+
@@ -0,0 +1,770 @@
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 "html.h"
20
+ #include "ruby.h"
21
+ #include <string.h>
22
+ #include <stdlib.h>
23
+ #include <stdio.h>
24
+ #include <ctype.h>
25
+
26
+ #include "houdini.h"
27
+
28
+ #define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)
29
+
30
+ int
31
+ sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
32
+ {
33
+ size_t i;
34
+ int closed = 0;
35
+
36
+ if (tag_size < 3 || tag_data[0] != '<')
37
+ return HTML_TAG_NONE;
38
+
39
+ i = 1;
40
+
41
+ if (tag_data[i] == '/') {
42
+ closed = 1;
43
+ i++;
44
+ }
45
+
46
+ for (; i < tag_size; ++i, ++tagname) {
47
+ if (*tagname == 0)
48
+ break;
49
+
50
+ if (tag_data[i] != *tagname)
51
+ return HTML_TAG_NONE;
52
+ }
53
+
54
+ if (i == tag_size)
55
+ return HTML_TAG_NONE;
56
+
57
+ if (isspace(tag_data[i]) || tag_data[i] == '>')
58
+ return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
59
+
60
+ return HTML_TAG_NONE;
61
+ }
62
+
63
+ static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length)
64
+ {
65
+ houdini_escape_html0(ob, source, length, 0);
66
+ }
67
+
68
+ static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length)
69
+ {
70
+ houdini_escape_href(ob, source, length);
71
+ }
72
+
73
+ /********************
74
+ * GENERIC RENDERER *
75
+ ********************/
76
+ static int
77
+ rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)
78
+ {
79
+ struct html_renderopt *options = opaque;
80
+
81
+ if (!link || !link->size)
82
+ return 0;
83
+
84
+ if ((options->flags & HTML_SAFELINK) != 0 &&
85
+ !sd_autolink_issafe(link->data, link->size) &&
86
+ type != MKDA_EMAIL)
87
+ return 0;
88
+
89
+ BUFPUTSL(ob, "<a href=\"");
90
+ if (type == MKDA_EMAIL)
91
+ BUFPUTSL(ob, "mailto:");
92
+ escape_href(ob, link->data, link->size);
93
+
94
+ if (options->link_attributes) {
95
+ bufputc(ob, '\"');
96
+ options->link_attributes(ob, link, opaque);
97
+ bufputc(ob, '>');
98
+ } else {
99
+ BUFPUTSL(ob, "\">");
100
+ }
101
+
102
+ /*
103
+ * Pretty printing: if we get an email address as
104
+ * an actual URI, e.g. `mailto:foo@bar.com`, we don't
105
+ * want to print the `mailto:` prefix
106
+ */
107
+ if (bufprefix(link, "mailto:") == 0) {
108
+ escape_html(ob, link->data + 7, link->size - 7);
109
+ } else {
110
+ escape_html(ob, link->data, link->size);
111
+ }
112
+
113
+ BUFPUTSL(ob, "</a>");
114
+
115
+ return 1;
116
+ }
117
+
118
+ static void
119
+ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
120
+ {
121
+ struct html_renderopt *options = opaque;
122
+
123
+ if (ob->size) bufputc(ob, '\n');
124
+
125
+ if (lang && lang->size) {
126
+ size_t i, cls;
127
+ if (options->flags & HTML_PRETTIFY) {
128
+ BUFPUTSL(ob, "<pre><code class=\"prettyprint ");
129
+ cls++;
130
+ } else {
131
+ BUFPUTSL(ob, "<pre><code class=\"");
132
+ }
133
+
134
+ for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
135
+ while (i < lang->size && isspace(lang->data[i]))
136
+ i++;
137
+
138
+ if (i < lang->size) {
139
+ size_t org = i;
140
+ while (i < lang->size && !isspace(lang->data[i]))
141
+ i++;
142
+
143
+ if (lang->data[org] == '.')
144
+ org++;
145
+
146
+ if (cls) bufputc(ob, ' ');
147
+ escape_html(ob, lang->data + org, i - org);
148
+ }
149
+ }
150
+
151
+ BUFPUTSL(ob, "\">");
152
+ } else if (options->flags & HTML_PRETTIFY) {
153
+ BUFPUTSL(ob, "<pre><code class=\"prettyprint\">");
154
+ } else {
155
+ BUFPUTSL(ob, "<pre><code>");
156
+ }
157
+
158
+ if (text)
159
+ escape_html(ob, text->data, text->size);
160
+
161
+ BUFPUTSL(ob, "</code></pre>\n");
162
+ }
163
+
164
+ static void
165
+ rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
166
+ {
167
+ if (ob->size) bufputc(ob, '\n');
168
+ BUFPUTSL(ob, "<blockquote>\n");
169
+ if (text) bufput(ob, text->data, text->size);
170
+ BUFPUTSL(ob, "</blockquote>\n");
171
+ }
172
+
173
+ static int
174
+ rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)
175
+ {
176
+ struct html_renderopt *options = opaque;
177
+ if (options->flags & HTML_PRETTIFY)
178
+ BUFPUTSL(ob, "<code class=\"prettyprint\">");
179
+ else
180
+ BUFPUTSL(ob, "<code>");
181
+ if (text) escape_html(ob, text->data, text->size);
182
+ BUFPUTSL(ob, "</code>");
183
+ return 1;
184
+ }
185
+
186
+ static int
187
+ rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)
188
+ {
189
+ if (!text || !text->size)
190
+ return 0;
191
+
192
+ BUFPUTSL(ob, "<del>");
193
+ bufput(ob, text->data, text->size);
194
+ BUFPUTSL(ob, "</del>");
195
+ return 1;
196
+ }
197
+
198
+ static int
199
+ rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)
200
+ {
201
+ if (!text || !text->size)
202
+ return 0;
203
+
204
+ BUFPUTSL(ob, "<strong>");
205
+ bufput(ob, text->data, text->size);
206
+ BUFPUTSL(ob, "</strong>");
207
+
208
+ return 1;
209
+ }
210
+
211
+ static int
212
+ rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)
213
+ {
214
+ if (!text || !text->size) return 0;
215
+ BUFPUTSL(ob, "<em>");
216
+ if (text) bufput(ob, text->data, text->size);
217
+ BUFPUTSL(ob, "</em>");
218
+ return 1;
219
+ }
220
+
221
+ static int
222
+ rndr_underline(struct buf *ob, const struct buf *text, void *opaque)
223
+ {
224
+ if (!text || !text->size)
225
+ return 0;
226
+
227
+ BUFPUTSL(ob, "<u>");
228
+ bufput(ob, text->data, text->size);
229
+ BUFPUTSL(ob, "</u>");
230
+
231
+ return 1;
232
+ }
233
+
234
+ static int
235
+ rndr_highlight(struct buf *ob, const struct buf *text, void *opaque)
236
+ {
237
+ if (!text || !text->size)
238
+ return 0;
239
+
240
+ BUFPUTSL(ob, "<mark>");
241
+ bufput(ob, text->data, text->size);
242
+ BUFPUTSL(ob, "</mark>");
243
+
244
+ return 1;
245
+ }
246
+
247
+ static int
248
+ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
249
+ {
250
+ if (!text || !text->size)
251
+ return 0;
252
+
253
+ BUFPUTSL(ob, "<q>");
254
+ bufput(ob, text->data, text->size);
255
+ BUFPUTSL(ob, "</q>");
256
+
257
+ return 1;
258
+ }
259
+
260
+ static int
261
+ rndr_linebreak(struct buf *ob, void *opaque)
262
+ {
263
+ struct html_renderopt *options = opaque;
264
+ bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");
265
+ return 1;
266
+ }
267
+
268
+ char *header_anchor(struct buf *text)
269
+ {
270
+ VALUE str = rb_str_new2(bufcstr(text));
271
+ VALUE space_regex = rb_reg_new(" +", 2 /* length */, 0);
272
+ VALUE tags_regex = rb_reg_new("<\\/?[^>]*>", 10, 0);
273
+
274
+ VALUE heading = rb_funcall(str, rb_intern("gsub"), 2, space_regex, rb_str_new2("-"));
275
+ heading = rb_funcall(heading, rb_intern("gsub"), 2, tags_regex, rb_str_new2(""));
276
+ heading = rb_funcall(heading, rb_intern("downcase"), 0);
277
+
278
+ return StringValueCStr(heading);
279
+ }
280
+
281
+ static void
282
+ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
283
+ {
284
+ struct html_renderopt *options = opaque;
285
+
286
+ if (ob->size)
287
+ bufputc(ob, '\n');
288
+
289
+ if ((options->flags & HTML_TOC) && (level <= options->toc_data.nesting_level))
290
+ bufprintf(ob, "<h%d id=\"%s\">", level, header_anchor(text));
291
+ else
292
+ bufprintf(ob, "<h%d>", level);
293
+
294
+ if (text) bufput(ob, text->data, text->size);
295
+ bufprintf(ob, "</h%d>\n", level);
296
+ }
297
+
298
+ static int
299
+ rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
300
+ {
301
+ struct html_renderopt *options = opaque;
302
+
303
+ if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
304
+ return 0;
305
+
306
+ BUFPUTSL(ob, "<a href=\"");
307
+
308
+ if (link && link->size)
309
+ escape_href(ob, link->data, link->size);
310
+
311
+ if (title && title->size) {
312
+ BUFPUTSL(ob, "\" title=\"");
313
+ escape_html(ob, title->data, title->size);
314
+ }
315
+
316
+ if (options->link_attributes) {
317
+ bufputc(ob, '\"');
318
+ options->link_attributes(ob, link, opaque);
319
+ bufputc(ob, '>');
320
+ } else {
321
+ BUFPUTSL(ob, "\">");
322
+ }
323
+
324
+ if (content && content->size) bufput(ob, content->data, content->size);
325
+ BUFPUTSL(ob, "</a>");
326
+ return 1;
327
+ }
328
+
329
+ static void
330
+ rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)
331
+ {
332
+ if (ob->size) bufputc(ob, '\n');
333
+ bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
334
+ if (text) bufput(ob, text->data, text->size);
335
+ bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
336
+ }
337
+
338
+ static void
339
+ rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)
340
+ {
341
+ BUFPUTSL(ob, "<li>");
342
+ if (text) {
343
+ size_t size = text->size;
344
+ while (size && text->data[size - 1] == '\n')
345
+ size--;
346
+
347
+ bufput(ob, text->data, size);
348
+ }
349
+ BUFPUTSL(ob, "</li>\n");
350
+ }
351
+
352
+ static void
353
+ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
354
+ {
355
+ struct html_renderopt *options = opaque;
356
+ size_t i = 0;
357
+
358
+ if (ob->size) bufputc(ob, '\n');
359
+
360
+ if (!text || !text->size)
361
+ return;
362
+
363
+ while (i < text->size && isspace(text->data[i])) i++;
364
+
365
+ if (i == text->size)
366
+ return;
367
+
368
+ BUFPUTSL(ob, "<p>");
369
+ if (options->flags & HTML_HARD_WRAP) {
370
+ size_t org;
371
+ while (i < text->size) {
372
+ org = i;
373
+ while (i < text->size && text->data[i] != '\n')
374
+ i++;
375
+
376
+ if (i > org)
377
+ bufput(ob, text->data + org, i - org);
378
+
379
+ /*
380
+ * do not insert a line break if this newline
381
+ * is the last character on the paragraph
382
+ */
383
+ if (i >= text->size - 1)
384
+ break;
385
+
386
+ rndr_linebreak(ob, opaque);
387
+ i++;
388
+ }
389
+ } else {
390
+ bufput(ob, &text->data[i], text->size - i);
391
+ }
392
+ BUFPUTSL(ob, "</p>\n");
393
+ }
394
+
395
+ static void
396
+ rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
397
+ {
398
+ size_t org, sz;
399
+ if (!text) return;
400
+ sz = text->size;
401
+ while (sz > 0 && text->data[sz - 1] == '\n') sz--;
402
+ org = 0;
403
+ while (org < sz && text->data[org] == '\n') org++;
404
+ if (org >= sz) return;
405
+ if (ob->size) bufputc(ob, '\n');
406
+ bufput(ob, text->data + org, sz - org);
407
+ bufputc(ob, '\n');
408
+ }
409
+
410
+ static int
411
+ rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)
412
+ {
413
+ if (!text || !text->size) return 0;
414
+ BUFPUTSL(ob, "<strong><em>");
415
+ bufput(ob, text->data, text->size);
416
+ BUFPUTSL(ob, "</em></strong>");
417
+ return 1;
418
+ }
419
+
420
+ static void
421
+ rndr_hrule(struct buf *ob, void *opaque)
422
+ {
423
+ struct html_renderopt *options = opaque;
424
+ if (ob->size) bufputc(ob, '\n');
425
+ bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
426
+ }
427
+
428
+ static int
429
+ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
430
+ {
431
+ struct html_renderopt *options = opaque;
432
+ if (!link || !link->size) return 0;
433
+
434
+ BUFPUTSL(ob, "<img src=\"");
435
+ escape_href(ob, link->data, link->size);
436
+ BUFPUTSL(ob, "\" alt=\"");
437
+
438
+ if (alt && alt->size)
439
+ escape_html(ob, alt->data, alt->size);
440
+
441
+ if (title && title->size) {
442
+ BUFPUTSL(ob, "\" title=\"");
443
+ escape_html(ob, title->data, title->size); }
444
+
445
+ bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
446
+ return 1;
447
+ }
448
+
449
+ static int
450
+ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
451
+ {
452
+ struct html_renderopt *options = opaque;
453
+
454
+ /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
455
+ * It doens't see if there are any valid tags, just escape all of them. */
456
+ if((options->flags & HTML_ESCAPE) != 0) {
457
+ escape_html(ob, text->data, text->size);
458
+ return 1;
459
+ }
460
+
461
+ if ((options->flags & HTML_SKIP_HTML) != 0)
462
+ return 1;
463
+
464
+ if ((options->flags & HTML_SKIP_STYLE) != 0 &&
465
+ sdhtml_is_tag(text->data, text->size, "style"))
466
+ return 1;
467
+
468
+ if ((options->flags & HTML_SKIP_LINKS) != 0 &&
469
+ sdhtml_is_tag(text->data, text->size, "a"))
470
+ return 1;
471
+
472
+ if ((options->flags & HTML_SKIP_IMAGES) != 0 &&
473
+ sdhtml_is_tag(text->data, text->size, "img"))
474
+ return 1;
475
+
476
+ bufput(ob, text->data, text->size);
477
+ return 1;
478
+ }
479
+
480
+ static void
481
+ rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)
482
+ {
483
+ if (ob->size) bufputc(ob, '\n');
484
+ BUFPUTSL(ob, "<table><thead>\n");
485
+ if (header)
486
+ bufput(ob, header->data, header->size);
487
+ BUFPUTSL(ob, "</thead><tbody>\n");
488
+ if (body)
489
+ bufput(ob, body->data, body->size);
490
+ BUFPUTSL(ob, "</tbody></table>\n");
491
+ }
492
+
493
+ static void
494
+ rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)
495
+ {
496
+ BUFPUTSL(ob, "<tr>\n");
497
+ if (text)
498
+ bufput(ob, text->data, text->size);
499
+ BUFPUTSL(ob, "</tr>\n");
500
+ }
501
+
502
+ static void
503
+ rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque)
504
+ {
505
+ if (flags & MKD_TABLE_HEADER) {
506
+ BUFPUTSL(ob, "<th");
507
+ } else {
508
+ BUFPUTSL(ob, "<td");
509
+ }
510
+
511
+ switch (flags & MKD_TABLE_ALIGNMASK) {
512
+ case MKD_TABLE_ALIGN_CENTER:
513
+ BUFPUTSL(ob, " style=\"text-align: center\">");
514
+ break;
515
+
516
+ case MKD_TABLE_ALIGN_L:
517
+ BUFPUTSL(ob, " style=\"text-align: left\">");
518
+ break;
519
+
520
+ case MKD_TABLE_ALIGN_R:
521
+ BUFPUTSL(ob, " style=\"text-align: right\">");
522
+ break;
523
+
524
+ default:
525
+ BUFPUTSL(ob, ">");
526
+ }
527
+
528
+ if (text)
529
+ bufput(ob, text->data, text->size);
530
+
531
+ if (flags & MKD_TABLE_HEADER) {
532
+ BUFPUTSL(ob, "</th>\n");
533
+ } else {
534
+ BUFPUTSL(ob, "</td>\n");
535
+ }
536
+ }
537
+
538
+ static int
539
+ rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)
540
+ {
541
+ if (!text || !text->size) return 0;
542
+ BUFPUTSL(ob, "<sup>");
543
+ bufput(ob, text->data, text->size);
544
+ BUFPUTSL(ob, "</sup>");
545
+ return 1;
546
+ }
547
+
548
+ static void
549
+ rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)
550
+ {
551
+ if (text)
552
+ escape_html(ob, text->data, text->size);
553
+ }
554
+
555
+ static void
556
+ rndr_footnotes(struct buf *ob, const struct buf *text, void *opaque)
557
+ {
558
+ struct html_renderopt *options = opaque;
559
+
560
+ if (ob->size) bufputc(ob, '\n');
561
+
562
+ BUFPUTSL(ob, "<div class=\"footnotes\">\n");
563
+ bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
564
+ BUFPUTSL(ob, "<ol>\n");
565
+
566
+ if (text)
567
+ bufput(ob, text->data, text->size);
568
+
569
+ BUFPUTSL(ob, "\n</ol>\n</div>\n");
570
+ }
571
+
572
+ static void
573
+ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void *opaque)
574
+ {
575
+ size_t i = 0;
576
+ int pfound = 0;
577
+
578
+ /* insert anchor at the end of first paragraph block */
579
+ if (text) {
580
+ while ((i+3) < text->size) {
581
+ if (text->data[i++] != '<') continue;
582
+ if (text->data[i++] != '/') continue;
583
+ if (text->data[i++] != 'p' && text->data[i] != 'P') continue;
584
+ if (text->data[i] != '>') continue;
585
+ i -= 3;
586
+ pfound = 1;
587
+ break;
588
+ }
589
+ }
590
+
591
+ bufprintf(ob, "\n<li id=\"fn%d\">\n", num);
592
+ if (pfound) {
593
+ bufput(ob, text->data, i);
594
+ bufprintf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
595
+ bufput(ob, text->data + i, text->size - i);
596
+ } else if (text) {
597
+ bufput(ob, text->data, text->size);
598
+ }
599
+ BUFPUTSL(ob, "</li>\n");
600
+ }
601
+
602
+ static int
603
+ rndr_footnote_ref(struct buf *ob, unsigned int num, void *opaque)
604
+ {
605
+ bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
606
+ return 1;
607
+ }
608
+
609
+ static void
610
+ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
611
+ {
612
+ struct html_renderopt *options = opaque;
613
+
614
+ if (level <= options->toc_data.nesting_level) {
615
+ /* set the level offset if this is the first header
616
+ * we're parsing for the document */
617
+ if (options->toc_data.current_level == 0)
618
+ options->toc_data.level_offset = level - 1;
619
+
620
+ level -= options->toc_data.level_offset;
621
+
622
+ if (level > options->toc_data.current_level) {
623
+ while (level > options->toc_data.current_level) {
624
+ BUFPUTSL(ob, "<ul>\n<li>\n");
625
+ options->toc_data.current_level++;
626
+ }
627
+ } else if (level < options->toc_data.current_level) {
628
+ BUFPUTSL(ob, "</li>\n");
629
+ while (level < options->toc_data.current_level) {
630
+ BUFPUTSL(ob, "</ul>\n</li>\n");
631
+ options->toc_data.current_level--;
632
+ }
633
+ BUFPUTSL(ob,"<li>\n");
634
+ } else {
635
+ BUFPUTSL(ob,"</li>\n<li>\n");
636
+ }
637
+
638
+ bufprintf(ob, "<a href=\"#%s\">", header_anchor(text));
639
+ if (text) escape_html(ob, text->data, text->size);
640
+ BUFPUTSL(ob, "</a>\n");
641
+ }
642
+ }
643
+
644
+ static int
645
+ toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
646
+ {
647
+ if (content && content->size)
648
+ bufput(ob, content->data, content->size);
649
+ return 1;
650
+ }
651
+
652
+ static void
653
+ toc_finalize(struct buf *ob, void *opaque)
654
+ {
655
+ struct html_renderopt *options = opaque;
656
+
657
+ while (options->toc_data.current_level > 0) {
658
+ BUFPUTSL(ob, "</li>\n</ul>\n");
659
+ options->toc_data.current_level--;
660
+ }
661
+ }
662
+
663
+ void
664
+ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, int nesting_level)
665
+ {
666
+ static const struct sd_callbacks cb_default = {
667
+ NULL,
668
+ NULL,
669
+ NULL,
670
+ toc_header,
671
+ NULL,
672
+ NULL,
673
+ NULL,
674
+ NULL,
675
+ NULL,
676
+ NULL,
677
+ NULL,
678
+ rndr_footnotes,
679
+ rndr_footnote_def,
680
+
681
+ NULL,
682
+ rndr_codespan,
683
+ rndr_double_emphasis,
684
+ rndr_emphasis,
685
+ rndr_underline,
686
+ rndr_highlight,
687
+ rndr_quote,
688
+ NULL,
689
+ NULL,
690
+ toc_link,
691
+ NULL,
692
+ rndr_triple_emphasis,
693
+ rndr_strikethrough,
694
+ rndr_superscript,
695
+ rndr_footnote_ref,
696
+
697
+ NULL,
698
+ NULL,
699
+
700
+ NULL,
701
+ toc_finalize,
702
+ };
703
+
704
+ memset(options, 0x0, sizeof(struct html_renderopt));
705
+ options->flags = HTML_TOC;
706
+ options->toc_data.nesting_level = nesting_level;
707
+
708
+ memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
709
+ }
710
+
711
+ void
712
+ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
713
+ {
714
+ static const struct sd_callbacks cb_default = {
715
+ rndr_blockcode,
716
+ rndr_blockquote,
717
+ rndr_raw_block,
718
+ rndr_header,
719
+ rndr_hrule,
720
+ rndr_list,
721
+ rndr_listitem,
722
+ rndr_paragraph,
723
+ rndr_table,
724
+ rndr_tablerow,
725
+ rndr_tablecell,
726
+ rndr_footnotes,
727
+ rndr_footnote_def,
728
+
729
+ rndr_autolink,
730
+ rndr_codespan,
731
+ rndr_double_emphasis,
732
+ rndr_emphasis,
733
+ rndr_underline,
734
+ rndr_highlight,
735
+ rndr_quote,
736
+ rndr_image,
737
+ rndr_linebreak,
738
+ rndr_link,
739
+ rndr_raw_html,
740
+ rndr_triple_emphasis,
741
+ rndr_strikethrough,
742
+ rndr_superscript,
743
+ rndr_footnote_ref,
744
+
745
+ NULL,
746
+ rndr_normal_text,
747
+
748
+ NULL,
749
+ NULL,
750
+ };
751
+
752
+ /* Prepare the options pointer */
753
+ memset(options, 0x0, sizeof(struct html_renderopt));
754
+ options->flags = render_flags;
755
+ options->toc_data.nesting_level = 99;
756
+
757
+ /* Prepare the callbacks */
758
+ memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
759
+
760
+ if (render_flags & HTML_SKIP_IMAGES)
761
+ callbacks->image = NULL;
762
+
763
+ if (render_flags & HTML_SKIP_LINKS) {
764
+ callbacks->link = NULL;
765
+ callbacks->autolink = NULL;
766
+ }
767
+
768
+ if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE)
769
+ callbacks->blockhtml = NULL;
770
+ }