Coverage Report

Created: 2024-02-25 06:38

/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
384k
#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML)
16
485k
#define USE_TASK_LIST(opt) (opt->flags & HOEDOWN_HTML_USE_TASK_LIST)
17
485k
#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
7.04M
{
58
7.04M
  hoedown_escape_html(ob, source, length, 0);
59
7.04M
}
60
61
static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length)
62
262k
{
63
262k
  hoedown_escape_href(ob, source, length);
64
262k
}
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
138k
{
72
  /* i keeps track of how much we've parsed so far. */
73
138k
  size_t i = 0;
74
138k
  int rendered_id = 0;
75
138k
  int must_free_class = 0;
76
77
259k
  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
187k
    int is_id = 0;
87
187k
    int is_class = 0;
88
187k
    size_t key_start = 0, key_end = 0, val_start = 0, val_end = 0;
89
90
    /* Skip to the first non-space character */
91
8.26M
    for (; i < size && buf[i] == ' '; ++i) {}
92
187k
    if (i >= size) break;
93
94
146k
    if (buf[i] == '#') {
95
      /* #id */
96
64.8k
      is_id = 1;
97
64.8k
      ++i;
98
82.0k
    } else if (buf[i] == '.') {
99
      /* .cls */
100
1.99k
      is_class = 1;
101
1.99k
      ++i;
102
1.99k
    }
103
146k
    if (is_id || is_class) {
104
66.8k
      val_start = i;
105
7.70M
      for (val_end = val_start; val_end < size && buf[val_end] != ' '; ++val_end) {}
106
66.8k
      i = val_end;
107
80.0k
    } else {
108
      /* key=... */
109
80.0k
      key_start = i;
110
6.62M
      for (key_end = key_start; key_end < size && buf[key_end] != ' ' && buf[key_end] != '='; ++key_end) {}
111
80.0k
      i = key_end;
112
80.0k
      if (i >= size) break;
113
55.5k
      ++i;
114
55.5k
      if (buf[key_end] != '=') continue;
115
44.1k
      if (strncasecmp((char *)buf + key_start, "id", key_end - key_start) == 0) {
116
2.62k
        is_id = 1;
117
41.5k
      } else if (strncasecmp((char *)buf + key_start, "class", key_end - key_start) == 0) {
118
37.1k
        is_class = 1;
119
37.1k
      }
120
121
44.1k
      val_start = i;
122
44.1k
      if (val_start < size && (buf[val_start] == '"' || buf[val_start] == '\'')) {
123
        /* key="quoted val" */
124
4.98k
        val_start += 1;
125
2.67M
        for (val_end = val_start; val_end < size && buf[val_end] != buf[val_start - 1]; ++val_end) {}
126
4.98k
        i = val_end;
127
4.98k
        if (i >= size) break;
128
3.65k
        ++i;
129
39.2k
      } else {
130
        /* key=val */
131
149k
        for (val_end = val_start; val_end < size && buf[val_end] != ' '; ++val_end) {}
132
39.2k
        i = val_end;
133
39.2k
      }
134
44.1k
    }
135
136
    /* Now that we found our keys and values, let's render the attribute. */
137
109k
    if (is_id) {
138
66.1k
      if (rendered_id) continue;
139
65.8k
      if (val_end == val_start) continue;
140
62.4k
      rendered_id = 1;
141
62.4k
      HOEDOWN_BUFPUTSL(ob, " id=\"");
142
62.4k
      escape_html(ob, buf + val_start, val_end - val_start);
143
62.4k
      hoedown_buffer_putc(ob, '"');
144
62.4k
    } else if (is_class) {
145
39.1k
      if (val_end == val_start) continue;
146
1.62k
      if (!class) {
147
1.32k
        class = hoedown_buffer_new(size);
148
1.32k
        must_free_class = 1;
149
1.32k
      }
150
1.62k
      escape_html(class, buf + val_start, val_end - val_start);
151
1.62k
      hoedown_buffer_putc(class, ' ');
152
4.44k
    } else {
153
4.44k
      if (key_end == key_start) continue;
154
4.44k
      hoedown_buffer_putc(ob, ' ');
155
4.44k
      escape_html(ob, buf + key_start, key_end - key_start);
156
4.44k
      HOEDOWN_BUFPUTSL(ob, "=\"");
157
4.44k
      escape_html(ob, buf + val_start, val_end - val_start);
158
4.44k
      hoedown_buffer_putc(ob, '"');
159
4.44k
    }
160
109k
  }
