bluecloth 2.0.0

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