rdiscount 1.2.11 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
- DLEXT = Config::CONFIG['DLEXT']
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
- Rake::GemPackageTask.new(spec) do |p|
30
- p.gem_spec = spec
31
- p.need_tar_gz = true
32
- p.need_tar = false
33
- p.need_zip = false
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 => [ "#{PKGNAME}.gem", "#{PKGNAME}.tar.gz" ] do |t|
154
+ task :release => [package('.gem'), package('.tar.gz')] do |t|
126
155
  sh <<-end
127
- rubyforge add_release wink rdiscount #{VERS} #{PKGNAME}.gem &&
128
- rubyforge add_file wink rdiscount #{VERS} #{PKGNAME}.tar.gz
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib")
4
+ require 'rdiscount'
5
+ STDOUT.write(RDiscount.new(ARGF.read).to_html)
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("&amp;", f);
314
340
  else if ( c == '<' )
315
341
  Qstring("&lt;", f);
316
- else if ( isalnum(c) || c == '.' || c == '-' || 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 ( !(linkykey(image, &link, f) && S(link.tag)) ) {
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) && !isalnum(peek(f,2)) )
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
- && !isalnum(peek(f,4)) )
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
- case '^': if ( isthisspace(f,-1) || isthisspace(f,1) )
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
- /* If RELAXED_EMPHASIS, underscores don't count when
952
- * they're in the middle of a word.
953
- */
954
- if ( (isthisspace(f,-1) && isthisspace(f,1))
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 ( isalnum(peek(f,j)) )
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>", pp->hnumber);
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 ) Qprintf(f, "<%s>", 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 ) Qprintf(f, "</%s>", 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>\n", (typ==UL)?'u':'o');
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, j;
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
- i = nextnonblank(t, i);
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
- return q;
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) || ishdr(q,&z) || islist(q,&z)) ) {
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,&trim) != top->typ) )
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 USER_FLAGS 0x00FF
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
@@ -49,6 +49,8 @@ ___mkd_freeParagraph(Paragraph *p)
49
49
  ___mkd_freeParagraph(p->down);
50
50
  if (p->text)
51
51
  ___mkd_freeLines(p->text);
52
+ if (p->ident)
53
+ free(p->ident);
52
54
  free(p);
53
55
  }
54
56
 
data/lib/rdiscount.rb CHANGED
@@ -24,6 +24,7 @@
24
24
  # end
25
25
  #
26
26
  class RDiscount
27
+ VERSION = '1.3.1'
27
28
 
28
29
  # Original Markdown formatted text.
29
30
  attr_reader :text
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
@@ -74,6 +74,11 @@ class MarkdownTest < Test::Unit::TestCase
74
74
  assert_equal "<p>Well that&rsquo;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&amp;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|
@@ -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>&ldquo;Quoted text&rdquo;</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>&ldquo;Quoted text&rdquo;</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>&ldquo;Quoted text&rdquo;</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.2.11
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: 2008-11-02 01:00:00 -07:00
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
- - test/MarkdownTest_1.0
29
- - test/MarkdownTest_1.0.3
30
- - test/benchmark.rb
31
- - test/benchmark.txt
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
- - ext/amalloc.h
46
- - ext/config.h
47
- - ext/cstring.h
48
- - ext/markdown.h
49
- - ext/mkdio.h
50
- - ext/rbstrio.h
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.2.0
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