161
162
138k
  if (class) {
163
1.61k
    if (class->size > 0) {
164
1.61k
      HOEDOWN_BUFPUTSL(ob, " class=\"");
165
1.61k
      hoedown_buffer_put(ob, class->data, class->size-1);
166
1.61k
      hoedown_buffer_putc(ob, '"');
167
1.61k
    }
168
1.61k
    if (must_free_class) {
169
1.32k
      hoedown_buffer_free(class);
170
1.32k
    }
171
1.61k
  }
172
138k
  return 1;
173
138k
}
174
175
static int
176
rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data)
177
4.34k
{
178
4.34k
  hoedown_html_renderer_state *state = data->opaque;
179
180
4.34k
  if (!link || !link->size)
181
0
    return 0;
182
183
4.34k
  HOEDOWN_BUFPUTSL(ob, "<a href=\"");
184
4.34k
  if (type == HOEDOWN_AUTOLINK_EMAIL)
185
1.21k
    HOEDOWN_BUFPUTSL(ob, "mailto:");
186
4.34k
  escape_href(ob, link->data, link->size);
187
188
4.34k
  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
4.34k
  } else {
193
4.34k
    HOEDOWN_BUFPUTSL(ob, "\">");
194
4.34k
  }
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
4.34k
  if (hoedown_buffer_prefix(link, "mailto:") == 0) {
202
76
    escape_html(ob, link->data + 7, link->size - 7);
203
4.26k
  } else {
204
4.26k
    escape_html(ob, link->data, link->size);
205
4.26k
  }
206
207
4.34k
  HOEDOWN_BUFPUTSL(ob, "</a>");
208
209
4.34k
  return 1;
