#include "lib/libbible.h" #include #include #include #include #include #include #include #include #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 use specified module\n"); printf(" --set-default-module use specified module by default in future runs\n"); printf(" --list-books list books available in the current module\n"); printf(" --list-chapters 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(" -s, --superscript when printing verse text, print verse numbers as superscript\n"); printf(" --list-installable= list bible versions available for download and install. Default lists for all languages.\n"); printf(" --install-network install module from the network where is LANG:NAME as provided by --list-installable\n"); printf(" --install-zip install module from a zip file\n"); printf(" --remove-module 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() { map> mods = libbible::getModules(); string mod = libbible::settingsRead("module"); if(mods.count(mod) > 0) { return mod; } if(!mods.empty()) { // Just get one for(auto pair : mods) { return pair.first.c_str(); } } printf("ERROR: No mods installed!\n"); exit(1); } void listModules() { map> 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> 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> installable = libbible::downloadModsAvailable(); map 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'}, {"superscript", no_argument, 0, 's'}, {"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 superscript = false; bool doListBooks = false; string listChaptersBook; string option; while ((opt = getopt_long(argc, argv, "hm:os", long_options, &option_index)) != -1) { switch(opt) { case 'h': usage(); return 0; case 'm': modname = string(optarg); break; case 'o': omitVerseNums = true; break; case 's': superscript = 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) { if(superscript) { out << " "; map d2s = {{'0', "⁰"}, {'1', "¹"}, {'2', "²"}, {'3', "³"}, {'4', "⁴"}, {'5', "⁵"}, {'6', "⁶"}, {'7', "⁷"}, {'8', "⁸"}, {'9', "⁹"}}; for(auto &d : to_string(tex.verse)) { out << d2s[d]; } } else { out << " (" << tex.verse << ") "; } } chapter = tex.chapter; verse = tex.verse; out << tex.text; if(!tex.text.empty() and 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; }