bluecloth 2.0.5 → 2.0.6.pre120

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/ext/bluecloth.h ADDED
@@ -0,0 +1,19 @@
1
+ /*
2
+ * BlueCloth -- a Ruby implementation of Markdown
3
+ * $Id$
4
+ *
5
+ */
6
+
7
+ #ifndef BLUECLOTH_H
8
+ #define BLUECLOTH_H
9
+
10
+ #include "config.h"
11
+
12
+ #include "mkdio.h"
13
+ #include "ruby.h"
14
+
15
+ #ifdef HAVE_RUBY_ENCODING_H
16
+ # include "ruby/encoding.h"
17
+ #endif
18
+
19
+ #endif
data/ext/config.h CHANGED
@@ -6,6 +6,10 @@
6
6
  #ifndef CONFIG_H_RZLE3ADO
7
7
  #define CONFIG_H_RZLE3ADO
8
8
 
9
+ #ifdef RUBY_EXTCONF_H
10
+ # include RUBY_EXTCONF_H
11
+ #endif
12
+
9
13
  #ifdef HAVE_SRANDOM
10
14
  # define INITRNG(x) srandom((unsigned int)x)
11
15
  #elif HAVE_SRAND
@@ -14,6 +18,10 @@
14
18
  # define INITRNG(x) (void)1
15
19
  #endif
16
20
 
21
+ #ifndef HAVE_BZERO
22
+ # define bzero(s, n) (memset((void *)s, 0, (size_t)n))
23
+ #endif
24
+
17
25
  #ifdef HAVE_STRCASECMP
18
26
  #elif HAVE_STRICMP
19
27
  # define strcasecmp stricmp
data/ext/cstring.h CHANGED
@@ -60,9 +60,10 @@
60
60
  * macro will work with it.
61
61
  */
62
62
  #define ANCHOR(t) struct { t *text, *end; }
63
+ #define E(t) ((t).end)
63
64
 
64
- #define ATTACH(t, p) ( (t).text ?( ((t).end->next = (p)), ((t).end = (p)) ) \
65
- :( ((t).text = (t).end = (p)) ) )
65
+ #define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \
66
+ : ( (T(t) = E(t) = (p)) ) )
66
67
 
67
68
  typedef STRING(char) Cstring;
68
69
 
data/ext/extconf.rb CHANGED
@@ -19,7 +19,7 @@ $CPPFLAGS << %Q{ -DVERSION=\\"#{version}\\"}
19
19
 
20
20
  # Add my own debugging hooks if building for me
21
21
  if ENV['DEBUGGING_BUILD']
22
- $CFLAGS << ' -ggdb' << ' -DDEBUG'
22
+ $CFLAGS << ' -ggdb' << ' -DDEBUG'
23
23
  end
24
24
 
25
25
  def fail( *messages )
@@ -31,6 +31,9 @@ end
31
31
  have_func( "srand" ) || have_func( "srandom" )
32
32
  have_func( "random" ) || have_func( "rand" )
33
33
 
34
+ # bzero() isn't ANSI C, so use memset() if it isn't defined
35
+ have_func( "bzero", %w[string.h strings.h] )
36
+
34
37
  unless have_func( "strcasecmp" ) || have_func( "stricmp" )
35
38
  fail( "This extension requires either strcasecmp() or stricmp()" )
36
39
  end
@@ -40,6 +43,10 @@ end
40
43
 
41
44
  have_header( 'mkdio.h' ) or fail( "missing mkdio.h" )
42
45
 
46
+ # Check for 1.9.xish encoding header
47
+ have_header( 'ruby/encoding.h' )
48
+
49
+ create_header()
43
50
  create_makefile( 'bluecloth_ext' )
44
51
 
45
52
  FileUtils.rm_rf( 'conftest.dSYM' ) # MacOS X cleanup
data/ext/generate.c CHANGED
@@ -632,9 +632,9 @@ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
632
632
 
633
633
  Qstring(tag->link_sfx, f);
634
634
 
