diff --git a/.gitignore b/.gitignore index 2a614aa..2ffc87e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ *.out *.app -push-checklist.org \ No newline at end of file +push-checklist.org +scriptura diff --git a/scabbard.cpp b/scabbard.cpp index ffde80e..98943d7 100644 --- a/scabbard.cpp +++ b/scabbard.cpp @@ -20,10 +20,15 @@ #include #include +#include +#include #include "scabbard.h" scabbard::scabbard() { + // initialize the thing! + swrd = *(new sword::SWMgr()); + /* store what markup information the user has requested be shown * SWBuf.c_str() returns the ascii code 48 for '0', so we have to do some * math to get the proper boolean setting */ @@ -31,9 +36,11 @@ scabbard::scabbard() { markstrongs = parseConf(config["markup"]["strongs"]); markfoot = parseConf(config["markup"]["footnotes"]); - /* everything following is based on constructing the menu used to display - * to the reader about what modules the system has - start with a - * temporary array of module types */ + // construct the menu list of modules + constructModlist(); +} + +void scabbard::constructModlist() { int tmpnum = 4; modtype tmpmods[4]; strcpy(tmpmods[0].label, "Generic Books"); @@ -187,6 +194,21 @@ const char* scabbard::getModDescription(const char* modname) { return text->getDescription(); } +int scabbard::modExists(const char* modname) { + sword::SWModule *text = swrd.getModule(modname); + return (text == 0 ? 0 : 1); +} + +starray scabbard::getModules() { + starray retval; + retval = stinit(retval); + for (iter = swrd.Modules.begin(); iter != swrd.Modules.end(); iter++) { + sword::SWModule *mod = iter->second; + retval = stappend(retval, mod->getName()); + } + return retval; +} + int scabbard::getKeyType(int modt) { return modules[modt].keytype; } @@ -228,6 +250,87 @@ const char* scabbard::search(modkey mod) { return directSearch(text, mod); } +void scabbard::installKJVA() { + sword::SWBuf swordhome = getenv("HOME"); + swordhome += "/.sword"; + + // ensure some directories exist -- apparently installmgr doesn't create them + if ((mkdir(swordhome + "/mods.d", 00755) != 0) && (errno != EEXIST)) { + printf("Error creating directory to perform the install:\n"); + printf("~/.sword/mods.d\n"); + printf("Please create manually or check directory permissions.\n"); + exit(0); + } + sword::SWBuf finaldir = swordhome.c_str(); + finaldir += "/modules"; + mkdir(finaldir.c_str(), 00755); + finaldir += "/texts"; + mkdir(finaldir.c_str(), 00755); + finaldir += "/ztext"; + mkdir(finaldir.c_str(), 00755); + finaldir += "/kjva"; + if ((mkdir(finaldir.c_str(), 00755) != 0) && (errno != EEXIST)) { + printf("Error creating directory to perform the install:\n"); + printf("~/.sword/modules/texts/ztext/kjva\n"); + printf("Please create manually or check directory permissions.\n"); + exit(0); + } + printf("Directories created for new module.\n"); + + // installmgr -init + sword::InstallMgr* im = new sword::InstallMgr(swordhome + "/InstallMgr"); + im->setUserDisclaimerConfirmed(true); + + // sync config (installmgr -sc) + sword::InstallSource is("FTP"); + is.caption = "CrossWire"; + is.source = "ftp.crosswire.org"; + is.directory = "/pub/sword/raw"; + + sword::SWConfig installconf(swordhome + "/InstallMgr/InstallMgr.conf"); + if (! installconf["General"]["PassiveFTP"]) + installconf["General"]["PassiveFTP"] = "true"; + if (! installconf["General"]["TimeoutMillis"]) + installconf["General"]["TimeoutMillis"] = "10000"; + if (! installconf["General"]["UnverifiedPeerAllowed"]) + installconf["General"]["UnverifiedPeerAllowed"] = false; + if (! installconf["General"]["FTPSource"]) + installconf["Sources"]["FTPSource"] = is.getConfEnt(); + installconf.save(); + im->refreshRemoteSourceConfiguration(); + + /* refresh remote source (installmgr -r) + * apparently we can't reuse our prior declared InstallSource here */ + sword::InstallSourceMap::iterator source = im->sources.find("CrossWire"); + sword::InstallSource *is2 = source->second; + if (source == im->sources.end()) { + fprintf(stderr, "Couldn't find remote source [%s]\n", "CrossWire"); + } + + if (int refresh = im->refreshRemoteSource(is2)) { + printf("Refresh call failed, error: %d\n", refresh); + printf("Most likely this is an InstalMgr configuration issue.\n"); + printf("Please ensure your SWORD environment is properly configured,\n"); + printf(" or download & install the module manually before trying again.\n"); + exit(0); + } + + // install module (installmgr -ri CrossWire KJVA) + sword::SWMgr* rmgr = is2->getMgr(); + sword::SWModule* modname = rmgr->getModule("KJVA"); + if (im->installModule(new sword::SWMgr(), 0, modname->getName(), is2)) { + printf("Error installing module.\n"); + printf("Most likely this is an environment configuration issue.\n"); + printf("Please ensure your SWORD environment is properly configured,\n"); + printf(" or download & install the module manually before trying again.\n"); + exit(0); + } + + // update internal module list & refresh SWMgr w/installed module info + swrd = *(new sword::SWMgr()); + constructModlist(); +} + /* really old code in case we need to pull output from diatheke FILE *modlist = popen("diatheke -b system -k modulelist", "r"); char buf[1024]; diff --git a/scabbard.h b/scabbard.h index 2dff922..a2ff93f 100644 --- a/scabbard.h +++ b/scabbard.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,9 @@ class scabbard { //!< modkey containing the search term ); + //! Construct the internal list of modules used in the selection menu. + void constructModlist(); + public: //! Takes as input various preferences on text markup from our config file scabbard(); @@ -113,11 +117,20 @@ class scabbard { int mod //!< from getModDescriptions() ); - /*! Get the full text name for a module identified by key name. + /*! Get the full text name for a module identified by key name. Vulnerable + * to malicious input. * \returns the module description */ const char* getModDescription(const char* modname //!< module internal name ); + //! Return an array of all module names. + starray getModules(); + + /*! Given a text name for a module, determine if it exists. + */ + int modExists(const char* modname //!< module internal name + ); + /*! Determine how a module is keyed/indexed. * \returns the proper keytype; see the keytype field for modkey */ int getKeyType(int modt //!< return value from getModClassifications() @@ -129,11 +142,14 @@ class scabbard { ); /*! Search for a term or passage. - * \returns the text requested, or in the case of a non-versekeyed text, - * the nearest alphabetical entry to the search term supplied */ + * \returns the text requested, or in the case of a non-versekeyed text, + * the nearest alphabetical entry to the search term supplied */ const char* search(modkey mod //!< the modkey supplied by the pane ); + //! Install KJVA from ftp.crosswire.org. + void installKJVA(); + }; #endif diff --git a/scriptura.cpp b/scriptura.cpp index 51bf9eb..b225796 100644 --- a/scriptura.cpp +++ b/scriptura.cpp @@ -205,16 +205,50 @@ int main(int argc, char** argv) { } config = *(new sword::SWConfig(configfile)); - // ensure some needed config settings are in place + /* load up a scabbard & set blank starting data - scabbard args are + * sanitized in the constructor */ + scabbard scab = {}; + + /* before we render anything, ensure some needed config settings are in + * place & sanitized, with needed changes saved back to file */ if (! strcmp(config["layout"]["panels"], "")) config["layout"]["panels"] = "v"; if (! strcmp(config["defaults"]["scope"], "")) config["defaults"]["scope"] = "Gen 1:1 - Rev 22:21"; if (! strcmp(config["defaults"]["searchkey"], "")) config["defaults"]["searchkey"] = "2 Tim 1:7"; - if (! strcmp(config["defaults"]["module"], "")) - // XXX -- should load install manager, and if default module is present, verify it - config["defaults"]["module"] = "KJVA"; + if ((! strcmp(config["defaults"]["module"], "")) || + (! scab.modExists(config["defaults"]["module"]))) { + /* prompt for a default module - we're on partial startup here and + * still outside of ncurses, so we do this the old fashioned way. */ + printf("You either do not have a default module selected, " + "or what you have chosen is not installed.\n"); + printf("Please select one before continuing.\n\n"); + + printf("0 - Install KJVA from ftp.crosswire.org.\n"); + starray mods = scab.getModules(); + for (int i = 0; i < mods.length; i++) { + const char* longname = scab.getModDescription(mods.strings[i]); + printf("%d - %s\n", i+1, longname); + } + printf("\nDefault module to choose: "); + + char readit[4]; + fgets(readit, 4, stdin); + + long selection = 0; + selection = strtol(readit, NULL, 10); + + const char* defmod; + if ((selection == 0) || (selection > mods.length)) { + printf("Installing from ftp.crosswire.org...\n"); + scab.installKJVA(); + defmod = "KJVA"; + printf("done.\n"); + } else defmod = mods.strings[selection - 1]; + + config["defaults"]["module"] = defmod; + } if (! strcmp(config["defaults"]["searchtype"], "")) { char* temp; asprintf(&temp, "%d", DEFSEARCH); @@ -233,6 +267,7 @@ int main(int argc, char** argv) { * keys & so on */ initscr(); cbreak(); + curs_set(0); noecho(); start_color(); @@ -275,9 +310,10 @@ int main(int argc, char** argv) { config[pages[i][j]]["searchkey"] = config["defaults"]["searchkey"]); } - // XXX -- confirm this module actually exists; if not, then use our default because we've already sanitized that - if (linking && (! strcmp(config[pages[i][j]]["module"], ""))) + if (linking && ((! strcmp(config[pages[i][j]]["module"], "")) || + (! scab.modExists(config[pages[i][j]]["module"])))) { config[pages[i][j]]["module"] = config["defaults"]["module"]; + } if (linking && (! strcmp(config[pages[i][j]]["keytype"], ""))) config[pages[i][j]]["keytype"] = config["defaults"]["keytype"]; @@ -296,11 +332,7 @@ int main(int argc, char** argv) { // set index for page list currentpage = 0; - /* load up a scabbard & set blank starting data - scabbard args are - * sanitized in the constructor */ - scabbard scab = {}; - - // configure panes + // initialize panes and render landmarks(); pane* p = (pane*) malloc(sizeof(pane) * NUMPANES); if (! p) wrapup(1, "Error allocating memory for panes.\n");