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.
 
 
 

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