Line | Count | Source |
1 | | #include <assert.h> |
2 | | #include <stdbool.h> |
3 | | #include <stdio.h> |
4 | | #include <stdlib.h> |
5 | | #include <string.h> |
6 | | |
7 | | #include "cmark.h" |
8 | | #include "node.h" |
9 | | #include "buffer.h" |
10 | | #include "utf8.h" |
11 | | #include "render.h" |
12 | | |
13 | 394k | #define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) |
14 | 3.91M | #define LIT(s) renderer->out(renderer, s, false, LITERAL) |
15 | 5.89M | #define CR() renderer->cr(renderer) |
16 | | #define BLANKLINE() renderer->blankline(renderer) |
17 | 12 | #define LIST_NUMBER_SIZE 20 |
18 | | |
19 | | // Functions to convert cmark_nodes to groff man strings. |
20 | | static void S_outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c, |
21 | 37.7M | unsigned char nextc) { |
22 | 37.7M | (void)(nextc); |
23 | | |
24 | 37.7M | if (escape == LITERAL) { |
25 | 0 | cmark_render_code_point(renderer, c); |
26 | 0 | return; |
27 | 0 | } |
28 | | |
29 | 37.7M | switch (c) { |
30 | 10.2k | case 46: |
31 | 10.2k | if (renderer->begin_line) { |
32 | 3.73k | cmark_render_ascii(renderer, "\\&."); |
33 | 6.47k | } else { |
34 | 6.47k | cmark_render_code_point(renderer, c); |
35 | 6.47k | } |
36 | 10.2k | break; |
37 | 4.38k | case 39: |
38 | 4.38k | if (renderer->begin_line) { |
39 | 11 | cmark_render_ascii(renderer, "\\&'"); |
40 | 4.37k | } else { |
41 | 4.37k | cmark_render_code_point(renderer, c); |
42 | 4.37k | } |
43 | 4.38k | break; |
44 | 488k | case 45: |
45 | 488k | cmark_render_ascii(renderer, "\\-"); |
46 | 488k | break; |
47 | 42.7k | case 92: |
48 | 42.7k | cmark_render_ascii(renderer, "\\e"); |
49 | 42.7k | break; |
50 | 105 | case 8216: // left single quote |
51 | 105 | cmark_render_ascii(renderer, "\\[oq]"); |
52 | 105 | break; |
53 | 2.11k | case 8217: // right single quote |
54 | 2.11k | cmark_render_ascii(renderer, "\\[cq]"); |
55 | 2.11k | break; |
56 | 756 | case 8220: // left double quote |
57 | 756 | cmark_render_ascii(renderer, "\\[lq]"); |
58 | 756 | break; |
59 | 167k | case 8221: // right double quote |
60 | 167k | cmark_render_ascii(renderer, "\\[rq]"); |
61 | 167k | break; |
62 | 2.62k | case 8212: // em dash |
63 | 2.62k | cmark_render_ascii(renderer, "\\[em]"); |
64 | 2.62k | break; |
65 | 52.5k | case 8211: // en dash |
66 | 52.5k | cmark_render_ascii(renderer, "\\[en]"); |
67 | 52.5k | break; |
68 | 36.9M | default: |
69 | 36.9M | cmark_render_code_point(renderer, c); |
70 | 37.7M | } |
71 | 37.7M | } |
72 | | |
73 | | static int S_render_node(cmark_renderer *renderer, cmark_node *node, |
74 | 5.42M | cmark_event_type ev_type, int options) { |
75 | 5.42M | cmark_node *tmp; |
76 | 5.42M | int list_number; |
77 | 5.42M | bool entering = (ev_type == CMARK_EVENT_ENTER); |
78 | 5.42M | bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options); |
79 | 5.42M | struct block_number *new_block_number; |
80 | 5.42M | cmark_mem *allocator = cmark_get_default_mem_allocator(); |
81 | | |
82 | | // avoid unused parameter error: |
83 | 5.42M | (void)(options); |
84 | | |
85 | | // indent inside nested lists |
86 | 5.42M | if (renderer->block_number_in_list_item && |
87 | 1.49M | node->type < CMARK_NODE_FIRST_INLINE) { |
88 | 1.48M | if (entering) { |
89 | 431k | renderer->block_number_in_list_item->number += 1; |
90 | 431k | if (renderer->block_number_in_list_item->number == 2) { |
91 | 214k | CR(); |
92 | 214k | LIT(".RS"); // indent |
93 | 214k | CR(); |
94 | 214k | } |
95 | 431k | } |
96 | 1.48M | } |
97 | | |
98 | 5.42M | switch (node->type) { |
99 | 352 | case CMARK_NODE_DOCUMENT: |
100 | 352 | break; |
101 | | |
102 | 1.15M | case CMARK_NODE_BLOCK_QUOTE: |
103 | 1.15M | if (entering) { |
104 | 576k | CR(); |
105 | 576k | LIT(".RS"); |
106 | 576k | CR(); |
107 | 576k | } else { |
108 | 576k | CR(); |
109 | 576k | LIT(".RE"); |
110 | 576k | CR(); |
111 | 576k | } |
112 | 1.15M | break; |
113 | | |
114 | 1.67M | case CMARK_NODE_LIST: |
115 | 1.67M | break; |
116 | | |
117 | 1.67M | case CMARK_NODE_ITEM: |
118 | 1.67M | if (entering) { |
119 | 838k | new_block_number = allocator->calloc(1, sizeof(struct block_number)); |
120 | 838k | new_block_number->number = 0; |
121 | 838k | new_block_number->parent = renderer->block_number_in_list_item; |
122 | 838k | renderer->block_number_in_list_item = new_block_number; |
123 | 838k | CR(); |
124 | 838k | LIT(".IP "); |
125 | 838k | if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { |
126 | 838k | LIT("\\[bu] 2"); |
127 | 838k | } else { |
128 | 12 | list_number = cmark_node_get_list_start(node->parent); |
129 | 12 | tmp = node; |
130 | 13 | while (tmp->prev) { |
131 | 1 | tmp = tmp->prev; |
132 | 1 | list_number += 1; |
133 | 1 | } |
134 | 12 | char list_number_s[LIST_NUMBER_SIZE]; |
135 | 12 | snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number); |
136 | 12 | LIT(list_number_s); |
137 | 12 | } |
138 | 838k | CR(); |
139 | 838k | } else { |
140 | 838k | if (renderer->block_number_in_list_item) { |
141 | 838k | if (renderer->block_number_in_list_item->number >= 2) { |
142 | 214k | CR(); |
143 | 214k | LIT(".RE"); // de-indent |
144 | 214k | } |
145 | 838k | new_block_number = renderer->block_number_in_list_item; |
146 | 838k | renderer->block_number_in_list_item = |
147 | 838k | renderer->block_number_in_list_item->parent; |
148 | 838k | allocator->free(new_block_number); |
149 | 838k | } |
150 | 838k | CR(); |
151 | 838k | } |
152 | 1.67M | break; |
153 | | |
154 | 39.2k | case CMARK_NODE_HEADING: |
155 | 39.2k | if (entering) { |
156 | 19.6k | CR(); |
157 | 19.6k | LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS"); |
158 | 19.6k | CR(); |
159 | 19.6k | } else { |
160 | 19.6k | CR(); |
161 | 19.6k | } |
162 | 39.2k | break; |
163 | | |
164 | 87.5k | case CMARK_NODE_CODE_BLOCK: |
165 | 87.5k | CR(); |
166 | 87.5k | LIT(".IP\n.nf\n\\f[C]\n"); |
167 | 87.5k | OUT(cmark_node_get_literal(node), false, NORMAL); |
168 | 87.5k | CR(); |
169 | 87.5k | LIT("\\f[]\n.fi"); |
170 | 87.5k | CR(); |
171 | 87.5k | break; |
172 | | |
173 | 5.85k | case CMARK_NODE_HTML_BLOCK: |
174 | 5.85k | break; |
175 | | |
176 | 0 | case CMARK_NODE_CUSTOM_BLOCK: |
177 | 0 | CR(); |
178 | 0 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), |
179 | 0 | false, LITERAL); |
180 | 0 | CR(); |
181 | 0 | break; |
182 | | |
183 | 5 | case CMARK_NODE_THEMATIC_BREAK: |
184 | 5 | CR(); |
185 | 5 | LIT(".PP\n * * * * *"); |
186 | 5 | CR(); |
187 | 5 | break; |
188 | | |
189 | 71.3k | case CMARK_NODE_PARAGRAPH: |
190 | 71.3k | if (entering) { |
191 | | // no blank line if first paragraph in list: |
192 | 35.6k | if (node->parent && node->parent->type == CMARK_NODE_ITEM && |
193 | 2.25k | node->prev == NULL) { |
194 | | // no blank line or .PP |
195 | 34.1k | } else { |
196 | 34.1k | CR(); |
197 | 34.1k | LIT(".PP"); |
198 | 34.1k | CR(); |
199 | 34.1k | } |
200 | 35.6k | } else { |
201 | 35.6k | CR(); |
202 | 35.6k | } |
203 | 71.3k | break; |
204 | | |
205 | 193k | case CMARK_NODE_TEXT: |
206 | 193k | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); |
207 | 193k | break; |
208 | | |
209 | 251 | case CMARK_NODE_LINEBREAK: |
210 | 251 | LIT(".PD 0\n.P\n.PD"); |
211 | 251 | CR(); |
212 | 251 | break; |
213 | | |
214 | 105k | case CMARK_NODE_SOFTBREAK: |
215 | 105k | if (options & CMARK_OPT_HARDBREAKS) { |
216 | 10.5k | LIT(".PD 0\n.P\n.PD"); |
217 | 10.5k | CR(); |
218 | 95.4k | } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) { |
219 | 39 | CR(); |
220 | 95.4k | } else { |
221 | 95.4k | OUT(" ", allow_wrap, LITERAL); |
222 | 95.4k | } |
223 | 105k | break; |
224 | | |
225 | 13.1k | case CMARK_NODE_CODE: |
226 | 13.1k | LIT("\\f[C]"); |
227 | 13.1k | OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); |
228 | 13.1k | LIT("\\f[]"); |
229 | 13.1k | break; |
230 | | |
231 | 16.4k | case CMARK_NODE_HTML_INLINE: |
232 | 16.4k | break; |
233 | | |
234 | 0 | case CMARK_NODE_CUSTOM_INLINE: |
235 | 0 | OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), |
236 | 0 | false, LITERAL); |
237 | 0 | break; |
238 | | |
239 | 365k | case CMARK_NODE_STRONG: |
240 | 365k | if (entering) { |
241 | 182k | LIT("\\f[B]"); |
242 | 182k | } else { |
243 | 182k | LIT("\\f[]"); |
244 | 182k | } |
245 | 365k | break; |
246 | | |
247 | 13.5k | case CMARK_NODE_EMPH: |
248 | 13.5k | if (entering) { |
249 | 6.78k | LIT("\\f[I]"); |
250 | 6.78k | } else { |
251 | 6.78k | LIT("\\f[]"); |
252 | 6.78k | } |
253 | 13.5k | break; |
254 | | |
255 | 9.37k | case CMARK_NODE_LINK: |
256 | 9.37k | if (!entering) { |
257 | 4.68k | LIT(" ("); |
258 | 4.68k | OUT(cmark_node_get_url(node), allow_wrap, URL); |
259 | 4.68k | LIT(")"); |
260 | 4.68k | } |
261 | 9.37k | break; |
262 | | |
263 | 88 | case CMARK_NODE_IMAGE: |
264 | 88 | if (entering) { |
265 | 44 | LIT("[IMAGE: "); |
266 | 44 | } else { |
267 | 44 | LIT("]"); |
268 | 44 | } |
269 | 88 | break; |
270 | | |
271 | 0 | default: |
272 | 0 | assert(false); |
273 | 0 | break; |
274 | 5.42M | } |
275 | | |
276 | 5.42M | return 1; |
277 | 5.42M | } |
278 | | |
279 | 176 | char *cmark_render_man(cmark_node *root, int options, int width) { |
280 | 176 | return cmark_render(root, options, width, S_outc, S_render_node); |
281 | 176 | } |