diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blocks.c | 124 | ||||
-rw-r--r-- | src/buffer.c | 25 | ||||
-rw-r--r-- | src/buffer.h | 1 | ||||
-rw-r--r-- | src/cmark.h | 1 | ||||
-rw-r--r-- | src/html/html.c | 466 | ||||
-rw-r--r-- | src/inlines.c | 272 | ||||
-rw-r--r-- | src/references.c | 42 |
7 files changed, 568 insertions, 363 deletions
diff --git a/src/blocks.c b/src/blocks.c index 7613c82..ccb84a7 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -20,14 +20,14 @@ static node_block* make_block(int tag, int start_line, int start_column) node_block* e; e = calloc(1, sizeof(*e)); - if(e != NULL) { - e->tag = tag; - e->open = true; - e->start_line = start_line; - e->start_column = start_column; - e->end_line = start_line; - strbuf_init(&e->string_content, 32); - } + if(e != NULL) { + e->tag = tag; + e->open = true; + e->start_line = start_line; + e->start_column = start_column; + e->end_line = start_line; + strbuf_init(&e->string_content, 32); + } return e; } @@ -252,12 +252,12 @@ static node_block* add_child(node_block* parent, return child; } + // Free a node_block list and any children. void cmark_free_nodes(node_block *e) { node_block * next; while (e != NULL) { - next = e->next; free_inlines(e->inline_content); strbuf_free(&e->string_content); if (e->tag == BLOCK_FENCED_CODE) { @@ -265,31 +265,63 @@ void cmark_free_nodes(node_block *e) } else if (e->tag == BLOCK_DOCUMENT) { reference_map_free(e->as.document.refmap); } - cmark_free_nodes(e->children); + if (e->last_child) { + // Splice children into list + e->last_child->next = e->next; + e->next = e->children; + } + next = e->next; free(e); e = next; } } +typedef struct BlockStack { + struct BlockStack *previous; + node_block *next_sibling; +} block_stack; + // Walk through node_block and all children, recursively, parsing // string content into inline content where appropriate. void process_inlines(node_block* cur, reference_map *refmap) { - switch (cur->tag) { - case BLOCK_PARAGRAPH: - case BLOCK_ATX_HEADER: - case BLOCK_SETEXT_HEADER: - cur->inline_content = parse_inlines(&cur->string_content, refmap); - break; + block_stack* stack = NULL; + block_stack* newstack = NULL; + + while (cur != NULL) { + switch (cur->tag) { + case BLOCK_PARAGRAPH: + case BLOCK_ATX_HEADER: + case BLOCK_SETEXT_HEADER: + cur->inline_content = parse_inlines(&cur->string_content, refmap); + break; - default: - break; - } + default: + break; + } - node_block *child = cur->children; - while (child != NULL) { - process_inlines(child, refmap); - child = child->next; + if (cur->children) { + newstack = (block_stack*)malloc(sizeof(block_stack)); + if (newstack == NULL) return; + newstack->previous = stack; + stack = newstack; + stack->next_sibling = cur->next; + cur = cur->children; + } else { + cur = cur->next; + } + + while (cur == NULL && stack != NULL) { + cur = stack->next_sibling; + newstack = stack->previous; + free(stack); + stack = newstack; + } + } + while (stack != NULL) { + newstack = stack->previous; + free(stack); + stack = newstack; } } @@ -311,16 +343,16 @@ static int parse_list_marker(chunk *input, int pos, struct ListData ** dataptr) return 0; } data = calloc(1, sizeof(*data)); - if(data == NULL) { - return 0; - } else { - data->marker_offset = 0; // will be adjusted later - data->list_type = bullet; - data->bullet_char = c; - data->start = 1; - data->delimiter = period; - data->tight = false; - } + if(data == NULL) { + return 0; + } else { + data->marker_offset = 0; // will be adjusted later + data->list_type = bullet; + data->bullet_char = c; + data->start = 1; + data->delimiter = period; + data->tight = false; + } } else if (isdigit(c)) { int start = 0; @@ -336,16 +368,16 @@ static int parse_list_marker(chunk *input, int pos, struct ListData ** dataptr) return 0; } data = calloc(1, sizeof(*data)); - if(data == NULL) { - return 0; - } else { - data->marker_offset = 0; // will be adjusted later - data->list_type = ordered; - data->bullet_char = 0; - data->start = start; - data->delimiter = (c == '.' ? period : parens); - data->tight = false; - } + if(data == NULL) { + return 0; + } else { + data->marker_offset = 0; // will be adjusted later + data->list_type = ordered; + data->bullet_char = 0; + data->start = start; + data->delimiter = (c == '.' ? period : parens); + data->tight = false; + } } else { return 0; } @@ -438,8 +470,8 @@ static void chop_trailing_hashtags(chunk *ch) // Check for a be a space before the final #s: if (n != orig_n && n >= 0 && peek_at(ch, n) == ' ') { - ch->len = n; - chunk_rtrim(ch); + ch->len = n; + chunk_rtrim(ch); } } @@ -462,7 +494,7 @@ static void incorporate_line(strbuf *line, int line_number, node_block** curptr) // Add a newline to the end if not present: if (line->ptr[line->size - 1] != '\n') { - strbuf_putc(line, '\n'); + strbuf_putc(line, '\n'); } input.data = line->ptr; input.len = line->size; diff --git a/src/buffer.c b/src/buffer.c index 1cdcae8..a5139fa 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -291,19 +291,11 @@ void strbuf_drop(strbuf *buf, int n) } } -void strbuf_trim(strbuf *buf) +void strbuf_rtrim(strbuf *buf) { - int i = 0; - if (!buf->size) return; - while (i < buf->size && isspace(buf->ptr[i])) - i++; - - strbuf_drop(buf, i); - - /* rtrim */ while (buf->size > 0) { if (!isspace(buf->ptr[buf->size - 1])) break; @@ -314,6 +306,21 @@ void strbuf_trim(strbuf *buf) buf->ptr[buf->size] = '\0'; } +void strbuf_trim(strbuf *buf) +{ + int i = 0; + + if (!buf->size) + return; + + while (i < buf->size && isspace(buf->ptr[i])) + i++; + + strbuf_drop(buf, i); + + strbuf_rtrim(buf); +} + // Destructively modify string, collapsing consecutive // space and newline characters into a single space. void strbuf_normalize_whitespace(strbuf *s) diff --git a/src/buffer.h b/src/buffer.h index 1bc1eee..63d6202 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -107,6 +107,7 @@ int strbuf_strchr(const strbuf *buf, int c, int pos); int strbuf_strrchr(const strbuf *buf, int c, int pos); void strbuf_drop(strbuf *buf, int n); void strbuf_truncate(strbuf *buf, int len); +void strbuf_rtrim(strbuf *buf); void strbuf_trim(strbuf *buf); void strbuf_normalize_whitespace(strbuf *s); void strbuf_unescape(strbuf *s); diff --git a/src/cmark.h b/src/cmark.h index e34df72..ff2f9a2 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -9,7 +9,6 @@ #define VERSION "0.1" #define CODE_INDENT 4 -#define STACK_LIMIT 1000 struct node_inl { enum { diff --git a/src/html/html.c b/src/html/html.c index fde1cb4..5f08506 100644 --- a/src/html/html.c +++ b/src/html/html.c @@ -8,6 +8,76 @@ #include "debug.h" #include "html/houdini.h" +typedef struct RenderStack { + struct RenderStack *previous; + char* literal; + union { + node_inl *inl; + node_block *block; + } next_sibling; + bool tight; + bool trim; +} render_stack; + +static void free_render_stack(render_stack * rstack) +{ + render_stack * tempstack; + while (rstack) { + tempstack = rstack; + rstack = rstack->previous; + free(tempstack); + } +} + +static render_stack* push_inline(render_stack* rstack, + node_inl* inl, + char* literal) +{ + render_stack* newstack; + newstack = (render_stack*)malloc(sizeof(render_stack)); + if (newstack == NULL) { + return NULL; + } + newstack->previous = rstack; + newstack->next_sibling.inl = inl; + newstack->literal = literal; + newstack->tight = false; + newstack->trim = false; + return newstack; +} + +static render_stack* push_block(render_stack* rstack, + node_block* block, + char* literal, + bool tight, + bool trim) +{ + render_stack* newstack; + newstack = (render_stack*)malloc(sizeof(render_stack)); + if (newstack == NULL) { + return NULL; + } + newstack->previous = rstack; + newstack->next_sibling.block = block; + newstack->literal = literal; + newstack->tight = tight; + newstack->trim = trim; + return newstack; +} + +static render_stack* pop_render_stack(render_stack* rstack) +{ + render_stack* top = rstack; + + if (rstack == NULL) { + return NULL; + } + rstack = rstack->previous; + top->previous = NULL; + free_render_stack(top); + return rstack; +} + // Functions to convert node_block and inline lists to HTML strings. static void escape_html(strbuf *dest, const unsigned char *source, int length) @@ -33,196 +103,276 @@ static inline void cr(strbuf *html) } // Convert an inline list to HTML. Returns 0 on success, and sets result. -static void inlines_to_html(strbuf *html, node_inl* ils) +static void inlines_to_plain_html(strbuf *html, node_inl* ils) { - strbuf scrap = GH_BUF_INIT; + node_inl* children; + bool visit_children; + render_stack* rstack = NULL; while(ils != NULL) { + visit_children = false; switch(ils->tag) { - case INL_STRING: - escape_html(html, ils->content.literal.data, ils->content.literal.len); - break; - - case INL_LINEBREAK: - strbuf_puts(html, "<br />\n"); - break; - - case INL_SOFTBREAK: - strbuf_putc(html, '\n'); - break; - - case INL_CODE: - strbuf_puts(html, "<code>"); - escape_html(html, ils->content.literal.data, ils->content.literal.len); - strbuf_puts(html, "</code>"); - break; - - case INL_RAW_HTML: - strbuf_put(html, - ils->content.literal.data, - ils->content.literal.len); - break; - - case INL_LINK: - strbuf_puts(html, "<a href=\""); - if (ils->content.linkable.url) - escape_href(html, ils->content.linkable.url, -1); - - if (ils->content.linkable.title) { - strbuf_puts(html, "\" title=\""); - escape_html(html, ils->content.linkable.title, -1); - } + case INL_STRING: + case INL_CODE: + case INL_RAW_HTML: + escape_html(html, ils->content.literal.data, ils->content.literal.len); + break; + + case INL_LINEBREAK: + case INL_SOFTBREAK: + strbuf_putc(html, '\n'); + break; + + case INL_LINK: + case INL_IMAGE: + children = ils->content.inlines; + visit_children = true; + rstack = push_inline(rstack, ils->next, ""); + break; + + case INL_STRONG: + case INL_EMPH: + children = ils->content.inlines; + visit_children = true; + rstack = push_inline(rstack, ils->next, ""); + break; + } + if (visit_children) { + ils = children; + } else { + ils = ils->next; + } + while (ils == NULL && rstack != NULL) { + strbuf_puts(html, rstack->literal); + ils = rstack->next_sibling.inl; + rstack = pop_render_stack(rstack); + } + } - strbuf_puts(html, "\">"); - inlines_to_html(html, ils->content.inlines); - strbuf_puts(html, "</a>"); - break; - - case INL_IMAGE: - strbuf_puts(html, "<img src=\""); - if (ils->content.linkable.url) - escape_href(html, ils->content.linkable.url, -1); - - inlines_to_html(&scrap, ils->content.inlines); - strbuf_puts(html, "\" alt=\""); - if (scrap.size) - escape_html(html, scrap.ptr, scrap.size); - strbuf_clear(&scrap); - - if (ils->content.linkable.title) { - strbuf_puts(html, "\" title=\""); - escape_html(html, ils->content.linkable.title, -1); - } + free_render_stack(rstack); +} - strbuf_puts(html, "\"/>"); - break; - case INL_STRONG: - strbuf_puts(html, "<strong>"); - inlines_to_html(html, ils->content.inlines); - strbuf_puts(html, "</strong>"); - break; +// Convert an inline list to HTML. Returns 0 on success, and sets result. +static void inlines_to_html(strbuf *html, node_inl* ils) +{ + node_inl* children; + render_stack* rstack = NULL; - case INL_EMPH: - strbuf_puts(html, "<em>"); - inlines_to_html(html, ils->content.inlines); - strbuf_puts(html, "</em>"); - break; + while(ils != NULL) { + children = NULL; + switch(ils->tag) { + case INL_STRING: + escape_html(html, ils->content.literal.data, ils->content.literal.len); + break; + + case INL_LINEBREAK: + strbuf_puts(html, "<br />\n"); + break; + + case INL_SOFTBREAK: + strbuf_putc(html, '\n'); + break; + + case INL_CODE: + strbuf_puts(html, "<code>"); + escape_html(html, ils->content.literal.data, ils->content.literal.len); + strbuf_puts(html, "</code>"); + break; + + case INL_RAW_HTML: + strbuf_put(html, + ils->content.literal.data, + ils->content.literal.len); + break; + + case INL_LINK: + strbuf_puts(html, "<a href=\""); + if (ils->content.linkable.url) + escape_href(html, ils->content.linkable.url, -1); + + if (ils->content.linkable.title) { + strbuf_puts(html, "\" title=\""); + escape_html(html, ils->content.linkable.title, -1); + } + + strbuf_puts(html, "\">"); + children = ils->content.inlines; + rstack = push_inline(rstack, ils->next, "</a>"); + break; + + case INL_IMAGE: + strbuf_puts(html, "<img src=\""); + if (ils->content.linkable.url) + escape_href(html, ils->content.linkable.url, -1); + + strbuf_puts(html, "\" alt=\""); + inlines_to_plain_html(html, ils->content.inlines); + + if (ils->content.linkable.title) { + strbuf_puts(html, "\" title=\""); + escape_html(html, ils->content.linkable.title, -1); + } + + strbuf_puts(html, "\"/>"); + break; + + case INL_STRONG: + strbuf_puts(html, "<strong>"); + children = ils->content.inlines; + rstack = push_inline(rstack, ils->next, "</strong>"); + break; + + case INL_EMPH: + strbuf_puts(html, "<em>"); + children = ils->content.inlines; + rstack = push_inline(rstack, ils->next, "</em>"); + break; + } + if (children) { + ils = children; + } else { + ils = ils->next; + } + while (ils == NULL && rstack != NULL) { + strbuf_puts(html, rstack->literal); + ils = rstack->next_sibling.inl; + rstack = pop_render_stack(rstack); } - ils = ils->next; } - strbuf_free(&scrap); + free_render_stack(rstack); } // Convert a node_block list to HTML. Returns 0 on success, and sets result. -static void blocks_to_html(strbuf *html, node_block *b, bool tight) +static void blocks_to_html(strbuf *html, node_block *b) { struct ListData *data; + render_stack* rstack = NULL; + bool visit_children = false; + bool tight = false; while(b != NULL) { + visit_children = false; switch(b->tag) { - case BLOCK_DOCUMENT: - blocks_to_html(html, b->children, false); - break; - - case BLOCK_PARAGRAPH: - if (tight) { - inlines_to_html(html, b->inline_content); - } else { - cr(html); - strbuf_puts(html, "<p>"); - inlines_to_html(html, b->inline_content); - strbuf_puts(html, "</p>\n"); - } - break; - - case BLOCK_BQUOTE: - cr(html); - strbuf_puts(html, "<blockquote>\n"); - blocks_to_html(html, b->children, false); - strbuf_puts(html, "</blockquote>\n"); - break; - - case BLOCK_LIST_ITEM: - cr(html); - strbuf_puts(html, "<li>"); - blocks_to_html(html, b->children, tight); - strbuf_trim(html); /* TODO: rtrim */ - strbuf_puts(html, "</li>\n"); - break; - - case BLOCK_LIST: - // make sure a list starts at the beginning of the line: - cr(html); - data = &(b->as.list); - - if (data->start > 1) { - strbuf_printf(html, "<%s start=\"%d\">\n", - data->list_type == bullet ? "ul" : "ol", - data->start); - } else { - strbuf_puts(html, data->list_type == bullet ? "<ul>\n" : "<ol>\n"); - } + case BLOCK_DOCUMENT: + rstack = push_block(rstack, b->next, "", false, false); + visit_children = true; + break; - blocks_to_html(html, b->children, data->tight); - strbuf_puts(html, data->list_type == bullet ? "</ul>" : "</ol>"); - strbuf_putc(html, '\n'); - break; - - case BLOCK_ATX_HEADER: - case BLOCK_SETEXT_HEADER: - cr(html); - strbuf_printf(html, "<h%d>", b->as.header.level); + case BLOCK_PARAGRAPH: + if (tight) { inlines_to_html(html, b->inline_content); - strbuf_printf(html, "</h%d>\n", b->as.header.level); - break; - - case BLOCK_INDENTED_CODE: - case BLOCK_FENCED_CODE: + } else { cr(html); - - strbuf_puts(html, "<pre><code"); - - if (b->tag == BLOCK_FENCED_CODE) { - strbuf *info = &b->as.code.info; - - if (strbuf_len(info) > 0) { - int first_tag = strbuf_strchr(info, ' ', 0); - if (first_tag < 0) - first_tag = strbuf_len(info); - - strbuf_puts(html, " class=\"language-"); - escape_html(html, info->ptr, first_tag); - strbuf_putc(html, '"'); - } + strbuf_puts(html, "<p>"); + inlines_to_html(html, b->inline_content); + strbuf_puts(html, "</p>\n"); + } + break; + + case BLOCK_BQUOTE: + cr(html); + strbuf_puts(html, "<blockquote>\n"); + rstack = push_block(rstack, b->next, "</blockquote>\n", tight, false); + tight = false; + visit_children = true; + break; + + case BLOCK_LIST_ITEM: + cr(html); + strbuf_puts(html, "<li>"); + rstack = push_block(rstack, b->next, "</li>\n", tight, true); + visit_children = true; + break; + + case BLOCK_LIST: + // make sure a list starts at the beginning of the line: + cr(html); + data = &(b->as.list); + + if (data->start > 1) { + strbuf_printf(html, "<%s start=\"%d\">\n", + data->list_type == bullet ? "ul" : "ol", + data->start); + } else { + strbuf_puts(html, data->list_type == bullet ? "<ul>\n" : "<ol>\n"); + } + + rstack = push_block(rstack, b->next, + data->list_type == bullet ? + "\n</ul>\n" : "\n</ol>\n", tight, false); + tight = data->tight; + visit_children = true; + break; + + case BLOCK_ATX_HEADER: + case BLOCK_SETEXT_HEADER: + cr(html); + strbuf_printf(html, "<h%d>", b->as.header.level); + inlines_to_html(html, b->inline_content); + strbuf_printf(html, "</h%d>\n", b->as.header.level); + break; + + case BLOCK_INDENTED_CODE: + case BLOCK_FENCED_CODE: + cr(html); + + strbuf_puts(html, "<pre><code"); + + if (b->tag == BLOCK_FENCED_CODE) { + strbuf *info = &b->as.code.info; + + if (strbuf_len(info) > 0) { + int first_tag = strbuf_strchr(info, ' ', 0); + if (first_tag < 0) + first_tag = strbuf_len(info); + + strbuf_puts(html, " class=\"language-"); + escape_html(html, info->ptr, first_tag); + strbuf_putc(html, '"'); } + } - strbuf_putc(html, '>'); - escape_html(html, b->string_content.ptr, b->string_content.size); - strbuf_puts(html, "</code></pre>\n"); - break; + strbuf_putc(html, '>'); + escape_html(html, b->string_content.ptr, b->string_content.size); + strbuf_puts(html, "</code></pre>\n"); + break; - case BLOCK_HTML: - strbuf_put(html, b->string_content.ptr, b->string_content.size); - break; + case BLOCK_HTML: + strbuf_put(html, b->string_content.ptr, b->string_content.size); + break; - case BLOCK_HRULE: - strbuf_puts(html, "<hr />\n"); - break; + case BLOCK_HRULE: + strbuf_puts(html, "<hr />\n"); + break; - case BLOCK_REFERENCE_DEF: - break; + case BLOCK_REFERENCE_DEF: + break; - default: - assert(false); + default: + assert(false); + } + if (visit_children) { + b = b->children; + } else { + b = b->next; + } + while (b == NULL && rstack != NULL) { + strbuf_puts(html, rstack->literal); + if (rstack->trim) { + strbuf_rtrim(html); + } + tight = rstack->tight; + b = rstack->next_sibling.block; + rstack = pop_render_stack(rstack); } - - b = b->next; } + + free_render_stack(rstack); } void cmark_render_html(strbuf *html, node_block *root) { - blocks_to_html(html, root, false); + blocks_to_html(html, root); } diff --git a/src/inlines.c b/src/inlines.c index 9216979..810230c 100644 --- a/src/inlines.c +++ b/src/inlines.c @@ -9,6 +9,7 @@ #include "utf8.h" #include "scanners.h" #include "inlines.h" +#include "debug.h" typedef struct InlineStack { struct InlineStack *previous; @@ -41,9 +42,9 @@ static unsigned char *bufdup(const unsigned char *buf) if (buf) { int len = strlen((char *)buf); new = calloc(len + 1, sizeof(*new)); - if(new != NULL) { - memcpy(new, buf, len + 1); - } + if(new != NULL) { + memcpy(new, buf, len + 1); + } } return new; @@ -52,13 +53,13 @@ static unsigned char *bufdup(const unsigned char *buf) static inline node_inl *make_link_(node_inl *label, unsigned char *url, unsigned char *title) { node_inl* e = calloc(1, sizeof(*e)); - if(e != NULL) { - e->tag = INL_LINK; - e->content.linkable.label = label; - e->content.linkable.url = url; - e->content.linkable.title = title; - e->next = NULL; - } + if(e != NULL) { + e->tag = INL_LINK; + e->content.linkable.label = label; + e->content.linkable.url = url; + e->content.linkable.title = title; + e->next = NULL; + } return e; } @@ -81,11 +82,11 @@ inline static node_inl* make_link(node_inl* label, chunk url, chunk title) inline static node_inl* make_inlines(int t, node_inl* contents) { node_inl * e = calloc(1, sizeof(*e)); - if(e != NULL) { - e->tag = t; - e->content.inlines = contents; - e->next = NULL; - } + if(e != NULL) { + e->tag = t; + e->content.inlines = contents; + e->next = NULL; + } return e; } @@ -93,11 +94,11 @@ inline static node_inl* make_inlines(int t, node_inl* contents) inline static node_inl* make_literal(int t, chunk s) { node_inl * e = calloc(1, sizeof(*e)); - if(e != NULL) { - e->tag = t; - e->content.literal = s; - e->next = NULL; - } + if(e != NULL) { + e->tag = t; + e->content.literal = s; + e->next = NULL; + } return e; } @@ -105,10 +106,10 @@ inline static node_inl* make_literal(int t, chunk s) inline static node_inl* make_simple(int t) { node_inl* e = calloc(1, sizeof(*e)); - if(e != NULL) { - e->tag = t; - e->next = NULL; - } + if(e != NULL) { + e->tag = t; + e->next = NULL; + } return e; } @@ -121,10 +122,28 @@ inline static node_inl* make_simple(int t) #define make_emph(contents) make_inlines(INL_EMPH, contents) #define make_strong(contents) make_inlines(INL_STRONG, contents) -// Free an inline list. +// Utility function used by free_inlines +void splice_into_list(node_inl* e, node_inl* children) { + node_inl * tmp; + if (children) { + tmp = children; + // Find last child + while (tmp->next) { + tmp = tmp->next; + } + // Splice children into list + tmp->next = e->next; + e->next = children; + } + return ; +} + +// Free an inline list. Avoid recursion to prevent stack overflows +// on deeply nested structures. extern void free_inlines(node_inl* e) { node_inl * next; + while (e != NULL) { switch (e->tag){ case INL_STRING: @@ -139,13 +158,14 @@ extern void free_inlines(node_inl* e) case INL_IMAGE: free(e->content.linkable.url); free(e->content.linkable.title); - free_inlines(e->content.linkable.label); + splice_into_list(e, e->content.linkable.label); break; case INL_EMPH: case INL_STRONG: - free_inlines(e->content.inlines); + splice_into_list(e, e->content.inlines); break; default: + log_warn("Unknown inline tag %d", e->tag); break; } next = e->next; @@ -297,8 +317,8 @@ static int scan_delims(subject* subj, unsigned char c, bool * can_open, bool * c advance(subj); } char_after = peek_char(subj); - *can_open = numdelims > 0 && numdelims <= 3 && !isspace(char_after); - *can_close = numdelims > 0 && numdelims <= 3 && !isspace(char_before); + *can_open = numdelims > 0 && !isspace(char_after); + *can_close = numdelims > 0 && !isspace(char_before); if (c == '_') { *can_open = *can_open && !isalnum(char_before); *can_close = *can_close && !isalnum(char_after); @@ -308,13 +328,13 @@ static int scan_delims(subject* subj, unsigned char c, bool * can_open, bool * c static void free_openers(subject* subj, inline_stack* istack) { - inline_stack * tempstack; - while (subj->emphasis_openers != istack) { - tempstack = subj->emphasis_openers; - subj->emphasis_openers = subj->emphasis_openers->previous; - subj->emphasis_nestlevel--; - free(tempstack); - } + inline_stack * tempstack; + while (subj->emphasis_openers != istack) { + tempstack = subj->emphasis_openers; + subj->emphasis_openers = subj->emphasis_openers->previous; + subj->emphasis_nestlevel--; + free(tempstack); + } } // Parse strong/emph or a fallback. @@ -324,6 +344,7 @@ static node_inl* handle_strong_emph(subject* subj, unsigned char c, node_inl **l bool can_open, can_close; int numdelims; int useDelims; + int openerDelims; inline_stack * istack; node_inl * inl; node_inl * emph; @@ -332,81 +353,84 @@ static node_inl* handle_strong_emph(subject* subj, unsigned char c, node_inl **l numdelims = scan_delims(subj, c, &can_open, &can_close); if (can_close) - { - // walk the stack and find a matching opener, if there is one - istack = subj->emphasis_openers; - while (true) { - if (istack == NULL) - goto cannotClose; - - if (istack->delim_char == c) - break; + // walk the stack and find a matching opener, if there is one + istack = subj->emphasis_openers; + while (true) + { + if (istack == NULL) + goto cannotClose; + + if (istack->delim_char == c) + break; + + istack = istack->previous; + } + + // calculate the actual number of delimeters used from this closer + openerDelims = istack->delim_count; + if (numdelims < 3 || openerDelims < 3) { + useDelims = numdelims <= openerDelims ? numdelims : openerDelims; + } else { // (numdelims >= 3 && openerDelims >= 3) + useDelims = numdelims % 2 == 0 ? 2 : 1; + } - istack = istack->previous; + if (istack->delim_count == useDelims) + { + // the opener is completely used up - remove the stack entry and reuse the inline element + inl = istack->first_inline; + inl->tag = useDelims == 1 ? INL_EMPH : INL_STRONG; + chunk_free(&inl->content.literal); + inl->content.inlines = inl->next; + inl->next = NULL; + + // remove this opener and all later ones from stack: + free_openers(subj, istack->previous); + *last = inl; + } + else + { + // the opener will only partially be used - stack entry remains (truncated) and a new inline is added. + inl = istack->first_inline; + istack->delim_count -= useDelims; + inl->content.literal.len = istack->delim_count; + + emph = useDelims == 1 ? make_emph(inl->next) : make_strong(inl->next); + inl->next = emph; + + // remove all later openers from stack: + free_openers(subj, istack); + + *last = emph; + } + + // if the closer was not fully used, move back a char or two and try again. + if (useDelims < numdelims) + { + subj->pos = subj->pos - numdelims + useDelims; + return NULL; + } + + return NULL; // make_str(chunk_literal("")); } - // calculate the actual number of delimeters used from this closer - useDelims = istack->delim_count; - if (useDelims == 3) useDelims = numdelims == 3 ? 1 : numdelims; - else if (useDelims > numdelims) useDelims = 1; - - if (istack->delim_count == useDelims) - { - // the opener is completely used up - remove the stack entry and reuse the inline element - inl = istack->first_inline; - inl->tag = useDelims == 1 ? INL_EMPH : INL_STRONG; - chunk_free(&inl->content.literal); - inl->content.inlines = inl->next; - inl->next = NULL; - - // remove this opener and all later ones from stack: - free_openers(subj, istack->previous); - *last = inl; - } - else - { - // the opener will only partially be used - stack entry remains (truncated) and a new inline is added. - inl = istack->first_inline; - istack->delim_count -= useDelims; - inl->content.literal.len = istack->delim_count; - - emph = useDelims == 1 ? make_emph(inl->next) : make_strong(inl->next); - inl->next = emph; - - // remove all later openers from stack: - free_openers(subj, istack); - - *last = emph; - } + cannotClose: + inl_text = make_str(chunk_dup(&subj->input, subj->pos - numdelims, numdelims)); - // if the closer was not fully used, move back a char or two and try again. - if (useDelims < numdelims) + if (can_open) { - subj->pos = subj->pos - numdelims + useDelims; - return handle_strong_emph(subj, c, last); + istack = (inline_stack*)malloc(sizeof(inline_stack)); + if (istack == NULL) { + return NULL; + } + istack->delim_count = numdelims; + istack->delim_char = c; + istack->first_inline = inl_text; + istack->previous = subj->emphasis_openers; + subj->emphasis_openers = istack; + subj->emphasis_nestlevel++; } - return NULL; // make_str(chunk_literal("")); - } - -cannotClose: - inl_text = make_str(chunk_dup(&subj->input, subj->pos - numdelims, numdelims)); - - if (can_open && subj->emphasis_nestlevel < STACK_LIMIT) - { - istack = (inline_stack*)malloc(sizeof(inline_stack)); - if (istack == NULL) { - return NULL; - } - istack->delim_count = numdelims; - istack->delim_char = c; - istack->first_inline = inl_text; - istack->previous = subj->emphasis_openers; - subj->emphasis_openers = istack; - subj->emphasis_nestlevel++; - } - return inl_text; } @@ -438,7 +462,7 @@ static node_inl* handle_entity(subject* subj) len = houdini_unescape_ent(&ent, subj->input.data + subj->pos, subj->input.len - subj->pos - ); + ); if (len == 0) return make_str(chunk_literal("&")); @@ -538,9 +562,9 @@ static node_inl* handle_pointy_brace(subject* subj) subj->pos += matchlen; return make_autolink( - make_str_with_entities(&contents), - contents, 0 - ); + make_str_with_entities(&contents), + contents, 0 + ); } // next try to match an email autolink @@ -550,9 +574,9 @@ static node_inl* handle_pointy_brace(subject* subj) subj->pos += matchlen; return make_autolink( - make_str_with_entities(&contents), - contents, 1 - ); + make_str_with_entities(&contents), + contents, 1 + ); } // finally, try to match an html tag @@ -594,8 +618,7 @@ static int link_label(subject* subj, chunk *raw_label) advance(subj); // advance past [ unsigned char c; - while ((c = peek_char(subj)) && - (c != ']' || (nestlevel > 0 && nestlevel < STACK_LIMIT))) { + while ((c = peek_char(subj)) && (c != ']' || nestlevel > 0)) { switch (c) { case '`': tmp = handle_backticks(subj); @@ -646,11 +669,12 @@ static node_inl* handle_left_bracket(subject* subj) int n; int sps; int found_label; - int endlabel, starturl, endurl, starttitle, endtitle, endall; + int endlabel, startpos, starturl, endurl, starttitle, endtitle, endall; chunk rawlabel; chunk url, title; + startpos = subj->pos; found_label = link_label(subj, &rawlabel); endlabel = subj->pos; @@ -679,13 +703,7 @@ static node_inl* handle_left_bracket(subject* subj) return make_link(lab, url, title); } else { - // if we get here, we matched a label but didn't get further: - subj->pos = endlabel; - lab = parse_chunk_inlines(&rawlabel, subj->refmap); - result = append_inlines(make_str(chunk_literal("[")), - append_inlines(lab, - make_str(chunk_literal("]")))); - return result; + goto noMatch; } } else { chunk rawlabel_tmp; @@ -710,16 +728,14 @@ static node_inl* handle_left_bracket(subject* subj) lab = parse_chunk_inlines(&rawlabel, NULL); result = make_ref_link(lab, ref); } else { - subj->pos = endlabel; - lab = parse_chunk_inlines(&rawlabel, subj->refmap); - result = append_inlines(make_str(chunk_literal("[")), - append_inlines(lab, make_str(chunk_literal("]")))); + goto noMatch; } return result; } } +noMatch: // If we fall through to here, it means we didn't match a link: - advance(subj); // advance past [ + subj->pos = startpos + 1; // advance past [ return make_str(chunk_literal("[")); } @@ -755,9 +771,9 @@ extern node_inl* parse_inlines_while(subject* subj, int (*f)(subject*)) node_inl** last = &result; node_inl* first = NULL; while ((*f)(subj) && parse_inline(subj, last)) { - if (!first) { - first = *last; - } + if (!first) { + first = *last; + } } inline_stack* istack = subj->emphasis_openers; diff --git a/src/references.c b/src/references.c index 04b9025..5ba4b24 100644 --- a/src/references.c +++ b/src/references.c @@ -16,12 +16,12 @@ refhash(const unsigned char *link_ref) static void reference_free(reference *ref) { - if(ref != NULL) { - free(ref->label); - free(ref->url); - free(ref->title); - free(ref); - } + if(ref != NULL) { + free(ref->label); + free(ref->url); + free(ref->title); + free(ref); + } } // normalize reference: collapse internal whitespace to single space, @@ -33,8 +33,8 @@ static unsigned char *normalize_reference(chunk *ref) strbuf normalized = GH_BUF_INIT; unsigned char *result; - if(ref == NULL) - return NULL; + if(ref == NULL) + return NULL; if (ref->len == 0) return NULL; @@ -50,7 +50,7 @@ static unsigned char *normalize_reference(chunk *ref) free(result); return NULL; } - + return result; } @@ -81,15 +81,15 @@ extern void reference_create(reference_map *map, chunk *label, chunk *url, chunk return; ref = calloc(1, sizeof(*ref)); - if(ref != NULL) { - ref->label = reflabel; - ref->hash = refhash(ref->label); - ref->url = clean_url(url); - ref->title = clean_title(title); - ref->next = NULL; - - add_reference(map, ref); - } + if(ref != NULL) { + ref->label = reflabel; + ref->hash = refhash(ref->label); + ref->url = clean_url(url); + ref->title = clean_title(title); + ref->next = NULL; + + add_reference(map, ref); + } } // Returns reference if refmap contains a reference with matching @@ -125,8 +125,8 @@ void reference_map_free(reference_map *map) { unsigned int i; - if(map == NULL) - return; + if(map == NULL) + return; for (i = 0; i < REFMAP_SIZE; ++i) { reference *ref = map->table[i]; @@ -144,5 +144,5 @@ void reference_map_free(reference_map *map) reference_map *reference_map_new(void) { - return calloc(1, sizeof(reference_map)); + return calloc(1, sizeof(reference_map)); } |