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