bluecloth 2.0.7 → 2.0.9
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.tar.gz.sig +0 -0
- data/ChangeLog +36 -6
- data/Rakefile +46 -26
- data/Rakefile.local +13 -6
- data/ext/VERSION +1 -1
- data/ext/bluecloth.c +46 -21
- data/ext/bluecloth.h +13 -2
- data/ext/cstring.h +3 -1
- data/ext/emmatch.c +188 -0
- data/ext/generate.c +203 -248
- data/ext/html5.c +24 -0
- data/ext/markdown.c +122 -98
- data/ext/markdown.h +16 -2
- data/ext/mkdio.c +43 -9
- data/ext/mkdio.h +11 -0
- data/ext/resource.c +1 -1
- data/ext/tags.c +110 -0
- data/ext/tags.h +18 -0
- data/lib/bluecloth.rb +33 -26
- data/rake/documentation.rb +115 -0
- data/rake/helpers.rb +375 -308
- data/rake/hg.rb +17 -3
- data/rake/manual.rb +11 -6
- data/rake/packaging.rb +7 -1
- data/rake/publishing.rb +162 -88
- data/spec/bluecloth/101_changes_spec.rb +1 -0
- data/spec/bluecloth/autolinks_spec.rb +1 -0
- data/spec/bluecloth/blockquotes_spec.rb +1 -0
- data/spec/bluecloth/code_spans_spec.rb +1 -0
- data/spec/bluecloth/emphasis_spec.rb +1 -0
- data/spec/bluecloth/entities_spec.rb +1 -0
- data/spec/bluecloth/hrules_spec.rb +1 -0
- data/spec/bluecloth/images_spec.rb +1 -0
- data/spec/bluecloth/inline_html_spec.rb +25 -61
- data/spec/bluecloth/lists_spec.rb +1 -0
- data/spec/bluecloth/paragraphs_spec.rb +1 -0
- data/spec/bluecloth/titles_spec.rb +1 -0
- data/spec/bluecloth_spec.rb +22 -6
- data/spec/bugfix_spec.rb +79 -2
- data/spec/contributions_spec.rb +1 -0
- data/spec/discount_spec.rb +46 -2
- data/spec/lib/helpers.rb +8 -8
- data/spec/lib/matchers.rb +5 -17
- data/spec/markdowntest_spec.rb +2 -34
- metadata +48 -17
- metadata.gz.sig +0 -0
- data/rake/rdoc.rb +0 -30
data/ext/html5.c
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
/* block-level tags for passing html5 blocks through the blender
|
2
|
+
*/
|
3
|
+
#include "tags.h"
|
4
|
+
|
5
|
+
void
|
6
|
+
mkd_with_html5_tags()
|
7
|
+
{
|
8
|
+
static int populated = 0;
|
9
|
+
|
10
|
+
if ( populated ) return;
|
11
|
+
populated = 1;
|
12
|
+
|
13
|
+
mkd_prepare_tags();
|
14
|
+
|
15
|
+
mkd_define_tag("ASIDE", 0);
|
16
|
+
mkd_define_tag("FOOTER", 0);
|
17
|
+
mkd_define_tag("HEADER", 0);
|
18
|
+
mkd_define_tag("HGROUP", 0);
|
19
|
+
mkd_define_tag("NAV", 0);
|
20
|
+
mkd_define_tag("SECTION", 0);
|
21
|
+
mkd_define_tag("ARTICLE", 0);
|
22
|
+
|
23
|
+
mkd_sort_tags();
|
24
|
+
}
|
data/ext/markdown.c
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
* The redistribution terms are provided in the COPYRIGHT file that must
|
5
5
|
* be distributed with this source code.
|
6
6
|
*/
|
7
|
+
#include "config.h"
|
8
|
+
|
7
9
|
#include <stdio.h>
|
8
10
|
#include <string.h>
|
9
11
|
#include <stdarg.h>
|
@@ -11,51 +13,15 @@
|
|
11
13
|
#include <time.h>
|
12
14
|
#include <ctype.h>
|
13
15
|
|
14
|
-
#include "config.h"
|
15
|
-
|
16
16
|
#include "cstring.h"
|
17
17
|
#include "markdown.h"
|
18
18
|
#include "amalloc.h"
|
19
|
-
|
20
|
-
/* block-level tags for passing html blocks through the blender
|
21
|
-
*/
|
22
|
-
struct kw {
|
23
|
-
char *id;
|
24
|
-
int size;
|
25
|
-
int selfclose;
|
26
|
-
} ;
|
27
|
-
|
28
|
-
#define KW(x) { x, sizeof(x)-1, 0 }
|
29
|
-
#define SC(x) { x, sizeof(x)-1, 1 }
|
30
|
-
|
31
|
-
static struct kw blocktags[] = { KW("!--"), KW("STYLE"), KW("SCRIPT"),
|
32
|
-
KW("ADDRESS"), KW("BDO"), KW("BLOCKQUOTE"),
|
33
|
-
KW("CENTER"), KW("DFN"), KW("DIV"), KW("H1"),
|
34
|
-
KW("H2"), KW("H3"), KW("H4"), KW("H5"),
|
35
|
-
KW("H6"), KW("LISTING"), KW("NOBR"),
|
36
|
-
KW("UL"), KW("P"), KW("OL"), KW("DL"),
|
37
|
-
KW("PLAINTEXT"), KW("PRE"), KW("TABLE"),
|
38
|
-
KW("WBR"), KW("XMP"), SC("HR"), SC("BR"),
|
39
|
-
KW("IFRAME"), KW("MAP") };
|
40
|
-
#define SZTAGS (sizeof blocktags / sizeof blocktags[0])
|
41
|
-
#define MAXTAG 11 /* sizeof "BLOCKQUOTE" */
|
19
|
+
#include "tags.h"
|
42
20
|
|
43
21
|
typedef int (*stfu)(const void*,const void*);
|
44
22
|
|
45
23
|
typedef ANCHOR(Paragraph) ParagraphRoot;
|
46
24
|
|
47
|
-
|
48
|
-
/* case insensitive string sort (for qsort() and bsearch() of block tags)
|
49
|
-
*/
|
50
|
-
static int
|
51
|
-
casort(struct kw *a, struct kw *b)
|
52
|
-
{
|
53
|
-
if ( a->size != b->size )
|
54
|
-
return a->size - b->size;
|
55
|
-
return strncasecmp(a->id, b->id, b->size);
|
56
|
-
}
|
57
|
-
|
58
|
-
|
59
25
|
/* case insensitive string sort for Footnote tags.
|
60
26
|
*/
|
61
27
|
int
|
@@ -135,19 +101,28 @@ ___mkd_tidy(Cstring *t)
|
|
135
101
|
}
|
136
102
|
|
137
103
|
|
104
|
+
static struct kw comment = { "!--", 3, 0 };
|
105
|
+
|
138
106
|
static struct kw *
|
139
107
|
isopentag(Line *p)
|
140
108
|
{
|
141
109
|
int i=0, len;
|
142
|
-
|
110
|
+
char *line;
|
143
111
|
|
144
112
|
if ( !p ) return 0;
|
145
113
|
|
114
|
+
line = T(p->text);
|
146
115
|
len = S(p->text);
|
147
116
|
|
148
|
-
if ( len < 3 ||
|
117
|
+
if ( len < 3 || line[0] != '<' )
|
149
118
|
return 0;
|
150
119
|
|
120
|
+
if ( line[1] == '!' && line[2] == '-' && line[3] == '-' )
|
121
|
+
/* comments need special case handling, because
|
122
|
+
* the !-- doesn't need to end in a whitespace
|
123
|
+
*/
|
124
|
+
return &comment;
|
125
|
+
|
151
126
|
/* find how long the tag is so we can check to see if
|
152
127
|
* it's a block-level tag
|
153
128
|
*/
|
@@ -156,13 +131,8 @@ isopentag(Line *p)
|
|
156
131
|
&& !isspace(T(p->text)[i]); ++i )
|
157
132
|
;
|
158
133
|
|
159
|
-
key.id = T(p->text)+1;
|
160
|
-
key.size = i-1;
|
161
|
-
|
162
|
-
if ( ret = bsearch(&key, blocktags, SZTAGS, sizeof key, (stfu)casort))
|
163
|
-
return ret;
|
164
134
|
|
165
|
-
return
|
135
|
+
return mkd_search_tags(T(p->text)+1, i-1);
|
166
136
|
}
|
167
137
|
|
168
138
|
|
@@ -171,6 +141,8 @@ typedef struct _flo {
|
|
171
141
|
int i;
|
172
142
|
} FLO;
|
173
143
|
|
144
|
+
#define floindex(x) (x.i)
|
145
|
+
|
174
146
|
|
175
147
|
static int
|
176
148
|
flogetc(FLO *f)
|
@@ -186,6 +158,41 @@ flogetc(FLO *f)
|
|
186
158
|
}
|
187
159
|
|
188
160
|
|
161
|
+
static void
|
162
|
+
splitline(Line *t, int cutpoint)
|
163
|
+
{
|
164
|
+
if ( t && (cutpoint < S(t->text)) ) {
|
165
|
+
Line *tmp = calloc(1, sizeof *tmp);
|
166
|
+
|
167
|
+
tmp->next = t->next;
|
168
|
+
t->next = tmp;
|
169
|
+
|
170
|
+
tmp->dle = t->dle;
|
171
|
+
SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
|
172
|
+
S(t->text) = cutpoint;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
static Line *
|
178
|
+
commentblock(Paragraph *p)
|
179
|
+
{
|
180
|
+
Line *t, *ret;
|
181
|
+
char *end;
|
182
|
+
|
183
|
+
for ( t = p->text; t ; t = t->next) {
|
184
|
+
if ( end = strstr(T(t->text), "-->") ) {
|
185
|
+
splitline(t, 3 + (end - T(t->text)) );
|
186
|
+
ret = t->next;
|
187
|
+
t->next = 0;
|
188
|
+
return ret;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
return t;
|
192
|
+
|
193
|
+
}
|
194
|
+
|
195
|
+
|
189
196
|
static Line *
|
190
197
|
htmlblock(Paragraph *p, struct kw *tag)
|
191
198
|
{
|
@@ -194,7 +201,10 @@ htmlblock(Paragraph *p, struct kw *tag)
|
|
194
201
|
int c;
|
195
202
|
int i, closing, depth=0;
|
196
203
|
|
197
|
-
if ( tag
|
204
|
+
if ( tag == &comment )
|
205
|
+
return commentblock(p);
|
206
|
+
|
207
|
+
if ( tag->selfclose ) {
|
198
208
|
ret = f.t->next;
|
199
209
|
f.t->next = 0;
|
200
210
|
return ret;
|
@@ -232,6 +242,7 @@ htmlblock(Paragraph *p, struct kw *tag)
|
|
232
242
|
}
|
233
243
|
if ( !f.t )
|
234
244
|
return 0;
|
245
|
+
splitline(f.t, floindex(f));
|
235
246
|
ret = f.t->next;
|
236
247
|
f.t->next = 0;
|
237
248
|
return ret;
|
@@ -244,23 +255,6 @@ htmlblock(Paragraph *p, struct kw *tag)
|
|
244
255
|
}
|
245
256
|
|
246
257
|
|
247
|
-
static Line *
|
248
|
-
comment(Paragraph *p)
|
249
|
-
{
|
250
|
-
Line *t, *ret;
|
251
|
-
|
252
|
-
for ( t = p->text; t ; t = t->next) {
|
253
|
-
if ( strstr(T(t->text), "-->") ) {
|
254
|
-
ret = t->next;
|
255
|
-
t->next = 0;
|
256
|
-
return ret;
|
257
|
-
}
|
258
|
-
}
|
259
|
-
return t;
|
260
|
-
|
261
|
-
}
|
262
|
-
|
263
|
-
|
264
258
|
/* tables look like
|
265
259
|
* header|header{|header}
|
266
260
|
* ------|------{|......}
|
@@ -305,8 +299,8 @@ isfootnote(Line *t)
|
|
305
299
|
for ( ++i; i < S(t->text) ; ++i ) {
|
306
300
|
if ( T(t->text)[i] == '[' )
|
307
301
|
return 0;
|
308
|
-
else if ( T(t->text)[i] == ']'
|
309
|
-
return 1;
|
302
|
+
else if ( T(t->text)[i] == ']' )
|
303
|
+
return ( T(t->text)[i+1] == ':' ) ;
|
310
304
|
}
|
311
305
|
return 0;
|
312
306
|
}
|
@@ -315,7 +309,14 @@ isfootnote(Line *t)
|
|
315
309
|
static int
|
316
310
|
isquote(Line *t)
|
317
311
|
{
|
318
|
-
|
312
|
+
int j;
|
313
|
+
|
314
|
+
for ( j=0; j < 4; j++ )
|
315
|
+
if ( T(t->text)[j] == '>' )
|
316
|
+
return 1;
|
317
|
+
else if ( !isspace(T(t->text)[j]) )
|
318
|
+
return 0;
|
319
|
+
return 0;
|
319
320
|
}
|
320
321
|
|
321
322
|
|
@@ -355,6 +356,34 @@ ishr(Line *t)
|
|
355
356
|
}
|
356
357
|
|
357
358
|
|
359
|
+
static int
|
360
|
+
issetext(Line *t, int *htyp)
|
361
|
+
{
|
362
|
+
int i;
|
363
|
+
/* then check for setext-style HEADER
|
364
|
+
* ======
|
365
|
+
*/
|
366
|
+
|
367
|
+
if ( t->next ) {
|
368
|
+
char *q = T(t->next->text);
|
369
|
+
int last = S(t->next->text);
|
370
|
+
|
371
|
+
if ( (*q == '=') || (*q == '-') ) {
|
372
|
+
/* ignore trailing whitespace */
|
373
|
+
while ( (last > 1) && isspace(q[last-1]) )
|
374
|
+
--last;
|
375
|
+
|
376
|
+
for (i=1; i < last; i++)
|
377
|
+
if ( q[0] != q[i] )
|
378
|
+
return 0;
|
379
|
+
*htyp = SETEXT;
|
380
|
+
return 1;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
return 0;
|
384
|
+
}
|
385
|
+
|
386
|
+
|
358
387
|
static int
|
359
388
|
ishdr(Line *t, int *htyp)
|
360
389
|
{
|
@@ -376,22 +405,7 @@ ishdr(Line *t, int *htyp)
|
|
376
405
|
return 1;
|
377
406
|
}
|
378
407
|
|
379
|
-
|
380
|
-
* ======
|
381
|
-
*/
|
382
|
-
|
383
|
-
if ( t->next ) {
|
384
|
-
char *q = T(t->next->text);
|
385
|
-
|
386
|
-
if ( (*q == '=') || (*q == '-') ) {
|
387
|
-
for (i=1; i < S(t->next->text); i++)
|
388
|
-
if ( q[0] != q[i] )
|
389
|
-
return 0;
|
390
|
-
*htyp = SETEXT;
|
391
|
-
return 1;
|
392
|
-
}
|
393
|
-
}
|
394
|
-
return 0;
|
408
|
+
return issetext(t, htyp);
|
395
409
|
}
|
396
410
|
|
397
411
|
|
@@ -475,7 +489,8 @@ headerblock(Paragraph *pp, int htyp)
|
|
475
489
|
* the leading and trailing `#`'s
|
476
490
|
*/
|
477
491
|
|
478
|
-
for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
|
492
|
+
for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
|
493
|
+
&& (i < 6); i++)
|
479
494
|
;
|
480
495
|
|
481
496
|
pp->hnumber = i;
|
@@ -629,7 +644,16 @@ quoteblock(Paragraph *p)
|
|
629
644
|
|
630
645
|
for ( t = p->text; t ; t = q ) {
|
631
646
|
if ( isquote(t) ) {
|
632
|
-
|
647
|
+
/* clip leading spaces */
|
648
|
+
for (qp = 0; T(t->text)[qp] != '>'; qp ++)
|
649
|
+
/* assert: the first nonblank character on this line
|
650
|
+
* will be a >
|
651
|
+
*/;
|
652
|
+
/* clip '>' */
|
653
|
+
qp++;
|
654
|
+
/* clip next space, if any */
|
655
|
+
if ( T(t->text)[qp] == ' ' )
|
656
|
+
qp++;
|
633
657
|
CLIP(t->text, 0, qp);
|
634
658
|
t->dle = mkd_firstnonblank(t);
|
635
659
|
}
|
@@ -709,8 +733,9 @@ listitem(Paragraph *p, int indent)
|
|
709
733
|
}
|
710
734
|
|
711
735
|
/* after a blank line, the next block needs to start with a line
|
712
|
-
* that's indented 4
|
713
|
-
*
|
736
|
+
* that's indented 4(? -- reference implementation allows a 1
|
737
|
+
* character indent, but that has unfortunate side effects here)
|
738
|
+
* spaces, but after that the line doesn't need any indentation
|
714
739
|
*/
|
715
740
|
if ( q != t->next ) {
|
716
741
|
if (q->dle < indent) {
|
@@ -718,10 +743,12 @@ listitem(Paragraph *p, int indent)
|
|
718
743
|
t->next = 0;
|
719
744
|
return q;
|
720
745
|
}
|
721
|
-
indent
|
746
|
+
/* indent at least 2, and at most as
|
747
|
+
* as far as the initial line was indented. */
|
748
|
+
indent = clip ? clip : 2;
|
722
749
|
}
|
723
750
|
|
724
|
-
if ( (q->dle < indent) && (ishr(q) || islist(q,&z)) && !
|
751
|
+
if ( (q->dle < indent) && (ishr(q) || islist(q,&z)) && !issetext(q,&z) ) {
|
725
752
|
q = t->next;
|
726
753
|
t->next = 0;
|
727
754
|
return q;
|
@@ -922,10 +949,7 @@ compile_document(Line *ptr, MMIOT *f)
|
|
922
949
|
T(source) = E(source) = 0;
|
923
950
|
}
|
924
951
|
p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML);
|
925
|
-
|
926
|
-
ptr = comment(p);
|
927
|
-
else
|
928
|
-
ptr = htmlblock(p, tag);
|
952
|
+
ptr = htmlblock(p, tag);
|
929
953
|
}
|
930
954
|
else if ( isfootnote(ptr) ) {
|
931
955
|
/* footnotes, like cats, sleep anywhere; pull them
|
@@ -1028,15 +1052,15 @@ compile(Line *ptr, int toplevel, MMIOT *f)
|
|
1028
1052
|
}
|
1029
1053
|
|
1030
1054
|
|
1031
|
-
|
1032
|
-
|
1055
|
+
void
|
1056
|
+
mkd_initialize()
|
1033
1057
|
{
|
1034
1058
|
static int first = 1;
|
1035
1059
|
|
1036
1060
|
if ( first-- > 0 ) {
|
1037
1061
|
first = 0;
|
1038
1062
|
INITRNG(time(0));
|
1039
|
-
|
1063
|
+
mkd_prepare_tags();
|
1040
1064
|
}
|
1041
1065
|
}
|
1042
1066
|
|
@@ -1060,13 +1084,13 @@ mkd_compile(Document *doc, int flags)
|
|
1060
1084
|
|
1061
1085
|
doc->compiled = 1;
|
1062
1086
|
memset(doc->ctx, 0, sizeof(MMIOT) );
|
1063
|
-
doc->ctx->
|
1064
|
-
doc->ctx->
|
1087
|
+
doc->ctx->cb = &(doc->cb);
|
1088
|
+
doc->ctx->flags = flags & USER_FLAGS;
|
1065
1089
|
CREATE(doc->ctx->in);
|
1066
1090
|
doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
|
1067
1091
|
CREATE(*doc->ctx->footnotes);
|
1068
1092
|
|
1069
|
-
|
1093
|
+
mkd_initialize();
|
1070
1094
|
|
1071
1095
|
doc->code = compile_document(T(doc->content), doc->ctx);
|
1072
1096
|
qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
|
data/ext/markdown.h
CHANGED
@@ -56,6 +56,17 @@ typedef struct block {
|
|
56
56
|
typedef STRING(block) Qblock;
|
57
57
|
|
58
58
|
|
59
|
+
typedef char* (*mkd_callback_t)(const char*, const int, void*);
|
60
|
+
typedef void (*mkd_free_t)(char*, void*);
|
61
|
+
|
62
|
+
typedef struct callback_data {
|
63
|
+
void *e_data; /* private data for callbacks */
|
64
|
+
mkd_callback_t e_url; /* url edit callback */
|
65
|
+
mkd_callback_t e_flags; /* extra href flags callback */
|
66
|
+
mkd_free_t e_free; /* edit/flags callback memory deallocator */
|
67
|
+
} Callback_data;
|
68
|
+
|
69
|
+
|
59
70
|
/* a magic markdown io thing holds all the data structures needed to
|
60
71
|
* do the backend processing of a markdown document
|
61
72
|
*/
|
@@ -75,13 +86,14 @@ typedef struct mmiot {
|
|
75
86
|
#define NO_PSEUDO_PROTO 0x0040
|
76
87
|
#define CDATA_OUTPUT 0x0080
|
77
88
|
#define NOTABLES 0x0400
|
89
|
+
#define NOSTRIKETHROUGH 0x0800
|
78
90
|
#define TOC 0x1000
|
79
91
|
#define MKD_1_COMPAT 0x2000
|
80
92
|
#define AUTOLINK 0x4000
|
81
93
|
#define SAFELINK 0x8000
|
82
94
|
#define USER_FLAGS 0xFCFF
|
83
95
|
#define EMBEDDED DENY_A|DENY_IMG|NO_PSEUDO_PROTO|CDATA_OUTPUT
|
84
|
-
|
96
|
+
Callback_data *cb;
|
85
97
|
} MMIOT;
|
86
98
|
|
87
99
|
|
@@ -93,6 +105,8 @@ typedef struct mmiot {
|
|
93
105
|
* root of the linked list of Lines.
|
94
106
|
*/
|
95
107
|
typedef struct document {
|
108
|
+
int magic; /* "I AM VALID" magic number */
|
109
|
+
#define VALID_DOCUMENT 0x19600731
|
96
110
|
Line *headers; /* title -> author(s) -> date */
|
97
111
|
ANCHOR(Line) content; /* uncompiled text, not valid after compile() */
|
98
112
|
Paragraph *code; /* intermediate code generated by compile() */
|
@@ -100,7 +114,7 @@ typedef struct document {
|
|
100
114
|
int html; /* set after (internal) htmlify() */
|
101
115
|
int tabstop; /* for properly expanding tabs (ick) */
|
102
116
|
MMIOT *ctx; /* backend buffers, flags, and structures */
|
103
|
-
|
117
|
+
Callback_data cb; /* callback functions & private data */
|
104
118
|
} Document;
|
105
119
|
|
106
120
|
|