pixeltrix-rdiscount 1.2.11

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,1333 @@
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
317
+ Qchar(c, f);
318
+ }
319
+ }
320
+
321
+
322
+ /* advance forward until the next character is not whitespace
323
+ */
324
+ static int
325
+ eatspace(MMIOT *f)
326
+ {
327
+ int c;
328
+
329
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
330
+ ;
331
+ return c;
332
+ }
333
+
334
+
335
+ /* (match (a (nested (parenthetical (string.)))))
336
+ */
337
+ static int
338
+ parenthetical(int in, int out, MMIOT *f)
339
+ {
340
+ int size, indent, c;
341
+
342
+ for ( indent=1,size=0; indent; size++ ) {
343
+ if ( (c = pull(f)) == EOF )
344
+ return EOF;
345
+ else if ( c == in )
346
+ ++indent;
347
+ else if ( c == out )
348
+ --indent;
349
+ }
350
+ return size-1;
351
+ }
352
+
353
+
354
+ /* extract a []-delimited label from the input stream.
355
+ */
356
+ static char *
357
+ linkylabel(MMIOT *f, int *sizep)
358
+ {
359
+ char *ptr = cursor(f);
360
+
361
+ if ( (*sizep = parenthetical('[',']',f)) != EOF )
362
+ return ptr;
363
+ return 0;
364
+ }
365
+
366
+
367
+ /* extract a (-prefixed url from the input stream.
368
+ * the label is either of the format `<link>`, where I
369
+ * extract until I find a >, or it is of the format
370
+ * `text`, where I extract until I reach a ')' or
371
+ * whitespace.
372
+ */
373
+ static char*
374
+ linkyurl(MMIOT *f, int *sizep)
375
+ {
376
+ int size = 0;
377
+ char *ptr;
378
+ int c;
379
+
380
+ if ( (c = eatspace(f)) == EOF )
381
+ return 0;
382
+
383
+ ptr = cursor(f);
384
+
385
+ if ( c == '<' ) {
386
+ pull(f);
387
+ ptr++;
388
+ if ( (size = parenthetical('<', '>', f)) == EOF )
389
+ return 0;
390
+ }
391
+ else {
392
+ for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
393
+ if ( c == EOF ) return 0;
394
+ if ( c == ')' )
395
+ shift(f, -1);
396
+ }
397
+ *sizep = size;
398
+ return ptr;
399
+ }
400
+
401
+
402
+ /* extract a =HHHxWWW size from the input stream
403
+ */
404
+ static int
405
+ linkysize(MMIOT *f, int *heightp, int *widthp)
406
+ {
407
+ int height=0, width=0;
408
+ int c;
409
+
410
+ *heightp = 0;
411
+ *widthp = 0;
412
+
413
+ if ( (c = eatspace(f)) != '=' )
414
+ return (c != EOF);
415
+ pull(f); /* eat '=' */
416
+
417
+ for ( c = pull(f); isdigit(c); c = pull(f))
418
+ width = (width * 10) + (c - '0');
419
+
420
+ if ( c == 'x' ) {
421
+ for ( c = pull(f); isdigit(c); c = pull(f))
422
+ height = (height*10) + (c - '0');
423
+
424
+ if ( c != EOF ) {
425
+ if ( !isspace(c) ) shift(f, -1);
426
+ *heightp = height;
427
+ *widthp = width;
428
+ return 1;
429
+ }
430
+ }
431
+ return 0;
432
+ }
433
+
434
+
435
+ /* extract a )-terminated title from the input stream.
436
+ */
437
+ static char*
438
+ linkytitle(MMIOT *f, int *sizep)
439
+ {
440
+ int countq=0, qc, c, size;
441
+ char *ret, *lastqc = 0;
442
+
443
+ eatspace(f);
444
+ if ( (qc=pull(f)) != '"' && qc != '\'' && qc != '(' )
445
+ return 0;
446
+
447
+ if ( qc == '(' ) qc = ')';
448
+
449
+ for ( ret = cursor(f); (c = pull(f)) != EOF; ) {
450
+ if ( (c == ')') && countq ) {
451
+ size = (lastqc ? lastqc : cursor(f)) - ret;
452
+ *sizep = size-1;
453
+ return ret;
454
+ }
455
+ else if ( c == qc ) {
456
+ lastqc = cursor(f);
457
+ countq++;
458
+ }
459
+ }
460
+ return 0;
461
+ }
462
+
463
+
464
+ /* look up (or construct) a footnote from the [xxx] link
465
+ * at the head of the stream.
466
+ */
467
+ static int
468
+ linkykey(int image, Footnote *val, MMIOT *f)
469
+ {
470
+ Footnote *ret;
471
+ Cstring mylabel;
472
+
473
+ memset(val, 0, sizeof *val);
474
+
475
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
476
+ return 0;
477
+
478
+ eatspace(f);
479
+ switch ( pull(f) ) {
480
+ case '(':
481
+ /* embedded link */
482
+ if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
483
+ return 0;
484
+
485
+ if ( image && !linkysize(f, &val->height, &val->width) )
486
+ return 0;
487
+
488
+ T(val->title) = linkytitle(f, &S(val->title));
489
+
490
+ return peek(f,0) == ')';
491
+
492
+ case '[':
493
+ /* footnote link */
494
+ mylabel = val->tag;
495
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
496
+ return 0;
497
+
498
+ if ( !S(val->tag) )
499
+ val->tag = mylabel;
500
+
501
+ ret = bsearch(val, T(*f->footnotes), S(*f->footnotes),
502
+ sizeof *val, (stfu)__mkd_footsort);
503
+
504
+ if ( ret ) {
505
+ val->tag = mylabel;
506
+ val->link = ret->link;
507
+ val->title = ret->title;
508
+ val->height = ret->height;
509
+ val->width = ret->width;
510
+ return 1;
511
+ }
512
+ }
513
+ return 0;
514
+ }
515
+
516
+
517
+ /*
518
+ * all the tag types that linkylinky can produce are
519
+ * defined by this structure.
520
+ */
521
+ typedef struct linkytype {
522
+ char *pat;
523
+ int szpat;
524
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
525
+ char *link_sfx; /* link suffix (eg: "\"" */
526
+ int WxH; /* this tag allows width x height arguments */
527
+ char *text_pfx; /* text prefix (eg: ">" */
528
+ char *text_sfx; /* text suffix (eg: "</a>" */
529
+ int flags; /* reparse flags */
530
+ } linkytype;
531
+
532
+ static linkytype imaget = { 0, 0, "<img src=\"", "\"",
533
+ 1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
534
+ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
535
+ 0, ">", "</a>", DENY_A };
536
+
537
+ /*
538
+ * pseudo-protocols for [][];
539
+ *
540
+ * id: generates <a id="link">tag</a>
541
+ * class: generates <span class="link">tag</span>
542
+ * raw: just dump the link without any processing
543
+ */
544
+ static linkytype specials[] = {
545
+ { "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
546
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
547
+ { "raw:", 4, 0, 0, 0, 0, 0, 0 },
548
+ } ;
549
+
550
+ #define NR(x) (sizeof x / sizeof x[0])
551
+
552
+ /* see if t contains one of our pseudo-protocols.
553
+ */
554
+ static linkytype *
555
+ extratag(Cstring t)
556
+ {
557
+ int i;
558
+ linkytype *r;
559
+
560
+ for ( i=0; i < NR(specials); i++ ) {
561
+ r = &specials[i];
562
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
563
+ return r;
564
+ }
565
+ return 0;
566
+ }
567
+
568
+
569
+ /*
570
+ * process embedded links and images
571
+ */
572
+ static int
573
+ linkylinky(int image, MMIOT *f)
574
+ {
575
+ int start = mmiottell(f);
576
+ Footnote link;
577
+ linkytype *tag;
578
+
579
+ if ( !(linkykey(image, &link, f) && S(link.tag)) ) {
580
+ mmiotseek(f, start);
581
+ return 0;
582
+ }
583
+
584
+ if ( image )
585
+ tag = &imaget;
586
+ else if ( (f->flags & NO_PSEUDO_PROTO) || (tag = extratag(link.link)) == 0 )
587
+ tag = &linkt;
588
+
589
+ if ( f->flags & tag-> flags ) {
590
+ mmiotseek(f, start);
591
+ return 0;
592
+ }
593
+
594
+ if ( tag->link_pfx ) {
595
+ Qstring(tag->link_pfx, f);
596
+ if ( f->base && (T(link.link)[tag->szpat] == '/') )
597
+ puturl(f->base, strlen(f->base), f);
598
+ puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
599
+ Qstring(tag->link_sfx, f);
600
+
601
+ if ( tag->WxH && link.height && link.width ) {
602
+ Qprintf(f," height=\"%d\"", link.height);
603
+ Qprintf(f, " width=\"%d\"", link.width);
604
+ }
605
+
606
+ if ( S(link.title) ) {
607
+ Qstring(" title=\"", f);
608
+ reparse(T(link.title), S(link.title), INSIDE_TAG, f);
609
+ Qchar('"', f);
610
+ }
611
+
612
+ Qstring(tag->text_pfx, f);
613
+ reparse(T(link.tag), S(link.tag), tag->flags, f);
614
+ Qstring(tag->text_sfx, f);
615
+ }
616
+ else
617
+ Qwrite(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
618
+
619
+ return 1;
620
+ }
621
+
622
+
623
+ /* write a character to output, doing text escapes ( & -> &amp;,
624
+ * > -> &gt; < -> &lt; )
625
+ */
626
+ static void
627
+ cputc(int c, MMIOT *f)
628
+ {
629
+ switch (c) {
630
+ case '&': Qstring("&amp;", f); break;
631
+ case '>': Qstring("&gt;", f); break;
632
+ case '<': Qstring("&lt;", f); break;
633
+ default : Qchar(c, f); break;
634
+ }
635
+ }
636
+
637
+
638
+ /*
639
+ * convert an email address to a string of nonsense
640
+ */
641
+ static void
642
+ mangle(char *s, int len, MMIOT *f)
643
+ {
644
+ while ( len-- > 0 ) {
645
+ Qstring("&#", f);
646
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
647
+ }
648
+ }
649
+
650
+
651
+ /* before letting a tag through, validate against
652
+ * DENY_A and DENY_IMG
653
+ */
654
+ static int
655
+ forbidden_tag(MMIOT *f)
656
+ {
657
+ int c = toupper(peek(f, 1));
658
+
659
+ if ( f->flags & DENY_HTML )
660
+ return 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, 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
+ { '\'', "'re>", "rsquo", 0 },
822
+ { '\'', "'ll>", "rsquo", 0 },
823
+ { '-', "--", "mdash", 1 },
824
+ { '-', "<->", "ndash", 0 },
825
+ { '.', "...", "hellip", 2 },
826
+ { '.', ". . .", "hellip", 4 },
827
+ { '(', "(c)", "copy", 2 },
828
+ { '(', "(r)", "reg", 2 },
829
+ { '(', "(tm)", "trade", 3 },
830
+ { '3', "<3/4>", "frac34", 2 },
831
+ { '3', "<3/4ths>", "frac34", 2 },
832
+ { '1', "<1/2>", "frac12", 2 },
833
+ { '1', "<1/4>", "frac14", 2 },
834
+ { '1', "<1/4th>", "frac14", 2 },
835
+ { '&', "&#0;", 0, 3 },
836
+ } ;
837
+ #define NRSMART ( sizeof smarties / sizeof smarties[0] )
838
+
839
+
840
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
841
+ */
842
+ static int
843
+ smartypants(int c, int *flags, MMIOT *f)
844
+ {
845
+ int i;
846
+
847
+ if ( f->flags & DENY_SMARTY )
848
+ return 0;
849
+
850
+ for ( i=0; i < NRSMART; i++)
851
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
852
+ if ( smarties[i].entity )
853
+ Qprintf(f, "&%s;", smarties[i].entity);
854
+ shift(f, smarties[i].shift);
855
+ return 1;
856
+ }
857
+
858
+ switch (c) {
859
+ case '<' : return 0;
860
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
861
+ break;
862
+
863
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
864
+ break;
865
+
866
+ case '`': if ( peek(f, 1) == '`' ) {
867
+ int j = 2;
868
+
869
+ while ( (c=peek(f,j)) != EOF ) {
870
+ if ( c == '\\' )
871
+ j += 2;
872
+ else if ( c == '`' )
873
+ break;
874
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
875
+ Qstring("&ldquo;", f);
876
+ reparse(cursor(f)+1, j-2, 0, f);
877
+ Qstring("&rdquo;", f);
878
+ shift(f,j+1);
879
+ return 1;
880
+ }
881
+ else ++j;
882
+ }
883
+
884
+ }
885
+ break;
886
+ }
887
+ return 0;
888
+ } /* smartypants */
889
+
890
+
891
+ #define tag_text(f) (f->flags & INSIDE_TAG)
892
+
893
+
894
+ static void
895
+ text(MMIOT *f)
896
+ {
897
+ int c, j;
898
+ int rep;
899
+ int smartyflags = 0;
900
+
901
+ while ( (c = pull(f)) != EOF ) {
902
+ if ( smartypants(c, &smartyflags, f) )
903
+ continue;
904
+ switch (c) {
905
+ case 0: break;
906
+
907
+ case '>': if ( tag_text(f) )
908
+ Qstring("&gt;", f);
909
+ else
910
+ Qchar(c, f);
911
+ break;
912
+
913
+ case '"': if ( tag_text(f) )
914
+ Qstring("&quot;", f);
915
+ else
916
+ Qchar(c, f);
917
+ break;
918
+
919
+ case '!': if ( peek(f,1) == '[' ) {
920
+ pull(f);
921
+ if ( tag_text(f) || !linkylinky(1, f) )
922
+ Qstring("![", f);
923
+ }
924
+ else
925
+ Qchar(c, f);
926
+ break;
927
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
928
+ Qchar(c, f);
929
+ break;
930
+ #if SUPERSCRIPT
931
+ case '^': if ( isthisspace(f,-1) || isthisspace(f,1) )
932
+ Qchar(c,f);
933
+ else {
934
+ char *sup = cursor(f);
935
+ int len = 0;
936
+ Qstring("<sup>",f);
937
+ while ( !isthisspace(f,1+len) ) {
938
+ ++len;
939
+ }
940
+ shift(f,len);
941
+ reparse(sup, len, 0, f);
942
+ Qstring("</sup>", f);
943
+ }
944
+ break;
945
+ #endif
946
+ case '_':
947
+ #if RELAXED_EMPHASIS
948
+ /* If RELAXED_EMPHASIS, underscores don't count when
949
+ * they're in the middle of a word.
950
+ */
951
+ if ( (isthisspace(f,-1) && isthisspace(f,1))
952
+ || (isalnum(peek(f,-1)) && isalnum(peek(f,1))) ) {
953
+ Qchar(c, f);
954
+ break;
955
+ }
956
+ /* else fall into the regular old emphasis case */
957
+ #endif
958
+ case '*': if ( tag_text(f) )
959
+ Qchar(c, f);
960
+ else {
961
+ for (rep = 1; peek(f,1) == c; pull(f) )
962
+ ++rep;
963
+ Qem(f,c,rep);
964
+ }
965
+ break;
966
+
967
+ case '`': if ( tag_text(f) )
968
+ Qchar(c, f);
969
+ else {
970
+ Qstring("<code>", f);
971
+ if ( peek(f, 1) == '`' ) {
972
+ pull(f);
973
+ code(2, f);
974
+ }
975
+ else
976
+ code(1, f);
977
+ Qstring("</code>", f);
978
+ }
979
+ break;
980
+
981
+ case '\\': switch ( c = pull(f) ) {
982
+ case '&': Qstring("&amp;", f);
983
+ break;
984
+ case '<': Qstring("&lt;", f);
985
+ break;
986
+ case '\\':
987
+ case '>': case '#': case '.': case '-':
988
+ case '+': case '{': case '}': case ']':
989
+ case '(': case ')': case '"': case '\'':
990
+ case '!': case '[': case '*': case '_':
991
+ case '`': Qchar(c, f);
992
+ break;
993
+ default:
994
+ Qchar('\\', f);
995
+ if ( c != EOF )
996
+ shift(f,-1);
997
+ break;
998
+ }
999
+ break;
1000
+
1001
+ case '<': if ( !maybe_tag_or_link(f) )
1002
+ Qstring("&lt;", f);
1003
+ break;
1004
+
1005
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
1006
+ while ( isalnum(peek(f,j)) )
1007
+ ++j;
1008
+
1009
+ if ( peek(f,j) != ';' )
1010
+ Qstring("&amp;", f);
1011
+ else
1012
+ Qchar(c, f);
1013
+ break;
1014
+
1015
+ default: Qchar(c, f);
1016
+ break;
1017
+ }
1018
+ }
1019
+ } /* text */
1020
+
1021
+
1022
+ static int
1023
+ endofcode(int escape, int offset, MMIOT *f)
1024
+ {
1025
+ switch (escape) {
1026
+ case 2: if ( peek(f, offset+1) == '`' ) {
1027
+ shift(f,1);
1028
+ case 1: shift(f,offset);
1029
+ return 1;
1030
+ }
1031
+ default:return 0;
1032
+ }
1033
+ }
1034
+
1035
+
1036
+ /* the only characters that have special meaning in a code block are
1037
+ * `<' and `&' , which are /always/ expanded to &lt; and &amp;
1038
+ */
1039
+ static void
1040
+ code(int escape, MMIOT *f)
1041
+ {
1042
+ int c;
1043
+
1044
+ if ( escape && (peek(f,1) == ' ') )
1045
+ shift(f,1);
1046
+
1047
+ while ( (c = pull(f)) != EOF ) {
1048
+ switch (c) {
1049
+ case ' ': if ( peek(f,1) == '`' && endofcode(escape, 1, f) )
1050
+ return;
1051
+ Qchar(c, f);
1052
+ break;
1053
+
1054
+ case '`': if ( endofcode(escape, 0, f) )
1055
+ return;
1056
+ Qchar(c, f);
1057
+ break;
1058
+
1059
+ case '\\': cputc(c, f);
1060
+ if ( peek(f,1) == '>' || (c = pull(f)) == EOF )
1061
+ break;
1062
+
1063
+ default: cputc(c, f);
1064
+ break;
1065
+ }
1066
+ }
1067
+ } /* code */
1068
+
1069
+
1070
+ /* print a header block
1071
+ */
1072
+ static void
1073
+ printheader(Paragraph *pp, MMIOT *f)
1074
+ {
1075
+ Qprintf(f, "<h%d>", pp->hnumber);
1076
+ push(T(pp->text->text), S(pp->text->text), f);
1077
+ text(f);
1078
+ Qprintf(f, "</h%d>", pp->hnumber);
1079
+ }
1080
+
1081
+
1082
+ static int
1083
+ printblock(Paragraph *pp, MMIOT *f)
1084
+ {
1085
+ Line *t = pp->text;
1086
+ static char *Begin[] = { "", "<p>", "<center>" };
1087
+ static char *End[] = { "", "</p>","</center>" };
1088
+
1089
+ while (t) {
1090
+ if ( S(t->text) ) {
1091
+ if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
1092
+ && T(t->text)[S(t->text)-1] == ' ') {
1093
+ push(T(t->text), S(t->text)-2, f);
1094
+ push("<br/>\n", 6, f);
1095
+ }
1096
+ else {
1097
+ push(T(t->text), S(t->text), f);
1098
+ if ( t->next )
1099
+ push("\n", 1, f);
1100
+ }
1101
+ }
1102
+ t = t->next;
1103
+ }
1104
+ Qstring(Begin[pp->align], f);
1105
+ text(f);
1106
+ Qstring(End[pp->align], f);
1107
+ return 1;
1108
+ }
1109
+
1110
+
1111
+ static void
1112
+ printcode(Line *t, MMIOT *f)
1113
+ {
1114
+ int blanks;
1115
+
1116
+ for ( blanks = 0; t ; t = t->next )
1117
+ if ( S(t->text) > t->dle ) {
1118
+ while ( blanks ) {
1119
+ push("\n", 1, f);
1120
+ --blanks;
1121
+ }
1122
+ push(T(t->text), S(t->text), f);
1123
+ push("\n", 1, f);
1124
+ }
1125
+ else blanks++;
1126
+
1127
+ Qstring("<pre><code>", f);
1128
+ code(0, f);
1129
+ Qstring("</code></pre>", f);
1130
+ }
1131
+
1132
+
1133
+ static void
1134
+ printhtml(Line *t, MMIOT *f)
1135
+ {
1136
+ int blanks;
1137
+
1138
+ for ( blanks=0; t ; t = t->next )
1139
+ if ( S(t->text) ) {
1140
+ for ( ; blanks; --blanks )
1141
+ Qchar('\n', f);
1142
+
1143
+ Qwrite(T(t->text), S(t->text), f);
1144
+ Qchar('\n', f);
1145
+ }
1146
+ else
1147
+ blanks++;
1148
+ }
1149
+
1150
+
1151
+ static void
1152
+ htmlify(Paragraph *p, char *block, MMIOT *f)
1153
+ {
1154
+ emblock(f);
1155
+ if ( block ) Qprintf(f, "<%s>", block);
1156
+ emblock(f);
1157
+
1158
+ while (( p = display(p, f) )) {
1159
+ emblock(f);
1160
+ Qstring("\n\n", f);
1161
+ }
1162
+
1163
+ if ( block ) Qprintf(f, "</%s>", block);
1164
+ emblock(f);
1165
+ }
1166
+
1167
+
1168
+ #if DL_TAG_EXTENSION
1169
+ static void
1170
+ definitionlist(Paragraph *p, MMIOT *f)
1171
+ {
1172
+ Line *tag;
1173
+
1174
+ if ( p ) {
1175
+ Qstring("<dl>\n", f);
1176
+
1177
+ for ( ; p ; p = p->next) {
1178
+ for ( tag = p->text; tag; tag = tag->next ) {
1179
+ Qstring("<dt>", f);
1180
+ reparse(T(tag->text), S(tag->text), 0, f);
1181
+ Qstring("</dt>\n", f);
1182
+ }
1183
+
1184
+ htmlify(p->down, "dd", f);
1185
+ }
1186
+
1187
+ Qstring("</dl>", f);
1188
+ }
1189
+ }
1190
+ #endif
1191
+
1192
+
1193
+ static void
1194
+ listdisplay(int typ, Paragraph *p, MMIOT* f)
1195
+ {
1196
+ if ( p ) {
1197
+ Qprintf(f, "<%cl>\n", (typ==UL)?'u':'o');
1198
+
1199
+ for ( ; p ; p = p->next ) {
1200
+ htmlify(p->down, "li", f);
1201
+ Qchar('\n', f);
1202
+ }
1203
+
1204
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
1205
+ }
1206
+ }
1207
+
1208
+
1209
+ /* dump out a Paragraph in the desired manner
1210
+ */
1211
+ static Paragraph*
1212
+ display(Paragraph *p, MMIOT *f)
1213
+ {
1214
+ if ( !p ) return 0;
1215
+
1216
+ switch ( p->typ ) {
1217
+ case STYLE:
1218
+ case WHITESPACE:
1219
+ break;
1220
+
1221
+ case HTML:
1222
+ printhtml(p->text, f);
1223
+ break;
1224
+
1225
+ case CODE:
1226
+ printcode(p->text, f);
1227
+ break;
1228
+
1229
+ case QUOTE:
1230
+ htmlify(p->down, "blockquote", f);
1231
+ break;
1232
+
1233
+ case UL:
1234
+ case OL:
1235
+ listdisplay(p->typ, p->down, f);
1236
+ break;
1237
+
1238
+ #if DL_TAG_EXTENSION
1239
+ case DL:
1240
+ definitionlist(p->down, f);
1241
+ break;
1242
+ #endif
1243
+
1244
+ case HR:
1245
+ Qstring("<hr />", f);
1246
+ break;
1247
+
1248
+ case HDR:
1249
+ printheader(p, f);
1250
+ break;
1251
+
1252
+ default:
1253
+ printblock(p, f);
1254
+ break;
1255
+ }
1256
+ return p->next;
1257
+ }
1258
+
1259
+
1260
+ /*
1261
+ * dump out stylesheet sections.
1262
+ */
1263
+ static int
1264
+ stylesheets(Paragraph *p, FILE *f)
1265
+ {
1266
+ Line* q;
1267
+
1268
+ for ( ; p ; p = p->next ) {
1269
+ if ( p->typ == STYLE ) {
1270
+ for ( q = p->text; q ; q = q->next )
1271
+ if ( fwrite(T(q->text), S(q->text), 1, f) == 1 )
1272
+ putc('\n', f);
1273
+ else
1274
+ return EOF;
1275
+ }
1276
+ if ( p->down && (stylesheets(p->down, f) == EOF) )
1277
+ return EOF;
1278
+ }
1279
+ return 0;
1280
+ }
1281
+
1282
+
1283
+ /* return a pointer to the compiled markdown
1284
+ * document.
1285
+ */
1286
+ int
1287
+ mkd_document(Document *p, char **res)
1288
+ {
1289
+ if ( p && p->compiled ) {
1290
+ if ( ! p->html ) {
1291
+ htmlify(p->code, 0, p->ctx);
1292
+ p->html = 1;
1293
+ }
1294
+
1295
+ *res = T(p->ctx->out);
1296
+ return S(p->ctx->out);
1297
+ }
1298
+ return EOF;
1299
+ }
1300
+
1301
+
1302
+ /* public interface for reparse()
1303
+ */
1304
+ int
1305
+ mkd_text(char *bfr, int size, FILE *output, int flags)
1306
+ {
1307
+ MMIOT f;
1308
+
1309
+ ___mkd_initmmiot(&f, 0);
1310
+ f.flags = flags & USER_FLAGS;
1311
+
1312
+ reparse(bfr, size, 0, &f);
1313
+ emblock(&f);
1314
+ if ( flags & CDATA_OUTPUT )
1315
+ ___mkd_xml(T(f.out), S(f.out), output);
1316
+ else
1317
+ fwrite(T(f.out), S(f.out), 1, output);
1318
+
1319
+ ___mkd_freemmiot(&f, 0);
1320
+ return 0;
1321
+ }
1322
+
1323
+
1324
+ /* dump any embedded styles
1325
+ */
1326
+ int
1327
+ mkd_style(Document *d, FILE *f)
1328
+ {
1329
+ if ( d && d->compiled )
1330
+ return stylesheets(d->code, f);
1331
+ return EOF;
1332
+ }
1333
+