|
61079c3…
|
mistachkin
|
1 |
/* |
|
61079c3…
|
mistachkin
|
2 |
** Copyright (c) 2012 D. Richard Hipp |
|
61079c3…
|
mistachkin
|
3 |
** |
|
61079c3…
|
mistachkin
|
4 |
** This program is free software; you can redistribute it and/or |
|
61079c3…
|
mistachkin
|
5 |
** modify it under the terms of the Simplified BSD License (also |
|
61079c3…
|
mistachkin
|
6 |
** known as the "2-Clause License" or "FreeBSD License".) |
|
61079c3…
|
mistachkin
|
7 |
|
|
61079c3…
|
mistachkin
|
8 |
** This program is distributed in the hope that it will be useful, |
|
61079c3…
|
mistachkin
|
9 |
** but without any warranty; without even the implied warranty of |
|
61079c3…
|
mistachkin
|
10 |
** merchantability or fitness for a particular purpose. |
|
61079c3…
|
mistachkin
|
11 |
** |
|
61079c3…
|
mistachkin
|
12 |
** Author contact information: |
|
61079c3…
|
mistachkin
|
13 |
** [email protected] |
|
61079c3…
|
mistachkin
|
14 |
** http://www.hwaci.com/drh/ |
|
61079c3…
|
mistachkin
|
15 |
** |
|
61079c3…
|
mistachkin
|
16 |
******************************************************************************* |
|
61079c3…
|
mistachkin
|
17 |
** |
|
61079c3…
|
mistachkin
|
18 |
** This file contains callbacks for the markdown parser that generate |
|
61079c3…
|
mistachkin
|
19 |
** XHTML output. |
|
61079c3…
|
mistachkin
|
20 |
*/ |
|
61079c3…
|
mistachkin
|
21 |
|
|
61079c3…
|
mistachkin
|
22 |
#include "config.h" |
|
61079c3…
|
mistachkin
|
23 |
#include "markdown_html.h" |
|
61079c3…
|
mistachkin
|
24 |
|
|
61079c3…
|
mistachkin
|
25 |
#if INTERFACE |
|
61079c3…
|
mistachkin
|
26 |
|
|
61079c3…
|
mistachkin
|
27 |
void markdown_to_html( |
|
61079c3…
|
mistachkin
|
28 |
struct Blob *input_markdown, |
|
61079c3…
|
mistachkin
|
29 |
struct Blob *output_title, |
|
61079c3…
|
mistachkin
|
30 |
struct Blob *output_body); |
|
61079c3…
|
mistachkin
|
31 |
|
|
61079c3…
|
mistachkin
|
32 |
#endif /* INTERFACE */ |
|
61079c3…
|
mistachkin
|
33 |
|
|
b61dcef…
|
drh
|
34 |
/* |
|
3990518…
|
george
|
35 |
** Markdown-internal helper for generating unique link reference IDs. |
|
3990518…
|
george
|
36 |
** Fields provide typed interpretation of the underline memory buffer. |
|
3990518…
|
george
|
37 |
*/ |
|
3990518…
|
george
|
38 |
typedef union bitfield64_t bitfield64_t; |
|
3990518…
|
george
|
39 |
union bitfield64_t{ |
|
3990518…
|
george
|
40 |
char c[8]; /* interpret as the array of signed characters */ |
|
3990518…
|
george
|
41 |
unsigned char b[8]; /* interpret as the array of unsigned characters */ |
|
3990518…
|
george
|
42 |
}; |
|
3990518…
|
george
|
43 |
|
|
3990518…
|
george
|
44 |
/* |
|
b61dcef…
|
drh
|
45 |
** An instance of the following structure is passed through the |
|
b61dcef…
|
drh
|
46 |
** "opaque" pointer. |
|
b61dcef…
|
drh
|
47 |
*/ |
|
b61dcef…
|
drh
|
48 |
typedef struct MarkdownToHtml MarkdownToHtml; |
|
b61dcef…
|
drh
|
49 |
struct MarkdownToHtml { |
|
b61dcef…
|
drh
|
50 |
Blob *output_title; /* Store the title here */ |
|
3990518…
|
george
|
51 |
bitfield64_t unique; /* Enables construction of unique #id elements */ |
|
3990518…
|
george
|
52 |
|
|
3990518…
|
george
|
53 |
#ifndef FOOTNOTES_WITHOUT_URI |
|
3990518…
|
george
|
54 |
Blob reqURI; /* REQUEST_URI with escaped quotes */ |
|
3990518…
|
george
|
55 |
#endif |
|
b61dcef…
|
drh
|
56 |
}; |
|
b61dcef…
|
drh
|
57 |
|
|
61079c3…
|
mistachkin
|
58 |
|
|
61079c3…
|
mistachkin
|
59 |
/* INTER_BLOCK -- skip a line between block level elements */ |
|
61079c3…
|
mistachkin
|
60 |
#define INTER_BLOCK(ob) \ |
|
48c47e1…
|
drh
|
61 |
do { if( blob_size(ob)>0 ) blob_append_char(ob, '\n'); } while (0) |
|
48c47e1…
|
drh
|
62 |
|
|
3990518…
|
george
|
63 |
/* |
|
3990518…
|
george
|
64 |
** FOOTNOTES_WITHOUT_URI macro was introduced by [2c1f8f3592ef00e0] |
|
3990518…
|
george
|
65 |
** to enable flexibility in rendering of footnote-specific hyperlinks. |
|
3990518…
|
george
|
66 |
** It may be defined for a particular build in order to omit |
|
3990518…
|
george
|
67 |
** full REQUEST_URIs within footnote-specific (and page-local) hyperlinks. |
|
3990518…
|
george
|
68 |
** This *is* used for the builds that incorporate 'base-href-fix' branch |
|
3990518…
|
george
|
69 |
** (which in turn fixes footnotes on the preview tab of /wikiedit page). |
|
3990518…
|
george
|
70 |
*/ |
|
3990518…
|
george
|
71 |
#ifndef FOOTNOTES_WITHOUT_URI |
|
3990518…
|
george
|
72 |
#define BLOB_APPEND_URI(dest,ctx) blob_appendb(dest,&((ctx)->reqURI)) |
|
3990518…
|
george
|
73 |
#else |
|
3990518…
|
george
|
74 |
#define BLOB_APPEND_URI(dest,ctx) |
|
3990518…
|
george
|
75 |
#endif |
|
3990518…
|
george
|
76 |
|
|
3990518…
|
george
|
77 |
/* Converts an integer to a textual base26 representation |
|
3990518…
|
george
|
78 |
** with proper null-termination. |
|
3990518…
|
george
|
79 |
* Return empty string if that integer is negative. */ |
|
3990518…
|
george
|
80 |
static bitfield64_t to_base26(int i, int uppercase){ |
|
3990518…
|
george
|
81 |
bitfield64_t x; |
|
3990518…
|
george
|
82 |
int j; |
|
3990518…
|
george
|
83 |
memset( &x, 0, sizeof(x) ); |
|
3990518…
|
george
|
84 |
if( i >= 0 ){ |
|
3990518…
|
george
|
85 |
for(j=7; j >= 0; j--){ |
|
3990518…
|
george
|
86 |
x.b[j] = (unsigned char)(uppercase?'A':'a') + i%26; |
|
3990518…
|
george
|
87 |
if( (i /= 26) == 0 ) break; |
|
3990518…
|
george
|
88 |
} |
|
3990518…
|
george
|
89 |
assert( j > 0 ); /* because 2^32 < 26^7 */ |
|
3990518…
|
george
|
90 |
for(i=0; i<8-j; i++) x.b[i] = x.b[i+j]; |
|
3990518…
|
george
|
91 |
for( ; i<8 ; i++) x.b[i] = 0; |
|
3990518…
|
george
|
92 |
} |
|
3990518…
|
george
|
93 |
assert( x.c[7] == 0 ); |
|
3990518…
|
george
|
94 |
return x; |
|
3990518…
|
george
|
95 |
} |
|
7950dc2…
|
drh
|
96 |
|
|
7950dc2…
|
drh
|
97 |
/* HTML escapes |
|
7950dc2…
|
drh
|
98 |
** |
|
7950dc2…
|
drh
|
99 |
** html_escape() converts < to <, > to >, and & to &. |
|
7950dc2…
|
drh
|
100 |
** html_quote() goes further and converts " into " and ' in '. |
|
7950dc2…
|
drh
|
101 |
*/ |
|
7950dc2…
|
drh
|
102 |
static void html_quote(struct Blob *ob, const char *data, size_t size){ |
|
61079c3…
|
mistachkin
|
103 |
size_t beg = 0, i = 0; |
|
61079c3…
|
mistachkin
|
104 |
while( i<size ){ |
|
61079c3…
|
mistachkin
|
105 |
beg = i; |
|
61079c3…
|
mistachkin
|
106 |
while( i<size |
|
61079c3…
|
mistachkin
|
107 |
&& data[i]!='<' |
|
61079c3…
|
mistachkin
|
108 |
&& data[i]!='>' |
|
61079c3…
|
mistachkin
|
109 |
&& data[i]!='"' |
|
61079c3…
|
mistachkin
|
110 |
&& data[i]!='&' |
|
7950dc2…
|
drh
|
111 |
&& data[i]!='\'' |
|
7950dc2…
|
drh
|
112 |
){ |
|
7950dc2…
|
drh
|
113 |
i++; |
|
7950dc2…
|
drh
|
114 |
} |
|
7950dc2…
|
drh
|
115 |
blob_append(ob, data+beg, i-beg); |
|
7950dc2…
|
drh
|
116 |
while( i<size ){ |
|
7950dc2…
|
drh
|
117 |
if( data[i]=='<' ){ |
|
3990518…
|
george
|
118 |
blob_append_literal(ob, "<"); |
|
7950dc2…
|
drh
|
119 |
}else if( data[i]=='>' ){ |
|
3990518…
|
george
|
120 |
blob_append_literal(ob, ">"); |
|
7950dc2…
|
drh
|
121 |
}else if( data[i]=='&' ){ |
|
3990518…
|
george
|
122 |
blob_append_literal(ob, "&"); |
|
7950dc2…
|
drh
|
123 |
}else if( data[i]=='"' ){ |
|
3990518…
|
george
|
124 |
blob_append_literal(ob, """); |
|
7950dc2…
|
drh
|
125 |
}else if( data[i]=='\'' ){ |
|
3990518…
|
george
|
126 |
blob_append_literal(ob, "'"); |
|
7950dc2…
|
drh
|
127 |
}else{ |
|
7950dc2…
|
drh
|
128 |
break; |
|
7950dc2…
|
drh
|
129 |
} |
|
7950dc2…
|
drh
|
130 |
i++; |
|
7950dc2…
|
drh
|
131 |
} |
|
7950dc2…
|
drh
|
132 |
} |
|
7950dc2…
|
drh
|
133 |
} |
|
7950dc2…
|
drh
|
134 |
static void html_escape(struct Blob *ob, const char *data, size_t size){ |
|
7950dc2…
|
drh
|
135 |
size_t beg = 0, i = 0; |
|
7950dc2…
|
drh
|
136 |
while( i<size ){ |
|
7950dc2…
|
drh
|
137 |
beg = i; |
|
7950dc2…
|
drh
|
138 |
while( i<size |
|
7950dc2…
|
drh
|
139 |
&& data[i]!='<' |
|
7950dc2…
|
drh
|
140 |
&& data[i]!='>' |
|
7950dc2…
|
drh
|
141 |
&& data[i]!='&' |
|
61079c3…
|
mistachkin
|
142 |
){ |
|
61079c3…
|
mistachkin
|
143 |
i++; |
|
61079c3…
|
mistachkin
|
144 |
} |
|
61079c3…
|
mistachkin
|
145 |
blob_append(ob, data+beg, i-beg); |
|
61079c3…
|
mistachkin
|
146 |
while( i<size ){ |
|
61079c3…
|
mistachkin
|
147 |
if( data[i]=='<' ){ |
|
3990518…
|
george
|
148 |
blob_append_literal(ob, "<"); |
|
61079c3…
|
mistachkin
|
149 |
}else if( data[i]=='>' ){ |
|
3990518…
|
george
|
150 |
blob_append_literal(ob, ">"); |
|
61079c3…
|
mistachkin
|
151 |
}else if( data[i]=='&' ){ |
|
3990518…
|
george
|
152 |
blob_append_literal(ob, "&"); |
|
61079c3…
|
mistachkin
|
153 |
}else{ |
|
61079c3…
|
mistachkin
|
154 |
break; |
|
61079c3…
|
mistachkin
|
155 |
} |
|
61079c3…
|
mistachkin
|
156 |
i++; |
|
61079c3…
|
mistachkin
|
157 |
} |
|
61079c3…
|
mistachkin
|
158 |
} |
|
61079c3…
|
mistachkin
|
159 |
} |
|
61079c3…
|
mistachkin
|
160 |
|
|
61079c3…
|
mistachkin
|
161 |
|
|
61079c3…
|
mistachkin
|
162 |
/* HTML block tags */ |
|
61079c3…
|
mistachkin
|
163 |
|
|
e214a57…
|
drh
|
164 |
/* Size of the prolog: "<div class='markdown'>\n" */ |
|
e214a57…
|
drh
|
165 |
#define PROLOG_SIZE 23 |
|
e214a57…
|
drh
|
166 |
|
|
e061a67…
|
mistachkin
|
167 |
static void html_prolog(struct Blob *ob, void *opaque){ |
|
e061a67…
|
mistachkin
|
168 |
INTER_BLOCK(ob); |
|
3990518…
|
george
|
169 |
blob_append_literal(ob, "<div class=\"markdown\">\n"); |
|
e214a57…
|
drh
|
170 |
assert( blob_size(ob)==PROLOG_SIZE ); |
|
e061a67…
|
mistachkin
|
171 |
} |
|
e061a67…
|
mistachkin
|
172 |
|
|
e061a67…
|
mistachkin
|
173 |
static void html_epilog(struct Blob *ob, void *opaque){ |
|
e061a67…
|
mistachkin
|
174 |
INTER_BLOCK(ob); |
|
3990518…
|
george
|
175 |
blob_append_literal(ob, "</div>\n"); |
|
e061a67…
|
mistachkin
|
176 |
} |
|
e061a67…
|
mistachkin
|
177 |
|
|
485fda6…
|
drh
|
178 |
static void html_blockhtml(struct Blob *ob, struct Blob *text, void *opaque){ |
|
61079c3…
|
mistachkin
|
179 |
char *data = blob_buffer(text); |
|
cc7f4df…
|
drh
|
180 |
size_t size = blob_size(text); |
|
b61dcef…
|
drh
|
181 |
Blob *title = ((MarkdownToHtml*)opaque)->output_title; |
|
cc7f4df…
|
drh
|
182 |
while( size>0 && fossil_isspace(data[0]) ){ data++; size--; } |
|
cc7f4df…
|
drh
|
183 |
while( size>0 && fossil_isspace(data[size-1]) ){ size--; } |
|
cc7f4df…
|
drh
|
184 |
/* If the first raw block is an <h1> element, then use it as the title. */ |
|
cc7f4df…
|
drh
|
185 |
if( blob_size(ob)<=PROLOG_SIZE |
|
cc7f4df…
|
drh
|
186 |
&& size>9 |
|
d407c38…
|
drh
|
187 |
&& title!=0 |
|
cc7f4df…
|
drh
|
188 |
&& sqlite3_strnicmp("<h1",data,3)==0 |
|
cc7f4df…
|
drh
|
189 |
&& sqlite3_strnicmp("</h1>", &data[size-5],5)==0 |
|
cc7f4df…
|
drh
|
190 |
){ |
|
3f99bca…
|
drh
|
191 |
int nTag = html_tag_length(data); |
|
cc7f4df…
|
drh
|
192 |
blob_append(title, data+nTag, size - nTag - 5); |
|
d407c38…
|
drh
|
193 |
return; |
|
cc7f4df…
|
drh
|
194 |
} |
|
61079c3…
|
mistachkin
|
195 |
INTER_BLOCK(ob); |
|
382f373…
|
drh
|
196 |
blob_append(ob, data, size); |
|
3990518…
|
george
|
197 |
blob_append_literal(ob, "\n"); |
|
61079c3…
|
mistachkin
|
198 |
} |
|
61079c3…
|
mistachkin
|
199 |
|
|
61079c3…
|
mistachkin
|
200 |
static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){ |
|
61079c3…
|
mistachkin
|
201 |
INTER_BLOCK(ob); |
|
3990518…
|
george
|
202 |
blob_append_literal(ob, "<pre><code>"); |
|
61079c3…
|
mistachkin
|
203 |
html_escape(ob, blob_buffer(text), blob_size(text)); |
|
3990518…
|
george
|
204 |
blob_append_literal(ob, "</code></pre>\n"); |
|
61079c3…
|
mistachkin
|
205 |
} |
|
61079c3…
|
mistachkin
|
206 |
|
|
61079c3…
|
mistachkin
|
207 |
static void html_blockquote(struct Blob *ob, struct Blob *text, void *opaque){ |
|
61079c3…
|
mistachkin
|
208 |
INTER_BLOCK(ob); |
|
3990518…
|
george
|
209 |
blob_append_literal(ob, "<blockquote>\n"); |
|
3990518…
|
george
|
210 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
211 |
blob_append_literal(ob, "</blockquote>\n"); |
|
61079c3…
|
mistachkin
|
212 |
} |
|
61079c3…
|
mistachkin
|
213 |
|
|
61079c3…
|
mistachkin
|
214 |
static void html_header( |
|
61079c3…
|
mistachkin
|
215 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
216 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
217 |
int level, |
|
61079c3…
|
mistachkin
|
218 |
void *opaque |
|
61079c3…
|
mistachkin
|
219 |
){ |
|
b61dcef…
|
drh
|
220 |
struct Blob *title = ((MarkdownToHtml*)opaque)->output_title; |
|
f9ead75…
|
drh
|
221 |
char *z = 0; |
|
f9ead75…
|
drh
|
222 |
int i,j; |
|
61079c3…
|
mistachkin
|
223 |
/* The first header at the beginning of a text is considered as |
|
61079c3…
|
mistachkin
|
224 |
* a title and not output. */ |
|
d407c38…
|
drh
|
225 |
if( blob_size(ob)<=PROLOG_SIZE && title!=0 && blob_size(title)==0 ){ |
|
3990518…
|
george
|
226 |
blob_appendb(title, text); |
|
d407c38…
|
drh
|
227 |
return; |
|
61079c3…
|
mistachkin
|
228 |
} |
|
61079c3…
|
mistachkin
|
229 |
INTER_BLOCK(ob); |
|
f9ead75…
|
drh
|
230 |
z = fossil_strdup(blob_buffer(text)); |
|
f9ead75…
|
drh
|
231 |
if( z==0 ){ |
|
f9ead75…
|
drh
|
232 |
j = 0; |
|
f9ead75…
|
drh
|
233 |
}else{ |
|
f9ead75…
|
drh
|
234 |
/* |
|
f9ead75…
|
drh
|
235 |
** The GitHub "slugify" algorithm converts the text of a markdown header |
|
f9ead75…
|
drh
|
236 |
** into a ID for that header. The algorithm is: |
|
f9ead75…
|
drh
|
237 |
** |
|
f9ead75…
|
drh
|
238 |
** 1. ASCII alphanumerics -> convert to lower case |
|
f9ead75…
|
drh
|
239 |
** 2. Spaces, hyphens, underscores -> convert to '-' |
|
f9ead75…
|
drh
|
240 |
** 3. Non-ASCII -> preserve as-is |
|
f9ead75…
|
drh
|
241 |
** 4. Other punctuation -> remove |
|
f9ead75…
|
drh
|
242 |
** 5. Multiple consecutive dashes -> collapse to one |
|
f9ead75…
|
drh
|
243 |
** 6. Leading and trailing dashes -> remove |
|
f9ead75…
|
drh
|
244 |
** 7. Markup <...> and &...; -> remove |
|
f9ead75…
|
drh
|
245 |
** |
|
f9ead75…
|
drh
|
246 |
** This implementation does the conversion in-place. |
|
f9ead75…
|
drh
|
247 |
*/ |
|
f9ead75…
|
drh
|
248 |
for(i=j=0; z[i]; i++){ |
|
f9ead75…
|
drh
|
249 |
if( fossil_isalnum(z[i]) ){ |
|
f9ead75…
|
drh
|
250 |
z[j++] = fossil_tolower(z[i]); |
|
f9ead75…
|
drh
|
251 |
}else if( fossil_isspace(z[i]) || z[i]=='-' || z[i]=='_' ){ |
|
f9ead75…
|
drh
|
252 |
if( j>0 && z[j-1]!='-' ) z[j++] = '-'; |
|
f9ead75…
|
drh
|
253 |
}else if( z[i]=='<' ){ |
|
f9ead75…
|
drh
|
254 |
do{ i++; }while( z[i]!=0 && z[i]!='>' ); |
|
f9ead75…
|
drh
|
255 |
}else if( z[i]=='&' ){ |
|
f9ead75…
|
drh
|
256 |
do{ i++; }while( z[i]!=0 && z[i]!=';' ); |
|
f9ead75…
|
drh
|
257 |
}else if( (z[i]&0x80)!=0 ){ |
|
f9ead75…
|
drh
|
258 |
z[j++] = z[i]; |
|
f9ead75…
|
drh
|
259 |
} |
|
f9ead75…
|
drh
|
260 |
} |
|
f9ead75…
|
drh
|
261 |
if( j>0 && z[j-1]=='-' ) j--; |
|
f9ead75…
|
drh
|
262 |
z[j] = 0; |
|
f9ead75…
|
drh
|
263 |
} |
|
f9ead75…
|
drh
|
264 |
if( j>0 ){ |
|
f9ead75…
|
drh
|
265 |
blob_appendf(ob, "<h%d id=\"%s\">", level, z); |
|
f9ead75…
|
drh
|
266 |
}else{ |
|
f9ead75…
|
drh
|
267 |
blob_appendf(ob, "<h%d>", level); |
|
f9ead75…
|
drh
|
268 |
} |
|
3990518…
|
george
|
269 |
blob_appendb(ob, text); |
|
61079c3…
|
mistachkin
|
270 |
blob_appendf(ob, "</h%d>", level); |
|
f9ead75…
|
drh
|
271 |
fossil_free(z); |
|
61079c3…
|
mistachkin
|
272 |
} |
|
61079c3…
|
mistachkin
|
273 |
|
|
61079c3…
|
mistachkin
|
274 |
static void html_hrule(struct Blob *ob, void *opaque){ |
|
61079c3…
|
mistachkin
|
275 |
INTER_BLOCK(ob); |
|
f5482a0…
|
wyoung
|
276 |
blob_append_literal(ob, "<hr>\n"); |
|
61079c3…
|
mistachkin
|
277 |
} |
|
61079c3…
|
mistachkin
|
278 |
|
|
61079c3…
|
mistachkin
|
279 |
|
|
61079c3…
|
mistachkin
|
280 |
static void html_list( |
|
61079c3…
|
mistachkin
|
281 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
282 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
283 |
int flags, |
|
61079c3…
|
mistachkin
|
284 |
void *opaque |
|
61079c3…
|
mistachkin
|
285 |
){ |
|
61079c3…
|
mistachkin
|
286 |
char ol[] = "ol"; |
|
61079c3…
|
mistachkin
|
287 |
char ul[] = "ul"; |
|
61079c3…
|
mistachkin
|
288 |
char *tag = (flags & MKD_LIST_ORDERED) ? ol : ul; |
|
61079c3…
|
mistachkin
|
289 |
INTER_BLOCK(ob); |
|
61079c3…
|
mistachkin
|
290 |
blob_appendf(ob, "<%s>\n", tag); |
|
3990518…
|
george
|
291 |
blob_appendb(ob, text); |
|
61079c3…
|
mistachkin
|
292 |
blob_appendf(ob, "</%s>\n", tag); |
|
61079c3…
|
mistachkin
|
293 |
} |
|
61079c3…
|
mistachkin
|
294 |
|
|
61079c3…
|
mistachkin
|
295 |
static void html_list_item( |
|
61079c3…
|
mistachkin
|
296 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
297 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
298 |
int flags, |
|
61079c3…
|
mistachkin
|
299 |
void *opaque |
|
61079c3…
|
mistachkin
|
300 |
){ |
|
61079c3…
|
mistachkin
|
301 |
char *text_data = blob_buffer(text); |
|
61079c3…
|
mistachkin
|
302 |
size_t text_size = blob_size(text); |
|
61079c3…
|
mistachkin
|
303 |
while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--; |
|
3990518…
|
george
|
304 |
blob_append_literal(ob, "<li>"); |
|
382f373…
|
drh
|
305 |
blob_append(ob, text_data, text_size); |
|
3990518…
|
george
|
306 |
blob_append_literal(ob, "</li>\n"); |
|
61079c3…
|
mistachkin
|
307 |
} |
|
61079c3…
|
mistachkin
|
308 |
|
|
61079c3…
|
mistachkin
|
309 |
static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){ |
|
61079c3…
|
mistachkin
|
310 |
INTER_BLOCK(ob); |
|
3990518…
|
george
|
311 |
blob_append_literal(ob, "<p>"); |
|
3990518…
|
george
|
312 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
313 |
blob_append_literal(ob, "</p>\n"); |
|
61079c3…
|
mistachkin
|
314 |
} |
|
61079c3…
|
mistachkin
|
315 |
|
|
61079c3…
|
mistachkin
|
316 |
|
|
61079c3…
|
mistachkin
|
317 |
static void html_table( |
|
61079c3…
|
mistachkin
|
318 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
319 |
struct Blob *head_row, |
|
61079c3…
|
mistachkin
|
320 |
struct Blob *rows, |
|
61079c3…
|
mistachkin
|
321 |
void *opaque |
|
61079c3…
|
mistachkin
|
322 |
){ |
|
61079c3…
|
mistachkin
|
323 |
INTER_BLOCK(ob); |
|
f0d11ab…
|
stephan
|
324 |
blob_append_literal(ob, "<table class='md-table'>\n"); |
|
61079c3…
|
mistachkin
|
325 |
if( head_row && blob_size(head_row)>0 ){ |
|
3990518…
|
george
|
326 |
blob_append_literal(ob, "<thead>\n"); |
|
3990518…
|
george
|
327 |
blob_appendb(ob, head_row); |
|
3990518…
|
george
|
328 |
blob_append_literal(ob, "</thead>\n<tbody>\n"); |
|
61079c3…
|
mistachkin
|
329 |
} |
|
61079c3…
|
mistachkin
|
330 |
if( rows ){ |
|
3990518…
|
george
|
331 |
blob_appendb(ob, rows); |
|
61079c3…
|
mistachkin
|
332 |
} |
|
61079c3…
|
mistachkin
|
333 |
if( head_row && blob_size(head_row)>0 ){ |
|
3990518…
|
george
|
334 |
blob_append_literal(ob, "</tbody>\n"); |
|
61079c3…
|
mistachkin
|
335 |
} |
|
3990518…
|
george
|
336 |
blob_append_literal(ob, "</table>\n"); |
|
61079c3…
|
mistachkin
|
337 |
} |
|
61079c3…
|
mistachkin
|
338 |
|
|
61079c3…
|
mistachkin
|
339 |
static void html_table_cell( |
|
61079c3…
|
mistachkin
|
340 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
341 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
342 |
int flags, |
|
61079c3…
|
mistachkin
|
343 |
void *opaque |
|
61079c3…
|
mistachkin
|
344 |
){ |
|
61079c3…
|
mistachkin
|
345 |
if( flags & MKD_CELL_HEAD ){ |
|
3990518…
|
george
|
346 |
blob_append_literal(ob, " <th"); |
|
61079c3…
|
mistachkin
|
347 |
}else{ |
|
3990518…
|
george
|
348 |
blob_append_literal(ob, " <td"); |
|
61079c3…
|
mistachkin
|
349 |
} |
|
61079c3…
|
mistachkin
|
350 |
switch( flags & MKD_CELL_ALIGN_MASK ){ |
|
61079c3…
|
mistachkin
|
351 |
case MKD_CELL_ALIGN_LEFT: { |
|
9f8bf69…
|
wyoung
|
352 |
blob_append_literal(ob, " style=\"text-align:left\""); |
|
61079c3…
|
mistachkin
|
353 |
break; |
|
61079c3…
|
mistachkin
|
354 |
} |
|
61079c3…
|
mistachkin
|
355 |
case MKD_CELL_ALIGN_RIGHT: { |
|
9f8bf69…
|
wyoung
|
356 |
blob_append_literal(ob, " style=\"text-align:right\""); |
|
61079c3…
|
mistachkin
|
357 |
break; |
|
61079c3…
|
mistachkin
|
358 |
} |
|
61079c3…
|
mistachkin
|
359 |
case MKD_CELL_ALIGN_CENTER: { |
|
9f8bf69…
|
wyoung
|
360 |
blob_append_literal(ob, " style=\"text-align:center\""); |
|
61079c3…
|
mistachkin
|
361 |
break; |
|
61079c3…
|
mistachkin
|
362 |
} |
|
61079c3…
|
mistachkin
|
363 |
} |
|
3990518…
|
george
|
364 |
blob_append_literal(ob, ">"); |
|
3990518…
|
george
|
365 |
blob_appendb(ob, text); |
|
61079c3…
|
mistachkin
|
366 |
if( flags & MKD_CELL_HEAD ){ |
|
3990518…
|
george
|
367 |
blob_append_literal(ob, "</th>\n"); |
|
61079c3…
|
mistachkin
|
368 |
}else{ |
|
3990518…
|
george
|
369 |
blob_append_literal(ob, "</td>\n"); |
|
61079c3…
|
mistachkin
|
370 |
} |
|
61079c3…
|
mistachkin
|
371 |
} |
|
61079c3…
|
mistachkin
|
372 |
|
|
61079c3…
|
mistachkin
|
373 |
static void html_table_row( |
|
61079c3…
|
mistachkin
|
374 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
375 |
struct Blob *cells, |
|
61079c3…
|
mistachkin
|
376 |
int flags, |
|
61079c3…
|
mistachkin
|
377 |
void *opaque |
|
61079c3…
|
mistachkin
|
378 |
){ |
|
3990518…
|
george
|
379 |
blob_append_literal(ob, " <tr>\n"); |
|
3990518…
|
george
|
380 |
blob_appendb(ob, cells); |
|
3990518…
|
george
|
381 |
blob_append_literal(ob, " </tr>\n"); |
|
3990518…
|
george
|
382 |
} |
|
3990518…
|
george
|
383 |
|
|
3990518…
|
george
|
384 |
/* |
|
3990518…
|
george
|
385 |
** Render a token of user provided classes. |
|
3990518…
|
george
|
386 |
** If bHTML is true then render HTML for (presumably) visible text, |
|
3990518…
|
george
|
387 |
** otherwise just a space-separated list of the derived classes. |
|
3990518…
|
george
|
388 |
*/ |
|
3990518…
|
george
|
389 |
static void append_footnote_upc( |
|
3990518…
|
george
|
390 |
struct Blob *ob, |
|
3990518…
|
george
|
391 |
const struct Blob *upc, /* token of user-provided classes */ |
|
3990518…
|
george
|
392 |
int bHTML |
|
3990518…
|
george
|
393 |
){ |
|
3990518…
|
george
|
394 |
const char *z = blob_buffer(upc); |
|
3990518…
|
george
|
395 |
int i, n = blob_size(upc); |
|
3990518…
|
george
|
396 |
|
|
3990518…
|
george
|
397 |
if( n<3 ) return; |
|
3990518…
|
george
|
398 |
assert( z[0]=='.' && z[n-1] == ':' ); |
|
3990518…
|
george
|
399 |
if( bHTML ){ |
|
3990518…
|
george
|
400 |
blob_append_literal(ob, "<span class='fn-upc'>" |
|
3990518…
|
george
|
401 |
"<span class='fn-upcDot'>.</span>"); |
|
3990518…
|
george
|
402 |
} |
|
3990518…
|
george
|
403 |
n = 0; |
|
3990518…
|
george
|
404 |
do{ |
|
3990518…
|
george
|
405 |
z++; |
|
3990518…
|
george
|
406 |
if( *z!='.' && *z!=':' ){ |
|
3990518…
|
george
|
407 |
assert( fossil_isalnum(*z) || *z=='-' ); |
|
3990518…
|
george
|
408 |
n++; |
|
3990518…
|
george
|
409 |
continue; |
|
3990518…
|
george
|
410 |
} |
|
3990518…
|
george
|
411 |
assert( n ); |
|
3990518…
|
george
|
412 |
if( bHTML ) blob_append_literal(ob, "<span class='"); |
|
3990518…
|
george
|
413 |
blob_append_literal(ob, "fn-upc-"); |
|
3990518…
|
george
|
414 |
|
|
3990518…
|
george
|
415 |
for(i=-n; i<0; i++){ |
|
3990518…
|
george
|
416 |
blob_append_char(ob, fossil_tolower(z[i]) ); |
|
3990518…
|
george
|
417 |
} |
|
3990518…
|
george
|
418 |
if( bHTML ){ |
|
3990518…
|
george
|
419 |
blob_append_literal(ob, "'>"); |
|
3990518…
|
george
|
420 |
blob_append(ob, z-n, n); |
|
3990518…
|
george
|
421 |
blob_append_literal(ob, "</span>"); |
|
3990518…
|
george
|
422 |
}else{ |
|
3990518…
|
george
|
423 |
blob_append_char(ob, ' '); |
|
3990518…
|
george
|
424 |
} |
|
3990518…
|
george
|
425 |
n = 0; |
|
3990518…
|
george
|
426 |
if( bHTML ){ |
|
3990518…
|
george
|
427 |
if( *z==':' ){ |
|
3990518…
|
george
|
428 |
blob_append_literal(ob,"<span class='fn-upcColon'>:</span>"); |
|
3990518…
|
george
|
429 |
}else{ |
|
3990518…
|
george
|
430 |
blob_append_literal(ob,"<span class='fn-upcDot'>.</span>"); |
|
3990518…
|
george
|
431 |
} |
|
3990518…
|
george
|
432 |
} |
|
3990518…
|
george
|
433 |
}while( *z != ':' ); |
|
3990518…
|
george
|
434 |
if( bHTML ) blob_append_literal(ob,"</span>\n"); |
|
3990518…
|
george
|
435 |
} |
|
3990518…
|
george
|
436 |
|
|
3990518…
|
george
|
437 |
static int html_footnote_ref( |
|
3990518…
|
george
|
438 |
struct Blob *ob, const struct Blob *span, const struct Blob *upc, |
|
3990518…
|
george
|
439 |
int iMark, int locus, void *opaque |
|
3990518…
|
george
|
440 |
){ |
|
3990518…
|
george
|
441 |
const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque; |
|
3990518…
|
george
|
442 |
const bitfield64_t l = to_base26(locus-1,0); |
|
3990518…
|
george
|
443 |
char pos[32]; |
|
3990518…
|
george
|
444 |
memset(pos,0,32); |
|
3990518…
|
george
|
445 |
assert( locus > 0 ); |
|
3990518…
|
george
|
446 |
/* expect BUGs if the following yields compiler warnings */ |
|
3990518…
|
george
|
447 |
if( iMark > 0 ){ /* a regular reference to a footnote */ |
|
dfa41af…
|
wyoung
|
448 |
sqlite3_snprintf(sizeof(pos), pos, "%s-%d-%s", ctx->unique.c, iMark, l.c); |
|
3990518…
|
george
|
449 |
if(span && blob_size(span)) { |
|
3990518…
|
george
|
450 |
blob_append_literal(ob,"<span class='"); |
|
3990518…
|
george
|
451 |
append_footnote_upc(ob, upc, 0); |
|
3990518…
|
george
|
452 |
blob_append_literal(ob,"notescope' id='noteref"); |
|
3990518…
|
george
|
453 |
blob_appendf(ob,"%s'>",pos); |
|
3990518…
|
george
|
454 |
blob_appendb(ob, span); |
|
3990518…
|
george
|
455 |
blob_trim(ob); |
|
3990518…
|
george
|
456 |
blob_append_literal(ob,"<sup class='noteref'><a href='"); |
|
3990518…
|
george
|
457 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
458 |
blob_appendf(ob,"#footnote%s'>%d</a></sup></span>", pos, iMark); |
|
3990518…
|
george
|
459 |
}else{ |
|
3990518…
|
george
|
460 |
blob_trim(ob); |
|
3990518…
|
george
|
461 |
blob_append_literal(ob,"<sup class='"); |
|
3990518…
|
george
|
462 |
append_footnote_upc(ob, upc, 0); |
|
3990518…
|
george
|
463 |
blob_append_literal(ob,"noteref'><a href='"); |
|
3990518…
|
george
|
464 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
465 |
blob_appendf(ob,"#footnote%s' id='noteref%s'>%d</a></sup>", |
|
3990518…
|
george
|
466 |
pos, pos, iMark); |
|
3990518…
|
george
|
467 |
} |
|
3990518…
|
george
|
468 |
}else{ /* misreference */ |
|
3990518…
|
george
|
469 |
assert( iMark == -1 ); |
|
3990518…
|
george
|
470 |
|
|
dfa41af…
|
wyoung
|
471 |
sqlite3_snprintf(sizeof(pos), pos, "%s-%s", ctx->unique.c, l.c); |
|
3990518…
|
george
|
472 |
if(span && blob_size(span)) { |
|
3990518…
|
george
|
473 |
blob_appendf(ob, "<span class='notescope' id='misref%s'>", pos); |
|
3990518…
|
george
|
474 |
blob_appendb(ob, span); |
|
3990518…
|
george
|
475 |
blob_trim(ob); |
|
3990518…
|
george
|
476 |
blob_append_literal(ob, "<sup class='noteref misref'><a href='"); |
|
3990518…
|
george
|
477 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
478 |
blob_appendf(ob, "#misreference%s'>misref</a></sup></span>", pos); |
|
3990518…
|
george
|
479 |
}else{ |
|
3990518…
|
george
|
480 |
blob_trim(ob); |
|
3990518…
|
george
|
481 |
blob_append_literal(ob, "<sup class='noteref misref'><a href='"); |
|
3990518…
|
george
|
482 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
483 |
blob_appendf(ob, "#misreference%s' id='misref%s'>", pos, pos); |
|
3990518…
|
george
|
484 |
blob_append_literal(ob, "misref</a></sup>"); |
|
3990518…
|
george
|
485 |
} |
|
3990518…
|
george
|
486 |
} |
|
3990518…
|
george
|
487 |
return 1; |
|
3990518…
|
george
|
488 |
} |
|
3990518…
|
george
|
489 |
|
|
3990518…
|
george
|
490 |
/* Render a single item of the footnotes list. |
|
3990518…
|
george
|
491 |
* Each backref gets a unique id to enable dynamic styling. */ |
|
3990518…
|
george
|
492 |
static void html_footnote_item( |
|
3990518…
|
george
|
493 |
struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque |
|
3990518…
|
george
|
494 |
){ |
|
3990518…
|
george
|
495 |
const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque; |
|
3990518…
|
george
|
496 |
const char * const unique = ctx->unique.c; |
|
3990518…
|
george
|
497 |
assert( nUsed >= 0 ); |
|
3990518…
|
george
|
498 |
/* expect BUGs if the following yields compiler warnings */ |
|
3990518…
|
george
|
499 |
|
|
3990518…
|
george
|
500 |
if( iMark < 0 ){ /* misreferences */ |
|
3990518…
|
george
|
501 |
assert( iMark == -1 ); |
|
3990518…
|
george
|
502 |
assert( nUsed ); |
|
3990518…
|
george
|
503 |
blob_append_literal(ob,"<li class='fn-misreference'>" |
|
3990518…
|
george
|
504 |
"<sup class='fn-backrefs'>"); |
|
3990518…
|
george
|
505 |
if( nUsed == 1 ){ |
|
3990518…
|
george
|
506 |
blob_appendf(ob,"<a id='misreference%s-a' href='", unique); |
|
3990518…
|
george
|
507 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
508 |
blob_appendf(ob,"#misref%s-a'>^</a>", unique); |
|
3990518…
|
george
|
509 |
}else{ |
|
3990518…
|
george
|
510 |
int i; |
|
3990518…
|
george
|
511 |
blob_append_char(ob, '^'); |
|
3990518…
|
george
|
512 |
for(i=0; i<nUsed && i<26; i++){ |
|
3990518…
|
george
|
513 |
const int c = i + (unsigned)'a'; |
|
3990518…
|
george
|
514 |
blob_appendf(ob," <a id='misreference%s-%c' href='", unique,c); |
|
3990518…
|
george
|
515 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
516 |
blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c); |
|
3990518…
|
george
|
517 |
} |
|
3990518…
|
george
|
518 |
if( i < nUsed ) blob_append_literal(ob," …"); |
|
3990518…
|
george
|
519 |
} |
|
3990518…
|
george
|
520 |
blob_append_literal(ob,"</sup>\n<span>Misreference</span>"); |
|
3990518…
|
george
|
521 |
}else if( iMark > 0 ){ /* regular, joined and overnested footnotes */ |
|
3990518…
|
george
|
522 |
char pos[24]; |
|
3990518…
|
george
|
523 |
int bJoin = 0; |
|
3990518…
|
george
|
524 |
#define _joined_footnote_indicator "<ul class='fn-joined'>" |
|
3990518…
|
george
|
525 |
#define _jfi_sz (sizeof(_joined_footnote_indicator)-1) |
|
3990518…
|
george
|
526 |
/* make.footnote_item() invocations should pass args accordingly */ |
|
3990518…
|
george
|
527 |
const struct Blob *upc = text+1; |
|
3990518…
|
george
|
528 |
assert( text ); |
|
3990518…
|
george
|
529 |
/* allow blob_size(text)==0 for constructs like [...](^ [] ()) */ |
|
3990518…
|
george
|
530 |
memset(pos,0,24); |
|
dfa41af…
|
wyoung
|
531 |
sqlite3_snprintf(sizeof(pos), pos, "%s-%d", unique, iMark); |
|
3990518…
|
george
|
532 |
blob_appendf(ob, "<li id='footnote%s' class='", pos); |
|
3990518…
|
george
|
533 |
if( nUsed ){ |
|
3990518…
|
george
|
534 |
if( blob_size(text)>=_jfi_sz && |
|
3990518…
|
george
|
535 |
!memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ |
|
3990518…
|
george
|
536 |
bJoin = 1; |
|
3990518…
|
george
|
537 |
blob_append_literal(ob, "fn-joined "); |
|
3990518…
|
george
|
538 |
} |
|
3990518…
|
george
|
539 |
append_footnote_upc(ob, upc, 0); |
|
3990518…
|
george
|
540 |
}else{ |
|
3990518…
|
george
|
541 |
blob_append_literal(ob, "fn-toodeep "); |
|
3990518…
|
george
|
542 |
} |
|
3990518…
|
george
|
543 |
if( nUsed <= 1 ){ |
|
3990518…
|
george
|
544 |
blob_append_literal(ob, "fn-monoref'><sup class='fn-backrefs'>"); |
|
3990518…
|
george
|
545 |
blob_appendf(ob,"<a id='footnote%s-a' href='", pos); |
|
3990518…
|
george
|
546 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
547 |
blob_appendf(ob,"#noteref%s-a'>^</a>", pos); |
|
3990518…
|
george
|
548 |
}else{ |
|
3990518…
|
george
|
549 |
int i; |
|
3990518…
|
george
|
550 |
blob_append_literal(ob, "fn-polyref'><sup class='fn-backrefs'>^"); |
|
3990518…
|
george
|
551 |
for(i=0; i<nUsed && i<26; i++){ |
|
3990518…
|
george
|
552 |
const int c = i + (unsigned)'a'; |
|
3990518…
|
george
|
553 |
blob_appendf(ob," <a id='footnote%s-%c' href='", pos,c); |
|
3990518…
|
george
|
554 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
555 |
blob_appendf(ob,"#noteref%s-%c'>%c</a>", pos,c, c); |
|
3990518…
|
george
|
556 |
} |
|
3990518…
|
george
|
557 |
/* It's unlikely that so many backrefs will be usefull */ |
|
3990518…
|
george
|
558 |
/* but maybe for some machine generated documents... */ |
|
3990518…
|
george
|
559 |
for(; i<nUsed && i<676; i++){ |
|
3990518…
|
george
|
560 |
const bitfield64_t l = to_base26(i,0); |
|
3990518…
|
george
|
561 |
blob_appendf(ob," <a id='footnote%s-%s' href='", pos, l.c); |
|
3990518…
|
george
|
562 |
BLOB_APPEND_URI(ob, ctx); |
|
3990518…
|
george
|
563 |
blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c); |
|
3990518…
|
george
|
564 |
} |
|
3990518…
|
george
|
565 |
if( i < nUsed ) blob_append_literal(ob," …"); |
|
3990518…
|
george
|
566 |
} |
|
3990518…
|
george
|
567 |
blob_append_literal(ob,"</sup>\n"); |
|
3990518…
|
george
|
568 |
if( bJoin ){ |
|
3990518…
|
george
|
569 |
blob_append_literal(ob,"<sup class='fn-joined'></sup><ul>"); |
|
3990518…
|
george
|
570 |
blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); |
|
3990518…
|
george
|
571 |
}else if( nUsed ){ |
|
3990518…
|
george
|
572 |
append_footnote_upc(ob, upc, 1); |
|
3990518…
|
george
|
573 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
574 |
}else{ |
|
3990518…
|
george
|
575 |
blob_append_literal(ob,"<i></i>\n" |
|
3990518…
|
george
|
576 |
"<pre><code class='language-markdown'>"); |
|
3990518…
|
george
|
577 |
if( blob_size(upc) ){ |
|
3990518…
|
george
|
578 |
blob_appendb(ob, upc); |
|
3990518…
|
george
|
579 |
} |
|
3990518…
|
george
|
580 |
html_escape(ob, blob_buffer(text), blob_size(text)); |
|
3990518…
|
george
|
581 |
blob_append_literal(ob,"</code></pre>"); |
|
3990518…
|
george
|
582 |
} |
|
3990518…
|
george
|
583 |
#undef _joined_footnote_indicator |
|
3990518…
|
george
|
584 |
#undef _jfi_sz |
|
3990518…
|
george
|
585 |
}else{ /* a footnote was defined but wasn't referenced */ |
|
3990518…
|
george
|
586 |
/* make.footnote_item() invocations should pass args accordingly */ |
|
3990518…
|
george
|
587 |
const struct Blob *id = text-1, *upc = text+1; |
|
3990518…
|
george
|
588 |
assert( !nUsed ); |
|
3990518…
|
george
|
589 |
assert( text ); |
|
3990518…
|
george
|
590 |
assert( blob_size(text) ); |
|
3990518…
|
george
|
591 |
assert( blob_size(id) ); |
|
3990518…
|
george
|
592 |
blob_append_literal(ob,"<li class='fn-unreferenced'>\n[^ <code>"); |
|
3990518…
|
george
|
593 |
html_escape(ob, blob_buffer(id), blob_size(id)); |
|
3990518…
|
george
|
594 |
blob_append_literal(ob, "</code> ]<i></i>\n" |
|
3990518…
|
george
|
595 |
"<pre><code class='language-markdown'>"); |
|
3990518…
|
george
|
596 |
if( blob_size(upc) ){ |
|
3990518…
|
george
|
597 |
blob_appendb(ob, upc); |
|
3990518…
|
george
|
598 |
} |
|
3990518…
|
george
|
599 |
html_escape(ob, blob_buffer(text), blob_size(text)); |
|
3990518…
|
george
|
600 |
blob_append_literal(ob,"</code></pre>"); |
|
3990518…
|
george
|
601 |
} |
|
3990518…
|
george
|
602 |
blob_append_literal(ob, "\n</li>\n"); |
|
61079c3…
|
mistachkin
|
603 |
} |
|
61079c3…
|
mistachkin
|
604 |
|
|
3990518…
|
george
|
605 |
static void html_footnotes( |
|
3990518…
|
george
|
606 |
struct Blob *ob, const struct Blob *items, void *opaque |
|
3990518…
|
george
|
607 |
){ |
|
3990518…
|
george
|
608 |
if( items && blob_size(items) ){ |
|
3990518…
|
george
|
609 |
blob_append_literal(ob, |
|
3990518…
|
george
|
610 |
"\n<hr class='footnotes-separator'/>\n<ol class='footnotes'>\n"); |
|
3990518…
|
george
|
611 |
blob_appendb(ob, items); |
|
3990518…
|
george
|
612 |
blob_append_literal(ob, "</ol>\n"); |
|
3990518…
|
george
|
613 |
} |
|
3990518…
|
george
|
614 |
} |
|
61079c3…
|
mistachkin
|
615 |
|
|
61079c3…
|
mistachkin
|
616 |
/* HTML span tags */ |
|
61079c3…
|
mistachkin
|
617 |
|
|
485fda6…
|
drh
|
618 |
static int html_raw_html_tag(struct Blob *ob, struct Blob *text, void *opaque){ |
|
c2875cb…
|
drh
|
619 |
blob_append(ob, blob_buffer(text), blob_size(text)); |
|
61079c3…
|
mistachkin
|
620 |
return 1; |
|
61079c3…
|
mistachkin
|
621 |
} |
|
61079c3…
|
mistachkin
|
622 |
|
|
61079c3…
|
mistachkin
|
623 |
static int html_autolink( |
|
61079c3…
|
mistachkin
|
624 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
625 |
struct Blob *link, |
|
61079c3…
|
mistachkin
|
626 |
enum mkd_autolink type, |
|
61079c3…
|
mistachkin
|
627 |
void *opaque |
|
61079c3…
|
mistachkin
|
628 |
){ |
|
61079c3…
|
mistachkin
|
629 |
if( !link || blob_size(link)<=0 ) return 0; |
|
3990518…
|
george
|
630 |
blob_append_literal(ob, "<a href=\""); |
|
3990518…
|
george
|
631 |
if( type==MKDA_IMPLICIT_EMAIL ) blob_append_literal(ob, "mailto:"); |
|
7950dc2…
|
drh
|
632 |
html_quote(ob, blob_buffer(link), blob_size(link)); |
|
3990518…
|
george
|
633 |
blob_append_literal(ob, "\">"); |
|
61079c3…
|
mistachkin
|
634 |
if( type==MKDA_EXPLICIT_EMAIL && blob_size(link)>7 ){ |
|
61079c3…
|
mistachkin
|
635 |
/* remove "mailto:" from displayed text */ |
|
61079c3…
|
mistachkin
|
636 |
html_escape(ob, blob_buffer(link)+7, blob_size(link)-7); |
|
61079c3…
|
mistachkin
|
637 |
}else{ |
|
61079c3…
|
mistachkin
|
638 |
html_escape(ob, blob_buffer(link), blob_size(link)); |
|
61079c3…
|
mistachkin
|
639 |
} |
|
3990518…
|
george
|
640 |
blob_append_literal(ob, "</a>"); |
|
61079c3…
|
mistachkin
|
641 |
return 1; |
|
3990518…
|
george
|
642 |
} |
|
3990518…
|
george
|
643 |
|
|
3990518…
|
george
|
644 |
/* |
|
aac2a35…
|
stephan
|
645 |
** Flags for use with/via pikchr_to_html_add_flags(). |
|
aac2a35…
|
stephan
|
646 |
*/ |
|
aac2a35…
|
stephan
|
647 |
static int pikchrToHtmlFlags = 0; |
|
aac2a35…
|
stephan
|
648 |
/* |
|
aac2a35…
|
stephan
|
649 |
** Sets additional pikchr_process() flags to use for all future calls |
|
aac2a35…
|
stephan
|
650 |
** to pikch_to_html(). This is intended to be used by commands such as |
|
aac2a35…
|
stephan
|
651 |
** test-wiki-render and test-markdown-render to set the |
|
aac2a35…
|
stephan
|
652 |
** PIKCHR_PROCESS_DARK_MODE flag for all embedded pikchr elements. |
|
aac2a35…
|
stephan
|
653 |
** |
|
aac2a35…
|
stephan
|
654 |
** Not all PIKCHR_PROCESS flags are legal, as pikchr_to_html() |
|
aac2a35…
|
stephan
|
655 |
** hard-codes a subset of flags and passing arbitrary flags here may |
|
aac2a35…
|
stephan
|
656 |
** interfere with that. |
|
aac2a35…
|
stephan
|
657 |
** |
|
aac2a35…
|
stephan
|
658 |
** The only tested/intended use of this function is to pass it either |
|
aac2a35…
|
stephan
|
659 |
** 0 or PIKCHR_PROCESS_DARK_MODE. |
|
aac2a35…
|
stephan
|
660 |
** |
|
aac2a35…
|
stephan
|
661 |
** Design note: this is not implemented as an additional argument to |
|
aac2a35…
|
stephan
|
662 |
** pikchr_to_html() because the commands for which dark-mode rendering |
|
aac2a35…
|
stephan
|
663 |
** are now supported (test-wiki-render and test-markdown-render) are |
|
aac2a35…
|
stephan
|
664 |
** far removed from their corresponding pikchr_to_html() calls and |
|
aac2a35…
|
stephan
|
665 |
** there is no direct path from those commands to those calls. A |
|
aac2a35…
|
stephan
|
666 |
** cleaner, but much more invasive, approach would be to add a flag to |
|
aac2a35…
|
stephan
|
667 |
** markdown_to_html(), extend the WIKI_... flags with |
|
aac2a35…
|
stephan
|
668 |
** WIKI_DARK_PIKCHR, and extend both wiki.c:Renderer and |
|
aac2a35…
|
stephan
|
669 |
** markdown_html.c:MarkdownToHtml to contain and pass on that flag. |
|
aac2a35…
|
stephan
|
670 |
*/ |
|
aac2a35…
|
stephan
|
671 |
void pikchr_to_html_add_flags( int f ){ |
|
aac2a35…
|
stephan
|
672 |
pikchrToHtmlFlags = f; |
|
aac2a35…
|
stephan
|
673 |
} |
|
aac2a35…
|
stephan
|
674 |
|
|
aac2a35…
|
stephan
|
675 |
/* |
|
a13082c…
|
drh
|
676 |
** The nSrc bytes at zSrc[] are Pikchr input text (allegedly). Process that |
|
a13082c…
|
drh
|
677 |
** text and insert the result in place of the original. |
|
a13082c…
|
drh
|
678 |
*/ |
|
1fc2df9…
|
drh
|
679 |
void pikchr_to_html( |
|
155d074…
|
drh
|
680 |
Blob *ob, /* Write the generated SVG here */ |
|
155d074…
|
drh
|
681 |
const char *zSrc, int nSrc, /* The Pikchr source text */ |
|
155d074…
|
drh
|
682 |
const char *zArg, int nArg /* Addition arguments */ |
|
155d074…
|
drh
|
683 |
){ |
|
938bb6c…
|
stephan
|
684 |
int pikFlags = PIKCHR_PROCESS_NONCE |
|
938bb6c…
|
stephan
|
685 |
| PIKCHR_PROCESS_DIV |
|
11e7960…
|
stephan
|
686 |
| PIKCHR_PROCESS_SRC |
|
aac2a35…
|
stephan
|
687 |
| PIKCHR_PROCESS_ERR_PRE |
|
aac2a35…
|
stephan
|
688 |
| pikchrToHtmlFlags; |
|
938bb6c…
|
stephan
|
689 |
Blob bSrc = empty_blob; |
|
08fe2bd…
|
drh
|
690 |
const char *zPikVar; |
|
08fe2bd…
|
drh
|
691 |
double rPikVar; |
|
938bb6c…
|
stephan
|
692 |
|
|
938bb6c…
|
stephan
|
693 |
while( nArg>0 ){ |
|
938bb6c…
|
stephan
|
694 |
int i; |
|
938bb6c…
|
stephan
|
695 |
for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){} |
|
938bb6c…
|
stephan
|
696 |
if( i==6 && strncmp(zArg, "center", 6)==0 ){ |
|
938bb6c…
|
stephan
|
697 |
pikFlags |= PIKCHR_PROCESS_DIV_CENTER; |
|
938bb6c…
|
stephan
|
698 |
}else if( i==6 && strncmp(zArg, "indent", 6)==0 ){ |
|
938bb6c…
|
stephan
|
699 |
pikFlags |= PIKCHR_PROCESS_DIV_INDENT; |
|
938bb6c…
|
stephan
|
700 |
}else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){ |
|
938bb6c…
|
stephan
|
701 |
pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT; |
|
938bb6c…
|
stephan
|
702 |
}else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){ |
|
938bb6c…
|
stephan
|
703 |
pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT; |
|
938bb6c…
|
stephan
|
704 |
}else if( i==6 && strncmp(zArg, "toggle", 6)==0 ){ |
|
938bb6c…
|
stephan
|
705 |
pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE; |
|
938bb6c…
|
stephan
|
706 |
}else if( i==6 && strncmp(zArg, "source", 6)==0 ){ |
|
938bb6c…
|
stephan
|
707 |
pikFlags |= PIKCHR_PROCESS_DIV_SOURCE; |
|
3f2c0af…
|
stephan
|
708 |
}else if( i==13 && strncmp(zArg, "source-inline", 13)==0 ){ |
|
3f2c0af…
|
stephan
|
709 |
pikFlags |= PIKCHR_PROCESS_DIV_SOURCE_INLINE; |
|
938bb6c…
|
stephan
|
710 |
} |
|
938bb6c…
|
stephan
|
711 |
while( i<nArg && fossil_isspace(zArg[i]) ){ i++; } |
|
938bb6c…
|
stephan
|
712 |
zArg += i; |
|
938bb6c…
|
stephan
|
713 |
nArg -= i; |
|
938bb6c…
|
stephan
|
714 |
} |
|
71c4db5…
|
drh
|
715 |
if( skin_detail_boolean("white-foreground") ){ |
|
71c4db5…
|
drh
|
716 |
pikFlags |= 0x02; /* PIKCHR_DARK_MODE */ |
|
71c4db5…
|
drh
|
717 |
} |
|
08fe2bd…
|
drh
|
718 |
zPikVar = skin_detail("pikchr-foreground"); |
|
08fe2bd…
|
drh
|
719 |
if( zPikVar && zPikVar[0] ){ |
|
08fe2bd…
|
drh
|
720 |
blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar); |
|
08fe2bd…
|
drh
|
721 |
} |
|
557f51b…
|
drh
|
722 |
zPikVar = skin_detail("pikchr-background"); |
|
557f51b…
|
drh
|
723 |
if( zPikVar && zPikVar[0] ){ |
|
557f51b…
|
drh
|
724 |
blob_appendf(&bSrc, "bgcolor = %s\n", zPikVar); |
|
557f51b…
|
drh
|
725 |
} |
|
08fe2bd…
|
drh
|
726 |
zPikVar = skin_detail("pikchr-scale"); |
|
08fe2bd…
|
drh
|
727 |
if( zPikVar |
|
a10f931…
|
drh
|
728 |
&& (rPikVar = atof(zPikVar))>=0.1 |
|
08fe2bd…
|
drh
|
729 |
&& rPikVar<10.0 |
|
08fe2bd…
|
drh
|
730 |
){ |
|
08fe2bd…
|
drh
|
731 |
blob_appendf(&bSrc, "scale = %.13g\n", rPikVar); |
|
08fe2bd…
|
drh
|
732 |
} |
|
08fe2bd…
|
drh
|
733 |
zPikVar = skin_detail("pikchr-fontscale"); |
|
08fe2bd…
|
drh
|
734 |
if( zPikVar |
|
a10f931…
|
drh
|
735 |
&& (rPikVar = atof(zPikVar))>=0.1 |
|
08fe2bd…
|
drh
|
736 |
&& rPikVar<10.0 |
|
08fe2bd…
|
drh
|
737 |
){ |
|
08fe2bd…
|
drh
|
738 |
blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar); |
|
0853ab7…
|
drh
|
739 |
} |
|
938bb6c…
|
stephan
|
740 |
blob_append(&bSrc, zSrc, nSrc) |
|
938bb6c…
|
stephan
|
741 |
/*have to dup input to ensure a NUL-terminated source string */; |
|
9164a5d…
|
drh
|
742 |
pikchr_process(blob_str(&bSrc), pikFlags, ob); |
|
938bb6c…
|
stephan
|
743 |
blob_reset(&bSrc); |
|
938bb6c…
|
stephan
|
744 |
} |
|
81caad6…
|
drh
|
745 |
|
|
81caad6…
|
drh
|
746 |
/* Invoked for `...` blocks where there are nSep grave accents in a |
|
81caad6…
|
drh
|
747 |
** row that serve as the delimiter. According to CommonMark: |
|
81caad6…
|
drh
|
748 |
** |
|
81caad6…
|
drh
|
749 |
** * https://spec.commonmark.org/0.29/#fenced-code-blocks |
|
81caad6…
|
drh
|
750 |
** * https://spec.commonmark.org/0.29/#code-spans |
|
81caad6…
|
drh
|
751 |
** |
|
81caad6…
|
drh
|
752 |
** If nSep is 1 or 2, then this is a code-span which is inline. |
|
81caad6…
|
drh
|
753 |
** If nSep is 3 or more, then this is a fenced code block |
|
81caad6…
|
drh
|
754 |
*/ |
|
485fda6…
|
drh
|
755 |
static int html_codespan( |
|
81caad6…
|
drh
|
756 |
struct Blob *ob, /* Write the output here */ |
|
2077ffe…
|
drh
|
757 |
struct Blob *text, /* The stuff in between the code span marks */ |
|
2077ffe…
|
drh
|
758 |
int nSep, /* Number of grave accents marks as delimiters */ |
|
2077ffe…
|
drh
|
759 |
void *opaque |
|
2077ffe…
|
drh
|
760 |
){ |
|
81caad6…
|
drh
|
761 |
if( text==0 ){ |
|
81caad6…
|
drh
|
762 |
/* no-op */ |
|
81caad6…
|
drh
|
763 |
}else if( nSep<=2 ){ |
|
81caad6…
|
drh
|
764 |
/* One or two graves: an in-line code span */ |
|
3990518…
|
george
|
765 |
blob_append_literal(ob, "<code>"); |
|
04de083…
|
drh
|
766 |
html_escape(ob, blob_buffer(text), blob_size(text)); |
|
3990518…
|
george
|
767 |
blob_append_literal(ob, "</code>"); |
|
81caad6…
|
drh
|
768 |
}else{ |
|
81caad6…
|
drh
|
769 |
/* Three or more graves: a fenced code block */ |
|
81caad6…
|
drh
|
770 |
int n = blob_size(text); |
|
81caad6…
|
drh
|
771 |
const char *z = blob_buffer(text); |
|
81caad6…
|
drh
|
772 |
int i; |
|
81caad6…
|
drh
|
773 |
for(i=0; i<n && z[i]!='\n'; i++){} |
|
81caad6…
|
drh
|
774 |
if( i>=n ){ |
|
c7600da…
|
drh
|
775 |
blob_appendf(ob, "<pre><code>%#h</code></pre>", n, z); |
|
81caad6…
|
drh
|
776 |
}else{ |
|
81caad6…
|
drh
|
777 |
int k, j; |
|
81caad6…
|
drh
|
778 |
i++; |
|
81caad6…
|
drh
|
779 |
for(k=0; k<i && fossil_isspace(z[k]); k++){} |
|
81caad6…
|
drh
|
780 |
if( k==i ){ |
|
c7600da…
|
drh
|
781 |
blob_appendf(ob, "<pre><code>%#h</code></pre>", n-i, z+i); |
|
81caad6…
|
drh
|
782 |
}else{ |
|
81caad6…
|
drh
|
783 |
for(j=k+1; j<i && !fossil_isspace(z[j]); j++){} |
|
a13082c…
|
drh
|
784 |
if( j-k==6 && strncmp(z+k,"pikchr",6)==0 ){ |
|
155d074…
|
drh
|
785 |
while( j<i && fossil_isspace(z[j]) ){ j++; } |
|
1fc2df9…
|
drh
|
786 |
pikchr_to_html(ob, z+i, n-i, z+j, i-j); |
|
a13082c…
|
drh
|
787 |
}else{ |
|
a13082c…
|
drh
|
788 |
blob_appendf(ob, "<pre><code class='language-%#h'>%#h</code></pre>", |
|
a13082c…
|
drh
|
789 |
j-k, z+k, n-i, z+i); |
|
a13082c…
|
drh
|
790 |
} |
|
81caad6…
|
drh
|
791 |
} |
|
81caad6…
|
drh
|
792 |
} |
|
04de083…
|
drh
|
793 |
} |
|
61079c3…
|
mistachkin
|
794 |
return 1; |
|
61079c3…
|
mistachkin
|
795 |
} |
|
61079c3…
|
mistachkin
|
796 |
|
|
61079c3…
|
mistachkin
|
797 |
static int html_double_emphasis( |
|
61079c3…
|
mistachkin
|
798 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
799 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
800 |
char c, |
|
61079c3…
|
mistachkin
|
801 |
void *opaque |
|
61079c3…
|
mistachkin
|
802 |
){ |
|
3990518…
|
george
|
803 |
blob_append_literal(ob, "<strong>"); |
|
3990518…
|
george
|
804 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
805 |
blob_append_literal(ob, "</strong>"); |
|
61079c3…
|
mistachkin
|
806 |
return 1; |
|
61079c3…
|
mistachkin
|
807 |
} |
|
61079c3…
|
mistachkin
|
808 |
|
|
61079c3…
|
mistachkin
|
809 |
static int html_emphasis( |
|
61079c3…
|
mistachkin
|
810 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
811 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
812 |
char c, |
|
61079c3…
|
mistachkin
|
813 |
void *opaque |
|
61079c3…
|
mistachkin
|
814 |
){ |
|
3990518…
|
george
|
815 |
blob_append_literal(ob, "<em>"); |
|
3990518…
|
george
|
816 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
817 |
blob_append_literal(ob, "</em>"); |
|
61079c3…
|
mistachkin
|
818 |
return 1; |
|
61079c3…
|
mistachkin
|
819 |
} |
|
61079c3…
|
mistachkin
|
820 |
|
|
61079c3…
|
mistachkin
|
821 |
static int html_image( |
|
61079c3…
|
mistachkin
|
822 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
823 |
struct Blob *link, |
|
61079c3…
|
mistachkin
|
824 |
struct Blob *title, |
|
61079c3…
|
mistachkin
|
825 |
struct Blob *alt, |
|
61079c3…
|
mistachkin
|
826 |
void *opaque |
|
61079c3…
|
mistachkin
|
827 |
){ |
|
3990518…
|
george
|
828 |
blob_append_literal(ob, "<img src=\""); |
|
7950dc2…
|
drh
|
829 |
html_quote(ob, blob_buffer(link), blob_size(link)); |
|
3990518…
|
george
|
830 |
blob_append_literal(ob, "\" alt=\""); |
|
7950dc2…
|
drh
|
831 |
html_quote(ob, blob_buffer(alt), blob_size(alt)); |
|
61079c3…
|
mistachkin
|
832 |
if( title && blob_size(title)>0 ){ |
|
3990518…
|
george
|
833 |
blob_append_literal(ob, "\" title=\""); |
|
7950dc2…
|
drh
|
834 |
html_quote(ob, blob_buffer(title), blob_size(title)); |
|
61079c3…
|
mistachkin
|
835 |
} |
|
f5482a0…
|
wyoung
|
836 |
blob_append_literal(ob, "\">"); |
|
61079c3…
|
mistachkin
|
837 |
return 1; |
|
61079c3…
|
mistachkin
|
838 |
} |
|
61079c3…
|
mistachkin
|
839 |
|
|
485fda6…
|
drh
|
840 |
static int html_linebreak(struct Blob *ob, void *opaque){ |
|
f5482a0…
|
wyoung
|
841 |
blob_append_literal(ob, "<br>\n"); |
|
61079c3…
|
mistachkin
|
842 |
return 1; |
|
61079c3…
|
mistachkin
|
843 |
} |
|
61079c3…
|
mistachkin
|
844 |
|
|
61079c3…
|
mistachkin
|
845 |
static int html_link( |
|
61079c3…
|
mistachkin
|
846 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
847 |
struct Blob *link, |
|
61079c3…
|
mistachkin
|
848 |
struct Blob *title, |
|
61079c3…
|
mistachkin
|
849 |
struct Blob *content, |
|
61079c3…
|
mistachkin
|
850 |
void *opaque |
|
61079c3…
|
mistachkin
|
851 |
){ |
|
38a4707…
|
drh
|
852 |
char *zLink = blob_buffer(link); |
|
774fb77…
|
drh
|
853 |
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0; |
|
774fb77…
|
drh
|
854 |
char zClose[20]; |
|
44545ee…
|
drh
|
855 |
|
|
44545ee…
|
drh
|
856 |
if( zLink==0 || zLink[0]==0 ){ |
|
44545ee…
|
drh
|
857 |
zClose[0] = 0; |
|
275da70…
|
danield
|
858 |
}else{ |
|
275da70…
|
danield
|
859 |
static const int flags = |
|
3b10e64…
|
drh
|
860 |
WIKI_NOBADLINKS | |
|
3b10e64…
|
drh
|
861 |
WIKI_MARKDOWNLINKS |
|
3b10e64…
|
drh
|
862 |
; |
|
3b10e64…
|
drh
|
863 |
wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle); |
|
44545ee…
|
drh
|
864 |
} |
|
774fb77…
|
drh
|
865 |
if( blob_size(content)==0 ){ |
|
3990518…
|
george
|
866 |
if( link ) blob_appendb(ob, link); |
|
774fb77…
|
drh
|
867 |
}else{ |
|
3990518…
|
george
|
868 |
blob_appendb(ob, content); |
|
774fb77…
|
drh
|
869 |
} |
|
774fb77…
|
drh
|
870 |
blob_append(ob, zClose, -1); |
|
61079c3…
|
mistachkin
|
871 |
return 1; |
|
61079c3…
|
mistachkin
|
872 |
} |
|
61079c3…
|
mistachkin
|
873 |
|
|
61079c3…
|
mistachkin
|
874 |
static int html_triple_emphasis( |
|
61079c3…
|
mistachkin
|
875 |
struct Blob *ob, |
|
61079c3…
|
mistachkin
|
876 |
struct Blob *text, |
|
61079c3…
|
mistachkin
|
877 |
char c, |
|
61079c3…
|
mistachkin
|
878 |
void *opaque |
|
61079c3…
|
mistachkin
|
879 |
){ |
|
3990518…
|
george
|
880 |
blob_append_literal(ob, "<strong><em>"); |
|
3990518…
|
george
|
881 |
blob_appendb(ob, text); |
|
3990518…
|
george
|
882 |
blob_append_literal(ob, "</em></strong>"); |
|
61079c3…
|
mistachkin
|
883 |
return 1; |
|
61079c3…
|
mistachkin
|
884 |
} |
|
61079c3…
|
mistachkin
|
885 |
|
|
61079c3…
|
mistachkin
|
886 |
|
|
61079c3…
|
mistachkin
|
887 |
static void html_normal_text(struct Blob *ob, struct Blob *text, void *opaque){ |
|
61079c3…
|
mistachkin
|
888 |
html_escape(ob, blob_buffer(text), blob_size(text)); |
|
61079c3…
|
mistachkin
|
889 |
} |
|
61079c3…
|
mistachkin
|
890 |
|
|
d407c38…
|
drh
|
891 |
/* |
|
d407c38…
|
drh
|
892 |
** Convert markdown into HTML. |
|
d407c38…
|
drh
|
893 |
** |
|
d407c38…
|
drh
|
894 |
** The document title is placed in output_title if not NULL. Or if |
|
d407c38…
|
drh
|
895 |
** output_title is NULL, the document title appears in the body. |
|
d407c38…
|
drh
|
896 |
*/ |
|
61079c3…
|
mistachkin
|
897 |
void markdown_to_html( |
|
d407c38…
|
drh
|
898 |
struct Blob *input_markdown, /* Markdown content to be rendered */ |
|
d407c38…
|
drh
|
899 |
struct Blob *output_title, /* Put title here. May be NULL */ |
|
d407c38…
|
drh
|
900 |
struct Blob *output_body /* Put document body here. */ |
|
61079c3…
|
mistachkin
|
901 |
){ |
|
61079c3…
|
mistachkin
|
902 |
struct mkd_renderer html_renderer = { |
|
e061a67…
|
mistachkin
|
903 |
/* prolog and epilog */ |
|
e061a67…
|
mistachkin
|
904 |
html_prolog, |
|
e061a67…
|
mistachkin
|
905 |
html_epilog, |
|
3990518…
|
george
|
906 |
html_footnotes, |
|
61079c3…
|
mistachkin
|
907 |
|
|
61079c3…
|
mistachkin
|
908 |
/* block level elements */ |
|
61079c3…
|
mistachkin
|
909 |
html_blockcode, |
|
61079c3…
|
mistachkin
|
910 |
html_blockquote, |
|
485fda6…
|
drh
|
911 |
html_blockhtml, |
|
61079c3…
|
mistachkin
|
912 |
html_header, |
|
61079c3…
|
mistachkin
|
913 |
html_hrule, |
|
61079c3…
|
mistachkin
|
914 |
html_list, |
|
61079c3…
|
mistachkin
|
915 |
html_list_item, |
|
61079c3…
|
mistachkin
|
916 |
html_paragraph, |
|
61079c3…
|
mistachkin
|
917 |
html_table, |
|
61079c3…
|
mistachkin
|
918 |
html_table_cell, |
|
61079c3…
|
mistachkin
|
919 |
html_table_row, |
|
3990518…
|
george
|
920 |
html_footnote_item, |
|
61079c3…
|
mistachkin
|
921 |
|
|
61079c3…
|
mistachkin
|
922 |
/* span level elements */ |
|
61079c3…
|
mistachkin
|
923 |
html_autolink, |
|
485fda6…
|
drh
|
924 |
html_codespan, |
|
61079c3…
|
mistachkin
|
925 |
html_double_emphasis, |
|
61079c3…
|
mistachkin
|
926 |
html_emphasis, |
|
61079c3…
|
mistachkin
|
927 |
html_image, |
|
485fda6…
|
drh
|
928 |
html_linebreak, |
|
61079c3…
|
mistachkin
|
929 |
html_link, |
|
485fda6…
|
drh
|
930 |
html_raw_html_tag, |
|
61079c3…
|
mistachkin
|
931 |
html_triple_emphasis, |
|
3990518…
|
george
|
932 |
html_footnote_ref, |
|
61079c3…
|
mistachkin
|
933 |
|
|
61079c3…
|
mistachkin
|
934 |
/* low level elements */ |
|
485fda6…
|
drh
|
935 |
0, /* entity */ |
|
61079c3…
|
mistachkin
|
936 |
html_normal_text, |
|
61079c3…
|
mistachkin
|
937 |
|
|
61079c3…
|
mistachkin
|
938 |
/* misc. parameters */ |
|
485fda6…
|
drh
|
939 |
"*_", /* emph_chars */ |
|
485fda6…
|
drh
|
940 |
0 /* opaque */ |
|
61079c3…
|
mistachkin
|
941 |
}; |
|
3990518…
|
george
|
942 |
static int invocation = -1; /* no marker for the first document */ |
|
3990518…
|
george
|
943 |
static const char* zRU = 0; /* REQUEST_URI with escaped quotes */ |
|
b61dcef…
|
drh
|
944 |
MarkdownToHtml context; |
|
b61dcef…
|
drh
|
945 |
memset(&context, 0, sizeof(context)); |
|
b61dcef…
|
drh
|
946 |
context.output_title = output_title; |
|
3990518…
|
george
|
947 |
context.unique = to_base26(invocation++,1); |
|
3990518…
|
george
|
948 |
if( !zRU ) zRU = escape_quotes(PD("REQUEST_URI","")); |
|
3990518…
|
george
|
949 |
#ifndef FOOTNOTES_WITHOUT_URI |
|
3990518…
|
george
|
950 |
blob_set( &context.reqURI, zRU ); |
|
3990518…
|
george
|
951 |
#endif |
|
b61dcef…
|
drh
|
952 |
html_renderer.opaque = &context; |
|
d407c38…
|
drh
|
953 |
if( output_title ) blob_reset(output_title); |
|
61079c3…
|
mistachkin
|
954 |
blob_reset(output_body); |
|
61079c3…
|
mistachkin
|
955 |
markdown(output_body, input_markdown, &html_renderer); |
|
0b24a45…
|
drh
|
956 |
} |
|
0b24a45…
|
drh
|
957 |
|
|
0b24a45…
|
drh
|
958 |
/* |
|
0b24a45…
|
drh
|
959 |
** Undo HTML escapes in Blob p. In other words convert: |
|
0b24a45…
|
drh
|
960 |
** |
|
0b24a45…
|
drh
|
961 |
** & -> & |
|
0b24a45…
|
drh
|
962 |
** < -> < |
|
0b24a45…
|
drh
|
963 |
** > -> > |
|
0b24a45…
|
drh
|
964 |
** " -> " |
|
0b24a45…
|
drh
|
965 |
** &#NNN; -> ascii character NNN |
|
0b24a45…
|
drh
|
966 |
*/ |
|
0b24a45…
|
drh
|
967 |
void markdown_dehtmlize_blob(Blob *p){ |
|
0b24a45…
|
drh
|
968 |
char *z; |
|
0b24a45…
|
drh
|
969 |
unsigned int j, k; |
|
0b24a45…
|
drh
|
970 |
|
|
0b24a45…
|
drh
|
971 |
z = p->aData; |
|
0b24a45…
|
drh
|
972 |
for(j=k=0; j<p->nUsed; j++){ |
|
0b24a45…
|
drh
|
973 |
char c = z[j]; |
|
0b24a45…
|
drh
|
974 |
if( c=='&' ){ |
|
0b24a45…
|
drh
|
975 |
if( z[j+1]=='#' && fossil_isdigit(z[j+2]) ){ |
|
0b24a45…
|
drh
|
976 |
int n = 3; |
|
0b24a45…
|
drh
|
977 |
int x = z[j+2] - '0'; |
|
0b24a45…
|
drh
|
978 |
if( fossil_isdigit(z[j+3]) ){ |
|
0b24a45…
|
drh
|
979 |
x = x*10 + z[j+3] - '0'; |
|
0b24a45…
|
drh
|
980 |
n++; |
|
0b24a45…
|
drh
|
981 |
if( fossil_isdigit(z[j+4]) ){ |
|
0b24a45…
|
drh
|
982 |
x = x*10 + z[j+4] - '0'; |
|
0b24a45…
|
drh
|
983 |
n++; |
|
0b24a45…
|
drh
|
984 |
} |
|
0b24a45…
|
drh
|
985 |
} |
|
0b24a45…
|
drh
|
986 |
if( z[j+n]==';' ){ |
|
0b24a45…
|
drh
|
987 |
z[k++] = (char)x; |
|
0b24a45…
|
drh
|
988 |
j += n; |
|
0b24a45…
|
drh
|
989 |
}else{ |
|
0b24a45…
|
drh
|
990 |
z[k++] = c; |
|
0b24a45…
|
drh
|
991 |
} |
|
0b24a45…
|
drh
|
992 |
}else if( memcmp(&z[j],"<",4)==0 ){ |
|
0b24a45…
|
drh
|
993 |
z[k++] = '<'; |
|
0b24a45…
|
drh
|
994 |
j += 3; |
|
0b24a45…
|
drh
|
995 |
}else if( memcmp(&z[j],">",4)==0 ){ |
|
0b24a45…
|
drh
|
996 |
z[k++] = '>'; |
|
0b24a45…
|
drh
|
997 |
j += 3; |
|
0b24a45…
|
drh
|
998 |
}else if( memcmp(&z[j],""",6)==0 ){ |
|
0b24a45…
|
drh
|
999 |
z[k++] = '"'; |
|
0b24a45…
|
drh
|
1000 |
j += 5; |
|
0b24a45…
|
drh
|
1001 |
}else if( memcmp(&z[j],"&",5)==0 ){ |
|
0b24a45…
|
drh
|
1002 |
z[k++] = '&'; |
|
0b24a45…
|
drh
|
1003 |
j += 4; |
|
0b24a45…
|
drh
|
1004 |
}else{ |
|
0b24a45…
|
drh
|
1005 |
z[k++] = c; |
|
0b24a45…
|
drh
|
1006 |
} |
|
0b24a45…
|
drh
|
1007 |
}else{ |
|
0b24a45…
|
drh
|
1008 |
z[k++] = c; |
|
0b24a45…
|
drh
|
1009 |
} |
|
0b24a45…
|
drh
|
1010 |
} |
|
0b24a45…
|
drh
|
1011 |
z[k] = 0; |
|
0b24a45…
|
drh
|
1012 |
p->nUsed = k; |
|
4dcea80…
|
drh
|
1013 |
} |