From c95a5db23d3b3f10f8e45de57e8dfb34c4261161 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 6 Jul 2010 21:06:50 +0100 Subject: [PATCH] Rewrite --show-aircraft and --aircraft handling to share directory search code. As a bonus, sort the --show-aircraft list case-insensitively. --- src/Main/fg_init.cxx | 390 +++++++++++++++++++++++++++++-------------- src/Main/fg_init.hxx | 9 + src/Main/options.cxx | 116 +------------ src/Main/options.hxx | 1 - 4 files changed, 276 insertions(+), 240 deletions(-) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 090be2619..6f2f48411 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -499,68 +500,153 @@ do_options (int argc, char ** argv) fgParseArgs(argc, argv); } - -static string fgFindAircraftPath( const SGPath &path, const string &aircraft, - SGPropertyNode *cache, int depth = 0 ) +template +void fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p)) { - const int MAXDEPTH = 1; - - ulDirEnt* dire; - ulDir *dirp = ulOpenDir(path.str().c_str()); - if (dirp == NULL) { - cerr << "Unable to open aircraft directory '" << path.str() << '\'' << endl; - exit(-1); + if (!dirPath.exists()) { + SG_LOG(SG_GENERAL, SG_WARN, "fgFindAircraftInDir: no such path:" << dirPath.str()); + return; + } + + bool recurse = true; + simgear::Dir dir(dirPath); + simgear::PathList setFiles(dir.children(simgear::Dir::TYPE_FILE, "-set.xml")); + simgear::PathList::iterator p; + for (p = setFiles.begin(); p != setFiles.end(); ++p) { + // check file name ends with -set.xml + + // if we found a -set.xml at this level, don't recurse any deeper + recurse = false; + + bool done = (obj->*pred)(*p); + if (done) { + return; } - - string result; - while ((dire = ulReadDir(dirp)) != NULL) { - if (dire->d_isdir) { - if (depth > MAXDEPTH) - continue; - - if (!strcmp("CVS", dire->d_name) || !strcmp(".", dire->d_name) - || !strcmp("..", dire->d_name) || !strcmp("AI", dire->d_name)) - continue; - - SGPath next = path; - next.append(dire->d_name); - - result = fgFindAircraftPath( next, aircraft, cache, depth + 1 ); - if ( ! result.empty() ) - break; - - } else { - int len = strlen(dire->d_name); - if (len < 9 || strcmp(dire->d_name + len - 8, "-set.xml")) - continue; - - // create cache node - int i = 0; - while (1) - if (!cache->getChild("aircraft", i++, false)) - break; - - SGPropertyNode *n, *entry = cache->getChild("aircraft", --i, true); - - n = entry->getNode("file", true); - n->setStringValue(dire->d_name); - n->setAttribute(SGPropertyNode::USERARCHIVE, true); - - n = entry->getNode("path", true); - n->setStringValue(path.str().c_str()); - n->setAttribute(SGPropertyNode::USERARCHIVE, true); - - if ( boost::equals(dire->d_name, aircraft.c_str(), is_iequal()) ) { - result = path.str(); - break; - } - } + } // of -set.xml iteration + + if (recurse) { + simgear::PathList subdirs(dir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT)); + for (p = subdirs.begin(); p != subdirs.end(); ++p) { + if (p->file() == "CVS") { + continue; + } + + fgFindAircraftInDir(*p, obj, pred); } - - ulCloseDir(dirp); - return result; + } // of recursive case } +class FindAndCacheAircraft +{ +public: + FindAndCacheAircraft(SGPropertyNode* autoSave) + { + _cache = autoSave->getNode("sim/startup/path-cache", true); + } + + bool loadAircraft() + { + std::string aircraft = fgGetString( "/sim/aircraft", ""); + if (aircraft.empty()) { + SG_LOG(SG_GENERAL, SG_ALERT, "no aircraft specified"); + return false; + } + + _searchAircraft = aircraft + "-set.xml"; + if (!checkCache()) { + // prepare cache for re-scan + SGPropertyNode *n = _cache->getNode("fg-root", true); + n->setStringValue(globals->get_fg_root().c_str()); + n->setAttribute(SGPropertyNode::USERARCHIVE, true); + _cache->removeChildren("aircraft"); + + SGPath aircraftDir(globals->get_fg_root()); + aircraftDir.append("Aircraft"); + + fgFindAircraftInDir(aircraftDir, this, &FindAndCacheAircraft::checkAircraft); + } + + if (_foundPath.str().empty()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find specified aircraft: " << aircraft ); + return false; + } + + SG_LOG(SG_GENERAL, SG_INFO, "Loading aircraft -set file from:" << _foundPath.str()); + fgSetString( "/sim/aircraft-dir", _foundPath.dir().c_str()); + if (!_foundPath.exists()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Unable to find -set file:" << _foundPath.str()); + return false; + } + + try { + readProperties(_foundPath.str(), globals->get_props()); + } catch ( const sg_exception &e ) { + SG_LOG(SG_INPUT, SG_ALERT, "Error reading aircraft: " << e.getFormattedMessage()); + return false; + } + + return true; + } + +private: + bool checkCache() + { + if (globals->get_fg_root() != _cache->getStringValue("fg-root", "")) { + return false; // cache mismatch + } + + vector cache = _cache->getChildren("aircraft"); + for (unsigned int i = 0; i < cache.size(); i++) { + const char *name = cache[i]->getStringValue("file", ""); + if (!boost::equals(_searchAircraft, name, is_iequal())) { + continue; + } + + SGPath xml(cache[i]->getStringValue("path", "")); + xml.append(name); + if (xml.exists()) { + _foundPath = xml; + return true; + } + + return false; + } // of aircraft in cache iteration + + return false; + } + + bool checkAircraft(const SGPath& p) + { + // create cache node + int i = 0; + while (1) { + if (!_cache->getChild("aircraft", i++, false)) + break; + } + + SGPropertyNode *n, *entry = _cache->getChild("aircraft", --i, true); + + std::string fileName(p.file()); + n = entry->getNode("file", true); + n->setStringValue(fileName); + n->setAttribute(SGPropertyNode::USERARCHIVE, true); + + n = entry->getNode("path", true); + n->setStringValue(p.dir()); + n->setAttribute(SGPropertyNode::USERARCHIVE, true); + + if ( boost::equals(fileName, _searchAircraft.c_str(), is_iequal()) ) { + _foundPath = p; + return true; + } + + return false; + } + + std::string _searchAircraft; + SGPath _foundPath; + SGPropertyNode* _cache; +}; // Read in configuration (file and command line) bool fgInitConfig ( int argc, char **argv ) { @@ -606,77 +692,22 @@ bool fgInitConfig ( int argc, char **argv ) { home->setAttribute(SGPropertyNode::WRITE, false); config.append( "autosave.xml" ); - SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << config.str()); - try { - readProperties(config.str(), &autosave, SGPropertyNode::USERARCHIVE); - } catch (...) { - SG_LOG(SG_INPUT, SG_DEBUG, "First time reading user settings"); + if (config.exists()) { + SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << config.str()); + try { + readProperties(config.str(), &autosave, SGPropertyNode::USERARCHIVE); + } catch (sg_exception& e) { + SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage() + << "(from " << e.getOrigin() << ")"); + } } - SG_LOG(SG_INPUT, SG_DEBUG, "Finished Reading user settings"); } - SGPropertyNode *cache_root = autosave.getNode("sim/startup/path-cache", true); - - + // Scan user config files and command line for a specified aircraft. fgInitFGAircraft(argc, argv); - - string aircraft = fgGetString( "/sim/aircraft", "" ); - if ( aircraft.size() > 0 ) { - SGPath aircraft_search( globals->get_fg_root() ); - aircraft_search.append( "Aircraft" ); - - string aircraft_set = aircraft + "-set.xml"; - string result; - - // check if the *-set.xml file is already in the cache - if (globals->get_fg_root() == cache_root->getStringValue("fg-root", "")) { - vector cache = cache_root->getChildren("aircraft"); - for (unsigned int i = 0; i < cache.size(); i++) { - const char *name = cache[i]->getStringValue("file", ""); - if (boost::equals(aircraft_set, name, is_iequal())) { - const char *path = cache[i]->getStringValue("path", ""); - SGPath xml(path); - xml.append(name); - if (xml.exists()) - result = path; - break; - } - } - } - - if (result.empty()) { - // prepare cache for rescan - SGPropertyNode *n = cache_root->getNode("fg-root", true); - n->setStringValue(globals->get_fg_root().c_str()); - n->setAttribute(SGPropertyNode::USERARCHIVE, true); - cache_root->removeChildren("aircraft"); - - result = fgFindAircraftPath( aircraft_search, aircraft_set, cache_root ); - } - - if ( !result.empty() ) { - fgSetString( "/sim/aircraft-dir", result.c_str() ); - SGPath full_name( result ); - full_name.append( aircraft_set ); - - SG_LOG(SG_INPUT, SG_INFO, "Reading default aircraft: " << aircraft - << " from " << full_name.str()); - try { - readProperties( full_name.str(), globals->get_props() ); - } catch ( const sg_exception &e ) { - string message = "Error reading default aircraft: "; - message += e.getFormattedMessage(); - SG_LOG(SG_INPUT, SG_ALERT, message); - exit(2); - } - } else { - SG_LOG( SG_INPUT, SG_ALERT, "Cannot find specified aircraft: " - << aircraft ); - return false; - } - - } else { - SG_LOG( SG_INPUT, SG_ALERT, "No default aircraft specified" ); + FindAndCacheAircraft f(&autosave); + if (!f.loadAircraft()) { + return false; } copyProperties(&autosave, globals->get_props()); @@ -1707,3 +1738,114 @@ void doSimulatorReset(void) // from gui_local.cxx -- TODO merge with fgReInitSu master_freeze->setBoolValue(false); } +/////////////////////////////////////////////////////////////////////////////// +// helper object to implement the --show-aircraft command. +// resides here so we can share the fgFindAircraftInDir template above, +// and hence ensure this command lists exectly the same aircraft as the normal +// loading path. +class ShowAircraft +{ +public: + ShowAircraft() + { + _minStatus = getNumMaturity(fgGetString("/sim/aircraft-min-status", "all")); + } + + + void show(const SGPath& path) + { + fgFindAircraftInDir(path, this, &ShowAircraft::processAircraft); + + std::sort(_aircraft.begin(), _aircraft.end(), ciLessLibC()); + SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows + cout << "Available aircraft:" << endl; + for ( unsigned int i = 0; i < _aircraft.size(); i++ ) { + cout << _aircraft[i] << endl; + } + } + +private: + bool processAircraft(const SGPath& path) + { + SGPropertyNode root; + try { + readProperties(path.str(), &root); + } catch (sg_exception& e) { + return false; + } + + int maturity = 0; + string descStr(" "); + descStr += path.file(); + + SGPropertyNode *node = root.getNode("sim"); + if (node) { + SGPropertyNode* desc = node->getNode("description"); + // if a status tag is found, read it in + if (node->hasValue("status")) { + maturity = getNumMaturity(node->getStringValue("status")); + } + + if (desc) { + if (descStr.size() <= 27+3) { + descStr.append(29+3-descStr.size(), ' '); + } else { + descStr += '\n'; + descStr.append( 32, ' '); + } + descStr += desc->getStringValue(); + } + } // of have 'sim' node + + if (maturity < _minStatus) { + return false; + } + + _aircraft.push_back(descStr); + return false; + } + + + int getNumMaturity(const char * str) + { + // changes should also be reflected in $FG_ROOT/data/options.xml & + // $FG_ROOT/data/Translations/string-default.xml + const char* levels[] = {"alpha","beta","early-production","production"}; + + if (!strcmp(str, "all")) { + return 0; + } + + for (size_t i=0; i<(sizeof(levels)/sizeof(levels[0]));i++) + if (strcmp(str,levels[i])==0) + return i; + + return 0; + } + + // recommended in Meyers, Effective STL when internationalization and embedded + // NULLs aren't an issue. Much faster than the STL or Boost lex versions. + struct ciLessLibC : public std::binary_function + { + bool operator()(const std::string &lhs, const std::string &rhs) const + { + return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ? 1 : 0; + } + }; + + int _minStatus; + string_list _aircraft; +}; + +void fgShowAircraft(const SGPath &path) +{ + ShowAircraft s; + s.show(path); + +#ifdef _MSC_VER + cout << "Hit a key to continue..." << endl; + cin.get(); +#endif +} + + diff --git a/src/Main/fg_init.hxx b/src/Main/fg_init.hxx index 58b48a8c2..da68ab2e2 100644 --- a/src/Main/fg_init.hxx +++ b/src/Main/fg_init.hxx @@ -30,6 +30,7 @@ // forward decls class SGPropertyNode; class SGTime; +class SGPath; // Read in configuration (files and command line optoins) but only set // fg_root @@ -86,6 +87,14 @@ SGTime *fgInitTime(); // set up a time offset (aka warp) if one is specified void fgInitTimeOffset(); +/* + * Search in the current directory, and in on directory deeper + * for -set.xml configuration files and show the aircaft name + * and the contents of the tag in a sorted manner. + * + * @parampath the directory to search for configuration files + */ +void fgShowAircraft(const SGPath &path); #endif // _FG_INIT_HXX diff --git a/src/Main/options.cxx b/src/Main/options.cxx index d3b282be8..e3dd7ab38 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1642,7 +1642,7 @@ fgParseArgs (int argc, char **argv) fgOptLogLevel( "alert" ); SGPath path( globals->get_fg_root() ); path.append("Aircraft"); - fgShowAircraft(path, true); + fgShowAircraft(path); exit(0); } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) { @@ -1847,117 +1847,3 @@ fgUsage (bool verbose) cin.get(); #endif } - -// A simple function to return an integer depending on the position -// of the status string within the array in order to determine the hierarchy. -unsigned int getNumMaturity(const char * str) -{ - // changes should also be reflected in $FG_ROOT/data/options.xml & - // $FG_ROOT/data/Translations/string-default.xml - const char* levels[] = {"alpha","beta","early-production","production"}; - - if (!strcmp(str, "all")) { - return 0; - } - - for (size_t i=0; i<(sizeof(levels)/sizeof(levels[0]));i++) - if (strcmp(str,levels[i])==0) - return i; - - return 0; -}; - - -static void fgSearchAircraft(const SGPath &path, string_list &aircraft, - bool recursive) -{ - if (!path.exists()) { - SG_LOG(SG_GENERAL, SG_WARN, "fgSearchAircraft: no such path:" << path.str()); - return; - } - - int requiredStatus = getNumMaturity(fgGetString("/sim/aircraft-min-status", "all")); - - simgear::Dir dir(path); - simgear::PathList setFiles(dir.children(simgear::Dir::TYPE_FILE, "-set.xml")); - simgear::PathList::iterator p; - for (p = setFiles.begin(); p != setFiles.end(); ++p) { - // check file name ends with -set.xml - - SGPropertyNode root; - try { - readProperties(p->str(), &root); - } catch (sg_exception& e) { - continue; - } - - int maturity = 0; - string descStr(" "); - descStr += path.file(); - - SGPropertyNode *node = root.getNode("sim"); - if (node) { - SGPropertyNode* desc = node->getNode("description"); - // if a status tag is found, read it in - if (node->hasValue("status")) { - maturity = getNumMaturity(node->getStringValue("status")); - } - - if (desc) { - if (descStr.size() <= 27+3) { - descStr.append(29+3-descStr.size(), ' '); - } else { - descStr += '\n'; - descStr.append( 32, ' '); - } - descStr += desc->getStringValue(); - } - } // of have 'sim' node - - if (maturity < requiredStatus) { - continue; - } - - // if we found a -set.xml at this level, don't recurse any deeper - recursive = false; - - aircraft.push_back(descStr); - } // of -set.xml iteration - - // recurse down if requested - if (recursive) { - simgear::PathList subdirs(dir.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT)); - for (p = subdirs.begin(); p != subdirs.end(); ++p) { - if (p->file() == "CVS") { - continue; - } - - fgSearchAircraft(*p, aircraft, recursive); - } - } // of recursive case -} - -/* - * Search in the current directory, and in on directory deeper - * for -set.xml configuration files and show the aircaft name - * and the contents of the tag in a sorted manner. - * - * @parampath the directory to search for configuration files - * @param recursive defines whether the directory should be searched recursively - */ -void fgShowAircraft(const SGPath &path, bool recursive) { - string_list aircraft; - - fgSearchAircraft( path, aircraft, recursive ); - - sort(aircraft.begin(), aircraft.end()); - SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows - cout << "Available aircraft:" << endl; - for ( unsigned int i = 0; i < aircraft.size(); i++ ) { - cout << aircraft[i] << endl; - } -#ifdef _MSC_VER - cout << "Hit a key to continue..." << endl; - cin.get(); -#endif -} diff --git a/src/Main/options.hxx b/src/Main/options.hxx index 2bdb86da5..4ad8b133d 100644 --- a/src/Main/options.hxx +++ b/src/Main/options.hxx @@ -33,6 +33,5 @@ extern void fgSetDefaults (); extern void fgParseArgs (int argc, char ** argv); extern void fgParseOptions (const string &file_path); extern void fgUsage (bool verbose = false); -extern void fgShowAircraft(const SGPath &path, bool recursive); #endif /* _OPTIONS_HXX */