cmark

My personal build of CMark ✏️

Commit
463d64219b2f47c9bfb50d05a7f4d2b2a4293a97
Parent
24643bde1d2c79cc512242379868efadf653c1da
Author
Nick Wellnhofer <wellnhofer@aevum.de>
Date

Switch cmark_node_inl over to cmark_node

Diffstat

10 files changed, 171 insertions, 190 deletions

Status File Name N° Changes Insertions Deletions
Modified src/ast.h 51 26 25
Modified src/blocks.c 2 1 1
Modified src/cmark.c 67 22 45
Modified src/cmark.h 5 1 4
Modified src/html/html.c 100 52 48
Modified src/inlines.c 75 38 37
Modified src/inlines.h 2 1 1
Modified src/main.c 2 1 1
Modified src/node.h 5 2 3
Modified src/print.c 52 27 25
diff --git a/src/ast.h b/src/ast.h
@@ -3,6 +3,7 @@
 
 #include <stdio.h>
 #include "config.h"
+#include "node.h"
 #include "buffer.h"
 #include "chunk.h"
 #include "cmark.h"
@@ -97,66 +98,66 @@ struct cmark_doc_parser {
 
 unsigned char *cmark_clean_autolink(chunk *url, int is_email);
 
-static inline cmark_node_inl *cmark_make_link(cmark_node_inl *label, unsigned char *url, unsigned char *title)
+static inline cmark_node *cmark_make_link(cmark_node *label, unsigned char *url, unsigned char *title)
 {
-	cmark_node_inl* e = (cmark_node_inl *)calloc(1, sizeof(*e));
+	cmark_node* e = (cmark_node *)calloc(1, sizeof(*e));
 	if(e != NULL) {
-		e->tag = CMARK_INL_LINK;
-		e->content.linkable.label = label;
-		e->content.linkable.url   = url;
-		e->content.linkable.title = title;
+		e->type = CMARK_NODE_LINK;
+		e->as.link.label = label;
+		e->as.link.url   = url;
+		e->as.link.title = title;
 		e->next = NULL;
 	}
 	return e;
 }
 
-static inline cmark_node_inl* cmark_make_autolink(cmark_node_inl* label, cmark_chunk url, int is_email)
+static inline cmark_node* cmark_make_autolink(cmark_node* label, cmark_chunk url, int is_email)
 {
 	return cmark_make_link(label, cmark_clean_autolink(&url, is_email), NULL);
 }
 
-static inline cmark_node_inl* cmark_make_inlines(cmark_inl_tag t, cmark_node_inl* contents)
+static inline cmark_node* cmark_make_inlines(cmark_node_type t, cmark_node* contents)
 {
-	cmark_node_inl * e = (cmark_node_inl *)calloc(1, sizeof(*e));
+	cmark_node * e = (cmark_node *)calloc(1, sizeof(*e));
 	if(e != NULL) {
-		e->tag = t;
-		e->content.inlines = contents;
+		e->type = t;
+		e->first_child = contents;
 		e->next = NULL;
 	}
 	return e;
 }
 
 // Create an inline with a literal string value.
-static inline cmark_node_inl* cmark_make_literal(cmark_inl_tag t, cmark_chunk s)
+static inline cmark_node* cmark_make_literal(cmark_node_type t, cmark_chunk s)
 {
-	cmark_node_inl * e = (cmark_node_inl *)calloc(1, sizeof(*e));
+	cmark_node * e = (cmark_node *)calloc(1, sizeof(*e));
 	if(e != NULL) {
-		e->tag = t;
-		e->content.literal = s;
+		e->type = t;
+		e->as.literal = s;
 		e->next = NULL;
 	}
 	return e;
 }
 
 // Create an inline with no value.
-static inline cmark_node_inl* cmark_make_simple(cmark_inl_tag t)
+static inline cmark_node* cmark_make_simple(cmark_node_type t)
 {
-	cmark_node_inl* e = (cmark_node_inl *)calloc(1, sizeof(*e));
+	cmark_node* e = (cmark_node *)calloc(1, sizeof(*e));
 	if(e != NULL) {
-		e->tag = t;
+		e->type = t;
 		e->next = NULL;
 	}
 	return e;
 }
 
 // Macros for creating various kinds of simple.
-#define cmark_make_str(s) cmark_make_literal(INL_STRING, s)
-#define cmark_make_code(s) cmark_make_literal(INL_CODE, s)
-#define cmark_make_raw_html(s) cmark_make_literal(INL_RAW_HTML, s)
-#define cmark_make_linebreak() cmark_make_simple(INL_LINEBREAK)
-#define cmark_make_softbreak() cmark_make_simple(INL_SOFTBREAK)
-#define cmark_make_emph(contents) cmark_make_inlines(INL_EMPH, contents)
-#define cmark_make_strong(contents) cmark_make_inlines(INL_STRONG, contents)
+#define cmark_make_str(s) cmark_make_literal(CMARK_NODE_STRING, s)
+#define cmark_make_code(s) cmark_make_literal(CMARK_NODE_INLINE_CODE, s)
+#define cmark_make_raw_html(s) cmark_make_literal(CMARK_NODE_INLINE_HTML, s)
+#define cmark_make_linebreak() cmark_make_simple(CMARK_NODE_LINEBREAK)
+#define cmark_make_softbreak() cmark_make_simple(CMARK_NODE_SOFTBREAK)
+#define cmark_make_emph(contents) cmark_make_inlines(CMARK_NODE_EMPH, contents)
+#define cmark_make_strong(contents) cmark_make_inlines(CMARK_NODE_STRONG, contents)
 
 
 
diff --git a/src/blocks.c b/src/blocks.c
@@ -295,7 +295,7 @@ static void process_inlines(cmark_node* cur, reference_map *refmap)
 			case NODE_PARAGRAPH:
 			case NODE_ATX_HEADER:
 			case NODE_SETEXT_HEADER:
-				cur->inline_content = parse_inlines(&cur->string_content, refmap);
+				cur->first_child = parse_inlines(&cur->string_content, refmap);
 				break;
 
 			default:
diff --git a/src/cmark.c b/src/cmark.c
@@ -70,14 +70,14 @@ unsigned char *cmark_markdown_to_html(unsigned char *text, int len)
 	blocks = cmark_parse_document(text, len);
 
 	result = cmark_render_html(blocks);
-	cmark_free_blocks(blocks);
+	cmark_free_nodes(blocks);
 
 	return result;
 }
 
