Coverage Report

Created: 2024-07-23 06:07

/src/hoextdown/src/html.c
Line
Count
Source (jump to first uncovered line)
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
#include "hash.h"
10
11
#ifdef _MSC_VER
12
#define strncasecmp    _strnicmp
13
#endif
14
15
557k
#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML)
16
548k
#define USE_TASK_LIST(opt) (opt->flags & HOEDOWN_HTML_USE_TASK_LIST)
17
548k
#define USE_RADIO_LIST(opt) (opt->flags & HOEDOWN_HTML_USE_RADIO_LIST)
18
19
0
#define RADIO_NAME_PREFIX "radio-list-"
20
0
#define RADIO_NAME_THUNK RADIO_NAME_PREFIX "00000000"
21
0
#define RADIO_NAME_THUNK_LEN (sizeof(RADIO_NAME_THUNK) - 1)
22
23
hoedown_html_tag
24
hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname)
25
0
{
26
0
  size_t i;
27
0
  int closed = 0;
28
29
0
  if (size < 3 || data[0] != '<')
30
0
    return HOEDOWN_HTML_TAG_NONE;
31
32
0
  i = 1;
33
34
0
  if (data[i] == '/') {
35
0
    closed = 1;
36
0
    i++;
37
0
  }
38
39
0
  for (; i < size; ++i, ++tagname) {
40
0
    if (*tagname == 0)
41
0
      break;
42
43
0
    if (data[i] != *tagname)
44
0
      return HOEDOWN_HTML_TAG_NONE;
45
0
  }
46
47
0
  if (i == size)
48
0
    return HOEDOWN_HTML_TAG_NONE;
49
50
0
  if (isspace(data[i]) || data[i] == '>')
51
0
    return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN;
52
53
0
  return HOEDOWN_HTML_TAG_NONE;
54
0
}
55
56
static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length)
57
12.2M
{
58
12.2M
  hoedown_escape_html(ob, source, length, 0);
59
12.2M
}
60
61
static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
62
296k
{
63
296k
  hoedown_escape_href(ob, source, length);
64
296k
}
65
66
/********************
67
 * GENERIC RENDERER *
68
 ********************/