635
- if ( tag->WxH && ref->height && ref->width ) {
636
- Qprintf(f," height=\"%d\"", ref->height);
637
- Qprintf(f, " width=\"%d\"", ref->width);
635
+ if ( tag->WxH) {
636
+ if ( ref->height) Qprintf(f," height=\"%d\"", ref->height);
637
+ if ( ref->width) Qprintf(f, " width=\"%d\"", ref->width);
638
638
  }
639
639
 
640
640
  if ( S(ref->title) ) {
@@ -661,44 +661,46 @@ static int
661
661
  linkylinky(int image, MMIOT *f)
662
662
  {
663
663
  int start = mmiottell(f);
664
- int implicit_mark;
665
664
  Cstring name;
666
665
  Footnote key, *ref;
667
666
 
668
667
  int status = 0;
669
668
 
670
669
  CREATE(name);
671
- bzero(&key, sizeof key);
670
+ memset(&key, 0, sizeof key);
672
671
 
673
672
  if ( linkylabel(f, &name) ) {
674
- implicit_mark = mmiottell(f);
675
- eatspace(f);
676
-
677
- switch ( pull(f) ) {
678
- case '(': /* embedded link */
673
+ if ( peek(f,1) == '(' ) {
674
+ pull(f);
679
675
  if ( linkyurl(f, image, &key) )
680
676
  status = linkyformat(f, name, image, &key);
681
- break;
677
+ }
678
+ else {
679
+ int goodlink, implicit_mark = mmiottell(f);
682
680
 
683
- case '[':/* footnote link */
684
- default: /* (undocumented) implicit link */
685
- if ( peek(f, 0) != '[' ) {
681
+ if ( eatspace(f) == '[' ) {
682
+ pull(f); /* consume leading '[' */
683
+ goodlink = linkylabel(f, &key.tag);
684
+ }
685
+ else {
686
+ /* new markdown implicit name syntax doesn't
687
+ * require a second []
688
+ */
686
689
  mmiotseek(f, implicit_mark);
687
- if ( f->flags & MKD_1_COMPAT )
688
- break;
690
+ goodlink = !(f->flags & MKD_1_COMPAT);
689
691
  }
690
- else if ( !linkylabel(f, &key.tag) )
691
- break;
692
692
 
693
- if ( !S(key.tag) ) {
694
- DELETE(key.tag);
695
- T(key.tag) = T(name);
696
- S(key.tag) = S(name);
697
- }
693
+ if ( goodlink ) {
694
+ if ( !S(key.tag) ) {
695
+ DELETE(key.tag);
696
+ T(key.tag) = T(name);
697
+ S(key.tag) = S(name);
698
+ }
698
699
 
699
- if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes),
700
- sizeof key, (stfu)__mkd_footsort) )
701
- status = linkyformat(f, name, image, ref);
700
+ if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes),
701
+ sizeof key, (stfu)__mkd_footsort) )
702
+ status = linkyformat(f, name, image, ref);
703
+ }
702
704
  }
703
705
  }
704
706
 
@@ -867,9 +869,19 @@ maybe_tag_or_link(MMIOT *f)
867
869
 
868
870
  if ( size ) {
869
871
  if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
872
+
873
+ /* It is not a html tag unless we find the closing '>' in
874
+ * the same block.
875
+ */
876
+ while ( (c = peek(f, size+1)) != '>' )
877
+ if ( c == EOF )
878
+ return 0;
879
+ else
880
+ size++;
881
+
870
882
  Qstring(forbidden_tag(f) ? "&lt;" : "<", f);
871
883
  while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
872
- cputc(pull(f), f);
884
+ Qchar(pull(f), f);
873
885
  return 1;
874
886
  }
875
887
  else if ( !isspace(c) && process_possible_link(f, size) ) {
@@ -1276,6 +1288,106 @@ printheader(Paragraph *pp, MMIOT *f)
1276
1288
  }
1277
1289
 
1278
1290
 
