rdiscount_wm 3.0.0.0

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/markdown.c ADDED
@@ -0,0 +1,1334 @@
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 "config.h"
8
+
9
+ #include <stdio.h>
10
+ #include <string.h>
11
+ #include <stdarg.h>
12
+ #include <stdlib.h>
13
+ #include <time.h>
14
+ #include <ctype.h>
15
+
16
+ #include "cstring.h"
17
+ #include "markdown.h"
18
+ #include "amalloc.h"
19
+ #include "tags.h"
20
+
21
+ typedef int (*stfu)(const void*,const void*);
22
+
23
+ typedef ANCHOR(Paragraph) ParagraphRoot;
24
+
25
+ static Paragraph *Pp(ParagraphRoot *, Line *, int);
26
+ static Paragraph *compile(Line *, int, MMIOT *);
27
+
28
+ /* case insensitive string sort for Footnote tags.
29
+ */
30
+ int
31
+ __mkd_footsort(Footnote *a, Footnote *b)
32
+ {
33
+ int i;
34
+ char ac, bc;
35
+
36
+ if ( S(a->tag) != S(b->tag) )
37
+ return S(a->tag) - S(b->tag);
38
+
39
+ for ( i=0; i < S(a->tag); i++) {
40
+ ac = tolower(T(a->tag)[i]);
41
+ bc = tolower(T(b->tag)[i]);
42
+
43
+ if ( isspace(ac) && isspace(bc) )
44
+ continue;
45
+ if ( ac != bc )
46
+ return ac - bc;
47
+ }
48
+ return 0;
49
+ }
50
+
51
+
52
+ /* find the first blank character after position <i>
53
+ */
54
+ static int
55
+ nextblank(Line *t, int i)
56
+ {
57
+ while ( (i < S(t->text)) && !isspace(T(t->text)[i]) )
58
+ ++i;
59
+ return i;
60
+ }
61
+
62
+
63
+ /* find the next nonblank character after position <i>
64
+ */
65
+ static int
66
+ nextnonblank(Line *t, int i)
67
+ {
68
+ while ( (i < S(t->text)) && isspace(T(t->text)[i]) )
69
+ ++i;
70
+ return i;
71
+ }
72
+
73
+
74
+ /* find the first nonblank character on the Line.
75
+ */
76
+ int
77
+ mkd_firstnonblank(Line *p)
78
+ {
79
+ return nextnonblank(p,0);
80
+ }
81
+
82
+
83
+ static inline int
84
+ blankline(Line *p)
85
+ {
86
+ return ! (p && (S(p->text) > p->dle) );
87
+ }
88
+
89
+
90
+ static Line *
91
+ skipempty(Line *p)
92
+ {
93
+ while ( p && (p->dle == S(p->text)) )
94
+ p = p->next;
95
+ return p;
96
+ }
97
+
98
+
99
+ void
100
+ ___mkd_tidy(Cstring *t)
101
+ {
102
+ while ( S(*t) && isspace(T(*t)[S(*t)-1]) )
103
+ --S(*t);
104
+ }
105
+
106
+
107
+ static struct kw comment = { "!--", 3, 0 };
108
+
109
+ static struct kw *
110
+ isopentag(Line *p)
111
+ {
112
+ int i=0, len;
113
+ char *line;
114
+
115
+ if ( !p ) return 0;
116
+
117
+ line = T(p->text);
118
+ len = S(p->text);
119
+
120
+ if ( len < 3 || line[0] != '<' )
121
+ return 0;
122
+
123
+ if ( line[1] == '!' && line[2] == '-' && line[3] == '-' )
124
+ /* comments need special case handling, because
125
+ * the !-- doesn't need to end in a whitespace
126
+ */
127
+ return &comment;
128
+
129
+ /* find how long the tag is so we can check to see if
130
+ * it's a block-level tag
131
+ */
132
+ for ( i=1; i < len && T(p->text)[i] != '>'
133
+ && T(p->text)[i] != '/'
134
+ && !isspace(T(p->text)[i]); ++i )
135
+ ;
136
+
137
+
138
+ return mkd_search_tags(T(p->text)+1, i-1);
139
+ }
140
+
141
+
142
+ typedef struct _flo {
143
+ Line *t;
144
+ int i;
145
+ } FLO;
146
+
147
+ #define floindex(x) (x.i)
148
+
149
+
150
+ static int
151
+ flogetc(FLO *f)
152
+ {
153
+ if ( f && f->t ) {
154
+ if ( f->i < S(f->t->text) )
155
+ return T(f->t->text)[f->i++];
156
+ f->t = f->t->next;
157
+ f->i = 0;
158
+ return flogetc(f);
159
+ }
160
+ return EOF;
161
+ }
162
+
163
+
164
+ static void
165
+ splitline(Line *t, int cutpoint)
166
+ {
167
+ if ( t && (cutpoint < S(t->text)) ) {
168
+ Line *tmp = calloc(1, sizeof *tmp);
169
+
170
+ tmp->next = t->next;
171
+ t->next = tmp;
172
+
173
+ tmp->dle = t->dle;
174
+ SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
175
+ S(t->text) = cutpoint;
176
+ }
177
+ }
178
+
179
+ #define UNCHECK(l) ((l)->flags &= ~CHECKED)
180
+ #define UNLESS_FENCED(t) if (fenced) { \
181
+ other = 1; l->count += (c == ' ' ? 0 : -1); \
182
+ } else { t; }
183
+
184
+ /*
185
+ * walk a line, seeing if it's any of half a dozen interesting regular
186
+ * types.
187
+ */
188
+ static void
189
+ checkline(Line *l)
190
+ {
191
+ int eol, i;
192
+ int dashes = 0, spaces = 0,
193
+ equals = 0, underscores = 0,
194
+ stars = 0, tildes = 0, other = 0,
195
+ backticks = 0, fenced = 0;
196
+
197
+ l->flags |= CHECKED;
198
+ l->kind = chk_text;
199
+ l->count = 0;
200
+
201
+ if (l->dle >= 4) { l->kind=chk_code; return; }
202
+
203
+ for ( eol = S(l->text); eol > l->dle && isspace(T(l->text)[eol-1]); --eol )
204
+ ;
205
+
206
+ for (i=l->dle; i<eol; i++) {
207
+ register int c = T(l->text)[i];
208
+
209
+ if ( c != ' ' ) l->count++;
210
+
211
+ switch (c) {
212
+ case '-': UNLESS_FENCED(dashes = 1); break;
213
+ case ' ': UNLESS_FENCED(spaces = 1); break;
214
+ case '=': equals = 1; break;
215
+ case '_': UNLESS_FENCED(underscores = 1); break;
216
+ case '*': stars = 1; break;
217
+ #if WITH_FENCED_CODE
218
+ case '~': if (other) return; fenced = 1; tildes = 1; break;
219
+ case '`': if (other) return; fenced = 1; backticks = 1; break;
220
+ #endif
221
+ default:
222
+ other = 1;
223
+ l->count--;
224
+ if (!fenced) return;
225
+ }
226
+ }
227
+
228
+ if ( dashes + equals + underscores + stars + tildes + backticks > 1 )
229
+ return;
230
+
231
+ if ( spaces ) {
232
+ if ( (underscores || stars || dashes) )
233
+ l->kind = chk_hr;
234
+ return;
235
+ }
236
+
237
+ if ( stars || underscores ) { l->kind = chk_hr; }
238
+ else if ( dashes ) { l->kind = chk_dash; }
239
+ else if ( equals ) { l->kind = chk_equal; }
240
+ #if WITH_FENCED_CODE
241
+ else if ( tildes ) { l->kind = chk_tilde; }
242
+ else if ( backticks ) { l->kind = chk_backtick; }
243
+ #endif
244
+ }
245
+
246
+
247
+
248
+ static Line *
249
+ commentblock(Paragraph *p, int *unclosed)
250
+ {
251
+ Line *t, *ret;
252
+ char *end;
253
+
254
+ for ( t = p->text; t ; t = t->next) {
255
+ if ( end = strstr(T(t->text), "-->") ) {
256
+ splitline(t, 3 + (end - T(t->text)) );
257
+ ret = t->next;
258
+ t->next = 0;
259
+ return ret;
260
+ }
261
+ }
262
+ *unclosed = 1;
263
+ return t;
264
+
265
+ }
266
+
267
+
268
+ static Line *
269
+ htmlblock(Paragraph *p, struct kw *tag, int *unclosed)
270
+ {
271
+ Line *ret;
272
+ FLO f = { p->text, 0 };
273
+ int c;
274
+ int i, closing, depth=0;
275
+
276
+ *unclosed = 0;
277
+
278
+ if ( tag == &comment )
279
+ return commentblock(p, unclosed);
280
+
281
+ if ( tag->selfclose ) {
282
+ ret = f.t->next;
283
+ f.t->next = 0;
284
+ return ret;
285
+ }
286
+
287
+ while ( (c = flogetc(&f)) != EOF ) {
288
+ if ( c == '<' ) {
289
+ /* tag? */
290
+ c = flogetc(&f);
291
+ if ( c == '!' ) { /* comment? */
292
+ if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) {
293
+ /* yes */
294
+ while ( (c = flogetc(&f)) != EOF ) {
295
+ if ( c == '-' && flogetc(&f) == '-'
296
+ && flogetc(&f) == '>')
297
+ /* consumed whole comment */
298
+ break;
299
+ }
300
+ }
301
+ }
302
+ else {
303
+ if ( closing = (c == '/') ) c = flogetc(&f);
304
+
305
+ for ( i=0; i < tag->size; c=flogetc(&f) ) {
306
+ if ( tag->id[i++] != toupper(c) )
307
+ break;
308
+ }
309
+
310
+ if ( (i == tag->size) && !isalnum(c) ) {
311
+ depth = depth + (closing ? -1 : 1);
312
+ if ( depth == 0 ) {
313
+ while ( c != EOF && c != '>' ) {
314
+ /* consume trailing gunk in close tag */
315
+ c = flogetc(&f);
316
+ }
317
+ if ( c == EOF )
318
+ break;
319
+ if ( !f.t )
320
+ return 0;
321
+ splitline(f.t, floindex(f));
322
+ ret = f.t->next;
323
+ f.t->next = 0;
324
+ return ret;
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ *unclosed = 1;
331
+ return 0;
332
+ }
333
+
334
+
335
+ /* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
336
+ */
337
+ static int
338
+ isfootnote(Line *t)
339
+ {
340
+ int i;
341
+
342
+ if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') )
343
+ return 0;
344
+
345
+ for ( ++i; i < S(t->text) ; ++i ) {
346
+ if ( T(t->text)[i] == '[' )
347
+ return 0;
348
+ else if ( T(t->text)[i] == ']' )
349
+ return ( T(t->text)[i+1] == ':' ) ;
350
+ }
351
+ return 0;
352
+ }
353
+
354
+
355
+ static inline int
356
+ isquote(Line *t)
357
+ {
358
+ return (t->dle < 4 && T(t->text)[t->dle] == '>');
359
+ }
360
+
361
+
362
+ static inline int
363
+ iscode(Line *t)
364
+ {
365
+ return (t->dle >= 4);
366
+ }
367
+
368
+
369
+ static inline int
370
+ ishr(Line *t)
371
+ {
372
+ if ( ! (t->flags & CHECKED) )
373
+ checkline(t);
374
+
375
+ if ( t->count > 2 )
376
+ return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal;
377
+ return 0;
378
+ }
379
+
380
+
381
+ static int
382
+ issetext(Line *t, int *htyp)
383
+ {
384
+ Line *n;
385
+
386
+ /* check for setext-style HEADER
387
+ * ======
388
+ */
389
+
390
+ if ( (n = t->next) ) {
391
+ if ( !(n->flags & CHECKED) )
392
+ checkline(n);
393
+
394
+ if ( n->kind == chk_dash || n->kind == chk_equal ) {
395
+ *htyp = SETEXT;
396
+ return 1;
397
+ }
398
+ }
399
+ return 0;
400
+ }
401
+
402
+
403
+ static int
404
+ ishdr(Line *t, int *htyp)
405
+ {
406
+ /* ANY leading `#`'s make this into an ETX header
407
+ */
408
+ if ( (t->dle == 0) && (S(t->text) > 1) && (T(t->text)[0] == '#') ) {
409
+ *htyp = ETX;
410
+ return 1;
411
+ }
412
+
413
+ /* And if not, maybe it's a SETEXT header instead
414
+ */
415
+ return issetext(t, htyp);
416
+ }
417
+
418
+
419
+ static inline int
420
+ end_of_block(Line *t)
421
+ {
422
+ int dummy;
423
+
424
+ if ( !t )
425
+ return 0;
426
+
427
+ return ( (S(t->text) <= t->dle) || ishr(t) || ishdr(t, &dummy) );
428
+ }
429
+
430
+
431
+ static Line*
432
+ is_discount_dt(Line *t, int *clip)
433
+ {
434
+ #if USE_DISCOUNT_DL
435
+ if ( t && t->next
436
+ && (S(t->text) > 2)
437
+ && (t->dle == 0)
438
+ && (T(t->text)[0] == '=')
439
+ && (T(t->text)[S(t->text)-1] == '=') ) {
440
+ if ( t->next->dle >= 4 ) {
441
+ *clip = 4;
442
+ return t;
443
+ }
444
+ else
445
+ return is_discount_dt(t->next, clip);
446
+ }
447
+ #endif
448
+ return 0;
449
+ }
450
+
451
+
452
+ static int
453
+ is_extra_dd(Line *t)
454
+ {
455
+ return (t->dle < 4) && (T(t->text)[t->dle] == ':')
456
+ && isspace(T(t->text)[t->dle+1]);
457
+ }
458
+
459
+
460
+ static Line*
461
+ is_extra_dt(Line *t, int *clip)
462
+ {
463
+ #if USE_EXTRA_DL
464
+
465
+ if ( t && t->next && S(t->text) && T(t->text)[0] != '='
466
+ && T(t->text)[S(t->text)-1] != '=') {
467
+ Line *x;
468
+
469
+ if ( iscode(t) || end_of_block(t) )
470
+ return 0;
471
+
472
+ if ( (x = skipempty(t->next)) && is_extra_dd(x) ) {
473
+ *clip = x->dle+2;
474
+ return t;
475
+ }
476
+
477
+ if ( x=is_extra_dt(t->next, clip) )
478
+ return x;
479
+ }
480
+ #endif
481
+ return 0;
482
+ }
483
+
484
+
485
+ static Line*
486
+ isdefinition(Line *t, int *clip, int *kind)
487
+ {
488
+ Line *ret;
489
+
490
+ *kind = 1;
491
+ if ( ret = is_discount_dt(t,clip) )
492
+ return ret;
493
+
494
+ *kind=2;
495
+ return is_extra_dt(t,clip);
496
+ }
497
+
498
+
499
+ static int
500
+ islist(Line *t, int *clip, DWORD flags, int *list_type)
501
+ {
502
+ int i, j;
503
+ char *q;
504
+
505
+ if ( end_of_block(t) )
506
+ return 0;
507
+
508
+ if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type) )
509
+ return DL;
510
+
511
+ if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
512
+ i = nextnonblank(t, t->dle+1);
513
+ *clip = (i > 4) ? 4 : i;
514
+ *list_type = UL;
515
+ return AL;
516
+ }
517
+
518
+ if ( (j = nextblank(t,t->dle)) > t->dle ) {
519
+ if ( T(t->text)[j-1] == '.' ) {
520
+
521
+ if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT))
522
+ && (j == t->dle + 2)
523
+ && isalpha(T(t->text)[t->dle]) ) {
524
+ j = nextnonblank(t,j);
525
+ *clip = (j > 4) ? 4 : j;
526
+ *list_type = AL;
527
+ return AL;
528
+ }
529
+
530
+ strtoul(T(t->text)+t->dle, &q, 10);
531
+ if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) {
532
+ j = nextnonblank(t,j);
533
+ /* *clip = j; */
534
+ *clip = (j > 4) ? 4 : j;
535
+ *list_type = OL;
536
+ return AL;
537
+ }
538
+ }
539
+ }
540
+ return 0;
541
+ }
542
+
543
+
544
+ static Line *
545
+ headerblock(Paragraph *pp, int htyp)
546
+ {
547
+ Line *ret = 0;
548
+ Line *p = pp->text;
549
+ int i, j;
550
+
551
+ switch (htyp) {
552
+ case SETEXT:
553
+ /* p->text is header, p->next->text is -'s or ='s
554
+ */
555
+ pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2;
556
+
557
+ ret = p->next->next;
558
+ ___mkd_freeLine(p->next);
559
+ p->next = 0;
560
+ break;
561
+
562
+ case ETX:
563
+ /* p->text is ###header###, so we need to trim off
564
+ * the leading and trailing `#`'s
565
+ */
566
+
567
+ for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1)
568
+ && (i < 6); i++)
569
+ ;
570
+
571
+ pp->hnumber = i;
572
+
573
+ while ( (i < S(p->text)) && isspace(T(p->text)[i]) )
574
+ ++i;
575
+
576
+ CLIP(p->text, 0, i);
577
+ UNCHECK(p);
578
+
579
+ for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j)
580
+ ;
581
+
582
+ while ( j && isspace(T(p->text)[j-1]) )
583
+ --j;
584
+
585
+ S(p->text) = j;
586
+
587
+ ret = p->next;
588
+ p->next = 0;
589
+ break;
590
+ }
591
+ return ret;
592
+ }
593
+
594
+
595
+ static Line *
596
+ codeblock(Paragraph *p)
597
+ {
598
+ Line *t = p->text, *r;
599
+
600
+ for ( ; t; t = r ) {
601
+ CLIP(t->text,0,4);
602
+ t->dle = mkd_firstnonblank(t);
603
+
604
+ if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
605
+ ___mkd_freeLineRange(t,r);
606
+ t->next = 0;
607
+ return r;
608
+ }
609
+ }
610
+ return t;
611
+ }
612
+
613
+
614
+ #ifdef WITH_FENCED_CODE
615
+ static int
616
+ iscodefence(Line *r, int size, line_type kind)
617
+ {
618
+ if ( !(r->flags & CHECKED) )
619
+ checkline(r);
620
+
621
+ if ( kind )
622
+ return (r->kind == kind) && (r->count >= size);
623
+ else
624
+ return (r->kind == chk_tilde || r->kind == chk_backtick) && (r->count >= size);
625
+ }
626
+
627
+ static Paragraph *
628
+ fencedcodeblock(ParagraphRoot *d, Line **ptr)
629
+ {
630
+ Line *first, *r;
631
+ Paragraph *ret;
632
+
633
+ first = (*ptr);
634
+
635
+ /* don't allow zero-length code fences
636
+ */
637
+ if ( (first->next == 0) || iscodefence(first->next, first->count, 0) )
638
+ return 0;
639
+
640
+ /* find the closing fence, discard the fences,
641
+ * return a Paragraph with the contents
642
+ */
643
+ for ( r = first; r && r->next; r = r->next )
644
+ if ( iscodefence(r->next, first->count, first->kind) ) {
645
+ (*ptr) = r->next->next;
646
+ ret = Pp(d, first->next, CODE);
647
+ if (S(first->text) - first->count > 0) {
648
+ char *lang_attr = T(first->text) + first->count;
649
+ while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
650
+ ret->lang = strdup(lang_attr);
651
+ }
652
+ else {
653
+ ret->lang = 0;
654
+ }
655
+ ___mkd_freeLine(first);
656
+ ___mkd_freeLine(r->next);
657
+ r->next = 0;
658
+ return ret;
659
+ }
660
+ return 0;
661
+ }
662
+ #endif
663
+
664
+
665
+ static int
666
+ centered(Line *first, Line *last)
667
+ {
668
+
669
+ if ( first&&last ) {
670
+ int len = S(last->text);
671
+
672
+ if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0)
673
+ && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) {
674
+ CLIP(first->text, 0, 2);
675
+ S(last->text) -= 2;
676
+ return CENTER;
677
+ }
678
+ }
679
+ return 0;
680
+ }
681
+
682
+
683
+ static int
684
+ endoftextblock(Line *t, int toplevelblock, DWORD flags)
685
+ {
686
+ int z;
687
+
688
+ if ( end_of_block(t) || isquote(t) )
689
+ return 1;
690
+
691
+ /* HORRIBLE STANDARDS KLUDGES:
692
+ * 1. non-toplevel paragraphs absorb adjacent code blocks
693
+ * 2. Toplevel paragraphs eat absorb adjacent list items,
694
+ * but sublevel blocks behave properly.
695
+ * (What this means is that we only need to check for code
696
+ * blocks at toplevel, and only check for list items at
697
+ * nested levels.)
698
+ */
699
+ return toplevelblock ? 0 : islist(t,&z,flags,&z);
700
+ }
701
+
702
+
703
+ static Line *
704
+ textblock(Paragraph *p, int toplevel, DWORD flags)
705
+ {
706
+ Line *t, *next;
707
+
708
+ for ( t = p->text; t ; t = next ) {
709
+ if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) {
710
+ p->align = centered(p->text, t);
711
+ t->next = 0;
712
+ return next;
713
+ }
714
+ }
715
+ return t;
716
+ }
717
+
718
+
719
+ /* length of the id: or class: kind in a special div-not-quote block
720
+ */
721
+ static int
722
+ szmarkerclass(char *p)
723
+ {
724
+ if ( strncasecmp(p, "id:", 3) == 0 )
725
+ return 3;
726
+ if ( strncasecmp(p, "class:", 6) == 0 )
727
+ return 6;
728
+ return 0;
729
+ }
730
+
731
+
732
+ /*
733
+ * check if the first line of a quoted block is the special div-not-quote
734
+ * marker %[kind:]name%
735
+ */
736
+ #define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') )
737
+
738
+ static int
739
+ isdivmarker(Line *p, int start, DWORD flags)
740
+ {
741
+ char *s;
742
+ int last, i;
743
+
744
+ if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
745
+ return 0;
746
+
747
+ start = nextnonblank(p, start);
748
+ last= S(p->text) - (1 + start);
749
+ s = T(p->text) + start;
750
+
751
+ if ( (last <= 0) || (*s != '%') || (s[last] != '%') )
752
+ return 0;
753
+
754
+ i = szmarkerclass(s+1);
755
+
756
+ if ( !iscsschar(s[i+1]) )
757
+ return 0;
758
+ while ( ++i < last )
759
+ if ( !(isdigit(s[i]) || iscsschar(s[i])) )
760
+ return 0;
761
+
762
+ return 1;
763
+ }
764
+
765
+
766
+ /*
767
+ * accumulate a blockquote.
768
+ *
769
+ * one sick horrible thing about blockquotes is that even though
770
+ * it just takes ^> to start a quote, following lines, if quoted,
771
+ * assume that the prefix is ``> ''. This means that code needs
772
+ * to be indented *5* spaces from the leading '>', but *4* spaces
773
+ * from the start of the line. This does not appear to be
774
+ * documented in the reference implementation, but it's the
775
+ * way the markdown sample web form at Daring Fireball works.
776
+ */
777
+ static Line *
778
+ quoteblock(Paragraph *p, DWORD flags)
779
+ {
780
+ Line *t, *q;
781
+ int qp;
782
+
783
+ for ( t = p->text; t ; t = q ) {
784
+ if ( isquote(t) ) {
785
+ /* clip leading spaces */
786
+ for (qp = 0; T(t->text)[qp] != '>'; qp ++)
787
+ /* assert: the first nonblank character on this line
788
+ * will be a >
789
+ */;
790
+ /* clip '>' */
791
+ qp++;
792
+ /* clip next space, if any */
793
+ if ( T(t->text)[qp] == ' ' )
794
+ qp++;
795
+ CLIP(t->text, 0, qp);
796
+ UNCHECK(t);
797
+ t->dle = mkd_firstnonblank(t);
798
+ }
799
+
800
+ q = skipempty(t->next);
801
+
802
+ if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1,flags))) ) {
803
+ ___mkd_freeLineRange(t, q);
804
+ t = q;
805
+ break;
806
+ }
807
+ }
808
+ if ( isdivmarker(p->text,0,flags) ) {
809
+ char *prefix = "class";
810
+ int i;
811
+
812
+ q = p->text;
813
+ p->text = p->text->next;
814
+
815
+ if ( (i = szmarkerclass(1+T(q->text))) == 3 )
816
+ /* and this would be an "%id:" prefix */
817
+ prefix="id";
818
+
819
+ if ( p->ident = malloc(4+strlen(prefix)+S(q->text)) )
820
+ sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2),
821
+ T(q->text)+(i+1) );
822
+
823
+ ___mkd_freeLine(q);
824
+ }
825
+ return t;
826
+ }
827
+
828
+
829
+ typedef int (*linefn)(Line *);
830
+
831
+
832
+ /*
833
+ * pull in a list block. A list block starts with a list marker and
834
+ * runs until the next list marker, the next non-indented paragraph,
835
+ * or EOF. You do not have to indent nonblank lines after the list
836
+ * marker, but multiple paragraphs need to start with a 4-space indent.
837
+ */
838
+ static Line *
839
+ listitem(Paragraph *p, int indent, DWORD flags, linefn check)
840
+ {
841
+ Line *t, *q;
842
+ int clip = indent;
843
+ int z;
844
+
845
+ for ( t = p->text; t ; t = q) {
846
+ CLIP(t->text, 0, clip);
847
+ UNCHECK(t);
848
+ t->dle = mkd_firstnonblank(t);
849
+
850
+ if ( (q = skipempty(t->next)) == 0 ) {
851
+ ___mkd_freeLineRange(t,q);
852
+ return 0;
853
+ }
854
+
855
+ /* after a blank line, the next block needs to start with a line
856
+ * that's indented 4(? -- reference implementation allows a 1
857
+ * character indent, but that has unfortunate side effects here)
858
+ * spaces, but after that the line doesn't need any indentation
859
+ */
860
+ if ( q != t->next ) {
861
+ if (q->dle < indent) {
862
+ q = t->next;
863
+ t->next = 0;
864
+ return q;
865
+ }
866
+ /* indent at least 2, and at most as
867
+ * as far as the initial line was indented. */
868
+ indent = clip ? clip : 2;
869
+ }
870
+
871
+ if ( (q->dle < indent) && (ishr(q) || islist(q,&z,flags,&z)
872
+ || (check && (*check)(q)))
873
+ && !issetext(q,&z) ) {
874
+ q = t->next;
875
+ t->next = 0;
876
+ return q;
877
+ }
878
+
879
+ clip = (q->dle > indent) ? indent : q->dle;
880
+ }
881
+ return t;
882
+ }
883
+
884
+
885
+ static Line *
886
+ definition_block(Paragraph *top, int clip, MMIOT *f, int kind)
887
+ {
888
+ ParagraphRoot d = { 0, 0 };
889
+ Paragraph *p;
890
+ Line *q = top->text, *text = 0, *labels;
891
+ int z, para;
892
+
893
+ while (( labels = q )) {
894
+
895
+ if ( (q = isdefinition(labels, &z, &kind)) == 0 )
896
+ break;
897
+
898
+ if ( (text = skipempty(q->next)) == 0 )
899
+ break;
900
+
901
+ if ( para = (text != q->next) )
902
+ ___mkd_freeLineRange(q, text);
903
+
904
+ q->next = 0;
905
+ if ( kind == 1 /* discount dl */ )
906
+ for ( q = labels; q; q = q->next ) {
907
+ CLIP(q->text, 0, 1);
908
+ UNCHECK(q);
909
+ S(q->text)--;
910
+ }
911
+
912
+ dd_block:
913
+ p = Pp(&d, text, LISTITEM);
914
+
915
+ text = listitem(p, clip, f->flags, (kind==2) ? is_extra_dd : 0);
916
+ p->down = compile(p->text, 0, f);
917
+ p->text = labels; labels = 0;
918
+
919
+ if ( para && p->down ) p->down->align = PARA;
920
+
921
+ if ( (q = skipempty(text)) == 0 )
922
+ break;
923
+
924
+ if ( para = (q != text) ) {
925
+ Line anchor;
926
+
927
+ anchor.next = text;
928
+ ___mkd_freeLineRange(&anchor,q);
929
+ text = q;
930
+
931
+ }
932
+
933
+ if ( kind == 2 && is_extra_dd(q) )
934
+ goto dd_block;
935
+ }
936
+ top->text = 0;
937
+ top->down = T(d);
938
+ return text;
939
+ }
940
+
941
+
942
+ static Line *
943
+ enumerated_block(Paragraph *top, int clip, MMIOT *f, int list_class)
944
+ {
945
+ ParagraphRoot d = { 0, 0 };
946
+ Paragraph *p;
947
+ Line *q = top->text, *text;
948
+ int para = 0, z;
949
+
950
+ while (( text = q )) {
951
+
952
+ p = Pp(&d, text, LISTITEM);
953
+ text = listitem(p, clip, f->flags, 0);
954
+
955
+ p->down = compile(p->text, 0, f);
956
+ p->text = 0;
957
+
958
+ if ( para && p->down ) p->down->align = PARA;
959
+
960
+ if ( (q = skipempty(text)) == 0
961
+ || islist(q, &clip, f->flags, &z) != list_class )
962
+ break;
963
+
964
+ if ( para = (q != text) ) {
965
+ Line anchor;
966
+
967
+ anchor.next = text;
968
+ ___mkd_freeLineRange(&anchor, q);
969
+
970
+ if ( p->down ) p->down->align = PARA;
971
+ }
972
+ }
973
+ top->text = 0;
974
+ top->down = T(d);
975
+ return text;
976
+ }
977
+
978
+
979
+ static int
980
+ tgood(char c)
981
+ {
982
+ switch (c) {
983
+ case '\'':
984
+ case '"': return c;
985
+ case '(': return ')';
986
+ }
987
+ return 0;
988
+ }
989
+
990
+
991
+ /*
992
+ * add a new (image or link) footnote to the footnote table
993
+ */
994
+ static Line*
995
+ addfootnote(Line *p, MMIOT* f)
996
+ {
997
+ int j, i;
998
+ int c;
999
+ Line *np = p->next;
1000
+
1001
+ Footnote *foot = &EXPAND(*f->footnotes);
1002
+
1003
+ CREATE(foot->tag);
1004
+ CREATE(foot->link);
1005
+ CREATE(foot->title);
1006
+ foot->flags = foot->height = foot->width = 0;
1007
+
1008
+ for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
1009
+ EXPAND(foot->tag) = T(p->text)[j];
1010
+
1011
+ EXPAND(foot->tag) = 0;
1012
+ S(foot->tag)--;
1013
+ j = nextnonblank(p, j+2);
1014
+
1015
+ if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
1016
+ while ( j < S(p->text) )
1017
+ EXPAND(foot->title) = T(p->text)[j++];
1018
+ goto skip_to_end;
1019
+ }
1020
+
1021
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
1022
+ EXPAND(foot->link) = T(p->text)[j++];
1023
+ EXPAND(foot->link) = 0;
1024
+ S(foot->link)--;
1025
+ j = nextnonblank(p,j);
1026
+
1027
+ if ( T(p->text)[j] == '=' ) {
1028
+ sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
1029
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
1030
+ ++j;
1031
+ j = nextnonblank(p,j);
1032
+ }
1033
+
1034
+
1035
+ if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) {
1036
+ ___mkd_freeLine(p);
1037
+ p = np;
1038
+ np = p->next;
1039
+ j = p->dle;
1040
+ }
1041
+
1042
+ if ( (c = tgood(T(p->text)[j])) ) {
1043
+ /* Try to take the rest of the line as a comment; read to
1044
+ * EOL, then shrink the string back to before the final
1045
+ * quote.
1046
+ */
1047
+ ++j; /* skip leading quote */
1048
+
1049
+ while ( j < S(p->text) )
1050
+ EXPAND(foot->title) = T(p->text)[j++];
1051
+
1052
+ while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c )
1053
+ --S(foot->title);
1054
+ if ( S(foot->title) ) /* skip trailing quote */
1055
+ --S(foot->title);
1056
+ EXPAND(foot->title) = 0;
1057
+ --S(foot->title);
1058
+ }
1059
+
1060
+ skip_to_end:
1061
+ ___mkd_freeLine(p);
1062
+ return np;
1063
+ }
1064
+
1065
+
1066
+ /*
1067
+ * allocate a paragraph header, link it to the
1068
+ * tail of the current document
1069
+ */
1070
+ static Paragraph *
1071
+ Pp(ParagraphRoot *d, Line *ptr, int typ)
1072
+ {
1073
+ Paragraph *ret = calloc(sizeof *ret, 1);
1074
+
1075
+ ret->text = ptr;
1076
+ ret->typ = typ;
1077
+
1078
+ return ATTACH(*d, ret);
1079
+ }
1080
+
1081
+
1082
+
1083
+ static Line*
1084
+ consume(Line *ptr, int *eaten)
1085
+ {
1086
+ Line *next;
1087
+ int blanks=0;
1088
+
1089
+ for (; ptr && blankline(ptr); ptr = next, blanks++ ) {
1090
+ next = ptr->next;
1091
+ ___mkd_freeLine(ptr);
1092
+ }
1093
+ if ( ptr ) *eaten = blanks;
1094
+ return ptr;
1095
+ }
1096
+
1097
+
1098
+ /*
1099
+ * top-level compilation; break the document into
1100
+ * style, html, and source blocks with footnote links
1101
+ * weeded out.
1102
+ */
1103
+ static Paragraph *
1104
+ compile_document(Line *ptr, MMIOT *f)
1105
+ {
1106
+ ParagraphRoot d = { 0, 0 };
1107
+ ANCHOR(Line) source = { 0, 0 };
1108
+ Paragraph *p = 0;
1109
+ struct kw *tag;
1110
+ int eaten, unclosed;
1111
+
1112
+ while ( ptr ) {
1113
+ if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) {
1114
+ int blocktype;
1115
+ /* If we encounter a html/style block, compile and save all
1116
+ * of the cached source BEFORE processing the html/style.
1117
+ */
1118
+ if ( T(source) ) {
1119
+ E(source)->next = 0;
1120
+ p = Pp(&d, 0, SOURCE);
1121
+ p->down = compile(T(source), 1, f);
1122
+ T(source) = E(source) = 0;
1123
+ }
1124
+
1125
+ if ( f->flags & MKD_NOSTYLE )
1126
+ blocktype = HTML;
1127
+ else
1128
+ blocktype = strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML;
1129
+ p = Pp(&d, ptr, blocktype);
1130
+ ptr = htmlblock(p, tag, &unclosed);
1131
+ if ( unclosed ) {
1132
+ p->typ = SOURCE;
1133
+ p->down = compile(p->text, 1, f);
1134
+ p->text = 0;
1135
+ }
1136
+ }
1137
+ else if ( isfootnote(ptr) ) {
1138
+ /* footnotes, like cats, sleep anywhere; pull them
1139
+ * out of the input stream and file them away for
1140
+ * later processing
1141
+ */
1142
+ ptr = consume(addfootnote(ptr, f), &eaten);
1143
+ }
1144
+ else {
1145
+ /* source; cache it up to wait for eof or the
1146
+ * next html/style block
1147
+ */
1148
+ ATTACH(source,ptr);
1149
+ ptr = ptr->next;
1150
+ }
1151
+ }
1152
+ if ( T(source) ) {
1153
+ /* if there's any cached source at EOF, compile
1154
+ * it now.
1155
+ */
1156
+ E(source)->next = 0;
1157
+ p = Pp(&d, 0, SOURCE);
1158
+ p->down = compile(T(source), 1, f);
1159
+ }
1160
+ return T(d);
1161
+ }
1162
+
1163
+
1164
+ static int
1165
+ first_nonblank_before(Line *j, int dle)
1166
+ {
1167
+ return (j->dle < dle) ? j->dle : dle;
1168
+ }
1169
+
1170
+
1171
+ static int
1172
+ actually_a_table(MMIOT *f, Line *pp)
1173
+ {
1174
+ Line *r;
1175
+ int j;
1176
+ int c;
1177
+
1178
+ /* tables need to be turned on */
1179
+ if ( f->flags & (MKD_STRICT|MKD_NOTABLES) )
1180
+ return 0;
1181
+
1182
+ /* tables need three lines */
1183
+ if ( !(pp && pp->next && pp->next->next) ) {
1184
+ return 0;
1185
+ }
1186
+
1187
+ /* all lines must contain |'s */
1188
+ for (r = pp; r; r = r->next )
1189
+ if ( !(r->flags & PIPECHAR) ) {
1190
+ return 0;
1191
+ }
1192
+
1193
+ /* if the header has a leading |, all lines must have leading |'s */
1194
+ if ( T(pp->text)[pp->dle] == '|' ) {
1195
+ for ( r = pp; r; r = r->next )
1196
+ if ( T(r->text)[first_nonblank_before(r,pp->dle)] != '|' ) {
1197
+ return 0;
1198
+ }
1199
+ }
1200
+
1201
+ /* second line must be only whitespace, -, |, or : */
1202
+ r = pp->next;
1203
+
1204
+ for ( j=r->dle; j < S(r->text); ++j ) {
1205
+ c = T(r->text)[j];
1206
+
1207
+ if ( !(isspace(c)||(c=='-')||(c==':')||(c=='|')) ) {
1208
+ return 0;
1209
+ }
1210
+ }
1211
+
1212
+ return 1;
1213
+ }
1214
+
1215
+
1216
+ /*
1217
+ * break a collection of markdown input into
1218
+ * blocks of lists, code, html, and text to
1219
+ * be marked up.
1220
+ */
1221
+ static Paragraph *
1222
+ compile(Line *ptr, int toplevel, MMIOT *f)
1223
+ {
1224
+ ParagraphRoot d = { 0, 0 };
1225
+ Paragraph *p = 0;
1226
+ Line *r;
1227
+ int para = toplevel;
1228
+ int blocks = 0;
1229
+ int hdr_type, list_type, list_class, indent;
1230
+
1231
+ ptr = consume(ptr, &para);
1232
+
1233
+ while ( ptr ) {
1234
+ if ( iscode(ptr) ) {
1235
+ p = Pp(&d, ptr, CODE);
1236
+
1237
+ if ( f->flags & MKD_1_COMPAT) {
1238
+ /* HORRIBLE STANDARDS KLUDGE: the first line of every block
1239
+ * has trailing whitespace trimmed off.
1240
+ */
1241
+ ___mkd_tidy(&p->text->text);
1242
+ }
1243
+
1244
+ ptr = codeblock(p);
1245
+ }
1246
+ #if WITH_FENCED_CODE
1247
+ else if ( iscodefence(ptr,3,0) && (p=fencedcodeblock(&d, &ptr)) )
1248
+ /* yay, it's already done */ ;
1249
+ #endif
1250
+ else if ( ishr(ptr) ) {
1251
+ p = Pp(&d, 0, HR);
1252
+ r = ptr;
1253
+ ptr = ptr->next;
1254
+ ___mkd_freeLine(r);
1255
+ }
1256
+ else if ( list_class = islist(ptr, &indent, f->flags, &list_type) ) {
1257
+ if ( list_class == DL ) {
1258
+ p = Pp(&d, ptr, DL);
1259
+ ptr = definition_block(p, indent, f, list_type);
1260
+ }
1261
+ else {
1262
+ p = Pp(&d, ptr, list_type);
1263
+ ptr = enumerated_block(p, indent, f, list_class);
1264
+ }
1265
+ }
1266
+ else if ( isquote(ptr) ) {
1267
+ p = Pp(&d, ptr, QUOTE);
1268
+ ptr = quoteblock(p, f->flags);
1269
+ p->down = compile(p->text, 1, f);
1270
+ p->text = 0;
1271
+ }
1272
+ else if ( ishdr(ptr, &hdr_type) ) {
1273
+ p = Pp(&d, ptr, HDR);
1274
+ ptr = headerblock(p, hdr_type);
1275
+ }
1276
+ else {
1277
+ p = Pp(&d, ptr, MARKUP);
1278
+ ptr = textblock(p, toplevel, f->flags);
1279
+ /* tables are a special kind of paragraph */
1280
+ if ( actually_a_table(f, p->text) )
1281
+ p->typ = TABLE;
1282
+ }
1283
+
1284
+ if ( (para||toplevel) && !p->align )
1285
+ p->align = PARA;
1286
+
1287
+ blocks++;
1288
+ para = toplevel || (blocks > 1);
1289
+ ptr = consume(ptr, &para);
1290
+
1291
+ if ( para && !p->align )
1292
+ p->align = PARA;
1293
+
1294
+ }
1295
+ return T(d);
1296
+ }
1297
+
1298
+
1299
+ /*
1300
+ * the guts of the markdown() function, ripped out so I can do
1301
+ * debugging.
1302
+ */
1303
+
1304
+ /*
1305
+ * prepare and compile `text`, returning a Paragraph tree.
1306
+ */
1307
+ int
1308
+ mkd_compile(Document *doc, DWORD flags)
1309
+ {
1310
+ if ( !doc )
1311
+ return 0;
1312
+
1313
+ if ( doc->compiled )
1314
+ return 1;
1315
+
1316
+ doc->compiled = 1;
1317
+ memset(doc->ctx, 0, sizeof(MMIOT) );
1318
+ doc->ctx->ref_prefix= doc->ref_prefix;
1319
+ doc->ctx->cb = &(doc->cb);
1320
+ doc->ctx->flags = flags & USER_FLAGS;
1321
+ CREATE(doc->ctx->in);
1322
+ doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
1323
+ CREATE(*doc->ctx->footnotes);
1324
+
1325
+ mkd_initialize();
1326
+
1327
+ doc->code = compile_document(T(doc->content), doc->ctx);
1328
+ qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
1329
+ sizeof T(*doc->ctx->footnotes)[0],
1330
+ (stfu)__mkd_footsort);
1331
+ memset(&doc->content, 0, sizeof doc->content);
1332
+ return 1;
1333
+ }
1334
+