rdiscount 1.5.8.1 → 1.6.3

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
@@ -100,7 +100,7 @@ task :gather => 'discount' do |t|
100
100
  files =
101
101
  FileList[
102
102
  'discount/{markdown,mkdio,amalloc,cstring}.h',
103
- 'discount/{markdown,docheader,dumptree,generate,mkdio,resource,toc,Csio,xml,css}.c'
103
+ 'discount/{markdown,docheader,dumptree,generate,mkdio,resource,toc,Csio,xml,css,basename,emmatch}.c'
104
104
  ]
105
105
  cp files, 'ext/',
106
106
  :preserve => true,
data/ext/basename.c ADDED
@@ -0,0 +1,43 @@
1
+ /*
2
+ * mkdio -- markdown front end input functions
3
+ *
4
+ * Copyright (C) 2007 David L Parsons.
5
+ * The redistribution terms are provided in the COPYRIGHT file that must
6
+ * be distributed with this source code.
7
+ */
8
+ #include "config.h"
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <ctype.h>
12
+
13
+ #include "mkdio.h"
14
+ #include "cstring.h"
15
+ #include "amalloc.h"
16
+
17
+ static char *
18
+ e_basename(const char *string, const int size, void *context)
19
+ {
20
+ char *ret;
21
+ char *base = (char*)context;
22
+
23
+ if ( base && string && (*string == '/') && (ret=malloc(strlen(base)+size+2)) ) {
24
+ strcpy(ret, base);
25
+ strncat(ret, string, size);
26
+ return ret;
27
+ }
28
+ return 0;
29
+ }
30
+
31
+ static void
32
+ e_free(char *string, void *context)
33
+ {
34
+ if ( string ) free(string);
35
+ }
36
+
37
+ void
38
+ mkd_basename(MMIOT *document, char *base)
39
+ {
40
+ mkd_e_url(document, e_basename);
41
+ mkd_e_data(document, base);
42
+ mkd_e_free(document, e_free);
43
+ }
data/ext/emmatch.c ADDED
@@ -0,0 +1,188 @@
1
+ /* markdown: a C implementation of John Gruber's Markdown markup language.
2
+ *
3
+ * Copyright (C) 2010 David L Parsons.
4
+ * The redistribution terms are provided in the COPYRIGHT file that must
5
+ * be distributed with this source code.
6
+ */
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+ #include <stdarg.h>
10
+ #include <stdlib.h>
11
+ #include <time.h>
12
+ #include <ctype.h>
13
+
14
+ #include "config.h"
15
+
16
+ #include "cstring.h"
17
+ #include "markdown.h"
18
+ #include "amalloc.h"
19
+
20
+
21
+ /* emmatch: the emphasis mangler that's run after a block
22
+ * of html has been generated.
23
+ *
24
+ * It should create MarkdownTest_1.0 (and _1.0.3)
25
+ * compatable emphasis for non-pathological cases
26
+ * and it should fail in a standards-compliant way
27
+ * when someone attempts to feed it junk.
28
+ *
29
+ * Emmatching is done after the input has been
30
+ * processed into a STRING (f->Q) of text and
31
+ * emphasis blocks. After ___mkd_emblock() finishes,
32
+ * it truncates f->Q and leaves the rendered paragraph
33
+ * if f->out.
34
+ */
35
+
36
+
37
+ /* empair() -- find the NEAREST matching emphasis token (or
38
+ * subtoken of a 3+ long emphasis token.
39
+ */
40
+ static int
41
+ empair(MMIOT *f, int first, int last, int match)
42
+ {
43
+
44
+ int i;
45
+ block *begin, *p;
46
+
47
+ begin = &T(f->Q)[first];
48
+
49
+ for (i=first+1; i <= last; i++) {
50
+ p = &T(f->Q)[i];
51
+
52
+ if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
53
+ continue; /* break? */
54
+
55
+ if ( p->b_type == begin->b_type ) {
56
+ if ( p->b_count == match ) /* exact match */
57
+ return i;
58
+
59
+ if ( p->b_count > 2 ) /* fuzzy match */
60
+ return i;
61
+ }
62
+ }
63
+ return 0;
64
+ } /* empair */
65
+
66
+
67
+ /* emfill() -- if an emphasis token has leftover stars or underscores,
68
+ * convert them back into character and append them to b_text.
69
+ */
70
+ static void
71
+ emfill(block *p)
72
+ {
73
+ int j;
74
+
75
+ if ( p->b_type == bTEXT )
76
+ return;
77
+
78
+ for (j=0; j < p->b_count; j++)
79
+ EXPAND(p->b_text) = p->b_char;
80
+ p->b_count = 0;
81
+ } /* emfill */
82
+
83
+
84
+ static void
85
+ emclose(MMIOT *f, int first, int last)
86
+ {
87
+ int j;
88
+
89
+ for (j=first+1; j<last-1; j++)
90
+ emfill(&T(f->Q)[j]);
91
+ }
92
+
93
+
94
+ static struct emtags {
95
+ char open[10];
96
+ char close[10];
97
+ int size;
98
+ } emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
99
+
100
+
101
+ static void emblock(MMIOT*,int,int);
102
+
103
+
104
+ /* emmatch() -- match emphasis for a single emphasis token.
105
+ */
106
+ static void
107
+ emmatch(MMIOT *f, int first, int last)
108
+ {
109
+ block *start = &T(f->Q)[first];
110
+ int e, e2, match;
111
+
112
+ switch (start->b_count) {
113
+ case 2: if ( e = empair(f,first,last,match=2) )
114
+ break;
115
+ case 1: e = empair(f,first,last,match=1);
116
+ break;
117
+ case 0: return;
118
+ default:
119
+ e = empair(f,first,last,1);
120
+ e2= empair(f,first,last,2);
121
+
122
+ if ( e2 >= e ) {
123
+ e = e2;
124
+ match = 2;
125
+ }
126
+ else
127
+ match = 1;
128
+ break;
129
+ }
130
+
131
+ if ( e ) {
132
+ /* if we found emphasis to match, match it, recursively call
133
+ * emblock to match emphasis inside the new html block, add
134
+ * the emphasis markers for the block, then (tail) recursively
135
+ * call ourself to match any remaining emphasis on this token.
136
+ */
137
+ block *end = &T(f->Q)[e];
138
+
139
+ end->b_count -= match;
140
+ start->b_count -= match;
141
+
142
+ emblock(f, first, e);
143
+
144
+ PREFIX(start->b_text, emtags[match-1].open, emtags[match-1].size-1);
145
+ SUFFIX(end->b_post, emtags[match-1].close, emtags[match-1].size);
146
+
147
+ emmatch(f, first, last);
148
+ }
149
+ } /* emmatch */
150
+
151
+
152
+ /* emblock() -- walk a blocklist, attempting to match emphasis
153
+ */
154
+ static void
155
+ emblock(MMIOT *f, int first, int last)
156
+ {
157
+ int i;
158
+
159
+ for ( i = first; i <= last; i++ )
160
+ if ( T(f->Q)[i].b_type != bTEXT )
161
+ emmatch(f, i, last);
162
+ emclose(f, first, last);
163
+ } /* emblock */
164
+
165
+
166
+ /* ___mkd_emblock() -- emblock a string of blocks, then concatinate the
167
+ * resulting text onto f->out.
168
+ */
169
+ void
170
+ ___mkd_emblock(MMIOT *f)
171
+ {
172
+ int i;
173
+ block *p;
174
+
175
+ emblock(f, 0, S(f->Q)-1);
176
+
177
+ for (i=0; i < S(f->Q); i++) {
178
+ p = &T(f->Q)[i];
179
+ emfill(p);
180
+
181
+ if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
182
+ DELETE(p->b_post); }
183
+ if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
184
+ DELETE(p->b_text); }
185
+ }
186
+
187
+ S(f->Q) = 0;
188
+ } /* ___mkd_emblock */
data/ext/generate.c CHANGED
@@ -21,8 +21,6 @@ typedef int (*stfu)(const void*,const void*);
21
21
 
