upgrade md4c to 601885f738

This commit is contained in:
Rasmus Andersson 2020-10-16 15:07:19 -07:00
parent 9941275ff7
commit 9e251082ee
7 changed files with 482 additions and 399 deletions

31
markdown.d.ts vendored
View file

@ -6,22 +6,16 @@ export function parse(s :Source, o? :ParseOptions & { asMemoryView? :never|false
export function parse(s :Source, o? :ParseOptions & { asMemoryView :true }) :Uint8Array export function parse(s :Source, o? :ParseOptions & { asMemoryView :true }) :Uint8Array
/** Markdown source code can be provided as a JavaScript string or UTF8 encoded data */ /** Markdown source code can be provided as a JavaScript string or UTF8 encoded data */
type Source = string|ArrayLike<number> type Source = string | ArrayLike<number>
/** Options for the parse function */ /** Options for the parse function */
export interface ParseOptions { export interface ParseOptions {
/** /** Customize parsing. Defaults to ParseFlags.DEFAULT */
* Customize parsing.
* If not provided, the following flags are used, equating to github-style parsing:
* COLLAPSE_WHITESPACE
* PERMISSIVE_ATX_HEADERS
* PERMISSIVE_URL_AUTO_LINKS
* STRIKETHROUGH
* TABLES
* TASK_LISTS
*/
parseFlags? :ParseFlags parseFlags? :ParseFlags
/** Select output format. Defaults to "html" */
format? : "html" | "xhtml"
/** /**
* asMemoryView=true causes parse() to return a view of heap memory as a Uint8Array, * asMemoryView=true causes parse() to return a view of heap memory as a Uint8Array,
* instead of a string. * instead of a string.
@ -51,7 +45,18 @@ export enum ParseFlags {
/** Enable tables extension. */ TABLES, /** Enable tables extension. */ TABLES,
/** Enable task list extension. */ TASK_LISTS, /** Enable task list extension. */ TASK_LISTS,
/** Enable wiki links extension. */ WIKI_LINKS, /** Enable wiki links extension. */ WIKI_LINKS,
/** Enable underline extension (disables '_' for emphasis) */ UNDERLINE,
/** Default flags */ DEFAULT, /** Default flags are:
/** Shorthand for NO_HTML_BLOCKS | NO_HTML_SPANS */ NO_HTML, * COLLAPSE_WHITESPACE |
* PERMISSIVE_ATX_HEADERS |
* PERMISSIVE_URL_AUTO_LINKS |
* STRIKETHROUGH |
* TABLES |
* TASK_LISTS
*/
DEFAULT,
/** Shorthand for NO_HTML_BLOCKS | NO_HTML_SPANS */
NO_HTML,
} }

View file

@ -34,6 +34,7 @@ typedef struct HtmlRenderer_st {
WBuf* outbuf; WBuf* outbuf;
int imgnest; int imgnest;
int addanchor; int addanchor;
u32 flags;
} HtmlRenderer; } HtmlRenderer;
@ -246,7 +247,7 @@ static void render_close_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det
render_literal(r, "\" title=\""); render_literal(r, "\" title=\"");
render_attribute(r, &det->title); render_attribute(r, &det->title);
} }
render_literal(r, "\">"); render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "\"/>" : "\">");
r->imgnest--; r->imgnest--;
} }
@ -273,7 +274,7 @@ static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
case MD_BLOCK_UL: render_literal(r, "<ul>\n"); break; case MD_BLOCK_UL: render_literal(r, "<ul>\n"); break;
case MD_BLOCK_OL: render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL*)detail); break; case MD_BLOCK_OL: render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL*)detail); break;
case MD_BLOCK_LI: render_open_li_block(r, (const MD_BLOCK_LI_DETAIL*)detail); break; case MD_BLOCK_LI: render_open_li_block(r, (const MD_BLOCK_LI_DETAIL*)detail); break;
case MD_BLOCK_HR: render_literal(r, "<hr>\n"); break; case MD_BLOCK_HR: render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr/>\n" : "<hr>\n"); break;
case MD_BLOCK_H: case MD_BLOCK_H:
{ {
render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]);
@ -324,14 +325,28 @@ static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
HtmlRenderer* r = (HtmlRenderer*) userdata; HtmlRenderer* r = (HtmlRenderer*) userdata;
if(r->imgnest > 0) { if(r->imgnest > 0) {
/* We are inside an image, i.e. rendering the ALT attribute of /* We are inside a Markdown image label. Markdown allows to use any
* <IMG> tag. */ * emphasis and other rich contents in that context similarly as in
* any link label.
*
* However, unlike in the case of links (where that contents becomes
* contents of the <a>...</a> tag), in the case of images the contents
* is supposed to fall into the attribute alt: <img alt="...">.
*
* In that context we naturally cannot output nested HTML tags. So lets
* suppress them and only output the plain text (i.e. what falls into
* text() callback).
*
* This make-it-a-plain-text approach is the recommended practice by
* CommonMark specification (for HTML output).
*/
return 0; return 0;
} }
switch(type) { switch(type) {
case MD_SPAN_EM: render_literal(r, "<em>"); break; case MD_SPAN_EM: render_literal(r, "<em>"); break;
case MD_SPAN_STRONG: render_literal(r, "<b>"); break; case MD_SPAN_STRONG: render_literal(r, "<b>"); break;
case MD_SPAN_U: render_literal(r, "<u>"); break;
case MD_SPAN_A: render_open_a_span(r, (MD_SPAN_A_DETAIL*) detail); break; case MD_SPAN_A: render_open_a_span(r, (MD_SPAN_A_DETAIL*) detail); break;
case MD_SPAN_IMG: render_open_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); break; case MD_SPAN_IMG: render_open_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); break;
case MD_SPAN_CODE: render_literal(r, "<code>"); break; case MD_SPAN_CODE: render_literal(r, "<code>"); break;
@ -348,8 +363,8 @@ static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
HtmlRenderer* r = (HtmlRenderer*) userdata; HtmlRenderer* r = (HtmlRenderer*) userdata;
if(r->imgnest > 0) { if(r->imgnest > 0) {
/* We are inside an image, i.e. rendering the ALT attribute of /* Ditto as in enter_span_callback(), except we have to allow the
* <IMG> tag. */ * end of the <img> tag. */
if(r->imgnest == 1 && type == MD_SPAN_IMG) if(r->imgnest == 1 && type == MD_SPAN_IMG)
render_close_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); render_close_img_span(r, (MD_SPAN_IMG_DETAIL*) detail);
return 0; return 0;
@ -358,6 +373,7 @@ static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
switch(type) { switch(type) {
case MD_SPAN_EM: render_literal(r, "</em>"); break; case MD_SPAN_EM: render_literal(r, "</em>"); break;
case MD_SPAN_STRONG: render_literal(r, "</b>"); break; case MD_SPAN_STRONG: render_literal(r, "</b>"); break;
case MD_SPAN_U: render_literal(r, "</u>"); break;
case MD_SPAN_A: render_literal(r, "</a>"); break; case MD_SPAN_A: render_literal(r, "</a>"); break;
case MD_SPAN_IMG: /*noop, handled above*/ break; case MD_SPAN_IMG: /*noop, handled above*/ break;
case MD_SPAN_CODE: render_literal(r, "</code>"); break; case MD_SPAN_CODE: render_literal(r, "</code>"); break;
@ -395,7 +411,17 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo
switch(type) { switch(type) {
case MD_TEXT_NULLCHAR: render_text(r, ucReplacementUTF8, sizeof(ucReplacementUTF8)); break; case MD_TEXT_NULLCHAR: render_text(r, ucReplacementUTF8, sizeof(ucReplacementUTF8)); break;
case MD_TEXT_BR: render_literal(r, (r->imgnest == 0 ? "<br>\n" : " ")); break; case MD_TEXT_BR:
render_literal(
r,
r->imgnest == 0 ?
((r->flags & MD_HTML_FLAG_XHTML) ? "<br/>\n" : "<br>\n") :
" "
);
break;
render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr/>\n" : "<hr>\n"); break;
case MD_TEXT_SOFTBR: render_literal(r, (r->imgnest == 0 ? "\n" : " ")); break; case MD_TEXT_SOFTBR: render_literal(r, (r->imgnest == 0 ? "\n" : " ")); break;
case MD_TEXT_HTML: render_text(r, text, size); break; case MD_TEXT_HTML: render_text(r, text, size); break;
case MD_TEXT_ENTITY: render_text(r, text, size); break; case MD_TEXT_ENTITY: render_text(r, text, size); break;
@ -409,9 +435,14 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo
// dlog("MD4C: %s\n", msg); // dlog("MD4C: %s\n", msg);
// } // }
int int fmt_html(
fmt_html(const MD_CHAR* input, MD_SIZE input_size, WBuf* outbuf, unsigned parser_flags) { const MD_CHAR* input,
HtmlRenderer render = { outbuf, 0, 0 }; MD_SIZE input_size,
WBuf* outbuf,
u32 parser_flags,
u32 render_flags
) {
HtmlRenderer render = { outbuf, 0, 0, render_flags };
MD_PARSER parser = { MD_PARSER parser = {
0, 0,

View file

@ -1,4 +1,6 @@
#pragma once #pragma once
#include "wbuf.h" #include "wbuf.h"
int fmt_html(const char* input, u32 inputlen, WBuf* outbuf, u32 parserFlags); #define MD_HTML_FLAG_XHTML 0x0008 // instead of e.g. <br>, generate <br/>
int fmt_html(const char* input, u32 inputlen, WBuf* outbuf, u32 parserFlags, u32 renderFlags);

View file

@ -5,13 +5,10 @@
#include "fmt_html.h" #include "fmt_html.h"
// #include "fmt_json.h" // #include "fmt_json.h"
// #include "md4c.h" // these should be in sync with "OutputFlags" in md.js
/* If set, debug output from md_parse() is sent to stderr. */
#define MD_RENDER_FLAG_DEBUG 0x0001
#define MD_RENDER_FLAG_VERBATIM_ENTITIES 0x0002
typedef enum OutputFlags { typedef enum OutputFlags {
OutputFlagsHTML = 1 << 0, OutputFlagHTML = 1 << 0,
OutputFlagXHTML = 1 << 1,
} OutputFlags; } OutputFlags;
typedef enum ErrorCode { typedef enum ErrorCode {
@ -44,10 +41,15 @@ export size_t parseUTF8(
WBufReset(&outbuf); WBufReset(&outbuf);
if (outflags & OutputFlagsHTML) { if (outflags & OutputFlagHTML) {
WBufReserve(&outbuf, inbuflen * 2); // approximate output size to minimize reallocations WBufReserve(&outbuf, inbuflen * 2); // approximate output size to minimize reallocations
if (fmt_html(inbufptr, inbuflen, &outbuf, parser_flags) != 0) { u32 render_flags = 0;
if (outflags & OutputFlagXHTML) {
render_flags |= MD_HTML_FLAG_XHTML;
}
if (fmt_html(inbufptr, inbuflen, &outbuf, parser_flags, render_flags) != 0) {
// fmt_html returns status of md_parse which only fails in extreme cases // fmt_html returns status of md_parse which only fails in extreme cases
// like when out of memory. md4c does not provide error codes or error messages. // like when out of memory. md4c does not provide error codes or error messages.
WErrSet(ERR_MD_PARSE, "md parser error"); WErrSet(ERR_MD_PARSE, "md parser error");

View file

@ -12,7 +12,7 @@ export const ParseFlags = {
PERMISSIVE_ATX_HEADERS: 0x0002, // Do not require space in ATX headers ( ###header ) PERMISSIVE_ATX_HEADERS: 0x0002, // Do not require space in ATX headers ( ###header )
PERMISSIVE_URL_AUTO_LINKS: 0x0004, // Recognize URLs as links even without <...> PERMISSIVE_URL_AUTO_LINKS: 0x0004, // Recognize URLs as links even without <...>
PERMISSIVE_EMAIL_AUTO_LINKS: 0x0008, // Recognize e-mails as links even without <...> PERMISSIVE_EMAIL_AUTO_LINKS: 0x0008, // Recognize e-mails as links even without <...>
NO_INDENTED_CODE_BLOCKS: 0x0010, // Disable indented code blocks. (Only fenced code works.) NO_INDENTED_CODE_BLOCKS: 0x0010, // Disable indented code blocks. (Only fenced code works)
NO_HTML_BLOCKS: 0x0020, // Disable raw HTML blocks. NO_HTML_BLOCKS: 0x0020, // Disable raw HTML blocks.
NO_HTML_SPANS: 0x0040, // Disable raw HTML (inline). NO_HTML_SPANS: 0x0040, // Disable raw HTML (inline).
TABLES: 0x0100, // Enable tables extension. TABLES: 0x0100, // Enable tables extension.
@ -21,6 +21,7 @@ export const ParseFlags = {
TASK_LISTS: 0x0800, // Enable task list extension. TASK_LISTS: 0x0800, // Enable task list extension.
LATEX_MATH_SPANS: 0x1000, // Enable $ and $$ containing LaTeX equations. LATEX_MATH_SPANS: 0x1000, // Enable $ and $$ containing LaTeX equations.
WIKI_LINKS: 0x2000, // Enable wiki links extension. WIKI_LINKS: 0x2000, // Enable wiki links extension.
UNDERLINE: 0x4000, // Enable underline extension (disables '_' for emphasis)
// Github style default flags // Github style default flags
DEFAULT: 0x0001 | 0x0002 | 0x0004 | 0x0200 | 0x0100 | 0x0800, DEFAULT: 0x0001 | 0x0002 | 0x0004 | 0x0200 | 0x0100 | 0x0800,
@ -34,38 +35,46 @@ export const ParseFlags = {
NO_HTML: 0x0020 | 0x0040, // NO_HTML_BLOCKS | NO_HTML_SPANS NO_HTML: 0x0020 | 0x0040, // NO_HTML_BLOCKS | NO_HTML_SPANS
} }
// these should be in sync with "OutputFlags" in md.c
const OutputFlags = { const OutputFlags = {
HTML: 1 << 0, // Output HTML HTML: 1 << 0, // Output HTML
} XHTML: 1 << 1, // Output XHTML (only has effect with HTML flag set)
const defaultOptions = {
parseFlags: ParseFlags.DEFAULT,
// how to format the output
format: "html",
// Return a view of heap memory as a Uint8Array, instead of a string.
//
// The returned Uint8Array is only valid until the next call to parse().
// If you need to keep the returned Uint8Array around, call Uint8Array.slice()
// to make a copy, as each call to parse() reuses the same underlying memory.
asMemoryView: false,
} }
export function parse(source, options) { export function parse(source, options) {
options = options ? {__proto__:defaultOptions, ...options} : defaultOptions options = options || {}
let outflags = (0
| (options.format == "html" ? OutputFlags.HTML : 0) let parseFlags = (
options.parseFlags === undefined ? ParseFlags.DEFAULT :
options.parseFlags
) )
let outputFlags = 0
switch (options.format) {
case "xhtml":
outputFlags |= OutputFlags.HTML | OutputFlags.XHTML
break
case "html":
case undefined:
case null:
case "":
outputFlags |= OutputFlags.HTML
break
default:
throw new Error(`invalid format "${options.format}"`)
}
let buf = typeof source == "string" ? utf8.encode(source) : source let buf = typeof source == "string" ? utf8.encode(source) : source
let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) => let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) =>
_parseUTF8(inptr, inlen, options.parseFlags, outflags, outptr) _parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr)
)) ))
// check for error and throw if needed // check for error and throw if needed
werrCheck() werrCheck()
// DEBUG
// if (outbuf) { // if (outbuf) {
// console.log(utf8.decode(outbuf)) // console.log(utf8.decode(outbuf))
// } // }

View file

@ -2,7 +2,7 @@
* MD4C: Markdown parser for C * MD4C: Markdown parser for C
* (http://github.com/mity/md4c) * (http://github.com/mity/md4c)
* *
* Copyright (c) 2016-2019 Martin Mitas * Copyright (c) 2016-2020 Martin Mitas
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -34,14 +34,23 @@
*** Miscellaneous Stuff *** *** Miscellaneous Stuff ***
*****************************/ *****************************/
#ifdef _MSC_VER #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199409L
/* MSVC does not understand "inline" when building as pure C (not C++). /* C89/90 or old compilers in general may not understand "inline". */
* However it understands "__inline" */ #if defined __GNUC__
#ifndef __cplusplus #define inline __inline__
#elif defined _MSC_VER
#define inline __inline #define inline __inline
#else
#define inline
#endif #endif
#endif #endif
/* Make the UTF-8 support the default. */
#if !defined MD4C_USE_ASCII && !defined MD4C_USE_UTF8 && !defined MD4C_USE_UTF16
#define MD4C_USE_UTF8
#endif
/* Magic for making wide literals with MD4C_USE_UTF16. */
#ifdef _T #ifdef _T
#undef _T #undef _T
#endif #endif
@ -126,21 +135,22 @@ struct MD_CTX_tag {
#endif #endif
/* For resolving of inline spans. */ /* For resolving of inline spans. */
MD_MARKCHAIN mark_chains[12]; MD_MARKCHAIN mark_chains[13];
#define PTR_CHAIN ctx->mark_chains[0] #define PTR_CHAIN (ctx->mark_chains[0])
#define TABLECELLBOUNDARIES ctx->mark_chains[1] #define TABLECELLBOUNDARIES (ctx->mark_chains[1])
#define ASTERISK_OPENERS_extraword_mod3_0 ctx->mark_chains[2] #define ASTERISK_OPENERS_extraword_mod3_0 (ctx->mark_chains[2])
#define ASTERISK_OPENERS_extraword_mod3_1 ctx->mark_chains[3] #define ASTERISK_OPENERS_extraword_mod3_1 (ctx->mark_chains[3])
#define ASTERISK_OPENERS_extraword_mod3_2 ctx->mark_chains[4] #define ASTERISK_OPENERS_extraword_mod3_2 (ctx->mark_chains[4])
#define ASTERISK_OPENERS_intraword_mod3_0 ctx->mark_chains[5] #define ASTERISK_OPENERS_intraword_mod3_0 (ctx->mark_chains[5])
#define ASTERISK_OPENERS_intraword_mod3_1 ctx->mark_chains[6] #define ASTERISK_OPENERS_intraword_mod3_1 (ctx->mark_chains[6])
#define ASTERISK_OPENERS_intraword_mod3_2 ctx->mark_chains[7] #define ASTERISK_OPENERS_intraword_mod3_2 (ctx->mark_chains[7])
#define UNDERSCORE_OPENERS ctx->mark_chains[8] #define UNDERSCORE_OPENERS (ctx->mark_chains[8])
#define TILDE_OPENERS ctx->mark_chains[9] #define TILDE_OPENERS_1 (ctx->mark_chains[9])
#define BRACKET_OPENERS ctx->mark_chains[10] #define TILDE_OPENERS_2 (ctx->mark_chains[10])
#define DOLLAR_OPENERS ctx->mark_chains[11] #define BRACKET_OPENERS (ctx->mark_chains[11])
#define DOLLAR_OPENERS (ctx->mark_chains[12])
#define OPENERS_CHAIN_FIRST 2 #define OPENERS_CHAIN_FIRST 2
#define OPENERS_CHAIN_LAST 11 #define OPENERS_CHAIN_LAST 12
int n_table_cell_boundaries; int n_table_cell_boundaries;
@ -203,14 +213,12 @@ struct MD_LINE_ANALYSIS_tag {
OFF beg; OFF beg;
OFF end; OFF end;
unsigned indent; /* Indentation level. */ unsigned indent; /* Indentation level. */
unsigned total_indent; /* Total indent in characters. */
}; };
typedef struct MD_LINE_tag MD_LINE; typedef struct MD_LINE_tag MD_LINE;
struct MD_LINE_tag { struct MD_LINE_tag {
OFF beg; OFF beg;
OFF end; OFF end;
unsigned total_indent; /* Total indent in characters. */
}; };
typedef struct MD_VERBATIMLINE_tag MD_VERBATIMLINE; typedef struct MD_VERBATIMLINE_tag MD_VERBATIMLINE;
@ -225,19 +233,11 @@ struct MD_VERBATIMLINE_tag {
*** Debugging *** *** Debugging ***
*******************/ *******************/
// change to 0 to disable MD_PARSER.debug_log
#define WITH_PARSER_DEBUG_LOG 0
#if WITH_PARSER_DEBUG_LOG
#define MD_LOG(msg) \ #define MD_LOG(msg) \
do { \ do { \
if(ctx->parser.debug_log != NULL) \ if(ctx->parser.debug_log != NULL) \
ctx->parser.debug_log((msg), ctx->userdata); \ ctx->parser.debug_log((msg), ctx->userdata); \
} while(0) } while(0)
#else
#define MD_LOG(msg) do{}while(0)
#endif
#ifdef DEBUG #ifdef DEBUG
#define MD_ASSERT(cond) \ #define MD_ASSERT(cond) \
@ -275,7 +275,7 @@ struct MD_VERBATIMLINE_tag {
/* Character classification. /* Character classification.
* Note we assume ASCII compatibility of code points < 128 here. */ * Note we assume ASCII compatibility of code points < 128 here. */
#define ISIN_(ch, ch_min, ch_max) ((ch_min) <= (unsigned)(ch) && (unsigned)(ch) <= (ch_max)) #define ISIN_(ch, ch_min, ch_max) ((ch_min) <= (unsigned)(ch) && (unsigned)(ch) <= (ch_max))
#define ISANYOF_(ch, palette) (md_strchr((palette), (ch)) != NULL) #define ISANYOF_(ch, palette) ((ch) != _T('\0') && md_strchr((palette), (ch)) != NULL)
#define ISANYOF2_(ch, ch1, ch2) ((ch) == (ch1) || (ch) == (ch2)) #define ISANYOF2_(ch, ch1, ch2) ((ch) == (ch1) || (ch) == (ch2))
#define ISANYOF3_(ch, ch1, ch2, ch3) ((ch) == (ch1) || (ch) == (ch2) || (ch) == (ch3)) #define ISANYOF3_(ch, ch1, ch2, ch3) ((ch) == (ch1) || (ch) == (ch2) || (ch) == (ch3))
#define ISASCII_(ch) ((unsigned)(ch) <= 127) #define ISASCII_(ch) ((unsigned)(ch) <= 127)
@ -306,16 +306,14 @@ struct MD_VERBATIMLINE_tag {
#define ISDIGIT(off) ISDIGIT_(CH(off)) #define ISDIGIT(off) ISDIGIT_(CH(off))
#define ISXDIGIT(off) ISXDIGIT_(CH(off)) #define ISXDIGIT(off) ISXDIGIT_(CH(off))
#define ISALNUM(off) ISALNUM_(CH(off)) #define ISALNUM(off) ISALNUM_(CH(off))
static inline const CHAR*
md_strchr(const CHAR* str, CHAR ch)
{ #if defined MD4C_USE_UTF16
OFF i; #define md_strchr wcschr
for(i = 0; str[i] != _T('\0'); i++) { #else
if(ch == str[i]) #define md_strchr strchr
return (str + i); #endif
}
return NULL;
}
/* Case insensitive check of string equality. */ /* Case insensitive check of string equality. */
static inline int static inline int
@ -548,22 +546,22 @@ struct MD_UNICODE_FOLD_INFO_tag {
R(0x2030,0x2043), R(0x2045,0x2051), R(0x2053,0x205e), R(0x207d,0x207e), R(0x208d,0x208e), R(0x2030,0x2043), R(0x2045,0x2051), R(0x2053,0x205e), R(0x207d,0x207e), R(0x208d,0x208e),
R(0x2308,0x230b), R(0x2329,0x232a), R(0x2768,0x2775), R(0x27c5,0x27c6), R(0x27e6,0x27ef), R(0x2308,0x230b), R(0x2329,0x232a), R(0x2768,0x2775), R(0x27c5,0x27c6), R(0x27e6,0x27ef),
R(0x2983,0x2998), R(0x29d8,0x29db), R(0x29fc,0x29fd), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70), R(0x2983,0x2998), R(0x29d8,0x29db), R(0x29fc,0x29fd), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70),
R(0x2e00,0x2e2e), R(0x2e30,0x2e4f), R(0x3001,0x3003), R(0x3008,0x3011), R(0x3014,0x301f), S(0x3030), R(0x2e00,0x2e2e), R(0x2e30,0x2e4f), S(0x2e52), R(0x3001,0x3003), R(0x3008,0x3011), R(0x3014,0x301f),
S(0x303d), S(0x30a0), S(0x30fb), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e), S(0x3030), S(0x303d), S(0x30a0), S(0x30fb), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e),
R(0xa6f2,0xa6f7), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f), R(0xa6f2,0xa6f7), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f),
S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaade,0xaadf), R(0xaaf0,0xaaf1), S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaade,0xaadf), R(0xaaf0,0xaaf1),
S(0xabeb), R(0xfd3e,0xfd3f), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe61), S(0xfe63), S(0xfe68), S(0xabeb), R(0xfd3e,0xfd3f), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe61), S(0xfe63), S(0xfe68),
R(0xfe6a,0xfe6b), R(0xff01,0xff03), R(0xff05,0xff0a), R(0xff0c,0xff0f), R(0xff1a,0xff1b), R(0xfe6a,0xfe6b), R(0xff01,0xff03), R(0xff05,0xff0a), R(0xff0c,0xff0f), R(0xff1a,0xff1b),
R(0xff1f,0xff20), R(0xff3b,0xff3d), S(0xff3f), S(0xff5b), S(0xff5d), R(0xff5f,0xff65), R(0x10100,0x10102), R(0xff1f,0xff20), R(0xff3b,0xff3d), S(0xff3f), S(0xff5b), S(0xff5d), R(0xff5f,0xff65), R(0x10100,0x10102),
S(0x1039f), S(0x103d0), S(0x1056f), S(0x10857), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f), S(0x1039f), S(0x103d0), S(0x1056f), S(0x10857), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f),
R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), R(0x10f55,0x10f59), R(0x11047,0x1104d), R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), S(0x10ead), R(0x10f55,0x10f59),
R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), R(0x11174,0x11175), R(0x111c5,0x111c8), R(0x11047,0x1104d), R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), R(0x11174,0x11175),
S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), S(0x112a9), R(0x1144b,0x1144f), R(0x111c5,0x111c8), S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), S(0x112a9),
S(0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), R(0x11641,0x11643), R(0x11660,0x1166c), R(0x1144b,0x1144f), R(0x1145a,0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), R(0x11641,0x11643),
R(0x1173c,0x1173e), S(0x1183b), S(0x119e2), R(0x11a3f,0x11a46), R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2), R(0x11660,0x1166c), R(0x1173c,0x1173e), S(0x1183b), R(0x11944,0x11946), S(0x119e2), R(0x11a3f,0x11a46),
R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8), S(0x11fff), R(0x12470,0x12474), R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2), R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8),
R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3b), S(0x16b44), R(0x16e97,0x16e9a), S(0x16fe2), S(0x11fff), R(0x12470,0x12474), R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3b), S(0x16b44),
S(0x1bc9f), R(0x1da87,0x1da8b), R(0x1e95e,0x1e95f) R(0x16e97,0x16e9a), S(0x16fe2), S(0x1bc9f), R(0x1da87,0x1da8b), R(0x1e95e,0x1e95f)
}; };
#undef R #undef R
#undef S #undef S
@ -586,52 +584,56 @@ struct MD_UNICODE_FOLD_INFO_tag {
static const unsigned FOLD_MAP_1[] = { static const unsigned FOLD_MAP_1[] = {
R(0x0041,0x005a), S(0x00b5), R(0x00c0,0x00d6), R(0x00d8,0x00de), R(0x0100,0x012e), R(0x0132,0x0136), R(0x0041,0x005a), S(0x00b5), R(0x00c0,0x00d6), R(0x00d8,0x00de), R(0x0100,0x012e), R(0x0132,0x0136),
R(0x0139,0x0147), R(0x014a,0x0176), S(0x0178), R(0x0179,0x017d), S(0x017f), S(0x0181), S(0x0182), R(0x0139,0x0147), R(0x014a,0x0176), S(0x0178), R(0x0179,0x017d), S(0x017f), S(0x0181), S(0x0182),
S(0x0186), S(0x0187), S(0x0189), S(0x018b), S(0x018e), S(0x018f), S(0x0190), S(0x0191), S(0x0193), S(0x0184), S(0x0186), S(0x0187), S(0x0189), S(0x018a), S(0x018b), S(0x018e), S(0x018f), S(0x0190),
S(0x0194), S(0x0196), S(0x0197), S(0x0198), S(0x019c), S(0x019d), S(0x019f), R(0x01a0,0x01a4), S(0x01a6), S(0x0191), S(0x0193), S(0x0194), S(0x0196), S(0x0197), S(0x0198), S(0x019c), S(0x019d), S(0x019f),
S(0x01a7), S(0x01a9), S(0x01ac), S(0x01ae), S(0x01af), S(0x01b1), S(0x01b3), S(0x01b7), S(0x01b8), R(0x01a0,0x01a4), S(0x01a6), S(0x01a7), S(0x01a9), S(0x01ac), S(0x01ae), S(0x01af), S(0x01b1), S(0x01b2),
S(0x01bc), S(0x01c4), S(0x01c5), S(0x01c7), S(0x01c8), S(0x01ca), R(0x01cb,0x01db), R(0x01de,0x01ee), S(0x01b3), S(0x01b5), S(0x01b7), S(0x01b8), S(0x01bc), S(0x01c4), S(0x01c5), S(0x01c7), S(0x01c8),
S(0x01f1), S(0x01f2), S(0x01f6), S(0x01f7), R(0x01f8,0x021e), S(0x0220), R(0x0222,0x0232), S(0x023a), S(0x01ca), R(0x01cb,0x01db), R(0x01de,0x01ee), S(0x01f1), S(0x01f2), S(0x01f4), S(0x01f6), S(0x01f7),
S(0x023b), S(0x023d), S(0x023e), S(0x0241), S(0x0243), S(0x0244), S(0x0245), R(0x0246,0x024e), S(0x0345), R(0x01f8,0x021e), S(0x0220), R(0x0222,0x0232), S(0x023a), S(0x023b), S(0x023d), S(0x023e), S(0x0241),
S(0x0370), S(0x0376), S(0x037f), S(0x0386), R(0x0388,0x038a), S(0x038c), S(0x038e), R(0x0391,0x03a1), S(0x0243), S(0x0244), S(0x0245), R(0x0246,0x024e), S(0x0345), S(0x0370), S(0x0372), S(0x0376), S(0x037f),
R(0x03a3,0x03ab), S(0x03c2), S(0x03cf), S(0x03d0), S(0x03d1), S(0x03d5), S(0x03d6), R(0x03d8,0x03ee), S(0x0386), R(0x0388,0x038a), S(0x038c), S(0x038e), S(0x038f), R(0x0391,0x03a1), R(0x03a3,0x03ab),
S(0x03f0), S(0x03f1), S(0x03f4), S(0x03f5), S(0x03f7), S(0x03f9), S(0x03fa), R(0x03fd,0x03ff), S(0x03c2), S(0x03cf), S(0x03d0), S(0x03d1), S(0x03d5), S(0x03d6), R(0x03d8,0x03ee), S(0x03f0), S(0x03f1),
R(0x0400,0x040f), R(0x0410,0x042f), R(0x0460,0x0480), R(0x048a,0x04be), S(0x04c0), R(0x04c1,0x04cd), S(0x03f4), S(0x03f5), S(0x03f7), S(0x03f9), S(0x03fa), R(0x03fd,0x03ff), R(0x0400,0x040f),
R(0x04d0,0x052e), R(0x0531,0x0556), R(0x10a0,0x10c5), S(0x10c7), S(0x10cd), R(0x13f8,0x13fd), S(0x1c80), R(0x0410,0x042f), R(0x0460,0x0480), R(0x048a,0x04be), S(0x04c0), R(0x04c1,0x04cd), R(0x04d0,0x052e),
S(0x1c81), S(0x1c82), S(0x1c83), S(0x1c85), S(0x1c86), S(0x1c87), S(0x1c88), R(0x1c90,0x1cba), R(0x0531,0x0556), R(0x10a0,0x10c5), S(0x10c7), S(0x10cd), R(0x13f8,0x13fd), S(0x1c80), S(0x1c81),
S(0x1c82), S(0x1c83), S(0x1c84), S(0x1c85), S(0x1c86), S(0x1c87), S(0x1c88), R(0x1c90,0x1cba),
R(0x1cbd,0x1cbf), R(0x1e00,0x1e94), S(0x1e9b), R(0x1ea0,0x1efe), R(0x1f08,0x1f0f), R(0x1f18,0x1f1d), R(0x1cbd,0x1cbf), R(0x1e00,0x1e94), S(0x1e9b), R(0x1ea0,0x1efe), R(0x1f08,0x1f0f), R(0x1f18,0x1f1d),
R(0x1f28,0x1f2f), R(0x1f38,0x1f3f), R(0x1f48,0x1f4d), S(0x1f59), S(0x1f5b), S(0x1f5d), S(0x1f5f), R(0x1f28,0x1f2f), R(0x1f38,0x1f3f), R(0x1f48,0x1f4d), S(0x1f59), S(0x1f5b), S(0x1f5d), S(0x1f5f),
R(0x1f68,0x1f6f), S(0x1fb8), S(0x1fba), S(0x1fbe), R(0x1fc8,0x1fcb), S(0x1fd8), S(0x1fda), S(0x1fe8), R(0x1f68,0x1f6f), S(0x1fb8), S(0x1fb9), S(0x1fba), S(0x1fbb), S(0x1fbe), R(0x1fc8,0x1fcb), S(0x1fd8),
S(0x1fea), S(0x1fec), S(0x1ff8), S(0x1ffa), S(0x2126), S(0x212a), S(0x212b), S(0x2132), R(0x2160,0x216f), S(0x1fd9), S(0x1fda), S(0x1fdb), S(0x1fe8), S(0x1fe9), S(0x1fea), S(0x1feb), S(0x1fec), S(0x1ff8),
S(0x2183), R(0x24b6,0x24cf), R(0x2c00,0x2c2e), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64), S(0x1ff9), S(0x1ffa), S(0x1ffb), S(0x2126), S(0x212a), S(0x212b), S(0x2132), R(0x2160,0x216f), S(0x2183),
R(0x2c67,0x2c6b), S(0x2c6d), S(0x2c6e), S(0x2c6f), S(0x2c70), S(0x2c72), S(0x2c75), S(0x2c7e), R(0x24b6,0x24cf), R(0x2c00,0x2c2e), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64), R(0x2c67,0x2c6b),
R(0x2c80,0x2ce2), S(0x2ceb), S(0x2cf2), R(0xa640,0xa66c), R(0xa680,0xa69a), R(0xa722,0xa72e), S(0x2c6d), S(0x2c6e), S(0x2c6f), S(0x2c70), S(0x2c72), S(0x2c75), S(0x2c7e), S(0x2c7f), R(0x2c80,0x2ce2),
R(0xa732,0xa76e), S(0xa779), S(0xa77d), R(0xa77e,0xa786), S(0xa78b), S(0xa78d), S(0xa790), S(0x2ceb), S(0x2ced), S(0x2cf2), R(0xa640,0xa66c), R(0xa680,0xa69a), R(0xa722,0xa72e), R(0xa732,0xa76e),
S(0xa779), S(0xa77b), S(0xa77d), R(0xa77e,0xa786), S(0xa78b), S(0xa78d), S(0xa790), S(0xa792),
R(0xa796,0xa7a8), S(0xa7aa), S(0xa7ab), S(0xa7ac), S(0xa7ad), S(0xa7ae), S(0xa7b0), S(0xa7b1), S(0xa7b2), R(0xa796,0xa7a8), S(0xa7aa), S(0xa7ab), S(0xa7ac), S(0xa7ad), S(0xa7ae), S(0xa7b0), S(0xa7b1), S(0xa7b2),
S(0xa7b3), R(0xa7b4,0xa7be), S(0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), R(0xab70,0xabbf), S(0xa7b3), R(0xa7b4,0xa7be), S(0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), S(0xa7c7), S(0xa7c9), S(0xa7f5),
R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), R(0x10c80,0x10cb2), R(0x118a0,0x118bf), R(0xab70,0xabbf), R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), R(0x10c80,0x10cb2),
R(0x16e40,0x16e5f), R(0x1e900,0x1e921) R(0x118a0,0x118bf), R(0x16e40,0x16e5f), R(0x1e900,0x1e921)
}; };
static const unsigned FOLD_MAP_1_DATA[] = { static const unsigned FOLD_MAP_1_DATA[] = {
0x0061, 0x007a, 0x03bc, 0x00e0, 0x00f6, 0x00f8, 0x00fe, 0x0101, 0x012f, 0x0133, 0x0137, 0x013a, 0x0148, 0x0061, 0x007a, 0x03bc, 0x00e0, 0x00f6, 0x00f8, 0x00fe, 0x0101, 0x012f, 0x0133, 0x0137, 0x013a, 0x0148,
0x014b, 0x0177, 0x00ff, 0x017a, 0x017e, 0x0073, 0x0253, 0x0183, 0x0254, 0x0188, 0x0256, 0x018c, 0x01dd, 0x014b, 0x0177, 0x00ff, 0x017a, 0x017e, 0x0073, 0x0253, 0x0183, 0x0185, 0x0254, 0x0188, 0x0256, 0x0257,
0x0259, 0x025b, 0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026f, 0x0272, 0x0275, 0x01a1, 0x01a5, 0x018c, 0x01dd, 0x0259, 0x025b, 0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026f, 0x0272, 0x0275,
0x0280, 0x01a8, 0x0283, 0x01ad, 0x0288, 0x01b0, 0x028a, 0x01b4, 0x0292, 0x01b9, 0x01bd, 0x01c6, 0x01c6, 0x01a1, 0x01a5, 0x0280, 0x01a8, 0x0283, 0x01ad, 0x0288, 0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b6, 0x0292,
0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01dc, 0x01df, 0x01ef, 0x01f3, 0x01f3, 0x0195, 0x01bf, 0x01f9, 0x021f, 0x01b9, 0x01bd, 0x01c6, 0x01c6, 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01dc, 0x01df, 0x01ef, 0x01f3, 0x01f3,
0x019e, 0x0223, 0x0233, 0x2c65, 0x023c, 0x019a, 0x2c66, 0x0242, 0x0180, 0x0289, 0x028c, 0x0247, 0x024f, 0x01f5, 0x0195, 0x01bf, 0x01f9, 0x021f, 0x019e, 0x0223, 0x0233, 0x2c65, 0x023c, 0x019a, 0x2c66, 0x0242,
0x03b9, 0x0371, 0x0377, 0x03f3, 0x03ac, 0x03ad, 0x03af, 0x03cc, 0x03cd, 0x03b1, 0x03c1, 0x03c3, 0x03cb, 0x0180, 0x0289, 0x028c, 0x0247, 0x024f, 0x03b9, 0x0371, 0x0373, 0x0377, 0x03f3, 0x03ac, 0x03ad, 0x03af,
0x03c3, 0x03d7, 0x03b2, 0x03b8, 0x03c6, 0x03c0, 0x03d9, 0x03ef, 0x03ba, 0x03c1, 0x03b8, 0x03b5, 0x03f8, 0x03cc, 0x03cd, 0x03ce, 0x03b1, 0x03c1, 0x03c3, 0x03cb, 0x03c3, 0x03d7, 0x03b2, 0x03b8, 0x03c6, 0x03c0,
0x03f2, 0x03fb, 0x037b, 0x037d, 0x0450, 0x045f, 0x0430, 0x044f, 0x0461, 0x0481, 0x048b, 0x04bf, 0x04cf, 0x03d9, 0x03ef, 0x03ba, 0x03c1, 0x03b8, 0x03b5, 0x03f8, 0x03f2, 0x03fb, 0x037b, 0x037d, 0x0450, 0x045f,
0x04c2, 0x04ce, 0x04d1, 0x052f, 0x0561, 0x0586, 0x2d00, 0x2d25, 0x2d27, 0x2d2d, 0x13f0, 0x13f5, 0x0432, 0x0430, 0x044f, 0x0461, 0x0481, 0x048b, 0x04bf, 0x04cf, 0x04c2, 0x04ce, 0x04d1, 0x052f, 0x0561, 0x0586,
0x0434, 0x043e, 0x0441, 0x0442, 0x044a, 0x0463, 0xa64b, 0x10d0, 0x10fa, 0x10fd, 0x10ff, 0x1e01, 0x1e95, 0x2d00, 0x2d25, 0x2d27, 0x2d2d, 0x13f0, 0x13f5, 0x0432, 0x0434, 0x043e, 0x0441, 0x0442, 0x0442, 0x044a,
0x1e61, 0x1ea1, 0x1eff, 0x1f00, 0x1f07, 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45, 0x0463, 0xa64b, 0x10d0, 0x10fa, 0x10fd, 0x10ff, 0x1e01, 0x1e95, 0x1e61, 0x1ea1, 0x1eff, 0x1f00, 0x1f07,
0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, 0x1f67, 0x1fb0, 0x1f70, 0x03b9, 0x1f72, 0x1f75, 0x1fd0, 0x1f76, 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60,
0x1fe0, 0x1f7a, 0x1fe5, 0x1f78, 0x1f7c, 0x03c9, 0x006b, 0x00e5, 0x214e, 0x2170, 0x217f, 0x2184, 0x24d0, 0x1f67, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x03b9, 0x1f72, 0x1f75, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0,
0x24e9, 0x2c30, 0x2c5e, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251, 0x0271, 0x0250, 0x0252, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x03c9, 0x006b, 0x00e5, 0x214e, 0x2170,
0x2c73, 0x2c76, 0x023f, 0x2c81, 0x2ce3, 0x2cec, 0x2cf3, 0xa641, 0xa66d, 0xa681, 0xa69b, 0xa723, 0xa72f, 0x217f, 0x2184, 0x24d0, 0x24e9, 0x2c30, 0x2c5e, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251,
0xa733, 0xa76f, 0xa77a, 0x1d79, 0xa77f, 0xa787, 0xa78c, 0x0265, 0xa791, 0xa797, 0xa7a9, 0x0266, 0x025c, 0x0271, 0x0250, 0x0252, 0x2c73, 0x2c76, 0x023f, 0x0240, 0x2c81, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641,
0x0261, 0x026c, 0x026a, 0x029e, 0x0287, 0x029d, 0xab53, 0xa7b5, 0xa7bf, 0xa7c3, 0xa794, 0x0282, 0x1d8e, 0xa66d, 0xa681, 0xa69b, 0xa723, 0xa72f, 0xa733, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa787, 0xa78c,
0x13a0, 0x13ef, 0xff41, 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, 0x118c0, 0x118df, 0x0265, 0xa791, 0xa793, 0xa797, 0xa7a9, 0x0266, 0x025c, 0x0261, 0x026c, 0x026a, 0x029e, 0x0287, 0x029d,
0x16e60, 0x16e7f, 0x1e922, 0x1e943 0xab53, 0xa7b5, 0xa7bf, 0xa7c3, 0xa794, 0x0282, 0x1d8e, 0xa7c8, 0xa7ca, 0xa7f6, 0x13a0, 0x13ef, 0xff41,
0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, 0x118c0, 0x118df, 0x16e60, 0x16e7f, 0x1e922,
0x1e943
}; };
static const unsigned FOLD_MAP_2[] = { static const unsigned FOLD_MAP_2[] = {
S(0x00df), S(0x0130), S(0x0149), S(0x01f0), S(0x0587), S(0x1e96), S(0x1e97), S(0x1e98), S(0x1e99), S(0x00df), S(0x0130), S(0x0149), S(0x01f0), S(0x0587), S(0x1e96), S(0x1e97), S(0x1e98), S(0x1e99),
@ -1338,8 +1340,9 @@ md_build_attr_append_substr(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build,
MD_TEXTTYPE* new_substr_types; MD_TEXTTYPE* new_substr_types;
OFF* new_substr_offsets; OFF* new_substr_offsets;
build->substr_alloc = (build->substr_alloc == 0 ? 8 : build->substr_alloc * 2); build->substr_alloc = (build->substr_alloc > 0
? build->substr_alloc + build->substr_alloc / 2
: 8);
new_substr_types = (MD_TEXTTYPE*) realloc(build->substr_types, new_substr_types = (MD_TEXTTYPE*) realloc(build->substr_types,
build->substr_alloc * sizeof(MD_TEXTTYPE)); build->substr_alloc * sizeof(MD_TEXTTYPE));
if(new_substr_types == NULL) { if(new_substr_types == NULL) {
@ -1465,8 +1468,8 @@ abort:
*** Dictionary of Reference Definitions *** *** Dictionary of Reference Definitions ***
*********************************************/ *********************************************/
#define MD_FNV1A_BASE 2166136261 #define MD_FNV1A_BASE 2166136261U
#define MD_FNV1A_PRIME 16777619 #define MD_FNV1A_PRIME 16777619U
static inline unsigned static inline unsigned
md_fnv1a(unsigned base, const void* data, size_t n) md_fnv1a(unsigned base, const void* data, size_t n)
@ -1488,12 +1491,12 @@ struct MD_REF_DEF_tag {
CHAR* label; CHAR* label;
CHAR* title; CHAR* title;
unsigned hash; unsigned hash;
SZ label_size : 24; SZ label_size;
unsigned label_needs_free : 1;
unsigned title_needs_free : 1;
SZ title_size; SZ title_size;
OFF dest_beg; OFF dest_beg;
OFF dest_end; OFF dest_end;
unsigned char label_needs_free : 1;
unsigned char title_needs_free : 1;
}; };
/* Label equivalence is quite complicated with regards to whitespace and case /* Label equivalence is quite complicated with regards to whitespace and case
@ -1573,8 +1576,8 @@ md_link_label_cmp(const CHAR* a_label, SZ a_size, const CHAR* b_label, SZ b_size
OFF b_off; OFF b_off;
int a_reached_end = FALSE; int a_reached_end = FALSE;
int b_reached_end = FALSE; int b_reached_end = FALSE;
MD_UNICODE_FOLD_INFO a_fi = { 0 }; MD_UNICODE_FOLD_INFO a_fi = { { 0 }, 0 };
MD_UNICODE_FOLD_INFO b_fi = { 0 }; MD_UNICODE_FOLD_INFO b_fi = { { 0 }, 0 };
OFF a_fi_off = 0; OFF a_fi_off = 0;
OFF b_fi_off = 0; OFF b_fi_off = 0;
int cmp; int cmp;
@ -1627,7 +1630,7 @@ md_ref_def_cmp(const void* a, const void* b)
} }
static int static int
md_ref_def_cmp_stable(const void* a, const void* b) md_ref_def_cmp_for_sort(const void* a, const void* b)
{ {
int cmp; int cmp;
@ -1680,6 +1683,7 @@ md_build_ref_def_hashtable(MD_CTX* ctx)
bucket = ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size]; bucket = ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size];
if(bucket == NULL) { if(bucket == NULL) {
/* The bucket is empty. Make it just point to the def. */
ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = def; ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = def;
continue; continue;
} }
@ -1691,12 +1695,12 @@ md_build_ref_def_hashtable(MD_CTX* ctx)
MD_REF_DEF* old_def = (MD_REF_DEF*) bucket; MD_REF_DEF* old_def = (MD_REF_DEF*) bucket;
if(md_link_label_cmp(def->label, def->label_size, old_def->label, old_def->label_size) == 0) { if(md_link_label_cmp(def->label, def->label_size, old_def->label, old_def->label_size) == 0) {
/* Ignore this ref. def. */ /* Duplicate label: Ignore this ref. def. */
continue; continue;
} }
/* Make the bucket capable of holding more ref. defs. */ /* Make the bucket complex, i.e. able to hold more ref. defs. */
list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 4 * sizeof(MD_REF_DEF*)); list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 2 * sizeof(MD_REF_DEF*));
if(list == NULL) { if(list == NULL) {
MD_LOG("malloc() failed."); MD_LOG("malloc() failed.");
goto abort; goto abort;
@ -1704,22 +1708,28 @@ md_build_ref_def_hashtable(MD_CTX* ctx)
list->ref_defs[0] = old_def; list->ref_defs[0] = old_def;
list->ref_defs[1] = def; list->ref_defs[1] = def;
list->n_ref_defs = 2; list->n_ref_defs = 2;
list->alloc_ref_defs = 4; list->alloc_ref_defs = 2;
ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list;
continue; continue;
} }
/* Append the def to the bucket list. */ /* Append the def to the complex bucket list.
*
* Note in this case we ignore potential duplicates to avoid expensive
* iterating over the complex bucket. Below, we revisit all the complex
* buckets and handle it more cheaply after the complex bucket contents
* is sorted. */
list = (MD_REF_DEF_LIST*) bucket; list = (MD_REF_DEF_LIST*) bucket;
if(list->n_ref_defs >= list->alloc_ref_defs) { if(list->n_ref_defs >= list->alloc_ref_defs) {
int alloc_ref_defs = list->alloc_ref_defs + list->alloc_ref_defs / 2;
MD_REF_DEF_LIST* list_tmp = (MD_REF_DEF_LIST*) realloc(list, MD_REF_DEF_LIST* list_tmp = (MD_REF_DEF_LIST*) realloc(list,
sizeof(MD_REF_DEF_LIST) + 2 * list->alloc_ref_defs * sizeof(MD_REF_DEF*)); sizeof(MD_REF_DEF_LIST) + alloc_ref_defs * sizeof(MD_REF_DEF*));
if(list_tmp == NULL) { if(list_tmp == NULL) {
MD_LOG("realloc() failed."); MD_LOG("realloc() failed.");
goto abort; goto abort;
} }
list = list_tmp; list = list_tmp;
list->alloc_ref_defs *= 2; list->alloc_ref_defs = alloc_ref_defs;
ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list;
} }
@ -1738,9 +1748,12 @@ md_build_ref_def_hashtable(MD_CTX* ctx)
continue; continue;
list = (MD_REF_DEF_LIST*) bucket; list = (MD_REF_DEF_LIST*) bucket;
qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_stable); qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_for_sort);
/* Disable duplicates. */ /* Disable all duplicates in the complex bucket by forcing all such
* records to point to the 1st such ref. def. I.e. no matter which
* record is found during the lookup, it will always point to the right
* ref. def. in ctx->ref_defs[]. */
for(j = 1; j < list->n_ref_defs; j++) { for(j = 1; j < list->n_ref_defs; j++) {
if(md_ref_def_cmp(&list->ref_defs[j-1], &list->ref_defs[j]) == 0) if(md_ref_def_cmp(&list->ref_defs[j-1], &list->ref_defs[j]) == 0)
list->ref_defs[j] = list->ref_defs[j-1]; list->ref_defs[j] = list->ref_defs[j-1];
@ -2063,21 +2076,18 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
OFF label_contents_beg; OFF label_contents_beg;
OFF label_contents_end; OFF label_contents_end;
int label_contents_line_index = -1; int label_contents_line_index = -1;
int label_is_multiline; int label_is_multiline = FALSE;
CHAR* label;
SZ label_size;
int label_needs_free = FALSE;
OFF dest_contents_beg; OFF dest_contents_beg;
OFF dest_contents_end; OFF dest_contents_end;
OFF title_contents_beg; OFF title_contents_beg;
OFF title_contents_end; OFF title_contents_end;
int title_contents_line_index; int title_contents_line_index;
int title_is_multiline; int title_is_multiline = FALSE;
OFF off; OFF off;
int line_index = 0; int line_index = 0;
int tmp_line_index; int tmp_line_index;
MD_REF_DEF* def; MD_REF_DEF* def = NULL;
int ret; int ret = 0;
/* Link label. */ /* Link label. */
if(!md_is_link_label(ctx, lines, n_lines, lines[0].beg, if(!md_is_link_label(ctx, lines, n_lines, lines[0].beg,
@ -2128,65 +2138,58 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
if(off < lines[line_index].end) if(off < lines[line_index].end)
return FALSE; return FALSE;
/* Construct label. */ /* So, it _is_ a reference definition. Remember it. */
if(!label_is_multiline) {
label = (CHAR*) STR(label_contents_beg);
label_size = label_contents_end - label_contents_beg;
label_needs_free = FALSE;
} else {
MD_CHECK(md_merge_lines_alloc(ctx, label_contents_beg, label_contents_end,
lines + label_contents_line_index, n_lines - label_contents_line_index,
_T(' '), &label, &label_size));
label_needs_free = TRUE;
}
/* Store the reference definition. */
if(ctx->n_ref_defs >= ctx->alloc_ref_defs) { if(ctx->n_ref_defs >= ctx->alloc_ref_defs) {
MD_REF_DEF* new_defs; MD_REF_DEF* new_defs;
ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0 ? ctx->alloc_ref_defs * 2 : 16); ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0
? ctx->alloc_ref_defs + ctx->alloc_ref_defs / 2
: 16);
new_defs = (MD_REF_DEF*) realloc(ctx->ref_defs, ctx->alloc_ref_defs * sizeof(MD_REF_DEF)); new_defs = (MD_REF_DEF*) realloc(ctx->ref_defs, ctx->alloc_ref_defs * sizeof(MD_REF_DEF));
if(new_defs == NULL) { if(new_defs == NULL) {
MD_LOG("realloc() failed."); MD_LOG("realloc() failed.");
ret = -1;
goto abort; goto abort;
} }
ctx->ref_defs = new_defs; ctx->ref_defs = new_defs;
} }
def = &ctx->ref_defs[ctx->n_ref_defs]; def = &ctx->ref_defs[ctx->n_ref_defs];
memset(def, 0, sizeof(MD_REF_DEF)); memset(def, 0, sizeof(MD_REF_DEF));
def->label = label; if(label_is_multiline) {
def->label_size = label_size; MD_CHECK(md_merge_lines_alloc(ctx, label_contents_beg, label_contents_end,
def->label_needs_free = label_needs_free; lines + label_contents_line_index, n_lines - label_contents_line_index,
_T(' '), &def->label, &def->label_size));
def->dest_beg = dest_contents_beg; def->label_needs_free = TRUE;
def->dest_end = dest_contents_end;
if(title_contents_beg >= title_contents_end) {
def->title = NULL;
def->title_size = 0;
} else if(!title_is_multiline) {
def->title = (CHAR*) STR(title_contents_beg);
def->title_size = title_contents_end - title_contents_beg;
} else { } else {
def->label = (CHAR*) STR(label_contents_beg);
def->label_size = label_contents_end - label_contents_beg;
}
if(title_is_multiline) {
MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end, MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end,
lines + title_contents_line_index, n_lines - title_contents_line_index, lines + title_contents_line_index, n_lines - title_contents_line_index,
_T('\n'), &def->title, &def->title_size)); _T('\n'), &def->title, &def->title_size));
def->title_needs_free = TRUE; def->title_needs_free = TRUE;
} else {
def->title = (CHAR*) STR(title_contents_beg);
def->title_size = title_contents_end - title_contents_beg;
} }
def->dest_beg = dest_contents_beg;
def->dest_end = dest_contents_end;
/* Success. */ /* Success. */
ctx->n_ref_defs++; ctx->n_ref_defs++;
return line_index + 1; return line_index + 1;
abort: abort:
/* Failure. */ /* Failure. */
if(label_needs_free) if(def != NULL && def->label_needs_free)
free(label); free(def->label);
return -1; if(def != NULL && def->title_needs_free)
free(def->title);
return ret;
} }
static int static int
@ -2479,7 +2482,7 @@ md_mark_chain(MD_CTX* ctx, int mark_index)
switch(mark->ch) { switch(mark->ch) {
case _T('*'): return md_asterisk_chain(ctx, mark->flags); case _T('*'): return md_asterisk_chain(ctx, mark->flags);
case _T('_'): return &UNDERSCORE_OPENERS; case _T('_'): return &UNDERSCORE_OPENERS;
case _T('~'): return &TILDE_OPENERS; case _T('~'): return (mark->end - mark->beg == 1) ? &TILDE_OPENERS_1 : &TILDE_OPENERS_2;
case _T('['): return &BRACKET_OPENERS; case _T('['): return &BRACKET_OPENERS;
case _T('|'): return &TABLECELLBOUNDARIES; case _T('|'): return &TABLECELLBOUNDARIES;
default: return NULL; default: return NULL;
@ -2492,7 +2495,9 @@ md_push_mark(MD_CTX* ctx)
if(ctx->n_marks >= ctx->alloc_marks) { if(ctx->n_marks >= ctx->alloc_marks) {
MD_MARK* new_marks; MD_MARK* new_marks;
ctx->alloc_marks = (ctx->alloc_marks > 0 ? ctx->alloc_marks * 2 : 64); ctx->alloc_marks = (ctx->alloc_marks > 0
? ctx->alloc_marks + ctx->alloc_marks / 2
: 64);
new_marks = realloc(ctx->marks, ctx->alloc_marks * sizeof(MD_MARK)); new_marks = realloc(ctx->marks, ctx->alloc_marks * sizeof(MD_MARK));
if(new_marks == NULL) { if(new_marks == NULL) {
MD_LOG("realloc() failed."); MD_LOG("realloc() failed.");
@ -2535,6 +2540,7 @@ md_mark_chain_append(MD_CTX* ctx, MD_MARKCHAIN* chain, int mark_index)
chain->head = mark_index; chain->head = mark_index;
ctx->marks[mark_index].prev = chain->tail; ctx->marks[mark_index].prev = chain->tail;
ctx->marks[mark_index].next = -1;
chain->tail = mark_index; chain->tail = mark_index;
} }
@ -2626,7 +2632,7 @@ md_rollback(MD_CTX* ctx, int opener_index, int closer_index, int how)
chain->head = -1; chain->head = -1;
} }
/* Go backwards so that un-resolved openers are re-added into their /* Go backwards so that unresolved openers are re-added into their
* respective chains, in the right order. */ * respective chains, in the right order. */
mark_index = closer_index - 1; mark_index = closer_index - 1;
while(mark_index > opener_index) { while(mark_index > opener_index) {
@ -2718,7 +2724,7 @@ md_build_mark_char_map(MD_CTX* ctx)
} }
} }
/* We limit code span marks to lower then 32 backticks. This solves the /* We limit code span marks to lower than 32 backticks. This solves the
* pathologic case of too many openers, each of different length: Their * pathologic case of too many openers, each of different length: Their
* resolving would be then O(n^2). */ * resolving would be then O(n^2). */
#define CODESPAN_MARK_MAXLEN 32 #define CODESPAN_MARK_MAXLEN 32
@ -2905,7 +2911,7 @@ md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end)
return FALSE; return FALSE;
off++; off++;
/* Labels delimited with '.'; each label is sequence of 1 - 62 alnum /* Labels delimited with '.'; each label is sequence of 1 - 63 alnum
* characters or '-', but '-' is not allowed as first or last char. */ * characters or '-', but '-' is not allowed as first or last char. */
label_len = 0; label_len = 0;
while(off < max_end) { while(off < max_end) {
@ -2918,7 +2924,7 @@ md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end)
else else
break; break;
if(label_len > 62) if(label_len > 63)
return FALSE; return FALSE;
off++; off++;
@ -3259,7 +3265,17 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode)
while(tmp < line_end && CH(tmp) == _T('~')) while(tmp < line_end && CH(tmp) == _T('~'))
tmp++; tmp++;
PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER); if(tmp - off < 3) {
unsigned flags = 0;
if(tmp < line_end && !ISUNICODEWHITESPACE(tmp))
flags |= MD_MARK_POTENTIAL_OPENER;
if(off > line->beg && !ISUNICODEWHITESPACEBEFORE(off))
flags |= MD_MARK_POTENTIAL_CLOSER;
if(flags != 0)
PUSH_MARK(ch, off, tmp, flags);
}
off = tmp; off = tmp;
continue; continue;
} }
@ -3407,92 +3423,91 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
continue; continue;
} }
/* Detect and resolve wiki links. */ /* Recognize and resolve wiki links.
* Wiki-links maybe '[[destination]]' or '[[destination|label]]'.
*/
if ((ctx->parser.flags & MD_FLAG_WIKILINKS) && if ((ctx->parser.flags & MD_FLAG_WIKILINKS) &&
next_opener != NULL && next_closer != NULL && (opener->end - opener->beg == 1) && /* not image */
(opener->end - opener->beg == 1) && next_opener != NULL && /* double '[' opener */
next_opener->ch == '[' &&
(next_opener->beg == opener->beg - 1) && (next_opener->beg == opener->beg - 1) &&
(next_closer->beg == closer->beg + 1) &&
(next_opener->end - next_opener->beg == 1) && (next_opener->end - next_opener->beg == 1) &&
(next_closer->end - next_closer->beg == 1) && next_closer != NULL && /* double ']' closer */
(next_opener->ch == '[' && next_closer->ch == ']')) next_closer->ch == ']' &&
(next_closer->beg == closer->beg + 1) &&
(next_closer->end - next_closer->beg == 1))
{ {
MD_MARK* delim = NULL;
int delim_index;
OFF dest_beg, dest_end;
is_link = TRUE; is_link = TRUE;
if (opener->end == closer->beg) /* We don't allow destination to be longer than 100 characters.
is_link = FALSE; * Lets scan to see whether there is '|'. (If not then the whole
* wiki-link has to be below the 100 characters.) */
int delim_index = opener_index; delim_index = opener_index + 1;
MD_MARK* delim = &ctx->marks[delim_index]; while(delim_index < closer_index) {
MD_MARK* m = &ctx->marks[delim_index];
/* To prevent runaway O(n^2) performance, don't look too far for the delimiter (.. < 100). */ if(m->ch == '|') {
while(is_link && delim_index < closer_index && (delim_index - opener_index) < 100 ) { delim = m;
if(delim->ch == '|' && delim->beg == opener->end) {
is_link = FALSE;
} else if(delim->ch == '|' && delim->end == closer->beg) {
break;
} else if(delim->ch == '|') {
opener->end = delim->beg;
break; break;
} }
if(m->ch != 'D' && m->beg - opener->end > 100)
break;
delim_index++; delim_index++;
delim = &ctx->marks[delim_index];
} }
dest_beg = opener->end;
OFF off = closer->beg-1; dest_end = (delim != NULL) ? delim->beg : closer->beg;
int count = 0; if(dest_end - dest_beg == 0 || dest_end - dest_beg > 100)
int has_label = (opener->end - opener->beg > 2);
const MD_LINE* line;
int line_index = n_lines-1;
/* An image inside the link target disables the wiki link. */
if( (has_label && last_img_beg >= opener->beg && last_img_end <= opener->end) ||
(!has_label && last_img_beg >= opener->beg && last_img_end <= closer->end))
is_link = FALSE; is_link = FALSE;
while(is_link && off > opener->beg && count++ < 100) { /* There may not be any new line in the destination. */
if(is_link) {
/* Newline not allowed in link target. */ OFF off;
if(has_label && (off <= opener->end) && ISNEWLINE(off)) for(off = dest_beg; off < dest_end; off++) {
if(ISNEWLINE(off)) {
is_link = FALSE; is_link = FALSE;
else if(!has_label && off > opener->end && ISNEWLINE(off)) break;
is_link = FALSE; }
else if(ISNEWLINE(off)) {
line = &lines[line_index--];
count = count - line->total_indent - 1; /* Count newline too. */
} }
off--;
} }
if(off > opener->beg)
is_link = FALSE;
if(is_link) { if(is_link) {
if(delim->ch == '|') if(delim != NULL) {
delim->flags |= MD_MARK_RESOLVED; if(delim->end < closer->beg) {
opener->end = delim->beg;
} else {
/* The pipe is just before the closer: [[foo|]] */
closer->beg = delim->beg;
delim = NULL;
}
}
opener->beg = next_opener->beg; opener->beg = next_opener->beg;
closer->end = next_closer->end;
opener->next = closer_index; opener->next = closer_index;
opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED;
closer->end = next_closer->end;
closer->prev = opener_index; closer->prev = opener_index;
closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED;
last_link_beg = opener->beg; last_link_beg = opener->beg;
last_link_end = closer->end; last_link_end = closer->end;
if ((opener->end - opener->beg > 2)) if(delim != NULL) {
delim->flags |= MD_MARK_RESOLVED;
md_rollback(ctx, opener_index, delim_index, MD_ROLLBACK_ALL);
md_analyze_link_contents(ctx, lines, n_lines, opener_index+1, closer_index); md_analyze_link_contents(ctx, lines, n_lines, opener_index+1, closer_index);
} else {
md_rollback(ctx, opener_index, closer_index, MD_ROLLBACK_ALL);
}
opener_index = next_index; opener_index = next_opener->prev;
continue; continue;
} }
} }
if(next_opener != NULL && next_opener->beg == closer->end) { if(next_opener != NULL && next_opener->beg == closer->end) {
if(next_closer->beg > closer->end + 1) { if(next_closer->beg > closer->end + 1) {
/* Might be full reference link. */ /* Might be full reference link. */
@ -3571,6 +3586,7 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
MD_ASSERT(ctx->marks[opener_index+2].ch == 'D'); MD_ASSERT(ctx->marks[opener_index+2].ch == 'D');
md_mark_store_ptr(ctx, opener_index+2, attr.title); md_mark_store_ptr(ctx, opener_index+2, attr.title);
/* The title might or might not have been allocated for us. */
if(attr.title_needs_free) if(attr.title_needs_free)
md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2); md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2);
ctx->marks[opener_index+2].prev = attr.title_size; ctx->marks[opener_index+2].prev = attr.title_size;
@ -3669,8 +3685,7 @@ md_analyze_emph(MD_CTX* ctx, int mark_index)
int i, n_opener_chains; int i, n_opener_chains;
unsigned flags = mark->flags; unsigned flags = mark->flags;
/* Apply "rule of three". (This is why we break asterisk opener /* Apply the "rule of three". */
* marks into multiple chains.) */
n_opener_chains = 0; n_opener_chains = 0;
opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0; opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0;
if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2)
@ -3706,7 +3721,7 @@ md_analyze_emph(MD_CTX* ctx, int mark_index)
if(opener != NULL) { if(opener != NULL) {
SZ opener_size = opener->end - opener->beg; SZ opener_size = opener->end - opener->beg;
SZ closer_size = mark->end - mark->beg; SZ closer_size = mark->end - mark->beg;
MD_MARKCHAIN* opener_chain = md_mark_chain(ctx, mark_index); MD_MARKCHAIN* opener_chain = md_mark_chain(ctx, opener_index);
if(opener_size > closer_size) { if(opener_size > closer_size) {
opener_index = md_split_emph_mark(ctx, opener_index, closer_size); opener_index = md_split_emph_mark(ctx, opener_index, closer_size);
@ -3729,20 +3744,23 @@ md_analyze_emph(MD_CTX* ctx, int mark_index)
static void static void
md_analyze_tilde(MD_CTX* ctx, int mark_index) md_analyze_tilde(MD_CTX* ctx, int mark_index)
{ {
/* We attempt to be Github Flavored Markdown compatible here. GFM says MD_MARK* mark = &ctx->marks[mark_index];
* that length of the tilde sequence is not important at all. Note that MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index);
* implies the TILDE_OPENERS chain can have at most one item. */
if(TILDE_OPENERS.head >= 0) { /* We attempt to be Github Flavored Markdown compatible here. GFM accepts
/* The chain already contains an opener, so we may resolve the span. */ * only tildes sequences of length 1 and 2, and the length of the opener
int opener_index = TILDE_OPENERS.head; * and closer has to match. */
if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && chain->head >= 0) {
int opener_index = chain->head;
md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING);
md_resolve_range(ctx, &TILDE_OPENERS, opener_index, mark_index); md_resolve_range(ctx, chain, opener_index, mark_index);
} else { return;
/* We can only be opener. */
md_mark_chain_append(ctx, &TILDE_OPENERS, mark_index);
} }
if(mark->flags & MD_MARK_POTENTIAL_OPENER)
md_mark_chain_append(ctx, chain, mark_index);
} }
static void static void
@ -3987,25 +4005,14 @@ static void
md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
int mark_beg, int mark_end) int mark_beg, int mark_end)
{ {
int i;
md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:.")); md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:."));
ASTERISK_OPENERS_extraword_mod3_0.head = -1;
ASTERISK_OPENERS_extraword_mod3_0.tail = -1; for(i = OPENERS_CHAIN_FIRST; i <= OPENERS_CHAIN_LAST; i++) {
ASTERISK_OPENERS_extraword_mod3_1.head = -1; ctx->mark_chains[i].head = -1;
ASTERISK_OPENERS_extraword_mod3_1.tail = -1; ctx->mark_chains[i].tail = -1;
ASTERISK_OPENERS_extraword_mod3_2.head = -1; }
ASTERISK_OPENERS_extraword_mod3_2.tail = -1;
ASTERISK_OPENERS_intraword_mod3_0.head = -1;
ASTERISK_OPENERS_intraword_mod3_0.tail = -1;
ASTERISK_OPENERS_intraword_mod3_1.head = -1;
ASTERISK_OPENERS_intraword_mod3_1.tail = -1;
ASTERISK_OPENERS_intraword_mod3_2.head = -1;
ASTERISK_OPENERS_intraword_mod3_2.tail = -1;
UNDERSCORE_OPENERS.head = -1;
UNDERSCORE_OPENERS.tail = -1;
TILDE_OPENERS.head = -1;
TILDE_OPENERS.tail = -1;
DOLLAR_OPENERS.head = -1;
DOLLAR_OPENERS.tail = -1;
} }
static int static int
@ -4113,7 +4120,23 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
} }
break; break;
case '_': case '_': /* Underline (or emphasis if we fall through). */
if(ctx->parser.flags & MD_FLAG_UNDERLINE) {
if(mark->flags & MD_MARK_OPENER) {
while(off < mark->end) {
MD_ENTER_SPAN(MD_SPAN_U, NULL);
off++;
}
} else {
while(off < mark->end) {
MD_LEAVE_SPAN(MD_SPAN_U, NULL);
off++;
}
}
break;
}
/* Fall though. */
case '*': /* Emphasis, strong emphasis. */ case '*': /* Emphasis, strong emphasis. */
if(mark->flags & MD_MARK_OPENER) { if(mark->flags & MD_MARK_OPENER) {
if((mark->end - off) % 2) { if((mark->end - off) % 2) {
@ -4159,19 +4182,18 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
{ {
const MD_MARK* opener = (mark->ch != ']' ? mark : &ctx->marks[mark->prev]); const MD_MARK* opener = (mark->ch != ']' ? mark : &ctx->marks[mark->prev]);
const MD_MARK* closer = &ctx->marks[opener->next]; const MD_MARK* closer = &ctx->marks[opener->next];
const MD_MARK* dest_mark;
const MD_MARK* title_mark;
if ((opener->ch == '[' && closer->ch == ']') && if ((opener->ch == '[' && closer->ch == ']') &&
opener->end - opener->beg >= 2 && opener->end - opener->beg >= 2 &&
closer->end - closer->beg == 2) closer->end - closer->beg >= 2)
{ {
const MD_MARK* delim = opener+3; /* Scan past the two dummy marks. */
int has_label = (opener->end - opener->beg > 2); int has_label = (opener->end - opener->beg > 2);
int target_sz; SZ target_sz;
if(has_label) if(has_label)
target_sz = opener->end - (opener->beg+2); target_sz = opener->end - (opener->beg+2);
else if(delim->ch == '|')
target_sz = (closer->beg-1) - opener->end;
else else
target_sz = closer->beg - opener->end; target_sz = closer->beg - opener->end;
@ -4182,10 +4204,9 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
break; break;
} }
const MD_MARK* dest_mark = opener+1; dest_mark = opener+1;
const MD_MARK* title_mark = opener+2;
MD_ASSERT(dest_mark->ch == 'D'); MD_ASSERT(dest_mark->ch == 'D');
title_mark = opener+2;
MD_ASSERT(title_mark->ch == 'D'); MD_ASSERT(title_mark->ch == 'D');
MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'), MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'),
@ -4557,7 +4578,7 @@ md_process_verbatim_block_contents(MD_CTX* ctx, MD_TEXTTYPE text_type, const MD_
MD_ASSERT(indent >= 0); MD_ASSERT(indent >= 0);
/* Output code indentation. */ /* Output code indentation. */
while(indent >= indent_chunk_size) { while(indent > (int) indent_chunk_size) {
MD_TEXT(text_type, indent_chunk_str, indent_chunk_size); MD_TEXT(text_type, indent_chunk_str, indent_chunk_size);
indent -= indent_chunk_size; indent -= indent_chunk_size;
} }
@ -4813,7 +4834,9 @@ md_push_block_bytes(MD_CTX* ctx, int n_bytes)
if(ctx->n_block_bytes + n_bytes > ctx->alloc_block_bytes) { if(ctx->n_block_bytes + n_bytes > ctx->alloc_block_bytes) {
void* new_block_bytes; void* new_block_bytes;
ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0 ? ctx->alloc_block_bytes * 2 : 512); ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0
? ctx->alloc_block_bytes + ctx->alloc_block_bytes / 2
: 512);
new_block_bytes = realloc(ctx->block_bytes, ctx->alloc_block_bytes); new_block_bytes = realloc(ctx->block_bytes, ctx->alloc_block_bytes);
if(new_block_bytes == NULL) { if(new_block_bytes == NULL) {
MD_LOG("realloc() failed."); MD_LOG("realloc() failed.");
@ -5003,7 +5026,6 @@ md_add_line_into_current_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* analysis)
line->beg = analysis->beg; line->beg = analysis->beg;
line->end = analysis->end; line->end = analysis->end;
line->total_indent = analysis->total_indent;
} }
ctx->current_block->n_lines++; ctx->current_block->n_lines++;
@ -5246,7 +5268,7 @@ md_is_html_block_start_condition(MD_CTX* ctx, OFF beg)
#ifdef X #ifdef X
#undef X #undef X
#endif #endif
#define X(name) { _T(name), sizeof(name)-1 } #define X(name) { _T(name), (sizeof(name)-1) / sizeof(CHAR) }
#define Xend { NULL, 0 } #define Xend { NULL, 0 }
static const TAG t1[] = { X("script"), X("pre"), X("style"), Xend }; static const TAG t1[] = { X("script"), X("pre"), X("style"), Xend };
@ -5302,7 +5324,7 @@ md_is_html_block_start_condition(MD_CTX* ctx, OFF beg)
/* Check for type 5: <![CDATA[ */ /* Check for type 5: <![CDATA[ */
if(off + 8 < ctx->size) { if(off + 8 < ctx->size) {
if(md_ascii_eq(STR(off), _T("![CDATA["), 8 * sizeof(CHAR))) if(md_ascii_eq(STR(off), _T("![CDATA["), 8))
return 5; return 5;
} }
} }
@ -5451,7 +5473,9 @@ md_push_container(MD_CTX* ctx, const MD_CONTAINER* container)
if(ctx->n_containers >= ctx->alloc_containers) { if(ctx->n_containers >= ctx->alloc_containers) {
MD_CONTAINER* new_containers; MD_CONTAINER* new_containers;
ctx->alloc_containers = (ctx->alloc_containers > 0 ? ctx->alloc_containers * 2 : 16); ctx->alloc_containers = (ctx->alloc_containers > 0
? ctx->alloc_containers + ctx->alloc_containers / 2
: 16);
new_containers = realloc(ctx->containers, ctx->alloc_containers * sizeof(MD_CONTAINER)); new_containers = realloc(ctx->containers, ctx->alloc_containers * sizeof(MD_CONTAINER));
if(new_containers == NULL) { if(new_containers == NULL) {
MD_LOG("realloc() failed."); MD_LOG("realloc() failed.");
@ -5783,7 +5807,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end,
#if 1 #if 1
/* This is 2nd half of the hack. If the flag is set (that is there /* This is 2nd half of the hack. If the flag is set (that is there
* were 2nd blank line at the start of the list item) and we would also * were 2nd blank line at the start of the list item) and we would also
* belonging to such list item, then interrupt the list. */ * belonging to such list item, than interrupt the list. */
ctx->last_line_has_list_loosening_effect = FALSE; ctx->last_line_has_list_loosening_effect = FALSE;
if(ctx->last_list_item_starts_with_two_blank_lines) { if(ctx->last_list_item_starts_with_two_blank_lines) {
if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') && if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') &&
@ -6008,8 +6032,6 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end,
break; break;
} }
line->total_indent = total_indent;
/* Scan for end of the line. /* Scan for end of the line.
* *
* Note this is quite a bottleneck of the parsing as we here iterate almost * Note this is quite a bottleneck of the parsing as we here iterate almost
@ -6245,10 +6267,8 @@ md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userd
int ret; int ret;
if(parser->abi_version != 0) { if(parser->abi_version != 0) {
#if WITH_PARSER_DEBUG_LOG
if(parser->debug_log != NULL) if(parser->debug_log != NULL)
parser->debug_log("Unsupported abi_version.", userdata); parser->debug_log("Unsupported abi_version.", userdata);
#endif
return -1; return -1;
} }

View file

@ -2,7 +2,7 @@
* MD4C: Markdown parser for C * MD4C: Markdown parser for C
* (http://github.com/mity/md4c) * (http://github.com/mity/md4c)
* *
* Copyright (c) 2016-2019 Martin Mitas * Copyright (c) 2016-2020 Martin Mitas
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -23,15 +23,15 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#ifndef MD4C_MARKDOWN_H #ifndef MD4C_H
#define MD4C_MARKDOWN_H #define MD4C_H
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if defined MD4C_USE_UTF16 #if defined MD4C_USE_UTF16
/* Magic to support UTF-16. Not that in order to use it, you have to define /* Magic to support UTF-16. Note that in order to use it, you have to define
* the macro MD4C_USE_UTF16 both when building MD4C as well as when * the macro MD4C_USE_UTF16 both when building MD4C as well as when
* including this header in your code. */ * including this header in your code. */
#ifdef _WIN32 #ifdef _WIN32
@ -119,7 +119,7 @@ typedef enum MD_SPANTYPE {
* Detail: Structure MD_SPAN_IMG_DETAIL. * Detail: Structure MD_SPAN_IMG_DETAIL.
* Note: Image text can contain nested spans and even nested images. * Note: Image text can contain nested spans and even nested images.
* If rendered into ALT attribute of HTML <IMG> tag, it's responsibility * If rendered into ALT attribute of HTML <IMG> tag, it's responsibility
* of the renderer to deal with it. * of the parser to deal with it.
*/ */
MD_SPAN_IMG, MD_SPAN_IMG,
@ -140,7 +140,11 @@ typedef enum MD_SPANTYPE {
/* Wiki links /* Wiki links
* Note: Recognized only when MD_FLAG_WIKILINKS is enabled. * Note: Recognized only when MD_FLAG_WIKILINKS is enabled.
*/ */
MD_SPAN_WIKILINK MD_SPAN_WIKILINK,
/* <u>...</u>
* Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */
MD_SPAN_U
} MD_SPANTYPE; } MD_SPANTYPE;
/* Text is the actual textual contents of span. */ /* Text is the actual textual contents of span. */
@ -167,7 +171,7 @@ typedef enum MD_TEXTTYPE {
* (c) Hexadecimal entity, e.g. &#x12AB; * (c) Hexadecimal entity, e.g. &#x12AB;
* *
* As MD4C is mostly encoding agnostic, application gets the verbatim * As MD4C is mostly encoding agnostic, application gets the verbatim
* entity text into the MD_RENDERER::text_callback(). */ * entity text into the MD_PARSER::text_callback(). */
MD_TEXT_ENTITY, MD_TEXT_ENTITY,
/* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`). /* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`).
@ -202,8 +206,13 @@ typedef enum MD_ALIGN {
* propagated within various detailed structures, but which still may contain * propagated within various detailed structures, but which still may contain
* string portions of different types like e.g. entities. * string portions of different types like e.g. entities.
* *
* So, for example, lets consider an image has a title attribute string * So, for example, lets consider this image:
* set to "foo &quot; bar". (Note the string size is 14.) *
* ![image alt text](http://example.org/image.png 'foo &quot; bar')
*
* The image alt text is propagated as a normal text via the MD_PARSER::text()
* callback. However, the image title ('foo &quot; bar') is propagated as
* MD_ATTRIBUTE in MD_SPAN_IMG_DETAIL::title.
* *
* Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following: * Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following:
* -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0) * -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0)
@ -211,10 +220,12 @@ typedef enum MD_ALIGN {
* -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10) * -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10)
* -- [3]: (n/a) (n/a ; substr_offsets[3] == 14) * -- [3]: (n/a) (n/a ; substr_offsets[3] == 14)
* *
* Note that these conditions are guaranteed: * Note that these invariants are always guaranteed:
* -- substr_offsets[0] == 0 * -- substr_offsets[0] == 0
* -- substr_offsets[LAST+1] == size * -- substr_offsets[LAST+1] == size
* -- Only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR substrings can appear. * -- Currently, only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR
* substrings can appear. This could change only of the specification
* changes.
*/ */
typedef struct MD_ATTRIBUTE { typedef struct MD_ATTRIBUTE {
const MD_CHAR* text; const MD_CHAR* text;
@ -280,7 +291,7 @@ typedef struct MD_SPAN_WIKILINK {
/* Flags specifying extensions/deviations from CommonMark specification. /* Flags specifying extensions/deviations from CommonMark specification.
* *
* By default (when MD_RENDERER::flags == 0), we follow CommonMark specification. * By default (when MD_PARSER::flags == 0), we follow CommonMark specification.
* The following flags may allow some extensions or deviations from it. * The following flags may allow some extensions or deviations from it.
*/ */
#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */ #define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */
@ -296,6 +307,7 @@ typedef struct MD_SPAN_WIKILINK {
#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */ #define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */
#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */ #define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */
#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */ #define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */
#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */
#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS) #define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS)
#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS) #define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS)
@ -312,7 +324,7 @@ typedef struct MD_SPAN_WIKILINK {
#define MD_DIALECT_COMMONMARK 0 #define MD_DIALECT_COMMONMARK 0
#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS) #define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS)
/* Renderer structure. /* Parser structure.
*/ */
typedef struct MD_PARSER { typedef struct MD_PARSER {
/* Reserved. Set to zero. /* Reserved. Set to zero.
@ -333,9 +345,10 @@ typedef struct MD_PARSER {
* *
* Note any strings provided to the callbacks as their arguments or as * Note any strings provided to the callbacks as their arguments or as
* members of any detail structure are generally not zero-terminated. * members of any detail structure are generally not zero-terminated.
* Application has take the respective size information into account. * Application has to take the respective size information into account.
* *
* Callbacks may abort further parsing of the document by returning non-zero. * Any rendering callback may abort further parsing of the document by
* returning non-zero.
*/ */
int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/); int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/); int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
@ -360,18 +373,19 @@ typedef struct MD_PARSER {
} MD_PARSER; } MD_PARSER;
/* For backward compatibility. Do not use in new code. */ /* For backward compatibility. Do not use in new code.
*/
typedef MD_PARSER MD_RENDERER; typedef MD_PARSER MD_RENDERER;
/* Parse the Markdown document stored in the string 'text' of size 'size'. /* Parse the Markdown document stored in the string 'text' of size 'size'.
* The renderer provides callbacks to be called during the parsing so the * The parser provides callbacks to be called during the parsing so the
* caller can render the document on the screen or convert the Markdown * caller can render the document on the screen or convert the Markdown
* to another format. * to another format.
* *
* Zero is returned on success. If a runtime error occurs (e.g. a memory * Zero is returned on success. If a runtime error occurs (e.g. a memory
* fails), -1 is returned. If the processing is aborted due any callback * fails), -1 is returned. If the processing is aborted due any callback
* returning non-zero, md_parse() the return value of the callback is returned. * returning non-zero, the return value of the callback is returned.
*/ */
int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata); int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata);
@ -380,4 +394,4 @@ int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* u
} /* extern "C" { */ } /* extern "C" { */
#endif #endif
#endif /* MD4C_MARKDOWN_H */ #endif /* MD4C_H */