rdiscount 1.2.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/ext/markdown.c ADDED
@@ -0,0 +1,866 @@
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
+ /* block-level tags for passing html blocks through the blender
21
+ */
22
+ struct kw {
23
+ char *id;
24
+ int siz;
25
+ } ;
26
+
27
+ #define KW(x) { x, sizeof(x)-1 }
28
+
29
+ static struct kw blocktags[] = { KW("!--"), KW("STYLE"), KW("SCRIPT"),
30
+ KW("ADDRESS"), KW("BDO"), KW("BLOCKQUOTE"),
31
+ KW("CENTER"), KW("DFN"), KW("DIV"), KW("H1"),
32
+ KW("H2"), KW("H3"), KW("H4"), KW("H5"),
33
+ KW("H6"), KW("LISTING"), KW("NOBR"),
34
+ KW("UL"), KW("P"), KW("OL"), KW("DL"),
35
+ KW("PLAINTEXT"), KW("PRE"), KW("TABLE"),
36
+ KW("WBR"), KW("XMP"), KW("HR"), KW("BR") };
37
+ #define SZTAGS (sizeof blocktags / sizeof blocktags[0])
38
+ #define MAXTAG 11 /* sizeof "BLOCKQUOTE" */
39
+
40
+ typedef int (*stfu)(const void*,const void*);
41
+
42
+ typedef ANCHOR(Paragraph) ParagraphRoot;
43
+
44
+
45
+ /* case insensitive string sort (for qsort() and bsearch() of block tags)
46
+ */
47
+ static int
48
+ casort(struct kw *a, struct kw *b)
49
+ {
50
+ if ( a->siz != b->siz )
51
+ return a->siz - b->siz;
52
+ return strncasecmp(a->id, b->id, b->siz);
53
+ }
54
+
55
+
56
+ /* case insensitive string sort for Footnote tags.
57
+ */
58
+ int
59
+ __mkd_footsort(Footnote *a, Footnote *b)
60
+ {
61
+ int i;
62
+ char ac, bc;
63
+
64
+ if ( S(a->tag) != S(b->tag) )
65
+ return S(a->tag) - S(b->tag);
66
+
67
+ for ( i=0; i < S(a->tag); i++) {
68
+ ac = tolower(T(a->tag)[i]);
69
+ bc = tolower(T(b->tag)[i]);
70
+
71
+ if ( isspace(ac) && isspace(bc) )
72
+ continue;
73
+ if ( ac != bc )
74
+ return ac - bc;
75
+ }
76
+ return 0;
77
+ }
78
+
79
+
80
+ /* find the first blank character after position <i>
81
+ */
82
+ static int
83
+ nextblank(Line *t, int i)
84
+ {
85
+ while ( (i < S(t->text)) && !isspace(T(t->text)[i]) )
86
+ ++i;
87
+ return i;
88
+ }
89
+
90
+
91
+ /* find the next nonblank character after position <i>
92
+ */
93
+ static int
94
+ nextnonblank(Line *t, int i)
95
+ {
96
+ while ( (i < S(t->text)) && isspace(T(t->text)[i]) )
97
+ ++i;
98
+ return i;
99
+ }
100
+
101
+
102
+ /* find the first nonblank character on the Line.
103
+ */
104
+ int
105
+ mkd_firstnonblank(Line *p)
106
+ {
107
+ return nextnonblank(p,0);
108
+ }
109
+
110
+
111
+ static int
112
+ blankline(Line *p)
113
+ {
114
+ return ! (p && (S(p->text) > p->dle) );
115
+ }
116
+
117
+
118
+ static Line *
119
+ skipempty(Line *p)
120
+ {
121
+ while ( p && (p->dle == S(p->text)) )
122
+ p = p->next;
123
+ return p;
124
+ }
125
+
126
+
127
+ static char *
128
+ isopentag(Line *p)
129
+ {
130
+ int i=0, len;
131
+ struct kw key, *ret;
132
+
133
+ if ( !p ) return 0;
134
+
135
+ len = S(p->text);
136
+
137
+ if ( len < 3 || T(p->text)[0] != '<' )
138
+ return 0;
139
+
140
+ /* find how long the tag is so we can check to see if
141
+ * it's a block-level tag
142
+ */
143
+ for ( i=1; i < len && T(p->text)[i] != '>'
144
+ && T(p->text)[i] != '/'
145
+ && !isspace(T(p->text)[i]); ++i )
146
+ ;
147
+
148
+ key.id = T(p->text)+1;
149
+ key.siz = i-1;
150
+
151
+ if ( ret = bsearch(&key,blocktags,SZTAGS,sizeof key, (stfu)casort))
152
+ return ret->id;
153
+
154
+ return 0;
155
+ }
156
+
157
+
158
+ static int
159
+ selfclose(Line *t, char *tag)
160
+ {
161
+ char *q = T(t->text);
162
+ int siz = strlen(tag);
163
+ int i;
164
+
165
+ if ( strcasecmp(tag, "HR") == 0 || strcasecmp(tag, "BR") == 0 )
166
+ /* <HR> and <BR> are self-closing block-level tags,
167
+ */
168
+ return 1;
169
+
170
+ i = S(t->text) - (siz + 3);
171
+
172
+ /* we specialcase start and end tags on the same line.
173
+ */
174
+ return ( i > 0 ) && (q[i] == '<') && (q[i+1] == '/')
175
+ && (q[i+2+siz] == '>')
176
+ && (strncasecmp(&q[i+2], tag, siz) == 0);
177
+ }
178
+
179
+
180
+ static Line *
181
+ htmlblock(Paragraph *p, char *tag)
182
+ {
183
+ Line *t = p->text, *ret;
184
+ int closesize;
185
+ char close[MAXTAG+4];
186
+
187
+ if ( selfclose(t, tag) || (strlen(tag) >= MAXTAG) ) {
188
+ ret = t->next;
189
+ t->next = 0;
190
+ return ret;
191
+ }
192
+
193
+ closesize = sprintf(close, "</%s>", tag);
194
+
195
+ for ( ; t ; t = t->next) {
196
+ if ( strncasecmp(T(t->text), close, closesize) == 0 ) {
197
+ ret = t->next;
198
+ t->next = 0;
199
+ return ret;
200
+ }
201
+ }
202
+ return 0;
203
+ }
204
+
205
+
206
+ static Line *
207
+ comment(Paragraph *p, char *key)
208
+ {
209
+ Line *t, *ret;
210
+
211
+ for ( t = p->text; t ; t = t->next) {
212
+ if ( strstr(T(t->text), "-->") ) {
213
+ ret = t->next;
214
+ t->next = 0;
215
+ return ret;
216
+ }
217
+ }
218
+ return t;
219
+
220
+ }
221
+
222
+
223
+ /* footnotes look like ^<whitespace>{0,3}[stuff]: <content>$
224
+ */
225
+ static int
226
+ isfootnote(Line *t)
227
+ {
228
+ int i;
229
+
230
+ if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') )
231
+ return 0;
232
+
233
+ for ( ++i; i < S(t->text) ; ++i ) {
234
+ if ( T(t->text)[i] == '[' )
235
+ return 0;
236
+ else if ( T(t->text)[i] == ']' && T(t->text)[i+1] == ':' )
237
+ return 1;
238
+ }
239
+ return 0;
240
+ }
241
+
242
+
243
+ static int
244
+ isquote(Line *t)
245
+ {
246
+ return ( T(t->text)[0] == '>' );
247
+ }
248
+
249
+
250
+ static int
251
+ dashchar(char c)
252
+ {
253
+ return (c == '*') || (c == '-') || (c == '_');
254
+ }
255
+
256
+
257
+ static int
258
+ iscode(Line *t)
259
+ {
260
+ return (t->dle >= 4);
261
+ }
262
+
263
+
264
+ static int
265
+ ishr(Line *t)
266
+ {
267
+ int i, count=0;
268
+ char dash = 0;
269
+ char c;
270
+
271
+ if ( iscode(t) ) return 0;
272
+
273
+ for ( i = 0; i < S(t->text); i++) {
274
+ c = T(t->text)[i];
275
+ if ( (dash == 0) && dashchar(c) )
276
+ dash = c;
277
+
278
+ if ( c == dash ) ++count;
279
+ else if ( !isspace(c) )
280
+ return 0;
281
+ }
282
+ return (count >= 3);
283
+ }
284
+
285
+
286
+ static int
287
+ ishdr(Line *t, int *htyp)
288
+ {
289
+ int i, j;
290
+
291
+
292
+ /* first check for etx-style ###HEADER###
293
+ */
294
+
295
+ /* leading run of `#`'s ?
296
+ */
297
+ for ( i=0; T(t->text)[i] == '#'; ++i)
298
+ ;
299
+
300
+ if ( i ) {
301
+ i = nextnonblank(t, i);
302
+
303
+ j = S(t->text)-1;
304
+
305
+ while ( (j > i) && (T(t->text)[j] == '#') )
306
+ --j;
307
+
308
+ while ( (j > 1) && isspace(T(t->text)[j]) )
309
+ --j;
310
+
311
+ if ( i < j ) {
312
+ *htyp = ETX;
313
+ return 1;
314
+ }
315
+ }
316
+
317
+ /* then check for setext-style HEADER
318
+ * ======
319
+ */
320
+
321
+ if ( t->next ) {
322
+ char *q = T(t->next->text);
323
+
324
+ if ( (*q == '=') || (*q == '-') ) {
325
+ for (i=1; i < S(t->next->text); i++)
326
+ if ( q[0] != q[i] )
327
+ return 0;
328
+ *htyp = SETEXT;
329
+ return 1;
330
+ }
331
+ }
332
+ return 0;
333
+ }
334
+
335
+
336
+ static int
337
+ isdefinition(Line *t)
338
+ {
339
+ #if DL_TAG_EXTENSION
340
+ return t && t->next
341
+ && (S(t->text) > 2)
342
+ && (t->dle == 0)
343
+ && (T(t->text)[0] == '=')
344
+ && (T(t->text)[S(t->text)-1] == '=')
345
+ && ( (t->next->dle >= 4) || isdefinition(t->next) );
346
+ #else
347
+ return 0;
348
+ #endif
349
+ }
350
+
351
+
352
+ static int
353
+ islist(Line *t, int *trim)
354
+ {
355
+ int i, j;
356
+ char *q;
357
+
358
+ if ( iscode(t) || blankline(t) || ishdr(t,&i) || ishr(t) )
359
+ return 0;
360
+
361
+ if ( isdefinition(t) ) {
362
+ *trim = 4;
363
+ return DL;
364
+ }
365
+
366
+ if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
367
+ i = nextnonblank(t, t->dle+1);
368
+ *trim = (i > 4) ? 4 : i;
369
+ return UL;
370
+ }
371
+
372
+ if ( (j = nextblank(t,t->dle)) > t->dle ) {
373
+ if ( T(t->text)[j-1] == '.' ) {
374
+ strtoul(T(t->text)+t->dle, &q, 10);
375
+ if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) {
376
+ j = nextnonblank(t,j);
377
+ *trim = j;
378
+ return OL;
379
+ }
380
+ }
381
+ }
382
+ return 0;
383
+ }
384
+
385
+
386
+ static Line *
387
+ headerblock(Paragraph *pp, int htyp)
388
+ {
389
+ Line *ret = 0;
390
+ Line *p = pp->text;
391
+ int i, j;
392
+
393
+ switch (htyp) {
394
+ case SETEXT:
395
+ /* p->text is header, p->next->text is -'s or ='s
396
+ */
397
+ pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2;
398
+
399
+ ret = p->next->next;
400
+ ___mkd_freeLine(p->next);
401
+ p->next = 0;
402
+ break;
403
+
404
+ case ETX:
405
+ /* p->text is ###header###, so we need to trim off
406
+ * the leading and trailing `#`'s
407
+ */
408
+
409
+ for (i=0; T(p->text)[i] == T(p->text)[0]; i++)
410
+ ;
411
+
412
+ pp->hnumber = i;
413
+
414
+ CLIP(p->text, 0, i);
415
+
416
+ for (j=S(p->text); j && (T(p->text)[j-1] == '#'); --j)
417
+ ;
418
+
419
+ S(p->text) = j;
420
+
421
+ ret = p->next;
422
+ p->next = 0;
423
+ break;
424
+ }
425
+ return ret;
426
+ }
427
+
428
+
429
+ static Line *
430
+ codeblock(Paragraph *p)
431
+ {
432
+ Line *t = p->text, *r;
433
+
434
+ /* HORRIBLE STANDARDS KLUDGE: the first line of every block
435
+ * has trailing whitespace trimmed off.
436
+ */
437
+ while ( S(t->text) && isspace(T(t->text)[S(t->text)-1]) )
438
+ --S(t->text);
439
+
440
+ for ( ; t; t = r ) {
441
+ CLIP(t->text,0,4);
442
+ t->dle = mkd_firstnonblank(t);
443
+
444
+ if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
445
+ ___mkd_freeLineRange(t,r);
446
+ t->next = 0;
447
+ return r;
448
+ }
449
+ }
450
+ return t;
451
+ }
452
+
453
+
454
+ static int
455
+ centered(Line *first, Line *last)
456
+ {
457
+
458
+ if ( first&&last ) {
459
+ int len = S(last->text);
460
+
461
+ if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0)
462
+ && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) {
463
+ CLIP(first->text, 0, 2);
464
+ S(last->text) -= 2;
465
+ return CENTER;
466
+ }
467
+ }
468
+ return 0;
469
+ }
470
+
471
+
472
+ static int
473
+ endoftextblock(Line *t, int toplevelblock)
474
+ {
475
+ int z;
476
+
477
+ if ( blankline(t)||isquote(t)||iscode(t)||ishdr(t,&z)||ishr(t) )
478
+ return 1;
479
+
480
+ /* HORRIBLE STANDARDS KLUDGE: Toplevel paragraphs eat absorb adjacent
481
+ * list items, but sublevel blocks behave properly.
482
+ */
483
+ return toplevelblock ? 0 : islist(t,&z);
484
+ }
485
+
486
+
487
+ static Line *
488
+ textblock(Paragraph *p, int toplevel)
489
+ {
490
+ Line *t, *next;
491
+
492
+ for ( t = p->text; t ; t = next )
493
+ if ( ((next = t->next) == 0) || endoftextblock(next, toplevel) ) {
494
+ p->align = centered(p->text, t);
495
+ t->next = 0;
496
+ return next;
497
+ }
498
+ return t;
499
+ }
500
+
501
+
502
+ /*
503
+ * accumulate a blockquote.
504
+ *
505
+ * one sick horrible thing about blockquotes is that even though
506
+ * it just takes ^> to start a quote, following lines, if quoted,
507
+ * assume that the prefix is ``>''. This means that code needs
508
+ * to be indented *5* spaces from the leading '>', but *4* spaces
509
+ * from the start of the line. This does not appear to be
510
+ * documented in the reference implementation, but it's the
511
+ * way the markdown sample web form at Daring Fireball works.
512
+ */
513
+ static Line *
514
+ quoteblock(Paragraph *p)
515
+ {
516
+ Line *t, *q;
517
+ int qp;
518
+
519
+ for ( t = p->text; t ; t = q ) {
520
+ if ( isquote(t) ) {
521
+ qp = (T(t->text)[1] == ' ') ? 2 : 1;
522
+ CLIP(t->text, 0, qp);
523
+ t->dle = mkd_firstnonblank(t);
524
+ }
525
+
526
+ if ( !(q = skipempty(t->next)) || ((q != t->next) && !isquote(q)) ) {
527
+ ___mkd_freeLineRange(t, q);
528
+ return q;
529
+ }
530
+ }
531
+ return t;
532
+ }
533
+
534
+
535
+ static Paragraph *Pp(ParagraphRoot *, Line *, int);
536
+ static Paragraph *compile(Line *, int, MMIOT *);
537
+
538
+
539
+ /*
540
+ * pull in a list block. A list block starts with a list marker and
541
+ * runs until the next list marker, the next non-indented paragraph,
542
+ * or EOF. You do not have to indent nonblank lines after the list
543
+ * marker, but multiple paragraphs need to start with a 4-space indent.
544
+ */
545
+ static Line *
546
+ listitem(Paragraph *p, int indent)
547
+ {
548
+ Line *t, *q;
549
+ int clip = indent;
550
+ int z;
551
+
552
+ for ( t = p->text; t ; t = q) {
553
+ CLIP(t->text, 0, clip);
554
+ t->dle = mkd_firstnonblank(t);
555
+
556
+ if ( (q = skipempty(t->next)) == 0 ) {
557
+ ___mkd_freeLineRange(t,q);
558
+ return 0;
559
+ }
560
+
561
+ /* after a blank line, the next block needs to start with a line
562
+ * that's indented 4 spaces, but after that the line doesn't
563
+ * need any indentation
564
+ */
565
+ if ( q != t->next ) {
566
+ if (q->dle < 4) {
567
+ q = t->next;
568
+ t->next = 0;
569
+ return q;
570
+ }
571
+ indent = 4;
572
+ }
573
+
574
+ if ( (q->dle < indent) && (ishr(q) || ishdr(q,&z) || islist(q,&z)) ) {
575
+ q = t->next;
576
+ t->next = 0;
577
+ return q;
578
+ }
579
+
580
+ clip = (q->dle > indent) ? indent : q->dle;
581
+ }
582
+ return t;
583
+ }
584
+
585
+
586
+ static Line *
587
+ listblock(Paragraph *top, int trim, MMIOT *f)
588
+ {
589
+ ParagraphRoot d = { 0, 0 };
590
+ Paragraph *p;
591
+ Line *q = top->text, *text;
592
+ Line *label;
593
+ int para = 0;
594
+
595
+ while (( text = q )) {
596
+ if ( top->typ == DL ) {
597
+ Line *lp;
598
+
599
+ for ( lp = label = text; lp ; lp = lp->next ) {
600
+ text = lp->next;
601
+ CLIP(lp->text, 0, 1);
602
+ S(lp->text)--;
603
+ if ( !isdefinition(lp->next) )
604
+ lp->next = 0;
605
+ }
606
+ }
607
+ else label = 0;
608
+
609
+ p = Pp(&d, text, LISTITEM);
610
+ text = listitem(p, trim);
611
+
612
+ p->down = compile(p->text, 0, f);
613
+ p->text = label;
614
+
615
+ if ( para && (top->typ != DL) ) p->down->align = PARA;
616
+
617
+ if ( !(q = skipempty(text)) || (islist(q,&trim) != top->typ) )
618
+ break;
619
+
620
+ if ( para = (q != text) ) {
621
+ Line anchor;
622
+
623
+ anchor.next = text;
624
+ ___mkd_freeLineRange(&anchor, q);
625
+ }
626
+
627
+ if ( para && (top->typ != DL) ) p->down->align = PARA;
628
+ }
629
+ top->text = 0;
630
+ top->down = T(d);
631
+ return text;
632
+ }
633
+
634
+
635
+ static int
636
+ tgood(char c)
637
+ {
638
+ switch (c) {
639
+ case '\'':
640
+ case '"': return c;
641
+ case '(': return ')';
642
+ }
643
+ return 0;
644
+ }
645
+
646
+
647
+ /*
648
+ * add a new (image or link) footnote to the footnote table
649
+ */
650
+ static Line*
651
+ addfootnote(Line *p, MMIOT* f)
652
+ {
653
+ int j, i;
654
+ int c;
655
+ Line *np = p->next;
656
+
657
+ Footnote *foot = &EXPAND(*f->footnotes);
658
+
659
+ CREATE(foot->tag);
660
+ CREATE(foot->link);
661
+ CREATE(foot->title);
662
+ foot->height = foot->width = 0;
663
+
664
+ for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
665
+ EXPAND(foot->tag) = T(p->text)[j];
666
+
667
+ EXPAND(foot->tag) = 0;
668
+ S(foot->tag)--;
669
+ j = nextnonblank(p, j+2);
670
+
671
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
672
+ EXPAND(foot->link) = T(p->text)[j++];
673
+ EXPAND(foot->link) = 0;
674
+ S(foot->link)--;
675
+ j = nextnonblank(p,j);
676
+
677
+ if ( T(p->text)[j] == '=' ) {
678
+ sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
679
+ while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
680
+ ++j;
681
+ j = nextnonblank(p,j);
682
+ }
683
+
684
+
685
+ if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) {
686
+ ___mkd_freeLine(p);
687
+ p = np;
688
+ np = p->next;
689
+ j = p->dle;
690
+ }
691
+
692
+ if ( (c = tgood(T(p->text)[j])) ) {
693
+ /* Try to take the rest of the line as a comment; read to
694
+ * EOL, then shrink the string back to before the final
695
+ * quote.
696
+ */
697
+ ++j; /* skip leading quote */
698
+
699
+ while ( j < S(p->text) )
700
+ EXPAND(foot->title) = T(p->text)[j++];
701
+
702
+ while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c )
703
+ --S(foot->title);
704
+ if ( S(foot->title) ) /* skip trailing quote */
705
+ --S(foot->title);
706
+ EXPAND(foot->title) = 0;
707
+ --S(foot->title);
708
+ }
709
+
710
+ ___mkd_freeLine(p);
711
+ return np;
712
+ }
713
+
714
+
715
+ /*
716
+ * allocate a paragraph header, link it to the
717
+ * tail of the current document
718
+ */
719
+ static Paragraph *
720
+ Pp(ParagraphRoot *d, Line *ptr, int typ)
721
+ {
722
+ Paragraph *ret = calloc(sizeof *ret, 1);
723
+
724
+ ret->text = ptr;
725
+ ret->typ = typ;
726
+
727
+ return ATTACH(*d, ret);
728
+ }
729
+
730
+
731
+
732
+ static Line*
733
+ consume(Line *ptr, int *eaten)
734
+ {
735
+ Line *next;
736
+ int blanks=0;
737
+
738
+ for (; ptr && blankline(ptr); ptr = next, blanks++ ) {
739
+ next = ptr->next;
740
+ ___mkd_freeLine(ptr);
741
+ }
742
+ if ( ptr ) *eaten = blanks;
743
+ return ptr;
744
+ }
745
+
746
+
747
+ /*
748
+ * break a collection of markdown input into
749
+ * blocks of lists, code, html, and text to
750
+ * be marked up.
751
+ */
752
+ static Paragraph *
753
+ compile(Line *ptr, int toplevel, MMIOT *f)
754
+ {
755
+ ParagraphRoot d = { 0, 0 };
756
+ Paragraph *p = 0;
757
+ char *key;
758
+ Line *r;
759
+ int para = toplevel;
760
+ int hdr_type, list_type, indent;
761
+
762
+ ptr = consume(ptr, &para);
763
+
764
+ while ( ptr ) {
765
+ if ( toplevel && (key = isopentag(ptr)) ) {
766
+ p = Pp(&d, ptr, strcmp(key, "STYLE") == 0 ? STYLE : HTML);
767
+ if ( strcmp(key, "!--") == 0 )
768
+ ptr = comment(p, key);
769
+ else
770
+ ptr = htmlblock(p, key);
771
+ }
772
+ else if ( iscode(ptr) ) {
773
+ p = Pp(&d, ptr, CODE);
774
+ ptr = codeblock(p);
775
+ }
776
+ else if ( ishr(ptr) ) {
777
+ p = Pp(&d, 0, HR);
778
+ r = ptr;
779
+ ptr = ptr->next;
780
+ ___mkd_freeLine(r);
781
+ }
782
+ else if (( list_type = islist(ptr, &indent) )) {
783
+ p = Pp(&d, ptr, list_type);
784
+ ptr = listblock(p, indent, f);
785
+ }
786
+ else if ( isquote(ptr) ) {
787
+ p = Pp(&d, ptr, QUOTE);
788
+ ptr = quoteblock(p);
789
+ p->down = compile(p->text, 1, f);
790
+ p->text = 0;
791
+ }
792
+ else if ( ishdr(ptr, &hdr_type) ) {
793
+ p = Pp(&d, ptr, HDR);
794
+ ptr = headerblock(p, hdr_type);
795
+ }
796
+ else if ( toplevel && (isfootnote(ptr)) ) {
797
+ ptr = consume(addfootnote(ptr, f), &para);
798
+ continue;
799
+ }
800
+ else {
801
+ p = Pp(&d, ptr, MARKUP);
802
+ ptr = textblock(p, toplevel);
803
+ }
804
+
805
+ if ( (para||toplevel) && !p->align )
806
+ p->align = PARA;
807
+
808
+ para = toplevel;
809
+ ptr = consume(ptr, &para);
810
+
811
+ if ( para && !p->align )
812
+ p->align = PARA;
813
+
814
+ }
815
+ return T(d);
816
+ }
817
+
818
+
819
+ static void
820
+ initialize()
821
+ {
822
+ static int first = 1;
823
+
824
+ if ( first-- > 0 ) {
825
+ first = 0;
826
+ INITRNG(time(0));
827
+ qsort(blocktags, SZTAGS, sizeof blocktags[0], (stfu)casort);
828
+ }
829
+ }
830
+
831
+
832
+ /*
833
+ * the guts of the markdown() function, ripped out so I can do
834
+ * debugging.
835
+ */
836
+
837
+ /*
838
+ * prepare and compile `text`, returning a Paragraph tree.
839
+ */
840
+ int
841
+ mkd_compile(Document *doc, int flags)
842
+ {
843
+ if ( !doc )
844
+ return 0;
845
+
846
+ if ( doc->compiled )
847
+ return 1;
848
+
849
+ doc->compiled = 1;
850
+ memset(doc->ctx, 0, sizeof(MMIOT) );
851
+ doc->ctx->flags = flags & USER_FLAGS;
852
+ doc->ctx->base = doc->base;
853
+ CREATE(doc->ctx->in);
854
+ doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
855
+ CREATE(*doc->ctx->footnotes);
856
+
857
+ initialize();
858
+
859
+ doc->code = compile(T(doc->content), 1, doc->ctx);
860
+ qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes),
861
+ sizeof T(*doc->ctx->footnotes)[0],
862
+ (stfu)__mkd_footsort);
863
+ memset(&doc->content, 0, sizeof doc->content);
864
+ return 1;
865
+ }
866
+