22
22
 
23
23
  /* forward declarations */
24
- static int iscodeblock(MMIOT*);
25
- static void code(int, MMIOT*);
26
24
  static void text(MMIOT *f);
27
25
  static Paragraph *display(Paragraph*, MMIOT*);
28
26
 
@@ -166,6 +164,16 @@ Qprintf(MMIOT *f, char *fmt, ...)
166
164
  }
167
165
 
168
166
 
167
+ /* Qcopy()
168
+ */
169
+ static void
170
+ Qcopy(int count, MMIOT *f)
171
+ {
172
+ while ( count-- > 0 )
173
+ Qchar(pull(f), f);
174
+ }
175
+
176
+
169
177
  /* Qem()
170
178
  */
171
179
  static void
@@ -182,118 +190,6 @@ Qem(MMIOT *f, char c, int count)
182
190
  }
183
191
 
184
192
 
185
- /* empair()
186
- */
187
- static int
188
- empair(MMIOT *f, int go, int level)
189
- {
190
-
191
- int i;
192
- block *begin, *p;
193
-
194
- begin = &T(f->Q)[go];
195
- for (i=go+1; i < S(f->Q); i++) {
196
- p = &T(f->Q)[i];
197
-
198
- if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
199
- break;
200
-
201
- if ( p->b_type == begin->b_type ) {
202
- if ( p->b_count == level ) /* exact match */
203
- return i-go;
204
-
205
- if ( p->b_count > 2 ) /* fuzzy match */
206
- return i-go;
207
- }
208
- }
209
- return EOF;
210
- }
211
-
212
-
213
-
214
- static struct emtags {
215
- char open[10];
216
- char close[10];
217
- int size;
218
- } emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
219
-
220
-
221
- static void
222
- emclose(Cstring *s, int level)
223
- {
224
- PREFIX(*s, emtags[level-1].close, emtags[level-1].size);
225
- }
226
-
227
-
228
- static void
229
- emopen(Cstring *s, int level)
230
- {
231
- SUFFIX(*s, emtags[level-1].open, emtags[level-1].size-1);
232
- }
233
-
234
-
235
- /* emmatch()
236
- */
237
- static void
238
- emmatch(MMIOT *f, int go)
239
- {
240
- block *start = &T(f->Q)[go], *end;
241
- int e, e2, i, match;
242
-
243
- while ( start->b_count ) {
244
- switch (start->b_count) {
245
- case 2: e = empair(f,go,match=2);
246
- if ( e != EOF ) break;
247
- case 1: e = empair(f,go,match=1); break;
248
- default:
249
- e = empair(f,go,1);
250
- e2= empair(f,go,2);
251
-
252
- if ( e == EOF || ((e2 != EOF) && (e2 >= e)) ) {
253
- e = e2;
254
- match = 2;
255
- }
256
- else
257
- match = 1;
258
- }
259
- if ( e != EOF ) {
260
- end = &T(f->Q)[go+e];
261
- emclose(&end->b_post, match);
262
- emopen(&start->b_text, match);
263
- end->b_count -= match;
264
- }
265
- else {
266
- for (i=0; i < match; i++)
267
- EXPAND(start->b_text) = start->b_char;
268
- }
269
-
270
- start->b_count -= match;
271
- }
272
- }
273
-
274
-
275
- /* ___mkd_emblock()
276
- */
277
- void
278
- ___mkd_emblock(MMIOT *f)
279
- {
280
- int i;
281
- block *p;
282
-
283
- for (i=0; i < S(f->Q); i++) {
284
- p = &T(f->Q)[i];
285
-
286
- if ( p->b_type != bTEXT ) emmatch(f, i);
287
-
288
- if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
289
- DELETE(p->b_post); }
290
- if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
291
- DELETE(p->b_text); }
292
- }
293
- S(f->Q) = 0;
294
- }
295
-
296
-
297
193
  /* generate html from a markup fragment
298
194
  */
