rdiscount 1.2.11 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +63 -34
- data/bin/rdiscount +5 -0
- data/ext/generate.c +81 -38
- data/ext/markdown.c +80 -21
- data/ext/markdown.h +7 -2
- data/ext/mkdio.c +16 -0
- data/ext/mkdio.h +6 -0
- data/ext/resource.c +2 -0
- data/lib/rdiscount.rb +1 -0
- data/rdiscount.gemspec +46 -0
- data/test/markdown_test.rb +5 -0
- data/test/rdiscount_test.rb +15 -0
- metadata +22 -21
data/Rakefile
CHANGED
@@ -1,43 +1,75 @@
|
|
1
1
|
require 'rake/clean'
|
2
|
-
require 'rake/packagetask'
|
3
|
-
require 'rake/gempackagetask'
|
4
2
|
|
5
3
|
task :default => 'test:unit'
|
6
4
|
|
7
|
-
|
8
|
-
VERS = '1.2.11'
|
9
|
-
|
10
|
-
spec =
|
11
|
-
Gem::Specification.new do |s|
|
12
|
-
s.name = "rdiscount"
|
13
|
-
s.version = VERS
|
14
|
-
s.summary = "Fast Implementation of Gruber's Markdown in C"
|
15
|
-
s.files = FileList['README.markdown','COPYING','Rakefile','test/**','{lib,ext}/**.rb','ext/*.{c,h}']
|
16
|
-
s.bindir = 'bin'
|
17
|
-
s.require_path = 'lib'
|
18
|
-
s.has_rdoc = true
|
19
|
-
s.extra_rdoc_files = ['COPYING']
|
20
|
-
s.test_files = FileList['test/*_test.rb']
|
21
|
-
s.extensions = ['ext/extconf.rb']
|
22
|
-
|
23
|
-
s.author = 'Ryan Tomayko'
|
24
|
-
s.email = 'r@tomayko.com'
|
25
|
-
s.homepage = 'http://github.com/rtomayko/rdiscount'
|
26
|
-
s.rubyforge_project = 'wink'
|
27
|
-
end
|
5
|
+
# PACKAGING =================================================================
|
28
6
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
7
|
+
# Load the gemspec using the same limitations as github
|
8
|
+
$spec =
|
9
|
+
begin
|
10
|
+
require 'rubygems/specification'
|
11
|
+
data = File.read('rdiscount.gemspec')
|
12
|
+
spec = nil
|
13
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
14
|
+
spec
|
34
15
|
end
|
35
16
|
|
17
|
+
def package(ext='')
|
18
|
+
"pkg/rdiscount-#{$spec.version}" + ext
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Build packages'
|
22
|
+
task :package => %w[.gem .tar.gz].map {|e| package(e)}
|
23
|
+
|
24
|
+
desc 'Build and install as local gem'
|
25
|
+
task :install => package('.gem') do
|
26
|
+
sh "gem install #{package('.gem')}"
|
27
|
+
end
|
28
|
+
|
29
|
+
directory 'pkg/'
|
30
|
+
|
31
|
+
file package('.gem') => %w[pkg/ rdiscount.gemspec] + $spec.files do |f|
|
32
|
+
sh "gem build rdiscount.gemspec"
|
33
|
+
mv File.basename(f.name), f.name
|
34
|
+
end
|
35
|
+
|
36
|
+
file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
|
37
|
+
sh "git archive --format=tar HEAD | gzip > #{f.name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# GEMSPEC HELPERS ==========================================================
|
41
|
+
|
42
|
+
def source_version
|
43
|
+
line = File.read('lib/rdiscount.rb')[/^\s*VERSION = .*/]
|
44
|
+
line.match(/.*VERSION = '(.*)'/)[1]
|
45
|
+
end
|
46
|
+
|
47
|
+
file 'rdiscount.gemspec' => FileList['Rakefile','lib/rdiscount.rb'] do |f|
|
48
|
+
# read spec file and split out manifest section
|
49
|
+
spec = File.read(f.name)
|
50
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
51
|
+
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
|
52
|
+
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
|
53
|
+
# determine file list from git ls-files
|
54
|
+
files = `git ls-files`.
|
55
|
+
split("\n").
|
56
|
+
sort.
|
57
|
+
reject{ |file| file =~ /^\./ || file =~ /^test\/MarkdownTest/ }.
|
58
|
+
map{ |file| " #{file}" }.
|
59
|
+
join("\n")
|
60
|
+
# piece file back together and write...
|
61
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
62
|
+
spec = [head,manifest,tail].join(" # = MANIFEST =\n")
|
63
|
+
File.open(f.name, 'w') { |io| io.write(spec) }
|
64
|
+
puts "updated #{f.name}"
|
65
|
+
end
|
36
66
|
|
37
67
|
# ==========================================================
|
38
68
|
# Ruby Extension
|
39
69
|
# ==========================================================
|
40
70
|
|
71
|
+
DLEXT = Config::CONFIG['DLEXT']
|
72
|
+
|
41
73
|
file 'ext/Makefile' => FileList['ext/{*.c,*.h,*.rb}'] do
|
42
74
|
chdir('ext') { ruby 'extconf.rb' }
|
43
75
|
end
|
@@ -55,7 +87,6 @@ end
|
|
55
87
|
desc 'Build the rdiscount extension'
|
56
88
|
task :build => "lib/rdiscount.#{DLEXT}"
|
57
89
|
|
58
|
-
|
59
90
|
# ==========================================================
|
60
91
|
# Testing
|
61
92
|
# ==========================================================
|
@@ -119,13 +150,11 @@ CLEAN.include 'doc'
|
|
119
150
|
# Rubyforge
|
120
151
|
# ==========================================================
|
121
152
|
|
122
|
-
PKGNAME = "pkg/rdiscount-#{VERS}"
|
123
|
-
|
124
153
|
desc 'Publish new release to rubyforge'
|
125
|
-
task :release => [
|
154
|
+
task :release => [package('.gem'), package('.tar.gz')] do |t|
|
126
155
|
sh <<-end
|
127
|
-
rubyforge add_release wink rdiscount #{
|
128
|
-
rubyforge add_file wink rdiscount #{
|
156
|
+
rubyforge add_release wink rdiscount #{$spec.version} #{package('.gem')} &&
|
157
|
+
rubyforge add_file wink rdiscount #{$spec.version} #{package('.tar.gz')}
|
129
158
|
end
|
130
159
|
end
|
131
160
|
|
data/bin/rdiscount
ADDED
data/ext/generate.c
CHANGED
@@ -26,6 +26,7 @@ typedef int (*stfu)(const void*,const void*);
|
|
26
26
|
|
27
27
|
|
28
28
|
/* forward declarations */
|
29
|
+
static int iscodeblock(MMIOT*);
|
29
30
|
static void code(int, MMIOT*);
|
30
31
|
static void text(MMIOT *f);
|
31
32
|
static Paragraph *display(Paragraph*, MMIOT*);
|
@@ -74,6 +75,31 @@ cursor(MMIOT *f)
|
|
74
75
|
}
|
75
76
|
|
76
77
|
|
78
|
+
static int
|
79
|
+
isthisspace(MMIOT *f, int i)
|
80
|
+
{
|
81
|
+
int c = peek(f, i);
|
82
|
+
|
83
|
+
return isspace(c) || (c == EOF);
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
static int
|
88
|
+
isthisalnum(MMIOT *f, int i)
|
89
|
+
{
|
90
|
+
int c = peek(f, i);
|
91
|
+
|
92
|
+
return (c != EOF) && isalnum(c);
|
93
|
+
}
|
94
|
+
|
95
|
+
|
96
|
+
static int
|
97
|
+
isthisnonword(MMIOT *f, int i)
|
98
|
+
{
|
99
|
+
return isthisspace(f, i) || ispunct(peek(f,i));
|
100
|
+
}
|
101
|
+
|
102
|
+
|
77
103
|
/* return/set the current cursor position
|
78
104
|
*/
|
79
105
|
#define mmiotseek(f,x) (f->isp = x)
|
@@ -313,8 +339,7 @@ puturl(char *s, int size, MMIOT *f)
|
|
313
339
|
Qstring("&", f);
|
314
340
|
else if ( c == '<' )
|
315
341
|
Qstring("<", f);
|
316
|
-
else if ( isalnum(c) || c
|
317
|
-
|| c == '=' || c == '?' || c == ':' || c == '#' )
|
342
|
+
else if ( isalnum(c) || ispunct(c) )
|
318
343
|
Qchar(c, f);
|
319
344
|
else
|
320
345
|
Qprintf(f, "%%%02X", c);
|
@@ -579,7 +604,7 @@ linkylinky(int image, MMIOT *f)
|
|
579
604
|
Footnote link;
|
580
605
|
linkytype *tag;
|
581
606
|
|
582
|
-
if ( !
|
607
|
+
if ( !linkykey(image, &link, f) ) {
|
583
608
|
mmiotseek(f, start);
|
584
609
|
return 0;
|
585
610
|
}
|
@@ -662,11 +687,11 @@ forbidden_tag(MMIOT *f)
|
|
662
687
|
if ( f->flags & DENY_HTML )
|
663
688
|
return 1;
|
664
689
|
|
665
|
-
if ( c == 'A' && (f->flags & DENY_A) && !
|
690
|
+
if ( c == 'A' && (f->flags & DENY_A) && !isthisalnum(f,2) )
|
666
691
|
return 1;
|
667
692
|
if ( c == 'I' && (f->flags & DENY_IMG)
|
668
693
|
&& strncasecmp(cursor(f)+1, "MG", 2) == 0
|
669
|
-
&& !
|
694
|
+
&& !isthisalnum(f,4) )
|
670
695
|
return 1;
|
671
696
|
return 0;
|
672
697
|
}
|
@@ -746,22 +771,6 @@ maybe_tag_or_link(MMIOT *f)
|
|
746
771
|
} /* maybe_tag_or_link */
|
747
772
|
|
748
773
|
|
749
|
-
static int
|
750
|
-
isthisspace(MMIOT *f, int i)
|
751
|
-
{
|
752
|
-
int c = peek(f, i);
|
753
|
-
|
754
|
-
return isspace(c) || (c == EOF);
|
755
|
-
}
|
756
|
-
|
757
|
-
|
758
|
-
static int
|
759
|
-
isthisnonword(MMIOT *f, int i)
|
760
|
-
{
|
761
|
-
return isthisspace(f, i) || ispunct(peek(f,i));
|
762
|
-
}
|
763
|
-
|
764
|
-
|
765
774
|
/* smartyquote code that's common for single and double quotes
|
766
775
|
*/
|
767
776
|
static int
|
@@ -931,7 +940,8 @@ text(MMIOT *f)
|
|
931
940
|
Qchar(c, f);
|
932
941
|
break;
|
933
942
|
#if SUPERSCRIPT
|
934
|
-
|
943
|
+
/* A^B -> A<sup>B</sup> */
|
944
|
+
case '^': if ( (f->flags & (STRICT|INSIDE_TAG)) || isthisspace(f,-1) || isthisspace(f,1) )
|
935
945
|
Qchar(c,f);
|
936
946
|
else {
|
937
947
|
char *sup = cursor(f);
|
@@ -948,11 +958,10 @@ text(MMIOT *f)
|
|
948
958
|
#endif
|
949
959
|
case '_':
|
950
960
|
#if RELAXED_EMPHASIS
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|| (isalnum(peek(f,-1)) && isalnum(peek(f,1))) ) {
|
961
|
+
/* Underscores don't count if they're in the middle of a word */
|
962
|
+
if ( (!(f->flags & STRICT))
|
963
|
+
&& ((isthisspace(f,-1) && isthisspace(f,1))
|
964
|
+
|| (isthisalnum(f,-1) && isthisalnum(f,1))) ){
|
956
965
|
Qchar(c, f);
|
957
966
|
break;
|
958
967
|
}
|
@@ -967,7 +976,7 @@ text(MMIOT *f)
|
|
967
976
|
}
|
968
977
|
break;
|
969
978
|
|
970
|
-
case '`': if ( tag_text(f) )
|
979
|
+
case '`': if ( tag_text(f) || !iscodeblock(f) )
|
971
980
|
Qchar(c, f);
|
972
981
|
else {
|
973
982
|
Qstring("<code>", f);
|
@@ -1006,7 +1015,7 @@ text(MMIOT *f)
|
|
1006
1015
|
break;
|
1007
1016
|
|
1008
1017
|
case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
|
1009
|
-
while (
|
1018
|
+
while ( isthisalnum(f,j) )
|
1010
1019
|
++j;
|
1011
1020
|
|
1012
1021
|
if ( peek(f,j) != ';' )
|
@@ -1019,9 +1028,31 @@ text(MMIOT *f)
|
|
1019
1028
|
break;
|
1020
1029
|
}
|
1021
1030
|
}
|
1031
|
+
/* truncate the input string after we've finished processing it */
|
1032
|
+
S(f->in) = f->isp = 0;
|
1022
1033
|
} /* text */
|
1023
1034
|
|
1024
1035
|
|
1036
|
+
static int
|
1037
|
+
iscodeblock(MMIOT *f)
|
1038
|
+
{
|
1039
|
+
int i=1, single = 1, c;
|
1040
|
+
|
1041
|
+
if ( peek(f,i) == '`' ) {
|
1042
|
+
single=0;
|
1043
|
+
i++;
|
1044
|
+
}
|
1045
|
+
while ( (c=peek(f,i)) != EOF ) {
|
1046
|
+
if ( (c == '`') && (single || peek(f,i+1) == '`') )
|
1047
|
+
return 1;
|
1048
|
+
else if ( c == '\\' )
|
1049
|
+
i++;
|
1050
|
+
i++;
|
1051
|
+
}
|
1052
|
+
return 0;
|
1053
|
+
|
1054
|
+
}
|
1055
|
+
|
1025
1056
|
static int
|
1026
1057
|
endofcode(int escape, int offset, MMIOT *f)
|
1027
1058
|
{
|
@@ -1075,7 +1106,13 @@ code(int escape, MMIOT *f)
|
|
1075
1106
|
static void
|
1076
1107
|
printheader(Paragraph *pp, MMIOT *f)
|
1077
1108
|
{
|
1078
|
-
Qprintf(f, "<h%d
|
1109
|
+
Qprintf(f, "<h%d", pp->hnumber);
|
1110
|
+
if ( f->flags & TOC ) {
|
1111
|
+
Qprintf(f, " id=\"", pp->hnumber);
|
1112
|
+
mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), Qchar, f);
|
1113
|
+
Qchar('"', f);
|
1114
|
+
}
|
1115
|
+
Qchar('>', f);
|
1079
1116
|
push(T(pp->text->text), S(pp->text->text), f);
|
1080
1117
|
text(f);
|
1081
1118
|
Qprintf(f, "</h%d>", pp->hnumber);
|
@@ -1152,10 +1189,11 @@ printhtml(Line *t, MMIOT *f)
|
|
1152
1189
|
|
1153
1190
|
|
1154
1191
|
static void
|
1155
|
-
htmlify(Paragraph *p, char *block, MMIOT *f)
|
1192
|
+
htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
|
1156
1193
|
{
|
1157
1194
|
emblock(f);
|
1158
|
-
if ( block )
|
1195
|
+
if ( block )
|
1196
|
+
Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
|
1159
1197
|
emblock(f);
|
1160
1198
|
|
1161
1199
|
while (( p = display(p, f) )) {
|
@@ -1163,7 +1201,8 @@ htmlify(Paragraph *p, char *block, MMIOT *f)
|
|
1163
1201
|
Qstring("\n\n", f);
|
1164
1202
|
}
|
1165
1203
|
|
1166
|
-
if ( block )
|
1204
|
+
if ( block )
|
1205
|
+
Qprintf(f, "</%s>", block);
|
1167
1206
|
emblock(f);
|
1168
1207
|
}
|
1169
1208
|
|
@@ -1184,7 +1223,7 @@ definitionlist(Paragraph *p, MMIOT *f)
|
|
1184
1223
|
Qstring("</dt>\n", f);
|
1185
1224
|
}
|
1186
1225
|
|
1187
|
-
htmlify(p->down, "dd", f);
|
1226
|
+
htmlify(p->down, "dd", p->ident, f);
|
1188
1227
|
}
|
1189
1228
|
|
1190
1229
|
Qstring("</dl>", f);
|
@@ -1197,10 +1236,13 @@ static void
|
|
1197
1236
|
listdisplay(int typ, Paragraph *p, MMIOT* f)
|
1198
1237
|
{
|
1199
1238
|
if ( p ) {
|
1200
|
-
Qprintf(f, "<%cl
|
1239
|
+
Qprintf(f, "<%cl", (typ==UL)?'u':'o');
|
1240
|
+
if ( typ == AL )
|
1241
|
+
Qprintf(f, " type=a");
|
1242
|
+
Qprintf(f, ">\n");
|
1201
1243
|
|
1202
1244
|
for ( ; p ; p = p->next ) {
|
1203
|
-
htmlify(p->down, "li", f);
|
1245
|
+
htmlify(p->down, "li", p->ident, f);
|
1204
1246
|
Qchar('\n', f);
|
1205
1247
|
}
|
1206
1248
|
|
@@ -1230,11 +1272,12 @@ display(Paragraph *p, MMIOT *f)
|
|
1230
1272
|
break;
|
1231
1273
|
|
1232
1274
|
case QUOTE:
|
1233
|
-
htmlify(p->down, "blockquote", f);
|
1275
|
+
htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
|
1234
1276
|
break;
|
1235
1277
|
|
1236
1278
|
case UL:
|
1237
1279
|
case OL:
|
1280
|
+
case AL:
|
1238
1281
|
listdisplay(p->typ, p->down, f);
|
1239
1282
|
break;
|
1240
1283
|
|
@@ -1291,7 +1334,7 @@ mkd_document(Document *p, char **res)
|
|
1291
1334
|
{
|
1292
1335
|
if ( p && p->compiled ) {
|
1293
1336
|
if ( ! p->html ) {
|
1294
|
-
htmlify(p->code, 0, p->ctx);
|
1337
|
+
htmlify(p->code, 0, 0, p->ctx);
|
1295
1338
|
p->html = 1;
|
1296
1339
|
}
|
1297
1340
|
|
data/ext/markdown.c
CHANGED
@@ -286,7 +286,7 @@ ishr(Line *t)
|
|
286
286
|
static int
|
287
287
|
ishdr(Line *t, int *htyp)
|
288
288
|
{
|
289
|
-
int i
|
289
|
+
int i;
|
290
290
|
|
291
291
|
|
292
292
|
/* first check for etx-style ###HEADER###
|
@@ -297,21 +297,11 @@ ishdr(Line *t, int *htyp)
|
|
297
297
|
for ( i=0; T(t->text)[i] == '#'; ++i)
|
298
298
|
;
|
299
299
|
|
300
|
+
/* ANY leading `#`'s make this into an ETX header
|
301
|
+
*/
|
300
302
|
if ( i ) {
|
301
|
-
|
302
|
-
|
303
|
-
j = S(t->text)-1;
|
304
|
-
|
305
|
-
while ( (j > i) && (T(t->text)[j] == '#') )
|
306
|
-
--j;
|
307
|
-
|
308
|
-
while ( (j > 1) && isspace(T(t->text)[j]) )
|
309
|
-
--j;
|
310
|
-
|
311
|
-
if ( i < j ) {
|
312
|
-
*htyp = ETX;
|
313
|
-
return 1;
|
314
|
-
}
|
303
|
+
*htyp = ETX;
|
304
|
+
return 1;
|
315
305
|
}
|
316
306
|
|
317
307
|
/* then check for setext-style HEADER
|
@@ -362,7 +352,7 @@ islist(Line *t, int *trim)
|
|
362
352
|
*trim = 4;
|
363
353
|
return DL;
|
364
354
|
}
|
365
|
-
|
355
|
+
|
366
356
|
if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
|
367
357
|
i = nextnonblank(t, t->dle+1);
|
368
358
|
*trim = (i > 4) ? 4 : i;
|
@@ -371,6 +361,13 @@ islist(Line *t, int *trim)
|
|
371
361
|
|
372
362
|
if ( (j = nextblank(t,t->dle)) > t->dle ) {
|
373
363
|
if ( T(t->text)[j-1] == '.' ) {
|
364
|
+
#if ALPHA_LIST
|
365
|
+
if ( (j == t->dle + 2) && isalpha(T(t->text)[t->dle]) ) {
|
366
|
+
j = nextnonblank(t,j);
|
367
|
+
*trim = j;
|
368
|
+
return AL;
|
369
|
+
}
|
370
|
+
#endif
|
374
371
|
strtoul(T(t->text)+t->dle, &q, 10);
|
375
372
|
if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) {
|
376
373
|
j = nextnonblank(t,j);
|
@@ -406,7 +403,7 @@ headerblock(Paragraph *pp, int htyp)
|
|
406
403
|
* the leading and trailing `#`'s
|
407
404
|
*/
|
408
405
|
|
409
|
-
for (i=0; T(p->text)[i] == T(p->text)[0]; i++)
|
406
|
+
for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1); i++)
|
410
407
|
;
|
411
408
|
|
412
409
|
pp->hnumber = i;
|
@@ -416,9 +413,12 @@ headerblock(Paragraph *pp, int htyp)
|
|
416
413
|
|
417
414
|
CLIP(p->text, 0, i);
|
418
415
|
|
419
|
-
for (j=S(p->text); j && (T(p->text)[j-1] == '#'); --j)
|
416
|
+
for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j)
|
420
417
|
;
|
421
418
|
|
419
|
+
while ( j && isspace(T(p->text)[j-1]) )
|
420
|
+
--j;
|
421
|
+
|
422
422
|
S(p->text) = j;
|
423
423
|
|
424
424
|
ret = p->next;
|
@@ -502,6 +502,47 @@ textblock(Paragraph *p, int toplevel)
|
|
502
502
|
}
|
503
503
|
|
504
504
|
|
505
|
+
/* length of the id: or class: kind in a special div-not-quote block
|
506
|
+
*/
|
507
|
+
static int
|
508
|
+
szmarkerclass(char *p)
|
509
|
+
{
|
510
|
+
if ( strncasecmp(p, "id:", 3) == 0 )
|
511
|
+
return 3;
|
512
|
+
if ( strncasecmp(p, "class:", 6) == 0 )
|
513
|
+
return 6;
|
514
|
+
return 0;
|
515
|
+
}
|
516
|
+
|
517
|
+
|
518
|
+
/*
|
519
|
+
* check if the first line of a quoted block is the special div-not-quote
|
520
|
+
* marker %[kind:]name%
|
521
|
+
*/
|
522
|
+
static int
|
523
|
+
isdivmarker(Line *p)
|
524
|
+
{
|
525
|
+
#if DIV_QUOTE
|
526
|
+
char *s = T(p->text);
|
527
|
+
int len = S(p->text);
|
528
|
+
int i;
|
529
|
+
|
530
|
+
if ( !(len && s[0] == '%' && s[len-1] == '%') ) return 0;
|
531
|
+
|
532
|
+
i = szmarkerclass(s+1);
|
533
|
+
--len;
|
534
|
+
|
535
|
+
while ( ++i < len )
|
536
|
+
if ( !isalnum(s[i]) )
|
537
|
+
return 0;
|
538
|
+
|
539
|
+
return 1;
|
540
|
+
#else
|
541
|
+
return 0;
|
542
|
+
#endif
|
543
|
+
}
|
544
|
+
|
545
|
+
|
505
546
|
/*
|
506
547
|
* accumulate a blockquote.
|
507
548
|
*
|
@@ -528,9 +569,27 @@ quoteblock(Paragraph *p)
|
|
528
569
|
|
529
570
|
if ( !(q = skipempty(t->next)) || ((q != t->next) && !isquote(q)) ) {
|
530
571
|
___mkd_freeLineRange(t, q);
|
531
|
-
|
572
|
+
t = q;
|
573
|
+
break;
|
532
574
|
}
|
533
575
|
}
|
576
|
+
if ( isdivmarker(p->text) ) {
|
577
|
+
char *prefix = "class";
|
578
|
+
int i;
|
579
|
+
|
580
|
+
q = p->text;
|
581
|
+
p->text = p->text->next;
|
582
|
+
|
583
|
+
if ( (i = szmarkerclass(1+T(q->text))) == 3 )
|
584
|
+
/* and this would be an "%id:" prefix */
|
585
|
+
prefix="id";
|
586
|
+
|
587
|
+
if ( p->ident = malloc(4+i+S(q->text)) )
|
588
|
+
sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2),
|
589
|
+
T(q->text)+(i+1) );
|
590
|
+
|
591
|
+
___mkd_freeLine(q);
|
592
|
+
}
|
534
593
|
return t;
|
535
594
|
}
|
536
595
|
|
@@ -574,7 +633,7 @@ listitem(Paragraph *p, int indent)
|
|
574
633
|
indent = 4;
|
575
634
|
}
|
576
635
|
|
577
|
-
if ( (q->dle < indent) && (ishr(q) ||
|
636
|
+
if ( (q->dle < indent) && (ishr(q) || islist(q,&z)) && !ishdr(q,&z) ) {
|
578
637
|
q = t->next;
|
579
638
|
t->next = 0;
|
580
639
|
return q;
|
@@ -617,7 +676,7 @@ listblock(Paragraph *top, int trim, MMIOT *f)
|
|
617
676
|
|
618
677
|
if ( para && (top->typ != DL) && p->down ) p->down->align = PARA;
|
619
678
|
|
620
|
-
if ( !(q = skipempty(text)) || (islist(q
|
679
|
+
if ( !(q = skipempty(text)) || (islist(q, &trim) == 0) )
|
621
680
|
break;
|
622
681
|
|
623
682
|
if ( para = (q != text) ) {
|
data/ext/markdown.h
CHANGED
@@ -33,8 +33,9 @@ typedef struct paragraph {
|
|
33
33
|
struct paragraph *next; /* next paragraph */
|
34
34
|
struct paragraph *down; /* recompiled contents of this paragraph */
|
35
35
|
struct line *text; /* all the text in this paragraph */
|
36
|
+
char *ident; /* %id% tag for QUOTE */
|
36
37
|
enum { WHITESPACE=0, CODE, QUOTE, MARKUP,
|
37
|
-
HTML, STYLE, DL, UL, OL, LISTITEM,
|
38
|
+
HTML, STYLE, DL, UL, OL, AL, LISTITEM,
|
38
39
|
HDR, HR } typ;
|
39
40
|
enum { IMPLICIT=0, PARA, CENTER} align;
|
40
41
|
int hnumber; /* <Hn> for typ == HDR */
|
@@ -68,10 +69,12 @@ typedef struct mmiot {
|
|
68
69
|
#define DENY_IMG 0x0002
|
69
70
|
#define DENY_SMARTY 0x0004
|
70
71
|
#define DENY_HTML 0x0008
|
72
|
+
#define STRICT 0x0010
|
71
73
|
#define INSIDE_TAG 0x0020
|
72
74
|
#define NO_PSEUDO_PROTO 0x0040
|
73
75
|
#define CDATA_OUTPUT 0x0080
|
74
|
-
#define
|
76
|
+
#define TOC 0x1000
|
77
|
+
#define USER_FLAGS 0xF0FF
|
75
78
|
#define EMBEDDED DENY_A|DENY_IMG|NO_PSEUDO_PROTO|CDATA_OUTPUT
|
76
79
|
char *base;
|
77
80
|
} MMIOT;
|
@@ -100,9 +103,11 @@ extern int mkd_firstnonblank(Line *);
|
|
100
103
|
extern int mkd_compile(Document *, int);
|
101
104
|
extern int mkd_document(Document *, char **);
|
102
105
|
extern int mkd_generatehtml(Document *, FILE *);
|
106
|
+
extern int mkd_style(Document *, FILE *);
|
103
107
|
extern void mkd_cleanup(Document *);
|
104
108
|
extern int mkd_text(char *, int, FILE*, int);
|
105
109
|
extern void mkd_basename(Document*, char *);
|
110
|
+
extern void mkd_string_to_anchor(char*,int, void(*)(int,void*), void*);
|
106
111
|
|
107
112
|
extern Document *mkd_in(FILE *, int);
|
108
113
|
extern Document *mkd_string(char*,int, int);
|
data/ext/mkdio.c
CHANGED
@@ -223,3 +223,19 @@ mkd_basename(Document *document, char *base)
|
|
223
223
|
if ( document )
|
224
224
|
document->base = base;
|
225
225
|
}
|
226
|
+
|
227
|
+
|
228
|
+
/* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
|
229
|
+
*/
|
230
|
+
void
|
231
|
+
mkd_string_to_anchor(char *s, int len, void(*outchar)(int,void*), void *out)
|
232
|
+
{
|
233
|
+
for ( ; len-- > 0; ++s ) {
|
234
|
+
if ( *s == ' ' || *s == '&' || *s == '<' || *s == '"' )
|
235
|
+
(*outchar)('+', out);
|
236
|
+
else if ( isalnum(*s) || ispunct(*s) )
|
237
|
+
(*outchar)(*s, out);
|
238
|
+
else
|
239
|
+
(*outchar)('~',out);
|
240
|
+
}
|
241
|
+
}
|
data/ext/mkdio.h
CHANGED
@@ -16,6 +16,8 @@ void mkd_basename(MMIOT*,char*);
|
|
16
16
|
*/
|
17
17
|
int mkd_compile(MMIOT*, int);
|
18
18
|
int mkd_generatehtml(MMIOT*,FILE*);
|
19
|
+
int mkd_generatetoc(MMIOT*,FILE*);
|
20
|
+
int mkd_xhtmlpage(MMIOT*,int,FILE*);
|
19
21
|
int mkd_cleanup(MMIOT*);
|
20
22
|
|
21
23
|
/* markup functions
|
@@ -25,6 +27,7 @@ int mkd_style(MMIOT*, FILE*);
|
|
25
27
|
int mkd_dump(MMIOT*, FILE*, int, char*);
|
26
28
|
int markdown(MMIOT*, FILE*, int);
|
27
29
|
void mkd_basename(MMIOT*,char*);
|
30
|
+
void mkd_string_to_anchor(char *, int, int (*)(int,void*), void*);
|
28
31
|
|
29
32
|
/* header block access
|
30
33
|
*/
|
@@ -46,9 +49,11 @@ extern char markdown_version[];
|
|
46
49
|
#define MKD_NOIMAGE 0x0002 /* don't do image processing, block <img> */
|
47
50
|
#define MKD_NOPANTS 0x0004 /* don't run smartypants() */
|
48
51
|
#define MKD_NOHTML 0x0008 /* don't allow raw html through AT ALL */
|
52
|
+
#define MKD_STRICT 0x0010 /* disable SUPERSCRIPT, RELAXED_EMPHASIS */
|
49
53
|
#define MKD_TAGTEXT 0x0020 /* don't expand `_` and `*` */
|
50
54
|
#define MKD_NO_EXT 0x0040 /* don't allow pseudo-protocols */
|
51
55
|
#define MKD_CDATA 0x0080 /* generate code for xml ![CDATA[...]] */
|
56
|
+
#define MKD_TOC 0x1000 /* do table-of-contents processing */
|
52
57
|
#define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
|
53
58
|
|
54
59
|
/* special flags for mkd_in() and mkd_string()
|
@@ -56,4 +61,5 @@ extern char markdown_version[];
|
|
56
61
|
#define MKD_NOHEADER 0x0100 /* don't process header blocks */
|
57
62
|
#define MKD_TABSTOP 0x0200 /* expand tabs to 4 spaces */
|
58
63
|
|
64
|
+
|
59
65
|
#endif/*_MKDIO_D*/
|
data/ext/resource.c
CHANGED
data/lib/rdiscount.rb
CHANGED
data/rdiscount.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'rdiscount'
|
3
|
+
s.version = '1.3.1'
|
4
|
+
s.summary = "Fast Implementation of Gruber's Markdown in C"
|
5
|
+
s.date = '2009-01-10'
|
6
|
+
s.email = 'r@tomayko.com'
|
7
|
+
s.homepage = 'http://github.com/rtomayko/rdiscount'
|
8
|
+
s.has_rdoc = true
|
9
|
+
s.authors = ["Ryan Tomayko", "Andrew White"]
|
10
|
+
# = MANIFEST =
|
11
|
+
s.files = %w[
|
12
|
+
COPYING
|
13
|
+
README.markdown
|
14
|
+
Rakefile
|
15
|
+
bin/rdiscount
|
16
|
+
ext/amalloc.h
|
17
|
+
ext/config.h
|
18
|
+
ext/cstring.h
|
19
|
+
ext/docheader.c
|
20
|
+
ext/dumptree.c
|
21
|
+
ext/extconf.rb
|
22
|
+
ext/generate.c
|
23
|
+
ext/markdown.c
|
24
|
+
ext/markdown.h
|
25
|
+
ext/mkdio.c
|
26
|
+
ext/mkdio.h
|
27
|
+
ext/rbstrio.c
|
28
|
+
ext/rbstrio.h
|
29
|
+
ext/rdiscount.c
|
30
|
+
ext/resource.c
|
31
|
+
lib/markdown.rb
|
32
|
+
lib/rdiscount.rb
|
33
|
+
rdiscount.gemspec
|
34
|
+
test/benchmark.rb
|
35
|
+
test/benchmark.txt
|
36
|
+
test/markdown_test.rb
|
37
|
+
test/rdiscount_test.rb
|
38
|
+
]
|
39
|
+
# = MANIFEST =
|
40
|
+
s.test_files = ["test/markdown_test.rb", "test/rdiscount_test.rb"]
|
41
|
+
s.extra_rdoc_files = ["COPYING"]
|
42
|
+
s.extensions = ["ext/extconf.rb"]
|
43
|
+
s.executables = ["rdiscount"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubyforge_project = 'wink'
|
46
|
+
end
|
data/test/markdown_test.rb
CHANGED
@@ -74,6 +74,11 @@ class MarkdownTest < Test::Unit::TestCase
|
|
74
74
|
assert_equal "<p>Well that’ll be the day</p>\n", markdown.to_html
|
75
75
|
end
|
76
76
|
|
77
|
+
def test_that_urls_are_not_doubly_escaped
|
78
|
+
markdown = Markdown.new('[Page 2](/search?query=Markdown+Test&page=2)')
|
79
|
+
assert_equal "<p><a href=\"/search?query=Markdown+Test&page=2\">Page 2</a></p>\n", markdown.to_html
|
80
|
+
end
|
81
|
+
|
77
82
|
# Build tests for each file in the MarkdownTest test suite
|
78
83
|
|
79
84
|
Dir["#{MARKDOWN_TEST_DIR}/Tests/*.text"].each do |text_file|
|
data/test/rdiscount_test.rb
CHANGED
@@ -12,4 +12,19 @@ class RDiscountTest < Test::Unit::TestCase
|
|
12
12
|
TEXT
|
13
13
|
RDiscount.new(text).to_html
|
14
14
|
end
|
15
|
+
|
16
|
+
def test_that_smart_converts_double_quotes_to_curly_quotes
|
17
|
+
rd = RDiscount.new(%("Quoted text"), :smart)
|
18
|
+
assert_equal %(<p>“Quoted text”</p>\n), rd.to_html
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_that_smart_converts_double_quotes_to_curly_quotes_before_a_heading
|
22
|
+
rd = RDiscount.new(%("Quoted text"\n\n# Heading), :smart)
|
23
|
+
assert_equal %(<p>“Quoted text”</p>\n\n<h1>Heading</h1>\n), rd.to_html
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_that_smart_converts_double_quotes_to_curly_quotes_after_a_heading
|
27
|
+
rd = RDiscount.new(%(# Heading\n\n"Quoted text"), :smart)
|
28
|
+
assert_equal %(<h1>Heading</h1>\n\n<p>“Quoted text”</p>\n), rd.to_html
|
29
|
+
end
|
15
30
|
end
|
metadata
CHANGED
@@ -1,53 +1,54 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdiscount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Tomayko
|
8
|
+
- Andrew White
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2009-01-10 00:00:00 -08:00
|
13
14
|
default_executable:
|
14
15
|
dependencies: []
|
15
16
|
|
16
17
|
description:
|
17
18
|
email: r@tomayko.com
|
18
|
-
executables:
|
19
|
-
|
19
|
+
executables:
|
20
|
+
- rdiscount
|
20
21
|
extensions:
|
21
22
|
- ext/extconf.rb
|
22
23
|
extra_rdoc_files:
|
23
24
|
- COPYING
|
24
25
|
files:
|
25
|
-
- README.markdown
|
26
26
|
- COPYING
|
27
|
+
- README.markdown
|
27
28
|
- Rakefile
|
28
|
-
-
|
29
|
-
-
|
30
|
-
-
|
31
|
-
-
|
32
|
-
- test/markdown_test.rb
|
33
|
-
- test/rdiscount_test.rb
|
34
|
-
- lib/markdown.rb
|
35
|
-
- lib/rdiscount.rb
|
36
|
-
- ext/extconf.rb
|
29
|
+
- bin/rdiscount
|
30
|
+
- ext/amalloc.h
|
31
|
+
- ext/config.h
|
32
|
+
- ext/cstring.h
|
37
33
|
- ext/docheader.c
|
38
34
|
- ext/dumptree.c
|
35
|
+
- ext/extconf.rb
|
39
36
|
- ext/generate.c
|
40
37
|
- ext/markdown.c
|
38
|
+
- ext/markdown.h
|
41
39
|
- ext/mkdio.c
|
40
|
+
- ext/mkdio.h
|
42
41
|
- ext/rbstrio.c
|
42
|
+
- ext/rbstrio.h
|
43
43
|
- ext/rdiscount.c
|
44
44
|
- ext/resource.c
|
45
|
-
-
|
46
|
-
-
|
47
|
-
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
45
|
+
- lib/markdown.rb
|
46
|
+
- lib/rdiscount.rb
|
47
|
+
- rdiscount.gemspec
|
48
|
+
- test/benchmark.rb
|
49
|
+
- test/benchmark.txt
|
50
|
+
- test/markdown_test.rb
|
51
|
+
- test/rdiscount_test.rb
|
51
52
|
has_rdoc: true
|
52
53
|
homepage: http://github.com/rtomayko/rdiscount
|
53
54
|
post_install_message:
|
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
71
|
requirements: []
|
71
72
|
|
72
73
|
rubyforge_project: wink
|
73
|
-
rubygems_version: 1.
|
74
|
+
rubygems_version: 1.3.1
|
74
75
|
signing_key:
|
75
76
|
specification_version: 2
|
76
77
|
summary: Fast Implementation of Gruber's Markdown in C
|