/src/cmark/src/references.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "cmark.h" |
2 | | #include "utf8.h" |
3 | | #include "parser.h" |
4 | | #include "references.h" |
5 | | #include "inlines.h" |
6 | | #include "chunk.h" |
7 | | |
8 | 12.5k | static void reference_free(cmark_reference_map *map, cmark_reference *ref) { |
9 | 12.5k | cmark_mem *mem = map->mem; |
10 | 12.5k | if (ref != NULL) { |
11 | 12.5k | mem->free(ref->label); |
12 | 12.5k | mem->free(ref->url); |
13 | 12.5k | mem->free(ref->title); |
14 | 12.5k | mem->free(ref); |
15 | 12.5k | } |
16 | 12.5k | } |
17 | | |
18 | | // normalize reference: collapse internal whitespace to single space, |
19 | | // remove leading/trailing whitespace, case fold |
20 | | // Return NULL if the reference name is actually empty (i.e. composed |
21 | | // solely from whitespace) |
22 | 78.0k | static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) { |
23 | 78.0k | cmark_strbuf normalized = CMARK_BUF_INIT(mem); |
24 | 78.0k | unsigned char *result; |
25 | | |
26 | 78.0k | if (ref == NULL) |
27 | 0 | return NULL; |
28 | | |
29 | 78.0k | if (ref->len == 0) |
30 | 0 | return NULL; |
31 | | |
32 | 78.0k | cmark_utf8proc_case_fold(&normalized, ref->data, ref->len); |
33 | 78.0k | cmark_strbuf_trim(&normalized); |
34 | 78.0k | cmark_strbuf_normalize_whitespace(&normalized); |
35 | | |
36 | 78.0k | result = cmark_strbuf_detach(&normalized); |
37 | 78.0k | assert(result); |
38 | | |
39 | 78.0k | if (result[0] == '\0') { |
40 | 1.79k | mem->free(result); |
41 | 1.79k | return NULL; |
42 | 1.79k | } |
43 | | |
44 | 76.2k | return result; |
45 | 78.0k | } |
46 | | |
47 | | void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label, |
48 | 12.5k | cmark_chunk *url, cmark_chunk *title) { |
49 | 12.5k | cmark_reference *ref; |
50 | 12.5k | unsigned char *reflabel = normalize_reference(map->mem, label); |
51 | | |
52 | | /* empty reference name, or composed from only whitespace */ |
53 | 12.5k | if (reflabel == NULL) |
54 | 0 | return; |
55 | | |
56 | 12.5k | assert(map->sorted == NULL); |
57 | | |
58 | 12.5k | ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref)); |
59 | 12.5k | ref->label = reflabel; |
60 | 12.5k | ref->url = cmark_clean_url(map->mem, url); |
61 | 12.5k | ref->title = cmark_clean_title(map->mem, title); |
62 | 12.5k | ref->age = map->size; |
63 | 12.5k | ref->next = map->refs; |
64 | | |
65 | 12.5k | if (ref->url != NULL) |
66 | 12.5k | ref->size += (int)strlen((char*)ref->url); |
67 | 12.5k | if (ref->title != NULL) |
68 | 5.05k | ref->size += (int)strlen((char*)ref->title); |
69 | | |
70 | 12.5k | map->refs = ref; |
71 | 12.5k | map->size++; |
72 | 12.5k | } |
73 | | |
74 | | static int |
75 | 126k | labelcmp(const unsigned char *a, const unsigned char *b) { |
76 | 126k | return strcmp((const char *)a, (const char *)b); |
77 | 126k | } |
78 | | |
79 | | static int |
80 | 45.1k | refcmp(const void *p1, const void *p2) { |
81 | 45.1k | cmark_reference *r1 = *(cmark_reference **)p1; |
82 | 45.1k | cmark_reference *r2 = *(cmark_reference **)p2; |
83 | 45.1k | int res = labelcmp(r1->label, r2->label); |
84 | 45.1k | return res ? res : ((int)r1->age - (int)r2->age); |
85 | 45.1k | } |
86 | | |
87 | | static int |
88 | 73.1k | refsearch(const void *label, const void *p2) { |
89 | 73.1k | cmark_reference *ref = *(cmark_reference **)p2; |
90 | 73.1k | return labelcmp((const unsigned char *)label, ref->label); |
91 | 73.1k | } |
92 | | |
93 | 2.85k | static void sort_references(cmark_reference_map *map) { |
94 | 2.85k | unsigned int i = 0, last = 0, size = map->size; |
95 | 2.85k | cmark_reference *r = map->refs, **sorted = NULL; |
96 | | |
97 | 2.85k | sorted = (cmark_reference **)map->mem->calloc(size, sizeof(cmark_reference *)); |
98 | 14.2k | while (r) { |
99 | 11.3k | sorted[i++] = r; |
100 | 11.3k | r = r->next; |
101 | 11.3k | } |
102 | | |
103 | 2.85k | qsort(sorted, size, sizeof(cmark_reference *), refcmp); |
104 | | |
105 | 11.3k | for (i = 1; i < size; i++) { |
106 | 8.53k | if (labelcmp(sorted[i]->label, sorted[last]->label) != 0) |
107 | 539 | sorted[++last] = sorted[i]; |
108 | 8.53k | } |
109 | 2.85k | map->sorted = sorted; |
110 | 2.85k | map->size = last + 1; |
111 | 2.85k | } |
112 | | |
113 | | // Returns reference if refmap contains a reference with matching |
114 | | // label, otherwise NULL. |
115 | | cmark_reference *cmark_reference_lookup(cmark_reference_map *map, |
116 | 107k | cmark_chunk *label) { |
117 | 107k | cmark_reference **ref = NULL; |
118 | 107k | cmark_reference *r = NULL; |
119 | 107k | unsigned char *norm; |
120 | | |
121 | 107k | if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH) |
122 | 27.9k | return NULL; |
123 | | |
124 | 79.6k | if (map == NULL || !map->size) |
125 | 14.1k | return NULL; |
126 | | |
127 | 65.5k | norm = normalize_reference(map->mem, label); |
128 | 65.5k | if (norm == NULL) |
129 | 1.79k | return NULL; |
130 | | |
131 | 63.7k | if (!map->sorted) |
132 | 2.85k | sort_references(map); |
133 | | |
134 | 63.7k | ref = (cmark_reference **)bsearch(norm, map->sorted, map->size, sizeof(cmark_reference *), |
135 | 63.7k | refsearch); |
136 | 63.7k | map->mem->free(norm); |
137 | | |
138 | 63.7k | if (ref != NULL) { |
139 | 55.0k | r = ref[0]; |
140 | | /* Check for expansion limit */ |
141 | 55.0k | if (map->max_ref_size && r->size > map->max_ref_size - map->ref_size) |
142 | 1.00k | return NULL; |
143 | 54.0k | map->ref_size += r->size; |
144 | 54.0k | } |
145 | | |
146 | 62.7k | return r; |
147 | 63.7k | } |
148 | | |
149 | 33.9k | void cmark_reference_map_free(cmark_reference_map *map) { |
150 | 33.9k | cmark_reference *ref; |
151 | | |
152 | 33.9k | if (map == NULL) |
153 | 0 | return; |
154 | | |
155 | 33.9k | ref = map->refs; |
156 | 46.4k | while (ref) { |
157 | 12.5k | cmark_reference *next = ref->next; |
158 | 12.5k | reference_free(map, ref); |
159 | 12.5k | ref = next; |
160 | 12.5k | } |
161 | | |
162 | 33.9k | map->mem->free(map->sorted); |
163 | 33.9k | map->mem->free(map); |
164 | 33.9k | } |
165 | | |
166 | 33.9k | cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) { |
167 | 33.9k | cmark_reference_map *map = |
168 | 33.9k | (cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map)); |
169 | 33.9k | map->mem = mem; |
170 | 33.9k | return map; |
171 | 33.9k | } |