greenmat 3.2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +14 -0
  3. data/Gemfile +9 -0
  4. data/README.md +36 -0
  5. data/Rakefile +62 -0
  6. data/bin/greenmat +7 -0
  7. data/ext/greenmat/autolink.c +296 -0
  8. data/ext/greenmat/autolink.h +49 -0
  9. data/ext/greenmat/buffer.c +196 -0
  10. data/ext/greenmat/buffer.h +83 -0
  11. data/ext/greenmat/extconf.rb +6 -0
  12. data/ext/greenmat/gm_markdown.c +161 -0
  13. data/ext/greenmat/gm_render.c +534 -0
  14. data/ext/greenmat/greenmat.h +30 -0
  15. data/ext/greenmat/houdini.h +29 -0
  16. data/ext/greenmat/houdini_href_e.c +108 -0
  17. data/ext/greenmat/houdini_html_e.c +83 -0
  18. data/ext/greenmat/html.c +826 -0
  19. data/ext/greenmat/html.h +84 -0
  20. data/ext/greenmat/html_blocks.h +229 -0
  21. data/ext/greenmat/html_smartypants.c +445 -0
  22. data/ext/greenmat/markdown.c +2912 -0
  23. data/ext/greenmat/markdown.h +138 -0
  24. data/ext/greenmat/stack.c +62 -0
  25. data/ext/greenmat/stack.h +26 -0
  26. data/greenmat.gemspec +72 -0
  27. data/lib/greenmat.rb +92 -0
  28. data/lib/greenmat/compat.rb +73 -0
  29. data/lib/greenmat/render_man.rb +65 -0
  30. data/lib/greenmat/render_strip.rb +48 -0
  31. data/test/benchmark.rb +24 -0
  32. data/test/custom_render_test.rb +28 -0
  33. data/test/greenmat_compat_test.rb +38 -0
  34. data/test/html5_test.rb +69 -0
  35. data/test/html_render_test.rb +241 -0
  36. data/test/html_toc_render_test.rb +76 -0
  37. data/test/markdown_test.rb +337 -0
  38. data/test/pathological_inputs_test.rb +34 -0
  39. data/test/safe_render_test.rb +36 -0
  40. data/test/smarty_html_test.rb +45 -0
  41. data/test/smarty_pants_test.rb +48 -0
  42. data/test/stripdown_render_test.rb +40 -0
  43. data/test/test_helper.rb +33 -0
  44. metadata +158 -0