69
static int
70
rndr_attributes(struct hoedown_buffer *ob, const uint8_t *buf, const size_t size, hoedown_buffer *class, const hoedown_renderer_data *data)
71
115k
{
72
  /* i keeps track of how much we've parsed so far. */
73
115k
  size_t i = 0;
74
115k
  int rendered_id = 0;
75
115k
  int must_free_class = 0;
76
77
263k
  while (i < size) {
78
    /* An attribute can come in a variety of shapes:
79
     * - #id, where "id" is a sequence of non-space characters
80
     * - .cls, where  "cls" is a sequence of non-space characters
81
     * - key=val, where "key" and "val" are sequences of non-space characters
82
     * - key="quoted val", where "quoted val" may not contain a quote.
83
     * We look for each case and set the key and val variables. We ignore text
84
     * that does not match the patterns above (e.g., single words).
85
     * */
86
162k
    int is_id = 0;
87
162k
    int is_class = 0;
88
162k
    size_t key_start = 0, key_end = 0, val_start = 0, val_end = 0;
89
90
    /* Skip to the first non-space character */
91
15.9M
    for (; i < size && buf[i] == ' '; ++i) {}
92
162k
    if (i >= size) break;
93
94
161k
    if (buf[i] == '#') {
95
      /* #id */
96
65.2k
      is_id = 1;
97
65.2k
      ++i;
98
96.2k
    } else if (buf[i] == '.') {
99
      /* .cls */
100
3.22k
      is_class = 1;
101
3.22k
      ++i;
102
3.22k
    }
103
161k
    if (is_id || is_class) {
104
68.4k
      val_start = i;
105
11.0M
      for (val_end = val_start; val_end < size && buf[val_end] != ' '; ++val_end) {}
106
68.4k
      i = val_end;
107
93.0k
    } else {
108
      /* key=... */
109
93.0k
      key_start = i;
110
6.26M
      for (key_end = key_start; key_end < size && buf[key_end] != ' ' && buf[key_end] != '='; ++key_end) {}
111
93.0k
      i = key_end;
112
93.0k
      if (i >= size) break;
113
79.3k
      ++i;
114
79.3k
      if (buf[key_end] != '=') continue;
115
68.6k
      if (strncasecmp((char *)buf + key_start, "id", key_end - key_start) == 0) {
116
23.8k
        is_id = 1;
117
44.7k
      } else if (strncasecmp((char *)buf + key_start, "class", key_end - key_start) == 0) {
118
21.1k
        is_class = 1;
119
21.1k
      }
120
121
68.6k
      val_start = i;
122
68.6k
      if (val_start < size && (buf[val_start] == '"' || buf[val_start] == '\'')) {
123
        /* key="quoted val" */
124
1.66k
        val_start += 1;
125
3.88M
        for (val_end = val_start; val_end < size && buf[val_end] != buf[val_start - 1]; ++val_end) {}
126
1.66k
        i = val_end;
127
1.66k
        if (i >= size) break;
128
1.35k
        ++i;
129
66.9k
      } else {
130
        /* key=val */
131
1.18M
        for (val_end = val_start; val_end < size && buf[val_end] != ' '; ++val_end) {}
132
66.9k
        i = val_end;
133
66.9k
      }
134
68.6k
    }
135
136
    /* Now that we found our keys and values, let's render the attribute. */
137
136k
    if (is_id) {
138
88.8k
      if (rendered_id) continue;
139
88.4k
      if (val_end == val_start) continue;
140
63.4k
      rendered_id = 1;
141
63.4k
      HOEDOWN_BUFPUTSL(ob, " id=\"");
142
63.4k
      escape_html(ob, buf + val_start, val_end - val_start);
143
63.4k
      hoedown_buffer_putc(ob, '"');
144
63.4k
    } else if (is_class) {
145
24.3k
      if (val_end == val_start) continue;
146
3.02k
      if (!class) {
147
2.68k
        class = hoedown_buffer_new(size);
148
2.68k
        must_free_class = 1;
149
2.68k
      }
150
3.02k
      escape_html(class, buf + val_start, val_end - val_start);
151
3.02k
      hoedown_buffer_putc(class, ' ');
152
23.5k
    } else {
153
23.5k
      if (key_end == key_start) continue;
154
23.5k
      hoedown_buffer_putc(ob, ' ');
155
23.5k
      escape_html(ob, buf + key_start, key_end - key_start);
156
23.5k
      HOEDOWN_BUFPUTSL(ob, "=\"");
157
23.5k
      escape_html(ob, buf + val_start, val_end - val_start);
158
23.5k
      hoedown_buffer_putc(ob, '"');
159
23.5k
    }
160
136k
  }
161
162
115k
  if (class) {
163
3.18k
    if (class->size > 0) {
164
3.18k
      HOEDOWN_BUFPUTSL(ob, " class=\"");
165
3.18k
      hoedown_buffer_put(ob, class->data, class->size-1);
166
3.18k
      hoedown_buffer_putc(ob, '"');
167
3.18k
    }
168
3.18k
    if (must_free_class) {
169
2.68k
      hoedown_buffer_free(class);
170
2.68k
    }
171
3.18k
  }
172
115k
  return 1;
173
115k
}
174
175
static int
176
rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)
177
7.27k
{
178
7.27k
  hoedown_html_renderer_state *state = data->opaque;
179
180
7.27k
  if (!link || !link->size)
181
0
    return 0;
182
183
7.27k
  HOEDOWN_BUFPUTSL(ob, "<a href=\"");
184
7.27k
  if (type == HOEDOWN_AUTOLINK_EMAIL)
185
1.02k
    HOEDOWN_BUFPUTSL(ob, "mailto:");
186
7.27k
  escape_href(ob, link->data, link->size);
187
188
7.27k
  if (state->link_attributes) {
189
0
    hoedown_buffer_putc(ob, '\"');
190
0
    state->link_attributes(ob, link, data);
191
0
    hoedown_buffer_putc(ob, '>');
192
7.27k
  } else {
193
7.27k
    HOEDOWN_BUFPUTSL(ob, "\">");
194
7.27k
  }
195
196
  /*
197
   * Pretty printing: if we get an email address as
198
   * an actual URI, e.g. `mailto:foo@bar.com`, we don't
199
   * want to print the `mailto:` prefix
200
   */
201
7.27k
  if (hoedown_buffer_prefix(link, "mailto:") == 0) {
202
235
    escape_html(ob, link->data + 7, link->size - 7);
203
7.04k
  } else {
204
7.04k
    escape_html(ob, link->data, link->size);
205
7.04k
  }
206
207
7.27k
  HOEDOWN_BUFPUTSL(ob, "</a>");
208
209
7.27k
  return 1;
210
7.27k
}
211
212
static void
213
rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_buffer *attr, const hoedown_renderer_data *data)
214
7.75k
{
215
7.75k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
216
217
7.75k
  if (lang) {
218
4.60k
    hoedown_html_renderer_state *state = data->opaque;
219
4.60k
    if ((state->flags & HOEDOWN_HTML_FENCED_CODE_SCRIPT) &&
220
4.60k
        lang->size > 7 && memcmp(lang->data, "script@", 7) == 0 && text) {
221
1.62k
      HOEDOWN_BUFPUTSL(ob, "<script type=\"");
222
1.62k
      escape_html(ob, lang->data + 7, lang->size - 7);
223
1.62k
      HOEDOWN_BUFPUTSL(ob, "\">\n");
224
1.62k
      hoedown_buffer_put(ob, text->data, text->size);
225
1.62k
      HOEDOWN_BUFPUTSL(ob, "</script>\n");
226
1.62k
      return;
227
1.62k
    }
228
2.97k
    HOEDOWN_BUFPUTSL(ob, "<pre><code");
229
2.97k
    if (attr && attr->size) {
230
504
      hoedown_buffer *lang_class = hoedown_buffer_new(lang->size + 9);
231
504
      if (lang->size) {
232
504
        HOEDOWN_BUFPUTSL(lang_class, "language-");
233
504
        escape_html(lang_class, lang->data, lang->size);
234
504
        if (lang_class->data[lang_class->size-1] != ' ') {
235
504
          hoedown_buffer_putc(lang_class, ' ');
236
504
        }
237
504
      }
238
504
      rndr_attributes(ob, attr->data, attr->size, lang_class, data);
239
504
      hoedown_buffer_free(lang_class);
240
2.47k
    } else {
241
2.47k
      HOEDOWN_BUFPUTSL(ob, " class=\"language-");
242
2.47k
      escape_html(ob, lang->data, lang->size);
243
2.47k
      hoedown_buffer_putc(ob, '"');
244
2.47k
    }
245
2.97k
    hoedown_buffer_putc(ob, '>');
246
3.14k
  } else if (attr && attr->size) {
247
349
    HOEDOWN_BUFPUTSL(ob, "<pre><code");
248
349
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
249
349
    hoedown_buffer_putc(ob, '>');
250
2.79k
  } else {
251
2.79k
    HOEDOWN_BUFPUTSL(ob, "<pre><code>");
252
2.79k
  }
253
254
6.12k
  if (text)
255
3.24k
    escape_html(ob, text->data, text->size);
256
257
6.12k
  HOEDOWN_BUFPUTSL(ob, "</code></pre>\n");
258
6.12k
}
259
260
static void
261
rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
262
20.7k
{
263
20.7k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
264
20.7k
  HOEDOWN_BUFPUTSL(ob, "<blockquote>\n");
265
20.7k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
266
20.7k
  HOEDOWN_BUFPUTSL(ob, "</blockquote>\n");
267
20.7k
}
268
269
static int
270
rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, const hoedown_renderer_data *data)
271
16.2k
{
272
16.2k
  HOEDOWN_BUFPUTSL(ob, "<code");
273
16.2k
  if (attr && attr->size) {
274
305
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
275
305
  }
276
16.2k
  hoedown_buffer_putc(ob, '>');
277
16.2k
  if (text) escape_html(ob, text->data, text->size);
278
16.2k
  HOEDOWN_BUFPUTSL(ob, "</code>");
279
16.2k
  return 1;
280
16.2k
}
281
282
static int
283
rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
284
5.52k
{
285
5.52k
  if (!content || !content->size)
286
3.42k
    return 0;
287
288
2.09k
  HOEDOWN_BUFPUTSL(ob, "<del>");
289
2.09k
  hoedown_buffer_put(ob, content->data, content->size);
290
2.09k
  HOEDOWN_BUFPUTSL(ob, "</del>");
291
2.09k
  return 1;
292
5.52k
}
293
294
static int
295
rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
296
14.1k
{
297
14.1k
  if (!content || !content->size)
298
4.61k
    return 0;
299
300
9.58k
  HOEDOWN_BUFPUTSL(ob, "<strong>");
301
9.58k
  hoedown_buffer_put(ob, content->data, content->size);
302
9.58k
  HOEDOWN_BUFPUTSL(ob, "</strong>");
303
304
9.58k
  return 1;
305
14.1k
}
306
307
static int
308
rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
309
1.44k
{
310
1.44k
  if (!content || !content->size) return 0;
311
1.13k
  HOEDOWN_BUFPUTSL(ob, "<em>");
312
1.13k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
313
1.13k
  HOEDOWN_BUFPUTSL(ob, "</em>");
314
1.13k
  return 1;
315
1.44k
}
316
317
static int
318
rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
319
79.7k
{
320
79.7k
  if (!content || !content->size)
321
925
    return 0;
322
323
78.8k
  HOEDOWN_BUFPUTSL(ob, "<u>");
324
78.8k
  hoedown_buffer_put(ob, content->data, content->size);
325
78.8k
  HOEDOWN_BUFPUTSL(ob, "</u>");
326
327
78.8k
  return 1;
328
79.7k
}
329
330
static int
331
rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
332
451
{
333
451
  if (!content || !content->size)
334
230
    return 0;
335
336
221
  HOEDOWN_BUFPUTSL(ob, "<mark>");
337
221
  hoedown_buffer_put(ob, content->data, content->size);
338
221
  HOEDOWN_BUFPUTSL(ob, "</mark>");
339
340
221
  return 1;
341
451
}
342
343
static int
344
rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
345
96.0k
{
346
96.0k
  if (!content || !content->size)
347
1.18k
    return 0;
348
349
94.8k
  HOEDOWN_BUFPUTSL(ob, "<q>");
350
94.8k
  hoedown_buffer_put(ob, content->data, content->size);
351
94.8k
  HOEDOWN_BUFPUTSL(ob, "</q>");
352
353
94.8k
  return 1;
354
96.0k
}
355
356
static int
357
rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data)
358
549k
{
359
549k
  hoedown_html_renderer_state *state = data->opaque;
360
549k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<br/>\n" : "<br>\n");
361
549k
  return 1;
362
549k
}
363
364
static void
365
rndr_header_id(hoedown_buffer *ob, const uint8_t *source, size_t length, int escape, const hoedown_renderer_data *data)
366
64.5k
{
367
64.5k
  size_t i = 0, n = 0;
368
64.5k
  hoedown_html_renderer_state *state = data->opaque;
369
64.5k
  hoedown_hash *hash = state->hash.header_id;
370
371
13.8M
  while (i < length) {
372
13.8M
    if (isalnum(source[i])) {
373
2.53M
      hoedown_buffer_putc(ob, tolower(source[i]));
374
11.2M
    } else if (source[i] == ' ') {
375
8.06M
      hoedown_buffer_putc(ob, '-');
376
8.06M
    } else if (source[i] == '-' || source[i] == '_') {
377
32.5k
      hoedown_buffer_putc(ob, source[i]);
378
3.17M
    } else if (!isascii(source[i])) {
379
135k
      if (escape) {
380
0
        hoedown_buffer_printf(ob, "%%%02X", source[i]);
381
135k
      } else {
382
135k
        hoedown_buffer_putc(ob, source[i]);
383
135k
      }
384
3.04M
    } else if (source[i] == '&') {
385
365k
      while (i < length && source[i] != ';') {
386
295k
        ++i;
387
295k
      }
388
2.97M
    } else if (source[i] == '<') {
389
230M
      while (i < length && source[i] != '>') {
390
230M
        ++i;
391
230M
      }
392
203k
    }
393
13.8M
    ++i;
394
13.8M
  }
395
396
64.5k
  if (hash) {
397
64.5k
    void *value = hoedown_hash_find(hash, (char *)source, length);
398
64.5k
    if (value) {
399
53.6k
      size_t *p = (size_t *)value;
400
53.6k
      ++(*p);
401
53.6k
      n = *p;
402
53.6k
    }
403
64.5k
    if (n > 0) {
404
53.6k
      hoedown_buffer_printf(ob, "-%ld", n);
405
53.6k
    } else if (hash) {
406
10.8k
      size_t *p = (size_t *)malloc(sizeof(size_t));
407
10.8k
      if (p) {
408
10.8k
        *p = 0;
409
10.8k
        hoedown_hash_add(hash, (char *)source, length, (void *)p, free);
410
10.8k
      }
411
10.8k
    }
412
64.5k
  }
413
64.5k
}
414
415
static void
416
rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data)
417
66.6k
{
418
66.6k
  hoedown_html_renderer_state *state = data->opaque;
419
420
66.6k
  if (ob->size)
421
62.5k
    hoedown_buffer_putc(ob, '\n');
422
423
66.6k
  hoedown_buffer *merged_attr = hoedown_buffer_new(sizeof(hoedown_buffer));
424
425
66.6k
  if (attr && attr->size) {
426
562
    hoedown_buffer_put(merged_attr, attr->data, attr->size);
427
562
  }
428
66.6k
  if (content && content->size && ((state->flags & HOEDOWN_HTML_HEADER_ID) || (level <= state->toc_data.nesting_level))) {
429
64.5k
    hoedown_buffer_puts(merged_attr, " #");
430
64.5k
    rndr_header_id(merged_attr, content->data, content->size, 0, data);
431
64.5k
  }
432
433
66.6k
  if (merged_attr && merged_attr->size) {
434
64.9k
    hoedown_buffer_printf(ob, "<h%d", level);
435
64.9k
    rndr_attributes(ob, merged_attr->data, merged_attr->size, NULL, data);
436
64.9k
    hoedown_buffer_putc(ob, '>');
437
64.9k
  } else {
438
1.68k
    hoedown_buffer_printf(ob, "<h%d>", level);
439
1.68k
  }
440
441
66.6k
  hoedown_buffer_free(merged_attr);
442
443
66.6k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
444
66.6k
  hoedown_buffer_printf(ob, "</h%d>\n", level);
445
66.6k
}
446
447
static int
448
rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data)
449
305k
{
450
305k
  hoedown_html_renderer_state *state = data->opaque;
451
452
305k
  HOEDOWN_BUFPUTSL(ob, "<a href=\"");
453
454
305k
  if (link && link->size) {
455
284k
    escape_href(ob, link->data, link->size);
456
284k
  }
457
458
305k
  if (title && title->size) {
459
179k
    HOEDOWN_BUFPUTSL(ob, "\" title=\"");
460
179k
    escape_html(ob, title->data, title->size);
461
179k
  }
462
463
305k
  hoedown_buffer_putc(ob, '"');
464
305k
  if (state->link_attributes) {
465
0
    state->link_attributes(ob, link, data);
466
0
  }
467
305k
  if (attr && attr->size) {
468
35.7k
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
469
35.7k
  }
470
305k
  hoedown_buffer_putc(ob, '>');
471
472
305k
  if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
473
305k
  HOEDOWN_BUFPUTSL(ob, "</a>");
474
305k
  return 1;
475
305k
}
476
477
static void
478
buffer_copy_with_replace(hoedown_buffer *dest, const hoedown_buffer *src, const char *search_str, hoedown_buffer *replace)
479
0
{
480
0
  const uint8_t *src_cur = src->data;
481
0
  const uint8_t *src_end = src->data + src->size;
482
0
  const char *search_end = search_str + replace->size;
483
484
0
  while (src_cur < src_end) {
485
0
    const char *search_ptr = search_str;
486
0
    const uint8_t *scan_ptr = src_cur;
487
0
    while (search_ptr < search_end && *search_ptr == *scan_ptr) {
488
0
      search_ptr++;
489
0
      scan_ptr++;
490
0
    }
491
0
    if (search_ptr == search_end) {
492
0
      hoedown_buffer_put(dest, replace->data, replace->size);
493
0
      src_cur += replace->size;
494
0
    } else {
495
0
      hoedown_buffer_putc(dest, *src_cur);
496
0
      src_cur++;
497
0
    }
498
0
  }
499
0
}
500
501
static void
502
rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, unsigned int flags, const hoedown_renderer_data *data)
503
49.8k
{
504
49.8k
        hoedown_html_renderer_state *state = data->opaque;
505
506
49.8k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
507
508
49.8k
  if (flags & HOEDOWN_LIST_ORDERED) {
509
18.5k
    HOEDOWN_BUFPUTSL(ob, "<ol");
510
31.2k
  } else if (flags & HOEDOWN_LIST_DEFINITION) {
511
21.9k
    HOEDOWN_BUFPUTSL(ob, "<dl");
512
21.9k
  } else {
513
9.24k
    HOEDOWN_BUFPUTSL(ob, "<ul");
514
9.24k
  }
515
49.8k
  if (attr && attr->size) {
516
357
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
517
357
  }
518
519
49.8k
  HOEDOWN_BUFPUTSL(ob, ">\n");
520
521
49.8k
  if (content) {
522
49.8k
    if (flags & HOEDOWN_LI_RADIO) {
523
      // Rewrite the content to fill in any list ids
524
0
      state->cur_list_id++;
525
0
      hoedown_buffer *replace = hoedown_buffer_new(RADIO_NAME_THUNK_LEN);
526
0
      hoedown_buffer_printf(replace, "%s%08x", RADIO_NAME_PREFIX, state->cur_list_id);
527
0
      buffer_copy_with_replace(ob, content, RADIO_NAME_THUNK, replace);
528
49.8k
    } else {
529
49.8k
      hoedown_buffer_put(ob, content->data, content->size);
530
49.8k
    }
531
49.8k
        }
532
533
49.8k
  if (flags & HOEDOWN_LIST_ORDERED) {
534
18.5k
    HOEDOWN_BUFPUTSL(ob, "</ol>\n");
535
31.2k
  } else if (flags & HOEDOWN_LIST_DEFINITION) {
536
21.9k
    HOEDOWN_BUFPUTSL(ob, "</dl>\n");
537
21.9k
  } else {
538
9.24k
    HOEDOWN_BUFPUTSL(ob, "</ul>\n");
539
9.24k
  }
540
49.8k
}
541
542
static void
543
rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, hoedown_list_flags *flags, const hoedown_renderer_data *data)
544
274k
{
545
274k
  if (content) {
546
274k
    hoedown_html_renderer_state *state = data->opaque;
547
274k
    size_t prefix = 0;
548
274k
    size_t size = content->size;
549
274k
    int is_li_tag = 0;
550
318k
    while (size && content->data[size - 1] == '\n')
551
44.4k
      size--;
552
553
274k
    if (*flags & HOEDOWN_LI_DD) {
554
108k
      HOEDOWN_BUFPUTSL(ob, "<dd");
555
165k
    } else  if (*flags & HOEDOWN_LI_DT) {
556
120k
      HOEDOWN_BUFPUTSL(ob, "<dt");
557
120k
    } else {
558
45.3k
      HOEDOWN_BUFPUTSL(ob, "<li");
559
45.3k
      is_li_tag = 1;
560
45.3k
    }
561
562
274k
    if (attr && attr->size) {
563
1.79k
      rndr_attributes(ob, attr->data, attr->size, NULL, data);
564
1.79k
    }
565
274k
    hoedown_buffer_putc(ob, '>');
566
567
274k
    if (USE_TASK_LIST(state) && is_li_tag && size >= 3) {
568
      /* Block list items are wrapped in <p> tags. Output the opening tag now,
569
       * then check for a task list. */
570
27.3k
      if (*flags & HOEDOWN_LI_BLOCK) {
571
14.4k
        prefix = 3;
572
14.4k
        hoedown_buffer_put(ob, content->data, prefix);
573
14.4k
      }
574
27.3k
      if (size >= prefix + 3) {
575
26.6k
        if (strncmp((char *)content->data + prefix, "[ ]", 3) == 0) {
576
213
          HOEDOWN_BUFPUTSL(ob, "<input type=\"checkbox\"");
577
213
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
578
213
          prefix += 3;
579
213
          *flags |= HOEDOWN_LI_TASK;
580
26.4k
        } else if (strncasecmp((char *)content->data + prefix, "[x]", 3) == 0) {
581
365
          HOEDOWN_BUFPUTSL(ob, "<input checked=\"\" type=\"checkbox\"");
582
365
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
583
365
          prefix += 3;
584
365
          *flags |= HOEDOWN_LI_TASK;
585
365
        }
586
26.6k
      }
587
27.3k
    }
588
274k
    if (USE_RADIO_LIST(state) && is_li_tag && size >= 3) {
589
0
      if (*flags & HOEDOWN_LI_BLOCK) {
590
0
        prefix = 3;
591
0
        hoedown_buffer_put(ob, content->data, prefix);
592
0
      }
593
0
      if (size >= prefix + 3) {
594
0
        if (strncmp((char *)content->data + prefix, "( )", 3) == 0) {
595
0
          HOEDOWN_BUFPUTSL(ob, "<input type=\"radio\" name=\"" RADIO_NAME_THUNK "\"");
596
0
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
597
0
          prefix += 3;
598
0
          *flags |= HOEDOWN_LI_RADIO;
599
0
        } else if (strncasecmp((char *)content->data + prefix, "(x)", 3) == 0) {
600
0
          HOEDOWN_BUFPUTSL(ob, "<input checked=\"\" type=\"radio\" name=\"" RADIO_NAME_THUNK "\"");
601
0
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
602
0
          prefix += 3;
603
0
          *flags |= HOEDOWN_LI_RADIO;
604
0
        }
605
0
      }
606
0
    }
607
608
274k
    hoedown_buffer_put(ob, content->data+prefix, size-prefix);
609
274k
  } else {
610
0
    if (*flags & HOEDOWN_LI_DD) {
611
0
      HOEDOWN_BUFPUTSL(ob, "<dd>");
612
0
    } else if (*flags & HOEDOWN_LI_DT) {
613
0
      HOEDOWN_BUFPUTSL(ob, "<dt>");
614
0
    } else {
615
0
      HOEDOWN_BUFPUTSL(ob, "<li>");
616
0
    }
617
0
  }
618
274k
  if (*flags & HOEDOWN_LI_DD) {
619
108k
    HOEDOWN_BUFPUTSL(ob, "</dd>\n");
620
165k
  } else if (*flags & HOEDOWN_LI_DT) {
621
120k
    HOEDOWN_BUFPUTSL(ob, "</dt>\n");
622
120k
  } else {
623
45.3k
    HOEDOWN_BUFPUTSL(ob, "</li>\n");
624
45.3k
  }
625
274k
}
626
627
static void
628
rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data)
629
219k
{
630
219k
  hoedown_html_renderer_state *state = data->opaque;
631
219k
  size_t i = 0;
632
633
219k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
634
635
219k
  if (!content || !content->size)
636
216
    return;
637
638
881k
  while (i < content->size && isspace(content->data[i])) i++;
639
640
219k
  if (i == content->size)
641
1.62k
    return;
642
643
217k
  HOEDOWN_BUFPUTSL(ob, "<p");
644
645
217k
  if (attr && attr->size) {
646
0
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
647
0
  }
648
649
217k
  HOEDOWN_BUFPUTSL(ob, ">");
650
651
217k
  if (state->flags & HOEDOWN_HTML_HARD_WRAP) {
652
217k
    size_t org;
653
731k
    while (i < content->size) {
654
731k
      org = i;
655
1.20G
      while (i < content->size && content->data[i] != '\n')
656
1.20G
        i++;
657
658
731k
      if (i > org)
659
731k
        hoedown_buffer_put(ob, content->data + org, i - org);
660
661
      /*
662
       * do not insert a line break if this newline
663
       * is the last character on the paragraph
664
       */
665
731k
      if (i >= content->size - 1)
666
217k
        break;
667
668
513k
      rndr_linebreak(ob, data);
669
513k
      i++;
670
513k
    }
671
217k
  } else if (state->flags & HOEDOWN_HTML_LINE_CONTINUE) {
672
0
    size_t org;
673
0
    while (i < content->size) {
674
0
      org = i;
675
0
      while (i < content->size && content->data[i] != '\n') {
676
0
        ++i;
677
0
      }
678
0
      if (i > org) {
679
0
        hoedown_buffer_put(ob, content->data + org, i - org);
680
0
      }
681
682
0
      if (i >= content->size - 1) {
683
0
        break;
684
0
      }
685
686
0
      if (content->data[i] == '\n' &&
687
0
        (isascii(content->data[i-1]) || isascii(content->data[i+1]))) {
688
0
        if (i < 5 ||
689
0
          strncmp((char *)content->data+i-5, "<br/>", 5) != 0 ||
690
0
          strncmp((char *)content->data+i-4, "<br>", 4) != 0) {
691
0
          HOEDOWN_BUFPUTSL(ob, " ");
692
0
        }
693
0
      }
694
0
      ++i;
695
0
    }
696
0
  } else {
697
0
    hoedown_buffer_put(ob, content->data + i, content->size - i);
698
0
  }
699
217k
  HOEDOWN_BUFPUTSL(ob, "</p>\n");
700
217k
}
701
702
static void
703
rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
704
0
{
705
0
  size_t org, sz;
706
707
0
  if (!text)
708
0
    return;
709
710
  /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */
711
0
  sz = text->size;
712
0
  while (sz > 0 && text->data[sz - 1] == '\n')
713
0
    sz--;
714
715
0
  org = 0;
716
0
  while (org < sz && text->data[org] == '\n')
717
0
    org++;
718
719
0
  if (org >= sz)
720
0
    return;
721
722
0
  if (ob->size)
723
0
    hoedown_buffer_putc(ob, '\n');
724
725
0
  hoedown_buffer_put(ob, text->data + org, sz - org);
726
0
  hoedown_buffer_putc(ob, '\n');
727
0
}
728
729
static int
730
rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
731
3.93k
{
732
3.93k
  if (!content || !content->size) return 0;
733
3.55k
  HOEDOWN_BUFPUTSL(ob, "<strong><em>");
734
3.55k
  hoedown_buffer_put(ob, content->data, content->size);
735
3.55k
  HOEDOWN_BUFPUTSL(ob, "</em></strong>");
736
3.55k
  return 1;
737
3.93k
}
738
739
static void
740
rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data)
741
3.70k
{
742
3.70k
  hoedown_html_renderer_state *state = data->opaque;
743
3.70k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
744
3.70k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
745
3.70k
}
746
747
static int
748
rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_buffer *attr, const hoedown_renderer_data *data)
749
9.47k
{
750
9.47k
  hoedown_html_renderer_state *state = data->opaque;
751
9.47k
  if (!link || !link->size) return 0;
752
753
3.86k
  HOEDOWN_BUFPUTSL(ob, "<img src=\"");
754
3.86k
  escape_href(ob, link->data, link->size);
755
3.86k
  HOEDOWN_BUFPUTSL(ob, "\" alt=\"");
756
757
3.86k
  if (alt && alt->size)
758
419
    escape_html(ob, alt->data, alt->size);
759
760
3.86k
  if (title && title->size) {
761
680
    HOEDOWN_BUFPUTSL(ob, "\" title=\"");
762
680
    escape_html(ob, title->data, title->size); }
763
764
3.86k
  hoedown_buffer_putc(ob, '"');
765
3.86k
  if (attr && attr->size) {
766
721
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
767
721
  }
768
769
3.86k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
770
3.86k
  return 1;
771
9.47k
}
772
773
static int
774
rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
775
28.4k
{
776
28.4k
  hoedown_html_renderer_state *state = data->opaque;
777
778
  /* ESCAPE overrides SKIP_HTML. It doesn't look to see if
779
   * there are any valid tags, just escapes all of them. */
780
28.4k
  if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) {
781
28.4k
    escape_html(ob, text->data, text->size);
782
28.4k
    return 1;
783
28.4k
  }
