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