A minimal Bible format
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.
 
 
 

182 lines
4.4 KiB

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "biblec.h"
  4. // Split chars and ints from string
  5. int strToInt(char *buf) {
  6. int ret = 0;
  7. for (; *buf != '\0'; buf++) {
  8. ret *= 10;
  9. ret += *buf - '0';
  10. }
  11. return ret;
  12. }
  13. // Parse BibleC index file, see
  14. // https://api.heb12.com/translations/biblec/web.i
  15. // TODO: better error detection
  16. int biblec_parse(struct BiblecTranslation *translation, char *indexLocation) {
  17. FILE *index = fopen(indexLocation, "r");
  18. if (index == NULL) {
  19. return BIBLEC_FILE_ERROR;
  20. }
  21. // If location is never filled in, then assume text
  22. // file location is in the same folder as the index file.
  23. strncpy(translation->location, indexLocation, MAX_LOCATION - 1);
  24. translation->location[strlen(indexLocation) - 1] = 't';
  25. int book = 0;
  26. char line[INDEX_MAX_LENGTH];
  27. while (fgets(line, INDEX_MAX_LENGTH, index) != NULL) {
  28. // Remove trailing breakline
  29. strtok(line, "\n");
  30. // Pointer to line content
  31. char *contents = line + 1;
  32. // Make a duplicate for manipulation
  33. char afterFirst[INDEX_MAX_LENGTH];
  34. strcpy(afterFirst, contents);
  35. if (line[0] == '#') {
  36. // Copy before ':' to afterFirst
  37. int c = 0;
  38. while (*contents != ':') {
  39. afterFirst[c] = *contents;
  40. contents++;
  41. c++;
  42. }
  43. afterFirst[c] = '\0';
  44. contents++; // Increment to skip ':'
  45. if (!strcmp(afterFirst, "name")) {
  46. strncpy(translation->name, contents, MAX_NAME - 1);
  47. } else if (!strcmp(afterFirst, "lang")) {
  48. strncpy(translation->lang, contents, MAX_LANG - 1);
  49. } else if (!strcmp(afterFirst, "location")) {
  50. strncpy(translation->location, contents, MAX_LOCATION - 1);
  51. } else if (!strcmp(afterFirst, "length")) {
  52. translation->length = strToInt(contents);
  53. }
  54. } else if (line[0] == '@') {
  55. char *bookInfo = strtok(afterFirst, " ");
  56. strcpy(translation->books[book].name, afterFirst);
  57. bookInfo = strtok(NULL, " ");
  58. translation->books[book].start = strToInt(bookInfo);
  59. bookInfo = strtok(NULL, " ");
  60. translation->books[book].length = strToInt(bookInfo);
  61. } else if (line[0] == '!') {
  62. // Loop through chapters and set them in the struct
  63. int currentChapter = 0;
  64. char *chapter = strtok(afterFirst, " ");
  65. while (chapter != NULL) {
  66. translation->books[book].chapters[currentChapter] = strToInt(chapter);
  67. chapter = strtok(NULL, " ");
  68. currentChapter++;
  69. }
  70. book++;
  71. }
  72. // Any other line start chars will be skipped
  73. }
  74. fclose(index);
  75. return 0;
  76. }
  77. int getBookID(struct BiblecTranslation *translation, char *book) {
  78. int bookID = BIBLEC_BOOK_ERROR;
  79. for (int i = 0; i < translation->length; i++) {
  80. if (!strcmp(book, translation->books[i].name)) {
  81. bookID = i;
  82. }
  83. }
  84. return bookID;
  85. }
  86. int biblec_next(struct BiblecReader *reader) {
  87. // Reached end of requested verses
  88. if (reader->linesRead > reader->to) {
  89. return 0;
  90. }
  91. // End of file
  92. if (fgets(reader->result, VERSE_LENGTH, reader->file) == NULL) {
  93. return 0;
  94. }
  95. strtok(reader->result, "\n"); // Strip '\n'
  96. reader->linesRead++;
  97. return 1;
  98. }
  99. // Create a new reader structure
  100. int biblec_new(struct BiblecReader *reader, struct BiblecTranslation *translation,
  101. char *book, int chapter, int verse, int to) {
  102. int c;
  103. reader->file = fopen(translation->location, "r");
  104. if (reader->file == NULL) {
  105. return BIBLEC_FILE_ERROR;
  106. }
  107. int bookID = getBookID(translation, book);
  108. if (bookID == BIBLEC_BOOK_ERROR) {
  109. return BIBLEC_BOOK_ERROR;
  110. }
  111. if (translation->books[bookID].length < chapter) {
  112. return BIBLEC_CHAPTER_ERROR;
  113. }
  114. // Grab start line, and add until specified chapter is reached.
  115. int line = translation->books[bookID].start;
  116. for (c = 0; c < chapter - 1; c++) {
  117. line += translation->books[bookID].chapters[c];
  118. }
  119. // When 0 is passed for "to", grab the entire chapter.
  120. // Else, "to" refers to how many verses to
  121. // count in the struct.
  122. if (to == 0) {
  123. to = translation->books[bookID].chapters[c] - 1;
  124. } else {
  125. to -= verse;
  126. }
  127. if (to < 0) {
  128. return BIBLEC_VERSE_ERROR;
  129. }
  130. // Add the line over to the specific verse
  131. line += verse - 1;
  132. reader->book = book;
  133. reader->chapter = chapter;
  134. reader->verse = verse;
  135. reader->to = to;
  136. reader->linesRead = 0;
  137. // Loop through until it gets to the line
  138. // This is fastest out of tested fgetc, fread
  139. char verseText[VERSE_LENGTH];
  140. for (int i = 0; i != line; i++) {
  141. if (fgets(verseText, VERSE_LENGTH, reader->file) == NULL) {
  142. return FILE_OVERFLOW;
  143. }
  144. }
  145. return 0;
  146. }
  147. void biblec_close(struct BiblecReader *reader) {
  148. fclose(reader->file);
  149. }