File Coverage

hoedown/src/html.c
Criterion Covered Total %
statement 87 341 25.5
branch 41 286 14.3
condition n/a
subroutine n/a
pod n/a
total 128 627 20.4


line stmt bran cond sub pod time code
1             #include "html.h"
2              
3             #include <string.h>
4             #include <stdlib.h>
5             #include <stdio.h>
6             #include <ctype.h>
7              
8             #include "escape.h"
9              
10             #define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML)
11              
12             struct rndr_state {
13             struct {
14             int header_count;
15             int current_level;
16             int level_offset;
17             int nesting_level;
18             } toc_data;
19              
20             unsigned int flags;
21              
22             /* extra callbacks */
23             void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, void *self);
24             };
25              
26             typedef struct rndr_state rndr_state;
27              
28             int
29 0           hoedown_html_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
30             {
31             size_t i;
32             int closed = 0;
33              
34 0 0         if (tag_size < 3 || tag_data[0] != '<')
    0          
35             return HOEDOWN_HTML_TAG_NONE;
36              
37             i = 1;
38              
39 0 0         if (tag_data[i] == '/') {
40             closed = 1;
41             i++;
42             }
43              
44 0 0         for (; i < tag_size; ++i, ++tagname) {
45 0 0         if (*tagname == 0)
46             break;
47              
48 0 0         if (tag_data[i] != *tagname)
49             return HOEDOWN_HTML_TAG_NONE;
50             }
51              
52 0 0         if (i == tag_size)
53             return HOEDOWN_HTML_TAG_NONE;
54              
55 0 0         if (isspace(tag_data[i]) || tag_data[i] == '>')
    0          
56 0 0         return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN;
57              
58             return HOEDOWN_HTML_TAG_NONE;
59             }
60              
61             static inline void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length)
62             {
63 18           hoedown_escape_html(ob, source, length, 0);
64             }
65              
66             static inline void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
67             {
68 1           hoedown_escape_href(ob, source, length);
69             }
70              
71             /********************
72              * GENERIC RENDERER *
73              ********************/
74             static int
75 1           rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, enum hoedown_autolink type, void *opaque)
76             {
77             rndr_state *state = opaque;
78            
79 1 50         if (!link || !link->size)
    50          
80             return 0;
81            
82 1           if ((state->flags & HOEDOWN_HTML_SAFELINK) != 0 &&
83 0 0         !hoedown_autolink_is_safe(link->data, link->size) &&
84             type != HOEDOWN_AUTOLINK_EMAIL)
85             return 0;
86            
87 1           HOEDOWN_BUFPUTSL(ob, "<a href=\"");
88 1 50         if (type == HOEDOWN_AUTOLINK_EMAIL)
89 0           HOEDOWN_BUFPUTSL(ob, "mailto:");
90 1           escape_href(ob, link->data, link->size);
91            
92 1 50         if (state->link_attributes) {
93 0           hoedown_buffer_putc(ob, '\"');
94 0           state->link_attributes(ob, link, opaque);
95 0           hoedown_buffer_putc(ob, '>');
96             } else {
97 1           HOEDOWN_BUFPUTSL(ob, "\">");
98             }
99            
100             /*
101             * Pretty printing: if we get an email address as
102             * an actual URI, e.g. `mailto:foo@bar.com`, we don't
103             * want to print the `mailto:` prefix
104             */
105 1 50         if (hoedown_buffer_prefix(link, "mailto:") == 0) {
106 0           escape_html(ob, link->data + 7, link->size - 7);
107             } else {
108 1           escape_html(ob, link->data, link->size);
109             }
110            
111 1           HOEDOWN_BUFPUTSL(ob, "</a>");
112            
113 1           return 1;
114             }
115            
116             static void
117 0           rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, void *opaque)
118             {
119             rndr_state *state = opaque;
120            
121 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
122            
123 0 0         if (lang && lang->size) {
    0          
124             size_t i, cls = 0;
125 0 0         if (state->flags & HOEDOWN_HTML_PRETTIFY) {
126 0           HOEDOWN_BUFPUTSL(ob, "<pre><code class=\"prettyprint");
127             cls++;
128             } else {
129 0           HOEDOWN_BUFPUTSL(ob, "<pre><code class=\"");
130             }
131              
132 0 0         for (i = 0; i < lang->size; ++i, ++cls) {
133 0 0         while (i < lang->size && isspace(lang->data[i]))
    0          
134 0           i++;
135              
136 0 0         if (i < lang->size) {
137             size_t org = i;
138 0 0         while (i < lang->size && !isspace(lang->data[i]))
    0          
139 0           i++;
140              
141 0 0         if (lang->data[org] == '.')
142 0           org++;
143              
144 0 0         if (cls) hoedown_buffer_putc(ob, ' ');
145 0           escape_html(ob, lang->data + org, i - org);
146             }
147             }
148              
149 0           HOEDOWN_BUFPUTSL(ob, "\">");
150 0 0         } else if (state->flags & HOEDOWN_HTML_PRETTIFY) {
151 0           HOEDOWN_BUFPUTSL(ob, "<pre><code class=\"prettyprint\">");
152             } else {
153 0           HOEDOWN_BUFPUTSL(ob, "<pre><code>");
154             }
155              
156 0 0         if (text)
157 0           escape_html(ob, text->data, text->size);
158              
159 0           HOEDOWN_BUFPUTSL(ob, "</code></pre>\n");
160 0           }
161              
162             static void
163 0           rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
164             {
165 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
166 0           HOEDOWN_BUFPUTSL(ob, "<blockquote>\n");
167 0 0         if (text) hoedown_buffer_put(ob, text->data, text->size);
168 0           HOEDOWN_BUFPUTSL(ob, "</blockquote>\n");
169 0           }
170              
171             static int
172 0           rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
173             {
174             rndr_state *state = opaque;
175 0 0         if (state->flags & HOEDOWN_HTML_PRETTIFY)
176 0           HOEDOWN_BUFPUTSL(ob, "<code class=\"prettyprint\">");
177             else
178 0           HOEDOWN_BUFPUTSL(ob, "<code>");
179 0 0         if (text) escape_html(ob, text->data, text->size);
180 0           HOEDOWN_BUFPUTSL(ob, "</code>");
181 0           return 1;
182             }
183              
184             static int
185 0           rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
186             {
187 0 0         if (!text || !text->size)
    0          
188             return 0;
189              
190 0           HOEDOWN_BUFPUTSL(ob, "<del>");
191 0           hoedown_buffer_put(ob, text->data, text->size);
192 0           HOEDOWN_BUFPUTSL(ob, "</del>");
193 0           return 1;
194             }
195              
196             static int
197 0           rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
198             {
199 0 0         if (!text || !text->size)
    0          
200             return 0;
201              
202 0           HOEDOWN_BUFPUTSL(ob, "<strong>");
203 0           hoedown_buffer_put(ob, text->data, text->size);
204 0           HOEDOWN_BUFPUTSL(ob, "</strong>");
205              
206 0           return 1;
207             }
208              
209             static int
210 0           rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
211             {
212 0 0         if (!text || !text->size) return 0;
    0          
213 0           HOEDOWN_BUFPUTSL(ob, "<em>");
214 0 0         if (text) hoedown_buffer_put(ob, text->data, text->size);
215 0           HOEDOWN_BUFPUTSL(ob, "</em>");
216 0           return 1;
217             }
218              
219             static int
220 0           rndr_underline(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
221             {
222 0 0         if (!text || !text->size)
    0          
223             return 0;
224              
225 0           HOEDOWN_BUFPUTSL(ob, "<u>");
226 0           hoedown_buffer_put(ob, text->data, text->size);
227 0           HOEDOWN_BUFPUTSL(ob, "</u>");
228              
229 0           return 1;
230             }
231              
232             static int
233 0           rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
234             {
235 0 0         if (!text || !text->size)
    0          
236             return 0;
237              
238 0           HOEDOWN_BUFPUTSL(ob, "<mark>");
239 0           hoedown_buffer_put(ob, text->data, text->size);
240 0           HOEDOWN_BUFPUTSL(ob, "</mark>");
241              
242 0           return 1;
243             }
244              
245             static int
246 0           rndr_quote(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
247             {
248 0 0         if (!text || !text->size)
    0          
249             return 0;
250              
251 0           HOEDOWN_BUFPUTSL(ob, "<q>");
252 0           hoedown_buffer_put(ob, text->data, text->size);
253 0           HOEDOWN_BUFPUTSL(ob, "</q>");
254              
255 0           return 1;
256             }
257              
258             static int
259 0           rndr_linebreak(hoedown_buffer *ob, void *opaque)
260             {
261             rndr_state *state = opaque;
262 0 0         hoedown_buffer_puts(ob, USE_XHTML(state) ? "<br/>\n" : "<br>\n");
    0          
263 0           return 1;
264             }
265              
266             static void
267 9           rndr_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaque)
268             {
269             rndr_state *state = opaque;
270              
271 9 100         if (ob->size)
272 6           hoedown_buffer_putc(ob, '\n');
273              
274 9 50         if ((state->flags & HOEDOWN_HTML_TOC) && (level <= state->toc_data.nesting_level))
    50          
275 9           hoedown_buffer_printf(ob, "<h%d id=\"toc_%d\">", level, state->toc_data.header_count++);
276             else
277 0           hoedown_buffer_printf(ob, "<h%d>", level);
278              
279 9 50         if (text) hoedown_buffer_put(ob, text->data, text->size);
280 9           hoedown_buffer_printf(ob, "</h%d>\n", level);
281 9           }
282              
283             static int
284 0           rndr_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *content, void *opaque)
285             {
286             rndr_state *state = opaque;
287              
288 0 0         if (link != NULL && (state->flags & HOEDOWN_HTML_SAFELINK) != 0 && !hoedown_autolink_is_safe(link->data, link->size))
    0          
    0          
289             return 0;
290              
291 0           HOEDOWN_BUFPUTSL(ob, "<a href=\"");
292              
293 0 0         if (link && link->size)
    0          
294 0           escape_href(ob, link->data, link->size);
295              
296 0 0         if (title && title->size) {
    0          
297 0           HOEDOWN_BUFPUTSL(ob, "\" title=\"");
298 0           escape_html(ob, title->data, title->size);
299             }
300              
301 0 0         if (state->link_attributes) {
302 0           hoedown_buffer_putc(ob, '\"');
303 0           state->link_attributes(ob, link, opaque);
304 0           hoedown_buffer_putc(ob, '>');
305             } else {
306 0           HOEDOWN_BUFPUTSL(ob, "\">");
307             }
308              
309 0 0         if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
    0          
310 0           HOEDOWN_BUFPUTSL(ob, "</a>");
311 0           return 1;
312             }
313              
314             static void
315 0           rndr_list(hoedown_buffer *ob, const hoedown_buffer *text, int flags, void *opaque)
316             {
317 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
318 0 0         hoedown_buffer_put(ob, flags & HOEDOWN_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
319 0 0         if (text) hoedown_buffer_put(ob, text->data, text->size);
320 0 0         hoedown_buffer_put(ob, flags & HOEDOWN_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
321 0           }
322              
323             static void
324 0           rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *text, int flags, void *opaque)
325             {
326 0           HOEDOWN_BUFPUTSL(ob, "<li>");
327 0 0         if (text) {
328 0           size_t size = text->size;
329 0 0         while (size && text->data[size - 1] == '\n')
    0          
330             size--;
331              
332 0           hoedown_buffer_put(ob, text->data, size);
333             }
334 0           HOEDOWN_BUFPUTSL(ob, "</li>\n");
335 0           }
336              
337             static void
338 1           rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
339             {
340             rndr_state *state = opaque;
341             size_t i = 0;
342              
343 1 50         if (ob->size) hoedown_buffer_putc(ob, '\n');
344              
345 1 50         if (!text || !text->size)
    50          
346             return;
347              
348 1 50         while (i < text->size && isspace(text->data[i])) i++;
    50          
349              
350 1 50         if (i == text->size)
351             return;
352              
353 1           HOEDOWN_BUFPUTSL(ob, "<p>");
354 1 50         if (state->flags & HOEDOWN_HTML_HARD_WRAP) {
355             size_t org;
356 0 0         while (i < text->size) {
357             org = i;
358 0 0         while (i < text->size && text->data[i] != '\n')
    0          
359 0           i++;
360              
361 0 0         if (i > org)
362 0           hoedown_buffer_put(ob, text->data + org, i - org);
363              
364             /*
365             * do not insert a line break if this newline
366             * is the last character on the paragraph
367             */
368 0 0         if (i >= text->size - 1)
369             break;
370              
371             rndr_linebreak(ob, opaque);
372 0           i++;
373             }
374             } else {
375 1           hoedown_buffer_put(ob, &text->data[i], text->size - i);
376             }
377 1           HOEDOWN_BUFPUTSL(ob, "</p>\n");
378             }
379              
380             static void
381 0           rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
382             {
383             size_t org, sz;
384 0 0         if (!text) return;
385 0           sz = text->size;
386 0 0         while (sz > 0 && text->data[sz - 1] == '\n') sz--;
    0          
387             org = 0;
388 0 0         while (org < sz && text->data[org] == '\n') org++;
    0          
389 0 0         if (org >= sz) return;
390 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
391 0           hoedown_buffer_put(ob, text->data + org, sz - org);
392 0           hoedown_buffer_putc(ob, '\n');
393             }
394              
395             static int
396 0           rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
397             {
398 0 0         if (!text || !text->size) return 0;
    0          
399 0           HOEDOWN_BUFPUTSL(ob, "<strong><em>");
400 0           hoedown_buffer_put(ob, text->data, text->size);
401 0           HOEDOWN_BUFPUTSL(ob, "</em></strong>");
402 0           return 1;
403             }
404              
405             static void
406 0           rndr_hrule(hoedown_buffer *ob, void *opaque)
407             {
408             rndr_state *state = opaque;
409 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
410 0 0         hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
411 0           }
412              
413             static int
414 0           rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, void *opaque)
415             {
416             rndr_state *state = opaque;
417 0 0         if (!link || !link->size) return 0;
    0          
418              
419 0           HOEDOWN_BUFPUTSL(ob, "<img src=\"");
420 0           escape_href(ob, link->data, link->size);
421 0           HOEDOWN_BUFPUTSL(ob, "\" alt=\"");
422              
423 0 0         if (alt && alt->size)
    0          
424 0           escape_html(ob, alt->data, alt->size);
425              
426 0 0         if (title && title->size) {
    0          
427 0           HOEDOWN_BUFPUTSL(ob, "\" title=\"");
428 0           escape_html(ob, title->data, title->size); }
429              
430 0 0         hoedown_buffer_puts(ob, USE_XHTML(state) ? "\"/>" : "\">");
431 0           return 1;
432             }
433              
434             static int
435 0           rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
436             {
437             rndr_state *state = opaque;
438              
439             /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
440             * It doens't see if there are any valid tags, just escape all of them. */
441 0 0         if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) {
442 0           escape_html(ob, text->data, text->size);
443 0           return 1;
444             }
445              
446 0 0         if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0)
447             return 1;
448              
449 0           if ((state->flags & HOEDOWN_HTML_SKIP_STYLE) != 0 &&
450 0           hoedown_html_is_tag(text->data, text->size, "style"))
451             return 1;
452              
453 0           if ((state->flags & HOEDOWN_HTML_SKIP_LINKS) != 0 &&
454 0           hoedown_html_is_tag(text->data, text->size, "a"))
455             return 1;
456              
457 0           if ((state->flags & HOEDOWN_HTML_SKIP_IMAGES) != 0 &&
458 0           hoedown_html_is_tag(text->data, text->size, "img"))
459             return 1;
460              
461 0           hoedown_buffer_put(ob, text->data, text->size);
462 0           return 1;
463             }
464              
465             static void
466 0           rndr_table(hoedown_buffer *ob, const hoedown_buffer *header, const hoedown_buffer *body, void *opaque)
467             {
468 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
469 0           HOEDOWN_BUFPUTSL(ob, "<table><thead>\n");
470 0 0         if (header)
471 0           hoedown_buffer_put(ob, header->data, header->size);
472 0           HOEDOWN_BUFPUTSL(ob, "</thead><tbody>\n");
473 0 0         if (body)
474 0           hoedown_buffer_put(ob, body->data, body->size);
475 0           HOEDOWN_BUFPUTSL(ob, "</tbody></table>\n");
476 0           }
477              
478             static void
479 0           rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
480             {
481 0           HOEDOWN_BUFPUTSL(ob, "<tr>\n");
482 0 0         if (text)
483 0           hoedown_buffer_put(ob, text->data, text->size);
484 0           HOEDOWN_BUFPUTSL(ob, "</tr>\n");
485 0           }
486              
487             static void
488 0           rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *text, int flags, void *opaque)
489             {
490 0 0         if (flags & HOEDOWN_TABLE_HEADER) {
491 0           HOEDOWN_BUFPUTSL(ob, "<th");
492             } else {
493 0           HOEDOWN_BUFPUTSL(ob, "<td");
494             }
495              
496 0           switch (flags & HOEDOWN_TABLE_ALIGNMASK) {
497             case HOEDOWN_TABLE_ALIGN_CENTER:
498 0           HOEDOWN_BUFPUTSL(ob, " style=\"text-align: center\">");
499 0           break;
500              
501             case HOEDOWN_TABLE_ALIGN_L:
502 0           HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">");
503 0           break;
504              
505             case HOEDOWN_TABLE_ALIGN_R:
506 0           HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">");
507 0           break;
508              
509             default:
510 0           HOEDOWN_BUFPUTSL(ob, ">");
511             }
512              
513 0 0         if (text)
514 0           hoedown_buffer_put(ob, text->data, text->size);
515              
516 0 0         if (flags & HOEDOWN_TABLE_HEADER) {
517 0           HOEDOWN_BUFPUTSL(ob, "</th>\n");
518             } else {
519 0           HOEDOWN_BUFPUTSL(ob, "</td>\n");
520             }
521 0           }
522              
523             static int
524 0           rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
525             {
526 0 0         if (!text || !text->size) return 0;
    0          
527 0           HOEDOWN_BUFPUTSL(ob, "<sup>");
528 0           hoedown_buffer_put(ob, text->data, text->size);
529 0           HOEDOWN_BUFPUTSL(ob, "</sup>");
530 0           return 1;
531             }
532              
533             static void
534 10           rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
535             {
536 10 50         if (text)
537 10           escape_html(ob, text->data, text->size);
538 10           }
539              
540             static void
541 0           rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *text, void *opaque)
542             {
543             rndr_state *state = opaque;
544              
545 0 0         if (ob->size) hoedown_buffer_putc(ob, '\n');
546 0           HOEDOWN_BUFPUTSL(ob, "<div class=\"footnotes\">\n");
547 0 0         hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
548 0           HOEDOWN_BUFPUTSL(ob, "<ol>\n");
549            
550 0 0         if (text)
551 0           hoedown_buffer_put(ob, text->data, text->size);
552            
553 0           HOEDOWN_BUFPUTSL(ob, "\n</ol>\n</div>\n");
554 0           }
555              
556             static void
557 0           rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *text, unsigned int num, void *opaque)
558             {
559             size_t i = 0;
560             int pfound = 0;
561            
562             /* insert anchor at the end of first paragraph block */
563 0 0         if (text) {
564 0 0         while ((i+3) < text->size) {
565 0 0         if (text->data[i++] != '<') continue;
566 0 0         if (text->data[i++] != '/') continue;
567 0 0         if (text->data[i++] != 'p' && text->data[i] != 'P') continue;
    0          
568 0 0         if (text->data[i] != '>') continue;
569             i -= 3;
570             pfound = 1;
571             break;
572             }
573             }
574            
575 0           hoedown_buffer_printf(ob, "\n<li id=\"fn%d\">\n", num);
576 0 0         if (pfound) {
577 0           hoedown_buffer_put(ob, text->data, i);
578 0           hoedown_buffer_printf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
579 0           hoedown_buffer_put(ob, text->data + i, text->size - i);
580 0 0         } else if (text) {
581 0           hoedown_buffer_put(ob, text->data, text->size);
582             }
583 0           HOEDOWN_BUFPUTSL(ob, "</li>\n");
584 0           }
585              
586             static int
587 0           rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, void *opaque)
588             {
589 0           hoedown_buffer_printf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
590 0           return 1;
591             }
592              
593             static void
594 7           toc_header(hoedown_buffer *ob, const hoedown_buffer *text, int level, void *opaque)
595             {
596             rndr_state *state = opaque;
597              
598 7 50         if (level <= state->toc_data.nesting_level) {
599             /* set the level offset if this is the first header
600             * we're parsing for the document */
601 7 100         if (state->toc_data.current_level == 0)
602 1           state->toc_data.level_offset = level - 1;
603              
604 7           level -= state->toc_data.level_offset;
605              
606 7 100         if (level > state->toc_data.current_level) {
607 10 100         while (level > state->toc_data.current_level) {
608 5           HOEDOWN_BUFPUTSL(ob, "<ul>\n<li>\n");
609 5           state->toc_data.current_level++;
610             }
611 2 50         } else if (level < state->toc_data.current_level) {
612 2           HOEDOWN_BUFPUTSL(ob, "</li>\n");
613 5 100         while (level < state->toc_data.current_level) {
614 3           HOEDOWN_BUFPUTSL(ob, "</ul>\n</li>\n");
615 3           state->toc_data.current_level--;
616             }
617 2           HOEDOWN_BUFPUTSL(ob,"<li>\n");
618             } else {
619 0           HOEDOWN_BUFPUTSL(ob,"</li>\n<li>\n");
620             }
621              
622 7           hoedown_buffer_printf(ob, "<a href=\"#toc_%d\">", state->toc_data.header_count++);
623 7 50         if (text) escape_html(ob, text->data, text->size);
624 7           HOEDOWN_BUFPUTSL(ob, "</a>\n");
625             }
626 7           }
627              
628             static int
629 0           toc_link(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *content, void *opaque)
630             {
631 0 0         if (content && content->size)
    0          
632 0           hoedown_buffer_put(ob, content->data, content->size);
633 0           return 1;
634             }
635              
636             static void
637 1           toc_finalize(hoedown_buffer *ob, void *opaque)
638             {
639             rndr_state *state = opaque;
640              
641 3 100         while (state->toc_data.current_level > 0) {
642 2           HOEDOWN_BUFPUTSL(ob, "</li>\n</ul>\n");
643 2           state->toc_data.current_level--;
644             }
645 1           }
646              
647             hoedown_renderer *
648 1           hoedown_html_toc_renderer_new(int nesting_level)
649             {
650             static const hoedown_renderer cb_default = {
651             NULL,
652             NULL,
653             NULL,
654             toc_header,
655             NULL,
656             NULL,
657             NULL,
658             NULL,
659             NULL,
660             NULL,
661             NULL,
662             NULL,
663             NULL,
664              
665             NULL,
666             rndr_codespan,
667             rndr_double_emphasis,
668             rndr_emphasis,
669             rndr_underline,
670             rndr_highlight,
671             rndr_quote,
672             NULL,
673             NULL,
674             toc_link,
675             NULL,
676             rndr_triple_emphasis,
677             rndr_strikethrough,
678             rndr_superscript,
679             NULL,
680              
681             NULL,
682             NULL,
683              
684             NULL,
685             toc_finalize,
686            
687             NULL
688             };
689              
690             rndr_state       *state;
691             hoedown_renderer *renderer;
692              
693             /* Prepare the state pointer */
694 1           state = malloc(sizeof(rndr_state));
695 1 50         if (!state)
696             return NULL;
697              
698             memset(state, 0x0, sizeof(rndr_state));
699              
700 1 50         if (nesting_level > 0) {
701 1           state->flags |= HOEDOWN_HTML_TOC;
702 1           state->toc_data.nesting_level = nesting_level;
703             }
704              
705             /* Prepare the renderer */
706 1           renderer = malloc(sizeof(hoedown_renderer));
707 1 50         if (!renderer) {
708 0           free(state);
709 0           return NULL;
710             }
711            
712             memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
713            
714 1           renderer->opaque = state;
715 1           return renderer;
716             }
717            
718             hoedown_renderer *
719 5           hoedown_html_renderer_new(unsigned int render_flags, int nesting_level)
720             {
721             static const hoedown_renderer cb_default = {
722             rndr_blockcode,
723             rndr_blockquote,
724             rndr_raw_block,
725             rndr_header,
726             rndr_hrule,
727             rndr_list,
728             rndr_listitem,
729             rndr_paragraph,
730             rndr_table,
731             rndr_tablerow,
732             rndr_tablecell,
733             rndr_footnotes,
734             rndr_footnote_def,
735            
736             rndr_autolink,
737             rndr_codespan,
738             rndr_double_emphasis,
739             rndr_emphasis,
740             rndr_underline,
741             rndr_highlight,
742             rndr_quote,
743             rndr_image,
744             rndr_linebreak,
745             rndr_link,
746             rndr_raw_html,
747             rndr_triple_emphasis,
748             rndr_strikethrough,
749             rndr_superscript,
750             rndr_footnote_ref,
751            
752             NULL,
753             rndr_normal_text,
754            
755             NULL,
756             NULL,
757            
758             NULL
759             };
760            
761             rndr_state *state;
762             hoedown_renderer *renderer;
763            
764             /* Prepare the state pointer */
765 5           state = malloc(sizeof(rndr_state));
766 5 50         if (!state)
767             return NULL;
768            
769             memset(state, 0x0, sizeof(rndr_state));
770            
771 5           state->flags = render_flags;
772            
773 5 100         if (nesting_level > 0) {
774 4           state->flags |= HOEDOWN_HTML_TOC;
775 4           state->toc_data.nesting_level = nesting_level;
776             }
777            
778             /* Prepare the renderer */
779 5           renderer = malloc(sizeof(hoedown_renderer));
780 5 50         if (!renderer) {
781 0           free(state);
782 0           return NULL;
783             }
784            
785             memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
786            
787 5 50         if (render_flags & HOEDOWN_HTML_SKIP_IMAGES)
788 0           renderer->image = NULL;
789            
790 5 50         if (render_flags & HOEDOWN_HTML_SKIP_LINKS) {
791 0           renderer->link = NULL;
792 0           renderer->autolink = NULL;
793             }
794            
795 5 50         if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE)
796 0           renderer->blockhtml = NULL;
797            
798 5           renderer->opaque = state;
799 5           return renderer;
800             }
801            
802             void
803 6           hoedown_html_renderer_free(hoedown_renderer *renderer)
804             {
805 6           free(renderer->opaque);
806 6           free(renderer);
807 6           }
808