-// Utility function used by free_inlines
-static void splice_into_list(cmark_node_inl* e, node_inl* children) {
-	cmark_node_inl * tmp;
+// Utility function used by cmark_free_nodes
+static void splice_into_list(cmark_node* e, cmark_node* children) {
+	cmark_node * tmp;
 	if (children) {
 		tmp = children;
 		// Find last child
@@ -91,43 +91,6 @@ static void splice_into_list(cmark_node_inl* e, node_inl* children) {
 	return ;
 }
 
-// Free an inline list.  Avoid recursion to prevent stack overflows
-// on deeply nested structures.
-void cmark_free_inlines(cmark_node_inl* e)
-{
-	node_inl * next;
-
-	while (e != NULL) {
-		switch (e->tag){
-		case CMARK_INL_STRING:
-		case CMARK_INL_RAW_HTML:
-		case CMARK_INL_CODE:
-			cmark_chunk_free(&e->content.literal);
-			break;
-		case CMARK_INL_LINEBREAK:
-		case CMARK_INL_SOFTBREAK:
-			break;
-		case CMARK_INL_LINK:
-		case CMARK_INL_IMAGE:
-			free(e->content.linkable.url);
-			free(e->content.linkable.title);
-			splice_into_list(e, e->content.linkable.label);
-			break;
-		case CMARK_INL_EMPH:
-		case CMARK_INL_STRONG:
-		        splice_into_list(e, e->content.inlines);
-			break;
-		default:
-		        fprintf(stderr, "[WARN] (%s:%d) Unknown inline tag %d",
-				__FILE__, __LINE__, e->tag);
-			break;
-		}
-		next = e->next;
-		free(e);
-		e = next;
-	}
-}
-
 unsigned char *cmark_clean_autolink(chunk *url, int is_email)
 {
 	strbuf buf = GH_BUF_INIT;
@@ -144,15 +107,29 @@ unsigned char *cmark_clean_autolink(chunk *url, int is_email)
 	return strbuf_detach(&buf);
 }
 
-// Free a node_block list and any children.
-void cmark_free_blocks(cmark_node *e)
+// Free a cmark_node list and any children.
+void cmark_free_nodes(cmark_node *e)
 {
 	cmark_node *next;
 	while (e != NULL) {
-		cmark_free_inlines(e->inline_content);
 		strbuf_free(&e->string_content);
-		if (e->type == NODE_FENCED_CODE) {
+		switch (e->type){
+		case NODE_FENCED_CODE:
 			strbuf_free(&e->as.code.info);
+			break;
+		case NODE_STRING:
+		case NODE_INLINE_HTML:
+		case NODE_INLINE_CODE:
+			cmark_chunk_free(&e->as.literal);
+			break;
+		case NODE_LINK:
+		case NODE_IMAGE:
+			free(e->as.link.url);
+			free(e->as.link.title);
+			splice_into_list(e, e->as.link.label);
+			break;
+		default:
+			break;
 		}
 		if (e->last_child) {
 			// Splice children into list
diff --git a/src/cmark.h b/src/cmark.h
@@ -80,10 +80,7 @@ CMARK_EXPORT
 unsigned char *cmark_markdown_to_html(unsigned char *text, int len);
 
 CMARK_EXPORT
-void cmark_free_blocks(cmark_node *e);
-
-CMARK_EXPORT
-void cmark_free_inlines(cmark_node_inl* e);
+void cmark_free_nodes(cmark_node *e);
 
 CMARK_EXPORT
 cmark_node_block *cmark_block_next(cmark_node_block *current);
diff --git a/src/html/html.c b/src/html/html.c
@@ -15,7 +15,7 @@ typedef struct RenderStack {
 	struct RenderStack *previous;
 	char* literal;
 	union {
-		node_inl *inl;
+		cmark_node *inl;
 		cmark_node *block;
 	} next_sibling;
 	bool tight;
@@ -33,7 +33,7 @@ static void free_render_stack(render_stack * rstack)
 }
 
 static render_stack* push_inline(render_stack* rstack,
-				 node_inl* inl,
+				 cmark_node* inl,
 				 char* literal)
 {
 	render_stack* newstack;
@@ -106,39 +106,41 @@ static inline void cr(strbuf *html)
 }
 
 // Convert an inline list to HTML.  Returns 0 on success, and sets result.
-static void inlines_to_plain_html(strbuf *html, node_inl* ils)
+static void inlines_to_plain_html(strbuf *html, cmark_node* ils)
 {
-	node_inl* children;
+	cmark_node* children;
 	bool visit_children;
 	render_stack* rstack = NULL;
 
 	while(ils != NULL) {
 		visit_children = false;
-		switch(ils->tag) {
-		case INL_STRING:
-		case INL_CODE:
-		case INL_RAW_HTML:
-			escape_html(html, ils->content.literal.data, ils->content.literal.len);
+		switch(ils->type) {
+		case NODE_STRING:
+		case NODE_INLINE_CODE:
+		case NODE_INLINE_HTML:
+			escape_html(html, ils->as.literal.data, ils->as.literal.len);
 			break;
 
-		case INL_LINEBREAK:
-		case INL_SOFTBREAK:
+		case NODE_LINEBREAK:
+		case NODE_SOFTBREAK:
 			strbuf_putc(html, '\n');
 			break;
 
-		case INL_LINK:
-		case INL_IMAGE:
-			children = ils->content.linkable.label;
+		case NODE_LINK:
+		case NODE_IMAGE:
+			children = ils->as.link.label;
 			visit_children = true;
 			rstack = push_inline(rstack, ils->next, "");
 			break;
 
-		case INL_STRONG:
-		case INL_EMPH:
-			children = ils->content.inlines;
+		case NODE_STRONG:
+		case NODE_EMPH:
+			children = ils->first_child;
 			visit_children = true;
 			rstack = push_inline(rstack, ils->next, "");
 			break;
+		default:
+			break;
 		}
 		if (visit_children) {
 			ils = children;
@@ -157,80 +159,82 @@ static void inlines_to_plain_html(strbuf *html, node_inl* ils)
 
 
 // Convert an inline list to HTML.  Returns 0 on success, and sets result.
-static void inlines_to_html(strbuf *html, node_inl* ils)
+static void inlines_to_html(strbuf *html, cmark_node* ils)
 {
-	node_inl* children;
+	cmark_node* children;
 	render_stack* rstack = NULL;
 
 	while(ils != NULL) {
 	        children = NULL;
-		switch(ils->tag) {
-		case INL_STRING:
-			escape_html(html, ils->content.literal.data, ils->content.literal.len);
+		switch(ils->type) {
+		case NODE_STRING:
+			escape_html(html, ils->as.literal.data, ils->as.literal.len);
 			break;
 
-		case INL_LINEBREAK:
+		case NODE_LINEBREAK:
 			strbuf_puts(html, "<br />\n");
 			break;
 
-		case INL_SOFTBREAK:
+		case NODE_SOFTBREAK:
 			strbuf_putc(html, '\n');
 			break;
 
-		case INL_CODE:
+		case NODE_INLINE_CODE:
 			strbuf_puts(html, "<code>");
-			escape_html(html, ils->content.literal.data, ils->content.literal.len);
+			escape_html(html, ils->as.literal.data, ils->as.literal.len);
 			strbuf_puts(html, "</code>");
 			break;
 
-		case INL_RAW_HTML:
+		case NODE_INLINE_HTML:
 			strbuf_put(html,
-				   ils->content.literal.data,
-				   ils->content.literal.len);
+				   ils->as.literal.data,
+				   ils->as.literal.len);
 			break;
 
-		case INL_LINK:
+		case NODE_LINK:
 			strbuf_puts(html, "<a href=\"");
-			if (ils->content.linkable.url)
-				escape_href(html, ils->content.linkable.url, -1);
+			if (ils->as.link.url)
+				escape_href(html, ils->as.link.url, -1);
 
-			if (ils->content.linkable.title) {
+			if (ils->as.link.title) {
 				strbuf_puts(html, "\" title=\"");
-				escape_html(html, ils->content.linkable.title, -1);
+				escape_html(html, ils->as.link.title, -1);
 			}
 
 			strbuf_puts(html, "\">");
-			children = ils->content.linkable.label;
+			children = ils->as.link.label;
 			rstack = push_inline(rstack, ils->next, "</a>");
 			break;
 
-		case INL_IMAGE:
+		case NODE_IMAGE:
 			strbuf_puts(html, "<img src=\"");
-			if (ils->content.linkable.url)
-				escape_href(html, ils->content.linkable.url, -1);
+			if (ils->as.link.url)
+				escape_href(html, ils->as.link.url, -1);
 
 			strbuf_puts(html, "\" alt=\"");
-			inlines_to_plain_html(html, ils->content.inlines);
+			inlines_to_plain_html(html, ils->as.link.label);
 
-			if (ils->content.linkable.title) {
+			if (ils->as.link.title) {
 				strbuf_puts(html, "\" title=\"");
-				escape_html(html, ils->content.linkable.title, -1);
+				escape_html(html, ils->as.link.title, -1);
 			}
 
 			strbuf_puts(html, "\"/>");
 			break;
 
-		case INL_STRONG:
+		case NODE_STRONG:
 			strbuf_puts(html, "<strong>");
-			children = ils->content.inlines;
+			children = ils->first_child;
 			rstack = push_inline(rstack, ils->next, "</strong>");
 			break;
 
-		case INL_EMPH:
+		case NODE_EMPH:
 			strbuf_puts(html, "<em>");
-			children = ils->content.inlines;
+			children = ils->first_child;
 			rstack = push_inline(rstack, ils->next, "</em>");
 			break;
+		default:
+			break;
 		}
 		if (children) {
 			ils = children;
@@ -265,11 +269,11 @@ static void blocks_to_html(strbuf *html, cmark_node *b)
 
 		case NODE_PARAGRAPH:
 			if (tight) {
-				inlines_to_html(html, b->inline_content);
+				inlines_to_html(html, b->first_child);
 			} else {
 				cr(html);
 				strbuf_puts(html, "<p>");
-				inlines_to_html(html, b->inline_content);
+				inlines_to_html(html, b->first_child);
 				strbuf_puts(html, "</p>\n");
 			}
 			break;
@@ -313,7 +317,7 @@ static void blocks_to_html(strbuf *html, cmark_node *b)
 		case NODE_SETEXT_HEADER:
 			cr(html);
 			strbuf_printf(html, "<h%d>", b->as.header.level);
-			inlines_to_html(html, b->inline_content);
+			inlines_to_html(html, b->first_child);
 			strbuf_printf(html, "</h%d>\n", b->as.header.level);
 			break;
 
diff --git a/src/inlines.c b/src/inlines.c
@@ -4,6 +4,7 @@
 #include <ctype.h>
 
 #include "config.h"
+#include "node.h"
 #include "ast.h"
 #include "references.h"
 #include "cmark.h"
@@ -16,7 +17,7 @@
 typedef struct DelimiterStack {
 	struct DelimiterStack *previous;
 	struct DelimiterStack *next;
-	node_inl *first_inline;
+	cmark_node *first_inline;
 	int delim_count;
 	unsigned char delim_char;
 	int position;
@@ -31,8 +32,8 @@ typedef struct Subject {
 	delimiter_stack *delimiters;
 } subject;
 
-static node_inl *parse_inlines_from_subject(subject* subj);
-static int parse_inline(subject* subj, node_inl ** last);
+static cmark_node *parse_inlines_from_subject(subject* subj);
+static int parse_inline(subject* subj, cmark_node ** last);
 
 static void subject_from_buf(subject *e, strbuf *buffer, reference_map *refmap);
 static int subject_find_special_char(subject *subj);
@@ -105,12 +106,12 @@ static inline chunk take_while(subject* subj, int (*f)(int))
 
 // Append inline list b to the end of inline list a.
 // Return pointer to head of new list.
-static inline cmark_node_inl* cmark_append_inlines(cmark_node_inl* a, cmark_node_inl* b)
+static inline cmark_node* cmark_append_inlines(cmark_node* a, cmark_node* b)
 {
 	if (a == NULL) {  // NULL acts like an empty list
 		return b;
 	}
-	cmark_node_inl* cur = a;
+	cmark_node* cur = a;
 	while (cur->next != NULL) {
 		cur = cur->next;
 	}
@@ -146,7 +147,7 @@ static int scan_to_closing_backticks(subject* subj, int openticklength)
 
 // Parse backtick code section or raw backticks, return an inline.
 // Assumes that the subject has a backtick at the current position.
-static node_inl* handle_backticks(subject *subj)
+static cmark_node* handle_backticks(subject *subj)
 {
 	chunk openticks = take_while(subj, isbacktick);
 	int startpos = subj->pos;
@@ -222,7 +223,7 @@ static delimiter_stack * push_delimiter(subject *subj,
 					unsigned char c,
 					bool can_open,
 					bool can_close,
-					node_inl *inl_text)
+					cmark_node *inl_text)
 {
 	delimiter_stack *istack =
 		(delimiter_stack*)malloc(sizeof(delimiter_stack));
@@ -245,10 +246,10 @@ static delimiter_stack * push_delimiter(subject *subj,
 
 // Parse strong/emph or a fallback.
 // Assumes the subject has '_' or '*' at the current position.
-static node_inl* handle_strong_emph(subject* subj, unsigned char c, node_inl **last)
+static cmark_node* handle_strong_emph(subject* subj, unsigned char c, cmark_node **last)
 {
 	int numdelims;
-	node_inl * inl_text;
+	cmark_node * inl_text;
 	bool can_open, can_close;
 
 	numdelims = scan_delims(subj, c, &can_open, &can_close);
@@ -268,7 +269,7 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 	delimiter_stack *closer = subj->delimiters;
 	delimiter_stack *opener, *tempstack, *nextstack;
 	int use_delims;
-	node_inl *inl, *tmp, *emph;
+	cmark_node *inl, *tmp, *emph;
 
 	// move back to first relevant delim.
 	while (closer != NULL && closer->previous != stack_bottom) {
@@ -302,8 +303,8 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 				// remove used delimiters from stack elements and associated inlines.
 				opener->delim_count -= use_delims;
 				closer->delim_count -= use_delims;
-				inl->content.literal.len = opener->delim_count;
-				closer->first_inline->content.literal.len = closer->delim_count;
+				inl->as.literal.len = opener->delim_count;
+				closer->first_inline->as.literal.len = closer->delim_count;
 
 				// free delimiters between opener and closer
 				tempstack = closer->previous;
@@ -318,7 +319,7 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 				emph = use_delims == 1 ? make_emph(inl->next) : make_strong(inl->next);
 				emph->next = closer->first_inline;
 				inl->next = emph;
-				tmp = emph->content.inlines;
+				tmp = emph->first_child;
 				while (tmp->next != NULL && tmp->next != closer->first_inline) {
 					tmp = tmp->next;
 				}
@@ -327,10 +328,10 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 				// if opener has 0 delims, remove it and its associated inline
 				if (opener->delim_count == 0) {
 					// replace empty opener inline with emph
-					chunk_free(&(inl->content.literal));
-					inl->tag = emph->tag;
+					chunk_free(&(inl->as.literal));
+					inl->type = emph->type;
 					inl->next = emph->next;
-					inl->content.inlines = emph->content.inlines;
+					inl->first_child = emph->first_child;
 					free(emph);
 					emph = inl;
 					// remove opener from stack
@@ -343,7 +344,7 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 					tmp = closer->first_inline;
 					emph->next = tmp->next;
 					tmp->next = NULL;
-					cmark_free_inlines(tmp);
+					cmark_free_nodes(tmp);
 					// remove closer from stack
 					tempstack = closer->next;
 					remove_delimiter(subj, closer);
@@ -363,7 +364,7 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 }
 
 // Parse backslash-escape or just a backslash, returning an inline.
-static node_inl* handle_backslash(subject *subj)
+static cmark_node* handle_backslash(subject *subj)
 {
 	advance(subj);
 	unsigned char nextchar = peek_char(subj);
@@ -380,7 +381,7 @@ static node_inl* handle_backslash(subject *subj)
 
 // Parse an entity or a regular "&" string.
 // Assumes the subject has an '&' character at the current position.
-static node_inl* handle_entity(subject* subj)
+static cmark_node* handle_entity(subject* subj)
 {
 	strbuf ent = GH_BUF_INIT;
 	size_t len;
@@ -401,7 +402,7 @@ static node_inl* handle_entity(subject* subj)
 
 // Like make_str, but parses entities.
 // Returns an inline sequence consisting of str and entity elements.
-static node_inl *make_str_with_entities(chunk *content)
+static cmark_node *make_str_with_entities(chunk *content)
 {
 	strbuf unescaped = GH_BUF_INIT;
 
@@ -459,7 +460,7 @@ unsigned char *clean_title(chunk *title)
 
 // Parse an autolink or HTML tag.
 // Assumes the subject has a '<' character at the current position.
-static node_inl* handle_pointy_brace(subject* subj)
+static cmark_node* handle_pointy_brace(subject* subj)
 {
 	int matchlen = 0;
 	chunk contents;
@@ -543,7 +544,7 @@ static int link_label(subject* subj, chunk *raw_label)
 }
 
 // Return a link, an image, or a literal close bracket.
-static node_inl* handle_close_bracket(subject* subj, node_inl **last)
+static cmark_node* handle_close_bracket(subject* subj, cmark_node **last)
 {
 	int initial_pos;
 	int starturl, endurl, starttitle, endtitle, endall;
@@ -555,8 +556,8 @@ static node_inl* handle_close_bracket(subject* subj, node_inl **last)
 	unsigned char *url, *title;
 	delimiter_stack *opener;
 	delimiter_stack *tempstack;
-	node_inl *link_text;
-	node_inl *inl;
+	cmark_node *link_text;
+	cmark_node *inl;
 	chunk raw_label;
 
 	advance(subj);  // advance past ]
@@ -642,12 +643,12 @@ noMatch:
 
 match:
 	inl = opener->first_inline;
-	inl->tag = is_image ? INL_IMAGE : INL_LINK;
-	chunk_free(&inl->content.literal);
-	inl->content.linkable.label = link_text;
+	inl->type = is_image ? NODE_IMAGE : NODE_LINK;
+	chunk_free(&inl->as.literal);
+	inl->as.link.label = link_text;
 	process_emphasis(subj, opener->previous);
-	inl->content.linkable.url   = url;
-	inl->content.linkable.title = title;
+	inl->as.link.url   = url;
+	inl->as.link.title = title;
 	inl->next = NULL;
 	*last = inl;
 
@@ -671,7 +672,7 @@ match:
 
 // Parse a hard or soft linebreak, returning an inline.
 // Assumes the subject has a newline at the current position.
-static node_inl* handle_newline(subject *subj)
+static cmark_node* handle_newline(subject *subj)
 {
 	int nlpos = subj->pos;
 	// skip over newline
@@ -690,11 +691,11 @@ static node_inl* handle_newline(subject *subj)
 }
 
 // Parse inlines til end of subject, returning inlines.
-extern node_inl* parse_inlines_from_subject(subject* subj)
+extern cmark_node* parse_inlines_from_subject(subject* subj)
 {
-	node_inl* result = NULL;
-	node_inl** last = &result;
-	node_inl* first = NULL;
+	cmark_node* result = NULL;
+	cmark_node** last = &result;
+	cmark_node* first = NULL;
 	while (!is_eof(subj) && parse_inline(subj, last)) {
 		if (!first) {
 			first = *last;
@@ -741,9 +742,9 @@ static int subject_find_special_char(subject *subj)
 // Parse an inline, advancing subject, and add it to last element.
 // Adjust tail to point to new last element of list.
 // Return 0 if no inline can be parsed, 1 otherwise.
-static int parse_inline(subject* subj, node_inl ** last)
+static int parse_inline(subject* subj, cmark_node ** last)
 {
-	node_inl* new_inl = NULL;
+	cmark_node* new_inl = NULL;
 	chunk contents;
 	unsigned char c;
 	int endpos;
@@ -811,7 +812,7 @@ static int parse_inline(subject* subj, node_inl ** last)
 	return 1;
 }
 
-extern node_inl* parse_inlines(strbuf *input, reference_map *refmap)
+extern cmark_node* parse_inlines(strbuf *input, reference_map *refmap)
 {
 	subject subj;
 	subject_from_buf(&subj, input, refmap);
diff --git a/src/inlines.h b/src/inlines.h
@@ -8,7 +8,7 @@ extern "C" {
 unsigned char *cmark_clean_url(cmark_chunk *url);
 unsigned char *cmark_clean_title(cmark_chunk *title);
 
-cmark_node_inl* cmark_parse_inlines(cmark_strbuf *input, cmark_reference_map *refmap);
+cmark_node* cmark_parse_inlines(cmark_strbuf *input, cmark_reference_map *refmap);
 
 int cmark_parse_reference_inline(cmark_strbuf *input, cmark_reference_map *refmap);
 
diff --git a/src/main.c b/src/main.c
@@ -101,7 +101,7 @@ int main(int argc, char *argv[])
 	end_timer("print_document");
 
 	start_timer();
-	cmark_free_blocks(document);
+	cmark_free_nodes(document);
 	end_timer("free_blocks");
 
 	free(files);
diff --git a/src/node.h b/src/node.h
@@ -7,6 +7,7 @@ extern "C" {
 
 #include "cmark.h"
 #include "buffer.h"
+#include "chunk.h"
 
 typedef enum {
     // Block
@@ -86,12 +87,10 @@ struct cmark_node {
 	bool open;
 	bool last_line_blank;
 
-	// Temp
-	cmark_node_inl *inline_content;
-
 	cmark_strbuf string_content;
 
 	union {
+		cmark_chunk       literal;
 		cmark_list        list;
 		cmark_fenced_code code;
 		cmark_header      header;
diff --git a/src/print.c b/src/print.c
@@ -35,7 +35,7 @@ static void print_str(const unsigned char *s, int len)
 }
 
 // Prettyprint an inline list, for debugging.
-static void print_inlines(node_inl* ils, int indent)
+static void print_inlines(cmark_node* ils, int indent)
 {
 	int i;
 
@@ -43,49 +43,51 @@ static void print_inlines(node_inl* ils, int indent)
 		for (i=0; i < indent; i++) {
 			putchar(' ');
 		}
-		switch(ils->tag) {
-		case INL_STRING:
+		switch(ils->type) {
+		case NODE_STRING:
 			printf("str ");
-			print_str(ils->content.literal.data, ils->content.literal.len);
+			print_str(ils->as.literal.data, ils->as.literal.len);
 			putchar('\n');
 			break;
-		case INL_LINEBREAK:
+		case NODE_LINEBREAK:
 			printf("linebreak\n");
 			break;
-		case INL_SOFTBREAK:
+		case NODE_SOFTBREAK:
 			printf("softbreak\n");
 			break;
-		case INL_CODE:
+		case NODE_INLINE_CODE:
 			printf("code ");
-			print_str(ils->content.literal.data, ils->content.literal.len);
+			print_str(ils->as.literal.data, ils->as.literal.len);
 			putchar('\n');
 			break;
-		case INL_RAW_HTML:
+		case NODE_INLINE_HTML:
 			printf("html ");
-			print_str(ils->content.literal.data, ils->content.literal.len);
+			print_str(ils->as.literal.data, ils->as.literal.len);
 			putchar('\n');
 			break;
-		case INL_LINK:
-		case INL_IMAGE:
-			printf("%s url=", ils->tag == INL_LINK ? "link" : "image");
+		case NODE_LINK:
+		case NODE_IMAGE:
+			printf("%s url=", ils->type == NODE_LINK ? "link" : "image");
 
-			if (ils->content.linkable.url)
-				print_str(ils->content.linkable.url, -1);
+			if (ils->as.link.url)
+				print_str(ils->as.link.url, -1);
 
-			if (ils->content.linkable.title) {
+			if (ils->as.link.title) {
 				printf(" title=");
-				print_str(ils->content.linkable.title, -1);
+				print_str(ils->as.link.title, -1);
 			}
 			putchar('\n');
-			print_inlines(ils->content.linkable.label, indent + 2);
+			print_inlines(ils->as.link.label, indent + 2);
 			break;
-		case INL_STRONG:
+		case NODE_STRONG:
 			printf("strong\n");
-			print_inlines(ils->content.linkable.label, indent + 2);
+			print_inlines(ils->as.link.label, indent + 2);
 			break;
-		case INL_EMPH:
+		case NODE_EMPH:
 			printf("emph\n");
-			print_inlines(ils->content.linkable.label, indent + 2);
+			print_inlines(ils->as.link.label, indent + 2);
+			break;
+		default:
 			break;
 		}
 		ils = ils->next;
@@ -133,15 +135,15 @@ static void print_blocks(cmark_node* b, int indent)
 			break;
 		case NODE_ATX_HEADER:
 			printf("atx_header (level=%d)\n", b->as.header.level);
-			print_inlines(b->inline_content, indent + 2);
+			print_inlines(b->first_child, indent + 2);
 			break;
 		case NODE_SETEXT_HEADER:
 			printf("setext_header (level=%d)\n", b->as.header.level);
-			print_inlines(b->inline_content, indent + 2);
+			print_inlines(b->first_child, indent + 2);
 			break;
 		case NODE_PARAGRAPH:
 			printf("paragraph\n");
-			print_inlines(b->inline_content, indent + 2);
+			print_inlines(b->first_child, indent + 2);
 			break;
 		case NODE_HRULE:
 			printf("hrule\n");