text-lookup.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 text_item *items;
  36. };
  37. static void lookup_getstringtoken(struct lexer *lex, struct strref *token)
  38. {
  39. const char *temp = lex->offset;
  40. bool was_backslash = false;
  41. while (*temp != 0 && *temp != '\n') {
  42. if (!was_backslash) {
  43. if (*temp == '\\') {
  44. was_backslash = true;
  45. } else if (*temp == '"') {
  46. temp++;
  47. break;
  48. }
  49. } else {
  50. was_backslash = false;
  51. }
  52. ++temp;
  53. }
  54. token->len += (size_t)(temp - lex->offset);
  55. if (*token->array == '"') {
  56. token->array++;
  57. token->len--;
  58. if (*(temp - 1) == '"')
  59. token->len--;
  60. }
  61. lex->offset = temp;
  62. }
  63. static bool lookup_gettoken(struct lexer *lex, struct strref *str)
  64. {
  65. struct base_token temp;
  66. base_token_clear(&temp);
  67. strref_clear(str);
  68. while (lexer_getbasetoken(lex, &temp, PARSE_WHITESPACE)) {
  69. char ch = *temp.text.array;
  70. if (!str->array) {
  71. /* comments are designated with a #, and end at LF */
  72. if (ch == '#') {
  73. while (*lex->offset != '\n' && *lex->offset != 0)
  74. ++lex->offset;
  75. } else if (temp.type == BASETOKEN_WHITESPACE) {
  76. strref_copy(str, &temp.text);
  77. break;
  78. } else {
  79. strref_copy(str, &temp.text);
  80. if (ch == '"') {
  81. lookup_getstringtoken(lex, str);
  82. break;
  83. } else if (ch == '=') {
  84. break;
  85. }
  86. }
  87. } else {
  88. if (temp.type == BASETOKEN_WHITESPACE || *temp.text.array == '=') {
  89. lex->offset -= temp.text.len;
  90. break;
  91. }
  92. if (ch == '#') {
  93. lex->offset--;
  94. break;
  95. }
  96. str->len += temp.text.len;
  97. }
  98. }
  99. return (str->len != 0);
  100. }
  101. static inline bool lookup_goto_nextline(struct lexer *p)
  102. {
  103. struct strref val;
  104. bool success = true;
  105. strref_clear(&val);
  106. while (true) {
  107. if (!lookup_gettoken(p, &val)) {
  108. success = false;
  109. break;
  110. }
  111. if (*val.array == '\n')
  112. break;
  113. }
  114. return success;
  115. }
  116. static char *convert_string(const char *str, size_t len)
  117. {
  118. struct dstr out;
  119. out.array = bstrdup_n(str, len);
  120. out.capacity = len + 1;
  121. out.len = len;
  122. dstr_replace(&out, "\\n", "\n");
  123. dstr_replace(&out, "\\t", "\t");
  124. dstr_replace(&out, "\\r", "\r");
  125. dstr_replace(&out, "\\\"", "\"");
  126. return out.array;
  127. }
  128. static void lookup_addfiledata(struct text_lookup *lookup, const char *file_data)
  129. {
  130. struct lexer lex;
  131. struct strref name, value;
  132. lexer_init(&lex);
  133. lexer_start(&lex, file_data);
  134. strref_clear(&name);
  135. strref_clear(&value);
  136. while (lookup_gettoken(&lex, &name)) {
  137. struct text_item *item;
  138. struct text_item *old;
  139. bool got_eq = false;
  140. if (*name.array == '\n')
  141. continue;
  142. getval:
  143. if (!lookup_gettoken(&lex, &value))
  144. break;
  145. if (*value.array == '\n')
  146. continue;
  147. else if (!got_eq && *value.array == '=') {
  148. got_eq = true;
  149. goto getval;
  150. }
  151. item = bzalloc(sizeof(struct text_item));
  152. item->lookup = bstrdup_n(name.array, name.len);
  153. item->value = convert_string(value.array, value.len);
  154. HASH_REPLACE_STR(lookup->items, lookup, item, old);
  155. if (old)
  156. text_item_destroy(old);
  157. if (!lookup_goto_nextline(&lex))
  158. break;
  159. }
  160. lexer_free(&lex);
  161. }
  162. static inline bool lookup_getstring(const char *lookup_val, const char **out, struct text_lookup *lookup)
  163. {
  164. struct text_item *item;
  165. if (!lookup->items)
  166. return false;
  167. HASH_FIND_STR(lookup->items, lookup_val, item);
  168. if (!item)
  169. return false;
  170. *out = item->value;
  171. return true;
  172. }
  173. /* ------------------------------------------------------------------------- */
  174. lookup_t *text_lookup_create(const char *path)
  175. {
  176. struct text_lookup *lookup = bzalloc(sizeof(struct text_lookup));
  177. if (!text_lookup_add(lookup, path)) {
  178. bfree(lookup);
  179. lookup = NULL;
  180. }
  181. return lookup;
  182. }
  183. bool text_lookup_add(lookup_t *lookup, const char *path)
  184. {
  185. struct dstr file_str;
  186. char *temp = NULL;
  187. FILE *file;
  188. file = os_fopen(path, "rb");
  189. if (!file)
  190. return false;
  191. os_fread_utf8(file, &temp);
  192. dstr_init_move_array(&file_str, temp);
  193. fclose(file);
  194. if (!file_str.array)
  195. return false;
  196. dstr_replace(&file_str, "\r", " ");
  197. lookup_addfiledata(lookup, file_str.array);
  198. dstr_free(&file_str);
  199. return true;
  200. }
  201. void text_lookup_destroy(lookup_t *lookup)
  202. {
  203. if (lookup) {
  204. struct text_item *item, *tmp;
  205. HASH_ITER (hh, lookup->items, item, tmp) {
  206. HASH_DELETE(hh, lookup->items, item);
  207. text_item_destroy(item);
  208. }
  209. bfree(lookup);
  210. }
  211. }
  212. bool text_lookup_getstr(lookup_t *lookup, const char *lookup_val, const char **out)
  213. {
  214. if (lookup)
  215. return lookup_getstring(lookup_val, out, lookup);
  216. return false;
  217. }