210
4.34k
}
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
3.73k
{
215
3.73k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
216
217
3.73k
  if (lang) {
218
1.95k
    hoedown_html_renderer_state *state = data->opaque;
219
1.95k
    if ((state->flags & HOEDOWN_HTML_FENCED_CODE_SCRIPT) &&
220
1.95k
        lang->size > 7 && memcmp(lang->data, "script@", 7) == 0 && text) {
221
392
      HOEDOWN_BUFPUTSL(ob, "<script type=\"");
222
392
      escape_html(ob, lang->data + 7, lang->size - 7);
223
392
      HOEDOWN_BUFPUTSL(ob, "\">\n");
224
392
      hoedown_buffer_put(ob, text->data, text->size);
225
392
      HOEDOWN_BUFPUTSL(ob, "</script>\n");
226
392
      return;
227
392
    }
228
1.56k
    HOEDOWN_BUFPUTSL(ob, "<pre><code");
229
1.56k
    if (attr && attr->size) {
230
283
      hoedown_buffer *lang_class = hoedown_buffer_new(lang->size + 9);
231
283
      if (lang->size) {
232
283
        HOEDOWN_BUFPUTSL(lang_class, "language-");
233
283
        escape_html(lang_class, lang->data, lang->size);
234
283
        if (lang_class->data[lang_class->size-1] != ' ') {
235
283
          hoedown_buffer_putc(lang_class, ' ');
236
283
        }
237
283
      }
238
283
      rndr_attributes(ob, attr->data, attr->size, lang_class, data);
239
283
      hoedown_buffer_free(lang_class);
240
1.27k
    } else {
241
1.27k
      HOEDOWN_BUFPUTSL(ob, " class=\"language-");
242
1.27k
      escape_html(ob, lang->data, lang->size);
243
1.27k
      hoedown_buffer_putc(ob, '"');
244
1.27k
    }
245
1.56k
    hoedown_buffer_putc(ob, '>');
246
1.77k
  } else if (attr && attr->size) {
247
224
    HOEDOWN_BUFPUTSL(ob, "<pre><code");
248
224
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
249
224
    hoedown_buffer_putc(ob, '>');
250
1.55k
  } else {
251
1.55k
    HOEDOWN_BUFPUTSL(ob, "<pre><code>");
252
1.55k
  }
253
254
3.33k
  if (text)
255
1.69k
    escape_html(ob, text->data, text->size);
256
257
3.33k
  HOEDOWN_BUFPUTSL(ob, "</code></pre>\n");
258
3.33k
}
259
260
static void
261
rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
262
25.2k
{
263
25.2k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
264
25.2k
  HOEDOWN_BUFPUTSL(ob, "<blockquote>\n");
265
25.2k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
266
25.2k
  HOEDOWN_BUFPUTSL(ob, "</blockquote>\n");
267
25.2k
}
268
269
static int
270
rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *attr, const hoedown_renderer_data *data)
271
26.4k
{
272
26.4k
  HOEDOWN_BUFPUTSL(ob, "<code");
273
26.4k
  if (attr && attr->size) {
274
120
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
275
120
  }
276
26.4k
  hoedown_buffer_putc(ob, '>');
277
26.4k
  if (text) escape_html(ob, text->data, text->size);
278
26.4k
  HOEDOWN_BUFPUTSL(ob, "</code>");
279
26.4k
  return 1;
280
26.4k
}
281
282
static int
283
rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
284
3.56k
{
285
3.56k
  if (!content || !content->size)
286
2.32k
    return 0;
287
288
1.23k
  HOEDOWN_BUFPUTSL(ob, "<del>");
289
1.23k
  hoedown_buffer_put(ob, content->data, content->size);
290
1.23k
  HOEDOWN_BUFPUTSL(ob, "</del>");
291
1.23k
  return 1;
292
3.56k
}
293
294
static int
295
rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
296
10.8k
{
297
10.8k
  if (!content || !content->size)
298
4.98k
    return 0;
299
300
5.85k
  HOEDOWN_BUFPUTSL(ob, "<strong>");
301
5.85k
  hoedown_buffer_put(ob, content->data, content->size);
302
5.85k
  HOEDOWN_BUFPUTSL(ob, "</strong>");
303
304
5.85k
  return 1;
305
10.8k
}
306
307
static int
308
rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
309
2.97k
{
310
2.97k
  if (!content || !content->size) return 0;
311
2.84k
  HOEDOWN_BUFPUTSL(ob, "<em>");
312
2.84k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
313
2.84k
  HOEDOWN_BUFPUTSL(ob, "</em>");
314
2.84k
  return 1;
315
2.97k
}
316
317
static int
318
rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
319
21.3k
{
320
21.3k
  if (!content || !content->size)
321
1.26k
    return 0;
322
323
20.1k
  HOEDOWN_BUFPUTSL(ob, "<u>");
324
20.1k
  hoedown_buffer_put(ob, content->data, content->size);
325
20.1k
  HOEDOWN_BUFPUTSL(ob, "</u>");
326
327
20.1k
  return 1;
328
21.3k
}
329
330
static int
331
rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
332
459
{
333
459
  if (!content || !content->size)
334
117
    return 0;
335
336
342
  HOEDOWN_BUFPUTSL(ob, "<mark>");
337
342
  hoedown_buffer_put(ob, content->data, content->size);
338
342
  HOEDOWN_BUFPUTSL(ob, "</mark>");
339
340
342
  return 1;
341
459
}
342
343
static int
344
rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
345
81.0k
{
346
81.0k
  if (!content || !content->size)
347
1.32k
    return 0;
348
349
79.6k
  HOEDOWN_BUFPUTSL(ob, "<q>");
350
79.6k
  hoedown_buffer_put(ob, content->data, content->size);
351
79.6k
  HOEDOWN_BUFPUTSL(ob, "</q>");
352
353
79.6k
  return 1;
354
81.0k
}
355
356
static int
357
rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data)
358
373k
{
359
373k
  hoedown_html_renderer_state *state = data->opaque;
360
373k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<br/>\n" : "<br>\n");
361
373k
  return 1;
362
373k
}
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.1k
{
367
64.1k
  size_t i = 0, n = 0;
368
64.1k
  hoedown_html_renderer_state *state = data->opaque;
369
64.1k
  hoedown_hash *hash = state->hash.header_id;
370
371
9.60M
  while (i < length) {
372
9.53M
    if (isalnum(source[i])) {
373
423k
      hoedown_buffer_putc(ob, tolower(source[i]));
374
9.11M
    } else if (source[i] == ' ') {
375
6.85M
      hoedown_buffer_putc(ob, '-');
376
6.85M
    } else if (source[i] == '-' || source[i] == '_') {
377
29.4k
      hoedown_buffer_putc(ob, source[i]);
378
2.22M
    } else if (!isascii(source[i])) {
379
168k
      if (escape) {
380
0
        hoedown_buffer_printf(ob, "%%%02X", source[i]);
381
168k
      } else {
382
168k
        hoedown_buffer_putc(ob, source[i]);
383
168k
      }
384
2.05M
    } else if (source[i] == '&') {
385
181k
      while (i < length && source[i] != ';') {
386
143k
        ++i;
387
143k
      }
388
2.02M
    } else if (source[i] == '<') {
389
181M
      while (i < length && source[i] != '>') {
390
181M
        ++i;
391
181M
      }
392
89.4k
    }
393
9.53M
    ++i;
394
9.53M
  }
395
396
64.1k
  if (hash) {
397
64.1k
    void *value = hoedown_hash_find(hash, (char *)source, length);
398
64.1k
    if (value) {
399
52.9k
      size_t *p = (size_t *)value;
400
52.9k
      ++(*p);
401
52.9k
      n = *p;
402
52.9k
    }
403
64.1k
    if (n > 0) {
404
52.9k
      hoedown_buffer_printf(ob, "-%ld", n);
405
52.9k
    } else if (hash) {
406
11.2k
      size_t *p = (size_t *)malloc(sizeof(size_t));
407
11.2k
      if (p) {
408
11.2k
        *p = 0;
409
11.2k
        hoedown_hash_add(hash, (char *)source, length, (void *)p, free);
410
11.2k
      }
411
11.2k
    }
412
64.1k
  }
413
64.1k
}
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.0k
{
418
66.0k
  hoedown_html_renderer_state *state = data->opaque;
419
420
66.0k
  if (ob->size)
421
62.0k
    hoedown_buffer_putc(ob, '\n');
422
423
66.0k
  hoedown_buffer *merged_attr = hoedown_buffer_new(sizeof(hoedown_buffer));
424
425
66.0k
  if (attr && attr->size) {
426
1.16k
    hoedown_buffer_put(merged_attr, attr->data, attr->size);
427
1.16k
  }
428
66.0k
  if (content && content->size && ((state->flags & HOEDOWN_HTML_HEADER_ID) || (level <= state->toc_data.nesting_level))) {
429
64.1k
    hoedown_buffer_puts(merged_attr, " #");
430
64.1k
    rndr_header_id(merged_attr, content->data, content->size, 0, data);
431
64.1k
  }
432
433
66.0k
  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.08k
    hoedown_buffer_printf(ob, "<h%d>", level);
439
1.08k
  }