784
785
0
  if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0)
786
0
    return 1;
787
788
0
  hoedown_buffer_put(ob, text->data, text->size);
789
0
  return 1;
790
0
}
791
792
static void
793
rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data)
794
11.0k
{
795
11.0k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
796
11.0k
    HOEDOWN_BUFPUTSL(ob, "<table");
797
11.0k
    if (attr) rndr_attributes(ob, attr->data, attr->size, NULL, data);
798
11.0k
    HOEDOWN_BUFPUTSL(ob, ">\n");
799
11.0k
    hoedown_buffer_put(ob, content->data, content->size);
800
11.0k
    HOEDOWN_BUFPUTSL(ob, "</table>\n");
801
11.0k
}
802
803
static void
804
rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
805
11.0k
{
806
11.0k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
807
11.0k
    HOEDOWN_BUFPUTSL(ob, "<thead>\n");
808
11.0k
    hoedown_buffer_put(ob, content->data, content->size);
809
11.0k
    HOEDOWN_BUFPUTSL(ob, "</thead>\n");
810
11.0k
}
811
812
static void
813
rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
814
11.0k
{
815
11.0k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
816
11.0k
    HOEDOWN_BUFPUTSL(ob, "<tbody>\n");
817
11.0k
    hoedown_buffer_put(ob, content->data, content->size);
818
11.0k
    HOEDOWN_BUFPUTSL(ob, "</tbody>\n");
819
11.0k
}
820
821
static void
822
rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
823
26.5k
{
824
26.5k
  HOEDOWN_BUFPUTSL(ob, "<tr>\n");
825
26.5k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
826
26.5k
  HOEDOWN_BUFPUTSL(ob, "</tr>\n");
827
26.5k
}
828
829
static void
830
rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data)
831
48.1k
{
832
48.1k
  if (flags & HOEDOWN_TABLE_HEADER) {
833
19.5k
    HOEDOWN_BUFPUTSL(ob, "<th");
834
28.5k
  } else {
835
28.5k
    HOEDOWN_BUFPUTSL(ob, "<td");
836
28.5k
  }
837
838
48.1k
  switch (flags & HOEDOWN_TABLE_ALIGNMASK) {
839
20.8k
  case HOEDOWN_TABLE_ALIGN_CENTER:
840
20.8k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: center\">");
841
20.8k
    break;
842
843
3.05k
  case HOEDOWN_TABLE_ALIGN_LEFT:
844
3.05k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">");
845
3.05k
    break;
