aboutsummaryrefslogtreecommitdiff
path: root/src/utf8.c
blob: 8a786b7346b726b5d4705feb77e69ec97f62013a (plain)
  1. #include <stdlib.h>
  2. #include <stdint.h>
  3. #include <unistd.h>
  4. #include <assert.h>
  5. #include "utf8.h"
  6. static const int8_t utf8proc_utf8class[256] = {
  7. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  8. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  9. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  10. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  11. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  12. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  13. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  14. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  15. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  16. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  17. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  18. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  19. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  20. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  21. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  22. 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 };
  23. static void encode_unknown(strbuf *buf)
  24. {
  25. static const uint8_t repl[] = {239, 191, 189};
  26. strbuf_put(buf, repl, 3);
  27. }
  28. int utf8proc_charlen(const uint8_t *str, int str_len)
  29. {
  30. int length, i;
  31. if (!str_len)
  32. return 0;
  33. length = utf8proc_utf8class[str[0]];
  34. if (!length)
  35. return -1;
  36. if (str_len >= 0 && length > str_len)
  37. return -str_len;
  38. for (i = 1; i < length; i++) {
  39. if ((str[i] & 0xC0) != 0x80)
  40. return -i;
  41. }
  42. return length;
  43. }
  44. void utf8proc_detab(strbuf *ob, const uint8_t *line, size_t size)
  45. {
  46. static const uint8_t whitespace[] = " ";
  47. size_t i = 0, tab = 0;
  48. while (i < size) {
  49. size_t org = i;
  50. while (i < size && line[i] != '\t' && line[i] <= 0x80) {
  51. i++; tab++;
  52. }
  53. if (i > org)
  54. strbuf_put(ob, line + org, i - org);
  55. if (i >= size)
  56. break;
  57. if (line[i] == '\t') {
  58. int numspaces = 4 - (tab % 4);
  59. strbuf_put(ob, whitespace, numspaces);
  60. i += 1;
  61. tab += numspaces;
  62. } else {
  63. int charlen = utf8proc_charlen(line + i, size - i);
  64. if (charlen >= 0) {
  65. strbuf_put(ob, line + i, charlen);
  66. } else {
  67. encode_unknown(ob);
  68. charlen = -charlen;
  69. }
  70. i += charlen;
  71. tab += 1;
  72. }
  73. }
  74. }
  75. int utf8proc_iterate(const uint8_t *str, int str_len, int32_t *dst)
  76. {
  77. int length;
  78. int32_t uc = -1;
  79. *dst = -1;
  80. length = utf8proc_charlen(str, str_len);
  81. if (length < 0)
  82. return -1;
  83. switch (length) {
  84. case 1:
  85. uc = str[0];
  86. break;
  87. case 2:
  88. uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
  89. if (uc < 0x80) uc = -1;
  90. break;
  91. case 3:
  92. uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
  93. + (str[2] & 0x3F);
  94. if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
  95. (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
  96. break;
  97. case 4:
  98. uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
  99. + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
  100. if (uc < 0x10000 || uc >= 0x110000) uc = -1;
  101. break;
  102. }
  103. if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
  104. return -1;
  105. *dst = uc;
  106. return length;
  107. }
  108. void utf8proc_encode_char(int32_t uc, strbuf *buf)
  109. {
  110. uint8_t dst[4];
  111. int len = 0;
  112. assert(uc >= 0);
  113. if (uc < 0x80) {
  114. dst[0] = uc;
  115. len = 1;
  116. } else if (uc < 0x800) {
  117. dst[0] = 0xC0 + (uc >> 6);
  118. dst[1] = 0x80 + (uc & 0x3F);
  119. len = 2;
  120. } else if (uc == 0xFFFF) {
  121. dst[0] = 0xFF;
  122. len = 1;
  123. } else if (uc == 0xFFFE) {
  124. dst[0] = 0xFE;
  125. len = 1;
  126. } else if (uc < 0x10000) {
  127. dst[0] = 0xE0 + (uc >> 12);
  128. dst[1] = 0x80 + ((uc >> 6) & 0x3F);
  129. dst[2] = 0x80 + (uc & 0x3F);
  130. len = 3;
  131. } else if (uc < 0x110000) {
  132. dst[0] = 0xF0 + (uc >> 18);
  133. dst[1] = 0x80 + ((uc >> 12) & 0x3F);
  134. dst[2] = 0x80 + ((uc >> 6) & 0x3F);
  135. dst[3] = 0x80 + (uc & 0x3F);
  136. len = 4;
  137. } else {
  138. encode_unknown(buf);
  139. return;
  140. }
  141. strbuf_put(buf, dst, len);
  142. }
  143. void utf8proc_case_fold(strbuf *dest, const uint8_t *str, int len)
  144. {
  145. int32_t c;
  146. #define bufpush(x) \
  147. utf8proc_encode_char(x, dest)
  148. while (len > 0) {
  149. int char_len = utf8proc_iterate(str, len, &c);
  150. if (char_len >= 0) {
  151. #include "case_fold_switch.inc"
  152. } else {
  153. encode_unknown(dest);
  154. char_len = -char_len;
  155. }
  156. str += char_len;
  157. len -= char_len;
  158. }
  159. }