440
441
66.0k
  hoedown_buffer_free(merged_attr);
442
443
66.0k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
444
66.0k
  hoedown_buffer_printf(ob, "</h%d>\n", level);
445
66.0k
}
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
258k
{
450
258k
  hoedown_html_renderer_state *state = data->opaque;
451
452
258k
  HOEDOWN_BUFPUTSL(ob, "<a href=\"");
453
454
258k
  if (link && link->size) {
455
252k
    escape_href(ob, link->data, link->size);
456
252k
  }
457
458
258k
  if (title && title->size) {
459
127k
    HOEDOWN_BUFPUTSL(ob, "\" title=\"");
460
127k
    escape_html(ob, title->data, title->size);
461
127k
  }
462
463
258k
  hoedown_buffer_putc(ob, '"');
464
258k
  if (state->link_attributes) {
465
0
    state->link_attributes(ob, link, data);
466
0
  }
467
258k
  if (attr && attr->size) {
468
52.6k
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
469
52.6k
  }
470
258k
  hoedown_buffer_putc(ob, '>');
471
472
258k
  if (content && content->size) hoedown_buffer_put(ob, content->data, content->size);
473
258k
  HOEDOWN_BUFPUTSL(ob, "</a>");
474
258k
  return 1;
475
258k
}
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
56.5k
{
504
56.5k
        hoedown_html_renderer_state *state = data->opaque;
505
506
56.5k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
507
508
56.5k
  if (flags & HOEDOWN_LIST_ORDERED) {
509
16.1k
    HOEDOWN_BUFPUTSL(ob, "<ol");
510
40.3k
  } else if (flags & HOEDOWN_LIST_DEFINITION) {
511
18.0k
    HOEDOWN_BUFPUTSL(ob, "<dl");
512
22.2k
  } else {
513
22.2k
    HOEDOWN_BUFPUTSL(ob, "<ul");
514
22.2k
  }
515
56.5k
  if (attr && attr->size) {
516
4.95k
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
517
4.95k
  }
518
519
56.5k
  HOEDOWN_BUFPUTSL(ob, ">\n");
520
521
56.5k
  if (content) {
522
56.5k
    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
56.5k
    } else {
529
56.5k
      hoedown_buffer_put(ob, content->data, content->size);
530
56.5k
    }
531
56.5k
        }