1291
+ enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT };
1292
+
1293
+ static char* alignments[] = { "", " align=\"center\"", " align=\"left\"",
1294
+ " align=\"right\"" };
1295
+
1296
+ typedef STRING(int) Istring;
1297
+
1298
+ static int
1299
+ splat(Line *p, char *block, Istring align, int force, MMIOT *f)
1300
+ {
1301
+ int first,
1302
+ idx = 0,
1303
+ colno = 0;
1304
+
1305
+ Qstring("<tr>\n", f);
1306
+ while ( idx < S(p->text) ) {
1307
+ first = idx;
1308
+ if ( force && (colno >= S(align)-1) )
1309
+ idx = S(p->text);
1310
+ else
1311
+ while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') )
1312
+ ++idx;
1313
+
1314
+ Qprintf(f, "<%s%s>",
1315
+ block,
1316
+ alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]);
1317
+ ___mkd_reparse(T(p->text)+first, idx-first, 0, f);
1318
+ Qprintf(f, "</%s>\n", block);
1319
+ idx++;
1320
+ colno++;
1321
+ }
1322
+ if ( force )
1323
+ while (colno < S(align) ) {
1324
+ Qprintf(f, "<%s></%s>\n", block, block);
1325
+ ++colno;
1326
+ }
1327
+ Qstring("</tr>\n", f);
1328
+ return colno;
1329
+ }
1330
+
1331
+ static int
1332
+ printtable(Paragraph *pp, MMIOT *f)
1333
+ {
1334
+ /* header, dashes, then lines of content */
1335
+
1336
+ Line *hdr, *dash, *body;
1337
+ Istring align;
1338
+ int start;
1339
+ int hcols;
1340
+ char *p;
1341
+
1342
+ if ( !(pp->text && pp->text->next) )
1343
+ return 0;
1344
+
1345
+ hdr = pp->text;
1346
+ dash= hdr->next;
1347
+ body= dash->next;
1348
+
1349
+ /* first figure out cell alignments */
1350
+
1351
+ CREATE(align);
1352
+
1353
+ for (p=T(dash->text), start=0; start < S(dash->text); ) {
1354
+ char first, last;
1355
+ int end;
1356
+
1357
+ last=first=0;
1358
+ for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
1359
+ if ( !isspace(p[end]) ) {
1360
+ if ( !first) first = p[end];
1361
+ last = p[end];
1362
+ }
1363
+ }
1364
+ EXPAND(align) = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
1365
+ : (( last == ':') ? a_RIGHT : a_NONE );
1366
+ start = 1+end;
1367
+ }
1368
+
1369
+ Qstring("<table>\n", f);
1370
+ Qstring("<thead>\n", f);
1371
+ hcols = splat(hdr, "th", align, 0, f);
1372
+ Qstring("</thead>\n", f);
1373
+
1374
+ if ( hcols < S(align) )
1375
+ S(align) = hcols;
1376
+ else
1377
+ while ( hcols > S(align) )
1378
+ EXPAND(align) = a_NONE;
1379
+
1380
+ Qstring("<tbody>\n", f);
1381
+ for ( ; body; body = body->next)
1382
+ splat(body, "td", align, 1, f);
1383
+ Qstring("</tbody>\n", f);
1384
+ Qstring("</table>\n", f);
1385
+
1386
+ DELETE(align);
1387
+ return 1;
1388
+ }
1389
+
1390
+
1279
1391
  static int
1280
1392
  printblock(Paragraph *pp, MMIOT *f)
1281
1393
  {
@@ -1382,6 +1494,7 @@ definitionlist(Paragraph *p, MMIOT *f)
1382
1494
  }
1383
1495
 
1384
1496
  htmlify(p->down, "dd", p->ident, f);
1497
+ Qchar('\n', f);
1385
1498
  }
1386
1499
 
1387
1500
  Qstring("</dl>", f);
@@ -1453,6 +1566,14 @@ display(Paragraph *p, MMIOT *f)
1453
1566
  printheader(p, f);
1454
1567
  break;
1455
1568
 
