rdiscount 1.2.11 → 1.3.1

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/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