Standalone Haplous Bible Parser
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.

274 lines
5.5 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "haplous.h"
  5. // seeks through a file line by line (with fgets) until it gets to the requested
  6. // book
  7. static int haplous_work_book_seek(FILE *file, char *id)
  8. {
  9. int n = 0;
  10. char line[17]; // #book:exod (ids can be up to 10 characters long)
  11. while (fgets(line, 17, file)) {
  12. if (line[0] == '\n') {
  13. n++;
  14. continue;
  15. }
  16. if (strncmp(line, "#book:", 6) == 0) {
  17. for (size_t i = 0; i != 10; ++i) {
  18. if (line[i + 6] != id[i] || i == strlen(id)) {
  19. goto continue_while;
  20. }
  21. if (line[i + 7] == '\n') {
  22. return n;
  23. }
  24. }
  25. }
  26. continue_while:
  27. n++;
  28. }
  29. return HAPLOUS_REF_NOT_FOUND;
  30. }
  31. // run haplous_work_book_find first
  32. static int haplous_work_chapter_seek(FILE *file, size_t chapter)
  33. {
  34. int n = 0;
  35. char line[13]; // #chapter:150
  36. size_t c = 0;
  37. while (fgets(line, 13, file)) {
  38. if (strncmp(line, "#chapter:", 9) == 0) {
  39. c++; // increase it first since "chapter" starts at 1
  40. if (c == chapter) {
  41. return n;
  42. }
  43. }
  44. n++;
  45. }
  46. return HAPLOUS_REF_NOT_FOUND;
  47. }
  48. // Get a range of verses
  49. // Allocates memory which caller owns in the end
  50. // TODO support custom allocators
  51. char *haplous_work_verses_get(FILE *file, struct haplous_reference ref,
  52. int *err)
  53. {
  54. if (ref.verse_start == 0) {
  55. *err = HAPLOUS_INVALID_REF;
  56. return NULL;
  57. }
  58. if (ref.verse_start > ref.verse_end) {
  59. *err = HAPLOUS_INVALID_REF;
  60. return NULL;
  61. }
  62. size_t buf_size = 1000;
  63. char *buffer = calloc(buf_size, sizeof(char));
  64. if (file == NULL) {
  65. *err = HAPLOUS_WORK_NOT_FOUND;
  66. return NULL;
  67. }
  68. fseek(file, 0, SEEK_SET);
  69. int line = 0;
  70. line = haplous_work_book_seek(file, ref.id);
  71. line = haplous_work_chapter_seek(file, ref.chapter);
  72. if (line < 0) {
  73. *err = HAPLOUS_REF_NOT_FOUND;
  74. return NULL;
  75. }
  76. int prev = '\0';
  77. int c;
  78. size_t bufi = 0;
  79. size_t verse = 1;
  80. while ((c = getc(file)) != EOF) {
  81. // detect end of the chapter
  82. if (prev == '\n' && c == '^') {
  83. // if at the end of the chapter, make sure it has found
  84. // all the required verses
  85. // TODO test
  86. if (verse < ref.verse_end) {
  87. *err = HAPLOUS_END_TOO_BIG; // TODO maybe
  88. // OUT_OF_RANGE?
  89. return NULL;
  90. }
  91. }
  92. if (verse >= ref.verse_start && verse <= ref.verse_end) {
  93. if (bufi >= buf_size) {
  94. // Increase by 2 to make sure there's always
  95. // space for the NULL terminator
  96. buf_size += 2;
  97. buffer = realloc(buffer, buf_size);
  98. if (buffer == NULL) {
  99. *err = HAPLOUS_OUT_OF_MEMORY;
  100. return buffer;
  101. }
  102. }
  103. buffer[bufi] = (char)c;
  104. bufi++;
  105. } else if (verse >= ref.verse_end) {
  106. break;
  107. }
  108. if (c == '\n') {
  109. verse++;
  110. }
  111. prev = c;
  112. }
  113. buffer[bufi] = '\0';
  114. *err = HAPLOUS_OK;
  115. return buffer;
  116. }
  117. // Get text from a full chapter separated by "\n"
  118. // disregards any verse information in ref
  119. // returns HAPLOUS_REF_NOT_FOUND upon errors
  120. // Allocates same as haplous_work_verses_get
  121. // It can return NULL, but never when an error is not set
  122. // TODO figure out error handling again
  123. // TODO decide whether or not to initialize buffer within or ourside of function
  124. char *haplous_work_chapter_get(FILE *file, struct haplous_reference ref,
  125. int *err)
  126. {
  127. if (ref.chapter == 0) {
  128. *err = HAPLOUS_INVALID_REF;
  129. return NULL;
  130. }
  131. if (file == NULL) {
  132. *err = HAPLOUS_WORK_NOT_FOUND;
  133. return NULL;
  134. }
  135. fseek(file, 0, SEEK_SET);
  136. int line = 0;
  137. line = haplous_work_book_seek(file, ref.id);
  138. line = haplous_work_chapter_seek(file, ref.chapter);
  139. if (line < 0) {
  140. *err = HAPLOUS_REF_NOT_FOUND;
  141. return NULL;
  142. }
  143. size_t buf_size = 1000;
  144. char *buffer = malloc(buf_size);
  145. if (buffer == NULL) {
  146. *err = HAPLOUS_OUT_OF_MEMORY;
  147. return NULL;
  148. }
  149. int c;
  150. size_t i = 0;
  151. while ((c = getc(file)) != EOF) {
  152. if (c == '^') {
  153. break;
  154. }
  155. if (i >= buf_size) {
  156. buf_size += 1;
  157. buffer = realloc(buffer, buf_size);
  158. if (buffer == NULL) {
  159. *err = HAPLOUS_OUT_OF_MEMORY;
  160. return buffer;
  161. }
  162. }
  163. buffer[i] = (char)c;
  164. i++;
  165. }
  166. buffer[i] = '\0';
  167. *err = HAPLOUS_OK;
  168. return buffer;
  169. }
  170. struct haplous_reader haplous_reader_new(struct haplous_work work, struct haplous_reference ref, int *err) {
  171. struct haplous_reader reader = {
  172. .work = work,
  173. .reference = ref,
  174. .verse = NULL,
  175. .current_verse = 1,
  176. };
  177. fseek(reader.work.file, 0, SEEK_SET);
  178. int line = 0;
  179. line = haplous_work_book_seek(reader.work.file, reader.reference.id);
  180. line = haplous_work_chapter_seek(reader.work.file, reader.reference.chapter);
  181. if (line < 0) {
  182. *err = HAPLOUS_REF_NOT_FOUND;
  183. return reader;
  184. }
  185. return reader;
  186. }
  187. // haplous_next provides an interface for obtaining a reference verse-by-verse
  188. int haplous_next(struct haplous_reader *reader) {
  189. // seek to the first verse
  190. int c;
  191. while ((c = getc(reader->work.file)) != EOF) {
  192. if (c == '\n') {
  193. reader->current_verse += 1;
  194. }
  195. if (reader->current_verse >= reader->reference.verse_start) {
  196. if (c != '\n') {
  197. fseek(reader->work.file, -1, SEEK_CUR);
  198. }
  199. break;
  200. }
  201. }
  202. size_t buf_size = 500;
  203. char *buffer = malloc(buf_size);
  204. if (buffer == NULL) {
  205. return HAPLOUS_OUT_OF_MEMORY;
  206. }
  207. // load the verse to the buffer
  208. size_t i = 0;
  209. while ((c = getc(reader->work.file)) != EOF) {
  210. if (c == '\n') {
  211. break;
  212. }
  213. if (i >= buf_size) {
  214. buf_size += 1;
  215. buffer = realloc(buffer, buf_size);
  216. if (buffer == NULL) {
  217. free(buffer);
  218. return HAPLOUS_OUT_OF_MEMORY;
  219. }
  220. }
  221. buffer[i] = (char)c;
  222. i++;
  223. }
  224. buffer[i] = '\0';
  225. reader->verse = buffer;
  226. if (reader->current_verse <= reader->reference.verse_end) {
  227. reader->current_verse += 1;
  228. return HAPLOUS_CONTINUE;
  229. } else {
  230. return HAPLOUS_OK;
  231. }
  232. }