1
0
Fork 0

Restructure option/config handling code, to avoid multiple scans & parses for special options (fg-root/fg-aircraft/aircraft). Push most of the code into a new Options class, inside options.cxx, and clean up various call-sites as a result.

This commit is contained in:
James Turner 2011-10-16 18:35:40 +01:00
parent 3d544fbc1f
commit b1c7495fec
9 changed files with 845 additions and 767 deletions

View file

@ -38,6 +38,10 @@
# include <fpu_control.h>
#endif
#ifndef _WIN32
# include <unistd.h> // for gethostname()
#endif
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
@ -61,14 +65,8 @@ using std::endl;
#include "fg_os.hxx"
#ifdef _MSC_VER
char homepath[256] = "";
char * homedir = homepath;
char *hostname = ::getenv( "COMPUTERNAME" );
#else
char *homedir = ::getenv( "HOME" );
char *hostname = ::getenv( "HOSTNAME" );
#endif
char *homedir = NULL;
char *hostname = NULL;
bool free_hostname = false;
// foreward declaration.
@ -179,9 +177,23 @@ int main ( int argc, char **argv ) {
// Windows has no $HOME aka %HOME%, so we have to construct the full path.
// make sure it fits into the buffer. Max. path length is 255, but who knows
// what's in these environment variables?
char homepath[256] = "";
homepath[sizeof(homepath)-1] = 0;
strncpy( homepath, ::getenv("APPDATA"), sizeof(homepath)-1 );
strncat( homepath, "\\flightgear.org", sizeof(homepath)-strlen(homepath)-1 );
homedir = strdup(homepath);
hostname = ::getenv( "COMPUTERNAME" );
#else
// Unix(alike) systems
char _hostname[256];
gethostname(_hostname, 256);
hostname = strdup(_hostname);
free_hostname = true;
homedir = ::getenv( "HOME" );
signal(SIGPIPE, SIG_IGN);
#endif
#ifdef PTW32_STATIC_LIB
@ -203,9 +215,6 @@ int main ( int argc, char **argv ) {
}
initFPE();
#endif
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
#if defined(sgi)
flush_fpe();
@ -230,6 +239,7 @@ int main ( int argc, char **argv ) {
#if defined( HAVE_BC5PLUS )
_control87(MCW_EM, MCW_EM); /* defined in float.h */
#endif
bool fgviewer = false;
for (int i = 0; i < argc; ++i) {
if (!strcmp("--fgviewer", argv[i])) {

View file

@ -121,225 +121,9 @@
#include "main.hxx"
#ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
#endif
using std::string;
using namespace boost::algorithm;
extern const char *default_root;
// Scan the command line options for the specified option and return
// the value.
static string fgScanForOption( const string& option, int argc, char **argv ) {
int i = 1;
if (hostname == NULL)
{
char _hostname[256];
if( gethostname(_hostname, 256) >= 0 ) {
hostname = strdup(_hostname);
free_hostname = true;
}
}
SG_LOG(SG_GENERAL, SG_INFO, "Scanning command line for: " << option );
int len = option.length();
while ( i < argc ) {
SG_LOG( SG_GENERAL, SG_DEBUG, "argv[" << i << "] = " << argv[i] );
string arg = argv[i];
if ( arg.find( option ) == 0 ) {
return arg.substr( len );
}
i++;
}
return "";
}
// Scan the user config files for the specified option and return
// the value.
static string fgScanForOption( const string& option, const string& path ) {
sg_gzifstream in( path );
if ( !in.is_open() ) {
return "";
}
SG_LOG( SG_GENERAL, SG_INFO, "Scanning " << path << " for: " << option );
int len = option.length();
in >> skipcomment;
while ( ! in.eof() ) {
string line;
getline( in, line, '\n' );
// catch extraneous (DOS) line ending character
if ( line[line.length() - 1] < 32 ) {
line = line.substr( 0, line.length()-1 );
}
if ( line.find( option ) == 0 ) {
return line.substr( len );
}
in >> skipcomment;
}
return "";
}
// Scan the user config files for the specified option and return
// the value.
static string fgScanForOption( const string& option ) {
string arg("");
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
// Next check home directory for .fgfsrc.hostname file
if ( arg.empty() ) {
if ( homedir != NULL && hostname != NULL && strlen(hostname) > 0) {
SGPath config( homedir );
config.append( ".fgfsrc" );
config.concat( "." );
config.concat( hostname );
arg = fgScanForOption( option, config.str() );
}
}
#endif
// Next check home directory for .fgfsrc file
if ( arg.empty() ) {
if ( homedir != NULL ) {
SGPath config( homedir );
config.append( ".fgfsrc" );
arg = fgScanForOption( option, config.str() );
}
}
if ( arg.empty() ) {
// Check for $fg_root/system.fgfsrc
SGPath config( globals->get_fg_root() );
config.append( "system.fgfsrc" );
arg = fgScanForOption( option, config.str() );
}
return arg;
}
// Read in configuration (files and command line options) but only set
// fg_root and aircraft_paths, which are needed *before* do_options() is called
// in fgInitConfig
bool fgInitFGRoot ( int argc, char **argv ) {
string root;
// First parse command line options looking for --fg-root=, this
// will override anything specified in a config file
root = fgScanForOption( "--fg-root=", argc, argv);
// Check in one of the user configuration files.
if (root.empty() )
root = fgScanForOption( "--fg-root=" );
// Next check if fg-root is set as an env variable
if ( root.empty() ) {
char *envp = ::getenv( "FG_ROOT" );
if ( envp != NULL ) {
root = envp;
}
}
// Otherwise, default to a random compiled-in location if we can't
// find fg-root any other way.
if ( root.empty() ) {
#if defined( __CYGWIN__ )
root = "../data";
#elif defined( _WIN32 )
root = "..\\data";
#elif defined(__APPLE__)
/*
The following code looks for the base package inside the application
bundle, in the standard Contents/Resources location.
*/
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
// look for a 'data' subdir
CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
// now convert down to a path, and the a c-string
CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
CFRelease(resourcesUrl);
CFRelease(dataDir);
CFRelease(path);
#else
root = PKGLIBDIR;
#endif
}
SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
globals->set_fg_root(root);
return true;
}
// Read in configuration (files and command line options) but only set
// aircraft
bool fgInitFGAircraft ( int argc, char **argv ) {
string aircraftDir = fgScanForOption("--fg-aircraft=", argc, argv);
if (aircraftDir.empty()) {
aircraftDir = fgScanForOption("--fg-aircraft=");
}
const char* envp = ::getenv("FG_AIRCRAFT");
if (aircraftDir.empty() && envp) {
globals->append_aircraft_paths(envp);
}
if (!aircraftDir.empty()) {
globals->append_aircraft_paths(aircraftDir);
}
string aircraft;
// First parse command line options looking for --aircraft=, this
// will override anything specified in a config file
aircraft = fgScanForOption( "--aircraft=", argc, argv );
if ( aircraft.empty() ) {
// check synonym option
aircraft = fgScanForOption( "--vehicle=", argc, argv );
}
// Check in one of the user configuration files.
if ( aircraft.empty() ) {
aircraft = fgScanForOption( "--aircraft=" );
}
if ( aircraft.empty() ) {
aircraft = fgScanForOption( "--vehicle=" );
}
// if an aircraft was specified, set the property name
if ( !aircraft.empty() ) {
SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
fgSetString("/sim/aircraft", aircraft.c_str() );
} else {
SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
}
return true;
}
// Return the current base package version
string fgBasePackageVersion() {
@ -477,45 +261,6 @@ bool fgDetectLanguage() {
return true;
}
// Attempt to locate and parse the various non-XML config files in order
// from least precidence to greatest precidence
static void
do_options (int argc, char ** argv)
{
// Check for $fg_root/system.fgfsrc
SGPath config( globals->get_fg_root() );
config.append( "system.fgfsrc" );
fgParseOptions(config.str());
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
if( hostname != NULL && strlen(hostname) > 0 ) {
config.concat( "." );
config.concat( hostname );
fgParseOptions(config.str());
}
#endif
// Check for ~/.fgfsrc
if ( homedir != NULL ) {
config.set( homedir );
config.append( ".fgfsrc" );
fgParseOptions(config.str());
}
#if defined( unix ) || defined( __CYGWIN__ ) || defined(_MSC_VER)
if( hostname != NULL && strlen(hostname) > 0 ) {
// Check for ~/.fgfsrc.hostname
config.concat( "." );
config.concat( hostname );
fgParseOptions(config.str());
}
#endif
// Parse remaining command line options
// These will override anything specified in a config file
fgParseArgs(argc, argv);
}
template <class T>
bool fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p))
{
@ -709,9 +454,8 @@ private:
// Read in configuration (file and command line)
bool fgInitConfig ( int argc, char **argv ) {
// First, set some sane default values
fgSetDefaults();
flightgear::Options::sharedInstance()->init(argc, argv);
// Read global preferences from $FG_ROOT/preferences.xml
SG_LOG(SG_INPUT, SG_INFO, "Reading global preferences");
fgLoadProps("preferences.xml", globals->get_props());
@ -761,8 +505,9 @@ bool fgInitConfig ( int argc, char **argv ) {
}
}
// Scan user config files and command line for a specified aircraft.
fgInitFGAircraft(argc, argv);
// Scan user config files and command line for a specified aircraft.
flightgear::Options::sharedInstance()->initAircraft();
FindAndCacheAircraft f(&autosave);
if (!f.loadAircraft()) {
return false;
@ -772,8 +517,8 @@ bool fgInitConfig ( int argc, char **argv ) {
// parse options after loading aircraft to ensure any user
// overrides of defaults are honored.
do_options(argc, argv);
flightgear::Options::sharedInstance()->processOptions();
return true;
}

View file

@ -32,11 +32,6 @@ class SGPropertyNode;
class SGTime;
class SGPath;
// Read in configuration (files and command line optoins) but only set
// fg_root
bool fgInitFGRoot ( int argc, char **argv );
// Return the current base package version
std::string fgBasePackageVersion();

View file

@ -188,7 +188,6 @@ fgviewerMain(int argc, char** argv)
globals = new FGGlobals;
fgInitFGRoot(arguments.argc(), arguments.argv());
if ( !fgInitConfig(arguments.argc(), arguments.argv()) ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
exit(-1);

View file

@ -24,6 +24,9 @@
# include <config.h>
#endif
#include <boost/foreach.hpp>
#include <algorithm>
#include <simgear/structure/commands.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
@ -236,26 +239,30 @@ void FGGlobals::set_fg_root (const string &root) {
simgear::ResourceManager::PRIORITY_DEFAULT);
}
void FGGlobals::set_fg_scenery (const string &scenery)
void FGGlobals::append_fg_scenery (const string &paths)
{
SGPath s;
if (scenery.empty()) {
s.set( fg_root );
s.append( "Scenery" );
} else
s.set( scenery );
string_list path_list = sgPathSplit( s.str() );
fg_scenery.clear();
// fg_scenery.clear();
SGPropertyNode* sim = fgGetNode("/sim", true);
for (unsigned i = 0; i < path_list.size(); i++) {
SGPath path(path_list[i]);
// find first unused fg-scenery property in /sim
int propIndex = 0;
while (sim->getChild("fg-scenery", propIndex) != NULL) {
++propIndex;
}
BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
if (!path.exists()) {
SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << path.str());
continue;
}
// check for duplicates
string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), path.str());
if (ex != fg_scenery.end()) {
SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << path.str());
continue;
}
simgear::Dir dir(path);
SGPath terrainDir(dir.file("Terrain"));
SGPath objectsDir(dir.file("Objects"));
@ -280,8 +287,7 @@ void FGGlobals::set_fg_scenery (const string &scenery)
fg_scenery.push_back("");
// make scenery dirs available to Nasal
sim->removeChild("fg-scenery", i, false);
SGPropertyNode* n = sim->getChild("fg-scenery", i, true);
SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
n->setStringValue(path.str());
n->setAttribute(SGPropertyNode::WRITE, false);
} // of path list iteration

View file

@ -194,7 +194,7 @@ public:
void set_fg_root (const std::string &root);
inline const string_list &get_fg_scenery () const { return fg_scenery; }
void set_fg_scenery (const std::string &scenery);
void append_fg_scenery (const std::string &scenery);
const string_list& get_aircraft_paths() const { return fg_aircraft_dirs; }
void append_aircraft_path(const std::string& path);

View file

@ -609,36 +609,12 @@ int fgMainInit( int argc, char **argv ) {
upper_case_property("/sim/tower/airport-id");
upper_case_property("/autopilot/route-manager/input");
// Scan the config file(s) and command line options to see if
// fg_root was specified (ignore all other options for now)
fgInitFGRoot(argc, argv);
// Check for the correct base package version
static char required_version[] = "2.5.0";
string base_version = fgBasePackageVersion();
if ( !(base_version == required_version) ) {
// tell the operator how to use this application
SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
cerr << endl << "Base package check failed:" << endl \
<< " Version " << base_version << " found at: " \
<< globals->get_fg_root() << endl \
<< " Version " << required_version << " is required." << endl \
<< "Please upgrade/downgrade base package and set the path to your fgdata" << endl \
<< "with --fg-root=path_to_your_fgdata" << endl;
#ifdef _MSC_VER
cerr << "Hit a key to continue..." << endl;
cin.get();
#endif
exit(-1);
}
// Load the configuration parameters. (Command line options
// override config file options. Config file options override
// defaults.)
if ( !fgInitConfig(argc, argv) ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
exit(-1);
SG_LOG( SG_GENERAL, SG_ALERT, "Config option parsing failed ..." );
exit(-1);
}
// Initialize the Window/Graphics environment.

File diff suppressed because it is too large Load diff

View file

@ -24,14 +24,85 @@
#ifndef _OPTIONS_HXX
#define _OPTIONS_HXX
#include <memory>
#include <string>
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/misc/strutils.hxx>
extern void fgSetDefaults ();
extern void fgParseArgs (int argc, char ** argv);
extern void fgParseOptions (const string &file_path);
extern void fgUsage (bool verbose = false);
// forward decls
class SGPath;
namespace flightgear
{
class Options
{
private:
Options();
public:
static Options* sharedInstance();
~Options();
/**
* pass command line arguments, read default config files
*/
void init(int argc, char* argv[]);
/**
* parse a config file (eg, .fgfsrc)
*/
void readConfig(const SGPath& path);
/**
* read the value for an option, if it has been set
*/
std::string valueForOption(const std::string& key, const std::string& defValue = std::string()) const;
/**
* return all values for a multi-valued option
*/
string_list valuesForOption(const std::string& key) const;
/**
* check if a particular option has been set (so far)
*/
bool isOptionSet(const std::string& key) const;
/**
* set an option value, assuming it is not already set (or multiple values
* are permitted)
* This can be used to inject option values, eg based upon environment variables
*/
int addOption(const std::string& key, const std::string& value);
/**
* apply option values to the simulation state
* (set properties, etc)
*/
void processOptions();
/**
* init the aircraft options
*/
void initAircraft();
private:
void showUsage() const;
int parseOption(const std::string& s);
void processArgResult(int result);
void setupRoot();
std::string platformDefaultRoot() const;
class OptionsPrivate;
std::auto_ptr<OptionsPrivate> p;
};
} // of namespace flightgear
#endif /* _OPTIONS_HXX */