1569
+ case TABLE:
1570
+ printtable(p, f);
1571
+ break;
1572
+
1573
+ case SOURCE:
1574
+ htmlify(p->down, 0, 0, f);
1575
+ break;
1576
+
1456
1577
  default:
1457
1578
  printblock(p, f);
1458
1579
  break;
data/ext/markdown.c CHANGED
@@ -32,11 +32,11 @@ static struct kw blocktags[] = { KW("!--"), KW("STYLE"), KW("SCRIPT"),
32
32
  KW("ADDRESS"), KW("BDO"), KW("BLOCKQUOTE"),
33
33
  KW("CENTER"), KW("DFN"), KW("DIV"), KW("H1"),
34
34
  KW("H2"), KW("H3"), KW("H4"), KW("H5"),
35
- KW("H6"), KW("IFRAME"), KW("LISTING"), KW("NOBR"),
35
+ KW("H6"), KW("LISTING"), KW("NOBR"),
36
36
  KW("UL"), KW("P"), KW("OL"), KW("DL"),
37
37
  KW("PLAINTEXT"), KW("PRE"), KW("TABLE"),
38
38
  KW("WBR"), KW("XMP"), SC("HR"), SC("BR"),
39
- KW("MAP") };
39
+ KW("IFRAME"), KW("MAP") };
40
40
  #define SZTAGS (sizeof blocktags / sizeof blocktags[0])
41
41
  #define MAXTAG 11 /* sizeof "BLOCKQUOTE" */
42
42
 
@@ -230,6 +230,8 @@ htmlblock(Paragraph *p, struct kw *tag)
230
230
  /* consume trailing gunk in close tag */
231
231
  c = flogetc(&f);
232
232
  }
233
+ if ( !f.t )
234
+ return 0;
233
235
  ret = f.t->next;
234
236
  f.t->next = 0;
235
237
  return ret;
@@ -259,6 +261,37 @@ comment(Paragraph *p)
259
261
  }
260
262
 
261
263
 
264
+ /* tables look like
265
+ * header|header{|header}
266
+ * ------|------{|......}
267
+ * {body lines}
268
+ */
269
+ static int
270
+ istable(Line *t)
271
+ {
272
+ char *p;
273
+ Line *dashes = t->next;
274
+ int contains = 0; /* found character bits; 0x01 is |, 0x02 is - */
275
+
276
+ /* two lines, first must contain | */
277
+ if ( !(dashes && memchr(T(t->text), '|', S(t->text))) )
278
+ return 0;
279
+
280
+ /* second line must contain - or | and nothing
281
+ * else except for whitespace or :
282
+ */
283
+ for ( p = T(dashes->text)+S(dashes->text)-1; p >= T(dashes->text); --p)
284
+ if ( *p == '|' )
285
+ contains |= 0x01;
286
+ else if ( *p == '-' )
287
+ contains |= 0x02;
288
+ else if ( ! ((*p == ':') || isspace(*p)) )
289
+ return 0;
290
+
291
+ return (contains & 0x03);
292
+ }
293
+
294
+
262
295
  /* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
263
296
  */
264
297
  static int
@@ -554,17 +587,17 @@ szmarkerclass(char *p)
554
587
  * marker %[kind:]name%
555
588
  */
556
589
  static int
557
- isdivmarker(Line *p)
590
+ isdivmarker(Line *p, int start)
558
591
  {
559
592
  #if DIV_QUOTE
560
593
  char *s = T(p->text);
561
594
  int len = S(p->text);
562
595
  int i;
563
596
 
564
- if ( !(len && s[0] == '%' && s[len-1] == '%') ) return 0;
597
+ if ( !(len && s[start] == '%' && s[len-1] == '%') ) return 0;
565
598
 
566
- i = szmarkerclass(s+1);
567
- --len;
599
+ i = szmarkerclass(s+start+1)+start;
600
+ len -= start+1;
568
601
 
569
602
  while ( ++i < len )
570
603
  if ( !isalnum(s[i]) )
@@ -601,13 +634,15 @@ quoteblock(Paragraph *p)
601
634
  t->dle = mkd_firstnonblank(t);
602
635
  }
