rdiscount 1.3.5 → 1.5.5
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/README.markdown +20 -10
- data/Rakefile +12 -11
- data/ext/Csio.c +14 -2
- data/ext/css.c +76 -0
- data/ext/cstring.h +7 -5
- data/ext/dumptree.c +5 -1
- data/ext/generate.c +473 -268
- data/ext/markdown.c +209 -96
- data/ext/markdown.h +16 -5
- data/ext/mkdio.c +68 -6
- data/ext/mkdio.h +20 -7
- data/ext/resource.c +13 -27
- data/ext/toc.c +24 -20
- data/ext/xml.c +82 -0
- data/lib/rdiscount.rb +2 -2
- data/rdiscount.gemspec +4 -2
- data/test/markdown_test.rb +17 -0
- metadata +5 -3
data/ext/markdown.c
CHANGED
@@ -21,10 +21,12 @@
|
|
21
21
|
*/
|
22
22
|
struct kw {
|
23
23
|
char *id;
|
24
|
-
int
|
24
|
+
int size;
|
25
|
+
int selfclose;
|
25
26
|
} ;
|
26
27
|
|
27
|
-
#define KW(x) { x, sizeof(x)-1 }
|
28
|
+
#define KW(x) { x, sizeof(x)-1, 0 }
|
29
|
+
#define SC(x) { x, sizeof(x)-1, 1 }
|
28
30
|
|
29
31
|
static struct kw blocktags[] = { KW("!--"), KW("STYLE"), KW("SCRIPT"),
|
30
32
|
KW("ADDRESS"), KW("BDO"), KW("BLOCKQUOTE"),
|
@@ -33,7 +35,8 @@ static struct kw blocktags[] = { KW("!--"), KW("STYLE"), KW("SCRIPT"),
|
|
33
35
|
KW("H6"), KW("LISTING"), KW("NOBR"),
|
34
36
|
KW("UL"), KW("P"), KW("OL"), KW("DL"),
|
35
37
|
KW("PLAINTEXT"), KW("PRE"), KW("TABLE"),
|
36
|
-
KW("WBR"), KW("XMP"),
|
38
|
+
KW("WBR"), KW("XMP"), SC("HR"), SC("BR"),
|
39
|
+
KW("IFRAME"), KW("MAP") };
|
37
40
|
#define SZTAGS (sizeof blocktags / sizeof blocktags[0])
|
38
41
|
#define MAXTAG 11 /* sizeof "BLOCKQUOTE" */
|
39
42
|
|
@@ -47,9 +50,9 @@ typedef ANCHOR(Paragraph) ParagraphRoot;
|
|
47
50
|
static int
|
48
51
|
casort(struct kw *a, struct kw *b)
|
49
52
|
{
|
50
|
-
if ( a->
|
51
|
-
return a->
|
52
|
-
return strncasecmp(a->id, b->id, b->
|
53
|
+
if ( a->size != b->size )
|
54
|
+
return a->size - b->size;
|
55
|
+
return strncasecmp(a->id, b->id, b->size);
|
53
56
|
}
|
54
57
|
|
55
58
|
|
@@ -125,14 +128,14 @@ skipempty(Line *p)
|
|
125
128
|
|
126
129
|
|
127
130
|
void
|
128
|
-
___mkd_tidy(
|
131
|
+
___mkd_tidy(Cstring *t)
|
129
132
|
{
|
130
|
-
while ( S(t
|
131
|
-
--S(t
|
133
|
+
while ( S(*t) && isspace(T(*t)[S(*t)-1]) )
|
134
|
+
--S(*t);
|
132
135
|
}
|
133
136
|
|
134
137
|
|
135
|
-
static
|
138
|
+
static struct kw *
|
136
139
|
isopentag(Line *p)
|
137
140
|
{
|
138
141
|
int i=0, len;
|
@@ -154,79 +157,84 @@ isopentag(Line *p)
|
|
154
157
|
;
|
155
158
|
|
156
159
|
key.id = T(p->text)+1;
|
157
|
-
key.
|
160
|
+
key.size = i-1;
|
158
161
|
|
159
|
-
if ( ret = bsearch(&key,blocktags,SZTAGS,sizeof key, (stfu)casort))
|
160
|
-
return ret
|
162
|
+
if ( ret = bsearch(&key, blocktags, SZTAGS, sizeof key, (stfu)casort))
|
163
|
+
return ret;
|
161
164
|
|
162
165
|
return 0;
|
163
166
|
}
|
164
167
|
|
165
168
|
|
166
|
-
|
167
|
-
|
168
|
-
{
|
169
|
-
char *q = T(t->text);
|
170
|
-
int siz = strlen(tag);
|
169
|
+
typedef struct _flo {
|
170
|
+
Line *t;
|
171
171
|
int i;
|
172
|
+
} FLO;
|
172
173
|
|
173
|
-
if ( strcasecmp(tag, "HR") == 0 || strcasecmp(tag, "BR") == 0 )
|
174
|
-
/* <HR> and <BR> are self-closing block-level tags,
|
175
|
-
*/
|
176
|
-
return 1;
|
177
174
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
175
|
+
static int
|
176
|
+
flogetc(FLO *f)
|
177
|
+
{
|
178
|
+
if ( f && f->t ) {
|
179
|
+
if ( f->i < S(f->t->text) )
|
180
|
+
return T(f->t->text)[f->i++];
|
181
|
+
f->t = f->t->next;
|
182
|
+
f->i = 0;
|
183
|
+
return flogetc(f);
|
184
|
+
}
|
185
|
+
return EOF;
|
185
186
|
}
|
186
187
|
|
187
188
|
|
188
189
|
static Line *
|
189
|
-
htmlblock(Paragraph *p,
|
190
|
+
htmlblock(Paragraph *p, struct kw *tag)
|
190
191
|
{
|
191
|
-
Line *
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
char *ps, *pse;
|
196
|
-
int depth;
|
197
|
-
|
198
|
-
tagsize = strlen(tag);
|
192
|
+
Line *ret;
|
193
|
+
FLO f = { p->text, 0 };
|
194
|
+
int c;
|
195
|
+
int i, closing, depth=0;
|
199
196
|
|
200
|
-
if ( selfclose
|
201
|
-
ret = t->next;
|
202
|
-
t->next = 0;
|
197
|
+
if ( tag->selfclose || (tag->size >= MAXTAG) ) {
|
198
|
+
ret = f.t->next;
|
199
|
+
f.t->next = 0;
|
203
200
|
return ret;
|
204
201
|
}
|
205
202
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
203
|
+
while ( (c = flogetc(&f)) != EOF ) {
|
204
|
+
if ( c == '<' ) {
|
205
|
+
/* tag? */
|
206
|
+
c = flogetc(&f);
|
207
|
+
if ( c == '!' ) { /* comment? */
|
208
|
+
if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) {
|
209
|
+
/* yes */
|
210
|
+
while ( (c = flogetc(&f)) != EOF ) {
|
211
|
+
if ( c == '-' && flogetc(&f) == '-'
|
212
|
+
&& flogetc(&f) == '>')
|
213
|
+
/* consumed whole comment */
|
214
|
+
break;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
else {
|
219
|
+
if ( closing = (c == '/') ) c = flogetc(&f);
|
220
|
+
|
221
|
+
for ( i=0; i < tag->size; c=flogetc(&f) ) {
|
222
|
+
if ( tag->id[i++] != toupper(c) )
|
223
|
+
break;
|
224
|
+
}
|
225
|
+
|
226
|
+
if ( (i == tag->size) && !isalnum(c) ) {
|
227
|
+
depth = depth + (closing ? -1 : 1);
|
228
|
+
if ( depth == 0 ) {
|
229
|
+
while ( c != EOF && c != '>' ) {
|
230
|
+
/* consume trailing gunk in close tag */
|
231
|
+
c = flogetc(&f);
|
232
|
+
}
|
233
|
+
ret = f.t->next;
|
234
|
+
f.t->next = 0;
|
235
|
+
return ret;
|
236
|
+
}
|
237
|
+
}
|
230
238
|
}
|
231
239
|
}
|
232
240
|
}
|
@@ -235,7 +243,7 @@ htmlblock(Paragraph *p, char *tag)
|
|
235
243
|
|
236
244
|
|
237
245
|
static Line *
|
238
|
-
comment(Paragraph *p
|
246
|
+
comment(Paragraph *p)
|
239
247
|
{
|
240
248
|
Line *t, *ret;
|
241
249
|
|
@@ -251,6 +259,30 @@ comment(Paragraph *p, char *key)
|
|
251
259
|
}
|
252
260
|
|
253
261
|
|
262
|
+
/* tables look like
|
263
|
+
* header|header{|header}
|
264
|
+
* ------|------{|......}
|
265
|
+
* {body lines}
|
266
|
+
*/
|
267
|
+
static int
|
268
|
+
istable(Line *t)
|
269
|
+
{
|
270
|
+
char *p;
|
271
|
+
Line *dashes = t->next;
|
272
|
+
|
273
|
+
/* two lines, first must contain | */
|
274
|
+
if ( !(dashes && memchr(T(t->text), '|', S(t->text))) )
|
275
|
+
return 0;
|
276
|
+
|
277
|
+
/* second line must be only whitespace, |, -, or - */
|
278
|
+
for ( p = T(dashes->text)+S(dashes->text)-1; p >= T(dashes->text); --p)
|
279
|
+
if ( ! ((*p == '|') || (*p == ':') || (*p == '-') || isspace(*p)) )
|
280
|
+
return 0;
|
281
|
+
|
282
|
+
return 1;
|
283
|
+
}
|
284
|
+
|
285
|
+
|
254
286
|
/* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
|
255
287
|
*/
|
256
288
|
static int
|
@@ -274,7 +306,11 @@ isfootnote(Line *t)
|
|
274
306
|
static int
|
275
307
|
isquote(Line *t)
|
276
308
|
{
|
277
|
-
|
309
|
+
char *pt = T(t->text);
|
310
|
+
return ( pt[0] == '>' ) ||
|
311
|
+
( pt[0] == ' ' && pt[1] == '>' ) ||
|
312
|
+
( pt[0] == ' ' && pt[1] == ' ' && pt[2] == '>') ||
|
313
|
+
( pt[0] == ' ' && pt[1] == ' ' && pt[2] == ' ' && pt[3] == '>');
|
278
314
|
}
|
279
315
|
|
280
316
|
|
@@ -330,7 +366,7 @@ ishdr(Line *t, int *htyp)
|
|
330
366
|
|
331
367
|
/* ANY leading `#`'s make this into an ETX header
|
332
368
|
*/
|
333
|
-
if ( i ) {
|
369
|
+
if ( i && (i < S(t->text) || i > 1) ) {
|
334
370
|
*htyp = ETX;
|
335
371
|
return 1;
|
336
372
|
}
|
@@ -546,17 +582,17 @@ szmarkerclass(char *p)
|
|
546
582
|
* marker %[kind:]name%
|
547
583
|
*/
|
548
584
|
static int
|
549
|
-
isdivmarker(Line *p)
|
585
|
+
isdivmarker(Line *p, int start)
|
550
586
|
{
|
551
587
|
#if DIV_QUOTE
|
552
588
|
char *s = T(p->text);
|
553
589
|
int len = S(p->text);
|
554
590
|
int i;
|
555
591
|
|
556
|
-
if ( !(len && s[
|
592
|
+
if ( !(len && s[start] == '%' && s[len-1] == '%') ) return 0;
|
557
593
|
|
558
|
-
i = szmarkerclass(s+1);
|
559
|
-
|
594
|
+
i = szmarkerclass(s+start+1)+start;
|
595
|
+
len -= start+1;
|
560
596
|
|
561
597
|
while ( ++i < len )
|
562
598
|
if ( !isalnum(s[i]) )
|
@@ -588,18 +624,22 @@ quoteblock(Paragraph *p)
|
|
588
624
|
|
589
625
|
for ( t = p->text; t ; t = q ) {
|
590
626
|
if ( isquote(t) ) {
|
591
|
-
|
627
|
+
char *p = strchr(T(t->text), '>');
|
628
|
+
if ( p[1] == ' ' ) p++;
|
629
|
+
qp = p - T(t->text) + 1;
|
592
630
|
CLIP(t->text, 0, qp);
|
593
631
|
t->dle = mkd_firstnonblank(t);
|
594
632
|
}
|
595
633
|
|
596
|
-
|
634
|
+
q = skipempty(t->next);
|
635
|
+
|
636
|
+
if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1))) ) {
|
597
637
|
___mkd_freeLineRange(t, q);
|
598
638
|
t = q;
|
599
639
|
break;
|
600
640
|
}
|
601
641
|
}
|
602
|
-
if ( isdivmarker(p->text) ) {
|
642
|
+
if ( isdivmarker(p->text,0) ) {
|
603
643
|
char *prefix = "class";
|
604
644
|
int i;
|
605
645
|
|
@@ -610,7 +650,7 @@ quoteblock(Paragraph *p)
|
|
610
650
|
/* and this would be an "%id:" prefix */
|
611
651
|
prefix="id";
|
612
652
|
|
613
|
-
if ( p->ident = malloc(4+
|
653
|
+
if ( p->ident = malloc(4+strlen(prefix)+S(q->text)) )
|
614
654
|
sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2),
|
615
655
|
T(q->text)+(i+1) );
|
616
656
|
|
@@ -620,6 +660,25 @@ quoteblock(Paragraph *p)
|
|
620
660
|
}
|
621
661
|
|
622
662
|
|
663
|
+
/*
|
664
|
+
* A table block starts with a table header (see istable()), and continues
|
665
|
+
* until EOF or a line that /doesn't/ contain a |.
|
666
|
+
*/
|
667
|
+
static Line *
|
668
|
+
tableblock(Paragraph *p)
|
669
|
+
{
|
670
|
+
Line *t, *q;
|
671
|
+
|
672
|
+
for ( t = p->text; t && (q = t->next); t = t->next ) {
|
673
|
+
if ( !memchr(T(q->text), '|', S(q->text)) ) {
|
674
|
+
t->next = 0;
|
675
|
+
return q;
|
676
|
+
}
|
677
|
+
}
|
678
|
+
return 0;
|
679
|
+
}
|
680
|
+
|
681
|
+
|
623
682
|
static Paragraph *Pp(ParagraphRoot *, Line *, int);
|
624
683
|
static Paragraph *compile(Line *, int, MMIOT *);
|
625
684
|
|
@@ -651,7 +710,7 @@ listitem(Paragraph *p, int indent)
|
|
651
710
|
* need any indentation
|
652
711
|
*/
|
653
712
|
if ( q != t->next ) {
|
654
|
-
if (q->dle <
|
713
|
+
if (q->dle < indent) {
|
655
714
|
q = t->next;
|
656
715
|
t->next = 0;
|
657
716
|
return q;
|
@@ -676,9 +735,10 @@ listblock(Paragraph *top, int trim, MMIOT *f)
|
|
676
735
|
{
|
677
736
|
ParagraphRoot d = { 0, 0 };
|
678
737
|
Paragraph *p;
|
679
|
-
Line *q = top->text, *text;
|
680
|
-
|
681
|
-
|
738
|
+
Line *q = top->text, *text, *label;
|
739
|
+
int isdl = (top->typ == DL),
|
740
|
+
para = 0,
|
741
|
+
ltype;
|
682
742
|
|
683
743
|
while (( text = q )) {
|
684
744
|
if ( top->typ == DL ) {
|
@@ -702,7 +762,8 @@ listblock(Paragraph *top, int trim, MMIOT *f)
|
|
702
762
|
|
703
763
|
if ( para && (top->typ != DL) && p->down ) p->down->align = PARA;
|
704
764
|
|
705
|
-
if ( !(q = skipempty(text)) || (islist(q, &trim) == 0)
|
765
|
+
if ( !(q = skipempty(text)) || ((ltype = islist(q, &trim)) == 0)
|
766
|
+
|| (isdl != (ltype == DL)) )
|
706
767
|
break;
|
707
768
|
|
708
769
|
if ( para = (q != text) ) {
|
@@ -832,6 +893,64 @@ consume(Line *ptr, int *eaten)
|
|
832
893
|
}
|
833
894
|
|
834
895
|
|
896
|
+
/*
|
897
|
+
* top-level compilation; break the document into
|
898
|
+
* style, html, and source blocks with footnote links
|
899
|
+
* weeded out.
|
900
|
+
*/
|
901
|
+
static Paragraph *
|
902
|
+
compile_document(Line *ptr, MMIOT *f)
|
903
|
+
{
|
904
|
+
ParagraphRoot d = { 0, 0 };
|
905
|
+
ANCHOR(Line) source = { 0, 0 };
|
906
|
+
Paragraph *p = 0;
|
907
|
+
struct kw *tag;
|
908
|
+
int eaten;
|
909
|
+
|
910
|
+
while ( ptr ) {
|
911
|
+
if ( !(f->flags & DENY_HTML) && (tag = isopentag(ptr)) ) {
|
912
|
+
/* If we encounter a html/style block, compile and save all
|
913
|
+
* of the cached source BEFORE processing the html/style.
|
914
|
+
*/
|
915
|
+
if ( T(source) ) {
|
916
|
+
E(source)->next = 0;
|
917
|
+
p = Pp(&d, 0, SOURCE);
|
918
|
+
p->down = compile(T(source), 1, f);
|
919
|
+
T(source) = E(source) = 0;
|
920
|
+
}
|
921
|
+
p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML);
|
922
|
+
if ( strcmp(tag->id, "!--") == 0 )
|
923
|
+
ptr = comment(p);
|
924
|
+
else
|
925
|
+
ptr = htmlblock(p, tag);
|
926
|
+
}
|
927
|
+
else if ( isfootnote(ptr) ) {
|
928
|
+
/* footnotes, like cats, sleep anywhere; pull them
|
929
|
+
* out of the input stream and file them away for
|
930
|
+
* later processing
|
931
|
+
*/
|
932
|
+
ptr = consume(addfootnote(ptr, f), &eaten);
|
933
|
+
}
|
934
|
+
else {
|
935
|
+
/* source; cache it up to wait for eof or the
|
936
|
+
* next html/style block
|
937
|
+
*/
|
938
|
+
ATTACH(source,ptr);
|
939
|
+
ptr = ptr->next;
|
940
|
+
}
|
941
|
+
}
|
942
|
+
if ( T(source) ) {
|
943
|
+
/* if there's any cached source at EOF, compile
|
944
|
+
* it now.
|
945
|
+
*/
|
946
|
+
E(source)->next = 0;
|
947
|
+
p = Pp(&d, 0, SOURCE);
|
948
|
+
p->down = compile(T(source), 1, f);
|
949
|
+
}
|
950
|
+
return T(d);
|
951
|
+
}
|
952
|
+
|
953
|
+
|
835
954
|
/*
|
836
955
|
* break a collection of markdown input into
|
837
956
|
* blocks of lists, code, html, and text to
|
@@ -842,29 +961,22 @@ compile(Line *ptr, int toplevel, MMIOT *f)
|
|
842
961
|
{
|
843
962
|
ParagraphRoot d = { 0, 0 };
|
844
963
|
Paragraph *p = 0;
|
845
|
-
char *key;
|
846
964
|
Line *r;
|
847
965
|
int para = toplevel;
|
966
|
+
int blocks = 0;
|
848
967
|
int hdr_type, list_type, indent;
|
849
968
|
|
850
969
|
ptr = consume(ptr, ¶);
|
851
970
|
|
852
971
|
while ( ptr ) {
|
853
|
-
if (
|
854
|
-
p = Pp(&d, ptr, strcmp(key, "STYLE") == 0 ? STYLE : HTML);
|
855
|
-
if ( strcmp(key, "!--") == 0 )
|
856
|
-
ptr = comment(p, key);
|
857
|
-
else
|
858
|
-
ptr = htmlblock(p, key);
|
859
|
-
}
|
860
|
-
else if ( iscode(ptr) ) {
|
972
|
+
if ( iscode(ptr) ) {
|
861
973
|
p = Pp(&d, ptr, CODE);
|
862
974
|
|
863
975
|
if ( f->flags & MKD_1_COMPAT) {
|
864
976
|
/* HORRIBLE STANDARDS KLUDGE: the first line of every block
|
865
977
|
* has trailing whitespace trimmed off.
|
866
978
|
*/
|
867
|
-
___mkd_tidy(p->text);
|
979
|
+
___mkd_tidy(&p->text->text);
|
868
980
|
}
|
869
981
|
|
870
982
|
ptr = codeblock(p);
|
@@ -889,9 +1001,9 @@ compile(Line *ptr, int toplevel, MMIOT *f)
|
|
889
1001
|
p = Pp(&d, ptr, HDR);
|
890
1002
|
ptr = headerblock(p, hdr_type);
|
891
1003
|
}
|
892
|
-
else if (
|
893
|
-
|
894
|
-
|
1004
|
+
else if ( istable(ptr) && !(f->flags & (STRICT|NOTABLES)) ) {
|
1005
|
+
p = Pp(&d, ptr, TABLE);
|
1006
|
+
ptr = tableblock(p);
|
895
1007
|
}
|
896
1008
|
else {
|
897
1009
|
p = Pp(&d, ptr, MARKUP);
|
@@ -901,7 +1013,8 @@ compile(Line *ptr, int toplevel, MMIOT *f)
|
|
901
1013
|
if ( (para||toplevel) && !p->align )
|
902
1014
|
p->align = PARA;
|
903
1015
|
|
904
|
-
|
1016
|
+
blocks++;
|
1017
|
+
para = toplevel || (blocks > 1);
|
905
1018
|
ptr = consume(ptr, ¶);
|
906
1019
|
|
907
1020
|
if ( para && !p->align )
|
@@ -952,7 +1065,7 @@ mkd_compile(Document *doc, int flags)
|
|
952
1065
|
|
953
1066
|
initialize();
|
954
1067
|
|
955
|
-
doc->code =
|
1068
|
+
doc->code = compile_document(T(doc->content), doc->ctx);
|
956
1069
|
qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
|
957
1070
|
sizeof T(*doc->ctx->footnotes)[0],
|
958
1071
|
(stfu)__mkd_footsort);
|