diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/autolink.c | 18 | ||||
-rw-r--r-- | src/autolink.h | 37 | ||||
-rw-r--r-- | src/buffer.c | 205 | ||||
-rw-r--r-- | src/buffer.h | 85 | ||||
-rw-r--r-- | src/document.c | 398 | ||||
-rw-r--r-- | src/document.h | 154 | ||||
-rw-r--r-- | src/escape.c | 143 | ||||
-rw-r--r-- | src/escape.h | 13 | ||||
-rw-r--r-- | src/html.c | 346 | ||||
-rw-r--r-- | src/html.h | 59 | ||||
-rw-r--r-- | src/html_smartypants.c | 12 | ||||
-rw-r--r-- | src/stack.c | 66 | ||||
-rw-r--r-- | src/stack.h | 37 | ||||
-rw-r--r-- | src/version.c | 8 | ||||
-rw-r--r-- | src/version.h | 21 |
15 files changed, 933 insertions, 669 deletions
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, "&"); break; @@ -91,7 +93,7 @@ hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size) case '\'': HOEDOWN_BUFPUTSL(ob, "'"); 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 } @@ -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, " <a href=\"#fnref%d\" rev=\"footnote\">↩</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; } @@ -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 } |