md4c

C Markdown parser. Fast. SAX-like interface. Compliant to CommonMark specification.
git clone https://noulin.net/git/md4c.git
Log | Files | Refs | README | LICENSE

commit 44fc7cf20de463c6d59777fb2773b874a351b244
parent 7906f4ee1bb3505924713ce0386cb0c86854a6a0
Author: Martin Mitas <mity@morous.org>
Date:   Wed, 30 Aug 2017 16:06:44 +0200

Merge branch 'master' of https://github.com/mity/md4c

Diffstat:
M.travis.yml | 19++++++++++++++-----
MCMakeLists.txt | 4----
MREADME.md | 4++--
Mmd2html/render_html.c | 23+++++++++++------------
Mmd4c/md4c.c | 51++++++++++++++++++++++++++++++++++-----------------
Mtest/coverage.txt | 7+++++++
Mtest/pathological_tests.py | 4++--
7 files changed, 70 insertions(+), 42 deletions(-)

diff --git a/.travis.yml b/.travis.yml @@ -6,16 +6,25 @@ language: c compiler: - gcc +addons: + apt: + packages: + - python3 # for running tests + - lcov # for generating code coverage report + before_script: - mkdir build - cd build - - CFLAGS='-g -O0 --coverage' cmake -G 'Unix Makefiles' .. + - CFLAGS='--coverage -g -O0' cmake -DCMAKE_BUILD_TYPE=Debug -G 'Unix Makefiles' .. script: - - make + - make VERBOSE=1 after_success: - - sudo apt-get install python3 - - pip install --user cpp-coveralls - ../scripts/run-tests.sh - - ~/.local/bin/coveralls -r .. --gcov-options '\-lp' -i md4c -i md2html + # Creating report + - lcov --directory . --capture --output-file coverage.info # capture coverage info + - lcov --remove coverage.info '/usr/*' --output-file coverage.info # filter out system + - lcov --list coverage.info # debug info + # Uploading report to CodeCov + - bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -15,10 +15,6 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") - - # By default, CMake uses -O3 for Release builds. Lets stick with safer -O2: - string(REGEX REPLACE "(^| )-O[0-9a-z]+" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") elseif(MSVC) # Disable warnings about the so-called unsecured functions: add_definitions(/D_CRT_SECURE_NO_WARNINGS) diff --git a/README.md b/README.md @@ -1,7 +1,7 @@ [![Build status (travis-ci.com)](https://img.shields.io/travis/mity/md4c/master.svg?label=linux%20build)](https://travis-ci.org/mity/md4c) [![Build status (appveyor.com)](https://img.shields.io/appveyor/ci/mity/md4c/master.svg?label=windows%20build)](https://ci.appveyor.com/project/mity/md4c/branch/master) -[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/mity-md4c.svg)](https://scan.coverity.com/projects/mity-md4c) -[![Coverage](https://img.shields.io/coveralls/mity/md4c/master.svg)](https://coveralls.io/github/mity/md4c) +[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/mity-md4c.svg?label=coverity%20scan)](https://scan.coverity.com/projects/mity-md4c) +[![Codecov](https://img.shields.io/codecov/c/github/mity/md4c/master.svg?label=code%20coverage)](https://codecov.io/github/mity/md4c) # MD4C Readme diff --git a/md2html/render_html.c b/md2html/render_html.c @@ -49,6 +49,7 @@ struct MD_RENDER_HTML_tag { void (*process_output)(const MD_CHAR*, MD_SIZE, void*); void* userdata; unsigned flags; + int image_nesting_level; }; @@ -251,19 +252,17 @@ render_attribute(MD_RENDER_HTML* r, const MD_ATTRIBUTE* attr, } -static int image_nesting_level = 0; - static void render_open_ol_block(MD_RENDER_HTML* r, const MD_BLOCK_OL_DETAIL* det) { char buf[64]; if(det->start == 1) { - RENDER_LITERAL(r, "<ol>"); + RENDER_LITERAL(r, "<ol>\n"); return; } - snprintf(buf, sizeof(buf), "<ol start=\"%u\">", det->start); + snprintf(buf, sizeof(buf), "<ol start=\"%u\">\n", det->start); RENDER_LITERAL(r, buf); } @@ -318,7 +317,7 @@ render_open_img_span(MD_RENDER_HTML* r, const MD_SPAN_IMG_DETAIL* det) RENDER_LITERAL(r, "\" alt=\""); - image_nesting_level++; + r->image_nesting_level++; } static void @@ -331,7 +330,7 @@ render_close_img_span(MD_RENDER_HTML* r, const MD_SPAN_IMG_DETAIL* det) RENDER_LITERAL(r, "\">"); - image_nesting_level--; + r->image_nesting_level--; } @@ -400,7 +399,7 @@ enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { MD_RENDER_HTML* r = (MD_RENDER_HTML*) userdata; - if(image_nesting_level > 0) { + if(r->image_nesting_level > 0) { /* We are inside an image, i.e. rendering the ALT attribute of * <IMG> tag. */ return 0; @@ -423,10 +422,10 @@ leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { MD_RENDER_HTML* r = (MD_RENDER_HTML*) userdata; - if(image_nesting_level > 0) { + if(r->image_nesting_level > 0) { /* We are inside an image, i.e. rendering the ALT attribute of * <IMG> tag. */ - if(image_nesting_level == 1 && type == MD_SPAN_IMG) + if(r->image_nesting_level == 1 && type == MD_SPAN_IMG) render_close_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); return 0; } @@ -450,8 +449,8 @@ text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdat switch(type) { case MD_TEXT_NULLCHAR: render_utf8_codepoint(r, 0x0000, render_text); break; - case MD_TEXT_BR: RENDER_LITERAL(r, (image_nesting_level == 0 ? "<br>\n" : " ")); break; - case MD_TEXT_SOFTBR: RENDER_LITERAL(r, (image_nesting_level == 0 ? "\n" : " ")); break; + case MD_TEXT_BR: RENDER_LITERAL(r, (r->image_nesting_level == 0 ? "<br>\n" : " ")); break; + case MD_TEXT_SOFTBR: RENDER_LITERAL(r, (r->image_nesting_level == 0 ? "\n" : " ")); break; case MD_TEXT_HTML: render_text(r, text, size); break; case MD_TEXT_ENTITY: render_entity(r, text, size, render_html_escaped); break; default: render_html_escaped(r, text, size); break; @@ -473,7 +472,7 @@ md_render_html(const MD_CHAR* input, MD_SIZE input_size, void (*process_output)(const MD_CHAR*, MD_SIZE, void*), void* userdata, unsigned parser_flags, unsigned renderer_flags) { - MD_RENDER_HTML render = { process_output, userdata, renderer_flags }; + MD_RENDER_HTML render = { process_output, userdata, renderer_flags, 0 }; MD_RENDERER renderer = { enter_block_callback, diff --git a/md4c/md4c.c b/md4c/md4c.c @@ -1997,14 +1997,6 @@ md_is_link_destination_B(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, return TRUE; } -static inline int -md_is_link_destination(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, - OFF* p_contents_beg, OFF* p_contents_end) -{ - return (md_is_link_destination_A(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end) || - md_is_link_destination_B(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end)); -} - static int md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF* p_end, int* p_beg_line_index, int* p_end_line_index, @@ -2069,7 +2061,9 @@ md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, * If there is an error (cannot alloc memory for storing it), we return -1. */ static int -md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +md_is_link_reference_definition_helper( + MD_CTX* ctx, const MD_LINE* lines, int n_lines, + int (*is_link_dest_fn)(MD_CTX*, OFF, OFF, OFF*, OFF*, OFF*)) { OFF label_contents_beg; OFF label_contents_end; @@ -2113,8 +2107,8 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) } /* Link destination. */ - if(!md_is_link_destination(ctx, off, lines[line_index].end, - &off, &dest_contents_beg, &dest_contents_end)) + if(!is_link_dest_fn(ctx, off, lines[line_index].end, + &off, &dest_contents_beg, &dest_contents_end)) return FALSE; /* (Optional) title. Note we interpret it as an title only if nothing @@ -2200,6 +2194,16 @@ abort: return -1; } +static inline int +md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +{ + int ret; + ret = md_is_link_reference_definition_helper(ctx, lines, n_lines, md_is_link_destination_A); + if(ret == 0) + ret = md_is_link_reference_definition_helper(ctx, lines, n_lines, md_is_link_destination_B); + return ret; +} + static int md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF end, MD_LINK_ATTR* attr) @@ -2255,8 +2259,9 @@ abort: } static int -md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, - OFF beg, OFF* p_end, MD_LINK_ATTR* attr) +md_is_inline_link_spec_helper(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + OFF beg, OFF* p_end, MD_LINK_ATTR* attr, + int (*is_link_dest_fn)(MD_CTX*, OFF, OFF, OFF*, OFF*, OFF*)) { int line_index = 0; int tmp_line_index; @@ -2284,10 +2289,14 @@ md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, } /* (Optional) link destination. */ - if(!md_is_link_destination(ctx, off, lines[line_index].end, - &off, &attr->dest_beg, &attr->dest_end)) { - attr->dest_beg = off; - attr->dest_end = off; + if(!is_link_dest_fn(ctx, off, lines[line_index].end, + &off, &attr->dest_beg, &attr->dest_end)) { + if(is_link_dest_fn == md_is_link_destination_B) { + attr->dest_beg = off; + attr->dest_end = off; + } else { + return FALSE; + } } /* (Optional) title. */ @@ -2341,6 +2350,14 @@ abort: return ret; } +static inline int +md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, + OFF beg, OFF* p_end, MD_LINK_ATTR* attr) +{ + return md_is_inline_link_spec_helper(ctx, lines, n_lines, beg, p_end, attr, md_is_link_destination_A) || + md_is_inline_link_spec_helper(ctx, lines, n_lines, beg, p_end, attr, md_is_link_destination_B); +} + static void md_free_ref_defs(MD_CTX* ctx) { diff --git a/test/coverage.txt b/test/coverage.txt @@ -110,6 +110,13 @@ a*b**c* ```````````````````````````````` +### [Issue 24](https://github.com/mity/md4c/issues/24) +```````````````````````````````` example +[a](<b>c) +. +<p><a href="%3Cb%3Ec">a</a></p> +```````````````````````````````` + ## Code coverage diff --git a/test/pathological_tests.py b/test/pathological_tests.py @@ -63,8 +63,8 @@ pathological = { ("[t](/u) " * 50000, re.compile("(<a href=\"/u\">t</a> ?){50000}")), "many references": - ("".join(map(lambda x: ("[" + str(x) + "]: /url\n\n[0]\n\n"), range(1,65000))), - re.compile("^(<p>\\[0\\]</p>\r?\n)*$")), + ("".join(map(lambda x: ("[" + str(x) + "]: u\n"), range(1,50000 * 16))) + "[0] " * 50000, + re.compile("(\[0\] ){49999}")) } whitespace_re = re.compile('/s+/')