diff options
Diffstat (limited to 'src/inlines.c')
-rw-r--r-- | src/inlines.c | 272 |
1 files changed, 144 insertions, 128 deletions
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; |