Bible text I/O library, for retrieving and navigating text in KJV Bible More details at: http://mbed.org/users/davervw/notebook/ebible-abstract/
BibleIO.cpp
- Committer:
- davervw
- Date:
- 2011-02-27
- Revision:
- 0:1f3d069211cd
File content as of revision 0:1f3d069211cd:
/* Bible I/O Class Implementation * * Copyright (c) 2011 David R. Van Wagner davervw@yahoo.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* * This class assumes bible text file is present and in a specific format extracted from source such as: * http://printkjv.ifbweb.com/AV_txt.zip */ #include <mbed.h> // mkdir #include <stdio.h> #include <string.h> #include <stdlib.h> #include <locale> #include "BibleIO.h" //extern Serial console; BibleIO::BibleIO(char* filename, void (*indexing_callback)(int, void*), void* indexing_context) { name = 0; num_books = 0; books = 0; bookmarks = 0; text_filename = filename; this->indexing_callback = indexing_callback; this->indexing_context = indexing_context; fp = fopen(text_filename, "r"); load_bookmarks(); if (!load_index()) build_index(); } BibleIO::~BibleIO() { delete [] name; name = 0; delete [] books; books = 0; delete [] bookmarks; bookmarks = 0; fclose(fp); } void BibleIO::build_index() { read_name(); read_preface(); read_books(); load_index(); } void BibleIO::read_name() { name = read_line(); //console.printf("name=%s\n", name); //lcd.printf("%s\n", name); } void BibleIO::read_preface() { char* line = 0; long book_offset = ftell(fp); long line_offset = book_offset; do { if (line != 0 && *line != 0) book_offset = line_offset; delete [] line; line_offset = ftell(fp); line = read_line(); //console.printf("%s\n", line); } while (strcmp(line, "CHAPTER 1") != 0); delete [] line; // read too far, reset file pointer fseek(fp, book_offset, SEEK_SET); } void BibleIO::read_books() { char* path = index_path(); mkdir(path, 0666); delete [] path; path = 0; char* fn = index_path("books"); FILE* index = fopen(fn, "w"); delete [] fn; fn = 0; long title_offset = 0; fwrite(&title_offset, sizeof(title_offset), 1, index); short book = 0; //lcd.cls(); if (indexing_callback != 0) (*indexing_callback)(0, indexing_context); //lcd.printf("Indexing0%%"); long offset = ftell(fp); fseek(fp, 0, SEEK_END); long filesize = ftell(fp); fseek(fp, offset, SEEK_SET); while(read_book(index, book)) { offset = ftell(fp); //lcd.printf("\n\n%d%%", offset*100/filesize); if (indexing_callback != 0) (*indexing_callback)(offset*100/filesize, indexing_context); ++book; } //lcd.cls(); //lcd.printf("Complete100%%"); if (indexing_callback != 0) (*indexing_callback)(100, indexing_context); //printf("%hd books\n", book); fclose(index); } bool BibleIO::read_book(FILE* index, short book) { long offset = ftell(fp); char *book_name = BibleIO::read_line(); if (book_name == 0 || *(book_name) == 0 || strcmp(book_name, "THE END") == 0 || (book_name[0] >= '0' && book_name[0] <= '9')) { delete [] book_name; return false; } fwrite(&offset, sizeof(offset), 1, index); format_book_name(book_name); //printf("book=%s\n", book_name); //lcd.cls(); //lcd.printf("%s_\n", book_name); delete [] book_name; short chapter = 0; while (read_chapter(book, chapter)) ++chapter; //console.printf("%hd chapters\n", chapter); long num_chapters = chapter; fwrite(&num_chapters, sizeof(num_chapters), 1, index); return true; } void BibleIO::format_book_name(char* &book_name) { char* last_word = strrchr(book_name, ' '); if (last_word != 0) { int number = 0; if (strstr(book_name, "MOSES") == 0) { if (strstr(book_name, " FIRST ") != 0) number = 1; else if (strstr(book_name, " SECOND ") != 0) number = 2; else if (strstr(book_name, " THIRD ") != 0) number = 3; } if (strstr(book_name, "SONG") != 0 || strstr(book_name, "ACTS ") != 0) last_word = book_name; if (strstr(book_name, "ECCLESIASTES") != 0 || strstr(book_name, "LAMENTATIONS") != 0 || strstr(book_name, "ACTS OF") != 0 || strstr(book_name, "REVELATION") != 0) { last_word = book_name; if (strncmp(last_word, "THE ", 4) == 0) last_word += 4; char* next_word = strchr(last_word, ' '); if (next_word != 0) *next_word = 0; } if (strncmp(last_word, "THE ", 4) == 0) last_word += 4; if (*last_word == ' ') ++last_word; if (last_word[strlen(last_word)-1] < 'A' || last_word[strlen(last_word)-1] > 'Z') last_word[strlen(last_word)-1] = 0; char* new_name = new char[strlen(last_word)+((number==0)?1:3)]; if (number == 0) strcpy(new_name, last_word); else sprintf(new_name, "%d %s", number, last_word); delete [] book_name; book_name = new_name; } } bool BibleIO::read_chapter(short book, short chapter) { char* line = 0; long offset; do { delete [] line; offset = ftell(fp); line = BibleIO::read_line(); } while (line != 0 && (*line == 0 || strstr(line, "CALLED,") != 0 || line[0]=='#')); if (line != 0 && strcmp(line, "THE END OF THE OLD TESTAMENT")==0) { do { delete [] line; offset = ftell(fp); line = BibleIO::read_line(); } while (line != 0 && strstr(line, "MATTHEW")==0); } if (line == 0 || (strncmp(line, "CHAPTER ", 8) != 0 && strncmp(line, "PSALM ", 6) != 0) || (line[0] == 'P' && atoi(line+6) != chapter+1) || (line[0] == 'C' && atoi(line+8) != chapter+1)) { delete [] line; fseek(fp, offset, SEEK_SET); return false; } delete [] line; char* path = index_path(book); mkdir(path, 0666); delete [] path; path = 0; char* fn = index_path(book, chapter); FILE* index = fopen(fn, "wb"); //console.printf("index file pointer=%08lx\n", (long)index); delete [] fn; fn = 0; // chapter name fwrite(&offset, sizeof(offset), 1, index); //console.printf("Chapter %d\n", chapter+1); // support Psalms optional description of chapter long desc_offset = ftell(fp); char* chapter_desc = BibleIO::read_line(); if (chapter_desc == 0 || (chapter_desc[0] >= '0' && chapter_desc[0] <= '9')) { delete [] chapter_desc; chapter_desc = 0; fseek(fp, desc_offset, SEEK_SET); } delete [] chapter_desc; // not currently used short verse = 0; while (read_verse(index, book, chapter, verse)) ++verse; //console.printf("\n"); //console.printf("%hd verses\n", verse); fclose(index); return true; } char* BibleIO::read_line() { char* line; const int size = 4096; line = new char[size]; fgets(line, size, fp); // remove CR/LF while (strlen(line)>0 && (line[strlen(line)-1] == 0x0a || line[strlen(line)-1] == 0x0d)) line[strlen(line)-1] = 0; // resize line to actual size char* new_line = new char[strlen(line)+1]; strcpy(new_line, line); delete [] line; line = 0; //console.printf("%s\n", new_line); return new_line; } bool BibleIO::load_index() { char* fn = index_path("books"); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; if (index == 0) return false; fseek(index, 0, SEEK_END); long filesize = ftell(index); num_books = (filesize - sizeof(long)) / sizeof(book_index); delete [] books; books = (bible_index*)new char[filesize]; fseek(index, 0, SEEK_SET); fread(books, filesize, 1, index); long save_offset = ftell(fp); fseek(fp, books->title_offset, SEEK_SET); delete [] name; name = read_line(); fseek(fp, save_offset, SEEK_SET); fclose(index); return true; } short BibleIO::get_num_books() { return num_books; } short BibleIO::get_num_chapters(short book) { return books->books[book].num_chapters; } short BibleIO::get_num_verses(short book, short chapter) { char* fn = index_path(book, chapter); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; fseek(index, 0, SEEK_END); long filesize = ftell(index); short num_verses = (filesize - sizeof(long)) / sizeof(chapter_index); fclose(index); return num_verses; } char* BibleIO::title() { char *t = new char[strlen(name)+1]; strcpy(t, name); return t; } char* BibleIO::title_book(short book) { if (book < 0 || book > num_books) return 0; long offset = ftell(fp); long title_offset = books->books[book].title_offset; fseek(fp, title_offset, SEEK_SET); char* title = read_line(); fseek(fp, offset, SEEK_SET); format_book_name(title); return title; } char* BibleIO::title_chapter(short book, short chapter) { char* fn = index_path(book, chapter); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; long title_offset; fread(&title_offset, sizeof(title_offset), 1, index); fclose(index); long offset = ftell(fp); fseek(fp, title_offset, SEEK_SET); char* title = read_line(); fseek(fp, offset, SEEK_SET); return title; } char* BibleIO::text_chapter(short book, short chapter) { if (book < 0 || book > num_books) return 0; char* fn = index_path(book, chapter); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; if (index == 0) return 0; long title_offset; fread(&title_offset, sizeof(title_offset), 1, index); fseek(index, 0, SEEK_END); long filesize = ftell(index); short num_verses = (filesize - sizeof(long)) / sizeof(chapter_index); fseek(index, sizeof(long) + (num_verses-1)*sizeof(long), SEEK_SET); long verse_offset = -1; fread(&verse_offset, sizeof(verse_offset), 1, index); fclose(index); long offset = ftell(fp); fseek(fp, verse_offset, SEEK_SET); char* last_verse = read_line(); delete [] last_verse; verse_offset = ftell(fp); fseek(fp, title_offset, SEEK_SET); int size = verse_offset-title_offset+1; char* text = new char[size]; fread(text, size-1, 1, fp); text[size-1] = 0; fseek(fp, offset, SEEK_SET); return text; } char* BibleIO::text_verse(short book, short chapter, short verse) { //console.printf("text_verse: book %hd, chapter %hd, verse %hd\n", book, chapter, verse); if (book > num_books) return 0; char* fn = index_path(book, chapter); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; if (index == 0) return 0; fseek(index, 0, SEEK_END); long filesize = ftell(index); short num_verses = (filesize - sizeof(long)) / sizeof(chapter_index); if (verse >= num_verses || verse < 0) { fclose(index); return 0; } fseek(index, sizeof(long) + verse*sizeof(long), SEEK_SET); long verse_offset = -1; fread(&verse_offset, sizeof(verse_offset), 1, index); fclose(index); long offset = ftell(fp); fseek(fp, verse_offset, SEEK_SET); char* text = read_line(); fseek(fp, offset, SEEK_SET); //console.printf("text: %s\n", text); return text; } bool BibleIO::read_verse(FILE* index, short book, short chapter, short verse) { long offset = ftell(fp); char* line = 0; do { delete [] line; line = BibleIO::read_line(); } while (line != 0 && (*line == 0 || line[0] >= 'A' && line[0] <= 'Z' && line[strlen(line)-1] == '.')); if (line == 0 || *line == 0 || atoi(line) == 0) { delete [] line; fseek(fp, offset, SEEK_SET); return false; } short verse_num = atoi(line); delete [] line; // !!! NOTE: this test fails at Philemon 1:2 because verses 1 & 2 are on the same line, for now ignore this test. Workaround is to edit text file to avoid error. // if (verse+1 != verse_num) // return false; //console.printf("%d ", verse_num); fwrite(&offset, sizeof(offset), 1, index); return true; } bool BibleIO::load_bookmarks() { delete [] bookmarks; bookmarks = 0; num_bookmarks = 0; char* fn = index_path("marks"); FILE* index = fopen(fn, "r"); delete [] fn; fn = 0; if (index == 0) return true; // okay it is not present yet, empty fseek(index, 0, SEEK_END); long filesize = ftell(index); num_bookmarks = filesize / sizeof(position); fseek(index, 0, SEEK_SET); bookmarks = new position[num_bookmarks]; fread(bookmarks, sizeof(position), num_bookmarks, index); fclose(index); return true; } bool BibleIO::save_bookmarks() { char* fn = index_path("marks"); remove(fn); FILE* index = fopen(fn, "w"); delete [] fn; fn = 0; if (index == 0) return false; fwrite(bookmarks, sizeof(position), num_bookmarks, index); fclose(index); return true; } bool BibleIO::bookmark_add(short book, short chapter, short verse) { int i; for (i=0; i<num_bookmarks; ++i) { if (bookmarks[i].book == book && bookmarks[i].chapter == chapter && bookmarks[i].verse == verse) { return false; // already present } } // resize list position* new_bookmarks = new position[num_bookmarks+1]; for (i=0; i<num_bookmarks; ++i) new_bookmarks[i] = bookmarks[i]; new_bookmarks[i].book = book; new_bookmarks[i].chapter = chapter; new_bookmarks[i].verse = verse; new_bookmarks[i].rsvd = 0; delete [] bookmarks; bookmarks = new_bookmarks; ++num_bookmarks; return save_bookmarks(); } bool BibleIO::bookmark_del(short book, short chapter, short verse) { // search for bookmark int i; for (i=0; i<num_bookmarks; ++i) if (bookmarks[i].book == book && bookmarks[i].chapter == chapter && bookmarks[i].verse == verse) break; if (i>=num_bookmarks) return false; // not found // move previous entries down for (int j=i+1; j<num_bookmarks; ++j) bookmarks[j-1] = bookmarks[j]; --num_bookmarks; // rewrite to filesystem return save_bookmarks(); } bool BibleIO::bookmark_prev(short &book, short &chapter, short &verse) { // search for bookmark int i; for (i=0; i<num_bookmarks; ++i) if (bookmarks[i].book == book && bookmarks[i].chapter == chapter && bookmarks[i].verse == verse) break; if (i==0) i=num_bookmarks; // wrap around // previous --i; if (i < 0 || i >= num_bookmarks) return false; book = bookmarks[i].book; chapter = bookmarks[i].chapter; verse = bookmarks[i].verse; return true; } bool BibleIO::bookmark_next(short &book, short &chapter, short &verse) { // search for bookmark int i; for (i=0; i<num_bookmarks; ++i) if (bookmarks[i].book == book && bookmarks[i].chapter == chapter && bookmarks[i].verse == verse) break; // next if (++i >= num_bookmarks) i = 0; if (i < 0 || i >= num_bookmarks) return false; book = bookmarks[i].book; chapter = bookmarks[i].chapter; verse = bookmarks[i].verse; return true; } char* BibleIO::index_path() { char* path = index_path(""); path[strlen(path)-1] = 0; // remove trailing slash return path; } char* BibleIO::index_path(char* name) { int base_len = strrchr(text_filename, '/') + 1 - text_filename; int index_len = base_len + strlen("index/") + strlen(name); char* index = new char[index_len+1]; memcpy(index, text_filename, base_len); index[base_len] = 0; strcat(index, "index/"); strcat(index, name); return index; } char* BibleIO::index_path(short book) { char indexname[50]; sprintf(indexname, "%hd", book); return index_path(indexname); } char* BibleIO::index_path(short book, short chapter) { char indexname[50]; sprintf(indexname, "%hd/%hd", book, chapter); return index_path(indexname); }