diff options
| -rw-r--r-- | src/commonmark.c | 186 | ||||
| -rw-r--r-- | src/latex.c | 166 | ||||
| -rw-r--r-- | src/render.c | 29 | ||||
| -rw-r--r-- | src/render.h | 17 | 
4 files changed, 198 insertions, 200 deletions
| diff --git a/src/commonmark.c b/src/commonmark.c index 5bbc8d4..274d4bc 100644 --- a/src/commonmark.c +++ b/src/commonmark.c @@ -12,6 +12,12 @@  #include "scanners.h"  #include "render.h" +#define safe_strlen(s) cmark_strbuf_safe_strlen(s) +#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) +#define LIT(s) renderer->out(renderer, s, false, LITERAL) +#define CR() renderer->cr(renderer) +#define BLANKLINE() renderer->blankline(renderer) +  // Functions to convert cmark_nodes to commonmark strings.  static inline void outc(cmark_renderer *renderer, @@ -58,13 +64,14 @@ static inline void outc(cmark_renderer *renderer,  }  static int -longest_backtick_sequence(cmark_chunk *code) +longest_backtick_sequence(const char *code)  {  	int longest = 0;  	int current = 0; -	int i = 0; -	while (i <= code->len) { -		if (code->data[i] == '`') { +	size_t i = 0; +	size_t code_len = safe_strlen(code); +	while (i <= code_len) { +		if (code[i] == '`') {  			current++;  		} else {  			if (current > longest) { @@ -78,13 +85,14 @@ longest_backtick_sequence(cmark_chunk *code)  }  static int -shortest_unused_backtick_sequence(cmark_chunk *code) +shortest_unused_backtick_sequence(const char *code)  {  	int32_t used = 1;  	int current = 0; -	int i = 0; -	while (i <= code->len) { -		if (code->data[i] == '`') { +	size_t i = 0; +	size_t code_len = safe_strlen(code); +	while (i <= code_len) { +		if (code[i] == '`') {  			current++;  		} else {  			if (current) { @@ -159,14 +167,13 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,                cmark_renderer *renderer)  {  	cmark_node *tmp; -	cmark_chunk *code;  	int list_number;  	cmark_delim_type list_delim;  	int numticks;  	int i;  	bool entering = (ev_type == CMARK_EVENT_ENTER); -	cmark_chunk *info; -	cmark_chunk *title; +	size_t info_len; +	size_t code_len;  	cmark_strbuf listmarker = GH_BUF_INIT;  	char *emph_delim;  	bufsize_t marker_width; @@ -195,12 +202,12 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  	case CMARK_NODE_BLOCK_QUOTE:  		if (entering) { -			lit(renderer, "> ", false); +			LIT("> ");  			cmark_strbuf_puts(renderer->prefix, "> ");  		} else {  			cmark_strbuf_truncate(renderer->prefix,  			                      renderer->prefix->size - 2); -			blankline(renderer); +			BLANKLINE();  		}  		break; @@ -210,7 +217,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  		     node->next->type == CMARK_NODE_LIST)) {  			// this ensures 2 blank lines after list,  			// if before code block or list: -			lit(renderer, "\n", false); +			LIT("\n");  			renderer->need_cr = 0;  		}  		break; @@ -240,10 +247,10 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  		if (entering) {  			if (cmark_node_get_list_type(node->parent) ==  			    CMARK_BULLET_LIST) { -				lit(renderer, "* ", false); +				LIT("* ");  				cmark_strbuf_puts(renderer->prefix, "  ");  			} else { -				lit(renderer, (char *)listmarker.ptr, false); +				LIT((char *)listmarker.ptr);  				for (i = marker_width; i--;) {  					cmark_strbuf_putc(renderer->prefix, ' ');  				} @@ -252,7 +259,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  			cmark_strbuf_truncate(renderer->prefix,  			                      renderer->prefix->size -  			                      marker_width); -			cr(renderer); +			CR();  		}  		cmark_strbuf_free(&listmarker);  		break; @@ -260,33 +267,35 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  	case CMARK_NODE_HEADER:  		if (entering) {  			for (int i = cmark_node_get_header_level(node); i > 0; i--) { -				lit(renderer, "#", false); +				LIT("#");  			} -			lit(renderer, " ", false); +			LIT(" ");  			renderer->no_wrap = true;  		} else {  			renderer->no_wrap = false; -			blankline(renderer); +			BLANKLINE();  		}  		break;  	case CMARK_NODE_CODE_BLOCK: -		blankline(renderer); -		info = &node->as.code.info; -		code = &node->as.code.literal; +		BLANKLINE(); +		const char* info = cmark_node_get_fence_info(node); +		info_len = safe_strlen(info); +		const char* code = cmark_node_get_literal(node); +		code_len = safe_strlen(code);  		// use indented form if no info, and code doesn't  		// begin or end with a blank line, and code isn't  		// first thing in a list item -		if (info->len == 0 && -		    (code->len > 2 && -		     !isspace(code->data[0]) && -		     !(isspace(code->data[code->len - 1]) && -		       isspace(code->data[code->len - 2]))) && +		if (info_len == 0 && +		    (code_len > 2 && +		     !isspace(code[0]) && +		     !(isspace(code[code_len - 1]) && +		       isspace(code[code_len - 2]))) &&  		    !(node->prev == NULL && node->parent &&  		      node->parent->type == CMARK_NODE_ITEM)) { -			lit(renderer, "    ", false); +			LIT("    ");  			cmark_strbuf_puts(renderer->prefix, "    "); -			out(renderer, node->as.code.literal, false, LITERAL); +			OUT(cmark_node_get_literal(node), false, LITERAL);  			cmark_strbuf_truncate(renderer->prefix,  			                      renderer->prefix->size - 4);  		} else { @@ -295,84 +304,85 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  				numticks = 3;  			}  			for (i = 0; i < numticks; i++) { -				lit(renderer, "`", false); +				LIT("`");  			} -			lit(renderer, " ", false); -			out(renderer, *info, false, LITERAL); -			cr(renderer); -			out(renderer, node->as.code.literal, false, LITERAL); -			cr(renderer); +			LIT(" "); +			OUT(info, false, LITERAL); +			CR(); +			OUT(cmark_node_get_literal(node), false, LITERAL); +			CR();  			for (i = 0; i < numticks; i++) { -				lit(renderer, "`", false); +				LIT("`");  			}  		} -		blankline(renderer); +		BLANKLINE();  		break;  	case CMARK_NODE_HTML: -		blankline(renderer); -		out(renderer, node->as.literal, false, LITERAL); -		blankline(renderer); +		BLANKLINE(); +		OUT(cmark_node_get_literal(node), false, LITERAL); +		BLANKLINE();  		break;  	case CMARK_NODE_HRULE: -		blankline(renderer); -		lit(renderer, "-----", false); -		blankline(renderer); +		BLANKLINE(); +		LIT("-----"); +		BLANKLINE();  		break;  	case CMARK_NODE_PARAGRAPH:  		if (!entering) { -			blankline(renderer); +			BLANKLINE();  		}  		break;  	case CMARK_NODE_TEXT: -		out(renderer, node->as.literal, true, NORMAL); +		OUT(cmark_node_get_literal(node), true, NORMAL);  		break;  	case CMARK_NODE_LINEBREAK:  		if (!(CMARK_OPT_HARDBREAKS & renderer->options)) { -			lit(renderer, "\\", false); +			LIT("\\");  		} -		cr(renderer); +		CR();  		break;  	case CMARK_NODE_SOFTBREAK:  		if (renderer->width == 0) { -			cr(renderer); +			CR();  		} else { -			lit(renderer, " ", true); +			OUT(" ", true, LITERAL);  		}  		break;  	case CMARK_NODE_CODE: -		code = &node->as.literal; +		code = cmark_node_get_literal(node); +		code_len = safe_strlen(code);  		numticks = shortest_unused_backtick_sequence(code);  		for (i = 0; i < numticks; i++) { -			lit(renderer, "`", false); +			LIT("`");  		} -		if (code->len == 0 || code->data[0] == '`') { -			lit(renderer, " ", false); +		if (code_len == 0 || code[0] == '`') { +			LIT(" ");  		} -		out(renderer, node->as.literal, true, LITERAL); -		if (code->len == 0 || code->data[code->len - 1] == '`') { -			lit(renderer, " ", false); +		OUT(cmark_node_get_literal(node), true, LITERAL); +		if (code_len == 0 || code[code_len - 1] == '`') { +			LIT(" ");  		}  		for (i = 0; i < numticks; i++) { -			lit(renderer, "`", false); +			LIT("`");  		}  		break;  	case CMARK_NODE_INLINE_HTML: -		out(renderer, node->as.literal, false, LITERAL); +		OUT(cmark_node_get_literal(node), false, LITERAL);  		break;  	case CMARK_NODE_STRONG:  		if (entering) { -			lit(renderer, "**", false); +			LIT("**");  		} else { -			lit(renderer, "**", false); +			LIT("**");  		}  		break; @@ -386,62 +396,56 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  			emph_delim = "*";  		}  		if (entering) { -			lit(renderer, emph_delim, false); +			LIT(emph_delim);  		} else { -			lit(renderer, emph_delim, false); +			LIT(emph_delim);  		}  		break;  	case CMARK_NODE_LINK:  		if (is_autolink(node)) {  			if (entering) { -				lit(renderer, "<", false); +				LIT("<");  				if (strncmp(cmark_node_get_url(node),  				            "mailto:", 7) == 0) { -					lit(renderer, -					    (char *)cmark_node_get_url(node) + 7, -					    false); +					LIT((char *)cmark_node_get_url(node) + 7);  				} else { -					lit(renderer, -					    (char *)cmark_node_get_url(node), -					    false); +					LIT((char *)cmark_node_get_url(node));  				} -				lit(renderer, ">", false); +				LIT(">");  				// return signal to skip contents of node...  				return 0;  			}  		} else {  			if (entering) { -				lit(renderer, "[", false); +				LIT("[");  			} else { -				lit(renderer, "](", false); -				out(renderer, -				    cmark_chunk_literal(cmark_node_get_url(node)), -				    false, URL); -				title = &node->as.link.title; -				if (title->len > 0) { -					lit(renderer, " \"", true); -					out(renderer, *title, false, TITLE); -					lit(renderer, "\"", false); +				LIT("]("); +				OUT(cmark_node_get_url(node), false, URL); +				const char* title = cmark_node_get_title(node); +				if (safe_strlen(title) > 0) { +					LIT(" \""); +					OUT(title, false, TITLE); +					LIT("\"");  				} -				lit(renderer, ")", false); +				LIT(")");  			}  		}  		break;  	case CMARK_NODE_IMAGE:  		if (entering) { -			lit(renderer, "; -			out(renderer, cmark_chunk_literal(cmark_node_get_url(node)), false, URL); -			title = &node->as.link.title; -			if (title->len > 0) { -				lit(renderer, " \"", true); -				out(renderer, *title, false, TITLE); -				lit(renderer, "\"", false); +			LIT("]("); +			OUT(cmark_node_get_url(node), false, URL); +			const char* title = cmark_node_get_title(node); +			if (safe_strlen(title) > 0) { +				OUT(" \"", true, LITERAL); +				OUT(title, false, TITLE); +				LIT("\"");  			} -			lit(renderer, ")", false); +			LIT(")");  		}  		break; diff --git a/src/latex.c b/src/latex.c index 6cca96e..058f212 100644 --- a/src/latex.c +++ b/src/latex.c @@ -12,6 +12,12 @@  #include "scanners.h"  #include "render.h" +#define safe_strlen(s) cmark_strbuf_safe_strlen(s) +#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) +#define LIT(s) renderer->out(renderer, s, false, LITERAL) +#define CR() renderer->cr(renderer) +#define BLANKLINE() renderer->blankline(renderer) +  static inline void outc(cmark_renderer *renderer,  			cmark_escaping escape,  			int32_t c, @@ -188,8 +194,7 @@ typedef enum  {  static link_type  get_link_type(cmark_node *node)  { -	cmark_chunk *title; -	cmark_chunk *url; +	size_t title_len, url_len;  	cmark_node *link_text;  	char *realurl;  	int realurllen; @@ -199,21 +204,25 @@ get_link_type(cmark_node *node)  		return NO_LINK;  	} -	url = &node->as.link.url; -	if (url->len == 0 || scan_scheme(url, 0) == 0) { +	const char* url = cmark_node_get_url(node); +	cmark_chunk url_chunk = cmark_chunk_literal(url); + +	url_len = safe_strlen(url); +	if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) {  		return NO_LINK;  	} -	title = &node->as.link.title; +	const char* title = cmark_node_get_title(node); +	title_len = safe_strlen(title);  	// if it has a title, we can't treat it as an autolink: -	if (title->len > 0) { +	if (title_len > 0) {  		return NORMAL_LINK;  	}  	link_text = node->first_child;  	cmark_consolidate_text_nodes(link_text); -	realurl = (char*)url->data; -	realurllen = url->len; +	realurl = (char*)url; +	realurllen = url_len;  	if (strncmp(realurl, "mailto:", 7) == 0) {  		realurl += 7;  		realurllen -= 7; @@ -255,7 +264,6 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  	char list_number_string[20];  	bool entering = (ev_type == CMARK_EVENT_ENTER);  	cmark_list_type list_type; -	cmark_chunk url;  	const char* roman_numerals[] = { "", "i", "ii", "iii", "iv", "v",  	                                 "vi", "vii", "viii", "ix", "x"  	                               }; @@ -284,11 +292,11 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  	case CMARK_NODE_BLOCK_QUOTE:  		if (entering) { -			lit(renderer, "\\begin{quote}", false); -			cr(renderer); +			LIT("\\begin{quote}"); +			CR();  		} else { -			lit(renderer, "\\end{quote}", false); -			blankline(renderer); +			LIT("\\end{quote}"); +			BLANKLINE();  		}  		break; @@ -298,44 +306,39 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  			if (list_type == CMARK_ORDERED_LIST) {  				renderer->enumlevel++;  			} -			lit(renderer, "\\begin{", false); -			lit(renderer, -			    list_type == CMARK_ORDERED_LIST ? -			    "enumerate" : "itemize", false); -			lit(renderer, "}", false); -			cr(renderer); +			LIT("\\begin{"); +			LIT(list_type == CMARK_ORDERED_LIST ? +			    "enumerate" : "itemize"); +			LIT("}"); +			CR();  			list_number = cmark_node_get_list_start(node);  			if (list_number > 1) {  				sprintf(list_number_string,  				         "%d", list_number); -				lit(renderer, "\\setcounter{enum", false); -				lit(renderer, (char *)roman_numerals[renderer->enumlevel], -				    false); -				lit(renderer, "}{", false); -				out(renderer, -				    cmark_chunk_literal(list_number_string), -				    false, NORMAL); -				lit(renderer, "}", false); -				cr(renderer); +				LIT("\\setcounter{enum"); +				LIT((char *)roman_numerals[renderer->enumlevel]); +				LIT("}{"); +				OUT(list_number_string, false, NORMAL); +				LIT("}"); +				CR();  			}  		} else {  			if (list_type == CMARK_ORDERED_LIST) {  				renderer->enumlevel--;  			} -			lit(renderer, "\\end{", false); -			lit(renderer, -			    list_type == CMARK_ORDERED_LIST ? -			    "enumerate" : "itemize", false); -			lit(renderer, "}", false); -			blankline(renderer); +			LIT("\\end{"); +			LIT(list_type == CMARK_ORDERED_LIST ? +			    "enumerate" : "itemize"); +			LIT("}"); +			BLANKLINE();  		}  		break;  	case CMARK_NODE_ITEM:  		if (entering) { -			lit(renderer, "\\item ", false); +			LIT("\\item ");  		} else { -			cr(renderer); +			CR();  		}  		break; @@ -343,74 +346,74 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  		if (entering) {  			switch (cmark_node_get_header_level(node)) {  			case 1: -				lit(renderer, "\\section", false); +				LIT("\\section");  				break;  			case 2: -				lit(renderer, "\\subsection", false); +				LIT("\\subsection");  				break;  			case 3: -				lit(renderer, "\\subsubsection", false); +				LIT("\\subsubsection");  				break;  			case 4: -				lit(renderer, "\\paragraph", false); +				LIT("\\paragraph");  				break;  			case 5: -				lit(renderer, "\\subparagraph", false); +				LIT("\\subparagraph");  				break;  			} -			lit(renderer, "{", false); +			LIT("{");  		} else { -			lit(renderer, "}", false); -			blankline(renderer); +			LIT("}"); +			BLANKLINE();  		}  		break;  	case CMARK_NODE_CODE_BLOCK: -		cr(renderer); -		lit(renderer, "\\begin{verbatim}", false); -		cr(renderer); -		out(renderer, node->as.code.literal, false, LITERAL); -		cr(renderer); -		lit(renderer, "\\end{verbatim}", false); -		blankline(renderer); +		CR(); +		LIT("\\begin{verbatim}"); +		CR(); +		OUT(cmark_node_get_literal(node), false, LITERAL); +		CR(); +		LIT("\\end{verbatim}"); +		BLANKLINE();  		break;  	case CMARK_NODE_HTML:  		break;  	case CMARK_NODE_HRULE: -		blankline(renderer); -		lit(renderer, "\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}", false); -		blankline(renderer); +		BLANKLINE(); +		LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}"); +		BLANKLINE();  		break;  	case CMARK_NODE_PARAGRAPH:  		if (!entering) { -			blankline(renderer); +			BLANKLINE();  		}  		break;  	case CMARK_NODE_TEXT: -		out(renderer, node->as.literal, true, NORMAL); +		OUT(cmark_node_get_literal(node), true, NORMAL);  		break;  	case CMARK_NODE_LINEBREAK: -		lit(renderer, "\\\\", false); -		cr(renderer); +		LIT("\\\\"); +		CR();  		break;  	case CMARK_NODE_SOFTBREAK:  		if (renderer->width == 0) { -			cr(renderer); +			CR();  		} else { -			lit(renderer, " ", true); +			OUT(" ", true, NORMAL);  		}  		break;  	case CMARK_NODE_CODE: -		lit(renderer, "\\texttt{", false); -		out(renderer, node->as.literal, false, NORMAL); -		lit(renderer, "}", false); +		LIT("\\texttt{"); +		OUT(cmark_node_get_literal(node), false, NORMAL); +		LIT("}");  		break;  	case CMARK_NODE_INLINE_HTML: @@ -418,55 +421,54 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,  	case CMARK_NODE_STRONG:  		if (entering) { -			lit(renderer, "\\textbf{", false); +			LIT("\\textbf{");  		} else { -			lit(renderer, "}", false); +			LIT("}");  		}  		break;  	case CMARK_NODE_EMPH:  		if (entering) { -			lit(renderer, "\\emph{", false); +			LIT("\\emph{");  		} else { -			lit(renderer, "}", false); +			LIT("}");  		}  		break;  	case CMARK_NODE_LINK:  		if (entering) { -			url = cmark_chunk_literal(cmark_node_get_url(node)); +			const char* url = cmark_node_get_url(node);  			// requires \usepackage{hyperref}  			switch(get_link_type(node)) {  			case URL_AUTOLINK: -				lit(renderer, "\\url{", false); -				out(renderer, url, false, URL); +				LIT("\\url{"); +				OUT(url, false, URL);  				break;  			case EMAIL_AUTOLINK: -				lit(renderer, "\\href{", false); -				out(renderer, url, false, URL); -				lit(renderer, "}\\nolinkurl{", false); +				LIT("\\href{"); +				OUT(url, false, URL); +				LIT("}\\nolinkurl{");  				break;  			case NORMAL_LINK: -				lit(renderer, "\\href{", false); -				out(renderer, url, false, URL); -				lit(renderer, "}{", false); +				LIT("\\href{"); +				OUT(url, false, URL); +				LIT("}{");  				break;  			case NO_LINK: -				lit(renderer, "{", false);  // error? +				LIT("{");  // error?  			}  		} else { -			lit(renderer, "}", false); +			LIT("}");  		}  		break;  	case CMARK_NODE_IMAGE:  		if (entering) { -			url = cmark_chunk_literal(cmark_node_get_url(node)); -			lit(renderer, "\\protect\\includegraphics{", false); +			LIT("\\protect\\includegraphics{");  			// requires \include{graphicx} -			out(renderer, url, false, URL); -			lit(renderer, "}", false); +			OUT(cmark_node_get_url(node), false, URL); +			LIT("}");  			return 0;  		}  		break; diff --git a/src/render.c b/src/render.c index 442a7fc..16caadc 100644 --- a/src/render.c +++ b/src/render.c @@ -5,27 +5,29 @@  #include "utf8.h"  #include "render.h" -void cr(cmark_renderer *renderer) +static inline +void S_cr(cmark_renderer *renderer)  {  	if (renderer->need_cr < 1) {  		renderer->need_cr = 1;  	}  } -void blankline(cmark_renderer *renderer) +static inline +void S_blankline(cmark_renderer *renderer)  {  	if (renderer->need_cr < 2) {  		renderer->need_cr = 2;  	}  } -void out(cmark_renderer *renderer, -	 cmark_chunk str, -	 bool wrap, -	 cmark_escaping escape) +static +void S_out(cmark_renderer *renderer, +	   const char *source, +	   bool wrap, +	   cmark_escaping escape)  { -	unsigned char* source = str.data; -	int length = str.len; +	int length = cmark_strbuf_safe_strlen(source);  	unsigned char nextc;  	int32_t c;  	int i = 0; @@ -61,7 +63,7 @@ void out(cmark_renderer *renderer,  			renderer->column = renderer->prefix->size;  		} -		len = utf8proc_iterate(source + i, length - i, &c); +		len = utf8proc_iterate((const uint8_t *)source + i, length - i, &c);  		if (len == -1) { // error condition  			return;  // return without rendering rest of string  		} @@ -114,12 +116,6 @@ void out(cmark_renderer *renderer,  	}  } -void lit(cmark_renderer *renderer, char *s, bool wrap) -{ -	cmark_chunk str = cmark_chunk_literal(s); -	out(renderer, str, wrap, LITERAL); -} -  char*  cmark_render(cmark_node *root,  	     int options, @@ -145,7 +141,8 @@ cmark_render(cmark_node *root,  	}  	cmark_renderer renderer = { options, &buf, &pref, 0, width, -				     0, 0, 0, true, false, false, outc }; +				    0, 0, 0, true, false, false, +	                            outc, S_cr, S_blankline, S_out };  	while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {  		cur = cmark_iter_get_node(iter); diff --git a/src/render.h b/src/render.h index 718050e..cf5b078 100644 --- a/src/render.h +++ b/src/render.h @@ -32,21 +32,16 @@ struct cmark_renderer {  		     cmark_escaping,  		     int32_t,  		     unsigned char); +	void (*cr)(struct cmark_renderer*); +	void (*blankline)(struct cmark_renderer*); +	void (*out)(struct cmark_renderer*, +		    const char *, +		    bool, +		    cmark_escaping);  };  typedef struct cmark_renderer cmark_renderer; -void cr(cmark_renderer *renderer); - -void blankline(cmark_renderer *renderer); - -void out(cmark_renderer *renderer, -	 cmark_chunk str, -	 bool wrap, -	 cmark_escaping escape); - -void lit(cmark_renderer *renderer, char *s, bool wrap); -  char*  cmark_render(cmark_node *root,  	     int options, | 
