rdiscount 1.2.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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/markdown.h
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
#ifndef _MARKDOWN_D
|
2
|
+
#define _MARKDOWN_D
|
3
|
+
|
4
|
+
#include "cstring.h"
|
5
|
+
|
6
|
+
/* reference-style links (and images) are stored in an array
|
7
|
+
* of footnotes.
|
8
|
+
*/
|
9
|
+
typedef struct footnote {
|
10
|
+
Cstring tag; /* the tag for the reference link */
|
11
|
+
Cstring link; /* what this footnote points to */
|
12
|
+
Cstring title; /* what it's called (TITLE= attribute) */
|
13
|
+
int height, width; /* dimensions (for image link) */
|
14
|
+
} Footnote;
|
15
|
+
|
16
|
+
/* each input line is read into a Line, which contains the line,
|
17
|
+
* the offset of the first non-space character [this assumes
|
18
|
+
* that all tabs will be expanded to spaces!], and a pointer to
|
19
|
+
* the next line.
|
20
|
+
*/
|
21
|
+
typedef struct line {
|
22
|
+
Cstring text;
|
23
|
+
struct line *next;
|
24
|
+
int dle;
|
25
|
+
} Line;
|
26
|
+
|
27
|
+
|
28
|
+
/* a paragraph is a collection of Lines, with links to the next paragraph
|
29
|
+
* and (if it's a QUOTE, UL, or OL) to the reparsed contents of this
|
30
|
+
* paragraph.
|
31
|
+
*/
|
32
|
+
typedef struct paragraph {
|
33
|
+
struct paragraph *next; /* next paragraph */
|
34
|
+
struct paragraph *down; /* recompiled contents of this paragraph */
|
35
|
+
struct line *text; /* all the text in this paragraph */
|
36
|
+
enum { WHITESPACE=0, CODE, QUOTE, MARKUP,
|
37
|
+
HTML, STYLE, DL, UL, OL, LISTITEM,
|
38
|
+
HDR, HR } typ;
|
39
|
+
enum { IMPLICIT=0, PARA, CENTER} align;
|
40
|
+
int hnumber; /* <Hn> for typ == HDR */
|
41
|
+
} Paragraph;
|
42
|
+
|
43
|
+
enum { ETX, SETEXT }; /* header types */
|
44
|
+
|
45
|
+
|
46
|
+
typedef struct block {
|
47
|
+
enum { bTEXT, bSTAR, bUNDER } b_type;
|
48
|
+
int b_count;
|
49
|
+
char b_char;
|
50
|
+
Cstring b_text;
|
51
|
+
Cstring b_post;
|
52
|
+
} block;
|
53
|
+
|
54
|
+
typedef STRING(block) Qblock;
|
55
|
+
|
56
|
+
|
57
|
+
/* a magic markdown io thing holds all the data structures needed to
|
58
|
+
* do the backend processing of a markdown document
|
59
|
+
*/
|
60
|
+
typedef struct mmiot {
|
61
|
+
Cstring out;
|
62
|
+
Cstring in;
|
63
|
+
Qblock Q;
|
64
|
+
int isp;
|
65
|
+
STRING(Footnote) *footnotes;
|
66
|
+
int flags;
|
67
|
+
#define DENY_A 0x0001
|
68
|
+
#define DENY_IMG 0x0002
|
69
|
+
#define DENY_SMARTY 0x0004
|
70
|
+
#define INSIDE_TAG 0x0020
|
71
|
+
#define NO_PSEUDO_PROTO 0x0040
|
72
|
+
#define CDATA_OUTPUT 0x0080
|
73
|
+
#define USER_FLAGS 0x00FF
|
74
|
+
#define EMBEDDED DENY_A|DENY_IMG|NO_PSEUDO_PROTO|CDATA_OUTPUT
|
75
|
+
char *base;
|
76
|
+
} MMIOT;
|
77
|
+
|
78
|
+
|
79
|
+
/*
|
80
|
+
* the mkdio text input functions return a document structure,
|
81
|
+
* which contains a header (retrieved from the document if
|
82
|
+
* markdown was configured * with the * --enable-pandoc-header
|
83
|
+
* and the document begins with a pandoc-style header) and the
|
84
|
+
* root of the linked list of Lines.
|
85
|
+
*/
|
86
|
+
typedef struct document {
|
87
|
+
Line *headers; /* title -> author(s) -> date */
|
88
|
+
ANCHOR(Line) content; /* uncompiled text, not valid after compile() */
|
89
|
+
Paragraph *code; /* intermediate code generated by compile() */
|
90
|
+
int compiled; /* set after mkd_compile() */
|
91
|
+
int html; /* set after (internal) htmlify() */
|
92
|
+
int tabstop; /* for properly expanding tabs (ick) */
|
93
|
+
MMIOT *ctx; /* backend buffers, flags, and structures */
|
94
|
+
char *base; /* url basename for url fragments */
|
95
|
+
} Document;
|
96
|
+
|
97
|
+
|
98
|
+
extern int mkd_firstnonblank(Line *);
|
99
|
+
extern int mkd_compile(Document *, int);
|
100
|
+
extern int mkd_document(Document *, char **);
|
101
|
+
extern int mkd_generatehtml(Document *, FILE *);
|
102
|
+
extern void mkd_cleanup(Document *);
|
103
|
+
extern int mkd_text(char *, int, FILE*, int);
|
104
|
+
extern void mkd_basename(Document*, char *);
|
105
|
+
|
106
|
+
extern Document *mkd_in(FILE *, int);
|
107
|
+
extern Document *mkd_string(char*,int, int);
|
108
|
+
|
109
|
+
#define NO_HEADER 0x0100
|
110
|
+
#define STD_TABSTOP 0x0200
|
111
|
+
#define INPUT_MASK (NO_HEADER|STD_TABSTOP)
|
112
|
+
|
113
|
+
|
114
|
+
/* internal resource handling functions.
|
115
|
+
*/
|
116
|
+
extern void ___mkd_freeLine(Line *);
|
117
|
+
extern void ___mkd_freeLines(Line *);
|
118
|
+
extern void ___mkd_freeParagraph(Paragraph *);
|
119
|
+
extern void ___mkd_freefootnotes(MMIOT *);
|
120
|
+
extern void ___mkd_initmmiot(MMIOT *, void *);
|
121
|
+
extern void ___mkd_freemmiot(MMIOT *, void *);
|
122
|
+
extern void ___mkd_freeLineRange(Line *, Line *);
|
123
|
+
extern void ___mkd_xml(char *, int, FILE *);
|
124
|
+
|
125
|
+
#endif/*_MARKDOWN_D*/
|
data/ext/mkdio.c
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
/*
|
2
|
+
* mkdio -- markdown front end input functions
|
3
|
+
*
|
4
|
+
* Copyright (C) 2007 David L Parsons.
|
5
|
+
* The redistribution terms are provided in the COPYRIGHT file that must
|
6
|
+
* be distributed with this source code.
|
7
|
+
*/
|
8
|
+
#include "config.h"
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <stdlib.h>
|
11
|
+
#include <ctype.h>
|
12
|
+
|
13
|
+
#include "cstring.h"
|
14
|
+
#include "markdown.h"
|
15
|
+
#include "amalloc.h"
|
16
|
+
|
17
|
+
typedef ANCHOR(Line) LineAnchor;
|
18
|
+
|
19
|
+
/* create a new blank Document
|
20
|
+
*/
|
21
|
+
static Document*
|
22
|
+
new_Document()
|
23
|
+
{
|
24
|
+
Document *ret = calloc(sizeof(Document), 1);
|
25
|
+
|
26
|
+
if ( ret ) {
|
27
|
+
if (( ret->ctx = calloc(sizeof(MMIOT), 1) ))
|
28
|
+
return ret;
|
29
|
+
free(ret);
|
30
|
+
}
|
31
|
+
return 0;
|
32
|
+
}
|
33
|
+
|
34
|
+
|
35
|
+
/* add a line to the markdown input chain
|
36
|
+
*/
|
37
|
+
static void
|
38
|
+
queue(Document* a, Cstring *line)
|
39
|
+
{
|
40
|
+
Line *p = calloc(sizeof *p, 1);
|
41
|
+
unsigned char c;
|
42
|
+
int xp = 0;
|
43
|
+
int size = S(*line);
|
44
|
+
unsigned char *str = T(*line);
|
45
|
+
|
46
|
+
CREATE(p->text);
|
47
|
+
ATTACH(a->content, p);
|
48
|
+
|
49
|
+
while ( size-- ) {
|
50
|
+
if ( (c = *str++) == '\t' ) {
|
51
|
+
/* expand tabs into ->tabstop spaces. We use ->tabstop
|
52
|
+
* because the ENTIRE FREAKING COMPUTER WORLD uses editors
|
53
|
+
* that don't do ^T/^D, but instead use tabs for indentation,
|
54
|
+
* and, of course, set their tabs down to 4 spaces
|
55
|
+
*/
|
56
|
+
do {
|
57
|
+
EXPAND(p->text) = ' ';
|
58
|
+
} while ( ++xp % a->tabstop );
|
59
|
+
}
|
60
|
+
else if ( c >= ' ' ) {
|
61
|
+
EXPAND(p->text) = c;
|
62
|
+
++xp;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
EXPAND(p->text) = 0;
|
66
|
+
S(p->text)--;
|
67
|
+
p->dle = mkd_firstnonblank(p);
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
/* trim leading blanks from a header line
|
72
|
+
*/
|
73
|
+
static void
|
74
|
+
snip(Line *p)
|
75
|
+
{
|
76
|
+
CLIP(p->text, 0, 1);
|
77
|
+
p->dle = mkd_firstnonblank(p);
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
/* build a Document from any old input.
|
82
|
+
*/
|
83
|
+
typedef unsigned int (*getc_func)(void*);
|
84
|
+
|
85
|
+
Document *
|
86
|
+
populate(getc_func getc, void* ctx, int flags)
|
87
|
+
{
|
88
|
+
Cstring line;
|
89
|
+
Document *a = new_Document();
|
90
|
+
int c;
|
91
|
+
#ifdef PANDOC_HEADER
|
92
|
+
int pandoc = 0;
|
93
|
+
#endif
|
94
|
+
|
95
|
+
if ( !a ) return 0;
|
96
|
+
|
97
|
+
a->tabstop = (flags & STD_TABSTOP) ? 4 : TABSTOP;
|
98
|
+
|
99
|
+
CREATE(line);
|
100
|
+
|
101
|
+
while ( (c = (*getc)(ctx)) != EOF ) {
|
102
|
+
if ( c == '\n' ) {
|
103
|
+
#ifdef PANDOC_HEADER
|
104
|
+
if ( pandoc != EOF && pandoc < 3 ) {
|
105
|
+
if ( S(line) && (T(line)[0] == '%') )
|
106
|
+
pandoc++;
|
107
|
+
else
|
108
|
+
pandoc = EOF;
|
109
|
+
}
|
110
|
+
#endif
|
111
|
+
queue(a, &line);
|
112
|
+
S(line) = 0;
|
113
|
+
}
|
114
|
+
else
|
115
|
+
EXPAND(line) = c;
|
116
|
+
}
|
117
|
+
|
118
|
+
if ( S(line) )
|
119
|
+
queue(a, &line);
|
120
|
+
|
121
|
+
DELETE(line);
|
122
|
+
|
123
|
+
#ifdef PANDOC_HEADER
|
124
|
+
if ( (pandoc == 3) && !(flags & NO_HEADER) ) {
|
125
|
+
/* the first three lines started with %, so we have a header.
|
126
|
+
* clip the first three lines out of content and hang them
|
127
|
+
* off header.
|
128
|
+
*/
|
129
|
+
a->headers = T(a->content);
|
130
|
+
T(a->content) = a->headers->next->next->next;
|
131
|
+
a->headers->next->next->next = 0;
|
132
|
+
snip(a->headers);
|
133
|
+
snip(a->headers->next);
|
134
|
+
snip(a->headers->next->next);
|
135
|
+
}
|
136
|
+
#endif
|
137
|
+
|
138
|
+
return a;
|
139
|
+
}
|
140
|
+
|
141
|
+
|
142
|
+
/* convert a file into a linked list
|
143
|
+
*/
|
144
|
+
Document *
|
145
|
+
mkd_in(FILE *f, int flags)
|
146
|
+
{
|
147
|
+
return populate((getc_func)fgetc, f, flags & INPUT_MASK);
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
/* return a single character out of a buffer
|
152
|
+
*/
|
153
|
+
struct string_ctx {
|
154
|
+
char *data; /* the unread data */
|
155
|
+
int size; /* and how much is there? */
|
156
|
+
} ;
|
157
|
+
|
158
|
+
|
159
|
+
static char
|
160
|
+
strget(struct string_ctx *in)
|
161
|
+
{
|
162
|
+
if ( !in->size ) return EOF;
|
163
|
+
|
164
|
+
--(in->size);
|
165
|
+
|
166
|
+
return *(in->data)++;
|
167
|
+
}
|
168
|
+
|
169
|
+
|
170
|
+
/* convert a block of text into a linked list
|
171
|
+
*/
|
172
|
+
Document *
|
173
|
+
mkd_string(char *buf, int len, int flags)
|
174
|
+
{
|
175
|
+
struct string_ctx about;
|
176
|
+
|
177
|
+
about.data = buf;
|
178
|
+
about.size = len;
|
179
|
+
|
180
|
+
return populate((getc_func)strget, &about, flags & INPUT_MASK);
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
/* write the html to a file (xmlified if necessary)
|
185
|
+
*/
|
186
|
+
int
|
187
|
+
mkd_generatehtml(Document *p, FILE *output)
|
188
|
+
{
|
189
|
+
char *doc;
|
190
|
+
int szdoc;
|
191
|
+
|
192
|
+
if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
|
193
|
+
if ( p->ctx->flags & CDATA_OUTPUT )
|
194
|
+
___mkd_xml(doc, szdoc, output);
|
195
|
+
else
|
196
|
+
fwrite(doc, szdoc, 1, output);
|
197
|
+
putc('\n', output);
|
198
|
+
return 0;
|
199
|
+
}
|
200
|
+
return -1;
|
201
|
+
}
|
202
|
+
|
203
|
+
|
204
|
+
/* convert some markdown text to html
|
205
|
+
*/
|
206
|
+
int
|
207
|
+
markdown(Document *document, FILE *out, int flags)
|
208
|
+
{
|
209
|
+
if ( mkd_compile(document, flags) ) {
|
210
|
+
mkd_generatehtml(document, out);
|
211
|
+
mkd_cleanup(document);
|
212
|
+
return 0;
|
213
|
+
}
|
214
|
+
return -1;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
void
|
219
|
+
mkd_basename(Document *document, char *base)
|
220
|
+
{
|
221
|
+
if ( document )
|
222
|
+
document->base = base;
|
223
|
+
}
|
data/ext/mkdio.h
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#ifndef _MKDIO_D
|
2
|
+
#define _MKDIO_D
|
3
|
+
|
4
|
+
#include <stdio.h>
|
5
|
+
|
6
|
+
typedef void MMIOT;
|
7
|
+
|
8
|
+
/* line builder for markdown()
|
9
|
+
*/
|
10
|
+
MMIOT *mkd_in(FILE*,int); /* assemble input from a file */
|
11
|
+
MMIOT *mkd_string(char*,int,int); /* assemble input from a buffer */
|
12
|
+
|
13
|
+
void mkd_basename(MMIOT*,char*);
|
14
|
+
|
15
|
+
/* compilation, debugging, cleanup
|
16
|
+
*/
|
17
|
+
int mkd_compile(MMIOT*, int);
|
18
|
+
int mkd_generatehtml(MMIOT*,FILE*);
|
19
|
+
int mkd_cleanup(MMIOT*);
|
20
|
+
|
21
|
+
/* markup functions
|
22
|
+
*/
|
23
|
+
int mkd_text(char *, int, FILE*, int);
|
24
|
+
int mkd_style(MMIOT*, FILE*);
|
25
|
+
int mkd_dump(MMIOT*, FILE*, int, char*);
|
26
|
+
int markdown(MMIOT*, FILE*, int);
|
27
|
+
void mkd_basename(MMIOT*,char*);
|
28
|
+
|
29
|
+
/* header block access
|
30
|
+
*/
|
31
|
+
char* mkd_doc_title(MMIOT*);
|
32
|
+
char* mkd_doc_author(MMIOT*);
|
33
|
+
char* mkd_doc_date(MMIOT*);
|
34
|
+
|
35
|
+
/* compiled data access
|
36
|
+
*/
|
37
|
+
int mkd_document(MMIOT*, char**);
|
38
|
+
|
39
|
+
/* version#.
|
40
|
+
*/
|
41
|
+
extern char markdown_version[];
|
42
|
+
|
43
|
+
/* special flags for markdown() and mkd_text()
|
44
|
+
*/
|
45
|
+
#define MKD_NOLINKS 0x0001 /* don't do link processing, block <a> tags */
|
46
|
+
#define MKD_NOIMAGE 0x0002 /* don't do image processing, block <img> */
|
47
|
+
#define MKD_NOPANTS 0x0004 /* don't run smartypants() */
|
48
|
+
#define MKD_TAGTEXT 0x0020 /* don't expand `_` and `*` */
|
49
|
+
#define MKD_NO_EXT 0x0040 /* don't allow pseudo-protocols */
|
50
|
+
#define MKD_CDATA 0x0080 /* generate code for xml ![CDATA[...]] */
|
51
|
+
#define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
|
52
|
+
|
53
|
+
/* special flags for mkd_in() and mkd_string()
|
54
|
+
*/
|
55
|
+
#define MKD_NOHEADER 0x0100 /* don't process header blocks */
|
56
|
+
#define MKD_TABSTOP 0x0200 /* expand tabs to 4 spaces */
|
57
|
+
|
58
|
+
#endif/*_MKDIO_D*/
|
data/ext/rbstrio.c
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#if defined(HAVE_FOPENCOOKIE)
|
2
|
+
# define _GNU_SOURCE
|
3
|
+
#endif
|
4
|
+
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include "rbstrio.h"
|
7
|
+
|
8
|
+
#define INCREMENT 1024
|
9
|
+
|
10
|
+
/* called when data is written to the stream. */
|
11
|
+
static int rb_str_io_write(void *cookie, const char *data, int len) {
|
12
|
+
VALUE buf = (VALUE)cookie;
|
13
|
+
rb_str_cat(buf, data, len);
|
14
|
+
return len;
|
15
|
+
}
|
16
|
+
|
17
|
+
/* called when the stream is closed */
|
18
|
+
static int rb_str_io_close(void *cookie) {
|
19
|
+
VALUE buf = (VALUE)cookie;
|
20
|
+
rb_gc_unregister_address(&buf);
|
21
|
+
return 0;
|
22
|
+
}
|
23
|
+
|
24
|
+
#if defined(HAVE_FOPENCOOKIE)
|
25
|
+
cookie_io_functions_t rb_str_io_functions =
|
26
|
+
{
|
27
|
+
(cookie_read_function_t*)NULL,
|
28
|
+
(cookie_write_function_t*)rb_str_io_write,
|
29
|
+
(cookie_seek_function_t*)NULL,
|
30
|
+
(cookie_close_function_t*)rb_str_io_close
|
31
|
+
};
|
32
|
+
#endif
|
33
|
+
|
34
|
+
/* create a stream backed by a Ruby string. */
|
35
|
+
FILE *rb_str_io_new(VALUE buf) {
|
36
|
+
FILE *rv;
|
37
|
+
Check_Type(buf, T_STRING);
|
38
|
+
#if defined(HAVE_FOPENCOOKIE)
|
39
|
+
rv = fopencookie((void*)buf, "w", rb_str_io_functions);
|
40
|
+
#else
|
41
|
+
rv = funopen((void*)buf, NULL, rb_str_io_write, NULL, rb_str_io_close);
|
42
|
+
#endif
|
43
|
+
/* TODO if (rv == NULL) */
|
44
|
+
rb_gc_register_address(&buf);
|
45
|
+
return rv;
|
46
|
+
}
|
47
|
+
|
48
|
+
/* vim: set ts=4 sw=4: */
|