aboutsummaryrefslogtreecommitdiff
path: root/src/references.c
blob: def4dd85b3bc542e7f227247feb0b26a56919a64 (plain)
  1. #include "cmark.h"
  2. #include "utf8.h"
  3. #include "references.h"
  4. #include "inlines.h"
  5. static unsigned int
  6. refhash(const unsigned char *link_ref)
  7. {
  8. unsigned int hash = 0;
  9. while (*link_ref)
  10. hash = (*link_ref++) + (hash << 6) + (hash << 16) - hash;
  11. return hash;
  12. }
  13. static void reference_free(reference *ref)
  14. {
  15. if(ref != NULL) {
  16. free(ref->label);
  17. free(ref->url);
  18. free(ref->title);
  19. free(ref);
  20. }
  21. }
  22. // normalize reference: collapse internal whitespace to single space,
  23. // remove leading/trailing whitespace, case fold
  24. // Return NULL if the reference name is actually empty (i.e. composed
  25. // solely from whitespace)
  26. static unsigned char *normalize_reference(chunk *ref)
  27. {
  28. strbuf normalized = GH_BUF_INIT;
  29. unsigned char *result;
  30. if(ref == NULL)
  31. return NULL;
  32. if (ref->len == 0)
  33. return NULL;
  34. utf8proc_case_fold(&normalized, ref->data, ref->len);
  35. strbuf_trim(&normalized);
  36. strbuf_normalize_whitespace(&normalized);
  37. result = strbuf_detach(&normalized);
  38. assert(result);
  39. if (result[0] == '\0') {
  40. free(result);
  41. return NULL;
  42. }
  43. return result;
  44. }
  45. static void add_reference(reference_map *map, reference* ref)
  46. {
  47. reference *t = ref->next = map->table[ref->hash % REFMAP_SIZE];
  48. while (t) {
  49. if (t->hash == ref->hash &&
  50. !strcmp((char *)t->label, (char *)ref->label)) {
  51. reference_free(ref);
  52. return;
  53. }
  54. t = t->next;
  55. }
  56. map->table[ref->hash % REFMAP_SIZE] = ref;
  57. }
  58. extern void reference_create(reference_map *map, chunk *label, chunk *url, chunk *title)
  59. {
  60. reference *ref;
  61. unsigned char *reflabel = normalize_reference(label);
  62. /* empty reference name, or composed from only whitespace */
  63. if (reflabel == NULL)
  64. return;
  65. ref = calloc(1, sizeof(*ref));
  66. if(ref != NULL) {
  67. ref->label = reflabel;
  68. ref->hash = refhash(ref->label);
  69. ref->url = clean_url(url);
  70. ref->title = clean_title(title);
  71. ref->next = NULL;
  72. add_reference(map, ref);
  73. }
  74. }
  75. // Returns reference if refmap contains a reference with matching
  76. // label, otherwise NULL.
  77. reference* reference_lookup(reference_map *map, chunk *label)
  78. {
  79. reference *ref = NULL;
  80. unsigned char *norm;
  81. unsigned int hash;
  82. if (label->len > MAX_LINK_LABEL_LENGTH)
  83. return NULL;
  84. if (map == NULL)
  85. return NULL;
  86. norm = normalize_reference(label);
  87. if (norm == NULL)
  88. return NULL;
  89. hash = refhash(norm);
  90. ref = map->table[hash % REFMAP_SIZE];
  91. while (ref) {
  92. if (ref->hash == hash &&
  93. !strcmp((char *)ref->label, (char *)norm))
  94. break;
  95. ref = ref->next;
  96. }
  97. free(norm);
  98. return ref;
  99. }
  100. void reference_map_free(reference_map *map)
  101. {
  102. unsigned int i;
  103. if(map == NULL)
  104. return;
  105. for (i = 0; i < REFMAP_SIZE; ++i) {
  106. reference *ref = map->table[i];
  107. reference *next;
  108. while (ref) {
  109. next = ref->next;
  110. reference_free(ref);
  111. ref = next;
  112. }
  113. }
  114. free(map);
  115. }
  116. reference_map *reference_map_new(void)
  117. {
  118. return calloc(1, sizeof(reference_map));
  119. }