bluecloth 2.0.5-x86-mingw32

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