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