@@ -0,0 +1,30 @@
1
+ #ifndef GREENMAT_H__
2
+ #define GREENMAT_H__
3
+
4
+ #define RSTRING_NOT_MODIFIED
5
+ #include "ruby.h"
6
+ #include <stdio.h>
7
+
8
+ #include <ruby/encoding.h>
9
+
10
+ #include "markdown.h"
11
+ #include "html.h"
12
+
13
+ #define CSTR2SYM(s) (ID2SYM(rb_intern((s))))
14
+
15
+ void Init_greenmat_rndr();
16
+
17
+ struct greenmat_renderopt {
18
+ struct html_renderopt html;
19
+ VALUE link_attributes;
20
+ VALUE self;
21
+ VALUE base_class;
22
+ rb_encoding *active_enc;
23
+ };
24
+
25
+ struct rb_greenmat_rndr {
26
+ struct sd_callbacks callbacks;
27
+ struct greenmat_renderopt options;
28
+ };
29
+
30
+ #endif
@@ -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] == '/' && !secure)
70
+ bufputc(ob, '/');
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,826 @@
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 <string.h>
21
+ #include <stdlib.h>
22
+ #include <stdio.h>
23
+ #include <ctype.h>
24
+
25
+ #include "houdini.h"
26
+
27
+ #define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)
28
+
29
+ int
30
+ sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
31
+ {
32
+ size_t i;
33
+ int closed = 0;
34
+
35
+ if (tag_size < 3 || tag_data[0] != '<')
36
+ return HTML_TAG_NONE;
37
+
38
+ i = 1;
39
+
40
+ if (tag_data[i] == '/') {
41
+ closed = 1;
42
+ i++;
43
+ }
44
+
45
+ for (; i < tag_size; ++i, ++tagname) {
46
+ if (*tagname == 0)
47
+ break;
48
+
49
+ if (tag_data[i] != *tagname)
50
+ return HTML_TAG_NONE;
51
+ }
52
+
53
+ if (i == tag_size)
54
+ return HTML_TAG_NONE;
55
+
56
+ if (isspace(tag_data[i]) || tag_data[i] == '>')
57
+ return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
58
+
59
+ return HTML_TAG_NONE;
60
+ }
61
+
62
+ static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length)
63
+ {
64
+ houdini_escape_html0(ob, source, length, 0);
65
+ }
66
+
67
+ static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length)
68
+ {
69
+ houdini_escape_href(ob, source, length);
70
+ }
71
+
72
+ /********************
73
+ * GENERIC RENDERER *
74
+ ********************/
75
+ static int
76
+ rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)
77
+ {
78
+ struct html_renderopt *options = opaque;
79
+
80
+ if (!link || !link->size)
81
+ return 0;
82
+
83
+ if ((options->flags & HTML_SAFELINK) != 0 &&
84
+ !sd_autolink_issafe(link->data, link->size) &&
85
+ type != MKDA_EMAIL)
86
+ return 0;
87
+
88
+ BUFPUTSL(ob, "<a href=\"");
89
+ if (type == MKDA_EMAIL)
90
+ BUFPUTSL(ob, "mailto:");
91
+ escape_href(ob, link->data, link->size);
92
+
93
+ if (options->link_attributes) {
94
+ bufputc(ob, '\"');
95
+ options->link_attributes(ob, link, opaque);
96
+ bufputc(ob, '>');
97
+ } else {
98
+ BUFPUTSL(ob, "\">");
99
+ }
100
+
101
+ /*
102
+ * Pretty printing: if we get an email address as
103
+ * an actual URI, e.g. `mailto:foo@bar.com`, we don't
104
+ * want to print the `mailto:` prefix
105
+ */
106
+ if (bufprefix(link, "mailto:") == 0) {
107
+ escape_html(ob, link->data + 7, link->size - 7);
108
+ } else {
109
+ escape_html(ob, link->data, link->size);
110
+ }
111
+
112
+ BUFPUTSL(ob, "</a>");
113
+
114
+ return 1;
115
+ }
116
+
117
+ static void
118
+ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
119
+ {
120
+ struct html_renderopt *options = opaque;
121
+
122
+ if (ob->size) bufputc(ob, '\n');
123
+
124
+ if (lang && lang->size) {
125
+ size_t i, cls;
126
+ if (options->flags & HTML_PRETTIFY) {
127
+ BUFPUTSL(ob, "<pre><code class=\"prettyprint lang-");
128
+ cls++;
129
+ } else {
130
+ BUFPUTSL(ob, "<pre><code class=\"");
131
+ }
132
+
133
+ for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
134
+ while (i < lang->size && isspace(lang->data[i]))
135
+ i++;
136
+
137
+ if (i < lang->size) {
138
+ size_t org = i;
139
+ while (i < lang->size && !isspace(lang->data[i]))
140
+ i++;
141
+
142
+ if (lang->data[org] == '.')
143
+ org++;
144
+
145
+ if (cls) bufputc(ob, ' ');
146
+ escape_html(ob, lang->data + org, i - org);
147
+ }
148
+ }
149
+
150
+ BUFPUTSL(ob, "\">");
151
+ } else if (options->flags & HTML_PRETTIFY) {
152
+ BUFPUTSL(ob, "<pre><code class=\"prettyprint\">");
153
+ } else {
154
+ BUFPUTSL(ob, "<pre><code>");
155
+ }
156
+
157
+ if (text)
158
+ escape_html(ob, text->data, text->size);
159
+
160
+ BUFPUTSL(ob, "</code></pre>\n");
161
+ }
162
+
163
+ static void
164
+ rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
165
+ {
166
+ if (ob->size) bufputc(ob, '\n');
167
+ BUFPUTSL(ob, "<blockquote>\n");
168
+ if (text) bufput(ob, text->data, text->size);
169
+ BUFPUTSL(ob, "</blockquote>\n");
170
+ }
171
+
172
+ static int
173
+ rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)
174
+ {
175
+ struct html_renderopt *options = opaque;
176
+ if (options->flags & HTML_PRETTIFY)
177
+ BUFPUTSL(ob, "<code class=\"prettyprint\">");
178
+ else
179
+ BUFPUTSL(ob, "<code>");
180
+ if (text) escape_html(ob, text->data, text->size);
181
+ BUFPUTSL(ob, "</code>");
182
+ return 1;
183
+ }
184
+
185
+ static int
186
+ rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)
187
+ {
188
+ if (!text || !text->size)
189
+ return 0;
190
+
191
+ BUFPUTSL(ob, "<del>");
192
+ bufput(ob, text->data, text->size);
193
+ BUFPUTSL(ob, "</del>");
194
+ return 1;
195
+ }
196
+
197
+ static int
198
+ rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)
199
+ {
200
+ if (!text || !text->size)
201
+ return 0;
202
+
203
+ BUFPUTSL(ob, "<strong>");
204
+ bufput(ob, text->data, text->size);
205
+ BUFPUTSL(ob, "</strong>");
206
+
207
+ return 1;
208
+ }
209
+
210
+ static int
211
+ rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)
212
+ {
213
+ if (!text || !text->size) return 0;
214
+ BUFPUTSL(ob, "<em>");
215
+ if (text) bufput(ob, text->data, text->size);
216
+ BUFPUTSL(ob, "</em>");
217
+ return 1;
218
+ }
219
+
220
+ static int
221
+ rndr_underline(struct buf *ob, const struct buf *text, void *opaque)
222
+ {
223
+ if (!text || !text->size)
224
+ return 0;
225
+
226
+ BUFPUTSL(ob, "<u>");
227
+ bufput(ob, text->data, text->size);
228
+ BUFPUTSL(ob, "</u>");
229
+
230
+ return 1;
231
+ }
232
+
233
+ static int
234
+ rndr_highlight(struct buf *ob, const struct buf *text, void *opaque)
235
+ {
236
+ if (!text || !text->size)
237
+ return 0;
238
+
239
+ BUFPUTSL(ob, "<mark>");
240
+ bufput(ob, text->data, text->size);
241
+ BUFPUTSL(ob, "</mark>");
242
+
243
+ return 1;
244
+ }
245
+
246
+ static int
247
+ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
248
+ {
249
+ if (!text || !text->size)
250
+ return 0;
251
+
252
+ BUFPUTSL(ob, "<q>");
253
+ bufput(ob, text->data, text->size);
254
+ BUFPUTSL(ob, "</q>");
255
+
256
+ return 1;
257
+ }
258
+
259
+ static int
260
+ rndr_linebreak(struct buf *ob, void *opaque)
261
+ {
262
+ struct html_renderopt *options = opaque;
263
+ bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");
264
+ return 1;
265
+ }
266
+
267
+ char *header_anchor(const struct buf *buffer)
268
+ {
269
+ size_t i, j, k, size = buffer->size;
270
+
271
+ char text[size];
272
+ strcpy(text, bufcstr(buffer));
273
+
274
+ char raw_string[size];
275
+
276
+ /* Strip down the inline HTML markup if needed */
277
+ if (strchr(text, '<') < strchr(text, '>')) {
278
+ char* part = strtok(text, "<>");
279
+
280
+ /* Once every two times, the yielded token is the
281
+ content of a HTML tag so we don't need to copy it */
282
+ for (k = 0; part != NULL; k++) {
283
+ if (k == 0)
284
+ strcpy(raw_string, part);
285
+ else if (k % 2 == 0)
286
+ strcat(raw_string, part);
287
+
288
+ part = strtok(NULL, "<>");
289
+ }
290
+
291
+ size = strlen(raw_string);
292
+ } else {
293
+ strcpy(raw_string, text);
294
+ }
295
+
296
+ char* heading = malloc(size * sizeof(char));
297
+
298
+ /* Dasherize the string removing extra white spaces
299
+ and stripped chars */
300
+ for (i = 0, j = 0; i < size; ++i, ++j) {
301
+ while ((i+1) < size && STRIPPED_CHAR(raw_string[i]) && STRIPPED_CHAR(raw_string[i+1]))
302
+ i++;
303
+
304
+ if (STRIPPED_CHAR(raw_string[i]))
305
+ heading[j] = '-';
306
+ else
307
+ heading[j] = tolower(raw_string[i]);
308
+ }
309
+
310
+ heading[j++] = '\0';
311
+ return heading;
312
+ }
313
+
314
+ static void
315
+ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
316
+ {
317
+ struct html_renderopt *options = opaque;
318
+
319
+ if (ob->size)
320
+ bufputc(ob, '\n');
321
+
322
+ if ((options->flags & HTML_TOC) && (level <= options->toc_data.nesting_level))
323
+ bufprintf(ob, "<h%d id=\"%s\">", level, header_anchor(text));
324
+ else
325
+ bufprintf(ob, "<h%d>", level);
326
+
327
+ if (text) bufput(ob, text->data, text->size);
328
+ bufprintf(ob, "</h%d>\n", level);
329
+ }
330
+
331
+ static int
332
+ rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
333
+ {
334
+ struct html_renderopt *options = opaque;
335
+
336
+ if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
337
+ return 0;
338
+
339
+ BUFPUTSL(ob, "<a href=\"");
340
+
341
+ if (link && link->size)
342
+ escape_href(ob, link->data, link->size);
343
+
344
+ if (title && title->size) {
345
+ BUFPUTSL(ob, "\" title=\"");
346
+ escape_html(ob, title->data, title->size);
347
+ }
348
+
349
+ if (options->link_attributes) {
350
+ bufputc(ob, '\"');
351
+ options->link_attributes(ob, link, opaque);
352
+ bufputc(ob, '>');
353
+ } else {
354
+ BUFPUTSL(ob, "\">");
355
+ }
356
+
357
+ if (content && content->size) bufput(ob, content->data, content->size);
358
+ BUFPUTSL(ob, "</a>");
359
+ return 1;
360
+ }
361
+
362
+ static void
363
+ rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)
364
+ {
365
+ if (ob->size) bufputc(ob, '\n');
366
+ bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
367
+ if (text) bufput(ob, text->data, text->size);
368
+ bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
369
+ }
370
+
371
+ static void
372
+ rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)
373
+ {
374
+ BUFPUTSL(ob, "<li>");
375
+ if (text) {
376
+ size_t size = text->size;
377
+ while (size && text->data[size - 1] == '\n')
378
+ size--;
379
+
380
+ bufput(ob, text->data, size);
381
+ }
382
+ BUFPUTSL(ob, "</li>\n");
383
+ }
384
+
385
+ static void
386
+ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
387
+ {
388
+ struct html_renderopt *options = opaque;
389
+ size_t i = 0;
390
+
391
+ if (ob->size) bufputc(ob, '\n');
392
+
393
+ if (!text || !text->size)
394
+ return;
395
+
396
+ while (i < text->size && isspace(text->data[i])) i++;
397
+
398
+ if (i == text->size)
399
+ return;
400
+
401
+ BUFPUTSL(ob, "<p>");
402
+ if (options->flags & HTML_HARD_WRAP) {
403
+ size_t org;
404
+ while (i < text->size) {
405
+ org = i;
406
+ while (i < text->size && text->data[i] != '\n')
407
+ i++;
408
+
409
+ if (i > org)
410
+ bufput(ob, text->data + org, i - org);
411
+
412
+ /*
413
+ * do not insert a line break if this newline
414
+ * is the last character on the paragraph
415
+ */
416
+ if (i >= text->size - 1)
417
+ break;
418
+
419
+ rndr_linebreak(ob, opaque);
420
+ i++;
421
+ }
422
+ } else {
423
+ bufput(ob, &text->data[i], text->size - i);
424
+ }
425
+ BUFPUTSL(ob, "</p>\n");
426
+ }
427
+
428
+ static void
429
+ rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
430
+ {
431
+ size_t org, size;
432
+ struct html_renderopt *options = opaque;
433
+
434
+ if (!text)
435
+ return;
436
+
437
+ size = text->size;
438
+ while (size > 0 && text->data[size - 1] == '\n')
439
+ size--;
440
+
441
+ for (org = 0; org < size && text->data[org] == '\n'; ++org)
442
+
443
+ if (org >= size)
444
+ return;
445
+
446
+ /* Remove style tags if the `:no_styles` option is enabled */
447
+ if ((options->flags & HTML_SKIP_STYLE) != 0 &&
448
+ sdhtml_is_tag(text->data, size, "style"))
449
+ return;
450
+
451
+ if (ob->size)
452
+ bufputc(ob, '\n');
453
+
454
+ bufput(ob, text->data + org, size - org);
455
+ bufputc(ob, '\n');
456
+ }
457
+
458
+ static int
459
+ rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)
460
+ {
461
+ if (!text || !text->size) return 0;
462
+ BUFPUTSL(ob, "<strong><em>");
463
+ bufput(ob, text->data, text->size);
464
+ BUFPUTSL(ob, "</em></strong>");
465
+ return 1;
466
+ }
467
+
468
+ static void
469
+ rndr_hrule(struct buf *ob, void *opaque)
470
+ {
471
+ struct html_renderopt *options = opaque;
472
+ if (ob->size) bufputc(ob, '\n');
473
+ bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
474
+ }
475
+
476
+ static int
477
+ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
478
+ {
479
+ struct html_renderopt *options = opaque;
480
+
481
+ if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
482
+ return 0;
483
+
484
+ BUFPUTSL(ob, "<img src=\"");
485
+ escape_href(ob, link->data, link->size);
486
+ BUFPUTSL(ob, "\" alt=\"");
487
+
488
+ if (alt && alt->size)
489
+ escape_html(ob, alt->data, alt->size);
490
+
491
+ if (title && title->size) {
492
+ BUFPUTSL(ob, "\" title=\"");
493
+ escape_html(ob, title->data, title->size); }
494
+
495
+ bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
496
+ return 1;
497
+ }
498
+
499
+ static int
500
+ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
501
+ {
502
+ struct html_renderopt *options = opaque;
503
+
504
+ /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
505
+ It doesn't see if there are any valid tags, just escape all of them. */
506
+ if((options->flags & HTML_ESCAPE) != 0) {
507
+ escape_html(ob, text->data, text->size);
508
+ return 1;
509
+ }
510
+
511
+ if ((options->flags & HTML_SKIP_HTML) != 0)
512
+ return 1;
513
+
514
+ if ((options->flags & HTML_SKIP_STYLE) != 0 &&
515
+ sdhtml_is_tag(text->data, text->size, "style"))
516
+ return 1;
517
+
518
+ if ((options->flags & HTML_SKIP_LINKS) != 0 &&
519
+ sdhtml_is_tag(text->data, text->size, "a"))
520
+ return 1;
521
+
522
+ if ((options->flags & HTML_SKIP_IMAGES) != 0 &&
523
+ sdhtml_is_tag(text->data, text->size, "img"))
524
+ return 1;
525
+
526
+ bufput(ob, text->data, text->size);
527
+ return 1;
528
+ }
529
+
530
+ static void
531
+ rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)
532
+ {
533
+ if (ob->size) bufputc(ob, '\n');
534
+ BUFPUTSL(ob, "<table><thead>\n");
535
+ if (header)
536
+ bufput(ob, header->data, header->size);
537
+ BUFPUTSL(ob, "</thead><tbody>\n");
538
+ if (body)
539
+ bufput(ob, body->data, body->size);
540
+ BUFPUTSL(ob, "</tbody></table>\n");
541
+ }
542
+
543
+ static void
544
+ rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)
545
+ {
546
+ BUFPUTSL(ob, "<tr>\n");
547
+ if (text)
548
+ bufput(ob, text->data, text->size);
549
+ BUFPUTSL(ob, "</tr>\n");
550
+ }
551
+
552
+ static void
553
+ rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque)
554
+ {
555
+ if (flags & MKD_TABLE_HEADER) {
556
+ BUFPUTSL(ob, "<th");
557
+ } else {
558
+ BUFPUTSL(ob, "<td");
559
+ }
560
+
561
+ switch (flags & MKD_TABLE_ALIGNMASK) {
562
+ case MKD_TABLE_ALIGN_CENTER:
563
+ BUFPUTSL(ob, " style=\"text-align: center\">");
564
+ break;
565
+
566
+ case MKD_TABLE_ALIGN_L:
567
+ BUFPUTSL(ob, " style=\"text-align: left\">");
568
+ break;
569
+
570
+ case MKD_TABLE_ALIGN_R:
571
+ BUFPUTSL(ob, " style=\"text-align: right\">");
572
+ break;
573
+
574
+ default:
575
+ BUFPUTSL(ob, ">");
576
+ }
577
+
578
+ if (text)
579
+ bufput(ob, text->data, text->size);
580
+
581
+ if (flags & MKD_TABLE_HEADER) {
582
+ BUFPUTSL(ob, "</th>\n");
583
+ } else {
584
+ BUFPUTSL(ob, "</td>\n");
585
+ }
586
+ }
587
+
588
+ static int
589
+ rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)
590
+ {
591
+ if (!text || !text->size) return 0;
592
+ BUFPUTSL(ob, "<sup>");
593
+ bufput(ob, text->data, text->size);
594
+ BUFPUTSL(ob, "</sup>");
595
+ return 1;
596
+ }
597
+
598
+ static void
599
+ rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)
600
+ {
601
+ if (text)
602
+ escape_html(ob, text->data, text->size);
603
+ }
604
+
605
+ static void
606
+ rndr_footnotes(struct buf *ob, const struct buf *text, void *opaque)
607
+ {
608
+ struct html_renderopt *options = opaque;
609
+
610
+ if (ob->size) bufputc(ob, '\n');
611
+
612
+ BUFPUTSL(ob, "<div class=\"footnotes\">\n");
613
+ bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
614
+ BUFPUTSL(ob, "<ol>\n");
615
+
616
+ if (text)
617
+ bufput(ob, text->data, text->size);
618
+
619
+ BUFPUTSL(ob, "\n</ol>\n</div>\n");
620
+ }
621
+
622
+ static void
623
+ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void *opaque)
624
+ {
625
+ size_t i = 0;
626
+ int pfound = 0;
627
+
628
+ /* insert anchor at the end of first paragraph block */
629
+ if (text) {
630
+ while ((i+3) < text->size) {
631
+ if (text->data[i++] != '<') continue;
632
+ if (text->data[i++] != '/') continue;
633
+ if (text->data[i++] != 'p' && text->data[i] != 'P') continue;
634
+ if (text->data[i] != '>') continue;
635
+ i -= 3;
636
+ pfound = 1;
637
+ break;
638
+ }
639
+ }
640
+
641
+ bufprintf(ob, "\n<li id=\"fn%d\">\n", num);
642
+ if (pfound) {
643
+ bufput(ob, text->data, i);
644
+ bufprintf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
645
+ bufput(ob, text->data + i, text->size - i);
646
+ } else if (text) {
647
+ bufput(ob, text->data, text->size);
648
+ }
649
+ BUFPUTSL(ob, "</li>\n");
650
+ }
651
+
652
+ static int
653
+ rndr_footnote_ref(struct buf *ob, unsigned int num, void *opaque)
654
+ {
655
+ bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
656
+ return 1;
657
+ }
658
+
659
+ static void
660
+ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
661
+ {
662
+ struct html_renderopt *options = opaque;
663
+
664
+ if (level <= options->toc_data.nesting_level) {
665
+ /* set the level offset if this is the first header
666
+ * we're parsing for the document */
667
+ if (options->toc_data.current_level == 0)
668
+ options->toc_data.level_offset = level - 1;
669
+
670
+ level -= options->toc_data.level_offset;
671
+
672
+ if (level > options->toc_data.current_level) {
673
+ while (level > options->toc_data.current_level) {
674
+ BUFPUTSL(ob, "<ul>\n<li>\n");
675
+ options->toc_data.current_level++;
676
+ }
677
+ } else if (level < options->toc_data.current_level) {
678
+ BUFPUTSL(ob, "</li>\n");
679
+ while (level < options->toc_data.current_level) {
680
+ BUFPUTSL(ob, "</ul>\n</li>\n");
681
+ options->toc_data.current_level--;
682
+ }
683
+ BUFPUTSL(ob,"<li>\n");
684
+ } else {
685
+ BUFPUTSL(ob,"</li>\n<li>\n");
686
+ }
687
+
688
+ bufprintf(ob, "<a href=\"#%s\">", header_anchor(text));
689
+
690
+ if (text) {
691
+ if (options->flags & HTML_ESCAPE)
692
+ escape_html(ob, text->data, text->size);
693
+ else
694
+ bufput(ob, text->data, text->size);
695
+ }
696
+
697
+ BUFPUTSL(ob, "</a>\n");
698
+ }
699
+ }
700
+
701
+ static int
702
+ toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
703
+ {
704
+ if (content && content->size)
705
+ bufput(ob, content->data, content->size);
706
+ return 1;
707
+ }
708
+
709
+ static void
710
+ toc_finalize(struct buf *ob, void *opaque)
711
+ {
712
+ struct html_renderopt *options = opaque;
713
+
714
+ while (options->toc_data.current_level > 0) {
715
+ BUFPUTSL(ob, "</li>\n</ul>\n");
716
+ options->toc_data.current_level--;
717
+ }
718
+ }
719
+
720
+ void
721
+ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
722
+ {
723
+ static const struct sd_callbacks cb_default = {
724
+ NULL,
725
+ NULL,
726
+ NULL,
727
+ toc_header,
728
+ NULL,
729
+ NULL,
730
+ NULL,
731
+ NULL,
732
+ NULL,
733
+ NULL,
734
+ NULL,
735
+ rndr_footnotes,
736
+ rndr_footnote_def,
737
+
738
+ NULL,
739
+ rndr_codespan,
740
+ rndr_double_emphasis,
741
+ rndr_emphasis,
742
+ rndr_underline,
743
+ rndr_highlight,
744
+ rndr_quote,
745
+ NULL,
746
+ NULL,
747
+ toc_link,
748
+ NULL,
749
+ rndr_triple_emphasis,
750
+ rndr_strikethrough,
751
+ rndr_superscript,
752
+ rndr_footnote_ref,
753
+
754
+ NULL,
755
+ NULL,
756
+
757
+ NULL,
758
+ toc_finalize,
759
+ };
760
+
761
+ memset(options, 0x0, sizeof(struct html_renderopt));
762
+ options->flags = render_flags;
763
+
764
+ memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
765
+ }
766
+
767
+ void
768
+ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
769
+ {
770
+ static const struct sd_callbacks cb_default = {
771
+ rndr_blockcode,
772
+ rndr_blockquote,
773
+ rndr_raw_block,
774
+ rndr_header,
775
+ rndr_hrule,
776
+ rndr_list,
777
+ rndr_listitem,
778
+ rndr_paragraph,
779
+ rndr_table,
780
+ rndr_tablerow,
781
+ rndr_tablecell,
782
+ rndr_footnotes,
783
+ rndr_footnote_def,
784
+
785
+ rndr_autolink,
786
+ rndr_codespan,
787
+ rndr_double_emphasis,
788
+ rndr_emphasis,
789
+ rndr_underline,
790
+ rndr_highlight,
791
+ rndr_quote,
792
+ rndr_image,
793
+ rndr_linebreak,
794
+ rndr_link,
795
+ rndr_raw_html,
796
+ rndr_triple_emphasis,
797
+ rndr_strikethrough,
798
+ rndr_superscript,
799
+ rndr_footnote_ref,
800
+
801
+ NULL,
802
+ rndr_normal_text,
803
+
804
+ NULL,
805
+ NULL,
806
+ };
807
+
808
+ /* Prepare the options pointer */
809
+ memset(options, 0x0, sizeof(struct html_renderopt));
810
+ options->flags = render_flags;
811
+ options->toc_data.nesting_level = 99;
812
+
813
+ /* Prepare the callbacks */
814
+ memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
815
+
816
+ if (render_flags & HTML_SKIP_IMAGES)
817
+ callbacks->image = NULL;
818
+
819
+ if (render_flags & HTML_SKIP_LINKS) {
820
+ callbacks->link = NULL;
821
+ callbacks->autolink = NULL;
822
+ }
823
+
824
+ if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE)
825
+ callbacks->blockhtml = NULL;
826
+ }