603
636
 
604
- if ( !(q = skipempty(t->next)) || ((q != t->next) && !isquote(q)) ) {
637
+ q = skipempty(t->next);
638
+
639
+ if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1))) ) {
605
640
  ___mkd_freeLineRange(t, q);
606
641
  t = q;
607
642
  break;
608
643
  }
609
644
  }
610
- if ( isdivmarker(p->text) ) {
645
+ if ( isdivmarker(p->text,0) ) {
611
646
  char *prefix = "class";
612
647
  int i;
613
648
 
@@ -628,6 +663,25 @@ quoteblock(Paragraph *p)
628
663
  }
629
664
 
630
665
 
666
+ /*
667
+ * A table block starts with a table header (see istable()), and continues
668
+ * until EOF or a line that /doesn't/ contain a |.
669
+ */
670
+ static Line *
671
+ tableblock(Paragraph *p)
672
+ {
673
+ Line *t, *q;
674
+
675
+ for ( t = p->text; t && (q = t->next); t = t->next ) {
676
+ if ( !memchr(T(q->text), '|', S(q->text)) ) {
677
+ t->next = 0;
678
+ return q;
679
+ }
680
+ }
681
+ return 0;
682
+ }
683
+
684
+
631
685
  static Paragraph *Pp(ParagraphRoot *, Line *, int);
632
686
  static Paragraph *compile(Line *, int, MMIOT *);
633
687
 
@@ -659,7 +713,7 @@ listitem(Paragraph *p, int indent)
659
713
  * need any indentation
660
714
  */
661
715
  if ( q != t->next ) {
662
- if (q->dle < 4) {
716
+ if (q->dle < indent) {
663
717
  q = t->next;
664
718
  t->next = 0;
665
719
  return q;
@@ -684,9 +738,10 @@ listblock(Paragraph *top, int trim, MMIOT *f)
684
738
  {
685
739
  ParagraphRoot d = { 0, 0 };
686
740
  Paragraph *p;
687
- Line *q = top->text, *text;
688
- Line *label;
689
- int para = 0;
741
+ Line *q = top->text, *text, *label;
742
+ int isdl = (top->typ == DL),
743
+ para = 0,
744
+ ltype;
690
745
 
691
746
  while (( text = q )) {
692
747
  if ( top->typ == DL ) {
@@ -710,7 +765,8 @@ listblock(Paragraph *top, int trim, MMIOT *f)
710
765
 
711
766
  if ( para && (top->typ != DL) && p->down ) p->down->align = PARA;
712
767
 
713
- if ( !(q = skipempty(text)) || (islist(q, &trim) == 0) )
768
+ if ( !(q = skipempty(text)) || ((ltype = islist(q, &trim)) == 0)
769
+ || (isdl != (ltype == DL)) )
714
770
  break;
715
771
 
716
772
  if ( para = (q != text) ) {
@@ -840,6 +896,64 @@ consume(Line *ptr, int *eaten)
840
896
  }
841
897
 
842
898
 
899
+ /*
900
+ * top-level compilation; break the document into
901
+ * style, html, and source blocks with footnote links
902
+ * weeded out.
903
+ */
904
+ static Paragraph *
905
+ compile_document(Line *ptr, MMIOT *f)
906
+ {
907
+ ParagraphRoot d = { 0, 0 };
908
+ ANCHOR(Line) source = { 0, 0 };
909
+ Paragraph *p = 0;
910
+ struct kw *tag;
911
+ int eaten;
912
+
913
+ while ( ptr ) {
914
+ if ( !(f->flags & DENY_HTML) && (tag = isopentag(ptr)) ) {
915
+ /* If we encounter a html/style block, compile and save all
916
+ * of the cached source BEFORE processing the html/style.
917
+ */
918
+ if ( T(source) ) {
919
+ E(source)->next = 0;
920
+ p = Pp(&d, 0, SOURCE);
921
+ p->down = compile(T(source), 1, f);
922
+ T(source) = E(source) = 0;
923
+ }
924
+ p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML);
925
+ if ( strcmp(tag->id, "!--") == 0 )
926
+ ptr = comment(p);
927
+ else
928
+ ptr = htmlblock(p, tag);
929
+ }
930
+ else if ( isfootnote(ptr) ) {
931
+ /* footnotes, like cats, sleep anywhere; pull them
932
+ * out of the input stream and file them away for
933
+ * later processing
934
+ */
935
+ ptr = consume(addfootnote(ptr, f), &eaten);
936
+ }
937
+ else {
938
+ /* source; cache it up to wait for eof or the
939
+ * next html/style block
940
+ */
941
+ ATTACH(source,ptr);
942
+ ptr = ptr->next;
943
+ }
944
+ }
945
+ if ( T(source) ) {
946
+ /* if there's any cached source at EOF, compile
947
+ * it now.
948
+ */
949
+ E(source)->next = 0;
950
+ p = Pp(&d, 0, SOURCE);
951
+ p->down = compile(T(source), 1, f);
952
+ }
953
+ return T(d);
954
+ }
955
+
956
+
843
957
  /*
844
958
  * break a collection of markdown input into
845
959
  * blocks of lists, code, html, and text to
@@ -850,22 +964,15 @@ compile(Line *ptr, int toplevel, MMIOT *f)
850
964
  {
851
965
  ParagraphRoot d = { 0, 0 };
852
966
  Paragraph *p = 0;
853
- struct kw *tag;
854
967
  Line *r;
855
968
  int para = toplevel;
969
+ int blocks = 0;
856
970
  int hdr_type, list_type, indent;
857
971
 
858
972
  ptr = consume(ptr, &para);
859
973
 
860
974
  while ( ptr ) {
861
- if ( toplevel && !(f->flags & DENY_HTML) && (tag = isopentag(ptr)) ) {
862
- p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML);
863
- if ( strcmp(tag->id, "!--") == 0 )
864
- ptr = comment(p);
865
- else
866
- ptr = htmlblock(p, tag);
867
- }
868
- else if ( iscode(ptr) ) {
975
+ if ( iscode(ptr) ) {
869
976
  p = Pp(&d, ptr, CODE);
870
977
 
871
978
  if ( f->flags & MKD_1_COMPAT) {
@@ -897,9 +1004,9 @@ compile(Line *ptr, int toplevel, MMIOT *f)
897
1004
  p = Pp(&d, ptr, HDR);
898
1005
  ptr = headerblock(p, hdr_type);
899
1006
  }
900
- else if ( toplevel && (isfootnote(ptr)) ) {
901
- ptr = consume(addfootnote(ptr, f), &para);
902
- continue;
1007
+ else if ( istable(ptr) && !(f->flags & (STRICT|NOTABLES)) ) {
1008
+ p = Pp(&d, ptr, TABLE);
1009
+ ptr = tableblock(p);
903
1010
  }
904
1011
  else {
905
1012
  p = Pp(&d, ptr, MARKUP);
@@ -909,7 +1016,8 @@ compile(Line *ptr, int toplevel, MMIOT *f)
909
1016
  if ( (para||toplevel) && !p->align )
910
1017
  p->align = PARA;
911
1018
 
912
- para = toplevel;
1019
+ blocks++;
1020
+ para = toplevel || (blocks > 1);
913
1021
  ptr = consume(ptr, &para);
914
1022
 
915
1023
  if ( para && !p->align )
@@ -960,7 +1068,7 @@ mkd_compile(Document *doc, int flags)
960
1068
 
961
1069
  initialize();
962
1070
 
963
- doc->code = compile(T(doc->content), 1, doc->ctx);
1071
+ doc->code = compile_document(T(doc->content), doc->ctx);
964
1072
  qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
965
1073
  sizeof T(*doc->ctx->footnotes)[0],
966
1074
  (stfu)__mkd_footsort);