diff options
author | Your Name <you@example.com> | 2022-02-18 20:35:38 -0500 |
---|---|---|
committer | Your Name <you@example.com> | 2022-02-18 20:35:38 -0500 |
commit | 55d58a16e2511741cc625e203205dec86144faf3 (patch) | |
tree | 311be7e5fbaf1bc8ece47dd4261af053f2da1c7c /src/bible.cc | |
parent | aa9dabdeaead3c8b1b11f9d4f321265c439bfbfc (diff) | |
download | libbible-55d58a16e2511741cc625e203205dec86144faf3.tar.gz libbible-55d58a16e2511741cc625e203205dec86144faf3.tar.bz2 libbible-55d58a16e2511741cc625e203205dec86144faf3.zip |
Reorganized repository layout
Diffstat (limited to 'src/bible.cc')
-rw-r--r-- | src/bible.cc | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/src/bible.cc b/src/bible.cc new file mode 100644 index 0000000..a09c0c0 --- /dev/null +++ b/src/bible.cc @@ -0,0 +1,329 @@ +#include "lib/libbible.h" +#include <string> +#include <sstream> +#include <algorithm> +#include <getopt.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <iostream> +#include "utf8.h" + +using namespace std; + +void usage() { + printf("\nUsage:\n bible [options] [reference]\n\n"); + printf("Print bible passages.\n\n"); + printf("Options:\n"); + printf(" -h, --help display this help message\n"); + printf(" --list-modules list all installed modules\n"); + printf(" -m, --module <mod> use specified module\n"); + printf(" --set-default-module <mod> use specified module by default in future runs\n"); + printf(" --list-books list books available in the current module\n"); + printf(" --list-chapters <book> list chapters available in book in the current module\n"); + printf(" -o, --omit-verse-numbers when printing verse text, skip printing verse and chapter numbers\n"); + printf(" --list-installable=<lang> list bible versions available for download and install. Default lists for all languages.\n"); + printf(" --install-network <mod> install module from the network where <mod> is LANG:NAME as provided by --list-installable\n"); + printf(" --install-zip <path> install module from a zip file\n"); + printf(" --remove-module <mod> delete a module from the system\n"); + printf("\n\nExamples:\n bible Gal 5:22-23\n"); + printf(" bible John 3:16\n bible Romans 12\n bible Matt 5:3-7:27\n"); + printf(" bible Genesis 1-3\n"); +} + +string getDefaultModule() { + return libbible::settingsRead("module"); +} + +void listModules() { + map<string, vector<string>> mods = libbible::getModules(); + string defaultMod = getDefaultModule(); + printf("Modules Installed:\n"); + for(auto pair : mods) { + if(pair.first == defaultMod) { + printf(" %s (default)\n", pair.first.c_str()); + } else { + printf(" %s\n", pair.first.c_str()); + } + } +} + +void setDefaultModule(string modname) { + libbible::settingsWrite("module", modname); +} + +void listBooks(string modname) { + map<string, vector<string>> mods = libbible::getModules(); + if(mods.find(modname) == mods.end()) { + printf("ERROR: Module \"%s\" not installed!\n", modname.c_str()); + } else { + printf("Books in Module %s:\n", modname.c_str()); + for(string book : mods[modname]) { + printf(" %s\n", book.c_str()); + } + } +} + +void listChapters(string modname, string book) { + printf("Valid chapters for book %s in module %s:\n", book.c_str(), modname.c_str()); + for(auto pass : libbible::getPassages(modname, book)) { + printf(" Chapter %d, Verses %d-%d\n", pass.chapterStart, pass.verseStart, pass.verseEnd); + } +} + +void listInstallable(string language) { + map<string, vector<string>> installable = libbible::downloadModsAvailable(); + map<string, string> languages = libbible::getLanguageNames(); + for(auto pair : installable) { + if(!language.empty() && language != pair.first) { + continue; + } + printf("For language %s:", pair.first.c_str()); + if(!languages[pair.first].empty()) { + printf(" (%s)", languages[pair.first].c_str()); + } + printf("\n"); + for(string name : pair.second) { + printf(" %s\n", name.c_str()); + } + } +} + +void installNetwork(string mod) { + //Split on : + if(mod.find(':') == string::npos) { + printf("Unable to process module \"%s\": Must contain colon separated language:name\n", mod.c_str()); + return; + } + string lang = mod.substr(0, mod.find(':')); + string name = mod.substr(mod.find(':')+1); + if(libbible::installModFromInternet(lang, name)) { + printf("Module installed.\n"); + } else { + printf("Error installing module!\n"); + } +} + +void installZip(string path) { + libbible::installModFromZip(path); +} + +void removeMod(string mod) { + libbible::uninstallMod(mod); +} + +void textWrap(istream& in, ostream& out, size_t width) { + string word; + string line; + char cur = '\0'; + size_t i = 0; + + while(in.get(cur)) { + if(isspace(cur)) { + word.clear(); + } + if(cur == '\n') { + out << line << '\n'; + line.clear(); + word.clear(); + continue; + } + word += cur; + line += cur; + // Anything matching \033.*?m doesn't count + size_t credits = 0; + size_t found = -1; + while((found = line.find("\033", found+1)) != string::npos) { + size_t first = line.find_first_of("m", found); + if(first != string::npos) { + credits += first - found + 1; + } else { + credits += line.size() - found; + } + } + string::iterator end_it = utf8::find_invalid(line.begin(), line.end()); + i = utf8::distance(line.begin(), end_it) - credits; + //printf("Word: %s, i: %ld\n", word.c_str(), i); + if(i > width) { + word.erase(0, word.find_first_not_of(" ")); + if(line.find_last_of(" ") != string::npos) { + line.erase(line.find_last_of(" ")); + out << line << '\n'; + } + line = word; + } + } + out << line; +} + +int main(int argc, char* argv[]) { + static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"list-modules", no_argument, 0, 0}, + {"module", required_argument, 0, 'm'}, + {"set-default-module", required_argument, 0, 0}, + {"list-books", no_argument, 0, 0}, + {"list-chapters", required_argument, 0, 0}, + {"omit-verse-numbers", no_argument, 0, 'o'}, + {"list-installable", optional_argument, 0, 0}, + {"install-network", required_argument, 0, 0}, + {"install-zip", required_argument, 0, 0}, + {"remove-module", required_argument, 0, 0} + }; + int opt, option_index; + string modname; + bool omitVerseNums = false; + bool doListBooks = false; + string listChaptersBook; + string option; + while ((opt = getopt_long(argc, argv, "hm:o", long_options, &option_index)) != -1) { + switch(opt) { + case 'h': + usage(); + return 0; + case 'm': + modname = string(optarg); + break; + case 'o': + omitVerseNums = true; + break; + case 0: + option = string(long_options[option_index].name); + if(option == "list-modules") { + listModules(); + return 0; + } else if(option == "set-default-module") { + setDefaultModule(string(optarg)); + } else if(option == "list-books") { + doListBooks = true; + } else if(option == "list-chapters") { + listChaptersBook = string(optarg); + } else if(option == "list-installable") { + if(optarg == nullptr) { + listInstallable(string()); + } else { + listInstallable(string(optarg)); + } + } else if(option == "install-network") { + installNetwork(string(optarg)); + } else if(option == "install-zip") { + installZip(string(optarg)); + } else if(option == "remove-module") { + removeMod(string(optarg)); + } + break; + default: + usage(); + return 1; + } + } + if(modname.empty()) { + modname = getDefaultModule(); + } + if(doListBooks) { + listBooks(modname); + } + if(! listChaptersBook.empty()) { + listChapters(modname, listChaptersBook); + } + string reference; + while(optind < argc) { + reference += argv[optind++]; + reference += " "; + } + if(reference.empty()) { + // That's all. + return 0; + } + + auto text = libbible::getText(libbible::getPassage(modname, reference)); + int chapter = 0; + int verse = 0; + const char* indent = " "; + bool isNewline = true; + stringstream out; + for(auto tex : text) { + if(!omitVerseNums && tex.chapter != chapter) { + out << tex.book << " Chapter " << tex.chapter << ":\n"; + } + bool isParagraph = false; + bool isIndent = false; + bool isDivineName = false; + bool isJesus = false; + bool isTitle = false; + bool isParallel = false; + bool isPreverse = false; + for(string modifier : tex.modifiers) { + if(modifier == "paragraph") { + isParagraph = true; + } else if (modifier == "line indent0") { + isIndent = true; + } else if (modifier == "divineName") { + isDivineName = true; + } else if (modifier == "wordsOfJesus") { + isJesus = true; + } else if (modifier == "title") { + isTitle = true; + } else if (modifier == "parallel") { + isParallel = true; + } else if (modifier == "preverse") { + isPreverse = true; + } + } + if(isPreverse or isTitle or isParallel) { + // Someday maybe we add this, but for now, omit + tex.text = ""; + } + if(isIndent) { + isParagraph = false; + if(isNewline) { + out << indent; + } + } + if(isParagraph) { + out << indent; + } + if(isDivineName) { + transform(tex.text.begin(), tex.text.end(), tex.text.begin(), ::toupper); + } + if(isJesus) { + out << "\033[;31m"; + } + if(omitVerseNums && tex.verse != verse) { + out << " "; + } else if(!omitVerseNums && tex.verse != verse) { + out << " (" << tex.verse << ") "; + } + chapter = tex.chapter; + verse = tex.verse; + out << tex.text; + if(tex.text.back() == '\n') { + isNewline = true; + } else { + isNewline = false; + } + if(isJesus) { + out << "\033[0m"; + } + } + out << "\n"; + + // Get window size + struct winsize size; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &size); + // size.ws_col is number of columns, or 0 if it's a pipe + int cols = size.ws_col; + // If terminal is too small, treat it like a pipe + if(cols < 10) { + cols = 0; + } + + // Now print + if(cols == 0) { + cout << out.str(); + } else { + stringstream out2; + textWrap(out, out2, cols); + cout << out2.str(); + } + return 0; +} |