846
847
21.7k
  case HOEDOWN_TABLE_ALIGN_RIGHT:
848
21.7k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">");
849
21.7k
    break;
850
851
2.53k
  default:
852
2.53k
    HOEDOWN_BUFPUTSL(ob, ">");
853
48.1k
  }
854
855
48.1k
  if (content)
856
48.1k
    hoedown_buffer_put(ob, content->data, content->size);
857
858
48.1k
  if (flags & HOEDOWN_TABLE_HEADER) {
859
19.5k
    HOEDOWN_BUFPUTSL(ob, "</th>\n");
860
28.5k
  } else {
861
28.5k
    HOEDOWN_BUFPUTSL(ob, "</td>\n");
862
28.5k
  }
863
48.1k
}
864
865
static int
866
rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
867
288k
{
868
288k
  if (!content || !content->size) return 0;
869
286k
  HOEDOWN_BUFPUTSL(ob, "<sup>");
870
286k
  hoedown_buffer_put(ob, content->data, content->size);
871
286k
  HOEDOWN_BUFPUTSL(ob, "</sup>");
872
286k
  return 1;
873
288k
}
874
875
static void
876
rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
877
11.7M
{
878
11.7M
  if (content)
879
11.7M
    escape_html(ob, content->data, content->size);
880
11.7M
}
881
882
static void
883
rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
884
521
{
885
521
  hoedown_html_renderer_state *state = data->opaque;
886
887
521
  if (ob->size) hoedown_buffer_putc(ob, '\n');
888
521
  HOEDOWN_BUFPUTSL(ob, "<div class=\"footnotes\">\n");
889
521
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
890
521
  HOEDOWN_BUFPUTSL(ob, "<ol>\n");
891
892
521
  if (content) hoedown_buffer_put(ob, content->data, content->size);
893
894
521
  HOEDOWN_BUFPUTSL(ob, "\n</ol>\n</div>\n");
895
521
}
896
897
static void
898
rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data)
899
675
{
900
675
  size_t i = 0;
901
675
  int pfound = 0;
902
903
  /* insert anchor at the end of first paragraph block */
904
675
  if (content) {
905
2.82M
    while ((i+3) < content->size) {
906
2.82M
      if (content->data[i++] != '<') continue;
907
6.41k
      if (content->data[i++] != '/') continue;
908
3.37k
      if (content->data[i++] != 'p' && content->data[i] != 'P') continue;
909
866
      if (content->data[i] != '>') continue;
910
401
      i -= 3;
911
401
      pfound = 1;
912
401
      break;
913
866
    }
914
675
  }
915
916
675
  hoedown_buffer_printf(ob, "\n<li id=\"fn%d\">\n", num);
917
675
  if (pfound) {
918
401
    hoedown_buffer_put(ob, content->data, i);
919
401
    hoedown_buffer_printf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
920
401
    hoedown_buffer_put(ob, content->data + i, content->size - i);
921
401
  } else if (content) {
922
274
    hoedown_buffer_put(ob, content->data, content->size);
923
274
  }
924
675
  HOEDOWN_BUFPUTSL(ob, "</li>\n");
925
675
}
926
927
static int
928
rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)
929
675
{
930
675
  hoedown_buffer_printf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
931
675
  return 1;
932
675
}
933
934
static int
935
rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)
936
227k
{
937
227k
  hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2);