532
533
56.5k
  if (flags & HOEDOWN_LIST_ORDERED) {
534
16.1k
    HOEDOWN_BUFPUTSL(ob, "</ol>\n");
535
40.3k
  } else if (flags & HOEDOWN_LIST_DEFINITION) {
536
18.0k
    HOEDOWN_BUFPUTSL(ob, "</dl>\n");
537
22.2k
  } else {
538
22.2k
    HOEDOWN_BUFPUTSL(ob, "</ul>\n");
539
22.2k
  }
540
56.5k
}
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
242k
{
545
242k
  if (content) {
546
242k
    hoedown_html_renderer_state *state = data->opaque;
547
242k
    size_t prefix = 0;
548
242k
    size_t size = content->size;
549
242k
    int is_li_tag = 0;
550
282k
    while (size && content->data[size - 1] == '\n')
551
39.5k
      size--;
552
553
242k
    if (*flags & HOEDOWN_LI_DD) {
554
75.6k
      HOEDOWN_BUFPUTSL(ob, "<dd");
555
167k
    } else  if (*flags & HOEDOWN_LI_DT) {
556
93.9k
      HOEDOWN_BUFPUTSL(ob, "<dt");
557
93.9k
    } else {
558
73.0k
      HOEDOWN_BUFPUTSL(ob, "<li");
559
73.0k
      is_li_tag = 1;
560
73.0k
    }
561
562
242k
    if (attr && attr->size) {
563
9.92k
      rndr_attributes(ob, attr->data, attr->size, NULL, data);
564
9.92k
    }
565
242k
    hoedown_buffer_putc(ob, '>');
566
567
242k
    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
44.2k
      if (*flags & HOEDOWN_LI_BLOCK) {
571
20.3k
        prefix = 3;
572
20.3k
        hoedown_buffer_put(ob, content->data, prefix);
573
20.3k
      }
574
44.2k
      if (size >= prefix + 3) {
575
43.2k
        if (strncmp((char *)content->data + prefix, "[ ]", 3) == 0) {
576
279
          HOEDOWN_BUFPUTSL(ob, "<input type=\"checkbox\"");
577
279
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
578
279
          prefix += 3;
579
279
          *flags |= HOEDOWN_LI_TASK;
580
42.9k
        } else if (strncasecmp((char *)content->data + prefix, "[x]", 3) == 0) {
581
251
          HOEDOWN_BUFPUTSL(ob, "<input checked=\"\" type=\"checkbox\"");
582
251
          hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
583
251
          prefix += 3;
584
251
          *flags |= HOEDOWN_LI_TASK;
585
251
        }
586
43.2k
      }
587
44.2k
    }
588
242k
    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
242k
    hoedown_buffer_put(ob, content->data+prefix, size-prefix);
609
242k
  } 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
242k
  if (*flags & HOEDOWN_LI_DD) {
619
75.6k
    HOEDOWN_BUFPUTSL(ob, "</dd>\n");
620
167k
  } else if (*flags & HOEDOWN_LI_DT) {
621
93.9k
    HOEDOWN_BUFPUTSL(ob, "</dt>\n");
622
93.9k
  } else {
623
73.0k
    HOEDOWN_BUFPUTSL(ob, "</li>\n");
624
73.0k
  }
