text-lookup.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (c) 2023 Lain Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <ctype.h>
  17. #include "dstr.h"
  18. #include "text-lookup.h"
  19. #include "lexer.h"
  20. #include "platform.h"
  21. #include "uthash.h"
  22. /* ------------------------------------------------------------------------- */
  23. struct text_item {
  24. char *lookup, *value;
  25. UT_hash_handle hh;
  26. };
  27. static inline void text_item_destroy(struct text_item *item)
  28. {
  29. bfree(item->lookup);
  30. bfree(item->value);
  31. bfree(item);
  32. }
  33. /* ------------------------------------------------------------------------- */
  34. struct text_lookup {
  35. struct dstr language;
  36. struct text_item *items;
  37. };
  38. static void lookup_getstringtoken(struct lexer *lex, struct strref *token)
  39. {
  40. const char *temp = lex->offset;
  41. bool was_backslash = false;
  42. while (*temp != 0 && *temp != '\n') {
  43. if (!was_backslash) {
  44. if (*temp == '\\') {
  45. was_backslash = true;
  46. } else if (*temp == '"') {
  47. temp++;
  48. break;
  49. }
  50. } else {
  51. was_backslash = false;
  52. }
  53. ++temp;
  54. }
  55. token->len += (size_t)(temp - lex->offset);
  56. if (*token->array == '"') {
  57. token->array++;
  58. token->len--;
  59. if (*(temp - 1) == '"')
  60. token->len--;
  61. }
  62. lex->offset = temp;
  63. }
  64. static bool lookup_gettoken(struct lexer *lex, struct strref *str)
  65. {
  66. struct base_token temp;
  67. base_token_clear(&temp);
  68. strref_clear(str);
  69. while (lexer_getbasetoken(lex, &temp, PARSE_WHITESPACE)) {
  70. char ch = *temp.text.array;
  71. if (!str->array) {
  72. /* comments are designated with a #, and end at LF */
  73. if (ch == '#') {
  74. while (ch != '\n' && ch != 0)
  75. ch = *(++lex->offset);
  76. } else if (temp.type == BASETOKEN_WHITESPACE) {
  77. strref_copy(str, &temp.text);
  78. break;
  79. } else {
  80. strref_copy(str, &temp.text);
  81. if (ch == '"') {
  82. lookup_getstringtoken(lex, str);
  83. break;
  84. } else if (ch == '=') {
  85. break;
  86. }
  87. }
  88. } else {
  89. if (temp.type == BASETOKEN_WHITESPACE ||
  90. *temp.text.array == '=') {
  91. lex->offset -= temp.text.len;
  92. break;
  93. }
  94. if (ch == '#') {
  95. lex->offset--;
  96. break;
  97. }
  98. str->len += temp.text.len;
  99. }
  100. }
  101. return (str->len != 0);
  102. }
  103. static inline bool lookup_goto_nextline(struct lexer *p)
  104. {
  105. struct strref val;
  106. bool success = true;
  107. strref_clear(&val);
  108. while (true) {
  109. if (!lookup_gettoken(p, &val)) {
  110. success = false;
  111. break;
  112. }
  113. if (*val.array == '\n')
  114. break;
  115. }
  116. return success;
  117. }
  118. static char *convert_string(const char *str, size_t len)
  119. {
  120. struct dstr out;
  121. out.array = bstrdup_n(str, len);
  122. out.capacity = len + 1;
  123. out.len = len;
  124. dstr_replace(&out, "\\n", "\n");
  125. dstr_replace(&out, "\\t", "\t");
  126. dstr_replace(&out, "\\r", "\r");
  127. dstr_replace(&out, "\\\"", "\"");
  128. return out.array;
  129. }
  130. static void lookup_addfiledata(struct text_lookup *lookup,
  131. const char *file_data)
  132. {
  133. struct lexer lex;
  134. struct strref name, value;
  135. lexer_init(&lex);
  136. lexer_start(&lex, file_data);
  137. strref_clear(&name);
  138. strref_clear(&value);
  139. while (lookup_gettoken(&lex, &name)) {
  140. struct text_item *item;
  141. struct text_item *old;
  142. bool got_eq = false;
  143. if (*name.array == '\n')
  144. continue;
  145. getval:
  146. if (!lookup_gettoken(&lex, &value))
  147. break;
  148. if (*value.array == '\n')
  149. continue;
  150. else if (!got_eq && *value.array == '=') {
  151. got_eq = true;
  152. goto getval;
  153. }
  154. item = bzalloc(sizeof(struct text_item));
  155. item->lookup = bstrdup_n(name.array, name.len);
  156. item->value = convert_string(value.array, value.len);
  157. HASH_REPLACE_STR(lookup->items, lookup, item, old);
  158. if (old)
  159. text_item_destroy(old);
  160. if (!lookup_goto_nextline(&lex))
  161. break;
  162. }
  163. lexer_free(&lex);
  164. }
  165. static inline bool lookup_getstring(const char *lookup_val, const char **out,
  166. struct text_lookup *lookup)
  167. {
  168. struct text_item *item;
  169. if (!lookup->items)
  170. return false;
  171. HASH_FIND_STR(lookup->items, lookup_val, item);
  172. if (!item)
  173. return false;
  174. *out = item->value;
  175. return true;
  176. }
  177. /* ------------------------------------------------------------------------- */
  178. lookup_t *text_lookup_create(const char *path)
  179. {
  180. struct text_lookup *lookup = bzalloc(sizeof(struct text_lookup));
  181. if (!text_lookup_add(lookup, path)) {
  182. bfree(lookup);
  183. lookup = NULL;
  184. }
  185. return lookup;
  186. }
  187. bool text_lookup_add(lookup_t *lookup, const char *path)
  188. {
  189. struct dstr file_str;
  190. char *temp = NULL;
  191. FILE *file;
  192. file = os_fopen(path, "rb");
  193. if (!file)
  194. return false;
  195. os_fread_utf8(file, &temp);
  196. dstr_init_move_array(&file_str, temp);
  197. fclose(file);
  198. if (!file_str.array)
  199. return false;
  200. dstr_replace(&file_str, "\r", " ");
  201. lookup_addfiledata(lookup, file_str.array);
  202. dstr_free(&file_str);
  203. return true;
  204. }
  205. void text_lookup_destroy(lookup_t *lookup)
  206. {
  207. if (lookup) {
  208. struct text_item *item, *tmp;
  209. HASH_ITER (hh, lookup->items, item, tmp) {
  210. HASH_DELETE(hh, lookup->items, item);
  211. text_item_destroy(item);
  212. }
  213. dstr_free(&lookup->language);
  214. bfree(lookup);
  215. }
  216. }
  217. bool text_lookup_getstr(lookup_t *lookup, const char *lookup_val,
  218. const char **out)
  219. {
  220. if (lookup)
  221. return lookup_getstring(lookup_val, out, lookup);
  222. return false;
  223. }