1
0
Fork 0

Rewrite --show-aircraft and --aircraft handling to share directory search code.

As a bonus, sort the --show-aircraft list case-insensitively.
This commit is contained in:
James Turner 2010-07-06 21:06:50 +01:00
parent af3f072c21
commit c95a5db23d
4 changed files with 276 additions and 240 deletions

View file

@ -57,6 +57,7 @@
#include <simgear/structure/exception.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/interpolator.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/model/particles.hxx>
@ -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 <class T>
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;
}
string result;
while ((dire = ulReadDir(dirp)) != NULL) {
if (dire->d_isdir) {
if (depth > MAXDEPTH)
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;
}
} // 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;
}
if (!strcmp("CVS", dire->d_name) || !strcmp(".", dire->d_name)
|| !strcmp("..", dire->d_name) || !strcmp("AI", dire->d_name))
fgFindAircraftInDir(*p, obj, pred);
}
} // 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<SGPropertyNode_ptr> 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 next = path;
next.append(dire->d_name);
SGPath xml(cache[i]->getStringValue("path", ""));
xml.append(name);
if (xml.exists()) {
_foundPath = xml;
return true;
}
result = fgFindAircraftPath( next, aircraft, cache, depth + 1 );
if ( ! result.empty() )
break;
return false;
} // of aircraft in cache iteration
} else {
int len = strlen(dire->d_name);
if (len < 9 || strcmp(dire->d_name + len - 8, "-set.xml"))
continue;
return false;
}
bool checkAircraft(const SGPath& p)
{
// create cache node
int i = 0;
while (1)
if (!cache->getChild("aircraft", i++, false))
while (1) {
if (!_cache->getChild("aircraft", i++, false))
break;
}
SGPropertyNode *n, *entry = cache->getChild("aircraft", --i, true);
SGPropertyNode *n, *entry = _cache->getChild("aircraft", --i, true);
std::string fileName(p.file());
n = entry->getNode("file", true);
n->setStringValue(dire->d_name);
n->setStringValue(fileName);
n->setAttribute(SGPropertyNode::USERARCHIVE, true);
n = entry->getNode("path", true);
n->setStringValue(path.str().c_str());
n->setStringValue(p.dir());
n->setAttribute(SGPropertyNode::USERARCHIVE, true);
if ( boost::equals(dire->d_name, aircraft.c_str(), is_iequal()) ) {
result = path.str();
break;
}
}
if ( boost::equals(fileName, _searchAircraft.c_str(), is_iequal()) ) {
_foundPath = p;
return true;
}
ulCloseDir(dirp);
return result;
}
return false;
}
std::string _searchAircraft;
SGPath _foundPath;
SGPropertyNode* _cache;
};
// Read in configuration (file and command line)
bool fgInitConfig ( int argc, char **argv ) {
@ -606,79 +692,24 @@ bool fgInitConfig ( int argc, char **argv ) {
home->setAttribute(SGPropertyNode::WRITE, false);
config.append( "autosave.xml" );
if (config.exists()) {
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");
} 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<SGPropertyNode_ptr> 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 );
FindAndCacheAircraft f(&autosave);
if (!f.loadAircraft()) {
return false;
}
} else {
SG_LOG( SG_INPUT, SG_ALERT, "No default aircraft specified" );
}
copyProperties(&autosave, globals->get_props());
// parse options after loading aircraft to ensure any user
@ -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<string, string, bool>
{
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
}

View file

@ -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 <aircraft>-set.xml configuration files and show the aircaft name
* and the contents of the<description> tag in a sorted manner.
*
* @parampath the directory to search for configuration files
*/
void fgShowAircraft(const SGPath &path);
#endif // _FG_INIT_HXX

View file

@ -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 <aircraft>-set.xml configuration files and show the aircaft name
* and the contents of the<description> 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
}

View file

@ -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 */