625
242k
}
626
627
static void
628
rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *attr, const hoedown_renderer_data *data)
629
226k
{
630
226k
  hoedown_html_renderer_state *state = data->opaque;
631
226k
  size_t i = 0;
632
633
226k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
634
635
226k
  if (!content || !content->size)
636
205
    return;
637
638
638k
  while (i < content->size && isspace(content->data[i])) i++;
639
640
226k
  if (i == content->size)
641
194
    return;
642
643
225k
  HOEDOWN_BUFPUTSL(ob, "<p");
644
645
225k
  if (attr && attr->size) {
646
0
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
647
0
  }
648
649
225k
  HOEDOWN_BUFPUTSL(ob, ">");
650
651
225k
  if (state->flags & HOEDOWN_HTML_HARD_WRAP) {
652
225k
    size_t org;
653
577k
    while (i < content->size) {
654
577k
      org = i;
655
770M
      while (i < content->size && content->data[i] != '\n')
656
769M
        i++;
657
658
577k
      if (i > org)
659
577k
        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
577k
      if (i >= content->size - 1)
666
225k
        break;
667
668
351k
      rndr_linebreak(ob, data);
669
351k
      i++;
670
351k
    }
671
225k
  } 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
225k
  HOEDOWN_BUFPUTSL(ob, "</p>\n");
700
225k
}
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
1.98k
{
732
1.98k
  if (!content || !content->size) return 0;
733
1.49k
  HOEDOWN_BUFPUTSL(ob, "<strong><em>");
734
1.49k
  hoedown_buffer_put(ob, content->data, content->size);
735
1.49k
  HOEDOWN_BUFPUTSL(ob, "</em></strong>");
736
1.49k
  return 1;
737
1.98k
}
738
739
static void
740
rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data)
741
3.75k
{
742
3.75k
  hoedown_html_renderer_state *state = data->opaque;
743
3.75k
  if (ob->size) hoedown_buffer_putc(ob, '\n');
744
3.75k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
745
3.75k
}
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
30.1k
{
750
30.1k
  hoedown_html_renderer_state *state = data->opaque;
751
30.1k
  if (!link || !link->size) return 0;
752
753
6.08k
  HOEDOWN_BUFPUTSL(ob, "<img src=\"");
754
6.08k
  escape_href(ob, link->data, link->size);
755
6.08k
  HOEDOWN_BUFPUTSL(ob, "\" alt=\"");
756
757
6.08k
  if (alt && alt->size)
758
435
    escape_html(ob, alt->data, alt->size);
759
760
6.08k
  if (title && title->size) {
761
2.01k
    HOEDOWN_BUFPUTSL(ob, "\" title=\"");
762
2.01k
    escape_html(ob, title->data, title->size); }
763
764
6.08k
  hoedown_buffer_putc(ob, '"');
765
6.08k
  if (attr && attr->size) {
766
1.41k
    rndr_attributes(ob, attr->data, attr->size, NULL, data);
767
1.41k
  }
768
769
6.08k
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "/>" : ">");
770
6.08k
  return 1;
771
30.1k
}
772
773
static int
774
rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data)
775
42.4k
{
776
42.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
42.4k
  if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) {
781
42.4k
    escape_html(ob, text->data, text->size);
782
42.4k
    return 1;
783
42.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
4.34k
{
795
4.34k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
796
4.34k
    HOEDOWN_BUFPUTSL(ob, "<table");
797
4.34k
    if (attr) rndr_attributes(ob, attr->data, attr->size, NULL, data);
798
4.34k
    HOEDOWN_BUFPUTSL(ob, ">\n");
799
4.34k
    hoedown_buffer_put(ob, content->data, content->size);
800
4.34k
    HOEDOWN_BUFPUTSL(ob, "</table>\n");
801
4.34k
}
802
803
static void
804
rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
805
4.34k
{
806
4.34k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
807
4.34k
    HOEDOWN_BUFPUTSL(ob, "<thead>\n");
808
4.34k
    hoedown_buffer_put(ob, content->data, content->size);
809
4.34k
    HOEDOWN_BUFPUTSL(ob, "</thead>\n");
810
4.34k
}
811
812
static void
813
rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
814
4.34k
{
815
4.34k
    if (ob->size) hoedown_buffer_putc(ob, '\n');
816
4.34k
    HOEDOWN_BUFPUTSL(ob, "<tbody>\n");
817
4.34k
    hoedown_buffer_put(ob, content->data, content->size);
818
4.34k
    HOEDOWN_BUFPUTSL(ob, "</tbody>\n");
819
4.34k
}
820
821
static void
822
rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
823
16.8k
{
824
16.8k
  HOEDOWN_BUFPUTSL(ob, "<tr>\n");
825
16.8k
  if (content) hoedown_buffer_put(ob, content->data, content->size);
826
16.8k
  HOEDOWN_BUFPUTSL(ob, "</tr>\n");
827
16.8k
}
828
829
static void
830
rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data)
831
23.3k
{
832
23.3k
  if (flags & HOEDOWN_TABLE_HEADER) {
833
6.49k
    HOEDOWN_BUFPUTSL(ob, "<th");
834
16.8k
  } else {
835
16.8k
    HOEDOWN_BUFPUTSL(ob, "<td");
836
16.8k
  }
837
838
23.3k
  switch (flags & HOEDOWN_TABLE_ALIGNMASK) {
839
5.83k
  case HOEDOWN_TABLE_ALIGN_CENTER:
840
5.83k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: center\">");
841
5.83k
    break;
842
843
2.55k
  case HOEDOWN_TABLE_ALIGN_LEFT:
844
2.55k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">");
845
2.55k
    break;