938
227k
  escape_html(ob, text->data, text->size);
939
227k
  hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2);
940
227k
  return 1;
941
227k
}
942
943
static void
944
toc_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, int level, const hoedown_renderer_data *data)
945
0
{
946
0
  hoedown_html_renderer_state *state = data->opaque;
947
948
0
  if (level <= state->toc_data.nesting_level) {
949
0
    if (level < state->toc_data.level_offset) {
950
0
      state->toc_data.current_level++;
951
0
      return;
952
0
    }
953
954
0
    if (level > state->toc_data.current_level) {
955
0
      while (level > state->toc_data.current_level) {
956
0
        HOEDOWN_BUFPUTSL(ob, "<ul>\n<li>\n");
957
0
        state->toc_data.current_level++;
958
0
      }
959
0
    } else if (level < state->toc_data.current_level) {
960
0
      HOEDOWN_BUFPUTSL(ob, "</li>\n");
961
0
      while (level < state->toc_data.current_level) {
962
0
        HOEDOWN_BUFPUTSL(ob, "</ul>\n</li>\n");
963
0
        state->toc_data.current_level--;
964
0
      }
965
0
      HOEDOWN_BUFPUTSL(ob,"<li>\n");
966
0
    } else {
967
0
      HOEDOWN_BUFPUTSL(ob,"</li>\n<li>\n");
968
0
    }
969
970
0
    if (attr && attr->size) {
971
0
      size_t n, i = 0;
972
0
      do {
973
0
        i++;
974
0
      } while (i < attr->size && attr->data[i-1] != '#');
975
0
      if (i < attr->size) {
976
0
        n = i;
977
0
        while (n < attr->size && attr->data[n] != '#' &&
978
0
             attr->data[n] != '.' && attr->data[n] != ' ') {
979
0
          n++;
980
0
        }
981
0
        HOEDOWN_BUFPUTSL(ob, "<a href=\"#");
982
0
        escape_html(ob, attr->data + i, n - i);
983
0
        HOEDOWN_BUFPUTSL(ob, "\">");
984
0
      }
985
0
    } else {
986
0
      hoedown_buffer_puts(ob, "<a href=\"#");
987
0
      rndr_header_id(ob, content->data, content->size, 1, data);
988
0
      hoedown_buffer_puts(ob, "\">");
989
0
    }
990
991
0
    if (content) {
992
0
      hoedown_buffer_put(ob, content->data, content->size);
993
0
    }
994
0
    HOEDOWN_BUFPUTSL(ob, "</a>\n");
995
0
  }
996
0
}
997
998
static int
999
toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *attr, const hoedown_renderer_data *data)
1000
0
{
1001
0
  if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
1002
0
  return 1;
1003
0
}
1004
1005
static void
1006
toc_initialize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data)
1007
0
{
1008
0
  hoedown_html_renderer_state *state = data->opaque;
1009
0
  if (state->toc_data.header) {
1010
0
    hoedown_buffer_printf(ob, "%s\n", state->toc_data.header);
1011
0
  }
1012
0
}
1013
1014
static void
1015
toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data)
1016
0
{
1017
0
  hoedown_html_renderer_state *state;
1018
1019
0
  if (inline_render)
1020
0
    return;
1021
1022
0
  state = data->opaque;
1023
1024
0
  while (state->toc_data.current_level > 0) {
1025
0
    HOEDOWN_BUFPUTSL(ob, "</li>\n</ul>\n");
1026
0
    state->toc_data.current_level--;
1027
0
    if (state->toc_data.current_level < state->toc_data.level_offset) {
1028
0
      break;
1029
0
    }
1030
0
  }
1031
1032
0
  if (state->toc_data.footer) {
1033
0
    hoedown_buffer_printf(ob, "%s\n", state->toc_data.footer);
1034
0
  }
1035
1036
0
  state->toc_data.header_count = 0;
1037
0
}
1038
1039
hoedown_renderer *
1040
hoedown_html_toc_renderer_new(int nesting_level)
1041
0
{
1042
0
  static const hoedown_renderer cb_default = {
1043
0
    NULL,
1044
1045
0
    NULL,
1046
0
    NULL,
1047
0
    toc_header,
1048
0
    NULL,
1049
0
    NULL,
1050
0
    NULL,
1051
0
    NULL,
1052
0
    NULL,
1053
0
    NULL,
1054
0
    NULL,
1055
0
    NULL,
1056
0
    NULL,
1057
0
    NULL,
1058
0
    NULL,
1059
0
    NULL,
1060
1061
0
    NULL,
1062
0
    rndr_codespan,
1063
0
    rndr_double_emphasis,
1064
0
    rndr_emphasis,
1065
0
    rndr_underline,
1066
0
    rndr_highlight,
1067
0
    rndr_quote,
1068
0
    NULL,
1069
0
    NULL,
1070
0
    toc_link,
1071
0
    rndr_triple_emphasis,
1072
0
    rndr_strikethrough,
1073
0
    rndr_superscript,
1074
0
    NULL,
1075
0
    NULL,
1076
0
    NULL,
1077
1078
0
    NULL,
1079
0
    rndr_normal_text,
1080
1081
0
    toc_initialize,
1082
0
    toc_finalize,
1083
1084
0
    NULL,
1085
1086
0
    NULL,
1087
0
    NULL,
1088
0
  };
1089
1090
0
  hoedown_html_renderer_state *state;
1091
0
  hoedown_renderer *renderer;
1092
1093
  /* Prepare the state pointer */
1094
0
  state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
1095
0
  memset(state, 0x0, sizeof(hoedown_html_renderer_state));
1096
1097
0
  state->toc_data.nesting_level = nesting_level;
1098
1099
  /* Prepare the renderer */
1100
0
  renderer = hoedown_malloc(sizeof(hoedown_renderer));
1101
0
  memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
1102
1103
0
  state->hash.header_id = hoedown_hash_new(0);
1104
1105
0
  renderer->opaque = state;
1106
0
  return renderer;
1107
0
}
1108
1109
hoedown_renderer *
1110
hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level)
1111
12.0k
{
1112
12.0k
  static const hoedown_renderer cb_default = {
1113
12.0k
    NULL,
1114
1115
12.0k
    rndr_blockcode,
1116
12.0k
    rndr_blockquote,
1117
12.0k
    rndr_header,
1118
12.0k
    rndr_hrule,
1119
12.0k
    rndr_list,
1120
12.0k
    rndr_listitem,
1121
12.0k
    rndr_paragraph,
1122
12.0k
    rndr_table,
1123
12.0k
    rndr_table_header,
1124
12.0k
    rndr_table_body,
1125
12.0k
    rndr_tablerow,
1126
12.0k
    rndr_tablecell,
1127
12.0k
    rndr_footnotes,
1128
12.0k
    rndr_footnote_def,
1129
12.0k
    rndr_raw_block,
1130
1131
12.0k
    rndr_autolink,
1132
12.0k
    rndr_codespan,
1133
12.0k
    rndr_double_emphasis,
1134
12.0k
    rndr_emphasis,
1135
12.0k
    rndr_underline,
1136
12.0k
    rndr_highlight,
1137
12.0k
    rndr_quote,
1138
12.0k
    rndr_image,
1139
12.0k
    rndr_linebreak,
1140
12.0k
    rndr_link,
1141
12.0k
    rndr_triple_emphasis,
1142
12.0k
    rndr_strikethrough,
1143
12.0k
    rndr_superscript,
1144
12.0k
    rndr_footnote_ref,
1145
12.0k
    rndr_math,
1146
12.0k
    rndr_raw_html,
1147
1148
12.0k
    NULL,
1149
12.0k
    rndr_normal_text,
1150
1151
12.0k
    NULL,
1152
12.0k
    NULL,
1153
1154
12.0k
    NULL,
1155
1156
12.0k
    NULL,
1157
12.0k
    NULL,
1158
12.0k
  };
1159
1160
12.0k
  hoedown_html_renderer_state *state;
1161
12.0k
  hoedown_renderer *renderer;
1162
1163
  /* Prepare the state pointer */
1164
12.0k
  state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
1165
12.0k
  memset(state, 0x0, sizeof(hoedown_html_renderer_state));
1166
1167
12.0k
  state->flags = render_flags;
1168
12.0k
  state->toc_data.nesting_level = nesting_level;
1169
1170
  /* Prepare the renderer */
1171
12.0k
  renderer = hoedown_malloc(sizeof(hoedown_renderer));
1172
12.0k
  memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
1173
1174
12.0k
  if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE)
1175
12.0k
    renderer->blockhtml = NULL;
1176
1177
12.0k
  state->hash.header_id = hoedown_hash_new(0);
1178
1179
12.0k
  renderer->opaque = state;
1180
1181
12.0k
  return renderer;
1182
12.0k
}
1183
1184
void
1185
hoedown_html_renderer_free(hoedown_renderer *renderer)
1186
12.0k
{
1187
12.0k
  if (renderer->opaque) {
1188
12.0k
    hoedown_html_renderer_state *state = renderer->opaque;
1189
12.0k
    if (state->hash.header_id) {
1190
12.0k
      hoedown_hash_free(state->hash.header_id);
1191
12.0k
    }
1192
12.0k
  }
1193
12.0k
  free(renderer->opaque);
1194
12.0k
  free(renderer);
1195
12.0k
}