bluecloth 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/ChangeLog +629 -0
  2. data/LICENSE +27 -0
  3. data/LICENSE.discount +47 -0
  4. data/README +71 -0
  5. data/Rakefile +319 -0
  6. data/Rakefile.local +63 -0
  7. data/bin/bluecloth +84 -0
  8. data/ext/VERSION +1 -0
  9. data/ext/amalloc.h +29 -0
  10. data/ext/bluecloth.c +373 -0
  11. data/ext/config.h +47 -0
  12. data/ext/cstring.h +73 -0
  13. data/ext/docheader.c +43 -0
  14. data/ext/extconf.rb +45 -0
  15. data/ext/generate.c +1387 -0
  16. data/ext/markdown.c +939 -0
  17. data/ext/markdown.h +135 -0
  18. data/ext/mkdio.c +241 -0
  19. data/ext/mkdio.h +66 -0
  20. data/ext/resource.c +169 -0
  21. data/ext/version.c +28 -0
  22. data/lib/bluecloth.rb +148 -0
  23. data/rake/191_compat.rb +26 -0
  24. data/rake/dependencies.rb +76 -0
  25. data/rake/helpers.rb +412 -0
  26. data/rake/manual.rb +782 -0
  27. data/rake/packaging.rb +116 -0
  28. data/rake/publishing.rb +321 -0
  29. data/rake/rdoc.rb +40 -0
  30. data/rake/style.rb +62 -0
  31. data/rake/svn.rb +639 -0
  32. data/rake/testing.rb +204 -0
  33. data/rake/verifytask.rb +64 -0
  34. data/rake/win32.rb +186 -0
  35. data/spec/bluecloth/101_changes_spec.rb +141 -0
  36. data/spec/bluecloth/autolinks_spec.rb +49 -0
  37. data/spec/bluecloth/blockquotes_spec.rb +143 -0
  38. data/spec/bluecloth/code_spans_spec.rb +164 -0
  39. data/spec/bluecloth/emphasis_spec.rb +164 -0
  40. data/spec/bluecloth/entities_spec.rb +65 -0
  41. data/spec/bluecloth/hrules_spec.rb +90 -0
  42. data/spec/bluecloth/images_spec.rb +92 -0
  43. data/spec/bluecloth/inline_html_spec.rb +238 -0
  44. data/spec/bluecloth/links_spec.rb +171 -0
  45. data/spec/bluecloth/lists_spec.rb +294 -0
  46. data/spec/bluecloth/paragraphs_spec.rb +75 -0
  47. data/spec/bluecloth/titles_spec.rb +305 -0
  48. data/spec/bluecloth_spec.rb +209 -0
  49. data/spec/bugfix_spec.rb +123 -0
  50. data/spec/contributions_spec.rb +85 -0
  51. data/spec/data/antsugar.txt +34 -0
  52. data/spec/data/markdowntest/Amps and angle encoding.html +17 -0
  53. data/spec/data/markdowntest/Amps and angle encoding.text +21 -0
  54. data/spec/data/markdowntest/Auto links.html +18 -0
  55. data/spec/data/markdowntest/Auto links.text +13 -0
  56. data/spec/data/markdowntest/Backslash escapes.html +118 -0
  57. data/spec/data/markdowntest/Backslash escapes.text +120 -0
  58. data/spec/data/markdowntest/Blockquotes with code blocks.html +15 -0
  59. data/spec/data/markdowntest/Blockquotes with code blocks.text +11 -0
  60. data/spec/data/markdowntest/Code Blocks.html +18 -0
  61. data/spec/data/markdowntest/Code Blocks.text +14 -0
  62. data/spec/data/markdowntest/Code Spans.html +5 -0
  63. data/spec/data/markdowntest/Code Spans.text +5 -0
  64. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.html +8 -0
  65. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.text +8 -0
  66. data/spec/data/markdowntest/Horizontal rules.html +71 -0
  67. data/spec/data/markdowntest/Horizontal rules.text +67 -0
  68. data/spec/data/markdowntest/Inline HTML (Advanced).html +15 -0
  69. data/spec/data/markdowntest/Inline HTML (Advanced).text +15 -0
  70. data/spec/data/markdowntest/Inline HTML (Simple).html +72 -0
  71. data/spec/data/markdowntest/Inline HTML (Simple).text +69 -0
  72. data/spec/data/markdowntest/Inline HTML comments.html +13 -0
  73. data/spec/data/markdowntest/Inline HTML comments.text +13 -0
  74. data/spec/data/markdowntest/Links, inline style.html +11 -0
  75. data/spec/data/markdowntest/Links, inline style.text +12 -0
  76. data/spec/data/markdowntest/Links, reference style.html +52 -0
  77. data/spec/data/markdowntest/Links, reference style.text +71 -0
  78. data/spec/data/markdowntest/Links, shortcut references.html +9 -0
  79. data/spec/data/markdowntest/Links, shortcut references.text +20 -0
  80. data/spec/data/markdowntest/Literal quotes in titles.html +3 -0
  81. data/spec/data/markdowntest/Literal quotes in titles.text +7 -0
  82. data/spec/data/markdowntest/Markdown Documentation - Basics.html +314 -0
  83. data/spec/data/markdowntest/Markdown Documentation - Basics.text +306 -0
  84. data/spec/data/markdowntest/Markdown Documentation - Syntax.html +942 -0
  85. data/spec/data/markdowntest/Markdown Documentation - Syntax.text +888 -0
  86. data/spec/data/markdowntest/Nested blockquotes.html +9 -0
  87. data/spec/data/markdowntest/Nested blockquotes.text +5 -0
  88. data/spec/data/markdowntest/Ordered and unordered lists.html +148 -0
  89. data/spec/data/markdowntest/Ordered and unordered lists.text +131 -0
  90. data/spec/data/markdowntest/Strong and em together.html +7 -0
  91. data/spec/data/markdowntest/Strong and em together.text +7 -0
  92. data/spec/data/markdowntest/Tabs.html +25 -0
  93. data/spec/data/markdowntest/Tabs.text +21 -0
  94. data/spec/data/markdowntest/Tidyness.html +8 -0
  95. data/spec/data/markdowntest/Tidyness.text +5 -0
  96. data/spec/data/ml-announce.txt +17 -0
  97. data/spec/data/re-overflow.txt +67 -0
  98. data/spec/data/re-overflow2.txt +281 -0
  99. data/spec/lib/constants.rb +5 -0
  100. data/spec/lib/helpers.rb +137 -0
  101. data/spec/lib/matchers.rb +235 -0
  102. data/spec/markdowntest_spec.rb +76 -0
  103. metadata +305 -0