846
847
6.63k
  case HOEDOWN_TABLE_ALIGN_RIGHT:
848
6.63k
    HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">");
849
6.63k
    break;
850
851
8.36k
  default:
852
8.36k
    HOEDOWN_BUFPUTSL(ob, ">");
853
23.3k
  }
854
855
23.3k
  if (content)
856
23.3k
    hoedown_buffer_put(ob, content->data, content->size);
857
858
23.3k
  if (flags & HOEDOWN_TABLE_HEADER) {
859
6.49k
    HOEDOWN_BUFPUTSL(ob, "</th>\n");
860
16.8k
  } else {
861
16.8k
    HOEDOWN_BUFPUTSL(ob, "</td>\n");
862
16.8k
  }
863
23.3k
}
864
865
static int
866
rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
867
134k
{
868
134k
  if (!content || !content->size) return 0;
869
129k
  HOEDOWN_BUFPUTSL(ob, "<sup>");
870
129k
  hoedown_buffer_put(ob, content->data, content->size);
871
129k
  HOEDOWN_BUFPUTSL(ob, "</sup>");
872
129k
  return 1;
873
134k
}
874
875
static void
876
rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
877
6.45M
{
878
6.45M
  if (content)
879
6.45M
    escape_html(ob, content->data, content->size);
880
6.45M
}
881
882
static void
883
rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)
884
490
{
885
490
  hoedown_html_renderer_state *state = data->opaque;
886
887
490
  if (ob->size) hoedown_buffer_putc(ob, '\n');
888
490
  HOEDOWN_BUFPUTSL(ob, "<div class=\"footnotes\">\n");
889
490
  hoedown_buffer_puts(ob, USE_XHTML(state) ? "<hr/>\n" : "<hr>\n");
890
490
  HOEDOWN_BUFPUTSL(ob, "<ol>\n");
891
892
490
  if (content) hoedown_buffer_put(ob, content->data, content->size);
893
894
490
  HOEDOWN_BUFPUTSL(ob, "\n</ol>\n</div>\n");
895
490
}
896
897
static void
898
rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data)
899
717
{
900
717
  size_t i = 0;
901
717
  int pfound = 0;
902
903
  /* insert anchor at the end of first paragraph block */
904
717
  if (content) {
905
2.19M
    while ((i+3) < content->size) {
906
2.19M
      if (content->data[i++] != '<') continue;
907
5.73k
      if (content->data[i++] != '/') continue;
908
2.92k
      if (content->data[i++] != 'p' && content->data[i] != 'P') continue;
909
588
      if (content->data[i] != '>') continue;
910
349
      i -= 3;
911
349
      pfound = 1;
912
349
      break;
913
588
    }
914
717
  }
915
916
717
  hoedown_buffer_printf(ob, "\n<li id=\"fn%d\">\n", num);
917
717
  if (pfound) {
918
349
    hoedown_buffer_put(ob, content->data, i);
919
349
    hoedown_buffer_printf(ob, "&nbsp;<a href=\"#fnref%d\" rev=\"footnote\">&#8617;</a>", num);
920
349
    hoedown_buffer_put(ob, content->data + i, content->size - i);
921
368
  } else if (content) {
922
368
    hoedown_buffer_put(ob, content->data, content->size);
923
368
  }
924
717
  HOEDOWN_BUFPUTSL(ob, "</li>\n");
925
717
}
926
927
static int
928
rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data)
929
717
{
930
717
  hoedown_buffer_printf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\" rel=\"footnote\">%d</a></sup>", num, num, num);
