summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Anderson <andersrb@gmail.com>2015-09-21 12:41:18 -0700
committerBrian Anderson <andersrb@gmail.com>2015-09-21 12:41:18 -0700
commit49c64b3cb5999cad0118fa5cb0cc60f5fcc70493 (patch)
treedd136dcea4700b26582bcd950ccacf1f19c2dd86
parent794d91b37020242446c53713c7ce0c09b33b5f0c (diff)
parentca4609d56a77378ef9cf6dd3caff3aec006ecce9 (diff)
downloadrust-hoedown-master.tar.gz
Merge pull request #4 from hoedown/masterHEADmaster
Upgrade hoedown to 3.0.5.
-rw-r--r--.gitignore1
-rw-r--r--LICENSE2
-rw-r--r--Makefile41
-rw-r--r--Makefile.win5
-rw-r--r--bin/common.h77
-rw-r--r--bin/hoedown.c579
-rw-r--r--bin/smartypants.c315
-rw-r--r--hoedown.def24
-rw-r--r--src/autolink.c18
-rw-r--r--src/autolink.h37
-rw-r--r--src/buffer.c205
-rw-r--r--src/buffer.h85
-rw-r--r--src/document.c398
-rw-r--r--src/document.h154
-rw-r--r--src/escape.c143
-rw-r--r--src/escape.h13
-rw-r--r--src/html.c346
-rw-r--r--src/html.h59
-rw-r--r--src/html_smartypants.c12
-rw-r--r--src/stack.c66
-rw-r--r--src/stack.h37
-rw-r--r--src/version.c8
-rw-r--r--src/version.h21
-rw-r--r--test/Tests/Escape character.html (renamed from test/MarkdownTest_1.0.3/Tests/Escape character.html)0
-rw-r--r--test/Tests/Escape character.text (renamed from test/MarkdownTest_1.0.3/Tests/Escape character.text)0
-rw-r--r--test/Tests/Formatting in Table of Contents.html15
-rw-r--r--test/Tests/Formatting in Table of Contents.text5
-rw-r--r--test/Tests/Math.html31
-rw-r--r--test/Tests/Math.text31
-rw-r--r--test/Tests/Table.html66
-rw-r--r--test/Tests/Table.text21
-rw-r--r--test/Tests/Underline.html1
-rw-r--r--test/Tests/Underline.text1
-rw-r--r--test/config.json116
-rwxr-xr-xtest/runner.py107
-rwxr-xr-xtest/runner.sh50
36 files changed, 1870 insertions, 1220 deletions
diff --git a/.gitignore b/.gitignore
index e885c6c..b8a9371 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ hoedown.exp
hoedown.lib
smartypants
libhoedown.so*
+libhoedown.a
diff --git a/LICENSE b/LICENSE
index 72ceb93..4e75de4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
Copyright (c) 2008, Natacha Porté
Copyright (c) 2011, Vicent Martí
-Copyright (c) 2013, Devin Torres and the Hoedown authors
+Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index 998a41d..75c3e26 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,12 @@
-CFLAGS = -g -O3 -Wall -Wextra -Wno-unused-parameter -Isrc
+CFLAGS = -g -O3 -ansi -pedantic -Wall -Wextra -Wno-unused-parameter
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+INCLUDEDIR = $(PREFIX)/include
+HOEDOWN_CFLAGS = $(CFLAGS) -Isrc
ifneq ($(OS),Windows_NT)
- CFLAGS += -fPIC
+ HOEDOWN_CFLAGS += -fPIC
endif
HOEDOWN_SRC=\
@@ -17,15 +22,15 @@ HOEDOWN_SRC=\
.PHONY: all test test-pl clean
-all: libhoedown.so hoedown smartypants
+all: libhoedown.so libhoedown.a hoedown smartypants
# Libraries
-libhoedown.so: libhoedown.so.1
+libhoedown.so: libhoedown.so.3
ln -f -s $^ $@
-libhoedown.so.1: $(HOEDOWN_SRC)
- $(CC) -shared $^ $(LDFLAGS) -o $@
+libhoedown.so.3: $(HOEDOWN_SRC)
+ $(CC) -Wl,-soname,$(@F) -shared $^ $(LDFLAGS) -o $@
libhoedown.a: $(HOEDOWN_SRC)
$(AR) rcs libhoedown.a $^
@@ -46,7 +51,7 @@ src/html_blocks.c: html_block_names.gperf
# Testing
test: hoedown
- test/runner.sh ./hoedown test/MarkdownTest_1.0.3/Tests
+ python test/runner.py
test-pl: hoedown
perl test/MarkdownTest_1.0.3/MarkdownTest.pl \
@@ -59,7 +64,27 @@ clean:
$(RM) libhoedown.so libhoedown.so.1 libhoedown.a
$(RM) hoedown smartypants hoedown.exe smartypants.exe
+# Installing
+
+install:
+ install -m755 -d $(DESTDIR)$(LIBDIR)
+ install -m755 -d $(DESTDIR)$(BINDIR)
+ install -m755 -d $(DESTDIR)$(INCLUDEDIR)
+
+ install -m644 libhoedown.a $(DESTDIR)$(LIBDIR)
+ install -m755 libhoedown.so.3 $(DESTDIR)$(LIBDIR)
+ ln -f -s libhoedown.so.3 $(DESTDIR)$(LIBDIR)/libhoedown.so
+
+ install -m755 hoedown $(DESTDIR)$(BINDIR)
+ install -m755 smartypants $(DESTDIR)$(BINDIR)
+
+ install -m755 -d $(DESTDIR)$(INCLUDEDIR)/hoedown
+ install -m644 src/*.h $(DESTDIR)$(INCLUDEDIR)/hoedown
+
# Generic object compilations
%.o: %.c
- $(CC) $(CFLAGS) -c -o $@ $<
+ $(CC) $(HOEDOWN_CFLAGS) -c -o $@ $<
+
+src/html_blocks.o: src/html_blocks.c
+ $(CC) $(HOEDOWN_CFLAGS) -Wno-static-in-inline -c -o $@ $<
diff --git a/Makefile.win b/Makefile.win
index cda24b9..ce5ffa2 100644
--- a/Makefile.win
+++ b/Makefile.win
@@ -34,3 +34,8 @@ clean:
.c.obj:
$(CC) $(CFLAGS) /c $< /Fo$@
+
+# Testing
+
+test: hoedown.exe
+ python test\runner.py
diff --git a/bin/common.h b/bin/common.h
index 9074bd6..e4d2eda 100644
--- a/bin/common.h
+++ b/bin/common.h
@@ -11,7 +11,8 @@
#define count_of(arr) (sizeof(arr)/sizeof(0[arr]))
int
-parseint(const char *string, long *result) {
+parseint(const char *string, long *result)
+{
char *end;
errno = 0;
*result = strtol(string, &end, 10);
@@ -19,7 +20,8 @@ parseint(const char *string, long *result) {
}
const char *
-strprefix(const char *str, const char *prefix) {
+strprefix(const char *str, const char *prefix)
+{
while (*prefix) {
if (!(*str && *str == *prefix)) return 0;
prefix++; str++;
@@ -28,7 +30,8 @@ strprefix(const char *str, const char *prefix) {
}
void
-print_option(char short_opt, const char *long_opt, const char *description) {
+print_option(char short_opt, const char *long_opt, const char *description)
+{
if (short_opt)
printf(" -%c, ", short_opt);
else
@@ -38,8 +41,68 @@ print_option(char short_opt, const char *long_opt, const char *description) {
}
void
-print_version() {
- int major, minor, revision;
- hoedown_version(&major, &minor, &revision);
- printf("Built with Hoedown v%d.%d.%d.\n", major, minor, revision);
+print_version()
+{
+ printf("Built with Hoedown " HOEDOWN_VERSION ".\n");
+}
+
+int
+parse_options(
+ int argc, char **argv,
+ int(*parse_short_option)(char opt, char *next, void *opaque),
+ int(*parse_long_option)(char *opt, char *next, void *opaque),
+ int(*parse_argument)(int argn, char *arg, int is_forced, void *opaque),
+ void *opaque)
+{
+ int result;
+ int i = 1, regular_args = 0;
+
+ /* Parse options mixed with arguments */
+ while (i < argc) {
+ char *arg = argv[i];
+
+ if (arg[0] == '-' && arg[1]) {
+ char *next_arg = (i+1 < argc) ? argv[i+1] : NULL;
+
+ if (arg[1] == '-' && !arg[2]) {
+ /* '--' signals end of options */
+ i++;
+ break;
+ }
+
+ if (arg[1] == '-') {
+ /* Long option */
+ result = parse_long_option(arg + 2, next_arg, opaque);
+ if (!result) return 0;
+ i += result;
+ } else {
+ /* Sequence of short options */
+ size_t pos;
+ for (pos = 1; arg[pos]; pos++) {
+ char *next = (arg[pos+1]) ? arg + pos+1 : next_arg;
+ result = parse_short_option(arg[pos], next, opaque);
+ if (!result) return 0;
+ if (result == 2) {
+ if (next == next_arg) i++;
+ break;
+ }
+ }
+ i++;
+ }
+ } else {
+ /* Argument */
+ result = parse_argument(regular_args++, arg, 0, opaque);
+ if (!result) return 0;
+ i++;
+ }
+ }
+
+ /* Parse rest as forced arguments */
+ while (i < argc) {
+ result = parse_argument(regular_args++, argv[i], 1, opaque);
+ if (!result) return 0;
+ i++;
+ }
+
+ return 1;
}
diff --git a/bin/hoedown.c b/bin/hoedown.c
index 79ba426..175471a 100644
--- a/bin/hoedown.c
+++ b/bin/hoedown.c
@@ -2,32 +2,16 @@
#include "html.h"
#include "common.h"
-//#include <time.h>
+#include <time.h>
-/* NULL RENDERER */
+
+/* FEATURES INFO / DEFAULTS */
enum renderer_type {
RENDERER_HTML,
- RENDERER_HTML_TOC,
- RENDERER_NULL,
+ RENDERER_HTML_TOC
};
-hoedown_renderer *
-null_renderer_new() {
- hoedown_renderer *rend = malloc(sizeof(hoedown_renderer));
- if (rend)
- memset(rend, 0x00, sizeof(hoedown_renderer));
- return rend;
-}
-
-void
-null_renderer_free(hoedown_renderer *rend) {
- free(rend);
-}
-
-
-/* FEATURES INFO / DEFAULTS */
-
struct extension_category_info {
unsigned int flags;
const char *option_name;
@@ -66,7 +50,6 @@ static struct extension_info extensions_info[] = {
{HOEDOWN_EXT_SUPERSCRIPT, "superscript", "Parse super^script."},
{HOEDOWN_EXT_MATH, "math", "Parse TeX $$math$$ syntax, Kramdown style."},
- {HOEDOWN_EXT_LAX_SPACING, "lax-spacing", "Don't require a blank line between some blocks."},
{HOEDOWN_EXT_NO_INTRA_EMPHASIS, "disable-intra-emphasis", "Disable emphasis_between_words."},
{HOEDOWN_EXT_SPACE_HEADERS, "space-headers", "Require a space after '#' in headers."},
{HOEDOWN_EXT_MATH_EXPLICIT, "math-explicit", "Instead of guessing by context, parse $inline math$ and $$always block math$$ (requires --math)."},
@@ -77,7 +60,6 @@ static struct extension_info extensions_info[] = {
static struct html_flag_info html_flags_info[] = {
{HOEDOWN_HTML_SKIP_HTML, "skip-html", "Strip all HTML tags."},
{HOEDOWN_HTML_ESCAPE, "escape", "Escape all HTML."},
- {HOEDOWN_HTML_SAFELINK, "safelink", "Only allow links to safe protocols."},
{HOEDOWN_HTML_HARD_WRAP, "hard-wrap", "Render each linebreak as <br>."},
{HOEDOWN_HTML_USE_XHTML, "xhtml", "Render XHTML."},
};
@@ -93,7 +75,11 @@ static const char *negative_prefix = "no-";
/* PRINT HELP */
void
-print_help(const char *basename) {
+print_help(const char *basename)
+{
+ size_t i;
+ size_t e;
+
/* usage */
printf("Usage: %s [OPTION]... [FILE]\n\n", basename);
@@ -107,7 +93,6 @@ print_help(const char *basename) {
print_option('t', "toc-level=N", "Maximum level for headers included in the TOC. Zero disables TOC (the default).");
print_option( 0, "html", "Render (X)HTML. The default.");
print_option( 0, "html-toc", "Render the Table of Contents in (X)HTML.");
- print_option( 0, "null", "Use a special \"null\" renderer that has no callbacks.");
print_option('T', "time", "Show time spent in rendering.");
print_option('i', "input-unit=N", "Reading block size. Default is " str(DEF_IUNIT) ".");
print_option('o', "output-unit=N", "Writing block size. Default is " str(DEF_OUNIT) ".");
@@ -116,8 +101,6 @@ print_help(const char *basename) {
printf("\n");
/* extensions */
- size_t i;
- size_t e;
for (i = 0; i < count_of(categories_info); i++) {
struct extension_category_info *category = categories_info+i;
printf("%s (--%s%s):\n", category->label, category_prefix, category->option_name);
@@ -143,358 +126,334 @@ print_help(const char *basename) {
"Options are processed in order, so in case of contradictory options the last specified stands.\n\n");
printf("When FILE is '-', read standard input. If no FILE was given, read standard input. Use '--' to signal end of option parsing. "
- "Exit status is 0 if no errors occured, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
+ "Exit status is 0 if no errors occurred, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
}
-/* MAIN LOGIC */
+/* OPTION PARSING */
-int
-main(int argc, char **argv)
-{
- int show_time = 0;
- //struct timespec start, end;
+struct option_data {
+ char *basename;
+ int done;
- /* buffers */
- hoedown_buffer *ib, *ob;
- size_t iunit = DEF_IUNIT, ounit = DEF_OUNIT;
+ /* time reporting */
+ int show_time;
- /* files */
- FILE *in = NULL;
+ /* I/O */
+ size_t iunit;
+ size_t ounit;
+ const char *filename;
/* renderer */
- int toc_level = 0;
- int renderer_type = RENDERER_HTML;
+ enum renderer_type renderer;
+ int toc_level;
+ hoedown_html_flags html_flags;
- /* document */
- hoedown_document *document;
- unsigned int extensions = 0;
- size_t max_nesting = DEF_MAX_NESTING;
-
- /* HTML renderer-specific */
- unsigned int html_flags = 0;
-
-
- /* option parsing */
- int just_args = 0;
- int i, j;
- for (i = 1; i < argc; i++) {
- char *arg = argv[i];
- if (!arg[0]) continue;
-
- if (just_args || arg[0] != '-') {
- /* regular argument */
- in = fopen(arg, "r");
- if (!in) {
- fprintf(stderr, "Unable to open input file \"%s\": %s\n", arg, strerror(errno));
- return 5;
- }
- continue;
- }
+ /* parsing */
+ hoedown_extensions extensions;
+ size_t max_nesting;
+};
- if (!arg[1]) {
- /* arg is "-" */
- in = stdin;
- continue;
- }
+int
+parse_short_option(char opt, char *next, void *opaque)
+{
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (opt == 'h') {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- if (arg[1] != '-') {
- /* parse short options */
- char opt;
- const char *val;
- for (j = 1; (opt = arg[j]); j++) {
- if (opt == 'h') {
- print_help(argv[0]);
- return 1;
- }
-
- if (opt == 'v') {
- print_version();
- return 1;
- }
-
- if (opt == 'T') {
- show_time = 1;
- continue;
- }
-
- /* options requiring value */
- if (arg[++j]) val = arg+j;
- else if (argv[++i]) val = argv[i];
- else {
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
-
- long int num;
- int isNum = parseint(val, &num);
-
- if (opt == 'n' && isNum) {
- max_nesting = num;
- break;
- }
-
- if (opt == 't' && isNum) {
- toc_level = num;
- break;
- }
-
- if (opt == 'i' && isNum) {
- iunit = num;
- break;
- }
-
- if (opt == 'o' && isNum) {
- ounit = num;
- break;
- }
-
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
- continue;
- }
+ if (opt == 'v') {
+ print_version();
+ data->done = 1;
+ return 0;
+ }
- if (!arg[2]) {
- /* arg is "--" */
- just_args = 1;
- continue;
- }
+ if (opt == 'T') {
+ data->show_time = 1;
+ return 1;
+ }
- /* parse long option */
- char opt [100];
- strncpy(opt, arg+2, 100);
- opt[99] = 0;
+ /* options requiring value */
+ /* FIXME: add validation */
- char *val = strchr(opt, '=');
+ if (opt == 'n' && isNum) {
+ data->max_nesting = num;
+ return 2;
+ }
- long int num = 0;
- int isNum = 0;
+ if (opt == 't' && isNum) {
+ data->toc_level = num;
+ return 2;
+ }
- if (val) {
- *val = 0;
- val++;
+ if (opt == 'i' && isNum) {
+ data->iunit = num;
+ return 2;
+ }
- if (*val)
- isNum = parseint(val, &num);
- }
+ if (opt == 'o' && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- int opt_parsed = 0;
+ fprintf(stderr, "Wrong option '-%c' found.\n", opt);
+ return 0;
+}
- if (strcmp(opt, "help")==0) {
- print_help(argv[0]);
- return 1;
- }
+int
+parse_category_option(char *opt, struct option_data *data)
+{
+ size_t i;
+ const char *name = strprefix(opt, category_prefix);
+ if (!name) return 0;
- if (strcmp(opt, "version")==0) {
- print_version();
+ for (i = 0; i < count_of(categories_info); i++) {
+ struct extension_category_info *category = &categories_info[i];
+ if (strcmp(name, category->option_name)==0) {
+ data->extensions |= category->flags;
return 1;
}
+ }
- if (strcmp(opt, "max-nesting")==0 && isNum) {
- opt_parsed = 1;
- max_nesting = num;
- }
- if (strcmp(opt, "toc-level")==0 && isNum) {
- opt_parsed = 1;
- toc_level = num;
- }
- if (strcmp(opt, "input-unit")==0 && isNum) {
- opt_parsed = 1;
- iunit = num;
- }
- if (strcmp(opt, "output-unit")==0 && isNum) {
- opt_parsed = 1;
- ounit = num;
- }
+ return 0;
+}
- if (strcmp(opt, "html")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_HTML;
- }
- if (strcmp(opt, "html-toc")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_HTML_TOC;
- }
- if (strcmp(opt, "null")==0) {
- opt_parsed = 1;
- renderer_type = RENDERER_NULL;
- }
+int
+parse_flag_option(char *opt, struct option_data *data)
+{
+ size_t i;
- const char *name;
- size_t i;
-
- /* extension categories */
- if ((name = strprefix(opt, category_prefix))) {
- for (i = 0; i < count_of(categories_info); i++) {
- struct extension_category_info *category = categories_info+i;
- if (strcmp(name, category->option_name)==0) {
- opt_parsed = 1;
- extensions |= category->flags;
- break;
- }
- }
+ for (i = 0; i < count_of(extensions_info); i++) {
+ struct extension_info *extension = &extensions_info[i];
+ if (strcmp(opt, extension->option_name)==0) {
+ data->extensions |= extension->flag;
+ return 1;
}
+ }
- /* extensions */
- for (i = 0; i < count_of(extensions_info); i++) {
- struct extension_info *extension = extensions_info+i;
- if (strcmp(opt, extension->option_name)==0) {
- opt_parsed = 1;
- extensions |= extension->flag;
- break;
- }
+ for (i = 0; i < count_of(html_flags_info); i++) {
+ struct html_flag_info *html_flag = &html_flags_info[i];
+ if (strcmp(opt, html_flag->option_name)==0) {
+ data->html_flags |= html_flag->flag;
+ return 1;
}
+ }
- /* html flags */
- for (i = 0; i < count_of(html_flags_info); i++) {
- struct html_flag_info *html_flag = html_flags_info+i;
- if (strcmp(opt, html_flag->option_name)==0) {
- opt_parsed = 1;
- html_flags |= html_flag->flag;
- break;
- }
- }
+ return 0;
+}
- /* negations */
- if ((name = strprefix(opt, negative_prefix))) {
- for (i = 0; i < count_of(categories_info); i++) {
- struct extension_category_info *category = categories_info+i;
- if (strcmp(name, category->option_name)==0) {
- opt_parsed = 1;
- extensions &= ~(category->flags);
- break;
- }
- }
- for (i = 0; i < count_of(extensions_info); i++) {
- struct extension_info *extension = extensions_info+i;
- if (strcmp(name, extension->option_name)==0) {
- opt_parsed = 1;
- extensions &= ~(extension->flag);
- break;
- }
- }
- for (i = 0; i < count_of(html_flags_info); i++) {
- struct html_flag_info *html_flag = html_flags_info+i;
- if (strcmp(name, html_flag->option_name)==0) {
- opt_parsed = 1;
- html_flags &= ~(html_flag->flag);
- break;
- }
- }
+int
+parse_negative_option(char *opt, struct option_data *data)
+{
+ size_t i;
+ const char *name = strprefix(opt, negative_prefix);
+ if (!name) return 0;
+
+ for (i = 0; i < count_of(categories_info); i++) {
+ struct extension_category_info *category = &categories_info[i];
+ if (strcmp(name, category->option_name)==0) {
+ data->extensions &= ~(category->flags);
+ return 1;
}
+ }
- if (strcmp(opt, "time")==0) {
- opt_parsed = 1;
- show_time = 1;
+ for (i = 0; i < count_of(extensions_info); i++) {
+ struct extension_info *extension = &extensions_info[i];
+ if (strcmp(name, extension->option_name)==0) {
+ data->extensions &= ~(extension->flag);
+ return 1;
}
+ }
- if (!opt_parsed) {
- fprintf(stderr, "Wrong option '%s' found.\n", arg);
+ for (i = 0; i < count_of(html_flags_info); i++) {
+ struct html_flag_info *html_flag = &html_flags_info[i];
+ if (strcmp(name, html_flag->option_name)==0) {
+ data->html_flags &= ~(html_flag->flag);
return 1;
}
}
- if (!in)
- in = stdin;
+ return 0;
+}
+int
+parse_long_option(char *opt, char *next, void *opaque)
+{
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (strcmp(opt, "help")==0) {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- /* reading everything */
- ib = hoedown_buffer_new(iunit);
- if (!ib) {
- fprintf(stderr, "Couldn't allocate input buffer.\n");
- return 4;
+ if (strcmp(opt, "version")==0) {
+ print_version();
+ data->done = 1;
+ return 0;
}
- while (!feof(in)) {
- if (ferror(in)) {
- fprintf(stderr, "I/O errors found while reading input.\n");
- return 5;
- }
- if (hoedown_buffer_grow(ib, ib->size + iunit) != HOEDOWN_BUF_OK) {
- fprintf(stderr, "Couldn't grow input buffer.\n");
- return 4;
- }
- ib->size += fread(ib->data + ib->size, 1, iunit, in);
+ if (strcmp(opt, "time")==0) {
+ data->show_time = 1;
+ return 1;
}
- if (in != stdin)
- fclose(in);
+ /* FIXME: validation */
+ if (strcmp(opt, "max-nesting")==0 && isNum) {
+ data->max_nesting = num;
+ return 2;
+ }
+ if (strcmp(opt, "toc-level")==0 && isNum) {
+ data->toc_level = num;
+ return 2;
+ }
+ if (strcmp(opt, "input-unit")==0 && isNum) {
+ data->iunit = num;
+ return 2;
+ }
+ if (strcmp(opt, "output-unit")==0 && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- /* creating the renderer */
- hoedown_renderer *renderer;
- void (*renderer_free)(hoedown_renderer*);
+ if (strcmp(opt, "html")==0) {
+ data->renderer = RENDERER_HTML;
+ return 1;
+ }
+ if (strcmp(opt, "html-toc")==0) {
+ data->renderer = RENDERER_HTML_TOC;
+ return 1;
+ }
- switch (renderer_type) {
- case RENDERER_HTML:
- renderer = hoedown_html_renderer_new(html_flags, toc_level);
- renderer_free = hoedown_html_renderer_free;
- break;
- case RENDERER_HTML_TOC:
- renderer = hoedown_html_toc_renderer_new(toc_level);
- renderer_free = hoedown_html_renderer_free;
- break;
- case RENDERER_NULL:
- renderer = null_renderer_new();
- renderer_free = null_renderer_free;
- break;
- default:
- renderer = NULL;
- renderer_free = NULL;
- };
+ if (parse_category_option(opt, data) || parse_flag_option(opt, data) || parse_negative_option(opt, data))
+ return 1;
- if (!renderer) {
- fprintf(stderr, "Couldn't allocate renderer.\n");
- return 4;
- }
+ fprintf(stderr, "Wrong option '--%s' found.\n", opt);
+ return 0;
+}
+int
+parse_argument(int argn, char *arg, int is_forced, void *opaque)
+{
+ struct option_data *data = opaque;
- /* performing markdown rendering */
- ob = hoedown_buffer_new(ounit);
- if (!ob) {
- fprintf(stderr, "Couldn't allocate output buffer.\n");
- return 4;
+ if (argn == 0) {
+ /* Input file */
+ if (strcmp(arg, "-")!=0 || is_forced) data->filename = arg;
+ return 1;
}
- document = hoedown_document_new(renderer, extensions, max_nesting);
- if (!document) {
- fprintf(stderr, "Couldn't allocate document parser.\n");
- return 4;
- }
+ fprintf(stderr, "Too many arguments.\n");
+ return 0;
+}
- //clock_gettime(CLOCK_MONOTONIC, &start);
- hoedown_document_render(document, ob, ib->data, ib->size);
- //clock_gettime(CLOCK_MONOTONIC, &end);
+/* MAIN LOGIC */
- /* writing the result to stdout */
- (void)fwrite(ob->data, 1, ob->size, stdout);
+int
+main(int argc, char **argv)
+{
+ struct option_data data;
+ clock_t t1, t2;
+ FILE *file = stdin;
+ hoedown_buffer *ib, *ob;
+ hoedown_renderer *renderer = NULL;
+ void (*renderer_free)(hoedown_renderer *) = NULL;
+ hoedown_document *document;
+ /* Parse options */
+ data.basename = argv[0];
+ data.done = 0;
+ data.show_time = 0;
+ data.iunit = DEF_IUNIT;
+ data.ounit = DEF_OUNIT;
+ data.filename = NULL;
+ data.renderer = RENDERER_HTML;
+ data.toc_level = 0;
+ data.html_flags = 0;
+ data.extensions = 0;
+ data.max_nesting = DEF_MAX_NESTING;
+
+ argc = parse_options(argc, argv, parse_short_option, parse_long_option, parse_argument, &data);
+ if (data.done) return 0;
+ if (!argc) return 1;
+
+ /* Open input file, if needed */
+ if (data.filename) {
+ file = fopen(data.filename, "r");
+ if (!file) {
+ fprintf(stderr, "Unable to open input file \"%s\": %s\n", data.filename, strerror(errno));
+ return 5;
+ }
+ }
- /* showing rendering time */
- if (show_time) {
- //TODO: enable this
- //long long elapsed = ( end.tv_sec*1000000000 + end.tv_nsec)
- // - (start.tv_sec*1000000000 + start.tv_nsec);
- //if (elapsed < 1000000000)
- // fprintf(stderr, "Time spent on rendering: %.2f ms.\n", ((double)elapsed)/1000000);
- //else
- // fprintf(stderr, "Time spent on rendering: %.3f s.\n", ((double)elapsed)/1000000000);
+ /* Read everything */
+ ib = hoedown_buffer_new(data.iunit);
+
+ if (hoedown_buffer_putf(ib, file)) {
+ fprintf(stderr, "I/O errors found while reading input.\n");
+ return 5;
}
+ if (file != stdin) fclose(file);
- /* cleanup */
- hoedown_buffer_free(ib);
- hoedown_buffer_free(ob);
+ /* Create the renderer */
+ switch (data.renderer) {
+ case RENDERER_HTML:
+ renderer = hoedown_html_renderer_new(data.html_flags, data.toc_level);
+ renderer_free = hoedown_html_renderer_free;
+ break;
+ case RENDERER_HTML_TOC:
+ renderer = hoedown_html_toc_renderer_new(data.toc_level);
+ renderer_free = hoedown_html_renderer_free;
+ break;
+ };
+ /* Perform Markdown rendering */
+ ob = hoedown_buffer_new(data.ounit);
+ document = hoedown_document_new(renderer, data.extensions, data.max_nesting);
+
+ t1 = clock();
+ hoedown_document_render(document, ob, ib->data, ib->size);
+ t2 = clock();
+
+ /* Cleanup */
+ hoedown_buffer_free(ib);
hoedown_document_free(document);
renderer_free(renderer);
+ /* Write the result to stdout */
+ (void)fwrite(ob->data, 1, ob->size, stdout);
+ hoedown_buffer_free(ob);
+
if (ferror(stdout)) {
fprintf(stderr, "I/O errors found while writing output.\n");
return 5;
}
+ /* Show rendering time */
+ if (data.show_time) {
+ double elapsed;
+
+ if (t1 == ((clock_t) -1) || t2 == ((clock_t) -1)) {
+ fprintf(stderr, "Failed to get the time.\n");
+ return 1;
+ }
+
+ elapsed = (double)(t2 - t1) / CLOCKS_PER_SEC;
+ if (elapsed < 1)
+ fprintf(stderr, "Time spent on rendering: %7.2f ms.\n", elapsed*1e3);
+ else
+ fprintf(stderr, "Time spent on rendering: %6.3f s.\n", elapsed);
+ }
+
return 0;
}
diff --git a/bin/smartypants.c b/bin/smartypants.c
index 5550157..de0512e 100644
--- a/bin/smartypants.c
+++ b/bin/smartypants.c
@@ -1,17 +1,20 @@
#include "html.h"
#include "common.h"
-//#include <time.h>
+/*#include <time.h>*/
+
+
+/* FEATURES INFO / DEFAULTS */
#define DEF_IUNIT 1024
#define DEF_OUNIT 64
-#define DEF_MAX_NESTING 16
/* PRINT HELP */
void
-print_help(const char *basename) {
+print_help(const char *basename)
+{
/* usage */
printf("Usage: %s [OPTION]... [FILE]\n\n", basename);
@@ -31,208 +34,188 @@ print_help(const char *basename) {
printf("Options are processed in order, so in case of contradictory options the last specified stands.\n\n");
printf("When FILE is '-', read standard input. If no FILE was given, read standard input. Use '--' to signal end of option parsing. "
- "Exit status is 0 if no errors occured, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
+ "Exit status is 0 if no errors occurred, 1 with option parsing errors, 4 with memory allocation errors or 5 with I/O errors.\n\n");
}
-/* MAIN LOGIC */
+/* OPTION PARSING */
+
+struct option_data {
+ char *basename;
+ int done;
+
+ /* time reporting */
+ int show_time;
+
+ /* I/O */
+ size_t iunit;
+ size_t ounit;
+ const char *filename;
+};
int
-main(int argc, char **argv)
+parse_short_option(char opt, char *next, void *opaque)
{
- int show_time = 0;
- //struct timespec start, end;
-
- /* buffers */
- hoedown_buffer *ib, *ob;
- size_t iunit = DEF_IUNIT, ounit = DEF_OUNIT;
-
- /* files */
- FILE *in = NULL;
-
-
- /* option parsing */
- int just_args = 0;
- int i, j;
- for (i = 1; i < argc; i++) {
- char *arg = argv[i];
- if (!arg[0]) continue;
-
- if (just_args || arg[0] != '-') {
- /* regular argument */
- in = fopen(arg, "r");
- if (!in) {
- fprintf(stderr, "Unable to open input file \"%s\": %s\n", arg, strerror(errno));
- return 5;
- }
- continue;
- }
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (opt == 'h') {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- if (!arg[1]) {
- /* arg is "-" */
- in = stdin;
- continue;
- }
+ if (opt == 'v') {
+ print_version();
+ data->done = 1;
+ return 0;
+ }
- if (arg[1] != '-') {
- /* parse short options */
- char opt;
- const char *val;
- for (j = 1; (opt = arg[j]); j++) {
- if (opt == 'h') {
- print_help(argv[0]);
- return 1;
- }
-
- if (opt == 'v') {
- print_version();
- return 1;
- }
-
- if (opt == 'T') {
- show_time = 1;
- continue;
- }
-
- /* options requiring value */
- if (arg[++j]) val = arg+j;
- else if (argv[++i]) val = argv[i];
- else {
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
-
- long int num;
- int isNum = parseint(val, &num);
-
- if (opt == 'i' && isNum) {
- iunit = num;
- break;
- }
-
- if (opt == 'o' && isNum) {
- ounit = num;
- break;
- }
-
- fprintf(stderr, "Wrong option '-%c' found.\n", opt);
- return 1;
- }
- continue;
- }
+ if (opt == 'T') {
+ data->show_time = 1;
+ return 1;
+ }
- if (!arg[2]) {
- /* arg is "--" */
- just_args = 1;
- continue;
- }
+ /* options requiring value */
+ /* FIXME: add validation */
- /* parse long option */
- char opt [100];
- strncpy(opt, arg+2, 100);
- opt[99] = 0;
+ if (opt == 'i' && isNum) {
+ data->iunit = num;
+ return 2;
+ }
- char *val = strchr(opt, '=');
+ if (opt == 'o' && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- long int num = 0;
- int isNum = 0;
+ fprintf(stderr, "Wrong option '-%c' found.\n", opt);
+ return 0;
+}
- if (val) {
- *val = 0;
- val++;
+int
+parse_long_option(char *opt, char *next, void *opaque)
+{
+ struct option_data *data = opaque;
+ long int num;
+ int isNum = next ? parseint(next, &num) : 0;
+
+ if (strcmp(opt, "help")==0) {
+ print_help(data->basename);
+ data->done = 1;
+ return 0;
+ }
- if (*val)
- isNum = parseint(val, &num);
- }
+ if (strcmp(opt, "version")==0) {
+ print_version();
+ data->done = 1;
+ return 0;
+ }
- int opt_parsed = 0;
+ if (strcmp(opt, "time")==0) {
+ data->show_time = 1;
+ return 1;
+ }
- if (strcmp(opt, "help")==0) {
- print_help(argv[0]);
- return 1;
- }
+ /* FIXME: validation */
- if (strcmp(opt, "version")==0) {
- print_version();
- return 1;
- }
+ if (strcmp(opt, "input-unit")==0 && isNum) {
+ data->iunit = num;
+ return 2;
+ }
+ if (strcmp(opt, "output-unit")==0 && isNum) {
+ data->ounit = num;
+ return 2;
+ }
- if (strcmp(opt, "input-unit")==0 && isNum) {
- opt_parsed = 1;
- iunit = num;
- }
- if (strcmp(opt, "output-unit")==0 && isNum) {
- opt_parsed = 1;
- ounit = num;
- }
+ fprintf(stderr, "Wrong option '--%s' found.\n", opt);
+ return 0;
+}
- if (strcmp(opt, "time")==0) {
- opt_parsed = 1;
- show_time = 1;
- }
+int
+parse_argument(int argn, char *arg, int is_forced, void *opaque)
+{
+ struct option_data *data = opaque;
- if (!opt_parsed) {
- fprintf(stderr, "Wrong option '%s' found.\n", arg);
- return 1;
- }
+ if (argn == 0) {
+ /* Input file */
+ if (strcmp(arg, "-")!=0 || is_forced) data->filename = arg;
+ return 1;
}
- if (!in)
- in = stdin;
+ fprintf(stderr, "Too many arguments.\n");
+ return 0;
+}
+
+
+/* MAIN LOGIC */
+int
+main(int argc, char **argv)
+{
+ struct option_data data;
+ /*struct timespec start, end;*/
+ FILE *file = stdin;
+ hoedown_buffer *ib, *ob;
- /* reading everything */
- ib = hoedown_buffer_new(iunit);
- if (!ib) {
- fprintf(stderr, "Couldn't allocate input buffer.\n");
- return 4;
+ /* Parse options */
+ data.basename = argv[0];
+ data.done = 0;
+ data.show_time = 0;
+ data.iunit = DEF_IUNIT;
+ data.ounit = DEF_OUNIT;
+ data.filename = NULL;
+
+ argc = parse_options(argc, argv, parse_short_option, parse_long_option, parse_argument, &data);
+ if (data.done) return 0;
+ if (!argc) return 1;
+
+ /* Open input file, if needed */
+ if (data.filename) {
+ file = fopen(data.filename, "r");
+ if (!file) {
+ fprintf(stderr, "Unable to open input file \"%s\": %s\n", data.filename, strerror(errno));
+ return 5;
+ }
}
- while (!feof(in)) {
- if (ferror(in)) {
+ /* Read everything */
+ ib = hoedown_buffer_new(data.iunit);
+
+ while (!feof(file)) {
+ if (ferror(file)) {
fprintf(stderr, "I/O errors found while reading input.\n");
return 5;
}
- if (hoedown_buffer_grow(ib, ib->size + iunit) != HOEDOWN_BUF_OK) {
- fprintf(stderr, "Couldn't grow input buffer.\n");
- return 4;
- }
- ib->size += fread(ib->data + ib->size, 1, iunit, in);
+ hoedown_buffer_grow(ib, ib->size + data.iunit);
+ ib->size += fread(ib->data + ib->size, 1, data.iunit, file);
}
- if (in != stdin)
- fclose(in);
+ if (file != stdin) fclose(file);
+ /* Perform SmartyPants processing */
+ ob = hoedown_buffer_new(data.ounit);
- /* performing SmartyPants processing */
- ob = hoedown_buffer_new(ounit);
- if (!ob) {
- fprintf(stderr, "Couldn't allocate output buffer.\n");
- return 4;
- }
-
- //clock_gettime(CLOCK_MONOTONIC, &start);
+ /*clock_gettime(CLOCK_MONOTONIC, &start);*/
hoedown_html_smartypants(ob, ib->data, ib->size);
- //clock_gettime(CLOCK_MONOTONIC, &end);
+ /*clock_gettime(CLOCK_MONOTONIC, &end);*/
-
- /* writing the result to stdout */
+ /* Write the result to stdout */
(void)fwrite(ob->data, 1, ob->size, stdout);
-
- /* showing rendering time */
- if (show_time) {
- //TODO: enable this
- //long long elapsed = ( end.tv_sec*1000000000 + end.tv_nsec)
- // - (start.tv_sec*1000000000 + start.tv_nsec);
- //if (elapsed < 1000000000)
- // fprintf(stderr, "Time spent on rendering: %.2f ms.\n", ((double)elapsed)/1000000);
- //else
- // fprintf(stderr, "Time spent on rendering: %.3f s.\n", ((double)elapsed)/1000000000);
+ /* Show rendering time */
+ if (data.show_time) {
+ /*TODO: enable this
+ long long elapsed = (end.tv_sec - start.tv_sec)*1e9 + (end.tv_nsec - start.tv_nsec);
+ if (elapsed < 1e9)
+ fprintf(stderr, "Time spent on rendering: %.2f ms.\n", ((double)elapsed)/1e6);
+ else
+ fprintf(stderr, "Time spent on rendering: %.3f s.\n", ((double)elapsed)/1e9);
+ */
}
-
- /* cleanup */
+ /* Cleanup */
hoedown_buffer_free(ib);
hoedown_buffer_free(ob);
diff --git a/hoedown.def b/hoedown.def
index 09b54a7..3b2c69f 100644
--- a/hoedown.def
+++ b/hoedown.def
@@ -4,30 +4,36 @@ EXPORTS
hoedown_autolink__www
hoedown_autolink__email
hoedown_autolink__url
- hoedown_buffer_grow
+ hoedown_buffer_init
hoedown_buffer_new
- hoedown_buffer_cstr
- hoedown_buffer_prefix
+ hoedown_buffer_reset
+ hoedown_buffer_grow
hoedown_buffer_put
hoedown_buffer_puts
hoedown_buffer_putc
- hoedown_buffer_free
- hoedown_buffer_reset
+ hoedown_buffer_set
+ hoedown_buffer_sets
+ hoedown_buffer_eq
+ hoedown_buffer_eqs
+ hoedown_buffer_prefix
hoedown_buffer_slurp
+ hoedown_buffer_cstr
hoedown_buffer_printf
+ hoedown_buffer_free
hoedown_document_new
hoedown_document_render
+ hoedown_document_render_inline
hoedown_document_free
- hoedown_escape_html
hoedown_escape_href
+ hoedown_escape_html
+ hoedown_html_smartypants
hoedown_html_is_tag
hoedown_html_renderer_new
hoedown_html_toc_renderer_new
hoedown_html_renderer_free
- hoedown_html_smartypants
- hoedown_stack_free
+ hoedown_stack_init
+ hoedown_stack_uninit
hoedown_stack_grow
- hoedown_stack_new
hoedown_stack_push
hoedown_stack_pop
hoedown_stack_top
diff --git a/src/autolink.c b/src/autolink.c
index f2245cb..e7019fd 100644
--- a/src/autolink.c
+++ b/src/autolink.c
@@ -5,26 +5,28 @@
#include <stdio.h>
#include <ctype.h>
-#ifdef _MSC_VER
+#ifndef _MSC_VER
+#include <strings.h>
+#else
#define strncasecmp _strnicmp
#endif
int
-hoedown_autolink_is_safe(const uint8_t *link, size_t link_len)
+hoedown_autolink_is_safe(const uint8_t *data, size_t size)
{
static const size_t valid_uris_count = 6;
static const char *valid_uris[] = {
- "#", "/", "http://", "https://", "ftp://", "mailto:"
+ "http://", "https://", "/", "#", "ftp://", "mailto:"
};
-
+ static const size_t valid_uris_size[] = { 7, 8, 1, 1, 6, 7 };
size_t i;
for (i = 0; i < valid_uris_count; ++i) {
- size_t len = strlen(valid_uris[i]);
+ size_t len = valid_uris_size[i];
- if (link_len > len &&
- strncasecmp((char *)link, valid_uris[i], len) == 0 &&
- isalnum(link[len]))
+ if (size > len &&
+ strncasecmp((char *)data, valid_uris[i], len) == 0 &&
+ isalnum(data[len]))
return 1;
}
diff --git a/src/autolink.h b/src/autolink.h
index 8860ee2..528885c 100644
--- a/src/autolink.h
+++ b/src/autolink.h
@@ -9,24 +9,35 @@
extern "C" {
#endif
-enum {
+
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_autolink_flags {
HOEDOWN_AUTOLINK_SHORT_DOMAINS = (1 << 0)
-};
+} hoedown_autolink_flags;
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_autolink_is_safe: verify that a URL has a safe protocol */
+int hoedown_autolink_is_safe(const uint8_t *data, size_t size);
-int
-hoedown_autolink_is_safe(const uint8_t *link, size_t link_len);
+/* hoedown_autolink__www: search for the next www link in data */
+size_t hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
-size_t
-hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link,
- uint8_t *data, size_t offset, size_t size, unsigned int flags);
+/* hoedown_autolink__email: search for the next email in data */
+size_t hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
-size_t
-hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link,
- uint8_t *data, size_t offset, size_t size, unsigned int flags);
+/* hoedown_autolink__url: search for the next URL in data */
+size_t hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link,
+ uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags);
-size_t
-hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link,
- uint8_t *data, size_t offset, size_t size, unsigned int flags);
#ifdef __cplusplus
}
diff --git a/src/buffer.c b/src/buffer.c
index 1ab9eac..af77b70 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5,7 +5,45 @@
#include <string.h>
#include <assert.h>
-/* hoedown_buffer_init: initialize a buffer with custom allocators */
+void *
+hoedown_malloc(size_t size)
+{
+ void *ret = malloc(size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
+void *
+hoedown_calloc(size_t nmemb, size_t size)
+{
+ void *ret = calloc(nmemb, size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
+void *
+hoedown_realloc(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+
+ if (!ret) {
+ fprintf(stderr, "Allocation failed.\n");
+ abort();
+ }
+
+ return ret;
+}
+
void
hoedown_buffer_init(
hoedown_buffer *buf,
@@ -14,8 +52,7 @@ hoedown_buffer_init(
hoedown_free_callback data_free,
hoedown_free_callback buffer_free)
{
- if (!buf)
- return;
+ assert(buf);
buf->data = NULL;
buf->size = buf->asize = 0;
@@ -25,21 +62,26 @@ hoedown_buffer_init(
buf->buffer_free = buffer_free;
}
-/* hoedown_buffer_new: allocation of a new buffer */
+void
+hoedown_buffer_uninit(hoedown_buffer *buf)
+{
+ assert(buf && buf->unit);
+ buf->data_free(buf->data);
+}
+
hoedown_buffer *
hoedown_buffer_new(size_t unit)
{
- hoedown_buffer *ret = malloc(sizeof (hoedown_buffer));
- hoedown_buffer_init(ret, unit, realloc, free, free);
+ hoedown_buffer *ret = hoedown_malloc(sizeof (hoedown_buffer));
+ hoedown_buffer_init(ret, unit, hoedown_realloc, free, free);
return ret;
}
-/* hoedown_buffer_free: decrease the reference count and free the buffer if needed */
void
hoedown_buffer_free(hoedown_buffer *buf)
{
- if (!buf)
- return;
+ if (!buf) return;
+ assert(buf && buf->unit);
buf->data_free(buf->data);
@@ -47,82 +89,111 @@ hoedown_buffer_free(hoedown_buffer *buf)
buf->buffer_free(buf);
}
-/* hoedown_buffer_reset: frees internal data of the buffer */
void
hoedown_buffer_reset(hoedown_buffer *buf)
{
- if (!buf)
- return;
+ assert(buf && buf->unit);
buf->data_free(buf->data);
buf->data = NULL;
buf->size = buf->asize = 0;
}
-/* hoedown_buffer_grow: increasing the allocated size to the given value */
-int
+void
hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz)
{
size_t neoasz;
- void *neodata;
-
assert(buf && buf->unit);
if (buf->asize >= neosz)
- return HOEDOWN_BUF_OK;
+ return;
neoasz = buf->asize + buf->unit;
while (neoasz < neosz)
neoasz += buf->unit;
- neodata = buf->data_realloc(buf->data, neoasz);
- if (!neodata)
- return HOEDOWN_BUF_ENOMEM;
-
- buf->data = neodata;
+ buf->data = buf->data_realloc(buf->data, neoasz);
buf->asize = neoasz;
- return HOEDOWN_BUF_OK;
}
-/* hoedown_buffer_put: appends raw data to a buffer */
void
-hoedown_buffer_put(hoedown_buffer *buf, const void *data, size_t len)
+hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size)
{
assert(buf && buf->unit);
- if (buf->size + len > buf->asize && hoedown_buffer_grow(buf, buf->size + len) < 0)
- return;
+ if (buf->size + size > buf->asize)
+ hoedown_buffer_grow(buf, buf->size + size);
- memcpy(buf->data + buf->size, data, len);
- buf->size += len;
+ memcpy(buf->data + buf->size, data, size);
+ buf->size += size;
}
-/* hoedown_buffer_puts: appends a NUL-terminated string to a buffer */
void
hoedown_buffer_puts(hoedown_buffer *buf, const char *str)
{
- hoedown_buffer_put(buf, str, strlen(str));
+ hoedown_buffer_put(buf, (const uint8_t *)str, strlen(str));
}
-
-/* hoedown_buffer_putc: appends a single uint8_t to a buffer */
void
hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c)
{
assert(buf && buf->unit);
- if (buf->size + 1 > buf->asize && hoedown_buffer_grow(buf, buf->size + 1) < 0)
- return;
+ if (buf->size >= buf->asize)
+ hoedown_buffer_grow(buf, buf->size + 1);
buf->data[buf->size] = c;
buf->size += 1;
}
int
+hoedown_buffer_putf(hoedown_buffer *buf, FILE *file)
+{
+ assert(buf && buf->unit);
+
+ while (!(feof(file) || ferror(file))) {
+ hoedown_buffer_grow(buf, buf->size + buf->unit);
+ buf->size += fread(buf->data + buf->size, 1, buf->unit, file);
+ }
+
+ return ferror(file);
+}
+
+void
+hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size)
+{
+ assert(buf && buf->unit);
+
+ if (size > buf->asize)
+ hoedown_buffer_grow(buf, size);
+
+ memcpy(buf->data, data, size);
+ buf->size = size;
+}
+
+void
+hoedown_buffer_sets(hoedown_buffer *buf, const char *str)
+{
+ hoedown_buffer_set(buf, (const uint8_t *)str, strlen(str));
+}
+
+int
+hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size)
+{
+ if (buf->size != size) return 0;
+ return memcmp(buf->data, data, size) == 0;
+}
+
+int
+hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str)
+{
+ return hoedown_buffer_eq(buf, (const uint8_t *)str, strlen(str));
+}
+
+int
hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix)
{
size_t i;
- assert(buf && buf->unit);
for (i = 0; i < buf->size; ++i) {
if (prefix[i] == 0)
@@ -135,22 +206,20 @@ hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix)
return 0;
}
-/* hoedown_buffer_slurp: removes a given number of bytes from the head of the array */
void
-hoedown_buffer_slurp(hoedown_buffer *buf, size_t len)
+hoedown_buffer_slurp(hoedown_buffer *buf, size_t size)
{
assert(buf && buf->unit);
- if (len >= buf->size) {
+ if (size >= buf->size) {
buf->size = 0;
return;
}
- buf->size -= len;
- memmove(buf->data, buf->data + len, buf->size);
+ buf->size -= size;
+ memmove(buf->data, buf->data + size, buf->size);
}
-/* hoedown_buffer_cstr: NULL-termination of the string array */
const char *
hoedown_buffer_cstr(hoedown_buffer *buf)
{
@@ -159,15 +228,12 @@ hoedown_buffer_cstr(hoedown_buffer *buf)
if (buf->size < buf->asize && buf->data[buf->size] == 0)
return (char *)buf->data;
- if (buf->size + 1 <= buf->asize || hoedown_buffer_grow(buf, buf->size + 1) == 0) {
- buf->data[buf->size] = 0;
- return (char *)buf->data;
- }
+ hoedown_buffer_grow(buf, buf->size + 1);
+ buf->data[buf->size] = 0;
- return NULL;
+ return (char *)buf->data;
}
-/* hoedown_buffer_printf: formatted printing to a buffer */
void
hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...)
{
@@ -176,9 +242,9 @@ hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...)
assert(buf && buf->unit);
- if (buf->size >= buf->asize && hoedown_buffer_grow(buf, buf->size + 1) < 0)
- return;
-
+ if (buf->size >= buf->asize)
+ hoedown_buffer_grow(buf, buf->size + 1);
+
va_start(ap, fmt);
n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
va_end(ap);
@@ -194,8 +260,7 @@ hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...)
}
if ((size_t)n >= buf->asize - buf->size) {
- if (hoedown_buffer_grow(buf, buf->size + n + 1) < 0)
- return;
+ hoedown_buffer_grow(buf, buf->size + n + 1);
va_start(ap, fmt);
n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
@@ -207,3 +272,37 @@ hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...)
buf->size += n;
}
+
+void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int c) {
+ unsigned char unichar[4];
+
+ assert(buf && buf->unit);
+
+ if (c < 0x80) {
+ hoedown_buffer_putc(buf, c);
+ }
+ else if (c < 0x800) {
+ unichar[0] = 192 + (c / 64);
+ unichar[1] = 128 + (c % 64);
+ hoedown_buffer_put(buf, unichar, 2);
+ }
+ else if (c - 0xd800u < 0x800) {
+ HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd");
+ }
+ else if (c < 0x10000) {
+ unichar[0] = 224 + (c / 4096);
+ unichar[1] = 128 + (c / 64) % 64;
+ unichar[2] = 128 + (c % 64);
+ hoedown_buffer_put(buf, unichar, 3);
+ }
+ else if (c < 0x110000) {
+ unichar[0] = 240 + (c / 262144);
+ unichar[1] = 128 + (c / 4096) % 64;
+ unichar[2] = 128 + (c / 64) % 64;
+ unichar[3] = 128 + (c % 64);
+ hoedown_buffer_put(buf, unichar, 4);
+ }
+ else {
+ HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd");
+ }
+}
diff --git a/src/buffer.h b/src/buffer.h
index 8ba3683..d7703f8 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -3,9 +3,11 @@
#ifndef HOEDOWN_BUFFER_H
#define HOEDOWN_BUFFER_H
+#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdint.h>
+#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
@@ -14,17 +16,17 @@ extern "C" {
#if defined(_MSC_VER)
#define __attribute__(x)
#define inline __inline
+#define __builtin_expect(x,n) x
#endif
-typedef enum {
- HOEDOWN_BUF_OK = 0,
- HOEDOWN_BUF_ENOMEM = -1
-} hoedown_buferror_t;
+
+/*********
+ * TYPES *
+ *********/
typedef void *(*hoedown_realloc_callback)(void *, size_t);
typedef void (*hoedown_free_callback)(void *);
-/* hoedown_buffer: character array buffer */
struct hoedown_buffer {
uint8_t *data; /* actual character data */
size_t size; /* size of the string */
@@ -38,9 +40,15 @@ struct hoedown_buffer {
typedef struct hoedown_buffer hoedown_buffer;
-/* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */
-#define HOEDOWN_BUFPUTSL(output, literal) \
- hoedown_buffer_put(output, literal, sizeof(literal) - 1)
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* allocation wrappers */
+void *hoedown_malloc(size_t size) __attribute__ ((malloc));
+void *hoedown_calloc(size_t nmemb, size_t size) __attribute__ ((malloc));
+void *hoedown_realloc(void *ptr, size_t size) __attribute__ ((malloc));
/* hoedown_buffer_init: initialize a buffer with custom allocators */
void hoedown_buffer_init(
@@ -51,32 +59,47 @@ void hoedown_buffer_init(
hoedown_free_callback buffer_free
);
-/* hoedown_buffer_new: allocation of a new buffer */
-hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc));
+/* hoedown_buffer_uninit: uninitialize an existing buffer */
+void hoedown_buffer_uninit(hoedown_buffer *buf);
-/* hoedown_buffer_free: decrease the reference count and free the buffer if needed */
-void hoedown_buffer_free(hoedown_buffer *buf);
+/* hoedown_buffer_new: allocate a new buffer */
+hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc));
-/* hoedown_buffer_reset: frees internal data of the buffer */
+/* hoedown_buffer_reset: free internal data of the buffer */
void hoedown_buffer_reset(hoedown_buffer *buf);
-/* hoedown_buffer_grow: increasing the allocated size to the given value */
-int hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz);
+/* hoedown_buffer_grow: increase the allocated size to the given value */
+void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz);
-/* hoedown_buffer_put: appends raw data to a buffer */
-void hoedown_buffer_put(hoedown_buffer *buf, const void *data, size_t len);
+/* hoedown_buffer_put: append raw data to a buffer */
+void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size);
-/* hoedown_buffer_puts: appends a NUL-terminated string to a buffer */
+/* hoedown_buffer_puts: append a NUL-terminated string to a buffer */
void hoedown_buffer_puts(hoedown_buffer *buf, const char *str);
-/* hoedown_buffer_putc: appends a single char to a buffer */
+/* hoedown_buffer_putc: append a single char to a buffer */
void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c);
+/* hoedown_buffer_putf: read from a file and append to a buffer, until EOF or error */
+int hoedown_buffer_putf(hoedown_buffer *buf, FILE* file);
+
+/* hoedown_buffer_set: replace the buffer's contents with raw data */
+void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size);
+
+/* hoedown_buffer_sets: replace the buffer's contents with a NUL-terminated string */
+void hoedown_buffer_sets(hoedown_buffer *buf, const char *str);
+
+/* hoedown_buffer_eq: compare a buffer's data with other data for equality */
+int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size);
+
+/* hoedown_buffer_eq: compare a buffer's data with NUL-terminated string for equality */
+int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str);
+
/* hoedown_buffer_prefix: compare the beginning of a buffer with a string */
int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix);
-/* hoedown_buffer_slurp: removes a given number of bytes from the head of the array */
-void hoedown_buffer_slurp(hoedown_buffer *buf, size_t len);
+/* hoedown_buffer_slurp: remove a given number of bytes from the head of the buffer */
+void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size);
/* hoedown_buffer_cstr: NUL-termination of the string array (making a C-string) */
const char *hoedown_buffer_cstr(hoedown_buffer *buf);
@@ -84,6 +107,26 @@ const char *hoedown_buffer_cstr(hoedown_buffer *buf);
/* hoedown_buffer_printf: formatted printing to a buffer */
void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+/* hoedown_buffer_put_utf8: put a Unicode character encoded as UTF-8 */
+void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int codepoint);
+
+/* hoedown_buffer_free: free the buffer */
+void hoedown_buffer_free(hoedown_buffer *buf);
+
+
+/* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */
+#define HOEDOWN_BUFPUTSL(output, literal) \
+ hoedown_buffer_put(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+/* HOEDOWN_BUFSETSL: optimized hoedown_buffer_sets of a string literal */
+#define HOEDOWN_BUFSETSL(output, literal) \
+ hoedown_buffer_set(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+/* HOEDOWN_BUFEQSL: optimized hoedown_buffer_eqs of a string literal */
+#define HOEDOWN_BUFEQSL(output, literal) \
+ hoedown_buffer_eq(output, (const uint8_t *)literal, sizeof(literal) - 1)
+
+
#ifdef __cplusplus
}
#endif
diff --git a/src/document.c b/src/document.c
index acf2572..ee0102f 100644
--- a/src/document.c
+++ b/src/document.c
@@ -1,5 +1,3 @@
-/* document.c - generic markdown parser */
-
#include "document.h"
#include <assert.h>
@@ -9,7 +7,9 @@
#include "stack.h"
-#ifdef _MSC_VER
+#ifndef _MSC_VER
+#include <strings.h>
+#else
#define strncasecmp _strnicmp
#endif
@@ -88,7 +88,7 @@ enum markdown_char_t {
MD_CHAR_LINK,
MD_CHAR_LANGLE,
MD_CHAR_ESCAPE,
- MD_CHAR_ENTITITY,
+ MD_CHAR_ENTITY,
MD_CHAR_AUTOLINK_URL,
MD_CHAR_AUTOLINK_EMAIL,
MD_CHAR_AUTOLINK_WWW,
@@ -114,16 +114,16 @@ static char_trigger markdown_char_ptrs[] = {
&char_math
};
-/* render • structure containing state for a parser instance */
struct hoedown_document {
hoedown_renderer md;
+ hoedown_renderer_data data;
struct link_ref *refs[REF_TABLE_SIZE];
struct footnote_list footnotes_found;
struct footnote_list footnotes_used;
uint8_t active_char[256];
hoedown_stack work_bufs[2];
- unsigned int ext_flags;
+ hoedown_extensions ext_flags;
size_t max_nesting;
int in_link_body;
};
@@ -132,7 +132,7 @@ struct hoedown_document {
* HELPER FUNCTIONS *
***************************/
-static inline hoedown_buffer *
+static hoedown_buffer *
newbuf(hoedown_document *doc, int type)
{
static const size_t buf_size[2] = {256, 64};
@@ -151,7 +151,7 @@ newbuf(hoedown_document *doc, int type)
return work;
}
-static inline void
+static void
popbuf(hoedown_document *doc, int type)
{
doc->work_bufs[type].size--;
@@ -194,10 +194,7 @@ add_link_ref(
struct link_ref **references,
const uint8_t *name, size_t name_size)
{
- struct link_ref *ref = calloc(1, sizeof(struct link_ref));
-
- if (!ref)
- return NULL;
+ struct link_ref *ref = hoedown_calloc(1, sizeof(struct link_ref));
ref->id = hash_link_ref(name, name_size);
ref->next = references[ref->id % REF_TABLE_SIZE];
@@ -246,9 +243,7 @@ free_link_refs(struct link_ref **references)
static struct footnote_ref *
create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size)
{
- struct footnote_ref *ref = calloc(1, sizeof(struct footnote_ref));
- if (!ref)
- return NULL;
+ struct footnote_ref *ref = hoedown_calloc(1, sizeof(struct footnote_ref));
ref->id = hash_link_ref(name, name_size);
@@ -258,7 +253,7 @@ create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name
static int
add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref)
{
- struct footnote_item *item = calloc(1, sizeof(struct footnote_item));
+ struct footnote_item *item = hoedown_calloc(1, sizeof(struct footnote_item));
if (!item)
return 0;
item->ref = ref;
@@ -325,14 +320,14 @@ free_footnote_list(struct footnote_list *list, int free_refs)
* should instead extract an Unicode codepoint from
* this character and check for space properties.
*/
-static inline int
+static int
_isspace(int c)
{
return c == ' ' || c == '\n';
}
/* is_empty_all: verify that all the data is spacing */
-static inline int
+static int
is_empty_all(const uint8_t *data, size_t size)
{
size_t i = 0;
@@ -344,7 +339,7 @@ is_empty_all(const uint8_t *data, size_t size)
* Replace all spacing characters in data with spaces. As a special
* case, this collapses a newline with the previous space, if possible.
*/
-static inline void
+static void
replace_spacing(hoedown_buffer *ob, const uint8_t *data, size_t size)
{
size_t i = 0, mark;
@@ -400,7 +395,7 @@ is_mail_autolink(uint8_t *data, size_t size)
/* tag_length • returns the length of the given tag, or 0 is it's not valid */
static size_t
-tag_length(uint8_t *data, size_t size, enum hoedown_autolink *autolink)
+tag_length(uint8_t *data, size_t size, hoedown_autolink_type *autolink)
{
size_t i, j;
@@ -454,7 +449,7 @@ tag_length(uint8_t *data, size_t size, enum hoedown_autolink *autolink)
*autolink = HOEDOWN_AUTOLINK_NONE;
}
- /* looking for sometinhg looking like a tag end */
+ /* looking for something looking like a tag end */
while (i < size && data[i] != '>') i++;
if (i >= size) return 0;
return i + 1;
@@ -464,9 +459,9 @@ tag_length(uint8_t *data, size_t size, enum hoedown_autolink *autolink)
static void
parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
{
- size_t i = 0, end = 0;
- uint8_t action = 0;
+ size_t i = 0, end = 0, consumed = 0;
hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL };
+ uint8_t *active_char = doc->active_char;
if (doc->work_bufs[BUFFER_SPAN].size +
doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting)
@@ -474,14 +469,13 @@ parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t si
while (i < size) {
/* copying inactive chars into the output */
- while (end < size && (action = doc->active_char[data[end]]) == 0) {
+ while (end < size && active_char[data[end]] == 0)
end++;
- }
if (doc->md.normal_text) {
work.data = data + i;
work.size = end - i;
- doc->md.normal_text(ob, &work, doc->md.opaque);
+ doc->md.normal_text(ob, &work, &doc->data);
}
else
hoedown_buffer_put(ob, data + i, end - i);
@@ -489,12 +483,13 @@ parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t si
if (end >= size) break;
i = end;
- end = markdown_char_ptrs[(int)action](ob, doc, data + i, i, size - i);
+ end = markdown_char_ptrs[ (int)active_char[data[end]] ](ob, doc, data + i, i - consumed, size - i);
if (!end) /* no action from the callback */
end = i + 1;
else {
i += end;
end = i;
+ consumed = i;
}
}
}
@@ -554,7 +549,7 @@ find_emph_char(uint8_t *data, size_t size, uint8_t c)
}
/* not a well-formed codespan; use found matching emph char */
- if (i >= size) return tmp_i;
+ if (bt < span_nb && i >= size) return tmp_i;
}
/* skipping a link */
else if (data[i] == '[') {
@@ -633,9 +628,9 @@ parse_emph1(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t siz
parse_inline(work, doc, data, i);
if (doc->ext_flags & HOEDOWN_EXT_UNDERLINE && c == '_')
- r = doc->md.underline(ob, work, doc->md.opaque);
+ r = doc->md.underline(ob, work, &doc->data);
else
- r = doc->md.emphasis(ob, work, doc->md.opaque);
+ r = doc->md.emphasis(ob, work, &doc->data);
popbuf(doc, BUFFER_SPAN);
return r ? i + 1 : 0;
@@ -663,11 +658,11 @@ parse_emph2(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t siz
parse_inline(work, doc, data, i);
if (c == '~')
- r = doc->md.strikethrough(ob, work, doc->md.opaque);
+ r = doc->md.strikethrough(ob, work, &doc->data);
else if (c == '=')
- r = doc->md.highlight(ob, work, doc->md.opaque);
+ r = doc->md.highlight(ob, work, &doc->data);
else
- r = doc->md.double_emphasis(ob, work, doc->md.opaque);
+ r = doc->md.double_emphasis(ob, work, &doc->data);
popbuf(doc, BUFFER_SPAN);
return r ? i + 2 : 0;
@@ -699,7 +694,7 @@ parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t siz
hoedown_buffer *work = newbuf(doc, BUFFER_SPAN);
parse_inline(work, doc, data, i);
- r = doc->md.triple_emphasis(ob, work, doc->md.opaque);
+ r = doc->md.triple_emphasis(ob, work, &doc->data);
popbuf(doc, BUFFER_SPAN);
return r ? i + 3 : 0;
@@ -723,36 +718,41 @@ parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t siz
static size_t
parse_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size, const char *end, size_t delimsz, int displaymode)
{
+ hoedown_buffer text = { NULL, 0, 0, 0, NULL, NULL, NULL };
size_t i = delimsz;
- if (!doc->md.math) return 0;
+
+ if (!doc->md.math)
+ return 0;
/* find ending delimiter */
while (1) {
- while (i < size && data[i] != (uint8_t)end[0]) i++;
- if (i >= size) return 0;
+ while (i < size && data[i] != (uint8_t)end[0])
+ i++;
+
+ if (i >= size)
+ return 0;
if (!is_escaped(data, i) && !(i + delimsz > size)
&& memcmp(data + i, end, delimsz) == 0)
break;
+
i++;
}
/* prepare buffers */
- hoedown_buffer text = { data + delimsz, i - delimsz, 0, 0, NULL, NULL, NULL };
-
- /* enforce spacing around the span */
- i += delimsz;
- if (offset && !_isspace(data[-1])) return 0;
- if (i < size && !_isspace(data[i])) return 0;
+ text.data = data + delimsz;
+ text.size = i - delimsz;
/* if this is a $$ and MATH_EXPLICIT is not active,
- * guess wether displaymode should be enabled from the context */
+ * guess whether displaymode should be enabled from the context */
+ i += delimsz;
if (delimsz == 2 && !(doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT))
displaymode = is_empty_all(data - offset, offset) && is_empty_all(data + i, size - i);
/* call callback */
- if (doc->md.math(ob, &text, displaymode, doc->md.opaque))
+ if (doc->md.math(ob, &text, displaymode, &doc->data))
return i;
+
return 0;
}
@@ -806,7 +806,7 @@ char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
while (ob->size && ob->data[ob->size - 1] == ' ')
ob->size--;
- return doc->md.linebreak(ob, doc->md.opaque) ? 1 : 0;
+ return doc->md.linebreak(ob, &doc->data) ? 1 : 0;
}
@@ -814,6 +814,7 @@ char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
static size_t
char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
size_t end, nb = 0, i, f_begin, f_end;
/* counting the number of backticks in the delimiter */
@@ -841,11 +842,13 @@ char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t o
/* real code span */
if (f_begin < f_end) {
- hoedown_buffer work = { data + f_begin, f_end - f_begin, 0, 0, NULL, NULL, NULL };
- if (!doc->md.codespan(ob, &work, doc->md.opaque))
+ work.data = data + f_begin;
+ work.size = f_end - f_begin;
+
+ if (!doc->md.codespan(ob, &work, &doc->data))
end = 0;
} else {
- if (!doc->md.codespan(ob, 0, doc->md.opaque))
+ if (!doc->md.codespan(ob, 0, &doc->data))
end = 0;
}
@@ -887,11 +890,11 @@ char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offs
hoedown_buffer *work = newbuf(doc, BUFFER_SPAN);
parse_inline(work, doc, data + f_begin, f_end - f_begin);
- if (!doc->md.quote(ob, work, doc->md.opaque))
+ if (!doc->md.quote(ob, work, &doc->data))
end = 0;
popbuf(doc, BUFFER_SPAN);
} else {
- if (!doc->md.quote(ob, 0, doc->md.opaque))
+ if (!doc->md.quote(ob, 0, &doc->data))
end = 0;
}
@@ -921,7 +924,7 @@ char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t off
if (doc->md.normal_text) {
work.data = data + 1;
work.size = 1;
- doc->md.normal_text(ob, &work, doc->md.opaque);
+ doc->md.normal_text(ob, &work, &doc->data);
}
else hoedown_buffer_putc(ob, data[1]);
} else if (size == 1) {
@@ -953,7 +956,7 @@ char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t off
if (doc->md.entity) {
work.data = data;
work.size = end;
- doc->md.entity(ob, &work, doc->md.opaque);
+ doc->md.entity(ob, &work, &doc->data);
}
else hoedown_buffer_put(ob, data, end);
@@ -964,22 +967,25 @@ char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t off
static size_t
char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size)
{
- enum hoedown_autolink altype = HOEDOWN_AUTOLINK_NONE;
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ hoedown_autolink_type altype = HOEDOWN_AUTOLINK_NONE;
size_t end = tag_length(data, size, &altype);
- hoedown_buffer work = { data, end, 0, 0, NULL, NULL, NULL };
int ret = 0;
+ work.data = data;
+ work.size = end;
+
if (end > 2) {
if (doc->md.autolink && altype != HOEDOWN_AUTOLINK_NONE) {
hoedown_buffer *u_link = newbuf(doc, BUFFER_SPAN);
work.data = data + 1;
work.size = end - 2;
unscape_text(u_link, &work);
- ret = doc->md.autolink(ob, u_link, altype, doc->md.opaque);
+ ret = doc->md.autolink(ob, u_link, altype, &doc->data);
popbuf(doc, BUFFER_SPAN);
}
- else if (doc->md.raw_html_tag)
- ret = doc->md.raw_html_tag(ob, &work, doc->md.opaque);
+ else if (doc->md.raw_html)
+ ret = doc->md.raw_html(ob, &work, &doc->data);
}
if (!ret) return 0;
@@ -1002,14 +1008,18 @@ char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size
HOEDOWN_BUFPUTSL(link_url, "http://");
hoedown_buffer_put(link_url, link->data, link->size);
- ob->size -= rewind;
+ if (ob->size > rewind)
+ ob->size -= rewind;
+ else
+ ob->size = 0;
+
if (doc->md.normal_text) {
link_text = newbuf(doc, BUFFER_SPAN);
- doc->md.normal_text(link_text, link, doc->md.opaque);
- doc->md.link(ob, link_url, NULL, link_text, doc->md.opaque);
+ doc->md.normal_text(link_text, link, &doc->data);
+ doc->md.link(ob, link_text, link_url, NULL, &doc->data);
popbuf(doc, BUFFER_SPAN);
} else {
- doc->md.link(ob, link_url, NULL, link, doc->md.opaque);
+ doc->md.link(ob, link, link_url, NULL, &doc->data);
}
popbuf(doc, BUFFER_SPAN);
}
@@ -1030,8 +1040,12 @@ char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, si
link = newbuf(doc, BUFFER_SPAN);
if ((link_len = hoedown_autolink__email(&rewind, link, data, offset, size, 0)) > 0) {
- ob->size -= rewind;
- doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, doc->md.opaque);
+ if (ob->size > rewind)
+ ob->size -= rewind;
+ else
+ ob->size = 0;
+
+ doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, &doc->data);
}
popbuf(doc, BUFFER_SPAN);
@@ -1050,8 +1064,12 @@ char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size
link = newbuf(doc, BUFFER_SPAN);
if ((link_len = hoedown_autolink__url(&rewind, link, data, offset, size, 0)) > 0) {
- ob->size -= rewind;
- doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, doc->md.opaque);
+ if (ob->size > rewind)
+ ob->size -= rewind;
+ else
+ ob->size = 0;
+
+ doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, &doc->data);
}
popbuf(doc, BUFFER_SPAN);
@@ -1106,7 +1124,7 @@ char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offse
/* render */
if (doc->md.footnote_ref)
- ret = doc->md.footnote_ref(ob, fr->num, doc->md.opaque);
+ ret = doc->md.footnote_ref(ob, fr->num, &doc->data);
}
goto cleanup;
@@ -1181,8 +1199,10 @@ char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offse
link_e--;
/* remove optional angle brackets around the link */
- if (data[link_b] == '<') link_b++;
- if (data[link_e - 1] == '>') link_e--;
+ if (data[link_b] == '<' && data[link_e - 1] == '>') {
+ link_b++;
+ link_e--;
+ }
/* building escaped link and title */
if (link_e > link_b) {
@@ -1271,9 +1291,9 @@ char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offse
if (ob->size && ob->data[ob->size - 1] == '!')
ob->size -= 1;
- ret = doc->md.image(ob, u_link, title, content, doc->md.opaque);
+ ret = doc->md.image(ob, u_link, title, content, &doc->data);
} else {
- ret = doc->md.link(ob, u_link, title, content, doc->md.opaque);
+ ret = doc->md.link(ob, content, u_link, title, &doc->data);
}
/* cleanup */
@@ -1312,7 +1332,7 @@ char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_
sup = newbuf(doc, BUFFER_SPAN);
parse_inline(sup, doc, data + sup_start, sup_len - sup_start);
- doc->md.superscript(ob, sup, doc->md.opaque);
+ doc->md.superscript(ob, sup, &doc->data);
popbuf(doc, BUFFER_SPAN);
return (sup_start == 2) ? sup_len + 1 : sup_len;
@@ -1616,7 +1636,7 @@ parse_blockquote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_
parse_block(out, doc, work_data, work_size);
if (doc->md.blockquote)
- doc->md.blockquote(ob, out, doc->md.opaque);
+ doc->md.blockquote(ob, out, &doc->data);
popbuf(doc, BUFFER_BLOCK);
return end;
}
@@ -1628,9 +1648,11 @@ parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
static size_t
parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
{
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
size_t i = 0, end = 0;
int level = 0;
- hoedown_buffer work = { data, 0, 0, 0, NULL, NULL, NULL };
+
+ work.data = data;
while (i < size) {
for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
@@ -1648,37 +1670,6 @@ parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
break;
}
- /*
- * Early termination of a paragraph with the same logic
- * as Markdown 1.0.0. If this logic is applied, the
- * Markdown 1.0.3 test suite won't pass cleanly
- *
- * :: If the first character in a new line is not a letter,
- * let's check to see if there's some kind of block starting
- * here
- */
- if ((doc->ext_flags & HOEDOWN_EXT_LAX_SPACING) && !isalnum(data[i])) {
- if (prefix_oli(data + i, size - i) ||
- prefix_uli(data + i, size - i)) {
- end = i;
- break;
- }
-
- /* see if an html block starts here */
- if (data[i] == '<' && doc->md.blockhtml &&
- parse_htmlblock(ob, doc, data + i, size - i, 0)) {
- end = i;
- break;
- }
-
- /* see if a code fence starts here */
- if ((doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) != 0 &&
- is_codefence(data + i, size - i, NULL, NULL)) {
- end = i;
- break;
- }
- }
-
i = end;
}
@@ -1690,7 +1681,7 @@ parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK);
parse_inline(tmp, doc, work.data, work.size);
if (doc->md.paragraph)
- doc->md.paragraph(ob, tmp, doc->md.opaque);
+ doc->md.paragraph(ob, tmp, &doc->data);
popbuf(doc, BUFFER_BLOCK);
} else {
hoedown_buffer *header_work;
@@ -1712,7 +1703,7 @@ parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
parse_inline(tmp, doc, work.data, work.size);
if (doc->md.paragraph)
- doc->md.paragraph(ob, tmp, doc->md.opaque);
+ doc->md.paragraph(ob, tmp, &doc->data);
popbuf(doc, BUFFER_BLOCK);
work.data += beg;
@@ -1725,7 +1716,7 @@ parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
parse_inline(header_work, doc, work.data, work.size);
if (doc->md.header)
- doc->md.header(ob, header_work, (int)level, doc->md.opaque);
+ doc->md.header(ob, header_work, (int)level, &doc->data);
popbuf(doc, BUFFER_SPAN);
}
@@ -1737,23 +1728,27 @@ parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
static size_t
parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size)
{
+ hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL };
+ hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL };
size_t i = 0, text_start, line_start;
size_t w, w2;
size_t width, width2;
uint8_t chr, chr2;
- hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL };
- hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL };
- // parse codefence line
- while (i < size && data[i] != '\n') i++;
+ /* parse codefence line */
+ while (i < size && data[i] != '\n')
+ i++;
+
w = parse_codefence(data, i, &lang, &width, &chr);
- if (!w) return 0;
+ if (!w)
+ return 0;
- // search for end
+ /* search for end */
i++;
text_start = i;
while ((line_start = i) < size) {
- while (i < size && data[i] != '\n') i++;
+ while (i < size && data[i] != '\n')
+ i++;
w2 = is_codefence(data + line_start, i - line_start, &width2, &chr2);
if (w == w2 && width == width2 && chr == chr2 &&
@@ -1762,11 +1757,12 @@ parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_
i++;
}
+
text.data = data + text_start;
text.size = line_start - text_start;
if (doc->md.blockcode)
- doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, doc->md.opaque);
+ doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, &doc->data);
return i;
}
@@ -1806,7 +1802,7 @@ parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
hoedown_buffer_putc(work, '\n');
if (doc->md.blockcode)
- doc->md.blockcode(ob, work, NULL, doc->md.opaque);
+ doc->md.blockcode(ob, work, NULL, &doc->data);
popbuf(doc, BUFFER_BLOCK);
return beg;
@@ -1815,7 +1811,7 @@ parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
/* parse_listitem • parsing of a single list item */
/* assuming initial prefix is already removed */
static size_t
-parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int *flags)
+parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags *flags)
{
hoedown_buffer *work = 0, *inter = 0;
size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
@@ -1942,7 +1938,7 @@ parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
/* render of li itself */
if (doc->md.listitem)
- doc->md.listitem(ob, inter, *flags, doc->md.opaque);
+ doc->md.listitem(ob, inter, *flags, &doc->data);
popbuf(doc, BUFFER_SPAN);
popbuf(doc, BUFFER_SPAN);
@@ -1952,7 +1948,7 @@ parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
/* parse_list • parsing ordered or unordered list block */
static size_t
-parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int flags)
+parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags flags)
{
hoedown_buffer *work = 0;
size_t i = 0, j;
@@ -1968,7 +1964,7 @@ parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size
}
if (doc->md.list)
- doc->md.list(ob, work, flags, doc->md.opaque);
+ doc->md.list(ob, work, flags, &doc->data);
popbuf(doc, BUFFER_BLOCK);
return i;
}
@@ -2000,7 +1996,7 @@ parse_atxheader(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
parse_inline(work, doc, data + i, end - i);
if (doc->md.header)
- doc->md.header(ob, work, (int)level, doc->md.opaque);
+ doc->md.header(ob, work, (int)level, &doc->data);
popbuf(doc, BUFFER_SPAN);
}
@@ -2018,7 +2014,7 @@ parse_footnote_def(hoedown_buffer *ob, hoedown_document *doc, unsigned int num,
parse_block(work, doc, data, size);
if (doc->md.footnote_def)
- doc->md.footnote_def(ob, work, num, doc->md.opaque);
+ doc->md.footnote_def(ob, work, num, &doc->data);
popbuf(doc, BUFFER_SPAN);
}
@@ -2043,14 +2039,14 @@ parse_footnote_list(hoedown_buffer *ob, hoedown_document *doc, struct footnote_l
}
if (doc->md.footnotes)
- doc->md.footnotes(ob, work, doc->md.opaque);
+ doc->md.footnotes(ob, work, &doc->data);
popbuf(doc, BUFFER_BLOCK);
}
/* htmlblock_is_end • check for end of HTML block : </tag>( *)\n */
/* returns tag length on match, 0 otherwise */
/* assumes data starts with "<" */
-static inline size_t
+static size_t
htmlblock_is_end(
const char *tag,
size_t tag_len,
@@ -2115,7 +2111,7 @@ htmlblock_find_end_strict(
while (i < size && data[i] != '\n') i++;
if (i < size) i++;
if (i == mark) return 0;
-
+
if (data[mark] == ' ' && mark > 0) continue;
mark += htmlblock_find_end(tag, tag_len, doc, data + mark, i - mark);
if (mark == i && (is_empty(data + i, size - i) || i >= size)) break;
@@ -2128,9 +2124,11 @@ htmlblock_find_end_strict(
static size_t
parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render)
{
- size_t i, j = 0, tag_end;
+ hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL };
+ size_t i, j = 0, tag_len, tag_end;
const char *curtag = NULL;
- hoedown_buffer work = { data, 0, 0, 0, NULL, NULL, NULL };
+
+ work.data = data;
/* identification of the opening tag */
if (size < 2 || data[0] != '<')
@@ -2161,7 +2159,7 @@ parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
if (j) {
work.size = i + j;
if (do_render && doc->md.blockhtml)
- doc->md.blockhtml(ob, &work, doc->md.opaque);
+ doc->md.blockhtml(ob, &work, &doc->data);
return work.size;
}
}
@@ -2178,7 +2176,7 @@ parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
if (j) {
work.size = i + j;
if (do_render && doc->md.blockhtml)
- doc->md.blockhtml(ob, &work, doc->md.opaque);
+ doc->md.blockhtml(ob, &work, &doc->data);
return work.size;
}
}
@@ -2189,7 +2187,7 @@ parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
}
/* looking for a matching closing tag in strict mode */
- size_t tag_len = strlen(curtag);
+ tag_len = strlen(curtag);
tag_end = htmlblock_find_end_strict(curtag, tag_len, doc, data, size);
/* if not found, trying a second pass looking for indented match */
@@ -2203,7 +2201,7 @@ parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t
/* the end of the block has been found */
work.size = tag_end;
if (do_render && doc->md.blockhtml)
- doc->md.blockhtml(ob, &work, doc->md.opaque);
+ doc->md.blockhtml(ob, &work, &doc->data);
return tag_end;
}
@@ -2215,10 +2213,10 @@ parse_table_row(
uint8_t *data,
size_t size,
size_t columns,
- int *col_data,
- int header_flag)
+ hoedown_table_flags *col_data,
+ hoedown_table_flags header_flag)
{
- size_t i = 0, col;
+ size_t i = 0, col, len;
hoedown_buffer *row_work = 0;
if (!doc->md.table_cell || !doc->md.table_row)
@@ -2240,8 +2238,16 @@ parse_table_row(
cell_start = i;
- size_t len = find_emph_char(data + i, size - i, '|');
- i += len ? len : size - i;
+ len = find_emph_char(data + i, size - i, '|');
+
+ /* Two possibilities for len == 0:
+ 1) No more pipe char found in the current line.
+ 2) The next pipe is right after the current one, i.e. empty cell.
+ For case 1, we skip to the end of line; for case 2 we just continue.
+ */
+ if (len == 0 && i < size && data[i] != '|')
+ len = size - i;
+ i += len;
cell_end = i - 1;
@@ -2249,7 +2255,7 @@ parse_table_row(
cell_end--;
parse_inline(cell_work, doc, data + cell_start, 1 + cell_end - cell_start);
- doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, doc->md.opaque);
+ doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, &doc->data);
popbuf(doc, BUFFER_SPAN);
i++;
@@ -2257,10 +2263,10 @@ parse_table_row(
for (; col < columns; ++col) {
hoedown_buffer empty_cell = { 0, 0, 0, 0, NULL, NULL, NULL };
- doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, doc->md.opaque);
+ doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, &doc->data);
}
- doc->md.table_row(ob, row_work, doc->md.opaque);
+ doc->md.table_row(ob, row_work, &doc->data);
popbuf(doc, BUFFER_SPAN);
}
@@ -2272,7 +2278,7 @@ parse_table_header(
uint8_t *data,
size_t size,
size_t *columns,
- int **column_data)
+ hoedown_table_flags **column_data)
{
int pipes;
size_t i = 0, col, header_end, under_end;
@@ -2300,7 +2306,7 @@ parse_table_header(
return 0;
*columns = pipes + 1;
- *column_data = calloc(*columns, sizeof(int));
+ *column_data = hoedown_calloc(*columns, sizeof(hoedown_table_flags));
/* Parse the header underline */
i++;
@@ -2366,12 +2372,14 @@ parse_table(
{
size_t i;
+ hoedown_buffer *work = 0;
hoedown_buffer *header_work = 0;
hoedown_buffer *body_work = 0;
size_t columns;
- int *col_data = NULL;
+ hoedown_table_flags *col_data = NULL;
+ work = newbuf(doc, BUFFER_BLOCK);
header_work = newbuf(doc, BUFFER_SPAN);
body_work = newbuf(doc, BUFFER_BLOCK);
@@ -2405,13 +2413,20 @@ parse_table(
i++;
}
+ if (doc->md.table_header)
+ doc->md.table_header(work, header_work, &doc->data);
+
+ if (doc->md.table_body)
+ doc->md.table_body(work, body_work, &doc->data);
+
if (doc->md.table)
- doc->md.table(ob, header_work, body_work, doc->md.opaque);
+ doc->md.table(ob, work, &doc->data);
}
free(col_data);
popbuf(doc, BUFFER_SPAN);
popbuf(doc, BUFFER_BLOCK);
+ popbuf(doc, BUFFER_BLOCK);
return i;
}
@@ -2443,7 +2458,7 @@ parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t siz
else if (is_hrule(txt_data, end)) {
if (doc->md.hrule)
- doc->md.hrule(ob, doc->md.opaque);
+ doc->md.hrule(ob, &doc->data);
while (beg < size && data[beg] != '\n')
beg++;
@@ -2523,7 +2538,7 @@ is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct fo
start = i;
- /* process lines similiar to a list item */
+ /* process lines similar to a list item */
while (i < end) {
while (i < end && data[i] != '\n' && data[i] != '\r') i++;
@@ -2560,7 +2575,7 @@ is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct fo
hoedown_buffer_put(contents, data + start + ind, i - start - ind);
/* add carriage return */
if (i < end) {
- hoedown_buffer_put(contents, "\n", 1);
+ hoedown_buffer_putc(contents, '\n');
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
i++;
if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++;
@@ -2703,13 +2718,22 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size)
{
+ /* This code makes two assumptions:
+ * - Input is valid UTF-8. (Any byte with top two bits 10 is skipped,
+ * whether or not it is a valid UTF-8 continuation byte.)
+ * - Input contains no combining characters. (Combining characters
+ * should be skipped but are not.)
+ */
size_t i = 0, tab = 0;
while (i < size) {
size_t org = i;
while (i < size && line[i] != '\t') {
- i++; tab++;
+ /* ignore UTF-8 continuation bytes */
+ if ((line[i] & 0xc0) != 0x80)
+ tab++;
+ i++;
}
if (i > org)
@@ -2733,24 +2757,27 @@ static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size)
hoedown_document *
hoedown_document_new(
const hoedown_renderer *renderer,
- unsigned int extensions,
+ hoedown_extensions extensions,
size_t max_nesting)
{
hoedown_document *doc = NULL;
assert(max_nesting > 0 && renderer);
- doc = malloc(sizeof(hoedown_document));
- if (!doc)
- return NULL;
-
+ doc = hoedown_malloc(sizeof(hoedown_document));
memcpy(&doc->md, renderer, sizeof(hoedown_renderer));
- hoedown_stack_new(&doc->work_bufs[BUFFER_BLOCK], 4);
- hoedown_stack_new(&doc->work_bufs[BUFFER_SPAN], 8);
+ doc->data.opaque = renderer->opaque;
+
+ hoedown_stack_init(&doc->work_bufs[BUFFER_BLOCK], 4);
+ hoedown_stack_init(&doc->work_bufs[BUFFER_SPAN], 8);
memset(doc->active_char, 0x0, 256);
+ if (extensions & HOEDOWN_EXT_UNDERLINE && doc->md.underline) {
+ doc->active_char['_'] = MD_CHAR_EMPHASIS;
+ }
+
if (doc->md.emphasis || doc->md.double_emphasis || doc->md.triple_emphasis) {
doc->active_char['*'] = MD_CHAR_EMPHASIS;
doc->active_char['_'] = MD_CHAR_EMPHASIS;
@@ -2766,12 +2793,12 @@ hoedown_document_new(
if (doc->md.linebreak)
doc->active_char['\n'] = MD_CHAR_LINEBREAK;
- if (doc->md.image || doc->md.link)
+ if (doc->md.image || doc->md.link || doc->md.footnotes || doc->md.footnote_ref)
doc->active_char['['] = MD_CHAR_LINK;
doc->active_char['<'] = MD_CHAR_LANGLE;
doc->active_char['\\'] = MD_CHAR_ESCAPE;
- doc->active_char['&'] = MD_CHAR_ENTITITY;
+ doc->active_char['&'] = MD_CHAR_ENTITY;
if (extensions & HOEDOWN_EXT_AUTOLINK) {
doc->active_char[':'] = MD_CHAR_AUTOLINK_URL;
@@ -2797,7 +2824,7 @@ hoedown_document_new(
}
void
-hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *document, size_t doc_size)
+hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size)
{
static const uint8_t UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
@@ -2807,11 +2834,9 @@ hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t
int footnotes_enabled;
text = hoedown_buffer_new(64);
- if (!text)
- return;
/* Preallocate enough space for our buffer to avoid expanding while copying */
- hoedown_buffer_grow(text, doc_size);
+ hoedown_buffer_grow(text, size);
/* reset the references table */
memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
@@ -2829,26 +2854,26 @@ hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t
/* Skip a possible UTF-8 BOM, even though the Unicode standard
* discourages having these in UTF-8 documents */
- if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)
+ if (size >= 3 && memcmp(data, UTF8_BOM, 3) == 0)
beg += 3;
- while (beg < doc_size) /* iterating over lines */
- if (footnotes_enabled && is_footnote(document, beg, doc_size, &end, &doc->footnotes_found))
+ while (beg < size) /* iterating over lines */
+ if (footnotes_enabled && is_footnote(data, beg, size, &end, &doc->footnotes_found))
beg = end;
- else if (is_ref(document, beg, doc_size, &end, doc->refs))
+ else if (is_ref(data, beg, size, &end, doc->refs))
beg = end;
else { /* skipping to the next line */
end = beg;
- while (end < doc_size && document[end] != '\n' && document[end] != '\r')
+ while (end < size && data[end] != '\n' && data[end] != '\r')
end++;
/* adding the line body if present */
if (end > beg)
- expand_tabs(text, document + beg, end - beg);
+ expand_tabs(text, data + beg, end - beg);
- while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
+ while (end < size && (data[end] == '\n' || data[end] == '\r')) {
/* add one \n per newline */
- if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
+ if (data[end] == '\n' || (end + 1 < size && data[end + 1] != '\n'))
hoedown_buffer_putc(text, '\n');
end++;
}
@@ -2861,7 +2886,7 @@ hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t
/* second pass: actual rendering */
if (doc->md.doc_header)
- doc->md.doc_header(ob, doc->md.opaque);
+ doc->md.doc_header(ob, 0, &doc->data);
if (text->size) {
/* adding a final newline if not already present */
@@ -2876,7 +2901,7 @@ hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t
parse_footnote_list(ob, doc, &doc->footnotes_used);
if (doc->md.doc_footer)
- doc->md.doc_footer(ob, doc->md.opaque);
+ doc->md.doc_footer(ob, 0, &doc->data);
/* clean-up */
hoedown_buffer_free(text);
@@ -2891,40 +2916,45 @@ hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t
}
void
-hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *document, size_t doc_size)
+hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size)
{
size_t i = 0, mark;
hoedown_buffer *text = hoedown_buffer_new(64);
- if (!text)
- return;
/* reset the references table */
memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
- /* first pass: convert all spacing to spaces */
- hoedown_buffer_grow(text, doc_size);
+ /* first pass: expand tabs and process newlines */
+ hoedown_buffer_grow(text, size);
while (1) {
mark = i;
- while (i < doc_size && document[i] != '\n' && document[i] != '\r')
+ while (i < size && data[i] != '\n' && data[i] != '\r')
i++;
- expand_tabs(text, document + mark, i - mark);
+ expand_tabs(text, data + mark, i - mark);
- if (i >= doc_size)
+ if (i >= size)
break;
- while (i < doc_size && (document[i] == '\n' || document[i] == '\r')) {
+ while (i < size && (data[i] == '\n' || data[i] == '\r')) {
/* add one \n per newline */
- if (document[i] == '\n' || (i + 1 < doc_size && document[i + 1] != '\n'))
+ if (data[i] == '\n' || (i + 1 < size && data[i + 1] != '\n'))
hoedown_buffer_putc(text, '\n');
i++;
}
}
/* second pass: actual rendering */
- hoedown_buffer_grow(ob, doc_size + (doc_size >> 1));
+ hoedown_buffer_grow(ob, text->size + (text->size >> 1));
+
+ if (doc->md.doc_header)
+ doc->md.doc_header(ob, 1, &doc->data);
+
parse_inline(ob, doc, text->data, text->size);
+ if (doc->md.doc_footer)
+ doc->md.doc_footer(ob, 1, &doc->data);
+
/* clean-up */
hoedown_buffer_free(text);
@@ -2943,8 +2973,8 @@ hoedown_document_free(hoedown_document *doc)
for (i = 0; i < (size_t)doc->work_bufs[BUFFER_BLOCK].asize; ++i)
hoedown_buffer_free(doc->work_bufs[BUFFER_BLOCK].item[i]);
- hoedown_stack_free(&doc->work_bufs[BUFFER_SPAN]);
- hoedown_stack_free(&doc->work_bufs[BUFFER_BLOCK]);
+ hoedown_stack_uninit(&doc->work_bufs[BUFFER_SPAN]);
+ hoedown_stack_uninit(&doc->work_bufs[BUFFER_BLOCK]);
free(doc);
}
diff --git a/src/document.h b/src/document.h
index 4547972..a8178fe 100644
--- a/src/document.h
+++ b/src/document.h
@@ -10,11 +10,12 @@
extern "C" {
#endif
-/*********
- * FLAGS *
- *********/
-enum hoedown_extensions {
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_extensions {
/* block-level extensions */
HOEDOWN_EXT_TABLES = (1 << 0),
HOEDOWN_EXT_FENCED_CODE = (1 << 1),
@@ -27,17 +28,16 @@ enum hoedown_extensions {
HOEDOWN_EXT_HIGHLIGHT = (1 << 6),
HOEDOWN_EXT_QUOTE = (1 << 7),
HOEDOWN_EXT_SUPERSCRIPT = (1 << 8),
- HOEDOWN_EXT_MATH = (1 << 13),
+ HOEDOWN_EXT_MATH = (1 << 9),
/* other flags */
- HOEDOWN_EXT_LAX_SPACING = (1 << 9),
- HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 10),
- HOEDOWN_EXT_SPACE_HEADERS = (1 << 11),
- HOEDOWN_EXT_MATH_EXPLICIT = (1 << 14),
+ HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 11),
+ HOEDOWN_EXT_SPACE_HEADERS = (1 << 12),
+ HOEDOWN_EXT_MATH_EXPLICIT = (1 << 13),
/* negative flags */
- HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 12)
-};
+ HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 14)
+} hoedown_extensions;
#define HOEDOWN_EXT_BLOCK (\
HOEDOWN_EXT_TABLES |\
@@ -54,7 +54,6 @@ enum hoedown_extensions {
HOEDOWN_EXT_MATH )
#define HOEDOWN_EXT_FLAGS (\
- HOEDOWN_EXT_LAX_SPACING |\
HOEDOWN_EXT_NO_INTRA_EMPHASIS |\
HOEDOWN_EXT_SPACE_HEADERS |\
HOEDOWN_EXT_MATH_EXPLICIT )
@@ -62,30 +61,37 @@ enum hoedown_extensions {
#define HOEDOWN_EXT_NEGATIVE (\
HOEDOWN_EXT_DISABLE_INDENTED_CODE )
-/* list/listitem flags */
-enum hoedown_listflags {
+typedef enum hoedown_list_flags {
HOEDOWN_LIST_ORDERED = (1 << 0),
HOEDOWN_LI_BLOCK = (1 << 1) /* <li> containing block data */
-};
+} hoedown_list_flags;
-enum hoedown_tableflags {
+typedef enum hoedown_table_flags {
HOEDOWN_TABLE_ALIGN_LEFT = 1,
HOEDOWN_TABLE_ALIGN_RIGHT = 2,
HOEDOWN_TABLE_ALIGN_CENTER = 3,
HOEDOWN_TABLE_ALIGNMASK = 3,
HOEDOWN_TABLE_HEADER = 4
-};
+} hoedown_table_flags;
-/* hoedown_autolink - type of autolink */
-enum hoedown_autolink {
+typedef enum hoedown_autolink_type {
HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/
HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */
HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */
-};
+} hoedown_autolink_type;
-/********************
- * TYPE DEFINITIONS *
- ********************/
+
+/*********
+ * TYPES *
+ *********/
+
+struct hoedown_document;
+typedef struct hoedown_document hoedown_document;
+
+struct hoedown_renderer_data {
+ void *opaque;
+};
+typedef struct hoedown_renderer_data hoedown_renderer_data;
/* hoedown_renderer - functions for rendering parsed data */
struct hoedown_renderer {
@@ -93,71 +99,71 @@ struct hoedown_renderer {
void *opaque;
/* block level callbacks - NULL skips the block */
- void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, void *opaque);
- void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- void (*blockhtml)(hoedown_buffer *ob,const hoedown_buffer *text, void *opaque);
- void (*header)(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaque);
- void (*hrule)(hoedown_buffer *ob, void *opaque);
- void (*list)(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque);
- void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque);
- void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- void (*table)(hoedown_buffer *ob, const hoedown_buffer *header, const hoedown_buffer *body, void *opaque);
- void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque);
- void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int num, void *opaque);
+ void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data);
+ void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data);
+ void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data);
+ void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data);
+ void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data);
+ void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data);
+ void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data);
+ void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
/* span level callbacks - NULL or return 0 prints the span verbatim */
- int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, enum hoedown_autolink type, void *opaque);
- int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*underline)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*quote)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, void *opaque);
- int (*linebreak)(hoedown_buffer *ob, void *opaque);
- int (*link)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *content, void *opaque);
- int (*raw_html_tag)(hoedown_buffer *ob, const hoedown_buffer *tag, void *opaque);
- int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
- int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, void *opaque);
- int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, void *opaque);
+ int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data);
+ int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+ int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data);
+ int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data);
+ int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data);
+ int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data);
+ int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data);
+ int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data);
+ int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
/* low level callbacks - NULL copies input directly into the output */
- void (*entity)(hoedown_buffer *ob, const hoedown_buffer *entity, void *opaque);
- void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque);
+ void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
+ void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data);
- /* header and footer */
- void (*doc_header)(hoedown_buffer *ob, void *opaque);
- void (*doc_footer)(hoedown_buffer *ob, void *opaque);
+ /* miscellaneous callbacks */
+ void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);
+ void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data);
};
-
typedef struct hoedown_renderer hoedown_renderer;
-struct hoedown_document;
-typedef struct hoedown_document hoedown_document;
+/*************
+ * FUNCTIONS *
+ *************/
-/**********************
- * EXPORTED FUNCTIONS *
- **********************/
-
-extern hoedown_document *
-hoedown_document_new(
+/* hoedown_document_new: allocate a new document processor instance */
+hoedown_document *hoedown_document_new(
const hoedown_renderer *renderer,
- unsigned int extensions,
- size_t max_nesting);
+ hoedown_extensions extensions,
+ size_t max_nesting
+) __attribute__ ((malloc));
+
+/* hoedown_document_render: render regular Markdown using the document processor */
+void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size);
-extern void
-hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *document, size_t doc_size);
+/* hoedown_document_render_inline: render inline Markdown using the document processor */
+void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size);
-extern void
-hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *document, size_t doc_size);
+/* hoedown_document_free: deallocate a document processor instance */
+void hoedown_document_free(hoedown_document *doc);
-extern void
-hoedown_document_free(hoedown_document *doc);
#ifdef __cplusplus
}
diff --git a/src/escape.c b/src/escape.c
index 749eb75..122c6ec 100644
--- a/src/escape.c
+++ b/src/escape.c
@@ -4,6 +4,11 @@
#include <stdio.h>
#include <string.h>
+
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
+
/*
* The following characters will not be escaped:
*
@@ -15,9 +20,9 @@
* - The characters which are *not* safe to be in
* an URL because they are RESERVED characters.
*
- * We asume (lazily) that any RESERVED char that
+ * We assume (lazily) that any RESERVED char that
* appears inside an URL is actually meant to
- * have its native function (i.e. as an URL
+ * have its native function (i.e. as an URL
* component/separator) and hence needs no escaping.
*
* There are two exceptions: the chacters & (amp)
@@ -30,58 +35,55 @@
*
*/
static const uint8_t HREF_SAFE[UINT8_MAX+1] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
void
-hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size)
+hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size)
{
static const char hex_chars[] = "0123456789ABCDEF";
- size_t i = 0, org;
+ size_t i = 0, mark;
char hex_str[3];
hex_str[0] = '%';
while (i < size) {
- org = i;
- while (i < size && HREF_SAFE[src[i]] != 0)
- i++;
+ mark = i;
+ while (i < size && HREF_SAFE[data[i]]) i++;
- if (i > org) {
- if (org == 0) {
- if (i >= size) {
- hoedown_buffer_put(ob, src, size);
- return;
- }
-
- }
+ /* Optimization for cases where there's nothing to escape */
+ if (mark == 0 && i >= size) {
+ hoedown_buffer_put(ob, data, size);
+ return;
+ }
- hoedown_buffer_put(ob, src + org, i - org);
+ if (likely(i > mark)) {
+ hoedown_buffer_put(ob, data + mark, i - mark);
}
/* escaping */
if (i >= size)
break;
- switch (src[i]) {
+ switch (data[i]) {
/* amp appears all the time in URLs, but needs
* HTML-entity escaping to be inside an href */
- case '&':
+ case '&':
HOEDOWN_BUFPUTSL(ob, "&amp;");
break;
@@ -91,7 +93,7 @@ hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size)
case '\'':
HOEDOWN_BUFPUTSL(ob, "&#x27;");
break;
-
+
/* the space can be escaped to %20 or a plus
* sign. we're going with the generic escape
* for now. the plus thing is more commonly seen
@@ -104,15 +106,16 @@ hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size)
/* every other character goes with a %XX escaping */
default:
- hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
- hex_str[2] = hex_chars[src[i] & 0xF];
- hoedown_buffer_put(ob, hex_str, 3);
+ hex_str[1] = hex_chars[(data[i] >> 4) & 0xF];
+ hex_str[2] = hex_chars[data[i] & 0xF];
+ hoedown_buffer_put(ob, (uint8_t *)hex_str, 3);
}
i++;
}
}
+
/**
* According to the OWASP rules:
*
@@ -125,21 +128,21 @@ hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size)
*
*/
static const uint8_t HTML_ESCAPE_TABLE[UINT8_MAX+1] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
@@ -154,36 +157,30 @@ static const char *HTML_ESCAPES[] = {
};
void
-hoedown_escape_html(hoedown_buffer *ob, const uint8_t *src, size_t size, int secure)
+hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure)
{
- size_t i = 0, org, esc = 0;
-
- while (i < size) {
- org = i;
- while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
- i++;
-
- if (i > org) {
- if (org == 0) {
- if (i >= size) {
- hoedown_buffer_put(ob, src, size);
- return;
- }
+ size_t i = 0, mark;
- }
+ while (1) {
+ mark = i;
+ while (i < size && HTML_ESCAPE_TABLE[data[i]] == 0) i++;
- hoedown_buffer_put(ob, src + org, i - org);
+ /* Optimization for cases where there's nothing to escape */
+ if (mark == 0 && i >= size) {
+ hoedown_buffer_put(ob, data, size);
+ return;
}
- /* escaping */
- if (i >= size)
- break;
+ if (likely(i > mark))
+ hoedown_buffer_put(ob, data + mark, i - mark);
+
+ if (i >= size) break;
/* The forward slash is only escaped in secure mode */
- if (src[i] == '/' && !secure) {
+ if (!secure && data[i] == '/') {
hoedown_buffer_putc(ob, '/');
} else {
- hoedown_buffer_puts(ob, HTML_ESCAPES[esc]);
+ hoedown_buffer_puts(ob, HTML_ESCAPES[HTML_ESCAPE_TABLE[data[i]]]);
}
i++;
diff --git a/src/escape.h b/src/escape.h
index 95be860..d7659c2 100644
--- a/src/escape.h
+++ b/src/escape.h
@@ -9,8 +9,17 @@
extern "C" {
#endif
-extern void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *src, size_t size, int secure);
-extern void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size);
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_escape_href: escape (part of) a URL inside HTML */
+void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_escape_html: escape HTML */
+void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure);
+
#ifdef __cplusplus
}
diff --git a/src/html.c b/src/html.c
index 1c65578..b5101c1 100644
--- a/src/html.c
+++ b/src/html.c
@@ -9,45 +9,45 @@
#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML)
-int
-hoedown_html_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
+hoedown_html_tag
+hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname)
{
size_t i;
int closed = 0;
- if (tag_size < 3 || tag_data[0] != '<')
+ if (size < 3 || data[0] != '<')
return HOEDOWN_HTML_TAG_NONE;
i = 1;
- if (tag_data[i] == '/') {
+ if (data[i] == '/') {
closed = 1;
i++;
}
- for (; i < tag_size; ++i, ++tagname) {
+ for (; i < size; ++i, ++tagname) {
if (*tagname == 0)
break;
- if (tag_data[i] != *tagname)
+ if (data[i] != *tagname)
return HOEDOWN_HTML_TAG_NONE;
}
- if (i == tag_size)
+ if (i == size)
return HOEDOWN_HTML_TAG_NONE;
- if (isspace(tag_data[i]) || tag_data[i] == '>')
+ if (isspace(data[i]) || data[i] == '>')
return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN;
return HOEDOWN_HTML_TAG_NONE;
}
-static inline void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length)
+static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length)
{
hoedown_escape_html(ob, source, length, 0);
}
-static inline void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
+static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
{
hoedown_escape_href(ob, source, length);
}
@@ -56,18 +56,13 @@ static inline void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t
* GENERIC RENDERER *
********************/
static int
-rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, enum hoedown_autolink type, void *opaque)
+rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (!link || !link->size)
return 0;
- if ((state->flags & HOEDOWN_HTML_SAFELINK) != 0 &&
- !hoedown_autolink_is_safe(link->data, link->size) &&
- type != HOEDOWN_AUTOLINK_EMAIL)
- return 0;
-
HOEDOWN_BUFPUTSL(ob, "<a href=\"");
if (type == HOEDOWN_AUTOLINK_EMAIL)
HOEDOWN_BUFPUTSL(ob, "mailto:");
@@ -75,7 +70,7 @@ rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, enum hoedown_autol
if (state->link_attributes) {
hoedown_buffer_putc(ob, '\"');
- state->link_attributes(ob, link, opaque);
+ state->link_attributes(ob, link, data);
hoedown_buffer_putc(ob, '>');
} else {
HOEDOWN_BUFPUTSL(ob, "\">");
@@ -98,7 +93,7 @@ rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, enum hoedown_autol
}
static void
-rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, void *opaque)
+rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data)
{
if (ob->size) hoedown_buffer_putc(ob, '\n');
@@ -117,16 +112,16 @@ rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buf
}
static void
-rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
if (ob->size) hoedown_buffer_putc(ob, '\n');
HOEDOWN_BUFPUTSL(ob, "<blockquote>\n");
- if (text) hoedown_buffer_put(ob, text->data, text->size);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</blockquote>\n");
}
static int
-rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
{
HOEDOWN_BUFPUTSL(ob, "<code>");
if (text) escape_html(ob, text->data, text->size);
@@ -135,91 +130,91 @@ rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
}
static int
-rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size)
+ if (!content || !content->size)
return 0;
HOEDOWN_BUFPUTSL(ob, "<del>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</del>");
return 1;
}
static int
-rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size)
+ if (!content || !content->size)
return 0;
HOEDOWN_BUFPUTSL(ob, "<strong>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</strong>");
return 1;
}
static int
-rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size) return 0;
+ if (!content || !content->size) return 0;
HOEDOWN_BUFPUTSL(ob, "<em>");
- if (text) hoedown_buffer_put(ob, text->data, text->size);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</em>");
return 1;
}
static int
-rndr_underline(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size)
+ if (!content || !content->size)
return 0;
HOEDOWN_BUFPUTSL(ob, "<u>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</u>");
return 1;
}
static int
-rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size)
+ if (!content || !content->size)
return 0;
HOEDOWN_BUFPUTSL(ob, "<mark>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</mark>");
return 1;
}
static int
-rndr_quote(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size)
+ if (!content || !content->size)
return 0;
HOEDOWN_BUFPUTSL(ob, "<q>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</q>");
return 1;
}
static int
-rndr_linebreak(hoedown_buffer *ob, void *opaque)
+rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
hoedown_buffer_puts(ob, USE_XHTML(state) ? "<br/>\n" : "<br>\n");
return 1;
}
static void
-rndr_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaque)
+rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (ob->size)
hoedown_buffer_putc(ob, '\n');
@@ -229,17 +224,14 @@ rndr_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opa
else
hoedown_buffer_printf(ob, "<h%d>", level);
- if (text) hoedown_buffer_put(ob, text->data, text->size);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
hoedown_buffer_printf(ob, "</h%d>\n", level);
}
static int
-rndr_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *content, void *opaque)
+rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
-
- if (link != NULL && (state->flags & HOEDOWN_HTML_SAFELINK) != 0 && !hoedown_autolink_is_safe(link->data, link->size))
- return 0;
+ hoedown_html_renderer_state *state = data->opaque;
HOEDOWN_BUFPUTSL(ob, "<a href=\"");
@@ -253,7 +245,7 @@ rndr_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *
if (state->link_attributes) {
hoedown_buffer_putc(ob, '\"');
- state->link_attributes(ob, link, opaque);
+ state->link_attributes(ob, link, data);
hoedown_buffer_putc(ob, '>');
} else {
HOEDOWN_BUFPUTSL(ob, "\">");
@@ -265,110 +257,120 @@ rndr_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *
}
static void
-rndr_list(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque)
+rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data)
{
if (ob->size) hoedown_buffer_putc(ob, '\n');
- hoedown_buffer_put(ob, flags & HOEDOWN_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
- if (text) hoedown_buffer_put(ob, text->data, text->size);
- hoedown_buffer_put(ob, flags & HOEDOWN_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
+ hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "<ol>\n" : "<ul>\n"), 5);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+ hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "</ol>\n" : "</ul>\n"), 6);
}
static void
-rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque)
+rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data)
{
HOEDOWN_BUFPUTSL(ob, "<li>");
- if (text) {
- size_t size = text->size;
- while (size && text->data[size - 1] == '\n')
+ if (content) {
+ size_t size = content->size;
+ while (size && content->data[size - 1] == '\n')
size--;
- hoedown_buffer_put(ob, text->data, size);
+ hoedown_buffer_put(ob, content->data, size);
}
HOEDOWN_BUFPUTSL(ob, "</li>\n");
}
static void
-rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
size_t i = 0;
if (ob->size) hoedown_buffer_putc(ob, '\n');
- if (!text || !text->size)
+ if (!content || !content->size)
return;
- while (i < text->size && isspace(text->data[i])) i++;
+ while (i < content->size && isspace(content->data[i])) i++;
- if (i == text->size)
+ if (i == content->size)
return;
HOEDOWN_BUFPUTSL(ob, "<p>");
if (state->flags & HOEDOWN_HTML_HARD_WRAP) {
size_t org;
- while (i < text->size) {
+ while (i < content->size) {
org = i;
- while (i < text->size && text->data[i] != '\n')
+ while (i < content->size && content->data[i] != '\n')
i++;
if (i > org)
- hoedown_buffer_put(ob, text->data + org, i - org);
+ hoedown_buffer_put(ob, content->data + org, i - org);
/*
* do not insert a line break if this newline
* is the last character on the paragraph
*/
- if (i >= text->size - 1)
+ if (i >= content->size - 1)
break;
- rndr_linebreak(ob, opaque);
+ rndr_linebreak(ob, data);
i++;
}
} else {
- hoedown_buffer_put(ob, &text->data[i], text->size - i);
+ hoedown_buffer_put(ob, content->data + i, content->size - i);
}
HOEDOWN_BUFPUTSL(ob, "</p>\n");
}
static void
-rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
{
size_t org, sz;
- if (!text) return;
- //FIXME: do we *really* need to trim the HTML?
- //how does that make a difference?
+
+ if (!text)
+ return;
+
+ /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */
sz = text->size;
- while (sz > 0 && text->data[sz - 1] == '\n') sz--;
+ while (sz > 0 && text->data[sz - 1] == '\n')
+ sz--;
+
org = 0;
- while (org < sz && text->data[org] == '\n') org++;
- if (org >= sz) return;
- if (ob->size) hoedown_buffer_putc(ob, '\n');
+ while (org < sz && text->data[org] == '\n')
+ org++;
+
+ if (org >= sz)
+ return;
+
+ if (ob->size)
+ hoedown_buffer_putc(ob, '\n');
+
hoedown_buffer_put(ob, text->data + org, sz - org);
hoedown_buffer_putc(ob, '\n');
}
static int
-rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size) return 0;
+ if (!content || !content->size) return 0;
HOEDOWN_BUFPUTSL(ob, "<strong><em>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</em></strong>");
return 1;
}
static void
-rndr_hrule(hoedown_buffer *ob, void *opaque)
+rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (ob->size) hoedown_buffer_putc(ob, '\n');
hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
}
static int
-rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, void *opaque)
+rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (!link || !link->size) return 0;
HOEDOWN_BUFPUTSL(ob, "<img src=\"");
@@ -387,9 +389,9 @@ rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer
}
static int
-rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
/* ESCAPE overrides SKIP_HTML. It doesn't look to see if
* there are any valid tags, just escapes all of them. */
@@ -406,29 +408,42 @@ rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
}
static void
-rndr_table(hoedown_buffer *ob, const hoedown_buffer *header, const hoedown_buffer *body, void *opaque)
+rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (ob->size) hoedown_buffer_putc(ob, '\n');
- HOEDOWN_BUFPUTSL(ob, "<table><thead>\n");
- if (header)
- hoedown_buffer_put(ob, header->data, header->size);
- HOEDOWN_BUFPUTSL(ob, "</thead><tbody>\n");
- if (body)
- hoedown_buffer_put(ob, body->data, body->size);
- HOEDOWN_BUFPUTSL(ob, "</tbody></table>\n");
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<table>\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</table>\n");
}
static void
-rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<thead>\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</thead>\n");
+}
+
+static void
+rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
+{
+ if (ob->size) hoedown_buffer_putc(ob, '\n');
+ HOEDOWN_BUFPUTSL(ob, "<tbody>\n");
+ hoedown_buffer_put(ob, content->data, content->size);
+ HOEDOWN_BUFPUTSL(ob, "</tbody>\n");
+}
+
+static void
+rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
HOEDOWN_BUFPUTSL(ob, "<tr>\n");
- if (text)
- hoedown_buffer_put(ob, text->data, text->size);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</tr>\n");
}
static void
-rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flags, void *opaque)
+rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data)
{
if (flags & HOEDOWN_TABLE_HEADER) {
HOEDOWN_BUFPUTSL(ob, "<th");
@@ -453,8 +468,8 @@ rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flag
HOEDOWN_BUFPUTSL(ob, ">");
}
- if (text)
- hoedown_buffer_put(ob, text->data, text->size);
+ if (content)
+ hoedown_buffer_put(ob, content->data, content->size);
if (flags & HOEDOWN_TABLE_HEADER) {
HOEDOWN_BUFPUTSL(ob, "</th>\n");
@@ -464,88 +479,87 @@ rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int flag
}
static int
-rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (!text || !text->size) return 0;
+ if (!content || !content->size) return 0;
HOEDOWN_BUFPUTSL(ob, "<sup>");
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</sup>");
return 1;
}
static void
-rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- if (text)
- escape_html(ob, text->data, text->size);
+ if (content)
+ escape_html(ob, content->data, content->size);
}
static void
-rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
+rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (ob->size) hoedown_buffer_putc(ob, '\n');
HOEDOWN_BUFPUTSL(ob, "<div class=\"footnotes\">\n");
hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
HOEDOWN_BUFPUTSL(ob, "<ol>\n");
-
- if (text)
- hoedown_buffer_put(ob, text->data, text->size);
-
+
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
+
HOEDOWN_BUFPUTSL(ob, "\n</ol>\n</div>\n");
}
static void
-rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int num, void *opaque)
+rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data)
{
size_t i = 0;
int pfound = 0;
-
+
/* insert anchor at the end of first paragraph block */
- if (text) {
- while ((i+3) < text->size) {
- if (text->data[i++] != '<') continue;
- if (text->data[i++] != '/') continue;
- if (text->data[i++] != 'p' && text->data[i] != 'P') continue;
- if (text->data[i] != '>') continue;
+ if (content) {
+ while ((i+3) < content->size) {
+ if (content->data[i++] != '<') continue;
+ if (content->data[i++] != '/') continue;
+ if (content->data[i++] != 'p' && content->data[i] != 'P') continue;
+ if (content->data[i] != '>') continue;
i -= 3;
pfound = 1;
break;
}
}
-
+
hoedown_buffer_printf(ob, "\n<li id=\"fn%d\">\n", num);
if (pfound) {
- hoedown_buffer_put(ob, text->data, i);
+ hoedown_buffer_put(ob, content->data, i);
hoedown_buffer_printf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
- hoedown_buffer_put(ob, text->data + i, text->size - i);
- } else if (text) {
- hoedown_buffer_put(ob, text->data, text->size);
+ hoedown_buffer_put(ob, content->data + i, content->size - i);
+ } else if (content) {
+ hoedown_buffer_put(ob, content->data, content->size);
}
HOEDOWN_BUFPUTSL(ob, "</li>\n");
}
static int
-rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, void *opaque)
+rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)
{
hoedown_buffer_printf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
return 1;
}
static int
-rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, void *opaque)
+rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)
{
- hoedown_buffer_put(ob, displaymode ? "\\[" : "\\(", 2);
+ hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2);
escape_html(ob, text->data, text->size);
- hoedown_buffer_put(ob, displaymode ? "\\]" : "\\)", 2);
+ hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2);
return 1;
}
static void
-toc_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaque)
+toc_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state = data->opaque;
if (level <= state->toc_data.nesting_level) {
/* set the level offset if this is the first header
@@ -572,28 +586,34 @@ toc_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaq
}
hoedown_buffer_printf(ob, "<a href=\"#toc_%d\">", state->toc_data.header_count++);
- if (text) escape_html(ob, text->data, text->size);
+ if (content) hoedown_buffer_put(ob, content->data, content->size);
HOEDOWN_BUFPUTSL(ob, "</a>\n");
}
}
static int
-toc_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *content, void *opaque)
+toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data)
{
- if (content && content->size)
- hoedown_buffer_put(ob, content->data, content->size);
+ if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
return 1;
}
static void
-toc_finalize(hoedown_buffer *ob, void *opaque)
+toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data)
{
- hoedown_html_renderer_state *state = opaque;
+ hoedown_html_renderer_state *state;
+
+ if (inline_render)
+ return;
+
+ state = data->opaque;
while (state->toc_data.current_level > 0) {
HOEDOWN_BUFPUTSL(ob, "</li>\n</ul>\n");
state->toc_data.current_level--;
}
+
+ state->toc_data.header_count = 0;
}
hoedown_renderer *
@@ -604,7 +624,6 @@ hoedown_html_toc_renderer_new(int nesting_level)
NULL,
NULL,
- NULL,
toc_header,
NULL,
NULL,
@@ -615,6 +634,9 @@ hoedown_html_toc_renderer_new(int nesting_level)
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
NULL,
rndr_codespan,
@@ -626,15 +648,15 @@ hoedown_html_toc_renderer_new(int nesting_level)
NULL,
NULL,
toc_link,
- NULL,
rndr_triple_emphasis,
rndr_strikethrough,
rndr_superscript,
NULL,
NULL,
-
NULL,
+
NULL,
+ rndr_normal_text,
NULL,
toc_finalize
@@ -644,46 +666,40 @@ hoedown_html_toc_renderer_new(int nesting_level)
hoedown_renderer *renderer;
/* Prepare the state pointer */
- state = malloc(sizeof(hoedown_html_renderer_state));
- if (!state)
- return NULL;
-
+ state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
memset(state, 0x0, sizeof(hoedown_html_renderer_state));
state->toc_data.nesting_level = nesting_level;
/* Prepare the renderer */
- renderer = malloc(sizeof(hoedown_renderer));
- if (!renderer) {
- free(state);
- return NULL;
- }
-
+ renderer = hoedown_malloc(sizeof(hoedown_renderer));
memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
-
+
renderer->opaque = state;
return renderer;
}
hoedown_renderer *
-hoedown_html_renderer_new(unsigned int render_flags, int nesting_level)
+hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level)
{
- static const hoedown_renderer cb_default = {
+ static const hoedown_renderer cb_default = {
NULL,
rndr_blockcode,
rndr_blockquote,
- rndr_raw_block,
rndr_header,
rndr_hrule,
rndr_list,
rndr_listitem,
rndr_paragraph,
rndr_table,
+ rndr_table_header,
+ rndr_table_body,
rndr_tablerow,
rndr_tablecell,
rndr_footnotes,
rndr_footnote_def,
+ rndr_raw_block,
rndr_autolink,
rndr_codespan,
@@ -695,12 +711,12 @@ hoedown_html_renderer_new(unsigned int render_flags, int nesting_level)
rndr_image,
rndr_linebreak,
rndr_link,
- rndr_raw_html,
rndr_triple_emphasis,
rndr_strikethrough,
rndr_superscript,
rndr_footnote_ref,
rndr_math,
+ rndr_raw_html,
NULL,
rndr_normal_text,
@@ -713,27 +729,19 @@ hoedown_html_renderer_new(unsigned int render_flags, int nesting_level)
hoedown_renderer *renderer;
/* Prepare the state pointer */
- state = malloc(sizeof(hoedown_html_renderer_state));
- if (!state)
- return NULL;
-
+ state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
memset(state, 0x0, sizeof(hoedown_html_renderer_state));
state->flags = render_flags;
state->toc_data.nesting_level = nesting_level;
/* Prepare the renderer */
- renderer = malloc(sizeof(hoedown_renderer));
- if (!renderer) {
- free(state);
- return NULL;
- }
-
+ renderer = hoedown_malloc(sizeof(hoedown_renderer));
memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE)
renderer->blockhtml = NULL;
-
+
renderer->opaque = state;
return renderer;
}
diff --git a/src/html.h b/src/html.h
index 46b424b..e46e7fd 100644
--- a/src/html.h
+++ b/src/html.h
@@ -1,30 +1,38 @@
-/* html.h - HTML renderer */
+/* html.h - HTML renderer and utilities */
#ifndef HOEDOWN_HTML_H
#define HOEDOWN_HTML_H
#include "document.h"
#include "buffer.h"
-#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef enum {
+
+/*************
+ * CONSTANTS *
+ *************/
+
+typedef enum hoedown_html_flags {
HOEDOWN_HTML_SKIP_HTML = (1 << 0),
HOEDOWN_HTML_ESCAPE = (1 << 1),
- HOEDOWN_HTML_SAFELINK = (1 << 2),
- HOEDOWN_HTML_HARD_WRAP = (1 << 3),
- HOEDOWN_HTML_USE_XHTML = (1 << 4)
+ HOEDOWN_HTML_HARD_WRAP = (1 << 2),
+ HOEDOWN_HTML_USE_XHTML = (1 << 3)
} hoedown_html_flags;
-typedef enum {
+typedef enum hoedown_html_tag {
HOEDOWN_HTML_TAG_NONE = 0,
HOEDOWN_HTML_TAG_OPEN,
HOEDOWN_HTML_TAG_CLOSE
} hoedown_html_tag;
+
+/*********
+ * TYPES *
+ *********/
+
struct hoedown_html_renderer_state {
void *opaque;
@@ -35,28 +43,39 @@ struct hoedown_html_renderer_state {
int nesting_level;
} toc_data;
- unsigned int flags;
+ hoedown_html_flags flags;
/* extra callbacks */
- void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, void *self);
+ void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, const hoedown_renderer_data *data);
};
-
typedef struct hoedown_html_renderer_state hoedown_html_renderer_state;
-int
-hoedown_html_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname);
-extern hoedown_renderer *
-hoedown_html_renderer_new(unsigned int render_flags, int nesting_level);
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_html_smartypants: process an HTML snippet using SmartyPants for smart punctuation */
+void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *data, size_t size);
+
+/* hoedown_html_is_tag: checks if data starts with a specific tag, returns the tag type or NONE */
+hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname);
+
+
+/* hoedown_html_renderer_new: allocates a regular HTML renderer */
+hoedown_renderer *hoedown_html_renderer_new(
+ hoedown_html_flags render_flags,
+ int nesting_level
+) __attribute__ ((malloc));
-extern hoedown_renderer *
-hoedown_html_toc_renderer_new(int nesting_level);
+/* hoedown_html_toc_renderer_new: like hoedown_html_renderer_new, but the returned renderer produces the Table of Contents */
+hoedown_renderer *hoedown_html_toc_renderer_new(
+ int nesting_level
+) __attribute__ ((malloc));
-extern void
-hoedown_html_renderer_free(hoedown_renderer *renderer);
+/* hoedown_html_renderer_free: deallocate an HTML renderer */
+void hoedown_html_renderer_free(hoedown_renderer *renderer);
-extern void
-hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size);
#ifdef __cplusplus
}
diff --git a/src/html_smartypants.c b/src/html_smartypants.c
index 9f9dcf0..b0904da 100644
--- a/src/html_smartypants.c
+++ b/src/html_smartypants.c
@@ -60,7 +60,7 @@ static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-static inline int
+static int
word_boundary(uint8_t c)
{
return c == 0 || isspace(c) || ispunct(c);
@@ -313,6 +313,16 @@ smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t
size_t tag, i = 0;
+ /* This is a comment. Copy everything verbatim until --> or EOF is seen. */
+ if (i + 4 < size && memcmp(text, "<!--", 4) == 0) {
+ i += 4;
+ while (i + 3 < size && memcmp(text + i, "-->", 3) != 0)
+ i++;
+ i += 3;
+ hoedown_buffer_put(ob, text, i + 1);
+ return i;
+ }
+
while (i < size && text[i] != '>')
i++;
diff --git a/src/stack.c b/src/stack.c
index d0dea29..5c6102c 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -1,70 +1,66 @@
#include "stack.h"
+#include "buffer.h"
+
+#include <stdlib.h>
#include <string.h>
+#include <assert.h>
-int
-hoedown_stack_new(hoedown_stack *st, size_t initial_size)
+void
+hoedown_stack_init(hoedown_stack *st, size_t initial_size)
{
+ assert(st);
+
st->item = NULL;
- st->size = 0;
- st->asize = 0;
+ st->size = st->asize = 0;
if (!initial_size)
initial_size = 8;
- return hoedown_stack_grow(st, initial_size);
+ hoedown_stack_grow(st, initial_size);
}
void
-hoedown_stack_free(hoedown_stack *st)
+hoedown_stack_uninit(hoedown_stack *st)
{
- if (!st)
- return;
+ assert(st);
free(st->item);
-
- st->item = NULL;
- st->size = 0;
- st->asize = 0;
}
-int
-hoedown_stack_grow(hoedown_stack *st, size_t new_size)
+void
+hoedown_stack_grow(hoedown_stack *st, size_t neosz)
{
- void **new_st;
+ assert(st);
- if (st->asize >= new_size)
- return 0;
-
- new_st = realloc(st->item, new_size * sizeof(void *));
- if (new_st == NULL)
- return -1;
-
- memset(new_st + st->asize, 0x0,
- (new_size - st->asize) * sizeof(void *));
+ if (st->asize >= neosz)
+ return;
- st->item = new_st;
- st->asize = new_size;
+ st->item = hoedown_realloc(st->item, neosz * sizeof(void *));
+ memset(st->item + st->asize, 0x0, (neosz - st->asize) * sizeof(void *));
- if (st->size > new_size)
- st->size = new_size;
+ st->asize = neosz;
- return 0;
+ if (st->size > neosz)
+ st->size = neosz;
}
-int
+void
hoedown_stack_push(hoedown_stack *st, void *item)
{
- if (hoedown_stack_grow(st, st->size * 2) < 0)
- return -1;
+ assert(st);
+
+ if (st->size >= st->asize)
+ hoedown_stack_grow(st, st->size * 2);
st->item[st->size++] = item;
- return 0;
}
void *
hoedown_stack_pop(hoedown_stack *st)
{
+ assert(st);
+
if (!st->size)
return NULL;
@@ -72,8 +68,10 @@ hoedown_stack_pop(hoedown_stack *st)
}
void *
-hoedown_stack_top(hoedown_stack *st)
+hoedown_stack_top(const hoedown_stack *st)
{
+ assert(st);
+
if (!st->size)
return NULL;
diff --git a/src/stack.h b/src/stack.h
index 9063592..bf9b439 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -3,26 +3,47 @@
#ifndef HOEDOWN_STACK_H
#define HOEDOWN_STACK_H
-#include <stdlib.h>
+#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
+
+/*********
+ * TYPES *
+ *********/
+
struct hoedown_stack {
void **item;
size_t size;
size_t asize;
};
-
typedef struct hoedown_stack hoedown_stack;
-int hoedown_stack_new(hoedown_stack *, size_t);
-void hoedown_stack_free(hoedown_stack *);
-int hoedown_stack_grow(hoedown_stack *, size_t);
-int hoedown_stack_push(hoedown_stack *, void *);
-void *hoedown_stack_pop(hoedown_stack *);
-void *hoedown_stack_top(hoedown_stack *);
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_stack_init: initialize a stack */
+void hoedown_stack_init(hoedown_stack *st, size_t initial_size);
+
+/* hoedown_stack_uninit: free internal data of the stack */
+void hoedown_stack_uninit(hoedown_stack *st);
+
+/* hoedown_stack_grow: increase the allocated size to the given value */
+void hoedown_stack_grow(hoedown_stack *st, size_t neosz);
+
+/* hoedown_stack_push: push an item to the top of the stack */
+void hoedown_stack_push(hoedown_stack *st, void *item);
+
+/* hoedown_stack_pop: retrieve and remove the item at the top of the stack */
+void *hoedown_stack_pop(hoedown_stack *st);
+
+/* hoedown_stack_top: retrieve the item at the top of the stack */
+void *hoedown_stack_top(const hoedown_stack *st);
+
#ifdef __cplusplus
}
diff --git a/src/version.c b/src/version.c
index 73a70bc..744209b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -1,9 +1,9 @@
#include "version.h"
void
-hoedown_version(int *ver_major, int *ver_minor, int *ver_revision)
+hoedown_version(int *major, int *minor, int *revision)
{
- *ver_major = HOEDOWN_VERSION_MAJOR;
- *ver_minor = HOEDOWN_VERSION_MINOR;
- *ver_revision = HOEDOWN_VERSION_REVISION;
+ *major = HOEDOWN_VERSION_MAJOR;
+ *minor = HOEDOWN_VERSION_MINOR;
+ *revision = HOEDOWN_VERSION_REVISION;
}
diff --git a/src/version.h b/src/version.h
index 3971658..d3247b6 100644
--- a/src/version.h
+++ b/src/version.h
@@ -7,13 +7,24 @@
extern "C" {
#endif
-#define HOEDOWN_VERSION "2.0.0"
-#define HOEDOWN_VERSION_MAJOR 2
+
+/*************
+ * CONSTANTS *
+ *************/
+
+#define HOEDOWN_VERSION "3.0.5"
+#define HOEDOWN_VERSION_MAJOR 3
#define HOEDOWN_VERSION_MINOR 0
-#define HOEDOWN_VERSION_REVISION 0
+#define HOEDOWN_VERSION_REVISION 5
+
+
+/*************
+ * FUNCTIONS *
+ *************/
+
+/* hoedown_version: retrieve Hoedown's version numbers */
+void hoedown_version(int *major, int *minor, int *revision);
-extern void
-hoedown_version(int *major, int *minor, int *revision);
#ifdef __cplusplus
}
diff --git a/test/MarkdownTest_1.0.3/Tests/Escape character.html b/test/Tests/Escape character.html
index a6863ba..a6863ba 100644
--- a/test/MarkdownTest_1.0.3/Tests/Escape character.html
+++ b/test/Tests/Escape character.html
diff --git a/test/MarkdownTest_1.0.3/Tests/Escape character.text b/test/Tests/Escape character.text
index 3eab090..3eab090 100644
--- a/test/MarkdownTest_1.0.3/Tests/Escape character.text
+++ b/test/Tests/Escape character.text
diff --git a/test/Tests/Formatting in Table of Contents.html b/test/Tests/Formatting in Table of Contents.html
new file mode 100644
index 0000000..8e7be95
--- /dev/null
+++ b/test/Tests/Formatting in Table of Contents.html
@@ -0,0 +1,15 @@
+<ul>
+<li>
+<a href="#toc_0">Header with special &amp; characters</a>
+<ul>
+<li>
+<a href="#toc_1">With <code>Code</code></a>
+<ul>
+<li>
+<a href="#toc_2">With <em>Emphasis</em></a>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
diff --git a/test/Tests/Formatting in Table of Contents.text b/test/Tests/Formatting in Table of Contents.text
new file mode 100644
index 0000000..b95fbaa
--- /dev/null
+++ b/test/Tests/Formatting in Table of Contents.text
@@ -0,0 +1,5 @@
+# Header with special & characters
+
+## With `Code`
+
+### With *Emphasis*
diff --git a/test/Tests/Math.html b/test/Tests/Math.html
new file mode 100644
index 0000000..72df249
--- /dev/null
+++ b/test/Tests/Math.html
@@ -0,0 +1,31 @@
+<p>\[
+1*2*3 multi-line math
+\]</p>
+
+<p>\( 1*2*3 inline-math \)</p>
+
+<p>\[ 1*2*3 math with dollar \]</p>
+
+<p>\[ 1*2*3 \$ \\ \text{dollar with escapes} \]</p>
+
+<p>\( \\ \text{backslash with escapes} \$ 1*2*3 \)</p>
+
+<p>( not <em>really</em> math )</p>
+
+<p>$$ also <em>not</em> math $$</p>
+
+<p>this \(*should* be\) math</p>
+
+<p>this\( *should* also be\) math</p>
+
+<p>and \(this *should* \)too</p>
+
+<p>Something \{ like <em>math</em> but \} is not</p>
+
+<p>Also \(like <em>math</em> but \) is not</p>
+
+<p>\\( should be *math* as well \\\)</p>
+
+<p>This is \( math, and the \\\( inner one \\\) should be \) preserved</p>
+
+<p>\[ did you &lt;em&gt; know &lt;/em&gt; this is math? \]</p>
diff --git a/test/Tests/Math.text b/test/Tests/Math.text
new file mode 100644
index 0000000..dbca401
--- /dev/null
+++ b/test/Tests/Math.text
@@ -0,0 +1,31 @@
+\\[
+1*2*3 multi-line math
+\\]
+
+\\( 1*2*3 inline-math \\)
+
+$$ 1*2*3 math with dollar $$
+
+$$ 1*2*3 \$ \\ \text{dollar with escapes} $$
+
+\\( \\ \text{backslash with escapes} \$ 1*2*3 \\)
+
+\( not *really* math \)
+
+\$$ also *not* math \$$
+
+this $$*should* be$$ math
+
+this$$ *should* also be$$ math
+
+and $$this *should* $$too
+
+Something \\{ like *math* but \\} is not
+
+Also \\\(like *math* but \\\) is not
+
+\\\\( should be *math* as well \\\\)
+
+This is \\( math, and the \\\( inner one \\\) should be \\) preserved
+
+$$ did you <em> know </em> this is math? $$
diff --git a/test/Tests/Table.html b/test/Tests/Table.html
new file mode 100644
index 0000000..f434598
--- /dev/null
+++ b/test/Tests/Table.html
@@ -0,0 +1,66 @@
+<h1>Standard table</h1>
+
+<table>
+ <thead>
+ <tr>
+ <th>headline1</th>
+ <th>headline2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>123</td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
+
+
+<h1>Cell alignment</h1>
+
+<table>
+ <thead>
+ <tr>
+ <th style="text-align: left">headline1</th>
+ <th style="text-align: center">headline2</th>
+ <th style="text-align: right">headline3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td style="text-align: left">123</td>
+ <td style="text-align: center"></td>
+ <td style="text-align: right"></td>
+ </tr>
+ </tbody>
+</table>
+
+
+<h1>Malformed table: missing cell at row in body</h1>
+
+<table>
+ <thead>
+ <tr>
+ <th>headline1</th>
+ <th>headline2</th>
+ <th>headline3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>12</td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>34</td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>56</td>
+ <td></td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
diff --git a/test/Tests/Table.text b/test/Tests/Table.text
new file mode 100644
index 0000000..37df539
--- /dev/null
+++ b/test/Tests/Table.text
@@ -0,0 +1,21 @@
+# Standard table
+
+|headline1|headline2|
+|---------|---------|
+|123 | |
+
+
+# Cell alignment
+
+|headline1|headline2|headline3|
+|:-------|:------:|------:|
+|123|||
+
+
+# Malformed table: missing cell at row in body
+
+|headline1|headline2|headline3|
+|-------|-------|-------|
+|12
+|34||
+|56|
diff --git a/test/Tests/Underline.html b/test/Tests/Underline.html
new file mode 100644
index 0000000..c2a8bba
--- /dev/null
+++ b/test/Tests/Underline.html
@@ -0,0 +1 @@
+<p>This <u>underline</u> will work.</p>
diff --git a/test/Tests/Underline.text b/test/Tests/Underline.text
new file mode 100644
index 0000000..8068546
--- /dev/null
+++ b/test/Tests/Underline.text
@@ -0,0 +1 @@
+This _underline_ will work.
diff --git a/test/config.json b/test/config.json
new file mode 100644
index 0000000..365c7bc
--- /dev/null
+++ b/test/config.json
@@ -0,0 +1,116 @@
+{
+ "tests": [
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Amps and angle encoding.text",
+ "output": "MarkdownTest_1.0.3/Tests/Amps and angle encoding.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Auto links.text",
+ "output": "MarkdownTest_1.0.3/Tests/Auto links.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Backslash escapes.text",
+ "output": "MarkdownTest_1.0.3/Tests/Backslash escapes.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Blockquotes with code blocks.text",
+ "output": "MarkdownTest_1.0.3/Tests/Blockquotes with code blocks.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Code Blocks.text",
+ "output": "MarkdownTest_1.0.3/Tests/Code Blocks.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Code Spans.text",
+ "output": "MarkdownTest_1.0.3/Tests/Code Spans.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Hard-wrapped paragraphs with list-like lines.text",
+ "output": "MarkdownTest_1.0.3/Tests/Hard-wrapped paragraphs with list-like lines.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Horizontal rules.text",
+ "output": "MarkdownTest_1.0.3/Tests/Horizontal rules.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Inline HTML (Advanced).text",
+ "output": "MarkdownTest_1.0.3/Tests/Inline HTML (Advanced).html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Inline HTML (Simple).text",
+ "output": "MarkdownTest_1.0.3/Tests/Inline HTML (Simple).html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Inline HTML comments.text",
+ "output": "MarkdownTest_1.0.3/Tests/Inline HTML comments.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Links, inline style.text",
+ "output": "MarkdownTest_1.0.3/Tests/Links, inline style.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Links, reference style.text",
+ "output": "MarkdownTest_1.0.3/Tests/Links, reference style.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Links, shortcut references.text",
+ "output": "MarkdownTest_1.0.3/Tests/Links, shortcut references.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Literal quotes in titles.text",
+ "output": "MarkdownTest_1.0.3/Tests/Literal quotes in titles.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Basics.text",
+ "output": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Basics.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Syntax.text",
+ "output": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Syntax.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Nested blockquotes.text",
+ "output": "MarkdownTest_1.0.3/Tests/Nested blockquotes.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Ordered and unordered lists.text",
+ "output": "MarkdownTest_1.0.3/Tests/Ordered and unordered lists.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Strong and em together.text",
+ "output": "MarkdownTest_1.0.3/Tests/Strong and em together.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Tabs.text",
+ "output": "MarkdownTest_1.0.3/Tests/Tabs.html"
+ },
+ {
+ "input": "MarkdownTest_1.0.3/Tests/Tidyness.text",
+ "output": "MarkdownTest_1.0.3/Tests/Tidyness.html"
+ },
+ {
+ "input": "Tests/Escape character.text",
+ "output": "Tests/Escape character.html"
+ },
+ {
+ "input": "Tests/Formatting in Table of Contents.text",
+ "output": "Tests/Formatting in Table of Contents.html",
+ "flags": ["--html-toc", "-t", "3"]
+ },
+ {
+ "input": "Tests/Math.text",
+ "output": "Tests/Math.html",
+ "flags": ["--math"]
+ },
+ {
+ "input": "Tests/Underline.text",
+ "output": "Tests/Underline.html",
+ "flags": ["--underline"]
+ },
+ {
+ "input": "Tests/Table.text",
+ "output": "Tests/Table.html",
+ "flags": ["--tables"]
+ }
+ ]
+}
diff --git a/test/runner.py b/test/runner.py
new file mode 100755
index 0000000..7cbb27d
--- /dev/null
+++ b/test/runner.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import difflib
+import json
+import os
+import re
+import subprocess
+import unittest
+
+TEST_ROOT = os.path.dirname(__file__)
+PROJECT_ROOT = os.path.dirname(TEST_ROOT)
+HOEDOWN = [os.path.abspath(os.path.join(PROJECT_ROOT, 'hoedown'))]
+TIDY = ['tidy', '--show-body-only', '1', '--show-warnings', '0',
+ '--quiet', '1']
+CONFIG_PATH = os.path.join(TEST_ROOT, 'config.json')
+SLUGIFY_PATTERN = re.compile(r'\W')
+
+
+def with_metaclass(meta, *bases):
+ """Metaclass injection utility from six.
+
+ See: https://pythonhosted.org/six/
+ """
+ class metaclass(meta):
+ def __new__(cls, name, this_bases, d):
+ return meta(name, bases, d)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+class TestFailed(AssertionError):
+ def __init__(self, name, expected, got):
+ super(TestFailed, self).__init__(self)
+ diff = difflib.unified_diff(
+ expected.splitlines(), got.splitlines(),
+ fromfile='Expected', tofile='Got',
+ )
+ self.description = '{name}\n{diff}'.format(
+ name=name, diff='\n'.join(diff),
+ )
+
+ def __str__(self):
+ return self.description
+
+
+def _test_func(test_case):
+ flags = test_case.get('flags') or []
+ hoedown_proc = subprocess.Popen(
+ HOEDOWN + flags + [os.path.join(TEST_ROOT, test_case['input'])],
+ stdout=subprocess.PIPE,
+ )
+ stdoutdata = hoedown_proc.communicate()[0]
+
+ got_tidy_proc = subprocess.Popen(
+ TIDY, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ )
+ got = got_tidy_proc.communicate(input=stdoutdata)[0].strip()
+
+ expected_tidy_proc = subprocess.Popen(
+ TIDY + [os.path.join(TEST_ROOT, test_case['output'])],
+ stdout=subprocess.PIPE,
+ )
+ expected = expected_tidy_proc.communicate()[0].strip()
+
+ # Cleanup.
+ hoedown_proc.stdout.close()
+ got_tidy_proc.stdout.close()
+ expected_tidy_proc.stdout.close()
+
+ try:
+ assert expected == got
+ except AssertionError:
+ raise TestFailed(test_case['input'], expected, got)
+
+
+def _make_test(test_case):
+ return lambda self: _test_func(test_case)
+
+
+class MarkdownTestsMeta(type):
+ """Meta class for ``MarkdownTestCase`` to inject test cases on the fly.
+ """
+ def __new__(meta, name, bases, attrs):
+ with open(CONFIG_PATH) as f:
+ config = json.load(f)
+
+ for test in config['tests']:
+ input_name = test['input']
+ attr_name = 'test_' + SLUGIFY_PATTERN.sub(
+ '_', os.path.splitext(input_name)[0].lower(),
+ )
+ func = _make_test(test)
+ func.__doc__ = input_name
+ if test.get('skip', False):
+ func = unittest.skip(input_name)(func)
+ if test.get('fail', False):
+ func = unittest.expectsFailure(func)
+ attrs[attr_name] = func
+ return type.__new__(meta, name, bases, attrs)
+
+
+class MarkdownTests(with_metaclass(MarkdownTestsMeta, unittest.TestCase)):
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/runner.sh b/test/runner.sh
deleted file mode 100755
index d60eac5..0000000
--- a/test/runner.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-
-POSIXLY_CORRECT=1
-export POSIXLY_CORRECT
-
-TIDY='tidy --show-body-only 1 --quiet 1 --show-warnings 0'
-SCRIPT="$1"
-TESTDIR="$2"
-PASSED=0
-FAILED=0
-
-abort() {
- echo "Error: $*"
- exit 1
-}
-
-test -f "$SCRIPT" || abort "argument #1 invalid; not a file"
-test -x "$SCRIPT" || abort "argument #1 invalid; not executable"
-echo "" | "$SCRIPT" || abort "argument #1 invalid; script failed to run"
-test -d "$TESTDIR" || abort "argument #2 invalid; not a directory"
-
-for TEXT in "$TESTDIR"/*.text; do
- test -f "$TEXT" || abort "empty or invalid test directory"
- printf "$(basename "$TEXT" .text) ... "
- HTML=$(echo "$TEXT" | sed 's/\.text$/.html/')
-
- # We use mktemp to create an unpredictable, temporary filename.
- # The created file is immediately deleted, since we only want a
- # name to pass to mkfifo and "mktemp -u" is not portable.
- PIPE=$(mktemp .testpipe-XXXXXXXX)
- test -f "$PIPE" -a -n "$PIPE" || abort "mktemp failed"
- trap 'rm -f "$PIPE"' EXIT INT TERM HUP
- rm -f "$PIPE"
- mkfifo -m 0600 "$PIPE" || abort "unable to create named pipe"
-
- $SCRIPT "$TEXT" | $TIDY > "$PIPE" &
- DIFF=$($TIDY "$HTML" | diff "$PIPE" -)
- if test "$?" = 0; then
- PASSED=$(expr $PASSED + 1)
- echo OK
- else
- FAILED=$(expr $FAILED + 1)
- echo FAILED
- printf "\n$DIFF\n\n"
- fi
- rm -f "$PIPE"
-done
-
-printf "\n\n$PASSED passed; $FAILED failed.\n"
-test "$FAILED" = 0 || exit 1