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,196 @@
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
+ #define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
19
+
20
+ #include "buffer.h"
21
+
22
+ #include <stdio.h>
23
+ #include <stdlib.h>
24
+ #include <string.h>
25
+ #include <assert.h>
26
+
27
+ /* MSVC compat */
28
+ #if defined(_MSC_VER)
29
+ # define _buf_vsnprintf _vsnprintf
30
+ #else
31
+ # define _buf_vsnprintf vsnprintf
32
+ #endif
33
+
34
+ int
35
+ bufprefix(const struct buf *buf, const char *prefix)
36
+ {
37
+ size_t i;
38
+ assert(buf && buf->unit);
39
+
40
+ for (i = 0; i < buf->size; ++i) {
41
+ if (prefix[i] == 0)
42
+ return 0;
43
+
44
+ if (buf->data[i] != prefix[i])
45
+ return buf->data[i] - prefix[i];
46
+ }
47
+
48
+ return 0;
49
+ }
50
+
51
+ /* bufgrow: increasing the allocated size to the given value */
52
+ int
53
+ bufgrow(struct buf *buf, size_t neosz)
54
+ {
55
+ size_t neoasz;
56
+ void *neodata;
57
+
58
+ assert(buf && buf->unit);
59
+
60
+ if (neosz > BUFFER_MAX_ALLOC_SIZE)
61
+ return BUF_ENOMEM;
62
+
63
+ if (buf->asize >= neosz)
64
+ return BUF_OK;
65
+
66
+ neoasz = buf->asize + buf->unit;
67
+ while (neoasz < neosz)
68
+ neoasz += buf->unit;
69
+
70
+ neodata = realloc(buf->data, neoasz);
71
+ if (!neodata)
72
+ return BUF_ENOMEM;
73
+
74
+ buf->data = neodata;
75
+ buf->asize = neoasz;
76
+ return BUF_OK;
77
+ }
78
+
79
+
80
+ /* bufnew: allocation of a new buffer */
81
+ struct buf *
82
+ bufnew(size_t unit)
83
+ {
84
+ struct buf *ret;
85
+ ret = malloc(sizeof (struct buf));
86
+
87
+ if (ret) {
88
+ ret->data = 0;
89
+ ret->size = ret->asize = 0;
90
+ ret->unit = unit;
91
+ }
92
+ return ret;
93
+ }
94
+
95
+ /* bufnullterm: NULL-termination of the string array */
96
+ const char *
97
+ bufcstr(const struct buf *buf)
98
+ {
99
+ assert(buf && buf->unit);
100
+
101
+ if (buf->size < buf->asize && buf->data[buf->size] == 0)
102
+ return (char *)buf->data;
103
+
104
+ if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == BUF_OK) {
105
+ buf->data[buf->size] = 0;
106
+ return (char *)buf->data;
107
+ }
108
+
109
+ return NULL;
110
+ }
111
+
112
+ /* bufprintf: formatted printing to a buffer */
113
+ void
114
+ bufprintf(struct buf *buf, const char *fmt, ...)
115
+ {
116
+ va_list ap;
117
+ int n;
118
+
119
+ assert(buf && buf->unit);
120
+
121
+ if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < BUF_OK)
122
+ return;
123
+
124
+ va_start(ap, fmt);
125
+ n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
126
+ va_end(ap);
127
+
128
+ if (n < 0) {
129
+ #ifdef _MSC_VER
130
+ va_start(ap, fmt);
131
+ n = _vscprintf(fmt, ap);
132
+ va_end(ap);
133
+ #else
134
+ return;
135
+ #endif
136
+ }
137
+
138
+ if ((size_t)n >= buf->asize - buf->size) {
139
+ if (bufgrow(buf, buf->size + n + 1) < BUF_OK)
140
+ return;
141
+
142
+ va_start(ap, fmt);
143
+ n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
144
+ va_end(ap);
145
+ }
146
+
147
+ if (n < 0)
148
+ return;
149
+
150
+ buf->size += n;
151
+ }
152
+
153
+ /* bufput: appends raw data to a buffer */
154
+ void
155
+ bufput(struct buf *buf, const void *data, size_t len)
156
+ {
157
+ assert(buf && buf->unit);
158
+
159
+ if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < BUF_OK)
160
+ return;
161
+
162
+ memcpy(buf->data + buf->size, data, len);
163
+ buf->size += len;
164
+ }
165
+
166
+ /* bufputs: appends a NUL-terminated string to a buffer */
167
+ void
168
+ bufputs(struct buf *buf, const char *str)
169
+ {
170
+ bufput(buf, str, strlen(str));
171
+ }
172
+
173
+
174
+ /* bufputc: appends a single uint8_t to a buffer */
175
+ void
176
+ bufputc(struct buf *buf, int c)
177
+ {
178
+ assert(buf && buf->unit);
179
+
180
+ if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < BUF_OK)
181
+ return;
182
+
183
+ buf->data[buf->size] = c;
184
+ buf->size += 1;
185
+ }
186
+
187
+ /* bufrelease: decrease the reference count and free the buffer if needed */
188
+ void
189
+ bufrelease(struct buf *buf)
190
+ {
191
+ if (!buf)
192
+ return;
193
+
194
+ free(buf->data);
195
+ free(buf);
196
+ }
@@ -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(const 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('greenmat')
6
+ create_makefile('greenmat')
@@ -0,0 +1,161 @@
1
+ /*
2
+ * Copyright (c) 2011, Vicent Marti
3
+ *
4
+ * Permission to use, copy, modify, and distribute this software for any
5
+ * purpose with or without fee is hereby granted, provided that the above
6
+ * copyright notice and this permission notice appear in all copies.
7
+ *
8
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ */
16
+ #include "greenmat.h"
17
+
18
+ VALUE rb_mGreenmat;
19
+ VALUE rb_cMarkdown;
20
+
21
+ extern VALUE rb_cRenderBase;
22
+
23
+ static void rb_greenmat_md_flags(VALUE hash, unsigned int *enabled_extensions_p)
24
+ {
25
+ unsigned int extensions = 0;
26
+
27
+ Check_Type(hash, T_HASH);
28
+
29
+ /**
30
+ * Markdown extensions -- all disabled by default
31
+ */
32
+ if (rb_hash_lookup(hash, CSTR2SYM("no_intra_emphasis")) == Qtrue)
33
+ extensions |= MKDEXT_NO_INTRA_EMPHASIS;
34
+
35
+ if (rb_hash_lookup(hash, CSTR2SYM("tables")) == Qtrue)
36
+ extensions |= MKDEXT_TABLES;
37
+
38
+ if (rb_hash_lookup(hash, CSTR2SYM("fenced_code_blocks")) == Qtrue)
39
+ extensions |= MKDEXT_FENCED_CODE;
40
+
41
+ if (rb_hash_lookup(hash, CSTR2SYM("disable_indented_code_blocks")) == Qtrue)
42
+ extensions |= MKDEXT_DISABLE_INDENTED_CODE;
43
+
44
+ if (rb_hash_lookup(hash, CSTR2SYM("autolink")) == Qtrue)
45
+ extensions |= MKDEXT_AUTOLINK;
46
+
47
+ if (rb_hash_lookup(hash, CSTR2SYM("strikethrough")) == Qtrue)
48
+ extensions |= MKDEXT_STRIKETHROUGH;
49
+
50
+ if (rb_hash_lookup(hash, CSTR2SYM("underline")) == Qtrue)
51
+ extensions |= MKDEXT_UNDERLINE;
52
+
53
+ if (rb_hash_lookup(hash, CSTR2SYM("highlight")) == Qtrue)
54
+ extensions |= MKDEXT_HIGHLIGHT;
55
+
56
+ if (rb_hash_lookup(hash, CSTR2SYM("quote")) == Qtrue)
57
+ extensions |= MKDEXT_QUOTE;
58
+
59
+ if (rb_hash_lookup(hash, CSTR2SYM("lax_spacing")) == Qtrue)
60
+ extensions |= MKDEXT_LAX_SPACING;
61
+
62
+ if (rb_hash_lookup(hash, CSTR2SYM("space_after_headers")) == Qtrue)
63
+ extensions |= MKDEXT_SPACE_HEADERS;
64
+
65
+ if (rb_hash_lookup(hash, CSTR2SYM("superscript")) == Qtrue)
66
+ extensions |= MKDEXT_SUPERSCRIPT;
67
+
68
+ if (rb_hash_lookup(hash, CSTR2SYM("footnotes")) == Qtrue)
69
+ extensions |= MKDEXT_FOOTNOTES;
70
+
71
+ *enabled_extensions_p = extensions;
72
+ }
73
+
74
+ static void
75
+ rb_greenmat_md__free(void *markdown)
76
+ {
77
+ sd_markdown_free((struct sd_markdown *)markdown);
78
+ }
79
+
80
+ static VALUE rb_greenmat_md__new(int argc, VALUE *argv, VALUE klass)
81
+ {
82
+ VALUE rb_markdown, rb_rndr, hash;
83
+ unsigned int extensions = 0;
84
+
85
+ struct rb_greenmat_rndr *rndr;
86
+ struct sd_markdown *markdown;
87
+
88
+ if (rb_scan_args(argc, argv, "11", &rb_rndr, &hash) == 2)
89
+ rb_greenmat_md_flags(hash, &extensions);
90
+
91
+ if (rb_obj_is_kind_of(rb_rndr, rb_cClass))
92
+ rb_rndr = rb_funcall(rb_rndr, rb_intern("new"), 0);
93
+
94
+ if (!rb_obj_is_kind_of(rb_rndr, rb_cRenderBase))
95
+ rb_raise(rb_eTypeError, "Invalid Renderer instance given");
96
+
97
+ Data_Get_Struct(rb_rndr, struct rb_greenmat_rndr, rndr);
98
+
99
+ markdown = sd_markdown_new(extensions, 16, &rndr->callbacks, &rndr->options);
100
+ if (!markdown)
101
+ rb_raise(rb_eRuntimeError, "Failed to create new Renderer class");
102
+
103
+ rb_markdown = Data_Wrap_Struct(klass, NULL, rb_greenmat_md__free, markdown);
104
+ rb_iv_set(rb_markdown, "@renderer", rb_rndr);
105
+
106
+ return rb_markdown;
107
+ }
108
+
109
+ static VALUE rb_greenmat_md_render(VALUE self, VALUE text)
110
+ {
111
+ VALUE rb_rndr;
112
+ struct buf *output_buf;
113
+ struct sd_markdown *markdown;
114
+
115
+ Check_Type(text, T_STRING);
116
+
117
+ rb_rndr = rb_iv_get(self, "@renderer");
118
+ Data_Get_Struct(self, struct sd_markdown, markdown);
119
+
120
+ if (rb_respond_to(rb_rndr, rb_intern("preprocess")))
121
+ text = rb_funcall(rb_rndr, rb_intern("preprocess"), 1, text);
122
+ if (NIL_P(text))
123
+ return Qnil;
124
+
125
+ struct rb_greenmat_rndr *renderer;
126
+ Data_Get_Struct(rb_rndr, struct rb_greenmat_rndr, renderer);
127
+ renderer->options.active_enc = rb_enc_get(text);
128
+
129
+ /* initialize buffers */
130
+ output_buf = bufnew(128);
131
+
132
+ /* render the magic */
133
+ sd_markdown_render(
134
+ output_buf,
135
+ (const uint8_t*)RSTRING_PTR(text),
136
+ RSTRING_LEN(text),
137
+ markdown);
138
+
139
+ /* build the Ruby string */
140
+ text = rb_enc_str_new((const char*)output_buf->data, output_buf->size, rb_enc_get(text));
141
+
142
+ bufrelease(output_buf);
143
+
144
+ if (rb_respond_to(rb_rndr, rb_intern("postprocess")))
145
+ text = rb_funcall(rb_rndr, rb_intern("postprocess"), 1, text);
146
+
147
+ return text;
148
+ }
149
+
150
+ __attribute__((visibility("default")))
151
+ void Init_greenmat()
152
+ {
153
+ rb_mGreenmat = rb_define_module("Greenmat");
154
+
155
+ rb_cMarkdown = rb_define_class_under(rb_mGreenmat, "Markdown", rb_cObject);
156
+ rb_define_singleton_method(rb_cMarkdown, "new", rb_greenmat_md__new, -1);
157
+ rb_define_method(rb_cMarkdown, "render", rb_greenmat_md_render, 1);
158
+
159
+ Init_greenmat_rndr();
160
+ }
161
+
@@ -0,0 +1,534 @@
1
+ /*
2
+ * Copyright (c) 2011, Vicent Marti
3
+ *
4
+ * Permission to use, copy, modify, and distribute this software for any
5
+ * purpose with or without fee is hereby granted, provided that the above
6
+ * copyright notice and this permission notice appear in all copies.
7
+ *
8
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ */
16
+
17
+ #include "greenmat.h"
18
+
19
+ #define SPAN_CALLBACK(method_name, ...) {\
20
+ struct greenmat_renderopt *opt = opaque;\
21
+ VALUE ret = rb_funcall(opt->self, rb_intern(method_name), __VA_ARGS__);\
22
+ if (NIL_P(ret)) return 0;\
23
+ Check_Type(ret, T_STRING);\
24
+ bufput(ob, RSTRING_PTR(ret), RSTRING_LEN(ret));\
25
+ return 1;\
26
+ }
27
+
28
+ #define BLOCK_CALLBACK(method_name, ...) {\
29
+ struct greenmat_renderopt *opt = opaque;\
30
+ VALUE ret = rb_funcall(opt->self, rb_intern(method_name), __VA_ARGS__);\
31
+ if (NIL_P(ret)) return;\
32
+ Check_Type(ret, T_STRING);\
33
+ bufput(ob, RSTRING_PTR(ret), RSTRING_LEN(ret));\
34
+ }
35
+
36
+ extern VALUE rb_mGreenmat;
37
+ VALUE rb_mRender;
38
+ VALUE rb_cRenderBase;
39
+ VALUE rb_cRenderHTML;
40
+ VALUE rb_cRenderHTML_TOC;
41
+ VALUE rb_mSmartyPants;
42
+
43
+ #define buf2str(t) ((t) ? rb_enc_str_new((const char*)(t)->data, (t)->size, opt->active_enc) : Qnil)
44
+
45
+ static void
46
+ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
47
+ {
48
+ BLOCK_CALLBACK("block_code", 2, buf2str(text), buf2str(lang));
49
+ }
50
+
51
+ static void
52
+ rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
53
+ {
54
+ BLOCK_CALLBACK("block_quote", 1, buf2str(text));
55
+ }
56
+
57
+ static void
58
+ rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
59
+ {
60
+ BLOCK_CALLBACK("block_html", 1, buf2str(text));
61
+ }
62
+
63
+ static void
64
+ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
65
+ {
66
+ BLOCK_CALLBACK("header", 2, buf2str(text), INT2FIX(level));
67
+ }
68
+
69
+ static void
70
+ rndr_hrule(struct buf *ob, void *opaque)
71
+ {
72
+ BLOCK_CALLBACK("hrule", 0);
73
+ }
74
+
75
+ static void
76
+ rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)
77
+ {
78
+ BLOCK_CALLBACK("list", 2, buf2str(text),
79
+ (flags & MKD_LIST_ORDERED) ? CSTR2SYM("ordered") : CSTR2SYM("unordered"));
80
+ }
81
+
82
+ static void
83
+ rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)
84
+ {
85
+ BLOCK_CALLBACK("list_item", 2, buf2str(text),
86
+ (flags & MKD_LIST_ORDERED) ? CSTR2SYM("ordered") : CSTR2SYM("unordered"));
87
+ }
88
+
89
+ static void
90
+ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
91
+ {
92
+ BLOCK_CALLBACK("paragraph", 1, buf2str(text));
93
+ }
94
+
95
+ static void
96
+ rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)
97
+ {
98
+ BLOCK_CALLBACK("table", 2, buf2str(header), buf2str(body));
99
+ }
100
+
101
+ static void
102
+ rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)
103
+ {
104
+ BLOCK_CALLBACK("table_row", 1, buf2str(text));
105
+ }
106
+
107
+ static void
108
+ rndr_tablecell(struct buf *ob, const struct buf *text, int align, void *opaque)
109
+ {
110
+ VALUE rb_align;
111
+
112
+ switch (align) {
113
+ case MKD_TABLE_ALIGN_L:
114
+ rb_align = CSTR2SYM("left");
115
+ break;
116
+
117
+ case MKD_TABLE_ALIGN_R:
118
+ rb_align = CSTR2SYM("right");
119
+ break;
120
+
121
+ case MKD_TABLE_ALIGN_CENTER:
122
+ rb_align = CSTR2SYM("center");
123
+ break;
124
+
125
+ default:
126
+ rb_align = Qnil;
127
+ break;
128
+ }
129
+
130
+ BLOCK_CALLBACK("table_cell", 2, buf2str(text), rb_align);
131
+ }
132
+
133
+ static void
134
+ rndr_footnotes(struct buf *ob, const struct buf *text, void *opaque)
135
+ {
136
+ BLOCK_CALLBACK("footnotes", 1, buf2str(text));
137
+ }
138
+
139
+ static void
140
+ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void *opaque)
141
+ {
142
+ BLOCK_CALLBACK("footnote_def", 2, buf2str(text), INT2FIX(num));
143
+ }
144
+
145
+
146
+ /***
147
+ * SPAN LEVEL
148
+ */
149
+ static int
150
+ rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)
151
+ {
152
+ SPAN_CALLBACK("autolink", 2, buf2str(link),
153
+ type == MKDA_NORMAL ? CSTR2SYM("url") : CSTR2SYM("email"));
154
+ }
155
+
156
+ static int
157
+ rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)
158
+ {
159
+ SPAN_CALLBACK("codespan", 1, buf2str(text));
160
+ }
161
+
162
+ static int
163
+ rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)
164
+ {
165
+ SPAN_CALLBACK("double_emphasis", 1, buf2str(text));
166
+ }
167
+
168
+ static int
169
+ rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)
170
+ {
171
+ SPAN_CALLBACK("emphasis", 1, buf2str(text));
172
+ }
173
+
174
+ static int
175
+ rndr_underline(struct buf *ob, const struct buf *text, void *opaque)
176
+ {
177
+ SPAN_CALLBACK("underline", 1, buf2str(text));
178
+ }
179
+
180
+ static int
181
+ rndr_highlight(struct buf *ob, const struct buf *text, void *opaque)
182
+ {
183
+ SPAN_CALLBACK("highlight", 1, buf2str(text));
184
+ }
185
+
186
+ static int
187
+ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
188
+ {
189
+ SPAN_CALLBACK("quote", 1, buf2str(text));
190
+ }
191
+
192
+ static int
193
+ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
194
+ {
195
+ SPAN_CALLBACK("image", 3, buf2str(link), buf2str(title), buf2str(alt));
196
+ }
197
+
198
+ static int
199
+ rndr_linebreak(struct buf *ob, void *opaque)
200
+ {
201
+ SPAN_CALLBACK("linebreak", 0);
202
+ }
203
+
204
+ static int
205
+ rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
206
+ {
207
+ SPAN_CALLBACK("link", 3, buf2str(link), buf2str(title), buf2str(content));
208
+ }
209
+
210
+ static int
211
+ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
212
+ {
213
+ SPAN_CALLBACK("raw_html", 1, buf2str(text));
214
+ }
215
+
216
+ static int
217
+ rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)
218
+ {
219
+ SPAN_CALLBACK("triple_emphasis", 1, buf2str(text));
220
+ }
221
+
222
+ static int
223
+ rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)
224
+ {
225
+ SPAN_CALLBACK("strikethrough", 1, buf2str(text));
226
+ }
227
+
228
+ static int
229
+ rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)
230
+ {
231
+ SPAN_CALLBACK("superscript", 1, buf2str(text));
232
+ }
233
+
234
+ static int
235
+ rndr_footnote_ref(struct buf *ob, unsigned int num, void *opaque)
236
+ {
237
+ SPAN_CALLBACK("footnote_ref", 1, INT2FIX(num));
238
+ }
239
+
240
+ /**
241
+ * direct writes
242
+ */
243
+ static void
244
+ rndr_entity(struct buf *ob, const struct buf *text, void *opaque)
245
+ {
246
+ BLOCK_CALLBACK("entity", 1, buf2str(text));
247
+ }
248
+
249
+ static void
250
+ rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)
251
+ {
252
+ BLOCK_CALLBACK("normal_text", 1, buf2str(text));
253
+ }
254
+
255
+ static void
256
+ rndr_doc_header(struct buf *ob, void *opaque)
257
+ {
258
+ BLOCK_CALLBACK("doc_header", 0);
259
+ }
260
+
261
+ static void
262
+ rndr_doc_footer(struct buf *ob, void *opaque)
263
+ {
264
+ BLOCK_CALLBACK("doc_footer", 0);
265
+ }
266
+
267
+ static int
268
+ cb_link_attribute(VALUE key, VALUE val, VALUE payload)
269
+ {
270
+ struct buf *ob = (struct buf *)payload;
271
+ key = rb_obj_as_string(key);
272
+ val = rb_obj_as_string(val);
273
+ bufprintf(ob, " %s=\"%s\"", StringValueCStr(key), StringValueCStr(val));
274
+ return 0;
275
+ }
276
+
277
+ static void
278
+ rndr_link_attributes(struct buf *ob, const struct buf *url, void *opaque)
279
+ {
280
+ struct greenmat_renderopt *opt = opaque;
281
+ struct rb_greenmat_rndr *rndr;
282
+
283
+ Data_Get_Struct(opt->self, struct rb_greenmat_rndr, rndr);
284
+ Check_Type(opt->link_attributes, T_HASH);
285
+ rb_hash_foreach(opt->link_attributes, &cb_link_attribute, (VALUE)ob);
286
+ }
287
+
288
+ static struct sd_callbacks rb_greenmat_callbacks = {
289
+ rndr_blockcode,
290
+ rndr_blockquote,
291
+ rndr_raw_block,
292
+ rndr_header,
293
+ rndr_hrule,
294
+ rndr_list,
295
+ rndr_listitem,
296
+ rndr_paragraph,
297
+ rndr_table,
298
+ rndr_tablerow,
299
+ rndr_tablecell,
300
+ rndr_footnotes,
301
+ rndr_footnote_def,
302
+
303
+ rndr_autolink,
304
+ rndr_codespan,
305
+ rndr_double_emphasis,
306
+ rndr_emphasis,
307
+ rndr_underline,
308
+ rndr_highlight,
309
+ rndr_quote,
310
+ rndr_image,
311
+ rndr_linebreak,
312
+ rndr_link,
313
+ rndr_raw_html,
314
+ rndr_triple_emphasis,
315
+ rndr_strikethrough,
316
+ rndr_superscript,
317
+ rndr_footnote_ref,
318
+
319
+ rndr_entity,
320
+ rndr_normal_text,
321
+
322
+ rndr_doc_header,
323
+ rndr_doc_footer,
324
+ };
325
+
326
+ static const char *rb_greenmat_method_names[] = {
327
+ "block_code",
328
+ "block_quote",
329
+ "block_html",
330
+ "header",
331
+ "hrule",
332
+ "list",
333
+ "list_item",
334
+ "paragraph",
335
+ "table",
336
+ "table_row",
337
+ "table_cell",
338
+ "footnotes",
339
+ "footnote_def",
340
+
341
+ "autolink",
342
+ "codespan",
343
+ "double_emphasis",
344
+ "emphasis",
345
+ "underline",
346
+ "highlight",
347
+ "quote",
348
+ "image",
349
+ "linebreak",
350
+ "link",
351
+ "raw_html",
352
+ "triple_emphasis",
353
+ "strikethrough",
354
+ "superscript",
355
+ "footnote_ref",
356
+
357
+ "entity",
358
+ "normal_text",
359
+
360
+ "doc_header",
361
+ "doc_footer"
362
+ };
363
+
364
+ static const size_t rb_greenmat_method_count = sizeof(rb_greenmat_method_names)/sizeof(char *);
365
+
366
+ static void rb_greenmat_rbase_mark(struct rb_greenmat_rndr *rndr)
367
+ {
368
+ if (rndr->options.link_attributes)
369
+ rb_gc_mark(rndr->options.link_attributes);
370
+ }
371
+
372
+ static VALUE rb_greenmat_rbase_alloc(VALUE klass)
373
+ {
374
+ struct rb_greenmat_rndr *rndr = ALLOC(struct rb_greenmat_rndr);
375
+ memset(rndr, 0x0, sizeof(struct rb_greenmat_rndr));
376
+ return Data_Wrap_Struct(klass, rb_greenmat_rbase_mark, NULL, rndr);
377
+ }
378
+
379
+ static void rb_greenmat__overload(VALUE self, VALUE base_class)
380
+ {
381
+ struct rb_greenmat_rndr *rndr;
382
+
383
+ Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
384
+ rndr->options.self = self;
385
+ rndr->options.base_class = base_class;
386
+
387
+ if (rb_obj_class(self) == rb_cRenderBase)
388
+ rb_raise(rb_eRuntimeError,
389
+ "The Greenmat::Render::Base class cannot be instantiated. "
390
+ "Create an inheriting class instead to implement a custom renderer.");
391
+
392
+ if (rb_obj_class(self) != base_class) {
393
+ void **source = (void **)&rb_greenmat_callbacks;
394
+ void **dest = (void **)&rndr->callbacks;
395
+ size_t i;
396
+
397
+ for (i = 0; i < rb_greenmat_method_count; ++i) {
398
+ if (rb_respond_to(self, rb_intern(rb_greenmat_method_names[i])))
399
+ dest[i] = source[i];
400
+ }
401
+ }
402
+ }
403
+
404
+ static VALUE rb_greenmat_rbase_init(VALUE self)
405
+ {
406
+ rb_greenmat__overload(self, rb_cRenderBase);
407
+ return Qnil;
408
+ }
409
+
410
+ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
411
+ {
412
+ struct rb_greenmat_rndr *rndr;
413
+ unsigned int render_flags = 0;
414
+ VALUE hash, link_attr = Qnil;
415
+
416
+ Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
417
+
418
+ if (rb_scan_args(argc, argv, "01", &hash) == 1) {
419
+ Check_Type(hash, T_HASH);
420
+
421
+ /* escape_html */
422
+ if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
423
+ render_flags |= HTML_ESCAPE;
424
+
425
+ /* filter_html */
426
+ if (rb_hash_aref(hash, CSTR2SYM("filter_html")) == Qtrue)
427
+ render_flags |= HTML_SKIP_HTML;
428
+
429
+ /* no_image */
430
+ if (rb_hash_aref(hash, CSTR2SYM("no_images")) == Qtrue)
431
+ render_flags |= HTML_SKIP_IMAGES;
432
+
433
+ /* no_links */
434
+ if (rb_hash_aref(hash, CSTR2SYM("no_links")) == Qtrue)
435
+ render_flags |= HTML_SKIP_LINKS;
436
+
437
+ /* prettify */
438
+ if (rb_hash_aref(hash, CSTR2SYM("prettify")) == Qtrue)
439
+ render_flags |= HTML_PRETTIFY;
440
+
441
+ /* filter_style */
442
+ if (rb_hash_aref(hash, CSTR2SYM("no_styles")) == Qtrue)
443
+ render_flags |= HTML_SKIP_STYLE;
444
+
445
+ /* safelink */
446
+ if (rb_hash_aref(hash, CSTR2SYM("safe_links_only")) == Qtrue)
447
+ render_flags |= HTML_SAFELINK;
448
+
449
+ if (rb_hash_aref(hash, CSTR2SYM("with_toc_data")) == Qtrue)
450
+ render_flags |= HTML_TOC;
451
+
452
+ if (rb_hash_aref(hash, CSTR2SYM("hard_wrap")) == Qtrue)
453
+ render_flags |= HTML_HARD_WRAP;
454
+
455
+ if (rb_hash_aref(hash, CSTR2SYM("xhtml")) == Qtrue)
456
+ render_flags |= HTML_USE_XHTML;
457
+
458
+ link_attr = rb_hash_aref(hash, CSTR2SYM("link_attributes"));
459
+ }
460
+
461
+ sdhtml_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, render_flags);
462
+ rb_greenmat__overload(self, rb_cRenderHTML);
463
+
464
+ if (!NIL_P(link_attr)) {
465
+ rndr->options.link_attributes = link_attr;
466
+ rndr->options.html.link_attributes = &rndr_link_attributes;
467
+ }
468
+
469
+ return Qnil;
470
+ }
471
+
472
+ static VALUE rb_greenmat_htmltoc_init(int argc, VALUE *argv, VALUE self)
473
+ {
474
+ struct rb_greenmat_rndr *rndr;
475
+ unsigned int render_flags = HTML_TOC;
476
+ VALUE hash, nesting_level = Qnil;
477
+
478
+ Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
479
+
480
+ if (rb_scan_args(argc, argv, "01", &hash) == 1) {
481
+ Check_Type(hash, T_HASH);
482
+
483
+ /* escape_html */
484
+ if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
485
+ render_flags |= HTML_ESCAPE;
486
+
487
+ /* Nesting level */
488
+ nesting_level = rb_hash_aref(hash, CSTR2SYM("nesting_level"));
489
+ }
490
+
491
+ sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, render_flags);
492
+ rb_greenmat__overload(self, rb_cRenderHTML_TOC);
493
+
494
+ if (!(NIL_P(nesting_level)))
495
+ rndr->options.html.toc_data.nesting_level = NUM2INT(nesting_level);
496
+ else
497
+ rndr->options.html.toc_data.nesting_level = 6;
498
+
499
+ return Qnil;
500
+ }
501
+
502
+ static VALUE rb_greenmat_smartypants_render(VALUE self, VALUE text)
503
+ {
504
+ VALUE result;
505
+ struct buf *output_buf;
506
+
507
+ Check_Type(text, T_STRING);
508
+
509
+ output_buf = bufnew(128);
510
+
511
+ sdhtml_smartypants(output_buf, (const uint8_t*)RSTRING_PTR(text), RSTRING_LEN(text));
512
+ result = rb_enc_str_new((const char*)output_buf->data, output_buf->size, rb_enc_get(text));
513
+
514
+ bufrelease(output_buf);
515
+ return result;
516
+ }
517
+
518
+ void Init_greenmat_rndr()
519
+ {
520
+ rb_mRender = rb_define_module_under(rb_mGreenmat, "Render");
521
+
522
+ rb_cRenderBase = rb_define_class_under(rb_mRender, "Base", rb_cObject);
523
+ rb_define_alloc_func(rb_cRenderBase, rb_greenmat_rbase_alloc);
524
+ rb_define_method(rb_cRenderBase, "initialize", rb_greenmat_rbase_init, 0);
525
+
526
+ rb_cRenderHTML = rb_define_class_under(rb_mRender, "HTML", rb_cRenderBase);
527
+ rb_define_method(rb_cRenderHTML, "initialize", rb_greenmat_html_init, -1);
528
+
529
+ rb_cRenderHTML_TOC = rb_define_class_under(rb_mRender, "HTML_TOC", rb_cRenderBase);
530
+ rb_define_method(rb_cRenderHTML_TOC, "initialize", rb_greenmat_htmltoc_init, -1);
531
+
532
+ rb_mSmartyPants = rb_define_module_under(rb_mRender, "SmartyPants");
533
+ rb_define_method(rb_mSmartyPants, "postprocess", rb_greenmat_smartypants_render, 1);
534
+ }