931
717
  return 1;
932
717
}
933
934
static int
935
rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data)
936
321k
{
937
321k
  hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2);
938
321k
  escape_html(ob, text->data, text->size);
939
321k
  hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2);
940
321k
  return 1;
941
321k
}
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
9.60k
{
1112
9.60k
  static const hoedown_renderer cb_default = {
1113
9.60k
    NULL,
1114
1115
9.60k
    rndr_blockcode,
1116
9.60k
    rndr_blockquote,
1117
9.60k
    rndr_header,
1118
9.60k
    rndr_hrule,
1119
9.60k
    rndr_list,
1120
9.60k
    rndr_listitem,
1121
9.60k
    rndr_paragraph,
1122
9.60k
    rndr_table,
1123
9.60k
    rndr_table_header,
1124
9.60k
    rndr_table_body,
1125
9.60k
    rndr_tablerow,
1126
9.60k
    rndr_tablecell,
1127
9.60k
    rndr_footnotes,
1128
9.60k
    rndr_footnote_def,
1129
9.60k
    rndr_raw_block,
1130
1131
9.60k
    rndr_autolink,
1132
9.60k
    rndr_codespan,
1133
9.60k
    rndr_double_emphasis,
1134
9.60k
    rndr_emphasis,
1135
9.60k
    rndr_underline,
1136
9.60k
    rndr_highlight,
1137
9.60k
    rndr_quote,
1138
9.60k
    rndr_image,
1139
9.60k
    rndr_linebreak,
1140
9.60k
    rndr_link,
1141
9.60k
    rndr_triple_emphasis,
1142
9.60k
    rndr_strikethrough,
1143
9.60k
    rndr_superscript,
1144
9.60k
    rndr_footnote_ref,
1145
9.60k
    rndr_math,
1146
9.60k
    rndr_raw_html,
1147
1148
9.60k
    NULL,
1149
9.60k
    rndr_normal_text,
1150
1151
9.60k
    NULL,
1152
9.60k
    NULL,
1153
1154
9.60k
    NULL,
1155
1156
9.60k
    NULL,
1157
9.60k
    NULL,
1158
9.60k
  };
1159
1160
9.60k
  hoedown_html_renderer_state *state;
1161
9.60k
  hoedown_renderer *renderer;
1162
1163
  /* Prepare the state pointer */
1164
9.60k
  state = hoedown_malloc(sizeof(hoedown_html_renderer_state));
1165
9.60k
  memset(state, 0x0, sizeof(hoedown_html_renderer_state));
1166
1167
9.60k
  state->flags = render_flags;
1168
9.60k
  state->toc_data.nesting_level = nesting_level;
1169
1170
  /* Prepare the renderer */
1171
9.60k
  renderer = hoedown_malloc(sizeof(hoedown_renderer));
1172
9.60k
  memcpy(renderer, &cb_default, sizeof(hoedown_renderer));
1173
1174
9.60k
  if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE)
1175
9.60k
    renderer->blockhtml = NULL;
1176
1177
9.60k
  state->hash.header_id = hoedown_hash_new(0);
1178
1179
9.60k
  renderer->opaque = state;
1180
1181
9.60k
  return renderer;
1182
9.60k
}
1183
1184
void
1185
hoedown_html_renderer_free(hoedown_renderer *renderer)
1186
9.60k
{
1187
9.60k
  if (renderer->opaque) {
1188
9.60k
    hoedown_html_renderer_state *state = renderer->opaque;
1189
9.60k
    if (state->hash.header_id) {
1190
9.60k
      hoedown_hash_free(state->hash.header_id);
1191
9.60k
    }
1192
9.60k
  }
1193
9.60k
  free(renderer->opaque);
1194
9.60k
  free(renderer);
1195
9.60k
}