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