summaryrefslogtreecommitdiff
path: root/src/cmark.c
blob: 7cc8e5c3b01a15b1bc805d8acab615f734b385f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include "node.h"
#include "references.h"
#include "html/houdini.h"
#include "cmark.h"
#include "buffer.h"
#include "ast.h"

// AST traversal and manipulation functions

cmark_node_block *cmark_block_next(cmark_node_block *current)
{
	return current->next;
}

cmark_node_block *cmark_block_previous(cmark_node_block *current)
{
	return current->prev;
}

cmark_node_block *cmark_block_parent(cmark_node_block *current)
{
	return current->parent;
}

cmark_node_block *cmark_block_children(cmark_node_block *current)
{
	return current->children;
}

void cmark_block_insert_before(cmark_node_block *new, cmark_node_block *current)
{
	// Find last node in new:
	cmark_node_block *new_last = new;
	while (new_last->next) {
		new_last = new_last->next;
	}
	new_last->next = current;
	current->prev = new_last;
	if (current->prev) {
		current->prev->next = new;
		new->prev = current->prev;
	}
}

void cmark_block_insert_after(cmark_node_block *current, cmark_node_block *new)
{
	// Find last node in new:
	cmark_node_block *new_last = new;
	while (new_last->next) {
		new_last = new_last->next;
	}
	if (current->next) {
		new_last->next = current->next;
		current->next->prev = new_last;
	}
	current->next = new;
	new->prev = current;
}

/* * */

unsigned char *cmark_markdown_to_html(unsigned char *text, int len)
{
	cmark_node *blocks;
	unsigned char *result;

	blocks = cmark_parse_document(text, len);

	result = cmark_render_html(blocks);
	cmark_free_nodes(blocks);

	return result;
}

// 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
		while (tmp->next) {
			tmp = tmp->next;
		}
		// Splice children into list
		tmp->next = e->next;
		e->next = children;
	}
	return ;
}

unsigned char *cmark_clean_autolink(chunk *url, int is_email)
{
	strbuf buf = GH_BUF_INIT;

	chunk_trim(url);

	if (url->len == 0)
		return NULL;

	if (is_email)
		strbuf_puts(&buf, "mailto:");

	houdini_unescape_html_f(&buf, url->data, url->len);
	return strbuf_detach(&buf);
}

// Free a cmark_node list and any children.
void cmark_free_nodes(cmark_node *e)
{
	cmark_node *next;
	while (e != NULL) {
		strbuf_free(&e->string_content);
		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
			e->last_child->next = e->next;
			e->next = e->first_child;
		}
		next = e->next;
		free(e);
		e = next;
	}
}