rdiscount 1.5.8.1 → 1.6.3

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