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