123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- #include "cmark.h"
- #include "utf8.h"
- #include "parser.h"
- #include "references.h"
- #include "inlines.h"
- #include "chunk.h"
- static unsigned int refhash(const unsigned char *link_ref) {
- unsigned int hash = 0;
- while (*link_ref)
- hash = (*link_ref++) + (hash << 6) + (hash << 16) - hash;
- return hash;
- }
- static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
- cmark_mem *mem = map->mem;
- if (ref != NULL) {
- mem->free(ref->label);
- cmark_chunk_free(mem, &ref->url);
- cmark_chunk_free(mem, &ref->title);
- mem->free(ref);
- }
- }
- // normalize reference: collapse internal whitespace to single space,
- // remove leading/trailing whitespace, case fold
- // Return NULL if the reference name is actually empty (i.e. composed
- // solely from whitespace)
- static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) {
- cmark_strbuf normalized = CMARK_BUF_INIT(mem);
- unsigned char *result;
- if (ref == NULL)
- return NULL;
- if (ref->len == 0)
- return NULL;
- cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
- cmark_strbuf_trim(&normalized);
- cmark_strbuf_normalize_whitespace(&normalized);
- result = cmark_strbuf_detach(&normalized);
- assert(result);
- if (result[0] == '\0') {
- mem->free(result);
- return NULL;
- }
- return result;
- }
- static void add_reference(cmark_reference_map *map, cmark_reference *ref) {
- cmark_reference *t = ref->next = map->table[ref->hash % REFMAP_SIZE];
- while (t) {
- if (t->hash == ref->hash && !strcmp((char *)t->label, (char *)ref->label)) {
- reference_free(map, ref);
- return;
- }
- t = t->next;
- }
- map->table[ref->hash % REFMAP_SIZE] = ref;
- }
- void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
- cmark_chunk *url, cmark_chunk *title) {
- cmark_reference *ref;
- unsigned char *reflabel = normalize_reference(map->mem, label);
- /* empty reference name, or composed from only whitespace */
- if (reflabel == NULL)
- return;
- ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
- ref->label = reflabel;
- ref->hash = refhash(ref->label);
- ref->url = cmark_clean_url(map->mem, url);
- ref->title = cmark_clean_title(map->mem, title);
- ref->next = NULL;
- add_reference(map, ref);
- }
- // Returns reference if refmap contains a reference with matching
- // label, otherwise NULL.
- cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
- cmark_chunk *label) {
- cmark_reference *ref = NULL;
- unsigned char *norm;
- unsigned int hash;
- if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
- return NULL;
- if (map == NULL)
- return NULL;
- norm = normalize_reference(map->mem, label);
- if (norm == NULL)
- return NULL;
- hash = refhash(norm);
- ref = map->table[hash % REFMAP_SIZE];
- while (ref) {
- if (ref->hash == hash && !strcmp((char *)ref->label, (char *)norm))
- break;
- ref = ref->next;
- }
- map->mem->free(norm);
- return ref;
- }
- void cmark_reference_map_free(cmark_reference_map *map) {
- unsigned int i;
- if (map == NULL)
- return;
- for (i = 0; i < REFMAP_SIZE; ++i) {
- cmark_reference *ref = map->table[i];
- cmark_reference *next;
- while (ref) {
- next = ref->next;
- reference_free(map, ref);
- ref = next;
- }
- }
- map->mem->free(map);
- }
- cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) {
- cmark_reference_map *map =
- (cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map));
- map->mem = mem;
- return map;
- }
|