cmark

My personal build of CMark ✏️

Commit
46330867a8d82bf45f2ea575b93c5f0d4496ca0c
Parent
c6460e00ea05da186e7d0ea7690b28f2b98a0ed9
Author
John MacFarlane <jgm@berkeley.edu>
Date

Restructured common renderer code.

* Added functions for cr, blankline, out to renderer object. * Removed lit (we'll handle this with a macro). * Changed type of out so it takes a regular string instead of a chunk. * Use macros LIT, OUT, BLANKLINE, CR in renderers to simplify code. (Not sure about this, but `renderer->out(renderer, ...)` sure is verbose.)

Diffstat

4 files changed, 198 insertions, 200 deletions

Status File Name N° Changes Insertions Deletions
Modified src/commonmark.c 186 95 91
Modified src/latex.c 166 84 82
Modified src/render.c 29 13 16
Modified src/render.h 17 6 11
diff --git 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, "![", 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) {
+				OUT(" \"", true, LITERAL);
+				OUT(title, false, TITLE);
+				LIT("\"");
 			}
-			lit(renderer, ")", false);
+			LIT(")");
 		}
 		break;
 
diff --git 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
@@ -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
@@ -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,