rdiscount 1.2.6.2

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