cmark

My personal build of CMark ✏️

references.c (4384B)

  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 static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
  9   cmark_mem *mem = map->mem;
 10   if (ref != NULL) {
 11     mem->free(ref->label);
 12     mem->free(ref->url);
 13     mem->free(ref->title);
 14     mem->free(ref);
 15   }
 16 }
 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 static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) {
 23   cmark_strbuf normalized = CMARK_BUF_INIT(mem);
 24   unsigned char *result;
 25 
 26   if (ref == NULL)
 27     return NULL;
 28 
 29   if (ref->len == 0)
 30     return NULL;
 31 
 32   cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
 33   cmark_strbuf_trim(&normalized);
 34   cmark_strbuf_normalize_whitespace(&normalized);
 35 
 36   result = cmark_strbuf_detach(&normalized);
 37   assert(result);
 38 
 39   if (result[0] == '\0') {
 40     mem->free(result);
 41     return NULL;
 42   }
 43 
 44   return result;
 45 }
 46 
 47 void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
 48                             cmark_chunk *url, cmark_chunk *title) {
 49   cmark_reference *ref;
 50   unsigned char *reflabel = normalize_reference(map->mem, label);
 51 
 52   /* empty reference name, or composed from only whitespace */
 53   if (reflabel == NULL)
 54     return;
 55 
 56   assert(map->sorted == NULL);
 57 
 58   ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
 59   ref->label = reflabel;
 60   ref->url = cmark_clean_url(map->mem, url);
 61   ref->title = cmark_clean_title(map->mem, title);
 62   ref->age = map->size;
 63   ref->next = map->refs;
 64 
 65   if (ref->url != NULL)
 66     ref->size += strlen((char*)ref->url);
 67   if (ref->title != NULL)
 68     ref->size += strlen((char*)ref->title);
 69 
 70   map->refs = ref;
 71   map->size++;
 72 }
 73 
 74 static int
 75 labelcmp(const unsigned char *a, const unsigned char *b) {
 76   return strcmp((const char *)a, (const char *)b);
 77 }
 78 
 79 static int
 80 refcmp(const void *p1, const void *p2) {
 81   cmark_reference *r1 = *(cmark_reference **)p1;
 82   cmark_reference *r2 = *(cmark_reference **)p2;
 83   int res = labelcmp(r1->label, r2->label);
 84   return res ? res : ((int)r1->age - (int)r2->age);
 85 }
 86 
 87 static int
 88 refsearch(const void *label, const void *p2) {
 89   cmark_reference *ref = *(cmark_reference **)p2;
 90   return labelcmp((const unsigned char *)label, ref->label);
 91 }
 92 
 93 static void sort_references(cmark_reference_map *map) {
 94   unsigned int i = 0, last = 0, size = map->size;
 95   cmark_reference *r = map->refs, **sorted = NULL;
 96 
 97   sorted = (cmark_reference **)map->mem->calloc(size, sizeof(cmark_reference *));
 98   while (r) {
 99     sorted[i++] = r;
100     r = r->next;
101   }
102 
103   qsort(sorted, size, sizeof(cmark_reference *), refcmp);
104 
105   for (i = 1; i < size; i++) {
106     if (labelcmp(sorted[i]->label, sorted[last]->label) != 0)
107       sorted[++last] = sorted[i];
108   }
109   map->sorted = sorted;
110   map->size = last + 1;
111 }
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                                         cmark_chunk *label) {
117   cmark_reference **ref = NULL;
118   cmark_reference *r = NULL;
119   unsigned char *norm;
120 
121   if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
122     return NULL;
123 
124   if (map == NULL || !map->size)
125     return NULL;
126 
127   norm = normalize_reference(map->mem, label);
128   if (norm == NULL)
129     return NULL;
130 
131   if (!map->sorted)
132     sort_references(map);
133 
134   ref = (cmark_reference **)bsearch(norm, map->sorted, map->size, sizeof(cmark_reference *),
135                 refsearch);
136   map->mem->free(norm);
137 
138   if (ref != NULL) {
139     r = ref[0];
140     /* Check for expansion limit */
141     if (map->max_ref_size && r->size > map->max_ref_size - map->ref_size)
142       return NULL;
143     map->ref_size += r->size;
144   }
145 
146   return r;
147 }
148 
149 void cmark_reference_map_free(cmark_reference_map *map) {
150   cmark_reference *ref;
151 
152   if (map == NULL)
153     return;
154 
155   ref = map->refs;
156   while (ref) {
157     cmark_reference *next = ref->next;
158     reference_free(map, ref);
159     ref = next;
160   }
161 
162   map->mem->free(map->sorted);
163   map->mem->free(map);
164 }
165 
166 cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) {
167   cmark_reference_map *map =
168       (cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map));
169   map->mem = mem;
170   return map;
171 }