/
rdiscount.c
159 lines (130 loc) · 4.61 KB
/
rdiscount.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdio.h>
#include <locale.h>
#include "ruby.h"
#include "mkdio.h"
typedef struct {
char *accessor_name;
int flag;
} AccessorFlagPair;
/*
* Maps accessor names on the RDiscount object to Discount flags.
*
* The following flags are handled specially:
* - MKD_TABSTOP: Always set.
* - MKD_NOHEADER: Always set.
* - MKD_DLEXTRA: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
* - MKD_FENCEDCODE: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
* - MKD_GITHUBTAGS: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
* - MKD_NOPANTS: Set unless the "smart" accessor returns true.
* - MKD_NOSTYLE: Set unless the "filter_styles" accessor returns true.
*
* See rb_rdiscount__get_flags() for the detailed implementation.
*/
static AccessorFlagPair ACCESSOR_2_FLAG[] = {
{ "filter_html", MKD_NOHTML },
{ "footnotes", MKD_EXTRA_FOOTNOTE },
{ "generate_toc", MKD_TOC },
{ "no_image", MKD_NOIMAGE },
{ "no_links", MKD_NOLINKS },
{ "no_tables", MKD_NOTABLES },
{ "strict", MKD_STRICT },
{ "autolink", MKD_AUTOLINK },
{ "safelink", MKD_SAFELINK },
{ "no_pseudo_protocols", MKD_NO_EXT },
{ "no_superscript", MKD_NOSUPERSCRIPT },
{ "no_strikethrough", MKD_NOSTRIKETHROUGH },
{ "latex", MKD_LATEX },
{ "explicitlist", MKD_EXPLICITLIST },
{ "md1compat", MKD_1_COMPAT },
{ NULL, 0 } /* sentinel */
};
static VALUE rb_cRDiscount;
int rb_rdiscount__get_flags(VALUE ruby_obj)
{
AccessorFlagPair *entry;
/* compile flags */
int flags = MKD_TABSTOP | MKD_NOHEADER | MKD_DLEXTRA | MKD_FENCEDCODE | MKD_GITHUBTAGS;
/* The "smart" accessor turns OFF the MKD_NOPANTS flag. */
if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue ) {
flags = flags | MKD_NOPANTS;
}
/* The "filter_styles" accessor turns OFF the MKD_NOSTYLE flag. */
if ( rb_funcall(ruby_obj, rb_intern("filter_styles"), 0) != Qtrue ) {
flags = flags | MKD_NOSTYLE;
}
/* Handle standard flags declared in ACCESSOR_2_FLAG */
for ( entry = ACCESSOR_2_FLAG; entry->accessor_name; entry++ ) {
if ( rb_funcall(ruby_obj, rb_intern(entry->accessor_name), 0) == Qtrue ) {
flags = flags | entry->flag;
}
}
return flags;
}
static VALUE
rb_rdiscount_to_html(int argc, VALUE *argv, VALUE self)
{
/* grab char pointer to markdown input text */
char *res;
int szres;
VALUE encoding;
VALUE text = rb_funcall(self, rb_intern("text"), 0);
VALUE buf = rb_str_buf_new(1024);
Check_Type(text, T_STRING);
int flags = rb_rdiscount__get_flags(self);
/*
* Force Discount to use ASCII character encoding for isalnum(), isalpha(),
* and similar functions.
*
* Ruby tends to use UTF-8 encoding, which is ill-defined for these
* functions since they expect 8-bit codepoints (and UTF-8 has codepoints
* of at least 21 bits).
*/
char *old_locale = strdup(setlocale(LC_CTYPE, NULL));
setlocale(LC_CTYPE, "C"); /* ASCII (and passthru characters > 127) */
MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
if ( mkd_compile(doc, flags) ) {
szres = mkd_document(doc, &res);
if ( szres != EOF ) {
rb_str_cat(buf, res, szres);
rb_str_cat(buf, "\n", 1);
}
}
mkd_cleanup(doc);
setlocale(LC_CTYPE, old_locale);
free(old_locale);
/* force the input encoding */
if ( rb_respond_to(text, rb_intern("encoding")) ) {
encoding = rb_funcall(text, rb_intern("encoding"), 0);
rb_funcall(buf, rb_intern("force_encoding"), 1, encoding);
}
return buf;
}
static VALUE
rb_rdiscount_toc_content(int argc, VALUE *argv, VALUE self)
{
char *res;
int szres;
int flags = rb_rdiscount__get_flags(self);
/* grab char pointer to markdown input text */
VALUE text = rb_funcall(self, rb_intern("text"), 0);
Check_Type(text, T_STRING);
/* allocate a ruby string buffer and wrap it in a stream */
VALUE buf = rb_str_buf_new(4096);
MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
if ( mkd_compile(doc, flags) ) {
szres = mkd_toc(doc, &res);
if ( szres != EOF ) {
rb_str_cat(buf, res, szres);
rb_str_cat(buf, "\n", 1);
}
}
mkd_cleanup(doc);
return buf;
}
void Init_rdiscount()
{
rb_cRDiscount = rb_define_class("RDiscount", rb_cObject);
rb_define_method(rb_cRDiscount, "to_html", rb_rdiscount_to_html, -1);
rb_define_method(rb_cRDiscount, "toc_content", rb_rdiscount_toc_content, -1);
}
/* vim: set ts=4 sw=4: */