299
195
  void
@@ -304,7 +200,7 @@ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f)
304
200
  ___mkd_initmmiot(&sub, f->footnotes);
305
201
 
306
202
  sub.flags = f->flags | flags;
307
- sub.base = f->base;
203
+ sub.cb = f->cb;
308
204
 
309
205
  push(bfr, size, &sub);
310
206
  EXPAND(sub.in) = 0;
@@ -345,6 +241,8 @@ puturl(char *s, int size, MMIOT *f, int display)
345
241
  Qstring("%22", f);
346
242
  else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) )
347
243
  Qchar(c, f);
244
+ else if ( c == 003 ) /* untokenize ^C */
245
+ Qstring(" ", f);
348
246
  else
349
247
  Qprintf(f, "%%%02X", c);
350
248
  }
@@ -512,27 +410,28 @@ linkyurl(MMIOT *f, int image, Footnote *p)
512
410
 
513
411
  /* prefixes for <automatic links>
514
412
  */
515
- static struct {
413
+ static struct _protocol {
516
414
  char *name;
517
415
  int nlen;
518
416
  } protocol[] = {
519
417
  #define _aprotocol(x) { x, (sizeof x)-1 }
520
- _aprotocol( "http://" ),
521
418
  _aprotocol( "https://" ),
522
- _aprotocol( "ftp://" ),
419
+ _aprotocol( "http://" ),
523
420
  _aprotocol( "news://" ),
421
+ _aprotocol( "ftp://" ),
524
422
  #undef _aprotocol
525
423
  };
526
424
  #define NRPROTOCOLS (sizeof protocol / sizeof protocol[0])
527
425
 
528
426
 
529
427
  static int
530
- isautoprefix(char *text)
428
+ isautoprefix(char *text, int size)
531
429
  {
532
430
  int i;
431
+ struct _protocol *p;
533
432
 
534
- for (i=0; i < NRPROTOCOLS; i++)
535
- if ( strncasecmp(text, protocol[i].name, protocol[i].nlen) == 0 )
433
+ for (i=0, p=protocol; i < NRPROTOCOLS; i++, p++)
434
+ if ( (size >= p->nlen) && strncasecmp(text, p->name, p->nlen) == 0 )
536
435
  return 1;
537
436
  return 0;
538
437
  }
@@ -584,8 +483,7 @@ pseudo(Cstring t)
584
483
  int i;
585
484
  linkytype *r;
586
485
 
587
- for ( i=0; i < NR(specials); i++ ) {
588
- r = &specials[i];
486
+ for ( i=0, r=specials; i < NR(specials); i++,r++ ) {
589
487
  if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
590
488
  return r;
591
489
  }
@@ -593,6 +491,36 @@ pseudo(Cstring t)
593
491
  }
594
492
 
595
493
 
494
+ /* print out the start of an `img' or `a' tag, applying callbacks as needed.
495
+ */
496
+ static void
497
+ printlinkyref(MMIOT *f, linkytype *tag, char *link, int size)
498
+ {
499
+ char *edit;
500
+
501
+ Qstring(tag->link_pfx, f);
502
+
503
+ if ( tag->kind & IS_URL ) {
504
+ if ( f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) {
505
+ puturl(edit, strlen(edit), f, 0);
506
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
507
+ }
508
+ else
509
+ puturl(link + tag->szpat, size - tag->szpat, f, 0);
510
+ }
511
+ else
512
+ ___mkd_reparse(link + tag->szpat, size - tag->szpat, INSIDE_TAG, f);
513
+
514
+ Qstring(tag->link_sfx, f);
515
+
516
+ if ( f->cb->e_flags && (edit = (*f->cb->e_flags)(link, size, f->cb->e_data)) ) {
517
+ Qchar(' ', f);
518
+ Qstring(edit, f);
519
+ if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data);
520
+ }
521
+ } /* printlinkyref */
522
+
523
+
596
524
  /* print out a linky (or fail if it's Not Allowed)
597
525
  */
