rdiscount 1.3.5 → 1.5.5
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/README.markdown +20 -10
- data/Rakefile +12 -11
- data/ext/Csio.c +14 -2
- data/ext/css.c +76 -0
- data/ext/cstring.h +7 -5
- data/ext/dumptree.c +5 -1
- data/ext/generate.c +473 -268
- data/ext/markdown.c +209 -96
- data/ext/markdown.h +16 -5
- data/ext/mkdio.c +68 -6
- data/ext/mkdio.h +20 -7
- data/ext/resource.c +13 -27
- data/ext/toc.c +24 -20
- data/ext/xml.c +82 -0
- data/lib/rdiscount.rb +2 -2
- data/rdiscount.gemspec +4 -2
- data/test/markdown_test.rb +17 -0
- metadata +5 -3
data/README.markdown
CHANGED
@@ -5,21 +5,25 @@ Discount is an implementation of John Gruber's Markdown markup language in C. It
|
|
5
5
|
implements all of the language described in [the markdown syntax document][1] and
|
6
6
|
passes the [Markdown 1.0 test suite][2].
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
CODE: `git clone git://github.com/rtomayko/rdiscount.git`
|
9
|
+
HOME: <http://github.com/rtomayko/rdiscount>
|
10
|
+
DOCS: <http://rdoc.info/projects/rtomayko/rdiscount>
|
11
|
+
BUGS: <http://github.com/rtomayko/rdiscount/issues>
|
12
|
+
|
13
|
+
Discount was developed by [David Loren Parsons][3]. The Ruby extension
|
14
|
+
is maintained by [Ryan Tomayko][4].
|
10
15
|
|
11
16
|
[1]: http://daringfireball.net/projects/markdown/syntax
|
12
17
|
[2]: http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip
|
13
18
|
[3]: http://www.pell.portland.or.us/~orc
|
14
|
-
[4]: http://tomayko.com/
|
19
|
+
[4]: http://tomayko.com/about
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
INSTALL, HACKING
|
22
|
+
----------------
|
18
23
|
|
19
|
-
|
20
|
-
follows:
|
24
|
+
New releases of RDiscount are published to [gemcutter][]:
|
21
25
|
|
22
|
-
$ [sudo] gem install rdiscount
|
26
|
+
$ [sudo] gem install rdiscount -s http://gemcutter.org
|
23
27
|
|
24
28
|
The RDiscount sources are available via Git:
|
25
29
|
|
@@ -27,9 +31,9 @@ The RDiscount sources are available via Git:
|
|
27
31
|
$ cd rdiscount
|
28
32
|
$ rake --tasks
|
29
33
|
|
30
|
-
|
34
|
+
[gemcutter]: http://gemcutter.org/gems/rdiscount
|
31
35
|
|
32
|
-
|
36
|
+
USAGE
|
33
37
|
-----
|
34
38
|
|
35
39
|
RDiscount implements the basic protocol popularized by RedCloth and adopted
|
@@ -39,6 +43,11 @@ by BlueCloth:
|
|
39
43
|
markdown = RDiscount.new("Hello World!")
|
40
44
|
puts markdown.to_html
|
41
45
|
|
46
|
+
Additional processing options can be turned on when creating the
|
47
|
+
RDiscount object:
|
48
|
+
|
49
|
+
markdown = RDiscount.new("Hello World!", :smart, :filter_html)
|
50
|
+
|
42
51
|
Inject RDiscount into your BlueCloth-using code by replacing your bluecloth
|
43
52
|
require statements with the following:
|
44
53
|
|
@@ -56,3 +65,4 @@ Discount is free software; it is released under a BSD-style license
|
|
56
65
|
that allows you to do as you wish with it as long as you don't attempt
|
57
66
|
to claim it as your own work. RDiscount adopts Discount's license
|
58
67
|
verbatim. See the file `COPYING` for more information.
|
68
|
+
|
data/Rakefile
CHANGED
@@ -4,15 +4,8 @@ task :default => :test
|
|
4
4
|
|
5
5
|
# PACKAGING =================================================================
|
6
6
|
|
7
|
-
|
8
|
-
$spec =
|
9
|
-
begin
|
10
|
-
require 'rubygems/specification'
|
11
|
-
data = File.read('rdiscount.gemspec')
|
12
|
-
spec = nil
|
13
|
-
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
14
|
-
spec
|
15
|
-
end
|
7
|
+
require 'rubygems/specification'
|
8
|
+
$spec = eval(File.read('rdiscount.gemspec'))
|
16
9
|
|
17
10
|
def package(ext='')
|
18
11
|
"pkg/rdiscount-#{$spec.version}" + ext
|
@@ -91,9 +84,17 @@ task :build => "lib/rdiscount.#{DLEXT}"
|
|
91
84
|
# Testing
|
92
85
|
# ==========================================================
|
93
86
|
|
87
|
+
def runner
|
88
|
+
if system("type turn 2>/dev/null 1>&2")
|
89
|
+
"turn"
|
90
|
+
else
|
91
|
+
"testrb"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
94
95
|
desc 'Run unit tests'
|
95
96
|
task 'test:unit' => [:build] do |t|
|
96
|
-
sh
|
97
|
+
sh "#{runner} test/markdown_test.rb test/rdiscount_test.rb"
|
97
98
|
end
|
98
99
|
|
99
100
|
desc 'Run conformance tests (MARKDOWN_TEST_VER=1.0)'
|
@@ -175,7 +176,7 @@ task :gather => 'discount' do |t|
|
|
175
176
|
files =
|
176
177
|
FileList[
|
177
178
|
'discount/{markdown,mkdio,amalloc,cstring}.h',
|
178
|
-
'discount/{markdown,docheader,dumptree,generate,mkdio,resource,toc,Csio}.c'
|
179
|
+
'discount/{markdown,docheader,dumptree,generate,mkdio,resource,toc,Csio,xml,css}.c'
|
179
180
|
]
|
180
181
|
cp files, 'ext/',
|
181
182
|
:preserve => true,
|
data/ext/Csio.c
CHANGED
@@ -26,15 +26,27 @@ Csprintf(Cstring *iot, char *fmt, ...)
|
|
26
26
|
do {
|
27
27
|
RESERVE(*iot, siz);
|
28
28
|
va_start(ptr, fmt);
|
29
|
-
siz = vsnprintf(T(*iot)+S(*iot),
|
29
|
+
siz = vsnprintf(T(*iot)+S(*iot), ALLOCATED(*iot)-S(*iot), fmt, ptr);
|
30
30
|
va_end(ptr);
|
31
|
-
} while ( siz > (
|
31
|
+
} while ( siz > (ALLOCATED(*iot)-S(*iot)) );
|
32
32
|
|
33
33
|
S(*iot) += siz;
|
34
34
|
return siz;
|
35
35
|
}
|
36
36
|
|
37
37
|
|
38
|
+
/* write() into a cstring
|
39
|
+
*/
|
40
|
+
int
|
41
|
+
Cswrite(Cstring *iot, char *bfr, int size)
|
42
|
+
{
|
43
|
+
RESERVE(*iot, size);
|
44
|
+
memcpy(T(*iot)+S(*iot), bfr, size);
|
45
|
+
S(*iot) += size;
|
46
|
+
return size;
|
47
|
+
}
|
48
|
+
|
49
|
+
|
38
50
|
/* reparse() into a cstring
|
39
51
|
*/
|
40
52
|
void
|
data/ext/css.c
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
/* markdown: a C implementation of John Gruber's Markdown markup language.
|
2
|
+
*
|
3
|
+
* Copyright (C) 2009 David L Parsons.
|
4
|
+
* The redistribution terms are provided in the COPYRIGHT file that must
|
5
|
+
* be distributed with this source code.
|
6
|
+
*/
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <string.h>
|
9
|
+
#include <stdarg.h>
|
10
|
+
#include <stdlib.h>
|
11
|
+
#include <time.h>
|
12
|
+
#include <ctype.h>
|
13
|
+
|
14
|
+
#include "config.h"
|
15
|
+
|
16
|
+
#include "cstring.h"
|
17
|
+
#include "markdown.h"
|
18
|
+
#include "amalloc.h"
|
19
|
+
|
20
|
+
|
21
|
+
/*
|
22
|
+
* dump out stylesheet sections.
|
23
|
+
*/
|
24
|
+
static void
|
25
|
+
stylesheets(Paragraph *p, Cstring *f)
|
26
|
+
{
|
27
|
+
Line* q;
|
28
|
+
|
29
|
+
for ( ; p ; p = p->next ) {
|
30
|
+
if ( p->typ == STYLE ) {
|
31
|
+
for ( q = p->text; q ; q = q->next )
|
32
|
+
Cswrite(f, T(q->text), S(q->text));
|
33
|
+
Csputc('\n', f);
|
34
|
+
}
|
35
|
+
if ( p->down )
|
36
|
+
stylesheets(p->down, f);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
/* dump any embedded styles to a string
|
42
|
+
*/
|
43
|
+
int
|
44
|
+
mkd_css(Document *d, char **res)
|
45
|
+
{
|
46
|
+
Cstring f;
|
47
|
+
|
48
|
+
if ( res && *res && d && d->compiled ) {
|
49
|
+
CREATE(f);
|
50
|
+
RESERVE(f, 100);
|
51
|
+
stylesheets(d->code, &f);
|
52
|
+
|
53
|
+
/* HACK ALERT! HACK ALERT! HACK ALERT! */
|
54
|
+
*res = T(f); /* we know that a T(Cstring) is a character pointer */
|
55
|
+
/* so we can simply pick it up and carry it away, */
|
56
|
+
return S(f); /* leaving the husk of the Ctring on the stack */
|
57
|
+
/* END HACK ALERT */
|
58
|
+
}
|
59
|
+
return EOF;
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
/* dump any embedded styles to a file
|
64
|
+
*/
|
65
|
+
int
|
66
|
+
mkd_generatecss(Document *d, FILE *f)
|
67
|
+
{
|
68
|
+
char *res;
|
69
|
+
int written = EOF, size = mkd_css(d, &res);
|
70
|
+
|
71
|
+
if ( size > 0 )
|
72
|
+
written = fwrite(res, size, 1, f);
|
73
|
+
if ( res )
|
74
|
+
free(res);
|
75
|
+
return (written == size) ? size : EOF;
|
76
|
+
}
|
data/ext/cstring.h
CHANGED
@@ -22,8 +22,8 @@
|
|
22
22
|
: (T(x) = T(x) ? realloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \
|
23
23
|
: malloc(sizeof T(x)[0] * ((x).alloc += 100)) )]
|
24
24
|
|
25
|
-
#define DELETE(x) (x)
|
26
|
-
|
25
|
+
#define DELETE(x) ALLOCATED(x) ? (free(T(x)), S(x) = (x).alloc = 0) \
|
26
|
+
: ( S(x) = 0 )
|
27
27
|
#define CLIP(t,i,sz) \
|
28
28
|
( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \
|
29
29
|
(memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \
|
@@ -50,7 +50,7 @@
|
|
50
50
|
*/
|
51
51
|
#define T(x) (x).text
|
52
52
|
#define S(x) (x).size
|
53
|
-
#define
|
53
|
+
#define ALLOCATED(x) (x).alloc
|
54
54
|
|
55
55
|
/* abstract anchor type that defines a list base
|
56
56
|
* with a function that attaches an element to
|
@@ -60,14 +60,16 @@
|
|
60
60
|
* macro will work with it.
|
61
61
|
*/
|
62
62
|
#define ANCHOR(t) struct { t *text, *end; }
|
63
|
+
#define E(t) ((t).end)
|
63
64
|
|
64
|
-
#define ATTACH(t, p) ( (t)
|
65
|
-
|
65
|
+
#define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \
|
66
|
+
: ( (T(t) = E(t) = (p)) ) )
|
66
67
|
|
67
68
|
typedef STRING(char) Cstring;
|
68
69
|
|
69
70
|
extern void Csputc(int, Cstring *);
|
70
71
|
extern int Csprintf(Cstring *, char *, ...);
|
72
|
+
extern int Cswrite(Cstring *, char *, int);
|
71
73
|
extern void Csreparse(Cstring *, char *, int, int);
|
72
74
|
|
73
75
|
#endif/*_CSTRING_D*/
|
data/ext/dumptree.c
CHANGED
@@ -30,7 +30,9 @@ Pptype(int typ)
|
|
30
30
|
case OL : return "ol";
|
31
31
|
case LISTITEM : return "item";
|
32
32
|
case HDR : return "header";
|
33
|
-
case HR : return "
|
33
|
+
case HR : return "hr";
|
34
|
+
case TABLE : return "table";
|
35
|
+
case SOURCE : return "source";
|
34
36
|
default : return "mystery node!";
|
35
37
|
}
|
36
38
|
}
|
@@ -106,6 +108,8 @@ dumptree(Paragraph *pp, Stack *sp, FILE *f)
|
|
106
108
|
printpfx(sp, f);
|
107
109
|
|
108
110
|
d = fprintf(f, "[%s", Pptype(pp->typ));
|
111
|
+
if ( pp->ident )
|
112
|
+
d += fprintf(f, " %s", pp->ident);
|
109
113
|
if ( pp->align )
|
110
114
|
d += fprintf(f, ", <%s>", Begin[pp->align]);
|
111
115
|
|
data/ext/generate.c
CHANGED
@@ -17,11 +17,6 @@
|
|
17
17
|
#include "markdown.h"
|
18
18
|
#include "amalloc.h"
|
19
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
20
|
typedef int (*stfu)(const void*,const void*);
|
26
21
|
|
27
22
|
|
@@ -119,7 +114,7 @@ shift(MMIOT *f, int i)
|
|
119
114
|
/* Qchar()
|
120
115
|
*/
|
121
116
|
static void
|
122
|
-
Qchar(
|
117
|
+
Qchar(int c, MMIOT *f)
|
123
118
|
{
|
124
119
|
block *cur;
|
125
120
|
|
@@ -328,18 +323,27 @@ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f)
|
|
328
323
|
* write out a url, escaping problematic characters
|
329
324
|
*/
|
330
325
|
static void
|
331
|
-
puturl(char *s, int size, MMIOT *f)
|
326
|
+
puturl(char *s, int size, MMIOT *f, int display)
|
332
327
|
{
|
333
328
|
unsigned char c;
|
334
329
|
|
335
330
|
while ( size-- > 0 ) {
|
336
331
|
c = *s++;
|
337
332
|
|
333
|
+
if ( c == '\\' && size-- > 0 ) {
|
334
|
+
c = *s++;
|
335
|
+
|
336
|
+
if ( !( ispunct(c) || isspace(c) ) )
|
337
|
+
Qchar('\\', f);
|
338
|
+
}
|
339
|
+
|
338
340
|
if ( c == '&' )
|
339
341
|
Qstring("&", f);
|
340
342
|
else if ( c == '<' )
|
341
343
|
Qstring("<", f);
|
342
|
-
else if (
|
344
|
+
else if ( c == '"' )
|
345
|
+
Qstring("%22", f);
|
346
|
+
else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) )
|
343
347
|
Qchar(c, f);
|
344
348
|
else
|
345
349
|
Qprintf(f, "%%%02X", c);
|
@@ -372,181 +376,164 @@ parenthetical(int in, int out, MMIOT *f)
|
|
372
376
|
return EOF;
|
373
377
|
else if ( c == in )
|
374
378
|
++indent;
|
379
|
+
else if ( (c == '\\') && (peek(f,1) == out) ) {
|
380
|
+
++size;
|
381
|
+
pull(f);
|
382
|
+
}
|
375
383
|
else if ( c == out )
|
376
384
|
--indent;
|
377
385
|
}
|
378
|
-
return size-1;
|
386
|
+
return size ? (size-1) : 0;
|
379
387
|
}
|
380
388
|
|
381
389
|
|
382
390
|
/* extract a []-delimited label from the input stream.
|
383
391
|
*/
|
384
|
-
static
|
385
|
-
linkylabel(MMIOT *f,
|
392
|
+
static int
|
393
|
+
linkylabel(MMIOT *f, Cstring *res)
|
386
394
|
{
|
387
395
|
char *ptr = cursor(f);
|
396
|
+
int size;
|
388
397
|
|
389
|
-
if ( (
|
390
|
-
|
398
|
+
if ( (size = parenthetical('[',']',f)) != EOF ) {
|
399
|
+
T(*res) = ptr;
|
400
|
+
S(*res) = size;
|
401
|
+
return 1;
|
402
|
+
}
|
391
403
|
return 0;
|
392
404
|
}
|
393
405
|
|
394
406
|
|
395
|
-
/*
|
396
|
-
* the label is either of the format `<link>`, where I
|
397
|
-
* extract until I find a >, or it is of the format
|
398
|
-
* `text`, where I extract until I reach a ')' or
|
399
|
-
* whitespace.
|
407
|
+
/* see if the quote-prefixed linky segment is actually a title.
|
400
408
|
*/
|
401
|
-
static
|
402
|
-
|
409
|
+
static int
|
410
|
+
linkytitle(MMIOT *f, char quote, Footnote *ref)
|
403
411
|
{
|
404
|
-
int
|
405
|
-
char *
|
406
|
-
|
412
|
+
int whence = mmiottell(f);
|
413
|
+
char *title = cursor(f);
|
414
|
+
char *e;
|
415
|
+
register int c;
|
407
416
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
return 0;
|
418
|
-
}
|
419
|
-
else {
|
420
|
-
for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
|
421
|
-
if ( c == EOF ) return 0;
|
422
|
-
if ( c == ')' )
|
423
|
-
shift(f, -1);
|
417
|
+
while ( (c = pull(f)) != EOF ) {
|
418
|
+
e = cursor(f);
|
419
|
+
if ( c == quote ) {
|
420
|
+
if ( (c = eatspace(f)) == ')' ) {
|
421
|
+
T(ref->title) = 1+title;
|
422
|
+
S(ref->title) = (e-title)-2;
|
423
|
+
return 1;
|
424
|
+
}
|
425
|
+
}
|
424
426
|
}
|
425
|
-
|
426
|
-
return
|
427
|
+
mmiotseek(f, whence);
|
428
|
+
return 0;
|
427
429
|
}
|
428
430
|
|
429
431
|
|
430
432
|
/* extract a =HHHxWWW size from the input stream
|
431
433
|
*/
|
432
434
|
static int
|
433
|
-
linkysize(MMIOT *f,
|
435
|
+
linkysize(MMIOT *f, Footnote *ref)
|
434
436
|
{
|
435
437
|
int height=0, width=0;
|
438
|
+
int whence = mmiottell(f);
|
436
439
|
int c;
|
437
440
|
|
438
|
-
|
439
|
-
|
441
|
+
if ( isspace(peek(f,0)) ) {
|
442
|
+
pull(f); /* eat '=' */
|
440
443
|
|
441
|
-
if ( (c = eatspace(f)) != '=' )
|
442
|
-
return (c != EOF);
|
443
|
-
pull(f); /* eat '=' */
|
444
|
-
|
445
|
-
for ( c = pull(f); isdigit(c); c = pull(f))
|
446
|
-
width = (width * 10) + (c - '0');
|
447
|
-
|
448
|
-
if ( c == 'x' ) {
|
449
444
|
for ( c = pull(f); isdigit(c); c = pull(f))
|
450
|
-
|
451
|
-
|
452
|
-
if ( c != EOF ) {
|
453
|
-
if ( !isspace(c) ) shift(f, -1);
|
454
|
-
*heightp = height;
|
455
|
-
*widthp = width;
|
456
|
-
return 1;
|
457
|
-
}
|
458
|
-
}
|
459
|
-
return 0;
|
460
|
-
}
|
445
|
+
width = (width * 10) + (c - '0');
|
461
446
|
|
447
|
+
if ( c == 'x' ) {
|
448
|
+
for ( c = pull(f); isdigit(c); c = pull(f))
|
449
|
+
height = (height*10) + (c - '0');
|
462
450
|
|
463
|
-
|
464
|
-
|
465
|
-
static char*
|
466
|
-
linkytitle(MMIOT *f, int *sizep)
|
467
|
-
{
|
468
|
-
int countq=0, qc, c, size;
|
469
|
-
char *ret, *lastqc = 0;
|
470
|
-
|
471
|
-
eatspace(f);
|
472
|
-
if ( (qc=pull(f)) != '"' && qc != '\'' && qc != '(' )
|
473
|
-
return 0;
|
451
|
+
if ( isspace(c) )
|
452
|
+
c = eatspace(f);
|
474
453
|
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
*sizep = size-1;
|
481
|
-
return ret;
|
482
|
-
}
|
483
|
-
else if ( c == qc ) {
|
484
|
-
lastqc = cursor(f);
|
485
|
-
countq++;
|
454
|
+
if ( (c == ')') || ((c == '\'' || c == '"') && linkytitle(f, c, ref)) ) {
|
455
|
+
ref->height = height;
|
456
|
+
ref->width = width;
|
457
|
+
return 1;
|
458
|
+
}
|
486
459
|
}
|
487
460
|
}
|
461
|
+
mmiotseek(f, whence);
|
488
462
|
return 0;
|
489
463
|
}
|
490
464
|
|
491
465
|
|
492
|
-
/*
|
493
|
-
*
|
466
|
+
/* extract a (-prefixed url from the input stream.
|
467
|
+
* the label is either of the format `<link>`, where I
|
468
|
+
* extract until I find a >, or it is of the format
|
469
|
+
* `text`, where I extract until I reach a ')', a quote,
|
470
|
+
* or (if image) a '='
|
494
471
|
*/
|
495
472
|
static int
|
496
|
-
|
473
|
+
linkyurl(MMIOT *f, int image, Footnote *p)
|
497
474
|
{
|
498
|
-
|
499
|
-
|
500
|
-
int here;
|
501
|
-
|
502
|
-
memset(val, 0, sizeof *val);
|
475
|
+
int c;
|
476
|
+
int mayneedtotrim=0;
|
503
477
|
|
504
|
-
if ( (
|
478
|
+
if ( (c = eatspace(f)) == EOF )
|
505
479
|
return 0;
|
506
480
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
/* embedded link */
|
512
|
-
if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
|
513
|
-
return 0;
|
481
|
+
if ( c == '<' ) {
|
482
|
+
pull(f);
|
483
|
+
mayneedtotrim=1;
|
484
|
+
}
|
514
485
|
|
515
|
-
|
486
|
+
T(p->link) = cursor(f);
|
487
|
+
for ( S(p->link)=0; (c = peek(f,1)) != ')'; ++S(p->link) ) {
|
488
|
+
if ( c == EOF )
|
516
489
|
return 0;
|
490
|
+
else if ( (c == '"' || c == '\'') && linkytitle(f, c, p) )
|
491
|
+
break;
|
492
|
+
else if ( image && (c == '=') && linkysize(f, p) )
|
493
|
+
break;
|
494
|
+
else if ( (c == '\\') && ispunct(peek(f,2)) ) {
|
495
|
+
++S(p->link);
|
496
|
+
pull(f);
|
497
|
+
}
|
498
|
+
pull(f);
|
499
|
+
}
|
500
|
+
if ( peek(f, 1) == ')' )
|
501
|
+
pull(f);
|
502
|
+
|
503
|
+
___mkd_tidy(&p->link);
|
504
|
+
|
505
|
+
if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') )
|
506
|
+
--S(p->link);
|
507
|
+
|
508
|
+
return 1;
|
509
|
+
}
|
517
510
|
|
518
|
-
T(val->title) = linkytitle(f, &S(val->title));
|
519
511
|
|
520
|
-
return peek(f,0) == ')';
|
521
512
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
513
|
+
/* prefixes for <automatic links>
|
514
|
+
*/
|
515
|
+
static struct {
|
516
|
+
char *name;
|
517
|
+
int nlen;
|
518
|
+
} protocol[] = {
|
519
|
+
#define _aprotocol(x) { x, (sizeof x)-1 }
|
520
|
+
_aprotocol( "http://" ),
|
521
|
+
_aprotocol( "https://" ),
|
522
|
+
_aprotocol( "ftp://" ),
|
523
|
+
_aprotocol( "news://" ),
|
524
|
+
#undef _aprotocol
|
525
|
+
};
|
526
|
+
#define NRPROTOCOLS (sizeof protocol / sizeof protocol[0])
|
529
527
|
|
530
|
-
if ( !S(val->tag) )
|
531
|
-
val->tag = mylabel;
|
532
|
-
}
|
533
|
-
else if ( f->flags & MKD_1_COMPAT )
|
534
|
-
break;
|
535
|
-
else
|
536
|
-
mmiotseek(f,here);
|
537
528
|
|
538
|
-
|
539
|
-
|
529
|
+
static int
|
530
|
+
isautoprefix(char *text)
|
531
|
+
{
|
532
|
+
int i;
|
540
533
|
|
541
|
-
|
542
|
-
|
543
|
-
val->link = ret->link;
|
544
|
-
val->title = ret->title;
|
545
|
-
val->height = ret->height;
|
546
|
-
val->width = ret->width;
|
534
|
+
for (i=0; i < NRPROTOCOLS; i++)
|
535
|
+
if ( strncasecmp(text, protocol[i].name, protocol[i].nlen) == 0 )
|
547
536
|
return 1;
|
548
|
-
}
|
549
|
-
}
|
550
537
|
return 0;
|
551
538
|
}
|
552
539
|
|
@@ -564,12 +551,14 @@ typedef struct linkytype {
|
|
564
551
|
char *text_pfx; /* text prefix (eg: ">" */
|
565
552
|
char *text_sfx; /* text suffix (eg: "</a>" */
|
566
553
|
int flags; /* reparse flags */
|
554
|
+
int kind; /* tag is url or something else? */
|
555
|
+
#define IS_URL 0x01
|
567
556
|
} linkytype;
|
568
557
|
|
569
558
|
static linkytype imaget = { 0, 0, "<img src=\"", "\"",
|
570
|
-
1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
|
559
|
+
1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG, IS_URL };
|
571
560
|
static linkytype linkt = { 0, 0, "<a href=\"", "\"",
|
572
|
-
0, ">", "</a>", DENY_A };
|
561
|
+
0, ">", "</a>", DENY_A, IS_URL };
|
573
562
|
|
574
563
|
/*
|
575
564
|
* pseudo-protocols for [][];
|
@@ -579,9 +568,10 @@ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
|
|
579
568
|
* raw: just dump the link without any processing
|
580
569
|
*/
|
581
570
|
static linkytype specials[] = {
|
582
|
-
{ "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
|
583
|
-
{ "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
|
584
|
-
{ "raw:", 4, 0, 0, 0, 0, 0, 0 },
|
571
|
+
{ "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0, IS_URL },
|
572
|
+
{ "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0, 0 },
|
573
|
+
{ "raw:", 4, 0, 0, 0, 0, 0, DENY_HTML, 0 },
|
574
|
+
{ "abbr:", 5, "<abbr title=\"", "\"", 0, ">", "</abbr>", 0, 0 },
|
585
575
|
} ;
|
586
576
|
|
587
577
|
#define NR(x) (sizeof x / sizeof x[0])
|
@@ -589,7 +579,7 @@ static linkytype specials[] = {
|
|
589
579
|
/* see if t contains one of our pseudo-protocols.
|
590
580
|
*/
|
591
581
|
static linkytype *
|
592
|
-
|
582
|
+
pseudo(Cstring t)
|
593
583
|
{
|
594
584
|
int i;
|
595
585
|
linkytype *r;
|
@@ -603,57 +593,124 @@ extratag(Cstring t)
|
|
603
593
|
}
|
604
594
|
|
605
595
|
|
606
|
-
/*
|
607
|
-
* process embedded links and images
|
596
|
+
/* print out a linky (or fail if it's Not Allowed)
|
608
597
|
*/
|
609
598
|
static int
|
610
|
-
|
599
|
+
linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
|
611
600
|
{
|
612
|
-
int start = mmiottell(f);
|
613
|
-
Footnote link;
|
614
601
|
linkytype *tag;
|
615
602
|
|
616
|
-
if ( !linkykey(image, &link, f) ) {
|
617
|
-
mmiotseek(f, start);
|
618
|
-
return 0;
|
619
|
-
}
|
620
|
-
|
621
603
|
if ( image )
|
622
604
|
tag = &imaget;
|
623
|
-
else if (
|
605
|
+
else if ( tag = pseudo(ref->link) ) {
|
606
|
+
if ( f->flags & (NO_PSEUDO_PROTO|SAFELINK) )
|
607
|
+
return 0;
|
608
|
+
}
|
609
|
+
else if ( (f->flags & SAFELINK) && T(ref->link)
|
610
|
+
&& (T(ref->link)[0] != '/')
|
611
|
+
&& !isautoprefix(T(ref->link)) )
|
612
|
+
/* if SAFELINK, only accept links that are local or
|
613
|
+
* a well-known protocol
|
614
|
+
*/
|
615
|
+
return 0;
|
616
|
+
else
|
624
617
|
tag = &linkt;
|
625
618
|
|
626
|
-
if ( f->flags & tag->
|
627
|
-
mmiotseek(f, start);
|
619
|
+
if ( f->flags & tag->flags )
|
628
620
|
return 0;
|
629
|
-
}
|
630
621
|
|
631
622
|
if ( tag->link_pfx ) {
|
632
623
|
Qstring(tag->link_pfx, f);
|
633
|
-
|
634
|
-
|
635
|
-
|
624
|
+
|
625
|
+
if ( tag->kind & IS_URL ) {
|
626
|
+
if ( f->base && T(ref->link) && (T(ref->link)[tag->szpat] == '/') )
|
627
|
+
puturl(f->base, strlen(f->base), f, 0);
|
628
|
+
puturl(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f, 0);
|
629
|
+
}
|
630
|
+
else
|
631
|
+
___mkd_reparse(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, INSIDE_TAG, f);
|
632
|
+
|
636
633
|
Qstring(tag->link_sfx, f);
|
637
634
|
|
638
|
-
if ( tag->WxH &&
|
639
|
-
Qprintf(f," height=\"%d\"",
|
640
|
-
Qprintf(f, " width=\"%d\"",
|
635
|
+
if ( tag->WxH && ref->height && ref->width ) {
|
636
|
+
Qprintf(f," height=\"%d\"", ref->height);
|
637
|
+
Qprintf(f, " width=\"%d\"", ref->width);
|
641
638
|
}
|
642
639
|
|
643
|
-
if ( S(
|
640
|
+
if ( S(ref->title) ) {
|
644
641
|
Qstring(" title=\"", f);
|
645
|
-
___mkd_reparse(T(
|
642
|
+
___mkd_reparse(T(ref->title), S(ref->title), INSIDE_TAG, f);
|
646
643
|
Qchar('"', f);
|
647
644
|
}
|
648
645
|
|
649
646
|
Qstring(tag->text_pfx, f);
|
650
|
-
___mkd_reparse(T(
|
647
|
+
___mkd_reparse(T(text), S(text), tag->flags, f);
|
651
648
|
Qstring(tag->text_sfx, f);
|
652
649
|
}
|
653
650
|
else
|
654
|
-
Qwrite(T(link
|
651
|
+
Qwrite(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f);
|
655
652
|
|
656
653
|
return 1;
|
654
|
+
} /* linkyformat */
|
655
|
+
|
656
|
+
|
657
|
+
/*
|
658
|
+
* process embedded links and images
|
659
|
+
*/
|
660
|
+
static int
|
661
|
+
linkylinky(int image, MMIOT *f)
|
662
|
+
{
|
663
|
+
int start = mmiottell(f);
|
664
|
+
Cstring name;
|
665
|
+
Footnote key, *ref;
|
666
|
+
|
667
|
+
int status = 0;
|
668
|
+
|
669
|
+
CREATE(name);
|
670
|
+
bzero(&key, sizeof key);
|
671
|
+
|
672
|
+
if ( linkylabel(f, &name) ) {
|
673
|
+
if ( peek(f,1) == '(' ) {
|
674
|
+
pull(f);
|
675
|
+
if ( linkyurl(f, image, &key) )
|
676
|
+
status = linkyformat(f, name, image, &key);
|
677
|
+
}
|
678
|
+
else {
|
679
|
+
int goodlink, implicit_mark = mmiottell(f);
|
680
|
+
|
681
|
+
if ( eatspace(f) == '[' ) {
|
682
|
+
pull(f); /* consume leading '[' */
|
683
|
+
goodlink = linkylabel(f, &key.tag);
|
684
|
+
}
|
685
|
+
else {
|
686
|
+
/* new markdown implicit name syntax doesn't
|
687
|
+
* require a second []
|
688
|
+
*/
|
689
|
+
mmiotseek(f, implicit_mark);
|
690
|
+
goodlink = !(f->flags & MKD_1_COMPAT);
|
691
|
+
}
|
692
|
+
|
693
|
+
if ( goodlink ) {
|
694
|
+
if ( !S(key.tag) ) {
|
695
|
+
DELETE(key.tag);
|
696
|
+
T(key.tag) = T(name);
|
697
|
+
S(key.tag) = S(name);
|
698
|
+
}
|
699
|
+
|
700
|
+
if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes),
|
701
|
+
sizeof key, (stfu)__mkd_footsort) )
|
702
|
+
status = linkyformat(f, name, image, ref);
|
703
|
+
}
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
DELETE(name);
|
708
|
+
___mkd_freefootnote(&key);
|
709
|
+
|
710
|
+
if ( status == 0 )
|
711
|
+
mmiotseek(f, start);
|
712
|
+
|
713
|
+
return status;
|
657
714
|
}
|
658
715
|
|
659
716
|
|
@@ -706,6 +763,80 @@ forbidden_tag(MMIOT *f)
|
|
706
763
|
}
|
707
764
|
|
708
765
|
|
766
|
+
/* Check a string to see if it looks like a mail address
|
767
|
+
* "looks like a mail address" means alphanumeric + some
|
768
|
+
* specials, then a `@`, then alphanumeric + some specials,
|
769
|
+
* but with a `.`
|
770
|
+
*/
|
771
|
+
static int
|
772
|
+
maybe_address(char *p, int size)
|
773
|
+
{
|
774
|
+
int ok = 0;
|
775
|
+
|
776
|
+
for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size)
|
777
|
+
;
|
778
|
+
|
779
|
+
if ( ! (size && *p == '@') )
|
780
|
+
return 0;
|
781
|
+
|
782
|
+
--size, ++p;
|
783
|
+
|
784
|
+
if ( size && *p == '.' ) return 0;
|
785
|
+
|
786
|
+
for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size )
|
787
|
+
if ( *p == '.' && size > 1 ) ok = 1;
|
788
|
+
|
789
|
+
return size ? 0 : ok;
|
790
|
+
}
|
791
|
+
|
792
|
+
|
793
|
+
/* The size-length token at cursor(f) is either a mailto:, an
|
794
|
+
* implicit mailto:, one of the approved url protocols, or just
|
795
|
+
* plain old text. If it's a mailto: or an approved protocol,
|
796
|
+
* linkify it, otherwise say "no"
|
797
|
+
*/
|
798
|
+
static int
|
799
|
+
process_possible_link(MMIOT *f, int size)
|
800
|
+
{
|
801
|
+
int address= 0;
|
802
|
+
int mailto = 0;
|
803
|
+
char *text = cursor(f);
|
804
|
+
|
805
|
+
if ( f->flags & DENY_A ) return 0;
|
806
|
+
|
807
|
+
if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) {
|
808
|
+
/* if it says it's a mailto, it's a mailto -- who am
|
809
|
+
* I to second-guess the user?
|
810
|
+
*/
|
811
|
+
address = 1;
|
812
|
+
mailto = 7; /* 7 is the length of "mailto:"; we need this */
|
813
|
+
}
|
814
|
+
else
|
815
|
+
address = maybe_address(text, size);
|
816
|
+
|
817
|
+
if ( address ) {
|
818
|
+
Qstring("<a href=\"", f);
|
819
|
+
if ( !mailto ) {
|
820
|
+
/* supply a mailto: protocol if one wasn't attached */
|
821
|
+
mangle("mailto:", 7, f);
|
822
|
+
}
|
823
|
+
mangle(text, size, f);
|
824
|
+
Qstring("\">", f);
|
825
|
+
mangle(text+mailto, size-mailto, f);
|
826
|
+
Qstring("</a>", f);
|
827
|
+
return 1;
|
828
|
+
}
|
829
|
+
else if ( isautoprefix(text) ) {
|
830
|
+
Qstring("<a href=\"", f);
|
831
|
+
puturl(text,size,f, 0);
|
832
|
+
Qstring("\">", f);
|
833
|
+
puturl(text,size,f, 1);
|
834
|
+
Qstring("</a>", f);
|
835
|
+
return 1;
|
836
|
+
}
|
837
|
+
return 0;
|
838
|
+
} /* process_possible_link */
|
839
|
+
|
709
840
|
|
710
841
|
/* a < may be just a regular character, the start of an embedded html
|
711
842
|
* tag, or the start of an <automatic link>. If it's an automatic
|
@@ -716,68 +847,69 @@ forbidden_tag(MMIOT *f)
|
|
716
847
|
static int
|
717
848
|
maybe_tag_or_link(MMIOT *f)
|
718
849
|
{
|
719
|
-
|
720
|
-
int
|
721
|
-
int maybetag=1, maybeaddress=0;
|
722
|
-
int mailto;
|
850
|
+
int c, size;
|
851
|
+
int maybetag = 1;
|
723
852
|
|
724
853
|
if ( f->flags & INSIDE_TAG )
|
725
854
|
return 0;
|
726
855
|
|
727
|
-
for ( size=0; (
|
728
|
-
if (
|
729
|
-
maybetag=0;
|
730
|
-
if ( c == '@' )
|
731
|
-
maybeaddress=1;
|
732
|
-
else if ( c == EOF )
|
856
|
+
for ( size=0; (c = peek(f, size+1)) != '>'; size++) {
|
857
|
+
if ( c == EOF )
|
733
858
|
return 0;
|
859
|
+
else if ( c == '\\' ) {
|
860
|
+
maybetag=0;
|
861
|
+
if ( peek(f, size+2) != EOF )
|
862
|
+
size++;
|
863
|
+
}
|
864
|
+
else if ( isspace(c) )
|
865
|
+
break;
|
866
|
+
else if ( ! (c == '/' || isalnum(c) ) )
|
867
|
+
maybetag=0;
|
734
868
|
}
|
735
869
|
|
736
|
-
if ( size
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
870
|
+
if ( size ) {
|
871
|
+
if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
|
872
|
+
Qstring(forbidden_tag(f) ? "<" : "<", f);
|
873
|
+
while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
|
874
|
+
cputc(pull(f), f);
|
875
|
+
return 1;
|
876
|
+
}
|
877
|
+
else if ( !isspace(c) && process_possible_link(f, size) ) {
|
878
|
+
shift(f, size+1);
|
879
|
+
return 1;
|
880
|
+
}
|
744
881
|
}
|
882
|
+
|
883
|
+
return 0;
|
884
|
+
}
|
745
885
|
|
746
|
-
if ( f->flags & DENY_A ) return 0;
|
747
|
-
|
748
|
-
text = cursor(f);
|
749
|
-
shift(f, size+1);
|
750
886
|
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
if ( maybeaddress ) {
|
887
|
+
/* autolinking means that all inline html is <a href'ified>. A
|
888
|
+
* autolink url is alphanumerics, slashes, periods, underscores,
|
889
|
+
* the at sign, colon, and the % character.
|
890
|
+
*/
|
891
|
+
static int
|
892
|
+
maybe_autolink(MMIOT *f)
|
893
|
+
{
|
894
|
+
register int c;
|
895
|
+
int size;
|
761
896
|
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
mangle("mailto:", 7, f);
|
897
|
+
/* greedily scan forward for the end of a legitimate link.
|
898
|
+
*/
|
899
|
+
for ( size=0; (c=peek(f, size+1)) != EOF; size++ )
|
900
|
+
if ( c == '\\' ) {
|
901
|
+
if ( peek(f, size+2) != EOF )
|
902
|
+
++size;
|
769
903
|
}
|
904
|
+
else if ( isspace(c) || strchr("'\"()[]{}<>`", c) )
|
905
|
+
break;
|
770
906
|
|
771
|
-
|
772
|
-
|
773
|
-
mangle(text+mailto, size-mailto, f);
|
774
|
-
Qstring("</a>", f);
|
907
|
+
if ( (size > 1) && process_possible_link(f, size) ) {
|
908
|
+
shift(f, size);
|
775
909
|
return 1;
|
776
910
|
}
|
777
|
-
|
778
|
-
shift(f, -(size+1));
|
779
911
|
return 0;
|
780
|
-
}
|
912
|
+
}
|
781
913
|
|
782
914
|
|
783
915
|
/* smartyquote code that's common for single and double quotes
|
@@ -865,7 +997,7 @@ smartypants(int c, int *flags, MMIOT *f)
|
|
865
997
|
{
|
866
998
|
int i;
|
867
999
|
|
868
|
-
if ( f->flags & DENY_SMARTY )
|
1000
|
+
if ( f->flags & (DENY_SMARTY|INSIDE_TAG) )
|
869
1001
|
return 0;
|
870
1002
|
|
871
1003
|
for ( i=0; i < NRSMART; i++)
|
@@ -919,12 +1051,23 @@ text(MMIOT *f)
|
|
919
1051
|
int rep;
|
920
1052
|
int smartyflags = 0;
|
921
1053
|
|
922
|
-
while (
|
1054
|
+
while (1) {
|
1055
|
+
if ( (f->flags & AUTOLINK) && isalpha(peek(f,1)) )
|
1056
|
+
maybe_autolink(f);
|
1057
|
+
|
1058
|
+
c = pull(f);
|
1059
|
+
|
1060
|
+
if (c == EOF)
|
1061
|
+
break;
|
1062
|
+
|
923
1063
|
if ( smartypants(c, &smartyflags, f) )
|
924
1064
|
continue;
|
925
1065
|
switch (c) {
|
926
1066
|
case 0: break;
|
927
1067
|
|
1068
|
+
case 3: Qstring("<br/>", f);
|
1069
|
+
break;
|
1070
|
+
|
928
1071
|
case '>': if ( tag_text(f) )
|
929
1072
|
Qstring(">", f);
|
930
1073
|
else
|
@@ -968,15 +1111,24 @@ text(MMIOT *f)
|
|
968
1111
|
case '_':
|
969
1112
|
#if RELAXED_EMPHASIS
|
970
1113
|
/* Underscores don't count if they're in the middle of a word */
|
971
|
-
if (
|
972
|
-
|
973
|
-
|
1114
|
+
if ( !(f->flags & STRICT) && isthisalnum(f,-1)
|
1115
|
+
&& isthisalnum(f,1) ) {
|
1116
|
+
Qchar(c, f);
|
1117
|
+
break;
|
1118
|
+
}
|
1119
|
+
#endif
|
1120
|
+
case '*':
|
1121
|
+
#if RELAXED_EMPHASIS
|
1122
|
+
/* Underscores & stars don't count if they're out in the middle
|
1123
|
+
* of whitespace */
|
1124
|
+
if ( !(f->flags & STRICT) && isthisspace(f,-1)
|
1125
|
+
&& isthisspace(f,1) ) {
|
974
1126
|
Qchar(c, f);
|
975
1127
|
break;
|
976
1128
|
}
|
977
1129
|
/* else fall into the regular old emphasis case */
|
978
1130
|
#endif
|
979
|
-
|
1131
|
+
if ( tag_text(f) )
|
980
1132
|
Qchar(c, f);
|
981
1133
|
else {
|
982
1134
|
for (rep = 1; peek(f,1) == c; pull(f) )
|
@@ -1128,6 +1280,106 @@ printheader(Paragraph *pp, MMIOT *f)
|
|
1128
1280
|
}
|
1129
1281
|
|
1130
1282
|
|
1283
|
+
enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT };
|
1284
|
+
|
1285
|
+
static char* alignments[] = { "", " align=\"center\"", " align=\"left\"",
|
1286
|
+
" align=\"right\"" };
|
1287
|
+
|
1288
|
+
typedef STRING(int) Istring;
|
1289
|
+
|
1290
|
+
static int
|
1291
|
+
splat(Line *p, char *block, Istring align, int force, MMIOT *f)
|
1292
|
+
{
|
1293
|
+
int first,
|
1294
|
+
idx = 0,
|
1295
|
+
colno = 0;
|
1296
|
+
|
1297
|
+
Qstring("<tr>\n", f);
|
1298
|
+
while ( idx < S(p->text) ) {
|
1299
|
+
first = idx;
|
1300
|
+
if ( force && (colno >= S(align)-1) )
|
1301
|
+
idx = S(p->text);
|
1302
|
+
else
|
1303
|
+
while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') )
|
1304
|
+
++idx;
|
1305
|
+
|
1306
|
+
Qprintf(f, "<%s%s>",
|
1307
|
+
block,
|
1308
|
+
alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]);
|
1309
|
+
___mkd_reparse(T(p->text)+first, idx-first, 0, f);
|
1310
|
+
Qprintf(f, "</%s>\n", block);
|
1311
|
+
idx++;
|
1312
|
+
colno++;
|
1313
|
+
}
|
1314
|
+
if ( force )
|
1315
|
+
while (colno < S(align) ) {
|
1316
|
+
Qprintf(f, "<%s></%s>\n", block, block);
|
1317
|
+
++colno;
|
1318
|
+
}
|
1319
|
+
Qstring("</tr>\n", f);
|
1320
|
+
return colno;
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
static int
|
1324
|
+
printtable(Paragraph *pp, MMIOT *f)
|
1325
|
+
{
|
1326
|
+
/* header, dashes, then lines of content */
|
1327
|
+
|
1328
|
+
Line *hdr, *dash, *body;
|
1329
|
+
Istring align;
|
1330
|
+
int start;
|
1331
|
+
int hcols;
|
1332
|
+
char *p;
|
1333
|
+
|
1334
|
+
if ( !(pp->text && pp->text->next && pp->text->next->next) )
|
1335
|
+
return 0;
|
1336
|
+
|
1337
|
+
hdr = pp->text;
|
1338
|
+
dash= hdr->next;
|
1339
|
+
body= dash->next;
|
1340
|
+
|
1341
|
+
/* first figure out cell alignments */
|
1342
|
+
|
1343
|
+
CREATE(align);
|
1344
|
+
|
1345
|
+
for (p=T(dash->text), start=0; start < S(dash->text); ) {
|
1346
|
+
char first, last;
|
1347
|
+
int end;
|
1348
|
+
|
1349
|
+
last=first=0;
|
1350
|
+
for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
|
1351
|
+
if ( !isspace(p[end]) ) {
|
1352
|
+
if ( !first) first = p[end];
|
1353
|
+
last = p[end];
|
1354
|
+
}
|
1355
|
+
}
|
1356
|
+
EXPAND(align) = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT)
|
1357
|
+
: (( last == ':') ? a_RIGHT : a_NONE );
|
1358
|
+
start = 1+end;
|
1359
|
+
}
|
1360
|
+
|
1361
|
+
Qstring("<table>\n", f);
|
1362
|
+
Qstring("<thead>\n", f);
|
1363
|
+
hcols = splat(hdr, "th", align, 0, f);
|
1364
|
+
Qstring("</thead>\n", f);
|
1365
|
+
|
1366
|
+
if ( hcols < S(align) )
|
1367
|
+
S(align) = hcols;
|
1368
|
+
else
|
1369
|
+
while ( hcols > S(align) )
|
1370
|
+
EXPAND(align) = a_NONE;
|
1371
|
+
|
1372
|
+
Qstring("<tbody>\n", f);
|
1373
|
+
for ( ; body; body = body->next)
|
1374
|
+
splat(body, "td", align, 1, f);
|
1375
|
+
Qstring("</tbody>\n", f);
|
1376
|
+
Qstring("</table>\n", f);
|
1377
|
+
|
1378
|
+
DELETE(align);
|
1379
|
+
return 1;
|
1380
|
+
}
|
1381
|
+
|
1382
|
+
|
1131
1383
|
static int
|
1132
1384
|
printblock(Paragraph *pp, MMIOT *f)
|
1133
1385
|
{
|
@@ -1140,10 +1392,10 @@ printblock(Paragraph *pp, MMIOT *f)
|
|
1140
1392
|
if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
|
1141
1393
|
&& T(t->text)[S(t->text)-1] == ' ') {
|
1142
1394
|
push(T(t->text), S(t->text)-2, f);
|
1143
|
-
push("
|
1395
|
+
push("\003\n", 2, f);
|
1144
1396
|
}
|
1145
1397
|
else {
|
1146
|
-
___mkd_tidy(t);
|
1398
|
+
___mkd_tidy(&t->text);
|
1147
1399
|
push(T(t->text), S(t->text), f);
|
1148
1400
|
if ( t->next )
|
1149
1401
|
push("\n", 1, f);
|
@@ -1234,6 +1486,7 @@ definitionlist(Paragraph *p, MMIOT *f)
|
|
1234
1486
|
}
|
1235
1487
|
|
1236
1488
|
htmlify(p->down, "dd", p->ident, f);
|
1489
|
+
Qchar('\n', f);
|
1237
1490
|
}
|
1238
1491
|
|
1239
1492
|
Qstring("</dl>", f);
|
@@ -1305,6 +1558,14 @@ display(Paragraph *p, MMIOT *f)
|
|
1305
1558
|
printheader(p, f);
|
1306
1559
|
break;
|
1307
1560
|
|
1561
|
+
case TABLE:
|
1562
|
+
printtable(p, f);
|
1563
|
+
break;
|
1564
|
+
|
1565
|
+
case SOURCE:
|
1566
|
+
htmlify(p->down, 0, 0, f);
|
1567
|
+
break;
|
1568
|
+
|
1308
1569
|
default:
|
1309
1570
|
printblock(p, f);
|
1310
1571
|
break;
|
@@ -1313,29 +1574,6 @@ display(Paragraph *p, MMIOT *f)
|
|
1313
1574
|
}
|
1314
1575
|
|
1315
1576
|
|
1316
|
-
/*
|
1317
|
-
* dump out stylesheet sections.
|
1318
|
-
*/
|
1319
|
-
static int
|
1320
|
-
stylesheets(Paragraph *p, FILE *f)
|
1321
|
-
{
|
1322
|
-
Line* q;
|
1323
|
-
|
1324
|
-
for ( ; p ; p = p->next ) {
|
1325
|
-
if ( p->typ == STYLE ) {
|
1326
|
-
for ( q = p->text; q ; q = q->next )
|
1327
|
-
if ( fwrite(T(q->text), S(q->text), 1, f) == 1 )
|
1328
|
-
putc('\n', f);
|
1329
|
-
else
|
1330
|
-
return EOF;
|
1331
|
-
}
|
1332
|
-
if ( p->down && (stylesheets(p->down, f) == EOF) )
|
1333
|
-
return EOF;
|
1334
|
-
}
|
1335
|
-
return 0;
|
1336
|
-
}
|
1337
|
-
|
1338
|
-
|
1339
1577
|
/* return a pointer to the compiled markdown
|
1340
1578
|
* document.
|
1341
1579
|
*/
|
@@ -1354,36 +1592,3 @@ mkd_document(Document *p, char **res)
|
|
1354
1592
|
return EOF;
|
1355
1593
|
}
|
1356
1594
|
|
1357
|
-
|
1358
|
-
/* public interface for ___mkd_reparse()
|
1359
|
-
*/
|
1360
|
-
int
|
1361
|
-
mkd_text(char *bfr, int size, FILE *output, int flags)
|
1362
|
-
{
|
1363
|
-
MMIOT f;
|
1364
|
-
|
1365
|
-
___mkd_initmmiot(&f, 0);
|
1366
|
-
f.flags = flags & USER_FLAGS;
|
1367
|
-
|
1368
|
-
___mkd_reparse(bfr, size, 0, &f);
|
1369
|
-
___mkd_emblock(&f);
|
1370
|
-
if ( flags & CDATA_OUTPUT )
|
1371
|
-
___mkd_xml(T(f.out), S(f.out), output);
|
1372
|
-
else
|
1373
|
-
fwrite(T(f.out), S(f.out), 1, output);
|
1374
|
-
|
1375
|
-
___mkd_freemmiot(&f, 0);
|
1376
|
-
return 0;
|
1377
|
-
}
|
1378
|
-
|
1379
|
-
|
1380
|
-
/* dump any embedded styles
|
1381
|
-
*/
|
1382
|
-
int
|
1383
|
-
mkd_style(Document *d, FILE *f)
|
1384
|
-
{
|
1385
|
-
if ( d && d->compiled )
|
1386
|
-
return stylesheets(d->code, f);
|
1387
|
-
return EOF;
|
1388
|
-
}
|
1389
|
-
|