IRC client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

format.c 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <stdbool.h>
  17. #include <stdint.h>
  18. #include <stdlib.h>
  19. #include <wchar.h>
  20. #include "chat.h"
  21. // Adapted from <https://github.com/cbreeden/fxhash/blob/master/lib.rs>.
  22. static uint32_t hashChar(uint32_t hash, char ch) {
  23. hash = (hash << 5) | (hash >> 27);
  24. hash ^= ch;
  25. hash *= 0x27220A95;
  26. return hash;
  27. }
  28. enum IRCColor formatColor(const char *str) {
  29. if (!str) return IRCDefault;
  30. uint32_t hash = 0;
  31. for (; str[0]; ++str) {
  32. hash = hashChar(hash, str[0]);
  33. }
  34. while (IRCBlack == (hash & IRCLightGray)) {
  35. hash = hashChar(hash, '\0');
  36. }
  37. return (hash & IRCLightGray);
  38. }
  39. void formatReset(struct Format *format) {
  40. format->bold = false;
  41. format->italic = false;
  42. format->underline = false;
  43. format->reverse = false;
  44. format->fg = IRCDefault;
  45. format->bg = IRCDefault;
  46. }
  47. static void parseColor(struct Format *format) {
  48. size_t len = MIN(wcsspn(format->str, L"0123456789"), 2);
  49. if (!len) {
  50. format->fg = IRCDefault;
  51. format->bg = IRCDefault;
  52. return;
  53. }
  54. format->fg = 0;
  55. for (size_t i = 0; i < len; ++i) {
  56. format->fg *= 10;
  57. format->fg += format->str[i] - L'0';
  58. }
  59. if (format->fg > IRCLightGray) format->fg = IRCDefault;
  60. format->str = &format->str[len];
  61. len = 0;
  62. if (format->str[0] == L',') {
  63. len = MIN(wcsspn(&format->str[1], L"0123456789"), 2);
  64. }
  65. if (!len) return;
  66. format->bg = 0;
  67. for (size_t i = 0; i < len; ++i) {
  68. format->bg *= 10;
  69. format->bg += format->str[1 + i] - L'0';
  70. }
  71. if (format->bg > IRCLightGray) format->bg = IRCDefault;
  72. format->str = &format->str[1 + len];
  73. }
  74. static const wchar_t Codes[] = {
  75. IRCBold, IRCColor, IRCReverse, IRCReset, IRCItalic, IRCUnderline, L'\0',
  76. };
  77. bool formatParse(struct Format *format, const wchar_t *split) {
  78. format->str += format->len;
  79. if (!format->str[0]) {
  80. if (split == format->str && !format->split) {
  81. format->len = 0;
  82. format->split = true;
  83. return true;
  84. }
  85. return false;
  86. }
  87. const wchar_t *init = format->str;
  88. for (bool done = false; !done;) {
  89. switch (format->str[0]) {
  90. break; case IRCBold: format->str++; format->bold ^= true;
  91. break; case IRCItalic: format->str++; format->italic ^= true;
  92. break; case IRCUnderline: format->str++; format->underline ^= true;
  93. break; case IRCReverse: format->str++; format->reverse ^= true;
  94. break; case IRCColor: format->str++; parseColor(format);
  95. break; case IRCReset: format->str++; formatReset(format);
  96. break; default: done = true;
  97. }
  98. }
  99. format->split = (split >= init && split <= format->str);
  100. format->len = wcscspn(format->str, Codes);
  101. if (split > format->str && split < &format->str[format->len]) {
  102. format->len = split - format->str;
  103. }
  104. return true;
  105. }
  106. #ifdef TEST
  107. #include <assert.h>
  108. static bool testColor(
  109. const wchar_t *str, enum IRCColor fg, enum IRCColor bg, size_t index
  110. ) {
  111. struct Format format = { .str = str };
  112. formatReset(&format);
  113. if (!formatParse(&format, NULL)) return false;
  114. if (format.fg != fg) return false;
  115. if (format.bg != bg) return false;
  116. return (format.str == &str[index]);
  117. }
  118. static bool testSplit(const wchar_t *str, size_t index) {
  119. struct Format format = { .str = str };
  120. formatReset(&format);
  121. bool split = false;
  122. while (formatParse(&format, &str[index])) {
  123. if (format.split && split) return false;
  124. if (format.split) split = true;
  125. }
  126. return split;
  127. }
  128. static bool testSplits(const wchar_t *str) {
  129. for (size_t i = 0; i <= wcslen(str); ++i) {
  130. if (!testSplit(str, i)) return false;
  131. }
  132. return true;
  133. }
  134. int main() {
  135. assert(testColor(L"\003a", IRCDefault, IRCDefault, 1));
  136. assert(testColor(L"\003,a", IRCDefault, IRCDefault, 1));
  137. assert(testColor(L"\003,1", IRCDefault, IRCDefault, 1));
  138. assert(testColor(L"\0031a", IRCBlack, IRCDefault, 2));
  139. assert(testColor(L"\0031,a", IRCBlack, IRCDefault, 2));
  140. assert(testColor(L"\00312a", IRCLightBlue, IRCDefault, 3));
  141. assert(testColor(L"\00312,a", IRCLightBlue, IRCDefault, 3));
  142. assert(testColor(L"\003123", IRCLightBlue, IRCDefault, 3));
  143. assert(testColor(L"\0031,1a", IRCBlack, IRCBlack, 4));
  144. assert(testColor(L"\0031,12a", IRCBlack, IRCLightBlue, 5));
  145. assert(testColor(L"\0031,123", IRCBlack, IRCLightBlue, 5));
  146. assert(testColor(L"\00312,1a", IRCLightBlue, IRCBlack, 5));
  147. assert(testColor(L"\00312,12a", IRCLightBlue, IRCLightBlue, 6));
  148. assert(testColor(L"\00312,123", IRCLightBlue, IRCLightBlue, 6));
  149. assert(testColor(L"\00316,16a", IRCDefault, IRCDefault, 6));
  150. assert(testColor(L"\00399,99a", IRCDefault, IRCDefault, 6));
  151. assert(testSplits(L""));
  152. assert(testSplits(L"ab"));
  153. assert(testSplits(L"\002"));
  154. assert(testSplits(L"\002ab"));
  155. assert(testSplits(L"a\002b"));
  156. assert(testSplits(L"\002\003"));
  157. assert(testSplits(L"a\002\003b"));
  158. assert(testSplits(L"a\0031b"));
  159. assert(testSplits(L"a\00312b"));
  160. assert(testSplits(L"a\00312,1b"));
  161. assert(testSplits(L"a\00312,12b"));
  162. }
  163. #endif