@@ -0,0 +1,47 @@
1
+ /*
2
+ * configuration for markdown, generated Tue Dec 23 06:19:25 PST 2008
3
+ * by ged@tenar.home.faeriemud.org
4
+ */
5
+
6
+ #ifndef CONFIG_H_RZLE3ADO
7
+ #define CONFIG_H_RZLE3ADO
8
+
9
+ #ifdef HAVE_SRANDOM
10
+ # define INITRNG(x) srandom((unsigned int)x)
11
+ #elif HAVE_SRAND
12
+ # define INITRNG(x) srand((unsigned int)x)
13
+ #else
14
+ # define INITRNG(x) (void)1
15
+ #endif
16
+
17
+ #ifdef HAVE_STRCASECMP
18
+ #elif HAVE_STRICMP
19
+ # define strcasecmp stricmp
20
+ #endif
21
+
22
+ #ifdef HAVE_STRNCASECMP
23
+ #elif HAVE_STRNICMP
24
+ # define strncasecmp strnicmp
25
+ #endif
26
+
27
+ #ifdef HAVE_RANDOM
28
+ # define COINTOSS() (random()&1)
29
+ #elif HAVE_RAND
30
+ # define COINTOSS() (rand()&1)
31
+ #else
32
+ # define COINTOSS() 1
33
+ #endif
34
+
35
+ #define TABSTOP 4
36
+ #undef USE_AMALLOC
37
+
38
+ /* Extensions */
39
+ #define SUPERSCRIPT 1
40
+ #define RELAXED_EMPHASIS 1
41
+ #define DIV_QUOTE 1
42
+ #define DL_TAG_EXTENSION 1
43
+ #define PANDOC_HEADER 1
44
+ #define ALPHA_LIST 1
45
+
46
+ #endif /* end of include guard: CONFIG_H_RZLE3ADO */
47
+
@@ -0,0 +1,73 @@
1
+ /* two template types: STRING(t) which defines a pascal-style string
2
+ * of element (t) [STRING(char) is the closest to the pascal string],
3
+ * and ANCHOR(t) which defines a baseplate that a linked list can be
4
+ * built up from. [The linked list /must/ contain a ->next pointer
5
+ * for linking the list together with.]
6
+ */
7
+ #ifndef _CSTRING_D
8
+ #define _CSTRING_D
9
+
10
+ #include <string.h>
11
+ #include <stdlib.h>
12
+
13
+ #include "amalloc.h"
14
+
15
+ /* expandable Pascal-style string.
16
+ */
17
+ #define STRING(type) struct { type *text; int size, alloc; }
18
+
19
+ #define CREATE(x) T(x) = (void*)(S(x) = (x).alloc = 0)
20
+ #define EXPAND(x) (S(x)++)[(S(x) < (x).alloc) \
21
+ ? (T(x)) \
22
+ : (T(x) = T(x) ? realloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \
23
+ : malloc(sizeof T(x)[0] * ((x).alloc += 100)) )]
24
+
25
+ #define DELETE(x) (x).alloc ? (free(T(x)), S(x) = (x).alloc = 0) \
26
+ : ( S(x) = 0 )
27
+ #define CLIP(t,i,sz) \
28
+ ( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \
29
+ (memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \
30
+ S(t) -= (sz)) : -1
31
+
32
+ #define RESERVE(x, sz) T(x) = ((x).alloc > S(x) + (sz) \
33
+ ? T(x) \
34
+ : T(x) \
35
+ ? realloc(T(x), sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))) \
36
+ : malloc(sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))))
37
+ #define SUFFIX(t,p,sz) \
38
+ memcpy(((S(t) += (sz)) - (sz)) + \
39
+ (T(t) = T(t) ? realloc(T(t), sizeof T(t)[0] * ((t).alloc += sz)) \
40
+ : malloc(sizeof T(t)[0] * ((t).alloc += sz))), \
41
+ (p), sizeof(T(t)[0])*(sz))
42
+
43
+ #define PREFIX(t,p,sz) \
44
+ RESERVE( (t), (sz) ); \
45
+ if ( S(t) ) { memmove(T(t)+(sz), T(t), S(t)); } \
46
+ memcpy( T(t), (p), (sz) ); \
47
+ S(t) += (sz)
48
+
49
+ /* reference-style links (and images) are stored in an array
50
+ */
51
+ #define T(x) (x).text
52
+ #define S(x) (x).size
53
+ #define ALL(x) (x).alloc
54
+
55
+ /* abstract anchor type that defines a list base
56
+ * with a function that attaches an element to
57
+ * the end of the list.
58
+ *
59
+ * the list base field is named .text so that the T()
60
+ * macro will work with it.
61
+ */
62
+ #define ANCHOR(t) struct { t *text, *end; }
63
+
64
+ #define ATTACH(t, p) ( (t).text ?( ((t).end->next = (p)), ((t).end = (p)) ) \
65
+ :( ((t).text = (t).end = (p)) ) )
66
+
67
+ typedef STRING(char) Cstring;
68
+
69
+ extern void Csputc(int, Cstring *);
70
+ extern int Csprintf(Cstring *, char *, ...);
71
+ extern void Csreparse(Cstring *, char *, int, int);
72
+
73
+ #endif/*_CSTRING_D*/
@@ -0,0 +1,43 @@
1
+ /*
2
+ * docheader -- get values from the document header
3
+ *
4
+ * Copyright (C) 2007 David L Parsons.
5
+ * The redistribution terms are provided in the COPYRIGHT file that must
6
+ * be distributed with this source code.
7
+ */
8
+ #include "config.h"
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <ctype.h>
12
+
13
+ #include "cstring.h"
14
+ #include "markdown.h"
15
+ #include "amalloc.h"
16
+
17
+ #define afterdle(t) (T((t)->text) + (t)->dle)
18
+
19
+ char *
20
+ mkd_doc_title(Document *doc)
21
+ {
22
+ if ( doc && doc->headers )
23
+ return afterdle(doc->headers);
24
+ return 0;
25
+ }
26
+
27
+
28
+ char *
29
+ mkd_doc_author(Document *doc)
30
+ {
31
+ if ( doc && doc->headers && doc->headers->next )
32
+ return afterdle(doc->headers->next);
33
+ return 0;
34
+ }
35
+
36
+
37
+ char *
38
+ mkd_doc_date(Document *doc)
39
+ {
40
+ if ( doc && doc->headers && doc->headers->next && doc->headers->next->next )
41
+ return afterdle(doc->headers->next->next);
42
+ return 0;
43
+ }
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mkmf'
4
+ require 'fileutils'
5
+ require 'pathname'
6
+ require 'rbconfig'
7
+ include Config
8
+
9
+ versionfile = Pathname.new( __FILE__ ).dirname + 'VERSION'
10
+ version = versionfile.read.chomp
11
+
12
+ # Thanks to Daniel Berger for helping me out with this. :)
13
+ if CONFIG['host_os'].match( 'mswin' )
14
+ $CFLAGS << ' -I.' << ' -W3' << ' -Zi'
15
+ else
16
+ $CFLAGS << ' -I.' << ' -Wall'
17
+ end
18
+ $CPPFLAGS << %Q{ -DVERSION=\\"#{version}\\"}
19
+
20
+ # Add my own debugging hooks if building for me
21
+ if ENV['DEBUGGING_BUILD']
22
+ $CFLAGS << ' -ggdb' << ' -DDEBUG'
23
+ end
24
+
25
+ def fail( *messages )
26
+ $stderr.puts( *messages )
27
+ exit( 1 )
28
+ end
29
+
30
+ # Stuff from configure.sh
31
+ have_func( "srand" ) || have_func( "srandom" )
32
+ have_func( "random" ) || have_func( "rand" )
33
+
34
+ unless have_func( "strcasecmp" ) || have_func( "stricmp" )
35
+ fail( "This extension requires either strcasecmp() or stricmp()" )
36
+ end
37
+ unless have_func( "strncasecmp" ) || have_func( "strnicmp" )
38
+ fail( "This extensions requires either strncasecmp() or strnicmp()" )
39
+ end
40
+
41
+ have_header( 'mkdio.h' ) or fail( "missing mkdio.h" )
42
+
43
+ create_makefile( 'bluecloth_ext' )
44
+
45
+ FileUtils.rm_rf( 'conftest.dSYM' ) # MacOS X cleanup
@@ -0,0 +1,1387 @@
1
+ /* markdown: a C implementation of John Gruber's Markdown markup language.
2
+ *
3
+ * Copyright (C) 2007 David L Parsons.
4
+ * The redistribution terms are provided in the COPYRIGHT file that must
5
+ * be distributed with this source code.
6
+ */
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+ #include <stdarg.h>
10
+ #include <stdlib.h>
11
+ #include <time.h>
12
+ #include <ctype.h>
13
+
14
+ #include "config.h"
15
+
16
+ #include "cstring.h"
17
+ #include "markdown.h"
18
+ #include "amalloc.h"
19
+
20
+ /* prefixes for <automatic links>
21
+ */
22
+ static char *autoprefix[] = { "http://", "https://", "ftp://", "news://" };
23
+ #define SZAUTOPREFIX (sizeof autoprefix / sizeof autoprefix[0])
24
+
25
+ typedef int (*stfu)(const void*,const void*);
26
+
27
+
28
+ /* forward declarations */
29
+ static int iscodeblock(MMIOT*);
30
+ static void code(int, MMIOT*);
31
+ static void text(MMIOT *f);
32
+ static Paragraph *display(Paragraph*, MMIOT*);
33
+
34
+ /* externals from markdown.c */
35
+ int __mkd_footsort(Footnote *, Footnote *);
36
+
37
+ /*
38
+ * push text into the generator input buffer
39
+ */
40
+ static void
41
+ push(char *bfr, int size, MMIOT *f)
42
+ {
43
+ while ( size-- > 0 )
44
+ EXPAND(f->in) = *bfr++;
45
+ }
46
+
47
+
48
+ /* look <i> characters ahead of the cursor.
49
+ */
50
+ static int
51
+ peek(MMIOT *f, int i)
52
+ {
53
+
54
+ i += (f->isp-1);
55
+
56
+ return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
57
+ }
58
+
59
+
60
+ /* pull a byte from the input buffer
61
+ */
62
+ static int
63
+ pull(MMIOT *f)
64
+ {
65
+ return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
66
+ }
67
+
68
+
69
+ /* return a pointer to the current position in the input buffer.
70
+ */
71
+ static char*
72
+ cursor(MMIOT *f)
73
+ {
74
+ return T(f->in) + f->isp;
75
+ }
76
+
77
+
78
+ static int
79
+ isthisspace(MMIOT *f, int i)
80
+ {
81
+ int c = peek(f, i);
82
+
83
+ return isspace(c) || (c == EOF);
84
+ }
85
+
86
+
87
+ static int
88
+ isthisalnum(MMIOT *f, int i)
89
+ {
90
+ int c = peek(f, i);
91
+
92
+ return (c != EOF) && isalnum(c);
93
+ }
94
+
95
+
96
+ static int
97
+ isthisnonword(MMIOT *f, int i)
98
+ {
99
+ return isthisspace(f, i) || ispunct(peek(f,i));
100
+ }
101
+
102
+
103
+ /* return/set the current cursor position
104
+ */
105
+ #define mmiotseek(f,x) (f->isp = x)
106
+ #define mmiottell(f) (f->isp)
107
+
108
+
109
+ /* move n characters forward ( or -n characters backward) in the input buffer.
110
+ */
111
+ static void
112
+ shift(MMIOT *f, int i)
113
+ {
114
+ if (f->isp + i >= 0 )
115
+ f->isp += i;
116
+ }
117
+
118
+
119
+ /* Qchar()
120
+ */
121
+ static void
122
+ Qchar(char c, MMIOT *f)
123
+ {
124
+ block *cur;
125
+
126
+ if ( S(f->Q) == 0 ) {
127
+ cur = &EXPAND(f->Q);
128
+ memset(cur, 0, sizeof *cur);
129
+ cur->b_type = bTEXT;
130
+ }
131
+ else
132
+ cur = &T(f->Q)[S(f->Q)-1];
133
+
134
+ EXPAND(cur->b_text) = c;
135
+
136
+ }
137
+
138
+
139
+ /* Qstring()
140
+ */
141
+ static void
142
+ Qstring(char *s, MMIOT *f)
143
+ {
144
+ while (*s)
145
+ Qchar(*s++, f);
146
+ }
147
+
148
+
149
+ /* Qwrite()
150
+ */
151
+ static void
152
+ Qwrite(char *s, int size, MMIOT *f)
153
+ {
154
+ while (size-- > 0)
155
+ Qchar(*s++, f);
156
+ }
157
+
158
+
159
+ /* Qprintf()
160
+ */
161
+ static void
162
+ Qprintf(MMIOT *f, char *fmt, ...)
163
+ {
164
+ char bfr[80];
165
+ va_list ptr;
166
+
167
+ va_start(ptr,fmt);
168
+ vsnprintf(bfr, sizeof bfr, fmt, ptr);
169
+ va_end(ptr);
170
+ Qstring(bfr, f);
171
+ }
172
+
173
+
174
+ /* Qem()
175
+ */
176
+ static void
177
+ Qem(MMIOT *f, char c, int count)
178
+ {
179
+ block *p = &EXPAND(f->Q);
180
+
181
+ memset(p, 0, sizeof *p);
182
+ p->b_type = (c == '*') ? bSTAR : bUNDER;
183
+ p->b_char = c;
184
+ p->b_count = count;
185
+
186
+ memset(&EXPAND(f->Q), 0, sizeof(block));
187
+ }
188
+
189
+
190
+ /* empair()
191
+ */
192
+ static int
193
+ empair(MMIOT *f, int go, int level)
194
+ {
195
+
196
+ int i;
197
+ block *begin, *p;
198
+
199
+ begin = &T(f->Q)[go];
200
+ for (i=go+1; i < S(f->Q); i++) {
201
+ p = &T(f->Q)[i];
202
+
203
+ if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
204
+ break;
205
+
206
+ if ( p->b_type == begin->b_type ) {
207
+ if ( p->b_count == level ) /* exact match */
208
+ return i-go;
209
+
210
+ if ( p->b_count > 2 ) /* fuzzy match */
211
+ return i-go;
212
+ }
213
+ }
214
+ return EOF;
215
+ }
216
+
217
+
218
+
219
+ static struct emtags {
220
+ char open[10];
221
+ char close[10];
222
+ int size;
223
+ } emtags[] = { { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };
224
+
225
+
226
+ static void
227
+ emclose(Cstring *s, int level)
228
+ {
229
+ PREFIX(*s, emtags[level-1].close, emtags[level-1].size);
230
+ }
231
+
232
+
233
+ static void
234
+ emopen(Cstring *s, int level)
235
+ {
236
+ SUFFIX(*s, emtags[level-1].open, emtags[level-1].size-1);
237
+ }
238
+
239
+
240
+ /* emmatch()
241
+ */
242
+ static void
243
+ emmatch(MMIOT *f, int go)
244
+ {
245
+ block *start = &T(f->Q)[go], *end;
246
+ int e, e2, i, match;
247
+
248
+ while ( start->b_count ) {
249
+ switch (start->b_count) {
250
+ case 2: e = empair(f,go,match=2);
251
+ if ( e != EOF ) break;
252
+ case 1: e = empair(f,go,match=1); break;
253
+ default:
254
+ e = empair(f,go,1);
255
+ e2= empair(f,go,2);
256
+
257
+ if ( e == EOF || ((e2 != EOF) && (e2 >= e)) ) {
258
+ e = e2;
259
+ match = 2;
260
+ }
261
+ else
262
+ match = 1;
263
+ }
264
+ if ( e != EOF ) {
265
+ end = &T(f->Q)[go+e];
266
+ emclose(&end->b_post, match);
267
+ emopen(&start->b_text, match);
268
+ end->b_count -= match;
269
+ }
270
+ else {
271
+ for (i=0; i < match; i++)
272
+ EXPAND(start->b_text) = start->b_char;
273
+ }
274
+
275
+ start->b_count -= match;
276
+ }
277
+ }
278
+
279
+
280
+ /* ___mkd_emblock()
281
+ */
282
+ void
283
+ ___mkd_emblock(MMIOT *f)
284
+ {
285
+ int i;
286
+ block *p;
287
+
288
+ for (i=0; i < S(f->Q); i++) {
289
+ p = &T(f->Q)[i];
290
+
291
+ if ( p->b_type != bTEXT ) emmatch(f, i);
292
+
293
+ if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
294
+ DELETE(p->b_post); }
295
+ if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
296
+ DELETE(p->b_text); }
297
+ }
298
+ S(f->Q) = 0;
299
+ }
300
+
301
+
302
+ /* generate html from a markup fragment
303
+ */
304
+ void
305
+ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f)
306
+ {
307
+ MMIOT sub;
308
+
309
+ ___mkd_initmmiot(&sub, f->footnotes);
310
+
311
+ sub.flags = f->flags | flags;
312
+ sub.base = f->base;
313
+
314
+ push(bfr, size, &sub);
315
+ EXPAND(sub.in) = 0;
316
+ S(sub.in)--;
317
+
318
+ text(&sub);
319
+ ___mkd_emblock(&sub);
320
+
321
+ Qwrite(T(sub.out), S(sub.out), f);
322
+
323
+ ___mkd_freemmiot(&sub, f->footnotes);
324
+ }
325
+
326
+
327
+ /*
328
+ * write out a url, escaping problematic characters
329
+ */
330
+ static void
331
+ puturl(char *s, int size, MMIOT *f)
332
+ {
333
+ unsigned char c;
334
+
335
+ while ( size-- > 0 ) {
336
+ c = *s++;
337
+
338
+ if ( c == '&' )
339
+ Qstring("&amp;", f);
340
+ else if ( c == '<' )
341
+ Qstring("&lt;", f);
342
+ else if ( isalnum(c) || ispunct(c) )
343
+ Qchar(c, f);
344
+ else
345
+ Qprintf(f, "%%%02X", c);
346
+ }
347
+ }
348
+
349
+
350
+ /* advance forward until the next character is not whitespace
351
+ */
352
+ static int
353
+ eatspace(MMIOT *f)
354
+ {
355
+ int c;
356
+
357
+ for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) )
358
+ ;
359
+ return c;
360
+ }
361
+
362
+
363
+ /* (match (a (nested (parenthetical (string.)))))
364
+ */
365
+ static int
366
+ parenthetical(int in, int out, MMIOT *f)
367
+ {
368
+ int size, indent, c;
369
+
370
+ for ( indent=1,size=0; indent; size++ ) {
371
+ if ( (c = pull(f)) == EOF )
372
+ return EOF;
373
+ else if ( c == in )
374
+ ++indent;
375
+ else if ( c == out )
376
+ --indent;
377
+ }
378
+ return size-1;
379
+ }
380
+
381
+
382
+ /* extract a []-delimited label from the input stream.
383
+ */
384
+ static char *
385
+ linkylabel(MMIOT *f, int *sizep)
386
+ {
387
+ char *ptr = cursor(f);
388
+
389
+ if ( (*sizep = parenthetical('[',']',f)) != EOF )
390
+ return ptr;
391
+ return 0;
392
+ }
393
+
394
+
395
+ /* extract a (-prefixed url from the input stream.
396
+ * the label is either of the format `<link>`, where I
397
+ * extract until I find a >, or it is of the format
398
+ * `text`, where I extract until I reach a ')' or
399
+ * whitespace.
400
+ */
401
+ static char*
402
+ linkyurl(MMIOT *f, int *sizep)
403
+ {
404
+ int size = 0;
405
+ char *ptr;
406
+ int c;
407
+
408
+ if ( (c = eatspace(f)) == EOF )
409
+ return 0;
410
+
411
+ ptr = cursor(f);
412
+
413
+ if ( c == '<' ) {
414
+ pull(f);
415
+ ptr++;
416
+ if ( (size = parenthetical('<', '>', f)) == EOF )
417
+ return 0;
418
+ }
419
+ else {
420
+ for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
421
+ if ( c == EOF ) return 0;
422
+ if ( c == ')' )
423
+ shift(f, -1);
424
+ }
425
+ *sizep = size;
426
+ return ptr;
427
+ }
428
+
429
+
430
+ /* extract a =HHHxWWW size from the input stream
431
+ */
432
+ static int
433
+ linkysize(MMIOT *f, int *heightp, int *widthp)
434
+ {
435
+ int height=0, width=0;
436
+ int c;
437
+
438
+ *heightp = 0;
439
+ *widthp = 0;
440
+
441
+ if ( (c = eatspace(f)) != '=' )
442
+ return (c != EOF);
443
+ pull(f); /* eat '=' */
444
+
445
+ for ( c = pull(f); isdigit(c); c = pull(f))
446
+ width = (width * 10) + (c - '0');
447
+
448
+ if ( c == 'x' ) {
449
+ for ( c = pull(f); isdigit(c); c = pull(f))
450
+ height = (height*10) + (c - '0');
451
+
452
+ if ( c != EOF ) {
453
+ if ( !isspace(c) ) shift(f, -1);
454
+ *heightp = height;
455
+ *widthp = width;
456
+ return 1;
457
+ }
458
+ }
459
+ return 0;
460
+ }
461
+
462
+
463
+ /* extract a )-terminated title from the input stream.
464
+ */
465
+ static char*
466
+ linkytitle(MMIOT *f, int *sizep)
467
+ {
468
+ int countq=0, qc, c, size;
469
+ char *ret, *lastqc = 0;
470
+
471
+ eatspace(f);
472
+ if ( (qc=pull(f)) != '"' && qc != '\'' && qc != '(' )
473
+ return 0;
474
+
475
+ if ( qc == '(' ) qc = ')';
476
+
477
+ for ( ret = cursor(f); (c = pull(f)) != EOF; ) {
478
+ if ( (c == ')') && countq ) {
479
+ size = (lastqc ? lastqc : cursor(f)) - ret;
480
+ *sizep = size-1;
481
+ return ret;
482
+ }
483
+ else if ( c == qc ) {
484
+ lastqc = cursor(f);
485
+ countq++;
486
+ }
487
+ }
488
+ return 0;
489
+ }
490
+
491
+
492
+ /* look up (or construct) a footnote from the [xxx] link
493
+ * at the head of the stream.
494
+ */
495
+ static int
496
+ linkykey(int image, Footnote *val, MMIOT *f)
497
+ {
498
+ Footnote *ret;
499
+ Cstring mylabel;
500
+ int here;
501
+
502
+ memset(val, 0, sizeof *val);
503
+
504
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
505
+ return 0;
506
+
507
+ here = mmiottell(f);
508
+ eatspace(f);
509
+ switch ( pull(f) ) {
510
+ case '(':
511
+ /* embedded link */
512
+ if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
513
+ return 0;
514
+
515
+ if ( image && !linkysize(f, &val->height, &val->width) )
516
+ return 0;
517
+
518
+ T(val->title) = linkytitle(f, &S(val->title));
519
+
520
+ return peek(f,0) == ')';
521
+
522
+ case '[': /* footnote links /as defined in the standard/ */
523
+ default: /* footnote links -- undocumented extension */
524
+ /* footnote link */
525
+ mylabel = val->tag;
526
+ if ( peek(f,0) == '[' ) {
527
+ if ( (T(val->tag) = linkylabel(f, &S(val->tag))) == 0 )
528
+ return 0;
529
+
530
+ if ( !S(val->tag) )
531
+ val->tag = mylabel;
532
+ }
533
+ else if ( f->flags & MKD_1_COMPAT )
534
+ break;
535
+ else
536
+ mmiotseek(f,here);
537
+
538
+ ret = bsearch(val, T(*f->footnotes), S(*f->footnotes),
539
+ sizeof *val, (stfu)__mkd_footsort);
540
+
541
+ if ( ret ) {
542
+ val->tag = mylabel;
543
+ val->link = ret->link;
544
+ val->title = ret->title;
545
+ val->height = ret->height;
546
+ val->width = ret->width;
547
+ return 1;
548
+ }
549
+ }
550
+ return 0;
551
+ }
552
+
553
+
554
+ /*
555
+ * all the tag types that linkylinky can produce are
556
+ * defined by this structure.
557
+ */
558
+ typedef struct linkytype {
559
+ char *pat;
560
+ int szpat;
561
+ char *link_pfx; /* tag prefix and link pointer (eg: "<a href="\"" */
562
+ char *link_sfx; /* link suffix (eg: "\"" */
563
+ int WxH; /* this tag allows width x height arguments */
564
+ char *text_pfx; /* text prefix (eg: ">" */
565
+ char *text_sfx; /* text suffix (eg: "</a>" */
566
+ int flags; /* reparse flags */
567
+ } linkytype;
568
+
569
+ static linkytype imaget = { 0, 0, "<img src=\"", "\"",
570
+ 1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
571
+ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
572
+ 0, ">", "</a>", DENY_A };
573
+
574
+ /*
575
+ * pseudo-protocols for [][];
576
+ *
577
+ * id: generates <a id="link">tag</a>
578
+ * class: generates <span class="link">tag</span>
579
+ * raw: just dump the link without any processing
580
+ */
581
+ static linkytype specials[] = {
582
+ { "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
583
+ { "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
584
+ { "raw:", 4, 0, 0, 0, 0, 0, 0 },
585
+ } ;
586
+
587
+ #define NR(x) (sizeof x / sizeof x[0])
588
+
589
+ /* see if t contains one of our pseudo-protocols.
590
+ */
591
+ static linkytype *
592
+ extratag(Cstring t)
593
+ {
594
+ int i;
595
+ linkytype *r;
596
+
597
+ for ( i=0; i < NR(specials); i++ ) {
598
+ r = &specials[i];
599
+ if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) )
600
+ return r;
601
+ }
602
+ return 0;
603
+ }
604
+
605
+
606
+ /*
607
+ * process embedded links and images
608
+ */
609
+ static int
610
+ linkylinky(int image, MMIOT *f)
611
+ {
612
+ int start = mmiottell(f);
613
+ Footnote link;
614
+ linkytype *tag;
615
+
616
+ if ( !linkykey(image, &link, f) ) {
617
+ mmiotseek(f, start);
618
+ return 0;
619
+ }
620
+
621
+ if ( image )
622
+ tag = &imaget;
623
+ else if ( (f->flags & NO_PSEUDO_PROTO) || (tag = extratag(link.link)) == 0 )
624
+ tag = &linkt;
625
+
626
+ if ( f->flags & tag-> flags ) {
627
+ mmiotseek(f, start);
628
+ return 0;
629
+ }
630
+
631
+ if ( tag->link_pfx ) {
632
+ Qstring(tag->link_pfx, f);
633
+ if ( f->base && (T(link.link)[tag->szpat] == '/') )
634
+ puturl(f->base, strlen(f->base), f);
635
+ puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
636
+ Qstring(tag->link_sfx, f);
637
+
638
+ if ( tag->WxH && link.height && link.width ) {
639
+ Qprintf(f," height=\"%d\"", link.height);
640
+ Qprintf(f, " width=\"%d\"", link.width);
641
+ }
642
+
643
+ if ( S(link.title) ) {
644
+ Qstring(" title=\"", f);
645
+ ___mkd_reparse(T(link.title), S(link.title), INSIDE_TAG, f);
646
+ Qchar('"', f);
647
+ }
648
+
649
+ Qstring(tag->text_pfx, f);
650
+ ___mkd_reparse(T(link.tag), S(link.tag), tag->flags, f);
651
+ Qstring(tag->text_sfx, f);
652
+ }
653
+ else
654
+ Qwrite(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
655
+
656
+ return 1;
657
+ }
658
+
659
+
660
+ /* write a character to output, doing text escapes ( & -> &amp;,
661
+ * > -> &gt; < -> &lt; )
662
+ */
663
+ static void
664
+ cputc(int c, MMIOT *f)
665
+ {
666
+ switch (c) {
667
+ case '&': Qstring("&amp;", f); break;
668
+ case '>': Qstring("&gt;", f); break;
669
+ case '<': Qstring("&lt;", f); break;
670
+ default : Qchar(c, f); break;
671
+ }
672
+ }
673
+
674
+
675
+ /*
676
+ * convert an email address to a string of nonsense
677
+ */
678
+ static void
679
+ mangle(char *s, int len, MMIOT *f)
680
+ {
681
+ while ( len-- > 0 ) {
682
+ Qstring("&#", f);
683
+ Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
684
+ }
685
+ }
686
+
687
+
688
+ /* before letting a tag through, validate against
689
+ * DENY_A and DENY_IMG
690
+ */
691
+ static int
692
+ forbidden_tag(MMIOT *f)
693
+ {
694
+ int c = toupper(peek(f, 1));
695
+
696
+ if ( f->flags & DENY_HTML )
697
+ return 1;
698
+
699
+ if ( c == 'A' && (f->flags & DENY_A) && !isthisalnum(f,2) )
700
+ return 1;
701
+ if ( c == 'I' && (f->flags & DENY_IMG)
702
+ && strncasecmp(cursor(f)+1, "MG", 2) == 0
703
+ && !isthisalnum(f,4) )
704
+ return 1;
705
+ return 0;
706
+ }
707
+
708
+
709
+
710
+ /* a < may be just a regular character, the start of an embedded html
711
+ * tag, or the start of an <automatic link>. If it's an automatic
712
+ * link, we also need to know if it's an email address because if it
713
+ * is we need to mangle it in our futile attempt to cut down on the
714
+ * spaminess of the rendered page.
715
+ */
716
+ static int
717
+ maybe_tag_or_link(MMIOT *f)
718
+ {
719
+ char *text;
720
+ int c, size, i;
721
+ int maybetag=1, maybeaddress=0;
722
+ int mailto;
723
+
724
+ if ( f->flags & INSIDE_TAG )
725
+ return 0;
726
+
727
+ for ( size=0; ((c = peek(f,size+1)) != '>') && !isspace(c); size++ ) {
728
+ if ( ! (c == '/' || isalnum(c) || c == '~') )
729
+ maybetag=0;
730
+ if ( c == '@' )
731
+ maybeaddress=1;
732
+ else if ( c == EOF )
733
+ return 0;
734
+ }
735
+
736
+ if ( size == 0 )
737
+ return 0;
738
+
739
+ if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
740
+ Qstring(forbidden_tag(f) ? "&lt;" : "<", f);
741
+ while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
742
+ cputc(pull(f), f);
743
+ return 1;
744
+ }
745
+
746
+ if ( f->flags & DENY_A ) return 0;
747
+
748
+ text = cursor(f);
749
+ shift(f, size+1);
750
+
751
+ for ( i=0; i < SZAUTOPREFIX; i++ )
752
+ if ( strncasecmp(text, autoprefix[i], strlen(autoprefix[i])) == 0 ) {
753
+ Qstring("<a href=\"", f);
754
+ puturl(text,size,f);
755
+ Qstring("\">", f);
756
+ puturl(text,size,f);
757
+ Qstring("</a>", f);
758
+ return 1;
759
+ }
760
+ if ( maybeaddress ) {
761
+
762
+ Qstring("<a href=\"", f);
763
+ if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 )
764
+ mailto = 7;
765
+ else {
766
+ mailto = 0;
767
+ /* supply a mailto: protocol if one wasn't attached */
768
+ mangle("mailto:", 7, f);
769
+ }
770
+
771
+ mangle(text, size, f);
772
+ Qstring("\">", f);
773
+ mangle(text+mailto, size-mailto, f);
774
+ Qstring("</a>", f);
775
+ return 1;
776
+ }
777
+
778
+ shift(f, -(size+1));
779
+ return 0;
780
+ } /* maybe_tag_or_link */
781
+
782
+
783
+ /* smartyquote code that's common for single and double quotes
784
+ */
785
+ static int
786
+ smartyquote(int *flags, char typeofquote, MMIOT *f)
787
+ {
788
+ int bit = (typeofquote == 's') ? 0x01 : 0x02;
789
+
790
+ if ( bit & (*flags) ) {
791
+ if ( isthisnonword(f,1) ) {
792
+ Qprintf(f, "&r%cquo;", typeofquote);
793
+ (*flags) &= ~bit;
794
+ return 1;
795
+ }
796
+ }
797
+ else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) {
798
+ Qprintf(f, "&l%cquo;", typeofquote);
799
+ (*flags) |= bit;
800
+ return 1;
801
+ }
802
+ return 0;
803
+ }
804
+
805
+
806
+ static int
807
+ islike(MMIOT *f, char *s)
808
+ {
809
+ int len;
810
+ int i;
811
+
812
+ if ( s[0] == '<' ) {
813
+ if ( !isthisnonword(f, -1) )
814
+ return 0;
815
+ ++s;
816
+ }
817
+
818
+ if ( !(len = strlen(s)) )
819
+ return 0;
820
+
821
+ if ( s[len-1] == '>' ) {
822
+ if ( !isthisnonword(f,len-1) )
823
+ return 0;
824
+ len--;
825
+ }
826
+
827
+ for (i=1; i < len; i++)
828
+ if (tolower(peek(f,i)) != s[i])
829
+ return 0;
830
+ return 1;
831
+ }
832
+
833
+
834
+ static struct smarties {
835
+ char c0;
836
+ char *pat;
837
+ char *entity;
838
+ int shift;
839
+ } smarties[] = {
840
+ { '\'', "'s>", "rsquo", 0 },
841
+ { '\'', "'t>", "rsquo", 0 },
842
+ { '-', "--", "mdash", 1 },
843
+ { '-', "<->", "ndash", 0 },
844
+ { '.', "...", "hellip", 2 },
845
+ { '.', ". . .", "hellip", 4 },
846
+ { '(', "(c)", "copy", 2 },
847
+ { '(', "(r)", "reg", 2 },
848
+ { '(', "(tm)", "trade", 3 },
849
+ { '3', "<3/4>", "frac34", 2 },
850
+ { '3', "<3/4ths>", "frac34", 2 },
851
+ { '1', "<1/2>", "frac12", 2 },
852
+ { '1', "<1/4>", "frac14", 2 },
853
+ { '1', "<1/4th>", "frac14", 2 },
854
+ { '&', "&#0;", 0, 3 },
855
+ } ;
856
+ #define NRSMART ( sizeof smarties / sizeof smarties[0] )
857
+
858
+
859
+ /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
860
+ */
861
+ static int
862
+ smartypants(int c, int *flags, MMIOT *f)
863
+ {
864
+ int i;
865
+
866
+ if ( f->flags & DENY_SMARTY )
867
+ return 0;
868
+
869
+ for ( i=0; i < NRSMART; i++)
870
+ if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) {
871
+ if ( smarties[i].entity )
872
+ Qprintf(f, "&%s;", smarties[i].entity);
873
+ shift(f, smarties[i].shift);
874
+ return 1;
875
+ }
876
+
877
+ switch (c) {
878
+ case '<' : return 0;
879
+ case '\'': if ( smartyquote(flags, 's', f) ) return 1;
880
+ break;
881
+
882
+ case '"': if ( smartyquote(flags, 'd', f) ) return 1;
883
+ break;
884
+
885
+ case '`': if ( peek(f, 1) == '`' ) {
886
+ int j = 2;
887
+
888
+ while ( (c=peek(f,j)) != EOF ) {
889
+ if ( c == '\\' )
890
+ j += 2;
891
+ else if ( c == '`' )
892
+ break;
893
+ else if ( c == '\'' && peek(f, j+1) == '\'' ) {
894
+ Qstring("&ldquo;", f);
895
+ ___mkd_reparse(cursor(f)+1, j-2, 0, f);
896
+ Qstring("&rdquo;", f);
897
+ shift(f,j+1);
898
+ return 1;
899
+ }
900
+ else ++j;
901
+ }
902
+
903
+ }
904
+ break;
905
+ }
906
+ return 0;
907
+ } /* smartypants */
908
+
909
+
910
+ #define tag_text(f) (f->flags & INSIDE_TAG)
911
+
912
+
913
+ static void
914
+ text(MMIOT *f)
915
+ {
916
+ int c, j;
917
+ int rep;
918
+ int smartyflags = 0;
919
+
920
+ while ( (c = pull(f)) != EOF ) {
921
+ if ( smartypants(c, &smartyflags, f) )
922
+ continue;
923
+ switch (c) {
924
+ case 0: break;
925
+
926
+ case '>': if ( tag_text(f) )
927
+ Qstring("&gt;", f);
928
+ else
929
+ Qchar(c, f);
930
+ break;
931
+
932
+ case '"': if ( tag_text(f) )
933
+ Qstring("&quot;", f);
934
+ else
935
+ Qchar(c, f);
936
+ break;
937
+
938
+ case '!': if ( peek(f,1) == '[' ) {
939
+ pull(f);
940
+ if ( tag_text(f) || !linkylinky(1, f) )
941
+ Qstring("![", f);
942
+ }
943
+ else
944
+ Qchar(c, f);
945
+ break;
946
+ case '[': if ( tag_text(f) || !linkylinky(0, f) )
947
+ Qchar(c, f);
948
+ break;
949
+ #if SUPERSCRIPT
950
+ /* A^B -> A<sup>B</sup> */
951
+ case '^': if ( (f->flags & (STRICT|INSIDE_TAG)) || isthisspace(f,-1) || isthisspace(f,1) )
952
+ Qchar(c,f);
953
+ else {
954
+ char *sup = cursor(f);
955
+ int len = 0;
956
+ Qstring("<sup>",f);
957
+ while ( !isthisspace(f,1+len) ) {
958
+ ++len;
959
+ }
960
+ shift(f,len);
961
+ ___mkd_reparse(sup, len, 0, f);
962
+ Qstring("</sup>", f);
963
+ }
964
+ break;
965
+ #endif
966
+ case '_':
967
+ #if RELAXED_EMPHASIS
968
+ /* Underscores don't count if they're in the middle of a word */
969
+ if ( (!(f->flags & STRICT))
970
+ && ((isthisspace(f,-1) && isthisspace(f,1))
971
+ || (isthisalnum(f,-1) && isthisalnum(f,1))) ){
972
+ Qchar(c, f);
973
+ break;
974
+ }
975
+ /* else fall into the regular old emphasis case */
976
+ #endif
977
+ case '*': if ( tag_text(f) )
978
+ Qchar(c, f);
979
+ else {
980
+ for (rep = 1; peek(f,1) == c; pull(f) )
981
+ ++rep;
982
+ Qem(f,c,rep);
983
+ }
984
+ break;
985
+
986
+ case '`': if ( tag_text(f) || !iscodeblock(f) )
987
+ Qchar(c, f);
988
+ else {
989
+ Qstring("<code>", f);
990
+ if ( peek(f, 1) == '`' ) {
991
+ pull(f);
992
+ code(2, f);
993
+ }
994
+ else
995
+ code(1, f);
996
+ Qstring("</code>", f);
997
+ }
998
+ break;
999
+
1000
+ case '\\': switch ( c = pull(f) ) {
1001
+ case '&': Qstring("&amp;", f);
1002
+ break;
1003
+ case '<': Qstring("&lt;", f);
1004
+ break;
1005
+ case '\\':
1006
+ case '>': case '#': case '.': case '-':
1007
+ case '+': case '{': case '}': case ']':
1008
+ case '(': case ')': case '"': case '\'':
1009
+ case '!': case '[': case '*': case '_':
1010
+ case '`': Qchar(c, f);
1011
+ break;
1012
+ default:
1013
+ Qchar('\\', f);
1014
+ if ( c != EOF )
1015
+ shift(f,-1);
1016
+ break;
1017
+ }
1018
+ break;
1019
+
1020
+ case '<': if ( !maybe_tag_or_link(f) )
1021
+ Qstring("&lt;", f);
1022
+ break;
1023
+
1024
+ case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
1025
+ while ( isthisalnum(f,j) )
1026
+ ++j;
1027
+
1028
+ if ( peek(f,j) != ';' )
1029
+ Qstring("&amp;", f);
1030
+ else
1031
+ Qchar(c, f);
1032
+ break;
1033
+
1034
+ default: Qchar(c, f);
1035
+ break;
1036
+ }
1037
+ }
1038
+ /* truncate the input string after we've finished processing it */
1039
+ S(f->in) = f->isp = 0;
1040
+ } /* text */
1041
+
1042
+
1043
+ static int
1044
+ iscodeblock(MMIOT *f)
1045
+ {
1046
+ int i=1, single = 1, c;
1047
+
1048
+ if ( peek(f,i) == '`' ) {
1049
+ single=0;
1050
+ i++;
1051
+ }
1052
+ while ( (c=peek(f,i)) != EOF ) {
1053
+ if ( (c == '`') && (single || peek(f,i+1) == '`') )
1054
+ return 1;
1055
+ else if ( c == '\\' )
1056
+ i++;
1057
+ i++;
1058
+ }
1059
+ return 0;
1060
+
1061
+ }
1062
+
1063
+ static int
1064
+ endofcode(int escape, int offset, MMIOT *f)
1065
+ {
1066
+ switch (escape) {
1067
+ case 2: if ( peek(f, offset+1) == '`' ) {
1068
+ shift(f,1);
1069
+ case 1: shift(f,offset);
1070
+ return 1;
1071
+ }
1072
+ default:return 0;
1073
+ }
1074
+ }
1075
+
1076
+
1077
+ /* the only characters that have special meaning in a code block are
1078
+ * `<' and `&' , which are /always/ expanded to &lt; and &amp;
1079
+ */
1080
+ static void
1081
+ code(int escape, MMIOT *f)
1082
+ {
1083
+ int c;
1084
+
1085
+ if ( escape && (peek(f,1) == ' ') )
1086
+ shift(f,1);
1087
+
1088
+ while ( (c = pull(f)) != EOF ) {
1089
+ switch (c) {
1090
+ case ' ': if ( peek(f,1) == '`' && endofcode(escape, 1, f) )
1091
+ return;
1092
+ Qchar(c, f);
1093
+ break;
1094
+
1095
+ case '`': if ( endofcode(escape, 0, f) )
1096
+ return;
1097
+ Qchar(c, f);
1098
+ break;
1099
+
1100
+ case '\\': cputc(c, f);
1101
+ if ( peek(f,1) == '>' || (c = pull(f)) == EOF )
1102
+ break;
1103
+
1104
+ default: cputc(c, f);
1105
+ break;
1106
+ }
1107
+ }
1108
+ } /* code */
1109
+
1110
+
1111
+ /* print a header block
1112
+ */
1113
+ static void
1114
+ printheader(Paragraph *pp, MMIOT *f)
1115
+ {
1116
+ Qprintf(f, "<h%d", pp->hnumber);
1117
+ if ( f->flags & TOC ) {
1118
+ Qprintf(f, " id=\"", pp->hnumber);
1119
+ mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), Qchar, f);
1120
+ Qchar('"', f);
1121
+ }
1122
+ Qchar('>', f);
1123
+ push(T(pp->text->text), S(pp->text->text), f);
1124
+ text(f);
1125
+ Qprintf(f, "</h%d>", pp->hnumber);
1126
+ }
1127
+
1128
+
1129
+ static int
1130
+ printblock(Paragraph *pp, MMIOT *f)
1131
+ {
1132
+ Line *t = pp->text;
1133
+ static char *Begin[] = { "", "<p>", "<center>" };
1134
+ static char *End[] = { "", "</p>","</center>" };
1135
+
1136
+ while (t) {
1137
+ if ( S(t->text) ) {
1138
+ if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
1139
+ && T(t->text)[S(t->text)-1] == ' ') {
1140
+ push(T(t->text), S(t->text)-2, f);
1141
+ push("<br/>\n", 6, f);
1142
+ }
1143
+ else {
1144
+ ___mkd_tidy(t);
1145
+ push(T(t->text), S(t->text), f);
1146
+ if ( t->next )
1147
+ push("\n", 1, f);
1148
+ }
1149
+ }
1150
+ t = t->next;
1151
+ }
1152
+ Qstring(Begin[pp->align], f);
1153
+ text(f);
1154
+ Qstring(End[pp->align], f);
1155
+ return 1;
1156
+ }
1157
+
1158
+
1159
+ static void
1160
+ printcode(Line *t, MMIOT *f)
1161
+ {
1162
+ int blanks;
1163
+
1164
+ for ( blanks = 0; t ; t = t->next )
1165
+ if ( S(t->text) > t->dle ) {
1166
+ while ( blanks ) {
1167
+ push("\n", 1, f);
1168
+ --blanks;
1169
+ }
1170
+ push(T(t->text), S(t->text), f);
1171
+ push("\n", 1, f);
1172
+ }
1173
+ else blanks++;
1174
+
1175
+ Qstring("<pre><code>", f);
1176
+ code(0, f);
1177
+ Qstring("</code></pre>", f);
1178
+ }
1179
+
1180
+
1181
+ static void
1182
+ printhtml(Line *t, MMIOT *f)
1183
+ {
1184
+ int blanks;
1185
+
1186
+ for ( blanks=0; t ; t = t->next )
1187
+ if ( S(t->text) ) {
1188
+ for ( ; blanks; --blanks )
1189
+ Qchar('\n', f);
1190
+
1191
+ Qwrite(T(t->text), S(t->text), f);
1192
+ Qchar('\n', f);
1193
+ }
1194
+ else
1195
+ blanks++;
1196
+ }
1197
+
1198
+
1199
+ static void
1200
+ htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
1201
+ {
1202
+ ___mkd_emblock(f);
1203
+ if ( block )
1204
+ Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
1205
+ ___mkd_emblock(f);
1206
+
1207
+ while (( p = display(p, f) )) {
1208
+ ___mkd_emblock(f);
1209
+ Qstring("\n\n", f);
1210
+ }
1211
+
1212
+ if ( block )
1213
+ Qprintf(f, "</%s>", block);
1214
+ ___mkd_emblock(f);
1215
+ }
1216
+
1217
+
1218
+ #if DL_TAG_EXTENSION
1219
+ static void
1220
+ definitionlist(Paragraph *p, MMIOT *f)
1221
+ {
1222
+ Line *tag;
1223
+
1224
+ if ( p ) {
1225
+ Qstring("<dl>\n", f);
1226
+
1227
+ for ( ; p ; p = p->next) {
1228
+ for ( tag = p->text; tag; tag = tag->next ) {
1229
+ Qstring("<dt>", f);
1230
+ ___mkd_reparse(T(tag->text), S(tag->text), 0, f);
1231
+ Qstring("</dt>\n", f);
1232
+ }
1233
+
1234
+ htmlify(p->down, "dd", p->ident, f);
1235
+ }
1236
+
1237
+ Qstring("</dl>", f);
1238
+ }
1239
+ }
1240
+ #endif
1241
+
1242
+
1243
+ static void
1244
+ listdisplay(int typ, Paragraph *p, MMIOT* f)
1245
+ {
1246
+ if ( p ) {
1247
+ Qprintf(f, "<%cl", (typ==UL)?'u':'o');
1248
+ if ( typ == AL )
1249
+ Qprintf(f, " type=a");
1250
+ Qprintf(f, ">\n");
1251
+
1252
+ for ( ; p ; p = p->next ) {
1253
+ htmlify(p->down, "li", p->ident, f);
1254
+ Qchar('\n', f);
1255
+ }
1256
+
1257
+ Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o');
1258
+ }
1259
+ }
1260
+
1261
+
1262
+ /* dump out a Paragraph in the desired manner
1263
+ */
1264
+ static Paragraph*
1265
+ display(Paragraph *p, MMIOT *f)
1266
+ {
1267
+ if ( !p ) return 0;
1268
+
1269
+ switch ( p->typ ) {
1270
+ case STYLE:
1271
+ case WHITESPACE:
1272
+ break;
1273
+
1274
+ case HTML:
1275
+ printhtml(p->text, f);
1276
+ break;
1277
+
1278
+ case CODE:
1279
+ printcode(p->text, f);
1280
+ break;
1281
+
1282
+ case QUOTE:
1283
+ htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
1284
+ break;
1285
+
1286
+ case UL:
1287
+ case OL:
1288
+ case AL:
1289
+ listdisplay(p->typ, p->down, f);
1290
+ break;
1291
+
1292
+ #if DL_TAG_EXTENSION
1293
+ case DL:
1294
+ definitionlist(p->down, f);
1295
+ break;
1296
+ #endif
1297
+
1298
+ case HR:
1299
+ Qstring("<hr />", f);
1300
+ break;
1301
+
1302
+ case HDR:
1303
+ printheader(p, f);
1304
+ break;
1305
+
1306
+ default:
1307
+ printblock(p, f);
1308
+ break;
1309
+ }
1310
+ return p->next;
1311
+ }
1312
+
1313
+
1314
+ /*
1315
+ * dump out stylesheet sections.
1316
+ */
1317
+ static int
1318
+ stylesheets(Paragraph *p, FILE *f)
1319
+ {
1320
+ Line* q;
1321
+
1322
+ for ( ; p ; p = p->next ) {
1323
+ if ( p->typ == STYLE ) {
1324
+ for ( q = p->text; q ; q = q->next )
1325
+ if ( fwrite(T(q->text), S(q->text), 1, f) == 1 )
1326
+ putc('\n', f);
1327
+ else
1328
+ return EOF;
1329
+ }
1330
+ if ( p->down && (stylesheets(p->down, f) == EOF) )
1331
+ return EOF;
1332
+ }
1333
+ return 0;
1334
+ }
1335
+
1336
+
1337
+ /* return a pointer to the compiled markdown
1338
+ * document.
1339
+ */
1340
+ int
1341
+ mkd_document(Document *p, char **res)
1342
+ {
1343
+ if ( p && p->compiled ) {
1344
+ if ( ! p->html ) {
1345
+ htmlify(p->code, 0, 0, p->ctx);
1346
+ p->html = 1;
1347
+ }
1348
+
1349
+ *res = T(p->ctx->out);
1350
+ return S(p->ctx->out);
1351
+ }
1352
+ return EOF;
1353
+ }
1354
+
1355
+
1356
+ /* public interface for ___mkd_reparse()
1357
+ */
1358
+ int
1359
+ mkd_text(char *bfr, int size, FILE *output, int flags)
1360
+ {
1361
+ MMIOT f;
1362
+
1363
+ ___mkd_initmmiot(&f, 0);
1364
+ f.flags = flags & USER_FLAGS;
1365
+
1366
+ ___mkd_reparse(bfr, size, 0, &f);
1367
+ ___mkd_emblock(&f);
1368
+ if ( flags & CDATA_OUTPUT )
1369
+ ___mkd_xml(T(f.out), S(f.out), output);
1370
+ else
1371
+ fwrite(T(f.out), S(f.out), 1, output);
1372
+
1373
+ ___mkd_freemmiot(&f, 0);
1374
+ return 0;
1375
+ }
1376
+
1377
+
1378
+ /* dump any embedded styles
1379
+ */
1380
+ int
1381
+ mkd_style(Document *d, FILE *f)
1382
+ {
1383
+ if ( d && d->compiled )
1384
+ return stylesheets(d->code, f);
1385
+ return EOF;
1386
+ }
1387
+