1
0
Fork 0
flightgear/utils/fgcom/fgcom_init.cxx

691 lines
17 KiB
C++
Raw Normal View History

//
// fgcom_init.cxx -- FGCOM configuration parsing and initialization
// FGCOM: Copyright (C) H. Wirtz <wirtz@dfn.de>
//
// Adaption of fg_init.cxx from FlightGear
// FlightGear: Copyright (C) 1997 Curtis L. Olson - http://www.flightgear.org/~curt
//
// Huge part rewritten by Tobias Ramforth to fit needs of FGCOM.
// <tobias@ramforth.com>
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined( _MSC_VER) || defined(__MINGW32__)
# include <direct.h> // for getcwd()
# define getcwd _getcwd
#endif
#ifdef _MSC_VER
#include "fgcom_getopt.h"
#else
#include <pwd.h>
#endif
#include <iostream>
#include <fstream>
#include <iomanip>
#include <map>
#include "fgcom_init.hxx"
#include "fgcom.hxx"
#include "utils.hxx"
#include <simgear/debug/logstream.hxx>
using namespace std;
using std::string;
using std::cerr;
using std::endl;
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
// Manipulators
istream& skip_eol( istream& in ) {
char c = '\0';
// skip to end of line.
while ( in.get(c) ) {
if ( (c == '\n') || (c == '\r') ) {
break;
}
}
return in;
}
istream& skip_ws( istream& in ) {
char c;
while ( in.get(c) ) {
if ( ! isspace( c ) ) {
// put back the non-space character
in.putback(c);
break;
}
}
return in;
}
istream& skip_comment( istream& in )
{
while ( in ) {
// skip whitespace
in >> skip_ws;
char c;
if ( in.get( c ) && c != '#' ) {
// not a comment
in.putback(c);
break;
}
in >> skip_eol;
}
return in;
}
/* program name */
extern char *prog;
extern const char * default_root;
static std::string config;
static OptionEntry *fgcomOptionArray = 0;
static void _doOptions (int argc, char **argv);
static int _parseOption (const std::string & arg, const std::string & next_arg);
static void _fgcomParseArgs (int argc, char **argv);
static void _fgcomParseOptions (const std::string & path);
// Read in configuration (file and command line)
bool fgcomInitOptions (const OptionEntry * fgcomOptions, int argc, char **argv)
{
if (!fgcomOptions) {
SG_LOG( SG_GENERAL, SG_ALERT, "Error! Uninitialized fgcomOptionArray!" );
return false;
}
// set option array
int n_options;
for (n_options = 0; fgcomOptions[n_options].long_option != NULL; n_options++) {}
fgcomOptionArray = (OptionEntry *) realloc (fgcomOptionArray, sizeof (OptionEntry) * (n_options + 1));
memcpy (fgcomOptionArray, fgcomOptions, sizeof (OptionEntry) * (n_options + 1));
// parse options
_doOptions (argc, argv);
return true;
}
// Create usage information out of fgcomOptionArray
void
fgcomUsage ()
{
size_t
max_length = 0;
if (!fgcomOptionArray)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Error! Options need to be initialized by calling 'fgcomInitConfig'!" );
return;
}
// find longest long_option
const OptionEntry *
currentEntry = &fgcomOptionArray[0];
while (currentEntry->long_option != 0)
{
size_t
current_length = strlen (currentEntry->long_option);
if (current_length > max_length)
{
max_length = current_length;
}
currentEntry++;
}
max_length *= 2;
max_length += 10; // for "-o, --option=, -option"
// print head
std::cout << " OPTION" << std::string (max_length - 8,
' ') << "\t\t" << "DESCRIPTION" <<
std::endl;
std::cout << std::endl;
// iterate through option array
currentEntry = &fgcomOptionArray[0];
while (currentEntry->long_option != 0)
{
size_t
current_length = strlen (currentEntry->long_option);
current_length *= 2;
current_length += 10;
std::string option = std::string (" -")
+ std::string (&currentEntry->option);
if (option.size() > 4)
option = option.substr(0,4);
option += std::string (", -")
+ std::string (currentEntry->long_option)
+ std::string (", --")
+ std::string (currentEntry->long_option)
+ std::string ("=") + std::string (max_length - current_length, ' ');
std::cout << option << "\t\t" << currentEntry->description;
if (currentEntry->has_param && (currentEntry->type != OPTION_NONE)
&& (currentEntry->default_value != 0))
{
std::cout << " (default: '";
if (currentEntry->type == OPTION_NONE)
{
}
else if (currentEntry->type == OPTION_BOOL)
{
std::cout << *(bool *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_STRING)
{
std::cout << (char *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_FLOAT)
{
std::cout << *(float *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_DOUBLE)
{
std::cout << *(double *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_FREQ)
{
std::cout << std::setw (7) << std::
setprecision (3) << *(double *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_INT)
{
std::cout << *(int *) currentEntry->default_value;
}
else if (currentEntry->type == OPTION_CHAR)
{
std::cout << *(char *) currentEntry->default_value;
}
std::cout << "')";
}
std::cout << std::endl;
currentEntry++;
}
std::cout << std::endl;
std::cout << " Available codecs:" << std::endl;
std::cout << " \t" <<
"u - ulaw (default and best codec because the mixing is based onto ulaw)"
<< std::endl;
std::cout << " \t" << "a - alaw" << std::endl;
std::cout << " \t" << "g - gsm" << std::endl;
std::cout << " \t" << "s - speex" << std::endl;
std::cout << " \t" << "7 - G.723" << std::endl;
std::cout << std::endl;
std::cout << std::endl;
std::cout << " Mode 1: client for COM1 of flightgear:" << std::endl;
std::cout << " \t" << "$ " << prog << std::endl;
std::cout << " - connects " << prog << " to fgfs at localhost:" <<
DEFAULT_FG_PORT << std::endl;
std::cout << " \t" << "$ " << prog << " -sother.host.tld -p23456" <<
std::endl;
std::cout << " - connects " << prog << " to fgfs at other.host.tld:23456" << std::endl;
std::cout << std::endl;
std::cout << " Mode 2: client for an ATC at <airport> on <frequency>:" <<
std::endl;
std::cout << " \t" << "$ " << prog << " -aKSFO -f120.500" << std::endl;
std::cout << " - sets up " << prog <<
" for an ATC radio at KSFO 120.500 MHz" << std::endl;
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Note that " << prog <<
" starts with a guest account unless you use -U and -P!" << std::endl;
std::cout << std::endl;
}
static char *
get_alternate_home(void)
{
char *ah = 0;
#ifdef _MSC_VER
char *app_data = getenv("LOCALAPPDATA");
if (app_data) {
ah = _strdup(app_data);
}
#else
struct passwd *
pwd = getpwent ();
ah = strdup (pwd->pw_dir);
#endif
return ah;
}
// Attempt to locate and parse the various non-XML config files in order
// from least precidence to greatest precidence
static void
_doOptions (int argc, char **argv)
{
char *
homedir = getenv ("HOME");
if (homedir == NULL)
{
homedir = get_alternate_home();
}
// Check for ~/.fgfsrc
if (homedir != NULL)
{
config = string (homedir);
#ifdef _WIN32
config.append ("\\");
#else
config.append ("/");
#endif
config.append (".fgcomrc");
_fgcomParseOptions (config);
}
// Parse remaining command line options
// These will override anything specified in a config file
_fgcomParseArgs (argc, argv);
}
// lookup maps
static std::map<string, string> fgcomOptionMap;
static std::map<string, size_t> fgcomLongOptionMap;
// Parse a single option
static int
_parseOption (const std::string & arg, const std::string & next_arg)
{
if (fgcomLongOptionMap.size () == 0)
{
size_t i = 0;
const OptionEntry * entry = &fgcomOptionArray[0];
while (entry->long_option != 0)
{
fgcomLongOptionMap.insert (std::pair < std::string,
size_t >
(std::string (entry->long_option), i));
fgcomOptionMap.insert (std::pair < std::string,
std::string >
(std::string (1, entry->option),
std::string (entry->long_option)));
i += 1;
entry += 1;
}
}
// General Options
if ((arg == "--help") || (arg == "-h") || (arg == "-?"))
{
// help/usage request
return FGCOM_OPTIONS_HELP;
}
else if ((arg == "--verbose") || (arg == "-v"))
{
// verbose help/usage request
return FGCOM_OPTIONS_VERBOSE_HELP;
}
else
{
std::map < string, size_t >::iterator it;
std::string arg_name, arg_value;
if (arg.find ("--") == 0)
{
size_t
pos = arg.find ('=');
if (pos == string::npos)
{
// did not find a value
arg_name = arg.substr (2);
// now there are two possibilities:
// 1: this is an option without a value
// 2: the value can be found in next_arg
if (next_arg.empty ())
{
// ok, value cannot be in next_arg
SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "'" );
}
else
{
if (next_arg.at (0) == '-')
{
// there is no value, new option starts in next_arg
SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "'" );
}
else
{
// the value is in next_arg
arg_value = std::string (next_arg);
SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
}
}
}
else
{
// found a value
arg_name = arg.substr (2, pos - 2);
arg_value = arg.substr (pos + 1);
SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
}
it = fgcomLongOptionMap.find (arg_name);
}
else
{
std::map < string, string >::iterator it_b;
arg_name = arg.substr (1, 1);
arg_value = arg.substr (2);
if (arg_name.empty ())
{
SG_LOG (SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'");
return FGCOM_OPTIONS_ERROR;
}
SG_LOG( SG_GENERAL, SG_DEBUG, "option '" << arg_name << "' with argument '" << arg_value << "'" );
it_b = fgcomOptionMap.find (arg_name);
if (it_b != fgcomOptionMap.end ())
{
it = fgcomLongOptionMap.find (it_b->second);
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
return FGCOM_OPTIONS_ERROR;
}
}
if (it != fgcomLongOptionMap.end ())
{
const OptionEntry *
entry = &fgcomOptionArray[it->second];
switch (entry->type)
{
case OPTION_BOOL:
*(bool *) entry->parameter = true;
break;
case OPTION_STRING:
if (entry->has_param && !arg_value.empty ())
{
*(char **) entry->parameter = strdup (arg_value.c_str ());
}
else if (entry->has_param)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
return FGCOM_OPTIONS_ERROR;
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
return FGCOM_OPTIONS_ERROR;
}
break;
case OPTION_FLOAT:
if (!arg_value.empty ())
{
#ifdef _MSC_VER
float temp = atof(arg_value.c_str ());
#else // !_MSC_VER
char *
end;
float
temp = strtof (arg_value.c_str (), &end);
errno = 0;
if (*end != '\0')
{
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse float value '" << arg_value << "' for option " << arg_name << "!" );
return FGCOM_OPTIONS_ERROR;
}
#endif // _MSC_VER y/n
*(float *) (entry->parameter) = temp;
if (*(float *) (entry->parameter) != temp
|| errno == ERANGE)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Float value '" << arg_value << "' for option " << arg_name << " out of range!" );
return FGCOM_OPTIONS_ERROR;
}
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
return FGCOM_OPTIONS_ERROR;
}
break;
case OPTION_DOUBLE:
case OPTION_FREQ:
if (!arg_value.empty ())
{
char *
end;
double
temp = strtod (arg_value.c_str (), &end);
errno = 0;
if (*end != '\0')
{
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse double value '" << arg_value << "' for option " << arg_name << "!" );
return FGCOM_OPTIONS_ERROR;
}
*(double *) (entry->parameter) = temp;
if (*(double *) (entry->parameter) != temp
|| errno == ERANGE)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Double value '" << arg_value << "' for option " << arg_name << " out of range!" );
return FGCOM_OPTIONS_ERROR;
}
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
return FGCOM_OPTIONS_ERROR;
}
break;
case OPTION_INT:
if (!arg_value.empty ())
{
char *
end;
long
temp = strtol (arg_value.c_str (), &end, 0);
errno = 0;
if (*end != '\0')
{
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot parse integer value '" << arg_value << "' for option " << arg_name << "!" );
return FGCOM_OPTIONS_ERROR;
}
*(int *) (entry->parameter) = temp;
if (*(int *) (entry->parameter) != temp || errno == ERANGE)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Integer value '" << arg_value << "' for option " << arg_name << " out of range!" );
return FGCOM_OPTIONS_ERROR;
}
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
return FGCOM_OPTIONS_ERROR;
}
break;
case OPTION_CHAR:
if (entry->has_param && !arg_value.empty ())
{
if (arg_value.length () > 1)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a single char as parameter" );
return FGCOM_OPTIONS_ERROR;
}
else
{
*(char *) entry->parameter = arg_value.c_str ()[0];
}
}
else if (entry->has_param)
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
return FGCOM_OPTIONS_ERROR;
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
return FGCOM_OPTIONS_ERROR;
}
break;
case OPTION_NONE:
*(bool *) entry->parameter = true;
break;
}
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
return FGCOM_OPTIONS_ERROR;
}
}
return FGCOM_OPTIONS_OK;
}
// Parse the command line options
static void
_fgcomParseArgs (int argc, char **argv)
{
SG_LOG( SG_GENERAL, SG_DEBUG, "Processing commandline options" );
for (int i = 1; i < argc; i++)
{
std::string arg = std::string (argv[i]);
std::string next_arg;
if (i < argc - 1)
{
next_arg = std::string (argv[i + 1]);
}
if (arg.find ('-') == 0)
{
if (arg == "--")
{
// do nothing
}
else
{
int
result = _parseOption (arg, next_arg);
if (result == FGCOM_OPTIONS_OK)
{
// that is great
}
else if (result == FGCOM_OPTIONS_HELP)
{
fgcomUsage ();
exit (0);
}
else
{
SG_LOG( SG_GENERAL, SG_ALERT, "Error parsing commandline options !" );
exit (1);
}
}
}
}
SG_LOG( SG_GENERAL, SG_ALERT, "Successfully parsed commandline options" );
}
// Parse config file options
static void
_fgcomParseOptions (const std::string & path)
{
if (is_file_or_directory(path.c_str()) != 1) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Error: Unable to open " << path );
return;
}
std::fstream in;
std::ios_base::openmode mode = std::ios_base::in;
in.open(path.c_str(),mode);
if (!in.is_open ()) {
SG_LOG( SG_GENERAL, SG_DEBUG, "Error: DEBUG: Unable to open " << path );
return;
}
SG_LOG(SG_GENERAL, SG_DEBUG, "Processing config file: " << path );
in >> skip_comment;
while (!in.eof ()) {
std::string line;
getline (in, line, '\n');
// catch extraneous (DOS) line ending character
int i;
for (i = line.length(); i > 0; i--) {
if (line[i - 1] > 32) {
break;
}
}
line = line.substr(0, i);
std::string next_arg;
if (_parseOption (line, next_arg) == FGCOM_OPTIONS_ERROR) {
SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: Config file parse error: " << path << " '" << line << "'" );
fgcomUsage ();
exit(1);
}
in >> skip_comment;
}
}
/* eof - fgcom_init.cpp */