rdiscount 1.2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/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
+