598
526
  static int
@@ -608,11 +536,11 @@ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
608
536
  }
609
537
  else if ( (f->flags & SAFELINK) && T(ref->link)
610
538
  && (T(ref->link)[0] != '/')
611
- && !isautoprefix(T(ref->link)) )
539
+ && !isautoprefix(T(ref->link), S(ref->link)) )
612
540
  /* if SAFELINK, only accept links that are local or
613
541
  * a well-known protocol
614
542
  */
615
- return 0;
543
+ return 0;
616
544
  else
617
545
  tag = &linkt;
618
546
 
@@ -620,21 +548,11 @@ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
620
548
  return 0;
621
549
 
622
550
  if ( tag->link_pfx ) {
623
- Qstring(tag->link_pfx, f);
624
-
625
- if ( tag->kind & IS_URL ) {
626
- if ( f->base && T(ref->link) && (T(ref->link)[tag->szpat] == '/') )
627
- puturl(f->base, strlen(f->base), f, 0);
628
- puturl(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f, 0);
629
- }
630
- else
631
- ___mkd_reparse(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, INSIDE_TAG, f);
632
-
633
- Qstring(tag->link_sfx, f);
551
+ printlinkyref(f, tag, T(ref->link), S(ref->link));
634
552
 
635
- if ( tag->WxH) {
636
- if ( ref->height) Qprintf(f," height=\"%d\"", ref->height);
637
- if ( ref->width) Qprintf(f, " width=\"%d\"", ref->width);
553
+ if ( tag->WxH ) {
554
+ if ( ref->height ) Qprintf(f," height=\"%d\"", ref->height);
555
+ if ( ref->width ) Qprintf(f, " width=\"%d\"", ref->width);
638
556
  }
639
557
 
640
558
  if ( S(ref->title) ) {
@@ -742,6 +660,89 @@ mangle(char *s, int len, MMIOT *f)
742
660
  }
743
661
 
744
662
 
663
+ /* nrticks() -- count up a row of tick marks
664
+ */
665
+ static int
666
+ nrticks(int offset, MMIOT *f)
667
+ {
668
+ int tick = 0;
669
+
670
+ while ( peek(f, offset+tick) == '`' ) tick++;
671
+
672
+ return tick;
673
+ } /* nrticks */
674
+
675
+
676
+ /* matchticks() -- match a certain # of ticks, and if that fails
677
+ * match the largest subset of those ticks.
678
+ *
679
+ * if a subset was matched, modify the passed in
680
+ * # of ticks so that the caller (text()) can
681
+ * appropriately process the horrible thing.
682
+ */
683
+ static int
684
+ matchticks(MMIOT *f, int *ticks)
685
+ {
686
+ int size, tick, c;
687
+ int subsize=0, subtick=0;
688
+
689
+ for (size = *ticks; (c=peek(f,size)) != EOF; ) {
690
+ if ( c == '`' )
691
+ if ( (tick=nrticks(size,f)) == *ticks )
692
+ return size;
693
+ else {
694
+ if ( tick > subtick ) {
695
+ subsize = size;
696
+ subtick = tick;
697
+ }
698
+ size += tick;
699
+ }
700
+ else
701
+ size++;
702
+ }
703
+ if ( subsize ) {
704
+ *ticks = subtick;
705
+ return subsize;
706
+ }
707
+ return 0;
708
+
709
+ } /* matchticks */
710
+
711
+
712
+ /* code() -- write a string out as code. The only characters that have
713
+ * special meaning in a code block are * `<' and `&' , which
714
+ * are /always/ expanded to &lt; and &amp;
715
+ */
716
+ static void
717
+ code(MMIOT *f, char *s, int length)
718
+ {
719
+ int i,c;
720
+
721
+ for ( i=0; i < length; i++ )
722
+ if ( (c = s[i]) == 003) /* ^C: expand back to 2 spaces */
723
+ Qstring(" ", f);
724
+ else
725
+ cputc(c, f);
726
+ } /* code */
727
+
728
+
729
+ /* codespan() -- write out a chunk of text as code, trimming one
730
+ * space off the front and/or back as appropriate.
731
+ */
732
+ static void
733
+ codespan(MMIOT *f, int size)
734
+ {
735
+ int i=0, c;
736
+
737
+ if ( size > 1 && peek(f, size-1) == ' ' ) --size;
738
+ if ( peek(f,i) == ' ' ) ++i, --size;
739
+
740
+ Qstring("<code>", f);
741
+ code(f, cursor(f)+(i-1), size);
742
+ Qstring("</code>", f);
743
+ } /* codespan */
744
+
745
+
745
746
  /* before letting a tag through, validate against
746
747
  * DENY_A and DENY_IMG
747
748
  */
@@ -826,10 +827,9 @@ process_possible_link(MMIOT *f, int size)
826
827
  Qstring("</a>", f);
827
828
  return 1;
828
829
  }
829
- else if ( isautoprefix(text) ) {
830
- Qstring("<a href=\"", f);
831
- puturl(text,size,f, 0);
832
- Qstring("\">", f);
830
+ else if ( isautoprefix(text, size) ) {
831
+ printlinkyref(f, &linkt, text, size);
832
+ Qchar('>', f);
833
833
  puturl(text,size,f, 1);
834
834
  Qstring("</a>", f);
835
835
  return 1;
@@ -879,7 +879,10 @@ maybe_tag_or_link(MMIOT *f)
879
879
  else
880
880
  size++;
881
881
 
882
- Qstring(forbidden_tag(f) ? "&lt;" : "<", f);
882
+ if ( forbidden_tag(f) )
883
+ return 0;
884
+
885
+ Qchar('<', f);
883
886
  while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
884
887
  Qchar(pull(f), f);
885
888
  return 1;
@@ -1065,7 +1068,7 @@ text(MMIOT *f)
1065
1068
  int smartyflags = 0;
1066
1069
 
1067
1070
  while (1) {
1068
- if ( (f->flags & AUTOLINK) && isalpha(peek(f,1)) )
1071
+ if ( (f->flags & AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) )
1069
1072
  maybe_autolink(f);
1070
1073
 
1071
1074
  c = pull(f);
@@ -1078,7 +1081,7 @@ text(MMIOT *f)
1078
1081
  switch (c) {
1079
1082
  case 0: break;
1080
1083
 
1081
- case 3: Qstring("<br/>", f);
1084
+ case 3: Qstring(tag_text(f) ? " " : "<br/>", f);
1082
1085
  break;
1083
1086
 
1084
1087
  case '>': if ( tag_text(f) )
@@ -1131,16 +1134,13 @@ text(MMIOT *f)
1131
1134
  }
1132
1135
  #endif
1133
1136
  case '*':
1134
- #if RELAXED_EMPHASIS
1135
1137
  /* Underscores & stars don't count if they're out in the middle
1136
1138
  * of whitespace */
1137
- if ( !(f->flags & STRICT) && isthisspace(f,-1)
1138
- && isthisspace(f,1) ) {
1139
+ if ( isthisspace(f,-1) && isthisspace(f,1) ) {
1139
1140
  Qchar(c, f);
1140
1141
  break;
1141
1142
  }
1142
1143
  /* else fall into the regular old emphasis case */
1143
- #endif
1144
1144
  if ( tag_text(f) )
1145
1145
  Qchar(c, f);
1146
1146
  else {
@@ -1150,17 +1150,20 @@ text(MMIOT *f)
1150
1150
  }
1151
1151
  break;
1152
1152
 
1153
- case '`': if ( tag_text(f) || !iscodeblock(f) )
1153
+ case '`': if ( tag_text(f) )
1154
1154
  Qchar(c, f);
1155
1155
  else {
1156
- Qstring("<code>", f);
1157
- if ( peek(f, 1) == '`' ) {
1158
- pull(f);
1159
- code(2, f);
1156
+ int size, tick = nrticks(0, f);
1157
+
1158
+ if ( size = matchticks(f, &tick) ) {
1159
+ shift(f, tick);
1160
+ codespan(f, size-tick);
1161
+ shift(f, size-1);
1162
+ }
1163
+ else {
1164
+ Qchar(c, f);
1165
+ Qcopy(tick-1, f);
1160
1166
  }
1161
- else
1162
- code(1, f);
1163
- Qstring("</code>", f);
1164
1167
  }
1165
1168
  break;
1166
1169
 
@@ -1169,11 +1172,10 @@ text(MMIOT *f)
1169
1172
  break;
1170
1173
  case '<': Qstring("&lt;", f);
1171
1174
  break;
1172
- case '\\':
1173
1175
  case '>': case '#': case '.': case '-':
1174
1176
  case '+': case '{': case '}': case ']':
1175
- case '(': case ')': case '"': case '\'':
1176
1177
  case '!': case '[': case '*': case '_':
1178
+ case '\\':case '(': case ')':
1177
1179
  case '`': Qchar(c, f);
1178
1180
  break;
1179
1181
  default:
@@ -1207,74 +1209,6 @@ text(MMIOT *f)
1207
1209
  } /* text */
1208
1210
 
1209
1211
 
1210
- static int
1211
- iscodeblock(MMIOT *f)
1212
- {
1213
- int i=1, single = 1, c;
1214
-
1215
- if ( peek(f,i) == '`' ) {
1216
- single=0;
1217
- i++;
1218
- }
1219
- while ( (c=peek(f,i)) != EOF ) {
1220
- if ( (c == '`') && (single || peek(f,i+1) == '`') )
1221
- return 1;
1222
- else if ( c == '\\' )
1223
- i++;
1224
- i++;
1225
- }
1226
- return 0;
1227
-
1228
- }
1229
-
1230
- static int
1231
- endofcode(int escape, int offset, MMIOT *f)
1232
- {
1233
- switch (escape) {
1234
- case 2: if ( peek(f, offset+1) == '`' ) {
1235
- shift(f,1);
1236
- case 1: shift(f,offset);
1237
- return 1;
1238
- }
1239
- default:return 0;
1240
- }
1241
- }
1242
-
1243
-
1244
- /* the only characters that have special meaning in a code block are
1245
- * `<' and `&' , which are /always/ expanded to &lt; and &amp;
1246
- */
1247
- static void
1248
- code(int escape, MMIOT *f)
1249
- {
1250
- int c;
1251
-
1252
- if ( escape && (peek(f,1) == ' ') )
1253
- shift(f,1);
1254
-
1255
- while ( (c = pull(f)) != EOF ) {
1256
- switch (c) {
1257
- case ' ': if ( peek(f,1) == '`' && endofcode(escape, 1, f) )
1258
- return;
1259
- Qchar(c, f);
1260
- break;
1261
-
1262
- case '`': if ( endofcode(escape, 0, f) )
1263
- return;
1264
- Qchar(c, f);
1265
- break;
1266
-
1267
- case '\\': cputc(c, f);
1268
- if ( peek(f,1) == '>' || (c = pull(f)) == EOF )
1269
- break;
1270
-
1271
- default: cputc(c, f);
1272
- break;
1273
- }
1274
- }
1275
- } /* code */
1276
-
1277
-
1278
1212
  /* print a header block
1279
1213
  */
1280
1214
  static void
@@ -1333,6 +1267,7 @@ splat(Line *p, char *block, Istring align, int force, MMIOT *f)
1333
1267
  return colno;
1334
1268
  }
1335
1269
 
1270
+
1336
1271
  static int
1337
1272
  printtable(Paragraph *pp, MMIOT *f)
1338
1273
  {
@@ -1402,8 +1337,9 @@ printblock(Paragraph *pp, MMIOT *f)
1402
1337
 
1403
1338
  while (t) {
1404
1339
  if ( S(t->text) ) {
1405
- if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
1406
- && T(t->text)[S(t->text)-1] == ' ') {
1340
+ if ( t->next && S(t->text) > 2
1341
+ && T(t->text)[S(t->text)-2] == ' '
1342
+ && T(t->text)[S(t->text)-1] == ' ' ) {
1407
1343
  push(T(t->text), S(t->text)-2, f);
1408
1344
  push("\003\n", 2, f);
1409
1345
  }
@@ -1428,19 +1364,18 @@ printcode(Line *t, MMIOT *f)
1428
1364
  {
1429
1365
  int blanks;
1430
1366
 
1431
- for ( blanks = 0; t ; t = t->next )
1367
+ Qstring("<pre><code>", f);
1368
+ for ( blanks = 0; t ; t = t->next ) {
1432
1369
  if ( S(t->text) > t->dle ) {
1433
1370
  while ( blanks ) {
1434
- push("\n", 1, f);
1371
+ Qchar('\n', f);
1435
1372
  --blanks;
1436
1373
  }
1437
- push(T(t->text), S(t->text), f);
1438
- push("\n", 1, f);
1374
+ code(f, T(t->text), S(t->text));
1375
+ Qchar('\n', f);
1439
1376
  }
1440
1377
  else blanks++;
1441
-
1442
- Qstring("<pre><code>", f);
1443
- code(0, f);
1378
+ }
1444
1379
  Qstring("</code></pre>", f);
1445
1380
  }
1446
1381
 
data/ext/markdown.c CHANGED
@@ -305,8 +305,8 @@ isfootnote(Line *t)
305
305
  for ( ++i; i < S(t->text) ; ++i ) {
306
306
  if ( T(t->text)[i] == '[' )
307
307
  return 0;
308
- else if ( T(t->text)[i] == ']' && T(t->text)[i+1] == ':' )
309
- return 1;
308
+ else if ( T(t->text)[i] == ']' )
309
+ return ( T(t->text)[i+1] == ':' ) ;
310
310
  }
311
311
  return 0;
312
312
  }
@@ -315,11 +315,14 @@ isfootnote(Line *t)
315
315
  static int
316
316
  isquote(Line *t)
317
317
  {
318
- char *pt = T(t->text);
319
- return ( pt[0] == '>' ) ||
320
- ( pt[0] == ' ' && pt[1] == '>' ) ||
321
- ( pt[0] == ' ' && pt[1] == ' ' && pt[2] == '>') ||
322
- ( pt[0] == ' ' && pt[1] == ' ' && pt[2] == ' ' && pt[3] == '>');
318
+ int j;
319
+
320
+ for ( j=0; j < 4; j++ )
321
+ if ( T(t->text)[j] == '>' )
322
+ return 1;
323
+ else if ( !isspace(T(t->text)[j]) )
324
+ return 0;
325
+ return 0;
323
326
  }
324
327
 
325
328
 
@@ -386,13 +389,14 @@ ishdr(Line *t, int *htyp)
386
389
 
387
390
  if ( t->next ) {
388
391
  char *q = T(t->next->text);
392
+ int last = S(t->next->text);
389
393
 
390
394
  if ( (*q == '=') || (*q == '-') ) {
391
- /* find trailing space on === line */
392
- int e = S(t->next->text) - 1;
393
- while (e > 1 && q[e] == ' ') e--;
395
+ /* ignore trailing whitespace */
396
+ while ( (last > 1) && isspace(q[last-1]) )
397
+ --last;
394
398
 
395
- for (i=1; i <= e; i++)
399
+ for (i=1; i < last; i++)
396
400
  if ( q[0] != q[i] )
397
401
  return 0;
398
402
  *htyp = SETEXT;
@@ -483,7 +487,8 @@ headerblock(Paragraph *pp, int htyp)
483
487
  * the leading and trailing `#`'s
484
488
  */
485
489
 
486
- for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1); i++)
490
+ for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
491
+ && (i < 6); i++)
487
492
  ;
488
493
 
489
494
  pp->hnumber = i;
@@ -637,9 +642,16 @@ quoteblock(Paragraph *p)
637
642
 
638
643
  for ( t = p->text; t ; t = q ) {
639
644
  if ( isquote(t) ) {
640
- char *p = strchr(T(t->text), '>');
641
- if ( p[1] == ' ' ) p++;
642
- qp = p - T(t->text) + 1;
645
+ /* clip leading spaces */
646
+ for (qp = 0; T(t->text)[qp] != '>'; qp ++)
647
+ /* assert: the first nonblank character on this line
648
+ * will be a >
649
+ */;
650
+ /* clip '>' */
651
+ qp++;
652
+ /* clip next space, if any */
653
+ if ( T(t->text)[qp] == ' ' )
654
+ qp++;
643
655
  CLIP(t->text, 0, qp);
644
656
  t->dle = mkd_firstnonblank(t);
645
657
  }
@@ -719,8 +731,9 @@ listitem(Paragraph *p, int indent)
719
731
  }
720
732
 
721
733
  /* after a blank line, the next block needs to start with a line
722
- * that's indented 4 spaces, but after that the line doesn't
723
- * need any indentation
734
+ * that's indented 4(? -- reference implementation allows a 1
735
+ * character indent, but that has unfortunate side effects here)
736
+ * spaces, but after that the line doesn't need any indentation
724
737
  */
725
738
  if ( q != t->next ) {
726
739
  if (q->dle < indent) {
@@ -728,7 +741,8 @@ listitem(Paragraph *p, int indent)
728
741
  t->next = 0;
729
742
  return q;
730
743
  }
731
- indent = 4;
744
+ /* indent as far as the initial line was indented. */
745
+ indent = clip;
732
746
  }
733
747
 
734
748
  if ( (q->dle < indent) && (ishr(q) || islist(q,&z)) && !ishdr(q,&z) ) {
@@ -1070,8 +1084,8 @@ mkd_compile(Document *doc, int flags)
1070
1084
 
1071
1085
  doc->compiled = 1;
1072
1086
  memset(doc->ctx, 0, sizeof(MMIOT) );
1073
- doc->ctx->flags = flags & USER_FLAGS;
1074
- doc->ctx->base = doc->base;
1087
+ doc->ctx->cb = &(doc->cb);
1088
+ doc->ctx->flags = flags & USER_FLAGS;
1075
1089
  CREATE(doc->ctx->in);
1076
1090
  doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
1077
1091
  CREATE(*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
  */
@@ -81,7 +92,7 @@ typedef struct mmiot {
81
92
  #define SAFELINK 0x8000
82
93
  #define USER_FLAGS 0xFCFF
83
94
  #define EMBEDDED DENY_A|DENY_IMG|NO_PSEUDO_PROTO|CDATA_OUTPUT
84
- char *base;
95
+ Callback_data *cb;
85
96
  } MMIOT;
86
97
 
87
98
 
@@ -100,7 +111,7 @@ typedef struct document {
100
111
  int html; /* set after (internal) htmlify() */
101
112
  int tabstop; /* for properly expanding tabs (ick) */
102
113
  MMIOT *ctx; /* backend buffers, flags, and structures */
103
- char *base; /* url basename for url fragments */
114
+ Callback_data cb; /* callback functions & private data */
104
115
  } Document;
105
116
 
106
117
 
data/ext/mkdio.c CHANGED
@@ -217,14 +217,6 @@ markdown(Document *document, FILE *out, int flags)
217
217
  }
218
218
 
219
219
 
220
- void
221
- mkd_basename(Document *document, char *base)
222
- {
223
- if ( document )
224
- document->base = base;
225
- }
226
-
227
-
228
220
  /* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
229
221
  */
230
222
  void
@@ -301,3 +293,43 @@ mkd_generateline(char *bfr, int size, FILE *output, int flags)
301
293
  ___mkd_freemmiot(&f, 0);
302
294
  return 0;
303
295
  }
296
+
297
+
298
+ /* set the url display callback
299
+ */
300
+ void
301
+ mkd_e_url(Document *f, mkd_callback_t edit)
302
+ {
303
+ if ( f )
304
+ f->cb.e_url = edit;
305
+ }
306
+
307
+
308
+ /* set the url options callback
309
+ */
310
+ void
311
+ mkd_e_flags(Document *f, mkd_callback_t edit)
312
+ {
313
+ if ( f )
314
+ f->cb.e_flags = edit;
315
+ }
316
+
317
+
318
+ /* set the url display/options deallocator
319
+ */
320
+ void
321
+ mkd_e_free(Document *f, mkd_free_t dealloc)
322
+ {
323
+ if ( f )
324
+ f->cb.e_free = dealloc;
325
+ }
326
+
327
+
328
+ /* set the url display/options context data field
329
+ */
330
+ void
331
+ mkd_e_data(Document *f, void *data)
332
+ {
333
+ if ( f )
334
+ f->cb.e_data = data;
335
+ }
data/ext/mkdio.h CHANGED
@@ -48,6 +48,16 @@ int mkd_generatecss(MMIOT*,FILE*);
48
48
  int mkd_generateline(char *, int, FILE*, int);
49
49
  #define mkd_text mkd_generateline
50
50
 
51
+ /* url generator callbacks
52
+ */
53
+ typedef char * (*mkd_callback_t)(const char*, const int, void*);
54
+ typedef void (*mkd_free_t)(char*, void*);
55
+
56
+ void mkd_e_url(void *, mkd_callback_t);
57
+ void mkd_e_flags(void *, mkd_callback_t);
58
+ void mkd_e_free(void *, mkd_free_t );
59
+ void mkd_e_data(void *, void *);
60
+
51
61
  /* version#.
52
62
  */
53
63
  extern char markdown_version[];
data/lib/rdiscount.rb CHANGED
@@ -24,7 +24,7 @@
24
24
  # end
25
25
  #
26
26
  class RDiscount
27
- VERSION = '1.5.8.1'
27
+ VERSION = '1.6.3'
28
28
 
29
29
  # Original Markdown formatted text.
30
30
  attr_reader :text
data/rdiscount.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rdiscount'
3
- s.version = '1.5.8.1'
3
+ s.version = '1.6.3'
4
4
  s.summary = "Fast Implementation of Gruber's Markdown in C"
5
5
  s.date = '2010-03-08'
6
6
  s.email = 'r@tomayko.com'
@@ -15,11 +15,13 @@ Gem::Specification.new do |s|
15
15
  bin/rdiscount
16
16
  ext/Csio.c
17
17
  ext/amalloc.h
18
+ ext/basename.c
18
19
  ext/config.h
19
20
  ext/css.c
20
21
  ext/cstring.h
21
22
  ext/docheader.c
22
23
  ext/dumptree.c
24
+ ext/emmatch.c
23
25
  ext/extconf.rb
24
26
  ext/generate.c
25
27
  ext/markdown.c
@@ -129,7 +129,7 @@ class MarkdownTest < Test::Unit::TestCase
129
129
  "=================== \n\n" +
130
130
  "By Candice Yellowflower \n"
131
131
  markdown = Markdown.new(text)
132
- assert_equal "<h1>The Ant-Sugar Tales </h1>\n\n<p>By Candice Yellowflower <br/>\n</p>\n",
132
+ assert_equal "<h1>The Ant-Sugar Tales </h1>\n\n<p>By Candice Yellowflower</p>\n",
133
133
  markdown.to_html
134
134
  end
135
135
 
metadata CHANGED
@@ -4,10 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
- - 5
8
- - 8
9
- - 1
10
- version: 1.5.8.1
7
+ - 6
8
+ - 3
9
+ version: 1.6.3
11
10
  platform: ruby
12
11
  authors:
13
12
  - Ryan Tomayko
@@ -36,11 +35,13 @@ files:
36
35
  - bin/rdiscount
37
36
  - ext/Csio.c
38
37
  - ext/amalloc.h
38
+ - ext/basename.c
39
39
  - ext/config.h
40
40
  - ext/css.c
41
41
  - ext/cstring.h
42
42
  - ext/docheader.c
43
43
  - ext/dumptree.c
44
+ - ext/emmatch.c
44
45
  - ext/extconf.rb
45
46
  - ext/generate.c
46
47
  - ext/markdown.c