aboutsummaryrefslogtreecommitdiff
path: root/src/bible.cc
diff options
context:
space:
mode:
authorYour Name <you@example.com>2022-02-18 20:35:38 -0500
committerYour Name <you@example.com>2022-02-18 20:35:38 -0500
commit55d58a16e2511741cc625e203205dec86144faf3 (patch)
tree311be7e5fbaf1bc8ece47dd4261af053f2da1c7c /src/bible.cc
parentaa9dabdeaead3c8b1b11f9d4f321265c439bfbfc (diff)
downloadlibbible-55d58a16e2511741cc625e203205dec86144faf3.tar.gz
libbible-55d58a16e2511741cc625e203205dec86144faf3.tar.bz2
libbible-55d58a16e2511741cc625e203205dec86144faf3.zip
Reorganized repository layout
Diffstat (limited to 'src/bible.cc')
-rw-r--r--src/bible.cc329
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;
+}