File: footnotes.c

package info (click to toggle)
ruby-commonmarker 0.23.10-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,456 kB
  • sloc: ansic: 10,575; ruby: 1,741; sh: 36; makefile: 22
file content (63 lines) | stat: -rw-r--r-- 1,784 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "cmark-gfm.h"
#include "parser.h"
#include "footnotes.h"
#include "inlines.h"
#include "chunk.h"

static void footnote_free(cmark_map *map, cmark_map_entry *_ref) {
  cmark_footnote *ref = (cmark_footnote *)_ref;
  cmark_mem *mem = map->mem;
  if (ref != NULL) {
    mem->free(ref->entry.label);
    if (ref->node)
      cmark_node_free(ref->node);
    mem->free(ref);
  }
}

void cmark_footnote_create(cmark_map *map, cmark_node *node) {
  cmark_footnote *ref;
  unsigned char *reflabel = normalize_map_label(map->mem, &node->as.literal);

  /* empty footnote name, or composed from only whitespace */
  if (reflabel == NULL)
    return;

  assert(map->sorted == NULL);

  ref = (cmark_footnote *)map->mem->calloc(1, sizeof(*ref));
  ref->entry.label = reflabel;
  ref->node = node;
  ref->entry.age = map->size;
  ref->entry.next = map->refs;

  map->refs = (cmark_map_entry *)ref;
  map->size++;
}

cmark_map *cmark_footnote_map_new(cmark_mem *mem) {
  return cmark_map_new(mem, footnote_free);
}

// Before calling `cmark_map_free` on a map with `cmark_footnotes`, first
// unlink all of the footnote nodes before freeing their memory.
//
// Sometimes, two (unused) footnote nodes can end up referencing each other,
// which as they get freed up by calling `cmark_map_free` -> `footnote_free` ->
// etc, can lead to a use-after-free error.
//
// Better to `unlink` every footnote node first, setting their next, prev, and
// parent pointers to NULL, and only then walk thru & free them up.
void cmark_unlink_footnotes_map(cmark_map *map) {
  cmark_map_entry *ref;
  cmark_map_entry *next;

  ref = map->refs;
  while(ref) {
    next = ref->next;
    if (((cmark_footnote *)ref)->node) {
      cmark_node_unlink(((cmark_footnote *)ref)->node);
    }
    ref = next;
  }
}