// This method converts encoded text into a passage. // The text is in the format as its complementary function, "encode", produces. Passage Passage::decode (const string& encoded) { Passage passage; vector <string> bits = filter_string_explode (encoded, '_'); if (!bits.empty ()) { string verse = bits.back (); if (!verse.empty ()) passage.verse = verse; bits.pop_back (); } if (!bits.empty ()) { string chapter = bits.back (); if (!chapter.empty()) passage.chapter = convert_to_int (chapter); bits.pop_back (); } if (!bits.empty ()) { string book = bits.back (); if (!book.empty()) passage.book = convert_to_int (book); bits.pop_back (); } if (!bits.empty ()) { passage.bible = hex2bin (bits.back ()); bits.pop_back (); } return passage; }
// If $command and $parameters are queued as a task, the function returns true. // Else it returns false. // It looks for an exact match. // Parameters left out are not checked. bool tasks_logic_queued (string command, vector <string> parameters) { // The lines to look for consist of the command followed by the parameters. vector <string> search (parameters); search.insert (search.begin (), command); // Go through all queued tasks. vector <string> files = filter_url_scandir (tasks_logic_folder ()); for (auto & file : files) { // Read the task's contents. string contents = filter_url_file_get_contents (filter_url_create_path (tasks_logic_folder (), file)); vector <string> lines = filter_string_explode (contents, '\n'); if (lines.empty ()) return false; // Look for a match. bool match = true; for (size_t i = 0; i < search.size (); i++) { if (i < lines.size ()) { if (search [i] != lines[i]) match = false; } else { match = false; } } if (match) return true; } // No match found. return false; }
// Returns the verse text given a $verse_number and $usfm code. // Handles combined verses. string usfm_get_verse_text (string usfm, int verse_number) { vector <string> result; bool hit = (verse_number == 0); vector <string> lines = filter_string_explode (usfm, '\n'); for (string line : lines) { vector <int> verses = usfm_get_verse_numbers (line); if (verse_number == 0) { if (verses.size () != 1) hit = false; if (hit) result.push_back (line); } else { if (in_array (verse_number, verses)) { // Desired verse found. hit = true; } else if (verses.size () == 1) { // No verse found: No change in situation. } else { // Outside desired verse. hit = false; } if (hit) result.push_back (line); } } // Return the verse text. string verseText = filter_string_implode (result, "\n"); return verseText; }
// A Bible has a standard order for the books, and it can have their books in a custom order. // This function returns either the standard order, or a custom order in case it is available for the $bible. vector <int> filter_passage_get_ordered_books (const string& bible) { Database_Bibles database_bibles; // The available books from the Bible. vector <int> projectbooks = database_bibles.getBooks (bible); // The book order from the settings, if any. string s_orderedbooks = Database_Config_Bible::getBookOrder (bible); vector <string> vs_orderedbooks = filter_string_explode (s_orderedbooks, ' '); // Keep books available in the Bible. vector <int> orderedbooks; for (string & book : vs_orderedbooks) { int bk = convert_to_int (book); if (find (projectbooks.begin(), projectbooks.end(), bk) != projectbooks.end()) { orderedbooks.push_back (bk); } } // Books in the Bible but not in the settings: Add them to the end. for (int book : projectbooks) { if (find (orderedbooks.begin(), orderedbooks.end(), book) == orderedbooks.end()) { orderedbooks.push_back (book); } } return orderedbooks; }
// Takes data POSTed from the browser, and parses it. void http_parse_post (string content, Webserver_Request * request) { // Read and parse the POST data. try { if (!content.empty ()) { // Standard parse. bool urlencoded = request->content_type.find ("urlencoded") != string::npos; ParseWebData::WebDataMap dataMap; ParseWebData::parse_post_data (content, request->content_type, dataMap); for (ParseWebData::WebDataMap::const_iterator iter = dataMap.begin(); iter != dataMap.end(); ++iter) { string value; if (urlencoded) value = filter_url_urldecode ((*iter).second.value); else value = (*iter).second.value; request->post [(*iter).first] = value; } // Special case: Extract the filename in case of a file upload. if (content.length () > 1000) content.resize (1000); if (content.find ("filename=") != string::npos) { vector <string> lines = filter_string_explode (content, '\n'); for (auto & line : lines) { if (line.find ("Content-Disposition") == string::npos) continue; size_t pos = line.find ("filename="); if (pos == string::npos) continue; line = line.substr (pos + 10); line = filter_string_trim (line); line.pop_back (); request->post ["filename"] = line; } } } } catch (...) { } }
// Takes the passage in $text, and explodes it into book, chapter, verse. // The book is the numerical identifier, not the string, e.g., // it would not return "Genesis", but identifier 1. Passage filter_passage_explode_passage (string text) { text = filter_passage_clean_passage (text); // Cut the text in its parts. vector <string> bits = filter_string_explode (text, ' '); // Defaults to empty passage. Passage passage; // Take the bits. if (!bits.empty ()) { string verse = bits.back (); if (!verse.empty ()) passage.verse = verse; bits.pop_back (); } if (!bits.empty ()) { string chapter = bits.back (); if (!chapter.empty()) passage.chapter = convert_to_int (chapter); bits.pop_back (); } string book = filter_string_implode (bits, " "); if (!book.empty()) { int bk = filter_passage_interpret_book (book); passage.book = bk; } // Return the result. return passage; }
void Database_Privileges::load (string username, const string & data) { // Clear all data for the user. { SqliteDatabase sql (database ()); sql.add ("DELETE FROM bibles WHERE username ="******";"); sql.execute (); sql.clear (); sql.add ("DELETE FROM features WHERE username ="******";"); sql.execute (); } vector <string> lines = filter_string_explode (data, '\n'); bool loading_bibles = false; string bible_value; int book_value = 0; bool write_value = false; bool loading_features = false; int counter = 0; for (auto & line : lines) { if (line == bibles_end ()) { loading_bibles = false; } if (line == features_end ()) { loading_features = false; } counter++; if (loading_bibles) { if (counter == 1) bible_value = line; if (counter == 2) book_value = convert_to_int (line); if (counter == 3) { write_value = (line == on ()); setBibleBook (username, bible_value, book_value, write_value); counter = 0; } } if (loading_features) { setFeature (username, convert_to_int (line), true); } if (line == bibles_start ()) { loading_bibles = true; counter = 0; } if (line == features_start ()) { loading_features = true; counter = 0; } } }
void client_logic_create_note_decode (string data, string& bible, int& book, int& chapter, int& verse, string& summary, string& contents, bool& raw) { vector <string> lines = filter_string_explode (data, '\n'); if (!lines.empty ()) { bible = lines [0]; lines.erase (lines.begin()); } if (!lines.empty ()) { book = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { chapter = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { verse = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { summary = lines [0]; lines.erase (lines.begin()); } if (!lines.empty ()) { raw = convert_to_bool (lines [0]); lines.erase (lines.begin()); } contents = filter_string_implode (lines, "\n"); }
// Gets the raw USFM for the bible and passage given. string search_logic_get_bible_verse_usfm (string bible, int book, int chapter, int verse) { vector <string> texts; string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); vector <string> lines = filter_string_explode (index, '\n'); int index_verse = 0; bool read_index_verse = false; int index_item = 0; for (auto & line : lines) { if (read_index_verse) { index_verse = convert_to_int (line); read_index_verse = false; } else if (line == search_logic_verse_separator ()) { read_index_verse = true; index_item = 0; } else if (line == search_logic_index_separator ()) { index_item++; } else if (index_item == USFM_RAW) { if (verse == index_verse) { texts.push_back (line); } } } return filter_string_implode (texts, "\n"); }
string resource_logic_get_divider (string resource) { vector <string> bits = filter_string_explode (resource, ' '); string colour = unicode_string_casefold (bits [0]); // The $ influences the resource's embedding through javascript. string html = "$<div class=\"width100\" style=\"background-color: " + colour + ";\"> </div>"; return html; }
// This function reads the $data for the Bible book abbreviations. // It then transforms that data into pairs of <book identifier, abbreviation>. // It returns them as a vector. vector <pair <int, string> > filter_abbreviations_read (string data) { vector <pair <int, string> > output; vector <string> v_data = filter_string_explode (data, '\n'); for (string & entry : v_data) { if (entry.empty ()) continue; entry = filter_string_trim (entry); vector <string> v_entry = filter_string_explode (entry, '='); if (v_entry.size() != 2) continue; string sbook = filter_string_trim (v_entry [0]); int book = Database_Books::getIdFromEnglish (sbook); if (book == 0) continue; string abbrev = filter_string_trim (v_entry [1]); if (abbrev == "") continue; output.push_back (make_pair (book, abbrev)); } return output; }
// Returns how many emails are waiting in the mail storage host's POP3 email inbox. int email_receive_count (string& error, bool verbose) { #ifdef CLIENT_PREPARED error = "Not implemented with embedded http library"; if (verbose) {} return 0; #else CURL *curl; CURLcode res = CURLE_OK; struct cstring s; init_string (&s); curl = curl_easy_init (); curl_easy_setopt (curl, CURLOPT_USERNAME, Database_Config_General::getMailStorageUsername ().c_str()); curl_easy_setopt (curl, CURLOPT_PASSWORD, Database_Config_General::getMailStoragePassword ().c_str()); curl_easy_setopt (curl, CURLOPT_URL, url ().c_str()); curl_easy_setopt (curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s); if (verbose) { curl_easy_setopt (curl, CURLOPT_DEBUGFUNCTION, filter_url_curl_debug_callback); curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); } // Some servers need this validation. curl_easy_setopt (curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); filter_url_curl_set_timeout (curl); res = curl_easy_perform (curl); int mailcount = 0; if (res == CURLE_OK) { string response = (char *) s.ptr; response = filter_string_trim (response); mailcount = filter_string_explode (response, '\n').size(); } else { error = curl_easy_strerror (res); } if (s.ptr) free (s.ptr); curl_easy_cleanup (curl); return mailcount; #endif }
// Returns the names of the available workspacees. vector <string> workspace_get_names (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; vector <string> workspacees; // The names and the order of the workspacees is taken from the URLs. string rawvalue = request->database_config_user()->getWorkspaceURLs (); vector <string> lines = filter_string_explode (rawvalue, '\n'); for (auto & line : lines) { vector <string> bits = filter_string_explode (line, '_'); if (bits.size() == 3) { if (find (workspacees.begin(), workspacees.end(), bits[0]) == workspacees.end()) { workspacees.push_back (bits[0]); } } } if (workspacees.empty ()) workspacees.push_back (workspace_get_active_name (request)); return workspacees; }
void Checks_Verses::missingPunctuationAtEnd (string bible, int book, int chapter, map <int, string> verses, string center_marks, string end_marks) { vector <string> centermarks = filter_string_explode (center_marks, ' '); vector <string> endmarks = filter_string_explode (end_marks, ' '); Database_Check database_check; for (auto element : verses) { int verse = element.first; string text = element.second; if (verse == 0) continue; if (text.empty ()) continue; size_t text_length = unicode_string_length (text); string lastCharacter = unicode_string_substr (text, text_length - 1, 1); if (in_array (lastCharacter, centermarks)) continue; if (in_array (lastCharacter, endmarks)) continue; database_check.recordOutput (bible, book, chapter, verse, "No punctuation at end of verse: " + lastCharacter); } }
// Gets the human-readable name of a $line like this: // [CrossWire] *[Shona] (1.1) - Shona Bible string sword_logic_get_name (string line) { vector <string> bits = filter_string_explode (line, '-'); if (bits.size () >= 2) { bits.erase (bits.begin ()); } line = filter_string_implode (bits, "-"); line = filter_string_trim (line); return line; }
void Editor_Export::closeElementNode (xmlNodePtr node) { // The tag and class names of this element node. string tagName ((char *) node->name); string className; xmlChar * property = xmlGetProp (node, BAD_CAST "class"); if (property) { className = (char *) property; xmlFree (property); } if (tagName == "p") { // While editing it happens that the p element does not have a class. // Use the 'p' class in such cases. if (className == "") className = "p"; if (noteOpeners.find (className) != noteOpeners.end()) { // Deal with note closers. currentLine += usfm_get_closing_usfm (className); } else { // Normally a p element closes the USFM line. flushLine (); mono = false; // Clear active character styles. characterStyles.clear(); } } if (tagName == "span") { // Do nothing for monospace elements, because the USFM would be the text nodes only. if (mono) return; // Do nothing without a class. if (className.empty()) return; // Do nothing if no endmarkers are supposed to be produced. if (suppressEndMarkers.find (className) != suppressEndMarkers.end()) return; // Add closing USFM, optionally closing embedded tags in reverse order. vector <string> classes = filter_string_explode (className, ' '); characterStyles = filter_string_array_diff (characterStyles, classes); reverse (classes.begin(), classes.end()); for (unsigned int offset = 0; offset < classes.size(); offset++) { bool embedded = (classes.size () > 1) && (offset == 0); if (!characterStyles.empty ()) embedded = true; currentLine += usfm_get_closing_usfm (classes [offset], embedded); } } if (tagName == "a") { // Do nothing for note citations in the text. } }
// This deals with sequences and ranges of verses, like the following: // Exod. 37:4-5, 14-15, 27-28 // It puts each verse on a separate line. vector <string> filter_passage_handle_sequences_ranges (const string& passage) { // A passage like Exod. 37:4-5, 14-15, 27-28 will be cut at the comma. // The resulting array contains the following: // Exod. 37:4-5 // 14-15 // 27-28 // It implies that the first sequence has book and chapter. vector <string> sequences = filter_string_explode (passage, ','); for (string & line : sequences) line = filter_string_trim (line); // Store output lines. vector <string> output; // Cut the passages at the hyphen. for (unsigned int offset = 0; offset < sequences.size(); offset++) { string sequence = sequences [offset]; vector <string> range = filter_string_explode (sequence, '-'); if (range.size () == 1) { output.push_back (filter_string_trim (range [0])); } else { string start = filter_string_trim (range [0]); output.push_back (start); if (offset == 0) { // Since the first bit contains book / chapter / verse, // extract the verse number. start = string (start.rbegin(), start.rend()); start = convert_to_string (convert_to_int (start)); start = string (start.rbegin(), start.rend()); } unsigned int end = convert_to_int (filter_string_trim (range [1])); for (size_t i = convert_to_int (start) + 1; i <= end; i++) { output.push_back (convert_to_string (i)); } } } // Result. return output; }
void workspace_delete (void * webserver_request, string workspace) { Webserver_Request * request = (Webserver_Request *) webserver_request; string rawvalue; vector <string> currentlines; vector <string> newlines; rawvalue = request->database_config_user()->getWorkspaceURLs (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceURLs (rawvalue); rawvalue = request->database_config_user()->getWorkspaceWidths (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceWidths (rawvalue); rawvalue = request->database_config_user()->getWorkspaceHeights (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceHeights (rawvalue); request->database_config_user()->setActiveWorkspace (""); // For a client, store the setting for sending to the server. workspace_cache_for_cloud (request, true, true, true); }
void Checks_Sentences::enterNames (string names_in) { names.clear (); names_in = filter_string_str_replace ("\n", " ", names_in); vector <string> names2 = filter_string_explode (names_in, ' '); for (auto name : names2) { if (name != "") { // Limit the length to the left of the suffix in the test. name = unicode_string_substr (name, 0, 11); names.push_back (name); } } }
// Returns the verse numbers in the string of $usfm code at line number $line_number. vector <int> usfm_linenumber_to_versenumber (string usfm, unsigned int line_number) { vector <int> verse_number = {0}; // Initial verse number. vector <string> lines = filter_string_explode (usfm, '\n'); for (unsigned int i = 0; i < lines.size(); i++) { if (i <= line_number) { vector <int> verse_numbers = usfm_get_verse_numbers (lines[i]); if (verse_numbers.size() >= 2) { verse_number = filter_string_array_diff (verse_numbers, {0}); } } } return verse_number; }
// Get installed SWORD modules. vector <string> sword_logic_get_installed () { vector <string> modules; string out_err; string sword_path = sword_logic_get_path (); filter_shell_run ("cd " + sword_path + "; installmgr -l", out_err); vector <string> lines = filter_string_explode (out_err, '\n'); for (auto line : lines) { line = filter_string_trim (line); if (line.empty ()) continue; if (line.find ("[") == string::npos) continue; modules.push_back (line); } return modules; }
// Returns the verse numbers in the string of $usfm code at offset $offset. // Offset is calculated with unicode_string_length to support UTF-8. vector <int> usfm_offset_to_versenumber (string usfm, unsigned int offset) { unsigned int totalOffset = 0; vector <string> lines = filter_string_explode (usfm, '\n'); for (unsigned int i = 0; i < lines.size(); i++) { int length = unicode_string_length (lines [i]); totalOffset += length; if (totalOffset >= offset) { return usfm_linenumber_to_versenumber (usfm, i); } // Add 1 for new line. totalOffset += 1; } return {0}; }
// Returns true if the email address is valid. bool filter_url_email_is_valid (string email) { const string valid_set ("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-"); // The @ character should appear only once. vector <string> atbits = filter_string_explode (email, '@'); if (atbits.size() != 2) return false; // The characters on the left of @ should be from the valid set. string left = atbits [0]; for (unsigned int i = 0; i < left.size(); i++) { char c = left [i]; if (valid_set.find (c) == string::npos) return false; } // The characters on the right of @ should be from the valid set. string right = atbits [1]; for (unsigned int i = 0; i < right.size(); i++) { char c = right [i]; if (valid_set.find (c) == string::npos) return false; } // The character . should appear at least once to the right of @. vector <string> dotbits = filter_string_explode (right, '.'); if (dotbits.size () < 2) return false; // The email address is valid. return true; }
// Returns the offset within the $usfm code where $verse number starts. int usfm_versenumber_to_offset (string usfm, int verse) { // Verse number 0 starts at offset 0. if (verse == 0) return 0; int totalOffset = 0; vector <string> lines = filter_string_explode (usfm, '\n'); for (string line : lines) { vector <int> verses = usfm_get_verse_numbers (line); for (auto & v : verses) { if (v == verse) return totalOffset; } totalOffset += unicode_string_length (line); // Add 1 for new line. totalOffset += 1; } return unicode_string_length (usfm); }
// Searches the text of the Bibles. // Returns an array with matching passages. // $search: Contains the text to search for. // $bibles: Array of Bible names to search in. vector <Passage> search_logic_search_text (string search, vector <string> bibles) { vector <Passage> passages; if (search == "") return passages; search = unicode_string_casefold (search); search = filter_string_str_replace (",", "", search); Database_Bibles database_bibles; for (auto bible : bibles) { vector <int> books = database_bibles.getBooks (bible); for (auto book : books) { vector <int> chapters = database_bibles.getChapters (bible, book); for (auto chapter : chapters) { string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); if (index.find (search) != string::npos) { vector <string> lines = filter_string_explode (index, '\n'); int index_verse = 0; bool read_index_verse = false; int index_item = 0; for (auto & line : lines) { if (read_index_verse) { index_verse = convert_to_int (line); read_index_verse = false; } else if (line == search_logic_verse_separator ()) { read_index_verse = true; index_item = 0; } else if (line == search_logic_index_separator ()) { index_item++; } else if (index_item == PLAIN_LOWER) { if (line.find (search) != string::npos) { passages.push_back (Passage (bible, book, chapter, convert_to_string (index_verse))); } } } } } } } return passages; }
void Editor_Html2Usfm::openInline (string className) { // It has been observed that the <span> elements of the character styles may be embedded, like so: // The <span class="add"> // <span class="nd">Lord God</span> // is calling</span> you</span><span>.</span> vector <string> classes = filter_string_explode (className, ' '); for (unsigned int offset = 0; offset < classes.size(); offset++) { bool embedded = (characterStyles.size () + offset) > 0; currentLine += usfm_get_opening_usfm (classes[offset], embedded); } // Store active character styles in some cases. bool store = true; if (suppressEndMarkers.find (className) != suppressEndMarkers.end ()) store = false; if (processingNote) store = false; if (store) { characterStyles.insert (characterStyles.end(), classes.begin(), classes.end()); } }
// Returns the string $usfm as one long string. // $usfm may contain new lines, but the resulting long string won't. string usfm_one_string (string usfm) { string long_string = ""; vector <string> usfm_lines = filter_string_explode (usfm, '\n'); for (string & line : usfm_lines) { line = filter_string_trim (line); // Skip empty line. if (line != "") { // The line will be appended to the output line. // If it does not start with a backslash (\), a space is inserted first. size_t pos = line.find ("\\"); if (pos != 0) { if (long_string != "") long_string += " "; } long_string += line; } } return long_string; }
// Returns the total verse count within a $bible. int search_logic_get_verse_count (string bible) { int verse_count = 0; Database_Bibles database_bibles; vector <int> books = database_bibles.getBooks (bible); for (auto book : books) { vector <int> chapters = database_bibles.getChapters (bible, book); for (auto chapter : chapters) { string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); vector <string> lines = filter_string_explode (index, '\n'); for (auto & line : lines) { if (line == search_logic_verse_separator ()) { verse_count++; } } } } return verse_count; }
// Import data. void Database_Versifications::input (const string& contents, const string& name) { // Delete old system if it is there, and create new one. erase (name); int id = createSystem (name); sqlite3 * db = connect (); database_sqlite_exec (db, "PRAGMA temp_store = MEMORY;"); database_sqlite_exec (db, "PRAGMA synchronous = OFF;"); database_sqlite_exec (db, "PRAGMA journal_mode = OFF;"); database_sqlite_exec (db, "BEGIN;"); vector <string> lines = filter_string_explode (contents, '\n'); for (auto line : lines) { line = filter_string_trim (line); if (line.empty ()) continue; Passage passage = filter_passage_explode_passage (line); if ((passage.book == 0) || (passage.chapter == 0) || (passage.verse.empty ())) { Database_Logs::log ("Malformed versification entry: " + line); continue; } SqliteSQL sql = SqliteSQL (); sql.add ("INSERT INTO data (system, book, chapter, verse) VALUES ("); sql.add (id); sql.add (","); sql.add (passage.book); sql.add (","); sql.add (passage.chapter); sql.add (","); sql.add (convert_to_int (passage.verse)); sql.add (");"); database_sqlite_exec (db, sql.sql); } database_sqlite_exec (db, "COMMIT;"); database_sqlite_disconnect (db); }
// Performs a case-sensitive search of the USFM of one $bible. // Returns an array with the rowid's of matching verses. // $search: Contains the text to search for. vector <Passage> search_logic_search_bible_usfm_case_sensitive (string bible, string search) { vector <Passage> passages; if (search == "") return passages; Database_Bibles database_bibles; vector <int> books = database_bibles.getBooks (bible); for (auto book : books) { vector <int> chapters = database_bibles.getChapters (bible, book); for (auto chapter : chapters) { string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); if (index.find (search) != string::npos) { vector <string> lines = filter_string_explode (index, '\n'); int index_verse = 0; bool read_index_verse = false; int index_item = 0; for (auto & line : lines) { if (read_index_verse) { index_verse = convert_to_int (line); read_index_verse = false; } else if (line == search_logic_verse_separator ()) { read_index_verse = true; index_item = 0; } else if (line == search_logic_index_separator ()) { index_item++; } else if (index_item == USFM_RAW) { if (line.find (search) != string::npos) { passages.push_back (Passage (bible, book, chapter, convert_to_string (index_verse))); } } } } } } return passages; }