rdiscount 1.2.6.2

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/ext/extconf.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('rdiscount')
4
+
5
+ HAVE_RANDOM = have_func('random')
6
+ HAVE_SRANDOM = have_func('srandom')
7
+ HAVE_FUNOPEN = have_func('funopen')
8
+ HAVE_FOPENCOOKIE = have_func('fopencookie')
9
+
10
+ unless HAVE_FUNOPEN || HAVE_FOPENCOOKIE
11
+ fail "No funopen or fopencookie support available."
12
+ end
13
+
14
+ create_makefile('rdiscount')
data/ext/generate.c ADDED
@@ -0,0 +1,1319 @@
1
+ /* markdown: a C implementation of John Gruber's Markdown markup language.
2
+ *
3
+ * Copyright (C) 2007 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
+ /* prefixes for <automatic links>
21
+ */
22
+ static char *autoprefix[] = { "http://", "https://", "ftp://", "news://" };
23
+ #define SZAUTOPREFIX (sizeof autoprefix / sizeof autoprefix[0])
24
+
25
+ typedef int (*stfu)(const void*,const void*);
26
+
27
+
28
+ /* forward declarations */
29
+ static void code(int, MMIOT*);
30
+ static void text(MMIOT *f);
31
+ static Paragraph *display(Paragraph*, MMIOT*);
32
+
33
+ /* externals from markdown.c */
34
+ int __mkd_footsort(Footnote *, Footnote *);
35
+
36
+ /*
37
+ * push text into the generator input buffer
38
+ */
39
+ static void
40
+ push(char *bfr, int size, MMIOT *f)
41
+ {
42
+ while ( size-- > 0 )
43
+ EXPAND(f->in) = *bfr++;
44
+ }
45
+
46
+
47
+ /* look <i> characters ahead of the cursor.
48
+ */
49
+ static int
50
+ peek(MMIOT *f, int i)
51
+ {
52
+
53
+ i += (f->isp-1);
54
+
55
+ return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
56
+ }
57
+
58
+
59
+ /* pull a byte from the input buffer
60
+ */
61
+ static int
62
+ pull(MMIOT *f)
63
+ {
64
+ return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
65
+ }
66
+
67
+
68
+ /* return a pointer to the current position in the input buffer.
69
+ */
70
+ static char*
71
+ cursor(MMIOT *f)
72
+ {
73
+ return T(f->in) + f->isp;
74
+ }
75
+
76
+
77
+ /* return/set the current cursor position
78
+ */
79
+ #define mmiotseek(f,x) (f->isp = x)
80
+ #define mmiottell(f) (f->isp)
81
+
82
+
83
+ /* move n characters forward ( or -n characters backward) in the input buffer.
84
+ */
85
+ static void
86
+ shift(MMIOT *f, int i)
87
+ {
88
+ if (f->isp + i >= 0 )
89
+ f->isp += i;
90
+ }
91
+
92
+
93
+ /* Qchar()
94
+ */
95
+ static void
96
+ Qchar(char c, MMIOT *f)
97
+ {
98
+ block *cur;
99
+
100
+ if ( S(f->Q) == 0 ) {
101
+ cur = &EXPAND(f->Q);
102
+ memset(cur, 0, sizeof *cur);
103
+ cur->b_type = bTEXT;
104
+ }
105
+ else
106
+ cur = &T(f->Q)[S(f->Q)-1];
107
+
108
+ EXPAND(cur->b_text) = c;
109
+
110
+ }
111
+
112
+
113
+ /* Qstring()
114
+ */
115
+ static void
116
+ Qstring(char *s, MMIOT *f)
117
+ {
118
+ while (*s)
119
+ Qchar(*s++, f);
120
+ }
121
+
122
+
123
+ /* Qwrite()
124
+ */
125
+ static void
126
+ Qwrite(char *s, int size, MMIOT *f)
127
+ {
128
+ while (size-- > 0)
129
+ Qchar(*s++, f);
130
+ }
131
+
132
+
133
+ /* Qprintf()
134
+ */
135
+ static void
136
+ Qprintf(MMIOT *f, char *fmt, ...)
137
+ {
138
+ char bfr[80];
139
+ va_list ptr;
140
+
141
+ va_start(ptr,fmt);
142
+ vsnprintf(bfr, sizeof bfr, fmt, ptr);
143
+ va_end(ptr);
144
+ Qstring(bfr, f);
145
+ }
146
+
147
+
148
+ /* Qem()
149
+ */
150
+ static void
151
+ Qem(MMIOT *f, char c, int count)
152
+ {
153
+ block *p = &EXPAND(f->Q);
154
+
155
+ memset(p, 0, sizeof *p);
156
+ p->b_type = (c == '*') ? bSTAR : bUNDER;
157
+ p->b_char = c;
158
+ p->b_count = count;
159
+
160
+ memset(&EXPAND(f->Q), 0, sizeof(block));
161
+ }
162
+
163
+
164
+ /* empair()
165
+ */
166
+ static int
167
+ empair(MMIOT *f, int go, int level)
168
+ {
169
+
170
+ int i;
171
+ block *begin, *p;
172
+
173
+ begin = &T(f->Q)[go];
174
+ for (i=go+1; i < S(f->Q); i++) {
175
+ p = &T(f->Q)[i];
176
+
177
+ if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
178
+ break;
179
+
180
+ if ( p->b_type == begin->b_type ) {
181
+ if ( p->b_count == level ) /* exact match */
182
+ return i-go;
183
+
184
+ if ( p->b_count > 2 ) /* fuzzy match */
185
+ return i-go;
186
+ }
187
+ }
188
+ return EOF;
189
+ }
190
+
191
+
192
+
193
+ static struct emtags {
194
+ char open[10];
195
+ char close[10];
196
+ int size;
197
+ } emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
198
+
199
+
200
+ static void
201
+ emclose(Cstring *s, int level)
202
+ {
203
+ PREFIX(*s, emtags[level-1].close, emtags[level-1].size);
204
+ }
205
+
206
+
207
+ static void
208
+ emopen(Cstring *s, int level)
209
+ {
210
+ SUFFIX(*s, emtags[level-1].open, emtags[level-1].size-1);
211
+ }
212
+
213
+
214
+ /* emmatch()
215
+ */
216
+ static void
217
+ emmatch(MMIOT *f, int go)
218
+ {
219
+ block *start = &T(f->Q)[go], *end;
220
+ int e, e2, i, match;
221
+
222
+ while ( start->b_count ) {
223
+ switch (start->b_count) {
224
+ case 2: e = empair(f,go,match=2);
225
+ if ( e != EOF ) break;
226
+ case 1: e = empair(f,go,match=1); break;
227
+ default:
228
+ e = empair(f,go,1);
229
+ e2= empair(f,go,2);
230
+
231
+ if ( e == EOF || ((e2 != EOF) && (e2 >= e)) ) {
232
+ e = e2;
233
+ match = 2;
234
+ }
235
+ else
236
+ match = 1;
237
+ }
238
+ if ( e != EOF ) {
239
+ end = &T(f->Q)[go+e];
240
+ emclose(&end->b_post, match);
241
+ emopen(&start->b_text, match);
242
+ end->b_count -= match;
243
+ }
244
+ else {
245
+ for (i=0; i < match; i++)
246
+ EXPAND(start->b_text) = start->b_char;
247
+ }
248
+
249
+ start->b_count -= match;
250
+ }
251
+ }
252
+
253
+
254
+ /* emblock()
255
+ */
256
+ static void
257
+ emblock(MMIOT *f)
258
+ {
259
+ int i;
260
+ block *p;
261
+
262
+ for (i=0; i < S(f->Q); i++) {
263
+ p = &T(f->Q)[i];
264
+
265
+ if ( p->b_type != bTEXT ) emmatch(f, i);
266
+
267
+ if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
268
+ DELETE(p->b_post); }
269
+ if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
270
+ DELETE(p->b_text); }
271
+ }
272
+ S(f->Q) = 0;
273
+ }
274
+
275
+
276
+ /* generate html from a markup fragment
277
+ */
278
+ static void
279
+ reparse(char *bfr, int size, int flags, MMIOT *f)
280
+ {
281
+ MMIOT sub;
282
+
283
+ ___mkd_initmmiot(&sub, f->footnotes);
284
+
285
+ sub.flags = f->flags | flags;
286
+ sub.base = f->base;
287
+
288
+ push(bfr, size, &sub);
289
+ EXPAND(sub.in) = 0;
290
+ S(sub.in)--;
291
+
292
+ text(&sub);
293
+ emblock(&sub);
294
+
295
+ Qwrite(T(sub.out), S(sub.out), f);
296
+
297
+ ___mkd_freemmiot(&sub, f->footnotes);
298
+ }
299
+
300
+
301
+ /*
302
+ * write out a url, escaping problematic characters
303
+ */
304
+ static void
305
+ puturl(char *s, int size, MMIOT *f)
306
+ {
307
+ unsigned char c;
308
+
309
+ while ( size-- > 0 ) {
310
+ c = *s++;
311
+
312
+ if ( c == '&' )
313
+ Qstring("&amp;", f);
314
+ else if ( c == '<' )
315
+ Qstring("&lt;", f);
316
+ else if ( isalnum(c) || c == '.' || c == '-' || c == '_' || c == '/'
317
+ || c == '=' || c == '?' || c == ':' || c == '#' )
318
+ Qchar(c, f);
319
+ else
320
+ Qprintf(f, "%%%02X", c);
321
+ }
322
+ }
323
+
324
+
325
+ /* advance forward until the next character is not whitespace
326
+ */
327
+ static int
328
+ eatspace(MMIOT *f)
329
+ {
330
+ int c;
331
+
332
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
333
+ ;
334
+ return c;
335
+ }
336
+
337
+
338
+ /* (match (a (nested (parenthetical (string.)))))
339
+ */
340
+ static int
341
+ parenthetical(int in, int out, MMIOT *f)
342
+ {
343
+ int size, indent, c;
344
+
345
+ for ( indent=1,size=0; indent; size++ ) {
346
+ if ( (c = pull(f)) == EOF )
347
+ return EOF;
348
+ else if ( c == in )
349
+ ++indent;
350
+ else if ( c == out )
351
+ --indent;
352
+ }
353
+ return size-1;
354
+ }
355
+
356
+
357
+ /* extract a []-delimited label from the input stream.
358
+ */
359
+ static char *
360
+ linkylabel(MMIOT *f, int *sizep)
361
+ {
362
+ char *ptr = cursor(f);
363
+
364
+ if ( (*sizep = parenthetical('[',']',f)) != EOF )
365
+ return ptr;
366
+ return 0;
367
+ }
368
+
369
+
370
+ /* extract a (-prefixed url from the input stream.
371
+ * the label is either of the format `<link>`, where I
372
+ * extract until I find a >, or it is of the format
373
+ * `text`, where I extract until I reach a ')' or
374
+ * whitespace.
375
+ */
376
+ static char*
377
+ linkyurl(MMIOT *f, int *sizep)
378
+ {
379
+ int size = 0;
380
+ char *ptr;
381
+ int c;
382
+
383
+ if ( (c = eatspace(f)) == EOF )
384
+ return 0;
385
+
386
+ ptr = cursor(f);
387
+
388
+ if ( c == '<' ) {
389
+ pull(f);
390
+ ptr++;
391
+ if ( (size = parenthetical('<', '>', f)) == EOF )
392
+ return 0;
393
+ }
394
+ else {
395
+ for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
396
+ if ( c == EOF ) return 0;
397
+ if ( c == ')' )
398
+ shift(f, -1);
399
+ }
400
+ *sizep = size;
401
+ return ptr;
402
+ }
403
+
404
+
405
+ /* extract a =HHHxWWW size from the input stream
406
+ */
407
+ static int
408
+ linkysize(MMIOT *f, int *heightp, int *widthp)
409
+ {
410
+ int height=0, width=0;
411
+ int c;
412
+
413
+ *heightp = 0;
414
+ *widthp = 0;
415
+
416
+ if ( (c = eatspace(f)) != '=' )
417
+ return (c != EOF);
418
+ pull(f); /* eat '=' */
419
+
420
+ for ( c = pull(f); isdigit(c); c = pull(f))
421
+ width = (width * 10) + (c - '0');
422
+
423
+ if ( c == 'x' ) {
424
+ for ( c = pull(f); isdigit(c); c = pull(f))
425
+ height = (height*10) + (c - '0');
426
+
427
+ if ( c != EOF ) {
428
+ if ( !isspace(c) ) shift(f, -1);
429
+ *heightp = height;
430
+ *widthp = width;
431
+ return 1;
432
+ }
433
+ }
434
+ return 0;
435
+ }
436
+
437
+
438
+ /* extract a )-terminated title from the input stream.
439
+ */
440
+ static char*
441
+ linkytitle(MMIOT *f, int *sizep)
442
+ {
443
+ int countq=0, qc, c, size;
444
+ char *ret, *lastqc = 0;
445
+
446
+ eatspace(f);
447
+ if ( (qc=pull(f)) != '"' && qc != '\'' && qc != '(' )
448
+ return 0;
449
+
450
+ if ( qc == '(' ) qc = ')';
451
+
452
+ for ( ret = cursor(f); (c = pull(f)) != EOF; ) {
453
+ if ( (c == ')') && countq ) {
454
+ size = (lastqc ? lastqc : cursor(f)) - ret;
455
+ *sizep = size-1;
456
+ return ret;
457
+ }
458
+ else if ( c == qc ) {
459
+ lastqc = cursor(f);
460
+ countq++;
461
+ }
462
+ }
463
+ return 0;
464
+ }
465
+
466
+
467
+ /* look up (or construct) a footnote from the [xxx] link
468
+ * at the head of the stream.
469
+ */
470
+ static int
471
+ linkykey(int image, Footnote *val, MMIOT *f)
472
+ {
473
+ Footnote *ret;
474
+ Cstring mylabel;
475
+
476
+ memset(val, 0, sizeof *val);
477
+
478
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
479
+ return 0;
480
+
481
+ eatspace(f);
482
+ switch ( pull(f) ) {
483
+ case '(':
484
+ /* embedded link */
485
+ if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
486
+ return 0;
487
+
488
+ if ( image && !linkysize(f, &val->height, &val->width) )
489
+ return 0;
490
+
491
+ T(val->title) = linkytitle(f, &S(val->title));
492
+
493
+ return peek(f,0) == ')';
494
+
495
+ case '[':
496
+ /* footnote link */
497
+ mylabel = val->tag;
498
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
499
+ return 0;
500
+
501
+ if ( !S(val->tag) )
502
+ val->tag = mylabel;
503
+
504
+ ret = bsearch(val, T(*f->footnotes), S(*f->footnotes),
505
+ sizeof *val, (stfu)__mkd_footsort);
506
+
507
+ if ( ret ) {
508
+ val->tag = mylabel;
509
+ val->link = ret->link;
510
+ val->title = ret->title;
511
+ val->height = ret->height;
512
+ val->width = ret->width;
513
+ return 1;
514
+ }
515
+ }
516
+ return 0;
517
+ }
518
+
519
+
520
+ /*
521
+ * all the tag types that linkylinky can produce are
522
+ * defined by this structure.
523
+ */
524
+ typedef struct linkytype {
525
+ char *pat;
526
+ int szpat;
527
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
528
+ char *link_sfx; /* link suffix (eg: "\"" */
529
+ int WxH; /* this tag allows width x height arguments */
530
+ char *text_pfx; /* text prefix (eg: ">" */
531
+ char *text_sfx; /* text suffix (eg: "</a>" */
532
+ int flags; /* reparse flags */
533
+ } linkytype;
534
+
535
+ static linkytype imaget = { 0, 0, "<img src=\"", "\"",
536
+ 1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
537
+ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
538
+ 0, ">", "</a>", DENY_A };
539
+
540
+ /*
541
+ * pseudo-protocols for [][];
542
+ *
543
+ * id: generates <a id="link">tag</a>
544
+ * class: generates <span class="link">tag</span>
545
+ * raw: just dump the link without any processing
546
+ */
547
+ static linkytype specials[] = {
548
+ { "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
549
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
550
+ { "raw:", 4, 0, 0, 0, 0, 0, 0 },
551
+ } ;
552
+
553
+ #define NR(x) (sizeof x / sizeof x[0])
554
+
555
+ /* see if t contains one of our pseudo-protocols.
556
+ */
557
+ static linkytype *
558
+ extratag(Cstring t)
559
+ {
560
+ int i;
561
+ linkytype *r;
562
+
563
+ for ( i=0; i < NR(specials); i++ ) {
564
+ r = &specials[i];
565
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
566
+ return r;
567
+ }
568
+ return 0;
569
+ }
570
+
571
+
572
+ /*
573
+ * process embedded links and images
574
+ */
575
+ static int
576
+ linkylinky(int image, MMIOT *f)
577
+ {
578
+ int start = mmiottell(f);
579
+ Footnote link;
580
+ linkytype *tag;
581
+
582
+ if ( !(linkykey(image, &link, f) && S(link.tag)) ) {
583
+ mmiotseek(f, start);
584
+ return 0;
585
+ }
586
+
587
+ if ( image )
588
+ tag = &imaget;
589
+ else if ( (f->flags & NO_PSEUDO_PROTO) || (tag = extratag(link.link)) == 0 )
590
+ tag = &linkt;
591
+
592
+ if ( f->flags & tag-> flags ) {
593
+ mmiotseek(f, start);
594
+ return 0;
595
+ }
596
+
597
+ if ( tag->link_pfx ) {
598
+ Qstring(tag->link_pfx, f);
599
+ if ( f->base && (T(link.link)[tag->szpat] == '/') )
600
+ puturl(f->base, strlen(f->base), f);
601
+ puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
602
+ Qstring(tag->link_sfx, f);
603
+
604
+ if ( tag->WxH && link.height && link.width ) {
605
+ Qprintf(f," height=\"%d\"", link.height);
606
+ Qprintf(f, " width=\"%d\"", link.width);
607
+ }
608
+
609
+ if ( S(link.title) ) {
610
+ Qstring(" title=\"", f);
611
+ reparse(T(link.title), S(link.title), INSIDE_TAG, f);
612
+ Qchar('"', f);
613
+ }
614
+
615
+ Qstring(tag->text_pfx, f);
616
+ reparse(T(link.tag), S(link.tag), tag->flags, f);
617
+ Qstring(tag->text_sfx, f);
618
+ }
619
+ else
620
+ Qwrite(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
621
+
622
+ return 1;
623
+ }
624
+
625
+
626
+ /* write a character to output, doing text escapes ( & -> &amp;,
627
+ * > -> &gt; < -> &lt; )
628
+ */
629
+ static void
630
+ cputc(int c, MMIOT *f)
631
+ {
632
+ switch (c) {
633
+ case '&': Qstring("&amp;", f); break;
634
+ case '>': Qstring("&gt;", f); break;
635
+ case '<': Qstring("&lt;", f); break;
636
+ default : Qchar(c, f); break;
637
+ }
638
+ }
639
+
640
+
641
+ /*
642
+ * convert an email address to a string of nonsense
643
+ */
644
+ static void
645
+ mangle(unsigned char *s, int len, MMIOT *f)
646
+ {
647
+ while ( len-- > 0 ) {
648
+ Qstring("&#", f);
649
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *s++);
650
+ }
651
+ }
652
+
653
+
654
+ /* before letting a tag through, validate against
655
+ * DENY_A and DENY_IMG
656
+ */
657
+ static int
658
+ forbidden_tag(MMIOT *f)
659
+ {
660
+ int c = toupper(peek(f, 1));
661
+
662
+ if ( c == 'A' && (f->flags & DENY_A) && !isalnum(peek(f,2)) )
663
+ return 1;
664
+ if ( c == 'I' && (f->flags & DENY_IMG)
665
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
666
+ && !isalnum(peek(f,4)) )
667
+ return 1;
668
+ return 0;
669
+ }
670
+
671
+
672
+
673
+ /* a < may be just a regular character, the start of an embedded html
674
+ * tag, or the start of an <automatic link>. If it's an automatic
675
+ * link, we also need to know if it's an email address because if it
676
+ * is we need to mangle it in our futile attempt to cut down on the
677
+ * spaminess of the rendered page.
678
+ */
679
+ static int
680
+ maybe_tag_or_link(MMIOT *f)
681
+ {
682
+ char *text;
683
+ int c, size, i;
684
+ int maybetag=1, maybeaddress=0;
685
+ int mailto;
686
+
687
+ if ( f->flags & INSIDE_TAG )
688
+ return 0;
689
+
690
+ for ( size=0; ((c = peek(f,size+1)) != '>') && !isspace(c); size++ ) {
691
+ if ( ! (c == '/' || isalnum(c) || c == '~') )
692
+ maybetag=0;
693
+ if ( c == '@' )
694
+ maybeaddress=1;
695
+ else if ( c == EOF )
696
+ return 0;
697
+ }
698
+
699
+ if ( size == 0 )
700
+ return 0;
701
+
702
+ if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
703
+ Qstring(forbidden_tag(f) ? "&lt;" : "<", f);
704
+ while ( ((c = peek(f, size+1)) != EOF) && (c != '>') )
705
+ cputc(pull(f), f);
706
+ return 1;
707
+ }
708
+
709
+ if ( f->flags & DENY_A ) return 0;
710
+
711
+ text = cursor(f);
712
+ shift(f, size+1);
713
+
714
+ for ( i=0; i < SZAUTOPREFIX; i++ )
715
+ if ( strncasecmp(text, autoprefix[i], strlen(autoprefix[i])) == 0 ) {
716
+ Qstring("<a href=\"", f);
717
+ puturl(text,size,f);
718
+ Qstring("\">", f);
719
+ puturl(text,size,f);
720
+ Qstring("</a>", f);
721
+ return 1;
722
+ }
723
+ if ( maybeaddress ) {
724
+
725
+ Qstring("<a href=\"", f);
726
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 )
727
+ mailto = 7;
728
+ else {
729
+ mailto = 0;
730
+ /* supply a mailto: protocol if one wasn't attached */
731
+ mangle("mailto:", 7, f);
732
+ }
733
+
734
+ mangle(text, size, f);
735
+ Qstring("\">", f);
736
+ mangle(text+mailto, size-mailto, f);
737
+ Qstring("</a>", f);
738
+ return 1;
739
+ }
740
+
741
+ shift(f, -(size+1));
742
+ return 0;
743
+ } /* maybe_tag_or_link */
744
+
745
+
746
+ static int
747
+ isthisspace(MMIOT *f, int i)
748
+ {
749
+ int c = peek(f, i);
750
+
751
+ return isspace(c) || (c == EOF);
752
+ }
753
+
754
+
755
+ static int
756
+ isthisnonword(MMIOT *f, int i)
757
+ {
758
+ return isthisspace(f, i) || ispunct(peek(f,i));
759
+ }
760
+
761
+
762
+ /* smartyquote code that's common for single and double quotes
763
+ */
764
+ static int
765
+ smartyquote(int *flags, char typeofquote, MMIOT *f)
766
+ {
767
+ int bit = (typeofquote == 's') ? 0x01 : 0x02;
768
+
769
+ if ( bit & (*flags) ) {
770
+ if ( isthisnonword(f,1) ) {
771
+ Qprintf(f, "&r%cquo;", typeofquote);
772
+ (*flags) &= ~bit;
773
+ return 1;
774
+ }
775
+ }
776
+ else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) {
777
+ Qprintf(f, "&l%cquo;", typeofquote);
778
+ (*flags) |= bit;
779
+ return 1;
780
+ }
781
+ return 0;
782
+ }
783
+
784
+
785
+ static int
786
+ islike(MMIOT *f, char *s)
787
+ {
788
+ int len;
789
+ int i;
790
+
791
+ if ( s[0] == '<' ) {
792
+ if ( !isthisnonword(f, -1) )
793
+ return 0;
794
+ ++s;
795
+ }
796
+
797
+ if ( !(len = strlen(s)) )
798
+ return 0;
799
+
800
+ if ( s[len-1] == '>' ) {
801
+ if ( !isthisnonword(f,len-1) )
802
+ return 0;
803
+ len--;
804
+ }
805
+
806
+ for (i=1; i < len; i++)
807
+ if (tolower(peek(f,i)) != s[i])
808
+ return 0;
809
+ return 1;
810
+ }
811
+
812
+
813
+ static struct smarties {
814
+ char c0;
815
+ char *pat;
816
+ char *entity;
817
+ int shift;
818
+ } smarties[] = {
819
+ { '\'', "'s>", "rsquo", 0 },
820
+ { '\'', "'t>", "rsquo", 0 },
821
+ { '-', "--", "mdash", 1 },
822
+ { '-', "<->", "ndash", 0 },
823
+ { '.', "...", "hellip", 2 },
824
+ { '.', ". . .", "hellip", 4 },
825
+ { '(', "(c)", "copy", 2 },
826
+ { '(', "(r)", "reg", 2 },
827
+ { '(', "(tm)", "trade", 3 },
828
+ { '3', "<3/4>", "frac34", 2 },
829
+ { '3', "<3/4ths>", "frac34", 2 },
830
+ { '1', "<1/2>", "frac12", 2 },
831
+ { '1', "<1/4>", "frac14", 2 },
832
+ { '1', "<1/4th>", "frac14", 2 },
833
+ { '&', "&#0;", 0, 3 },
834
+ } ;
835
+ #define NRSMART ( sizeof smarties / sizeof smarties[0] )
836
+
837
+
838
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
839
+ */
840
+ static int
841
+ smartypants(int c, int *flags, MMIOT *f)
842
+ {
843
+ int i;
844
+
845
+ if ( f->flags & DENY_SMARTY )
846
+ return 0;
847
+
848
+ for ( i=0; i < NRSMART; i++)
849
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
850
+ if ( smarties[i].entity )
851
+ Qprintf(f, "&%s;", smarties[i].entity);
852
+ shift(f, smarties[i].shift);
853
+ return 1;
854
+ }
855
+
856
+ switch (c) {
857
+ case '<' : return 0;
858
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
859
+ break;
860
+
861
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
862
+ break;
863
+
864
+ case '`': if ( peek(f, 1) == '`' ) {
865
+ int j = 2;
866
+
867
+ while ( (c=peek(f,j)) != EOF ) {
868
+ if ( c == '\\' )
869
+ j += 2;
870
+ else if ( c == '`' )
871
+ break;
872
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
873
+ Qstring("&ldquo;", f);
874
+ reparse(cursor(f)+1, j-2, 0, f);
875
+ Qstring("&rdquo;", f);
876
+ shift(f,j+1);
877
+ return 1;
878
+ }
879
+ else ++j;
880
+ }
881
+
882
+ }
883
+ break;
884
+ }
885
+ return 0;
886
+ } /* smartypants */
887
+
888
+
889
+ #define tag_text(f) (f->flags & INSIDE_TAG)
890
+
891
+
892
+ static void
893
+ text(MMIOT *f)
894
+ {
895
+ int c, j;
896
+ int rep;
897
+ int smartyflags = 0;
898
+
899
+ while ( (c = pull(f)) != EOF ) {
900
+ if ( smartypants(c, &smartyflags, f) )
901
+ continue;
902
+ switch (c) {
903
+ case 0: break;
904
+
905
+ case '>': if ( tag_text(f) )
906
+ Qstring("&gt;", f);
907
+ else
908
+ Qchar(c, f);
909
+ break;
910
+
911
+ case '"': if ( tag_text(f) )
912
+ Qstring("&quot;", f);
913
+ else
914
+ Qchar(c, f);
915
+ break;
916
+
917
+ case '!': if ( peek(f,1) == '[' ) {
918
+ pull(f);
919
+ if ( tag_text(f) || !linkylinky(1, f) )
920
+ Qstring("![", f);
921
+ }
922
+ else
923
+ Qchar(c, f);
924
+ break;
925
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
926
+ Qchar(c, f);
927
+ break;
928
+ case '*':
929
+ case '_': if ( tag_text(f) )
930
+ Qchar(c, f);
931
+ #if RELAXED_EMPHASIS
932
+ else if ( peek(f,1) == c ) {
933
+ for ( rep = 1; peek(f,1) == c; pull(f) )
934
+ ++rep;
935
+
936
+ Qem(f, c, rep);
937
+ }
938
+ else if ( (isthisspace(f,-1) && isthisspace(f,1))
939
+ || (isalnum(peek(f,-1)) && isalnum(peek(f,1))) )
940
+ Qchar(c, f);
941
+ else {
942
+ Qem(f, c, 1);
943
+ }
944
+ #else
945
+ else {
946
+ for (rep = 1; peek(f,1) == c; pull(f) )
947
+ ++rep;
948
+ Qem(f,c,rep);
949
+ }
950
+ #endif
951
+ break;
952
+
953
+ case '`': if ( tag_text(f) )
954
+ Qchar(c, f);
955
+ else {
956
+ Qstring("<code>", f);
957
+ if ( peek(f, 1) == '`' ) {
958
+ pull(f);
959
+ code(2, f);
960
+ }
961
+ else
962
+ code(1, f);
963
+ Qstring("</code>", f);
964
+ }
965
+ break;
966
+
967
+ case '\\': switch ( c = pull(f) ) {
968
+ case '&': Qstring("&amp;", f);
969
+ break;
970
+ case '<': Qstring("&lt;", f);
971
+ break;
972
+ case '\\':
973
+ case '>': case '#': case '.': case '-':
974
+ case '+': case '{': case '}': case ']':
975
+ case '(': case ')': case '"': case '\'':
976
+ case '!': case '[': case '*': case '_':
977
+ case '`': Qchar(c, f);
978
+ break;
979
+ default:
980
+ Qchar('\\', f);
981
+ if ( c != EOF )
982
+ shift(f,-1);
983
+ break;
984
+ }
985
+ break;
986
+
987
+ case '<': if ( !maybe_tag_or_link(f) )
988
+ Qstring("&lt;", f);
989
+ break;
990
+
991
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
992
+ while ( isalnum(peek(f,j)) )
993
+ ++j;
994
+
995
+ if ( peek(f,j) != ';' )
996
+ Qstring("&amp;", f);
997
+ else
998
+ Qchar(c, f);
999
+ break;
1000
+
1001
+ default: Qchar(c, f);
1002
+ break;
1003
+ }
1004
+ }
1005
+ } /* text */
1006
+
1007
+
1008
+ static int
1009
+ endofcode(int escape, int offset, MMIOT *f)
1010
+ {
1011
+ switch (escape) {
1012
+ case 2: if ( peek(f, offset+1) == '`' ) {
1013
+ shift(f,1);
1014
+ case 1: shift(f,offset);
1015
+ return 1;
1016
+ }
1017
+ default:return 0;
1018
+ }
1019
+ }
1020
+
1021
+
1022
+ /* the only characters that have special meaning in a code block are
1023
+ * `<' and `&' , which are /always/ expanded to &lt; and &amp;
1024
+ */
1025
+ static void
1026
+ code(int escape, MMIOT *f)
1027
+ {
1028
+ int c;
1029
+
1030
+ if ( escape && (peek(f,1) == ' ') )
1031
+ shift(f,1);
1032
+
1033
+ while ( (c = pull(f)) != EOF ) {
1034
+ switch (c) {
1035
+ case ' ': if ( peek(f,1) == '`' && endofcode(escape, 1, f) )
1036
+ return;
1037
+ Qchar(c, f);
1038
+ break;
1039
+
1040
+ case '`': if ( endofcode(escape, 0, f) )
1041
+ return;
1042
+ Qchar(c, f);
1043
+ break;
1044
+
1045
+ case '\\': cputc(c, f);
1046
+ if ( peek(f,1) == '>' || (c = pull(f)) == EOF )
1047
+ break;
1048
+
1049
+ default: cputc(c, f);
1050
+ break;
1051
+ }
1052
+ }
1053
+ } /* code */
1054
+
1055
+
1056
+ /* print a header block
1057
+ */
1058
+ static void
1059
+ printheader(Paragraph *pp, MMIOT *f)
1060
+ {
1061
+ Qprintf(f, "<h%d>", pp->hnumber);
1062
+ push(T(pp->text->text), S(pp->text->text), f);
1063
+ text(f);
1064
+ Qprintf(f, "</h%d>", pp->hnumber);
1065
+ }
1066
+
1067
+
1068
+ static int
1069
+ printblock(Paragraph *pp, MMIOT *f)
1070
+ {
1071
+ Line *t = pp->text;
1072
+ static char *Begin[] = { "", "<p>", "<center>" };
1073
+ static char *End[] = { "", "</p>","</center>" };
1074
+
1075
+ while (t) {
1076
+ if ( S(t->text) ) {
1077
+ if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
1078
+ && T(t->text)[S(t->text)-1] == ' ') {
1079
+ push(T(t->text), S(t->text)-2, f);
1080
+ push("<br/>\n", 6, f);
1081
+ }
1082
+ else {
1083
+ push(T(t->text), S(t->text), f);
1084
+ if ( t->next )
1085
+ push("\n", 1, f);
1086
+ }
1087
+ }
1088
+ t = t->next;
1089
+ }
1090
+ Qstring(Begin[pp->align], f);
1091
+ text(f);
1092
+ Qstring(End[pp->align], f);
1093
+ return 1;
1094
+ }
1095
+
1096
+
1097
+ static void
1098
+ printcode(Line *t, MMIOT *f)
1099
+ {
1100
+ int blanks;
1101
+
1102
+ for ( blanks = 0; t ; t = t->next )
1103
+ if ( S(t->text) > t->dle ) {
1104
+ while ( blanks ) {
1105
+ push("\n", 1, f);
1106
+ --blanks;
1107
+ }
1108
+ push(T(t->text), S(t->text), f);
1109
+ push("\n", 1, f);
1110
+ }
1111
+ else blanks++;
1112
+
1113
+ Qstring("<pre><code>", f);
1114
+ code(0, f);
1115
+ Qstring("</code></pre>", f);
1116
+ }
1117
+
1118
+
1119
+ static void
1120
+ printhtml(Line *t, MMIOT *f)
1121
+ {
1122
+ int blanks;
1123
+
1124
+ for ( blanks=0; t ; t = t->next )
1125
+ if ( S(t->text) ) {
1126
+ for ( ; blanks; --blanks )
1127
+ Qchar('\n', f);
1128
+
1129
+ Qwrite(T(t->text), S(t->text), f);
1130
+ Qchar('\n', f);
1131
+ }
1132
+ else
1133
+ blanks++;
1134
+ }
1135
+
1136
+
1137
+ static void
1138
+ htmlify(Paragraph *p, char *block, MMIOT *f)
1139
+ {
1140
+ emblock(f);
1141
+ if ( block ) Qprintf(f, "<%s>", block);
1142
+ emblock(f);
1143
+
1144
+ while (( p = display(p, f) )) {
1145
+ emblock(f);
1146
+ Qstring("\n\n", f);
1147
+ }
1148
+
1149
+ if ( block ) Qprintf(f, "</%s>", block);
1150
+ emblock(f);
1151
+ }
1152
+
1153
+
1154
+ #if DL_TAG_EXTENSION
1155
+ static void
1156
+ definitionlist(Paragraph *p, MMIOT *f)
1157
+ {
1158
+ Line *tag;
1159
+
1160
+ if ( p ) {
1161
+ Qstring("<dl>\n", f);
1162
+
1163
+ for ( ; p ; p = p->next) {
1164
+ for ( tag = p->text; tag; tag = tag->next ) {
1165
+ Qstring("<dt>", f);
1166
+ reparse(T(tag->text), S(tag->text), 0, f);
1167
+ Qstring("</dt>\n", f);
1168
+ }
1169
+
1170
+ htmlify(p->down, "dd", f);
1171
+ }
1172
+
1173
+ Qstring("</dl>", f);
1174
+ }
1175
+ }
1176
+ #endif
1177
+
1178
+
1179
+ static void
1180
+ listdisplay(int typ, Paragraph *p, MMIOT* f)
1181
+ {
1182
+ if ( p ) {
1183
+ Qprintf(f, "<%cl>\n", (typ==UL)?'u':'o');
1184
+
1185
+ for ( ; p ; p = p->next ) {
1186
+ htmlify(p->down, "li", f);
1187
+ Qchar('\n', f);
1188
+ }
1189
+
1190
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
1191
+ }
1192
+ }
1193
+
1194
+
1195
+ /* dump out a Paragraph in the desired manner
1196
+ */
1197
+ static Paragraph*
1198
+ display(Paragraph *p, MMIOT *f)
1199
+ {
1200
+ if ( !p ) return 0;
1201
+
1202
+ switch ( p->typ ) {
1203
+ case STYLE:
1204
+ case WHITESPACE:
1205
+ break;
1206
+
1207
+ case HTML:
1208
+ printhtml(p->text, f);
1209
+ break;
1210
+
1211
+ case CODE:
1212
+ printcode(p->text, f);
1213
+ break;
1214
+
1215
+ case QUOTE:
1216
+ htmlify(p->down, "blockquote", f);
1217
+ break;
1218
+
1219
+ case UL:
1220
+ case OL:
1221
+ listdisplay(p->typ, p->down, f);
1222
+ break;
1223
+
1224
+ #if DL_TAG_EXTENSION
1225
+ case DL:
1226
+ definitionlist(p->down, f);
1227
+ break;
1228
+ #endif
1229
+
1230
+ case HR:
1231
+ Qstring("<hr />", f);
1232
+ break;
1233
+
1234
+ case HDR:
1235
+ printheader(p, f);
1236
+ break;
1237
+
1238
+ default:
1239
+ printblock(p, f);
1240
+ break;
1241
+ }
1242
+ return p->next;
1243
+ }
1244
+
1245
+
1246
+ /*
1247
+ * dump out stylesheet sections.
1248
+ */
1249
+ static int
1250
+ stylesheets(Paragraph *p, FILE *f)
1251
+ {
1252
+ Line* q;
1253
+
1254
+ for ( ; p ; p = p->next ) {
1255
+ if ( p->typ == STYLE ) {
1256
+ for ( q = p->text; q ; q = q->next )
1257
+ if ( fwrite(T(q->text), S(q->text), 1, f) == 1 )
1258
+ putc('\n', f);
1259
+ else
1260
+ return EOF;
1261
+ }
1262
+ if ( p->down && (stylesheets(p->down, f) == EOF) )
1263
+ return EOF;
1264
+ }
1265
+ return 0;
1266
+ }
1267
+
1268
+
1269
+ /* return a pointer to the compiled markdown
1270
+ * document.
1271
+ */
1272
+ int
1273
+ mkd_document(Document *p, char **res)
1274
+ {
1275
+ if ( p && p->compiled ) {
1276
+ if ( ! p->html ) {
1277
+ htmlify(p->code, 0, p->ctx);
1278
+ p->html = 1;
1279
+ }
1280
+
1281
+ *res = T(p->ctx->out);
1282
+ return S(p->ctx->out);
1283
+ }
1284
+ return EOF;
1285
+ }
1286
+
1287
+
1288
+ /* public interface for reparse()
1289
+ */
1290
+ int
1291
+ mkd_text(char *bfr, int size, FILE *output, int flags)
1292
+ {
1293
+ MMIOT f;
1294
+
1295
+ ___mkd_initmmiot(&f, 0);
1296
+ f.flags = flags & USER_FLAGS;
1297
+
1298
+ reparse(bfr, size, 0, &f);
1299
+ emblock(&f);
1300
+ if ( flags & CDATA_OUTPUT )
1301
+ ___mkd_xml(T(f.out), S(f.out), output);
1302
+ else
1303
+ fwrite(T(f.out), S(f.out), 1, output);
1304
+
1305
+ ___mkd_freemmiot(&f, 0);
1306
+ return 0;
1307
+ }
1308
+
1309
+
1310
+ /* dump any embedded styles
1311
+ */
1312
+ int
1313
+ mkd_style(Document *d, FILE *f)
1314
+ {
1315
+ if ( d && d->compiled )
1316
+ return stylesheets(d->code, f);
1317
+ return EOF;
1318
+ }
1319
+