bluecloth 2.0.5 → 2.0.6.pre120

Sign up to get free protection for your applications and to get access to all the features.
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);