added I/O for configuration files and changed the server to use

an external file for its configuration (was hard coding a config
for testing).
This commit is contained in:
crs 2002-05-31 17:32:26 +00:00
parent 0eccd52226
commit 942e57fc8d
3 changed files with 364 additions and 9 deletions

View File

@ -1,5 +1,12 @@
#include "CConfig.h"
#include <assert.h>
// FIXME -- fix this with automake and config.h
#if !defined(CONFIG_PLATFORM_LINUX)
#include <istream>
#include <ostream>
#else
#include <iostream>
#endif
//
// CConfig
@ -75,6 +82,50 @@ void CConfig::disconnect(const CString& srcName,
index->second.m_neighbor[srcSide - kFirstDirection].erase();
}
bool CConfig::isValidScreenName(const CString& name) const
{
// name is valid if matches validname
// name ::= [A-Za-z0-9] | [A-Za-z0-9][-A-Za-z0-9]*[A-Za-z0-9]
// domain ::= . name
// validname ::= name domain*
// check each dot separated part
CString::size_type b = 0;
for (;;) {
// find end of part
CString::size_type e = name.find('.', b);
if (e == CString::npos) {
e = name.size();
}
// part may not be empty
if (e - b < 1) {
return false;
}
// check first and last characters
if (!isalnum(name[b]) || !isalnum(name[e - 1])) {
return false;
}
// check interior characters
for (CString::size_type i = b; i < e; ++i) {
if (!isalnum(name[i]) && name[i] != '-') {
return false;
}
}
// next part
if (e == name.size()) {
// no more parts
break;
}
b = e + 1;
}
return true;
}
CConfig::const_iterator CConfig::begin() const
{
return const_iterator(m_map.begin());
@ -85,6 +136,11 @@ CConfig::const_iterator CConfig::end() const
return const_iterator(m_map.end());
}
bool CConfig::isScreen(const CString& name)
{
return (m_map.count(name) > 0);
}
CString CConfig::getNeighbor(const CString& srcName,
EDirection srcSide) const
{
@ -103,3 +159,255 @@ const char* CConfig::dirName(EDirection dir)
static const char* s_name[] = { "left", "right", "top", "bottom" };
return s_name[dir - kFirstDirection];
}
bool CConfig::readLine(istream& s, CString& line)
{
s >> std::ws;
while (getline(s, line)) {
// strip comments and then trailing whitespace
CString::size_type i = line.rfind('#');
if (i != CString::npos) {
line.erase(i);
}
i = line.find_last_not_of(" \t");
if (i != CString::npos) {
line.erase(i + 1);
}
// return non empty line
if (!line.empty()) {
return true;
}
s >> std::ws;
}
return false;
}
void CConfig::readSection(istream& s)
{
static const char s_section[] = "section:";
static const char s_screens[] = "screens";
static const char s_links[] = "links";
CString line;
if (!readLine(s, line)) {
// no more sections
return;
}
// should be a section header
if (line.find(s_section) != 0) {
throw XConfigRead("found data outside section");
}
// get section name
CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
if (i == CString::npos) {
throw XConfigRead("section name is missing");
}
CString name = line.substr(i);
i = name.find_first_of(" \t");
if (i != CString::npos) {
throw XConfigRead("unexpected data after section name");
}
// read section
if (name == s_screens) {
readSectionScreens(s);
}
else if (name == s_links) {
readSectionLinks(s);
}
else {
throw XConfigRead("unknown section name");
}
}
void CConfig::readSectionScreens(istream& s)
{
CString line;
CString name;
while (readLine(s, line)) {
// check for end of section
if (line == "end") {
return;
}
// see if it's the next screen
if (line[line.size() - 1] == ':') {
// strip :
name = line.substr(0, line.size() - 1);
// verify validity of screen name
if (!isValidScreenName(name)) {
throw XConfigRead("invalid screen name");
}
// add the screen to the configuration
addScreen(name);
}
else if (name.empty()) {
throw XConfigRead("argument before first screen");
}
else {
throw XConfigRead("unknown argument");
}
}
throw XConfigRead("unexpected end of screens section");
}
void CConfig::readSectionLinks(istream& s)
{
CString line;
CString screen;
while (readLine(s, line)) {
// check for end of section
if (line == "end") {
return;
}
// see if it's the next screen
if (line[line.size() - 1] == ':') {
// strip :
screen = line.substr(0, line.size() - 1);
// verify we known about the screen
if (!isScreen(screen)) {
throw XConfigRead("unknown screen name");
}
}
else if (screen.empty()) {
throw XConfigRead("argument before first screen");
}
else {
// parse argument: `<name>=<value>'
CString::size_type i = line.find_first_of(" \t=");
if (i == 0) {
throw XConfigRead("missing argument name");
}
if (i == CString::npos) {
throw XConfigRead("missing = in argument");
}
CString name = line.substr(0, i);
i = line.find_first_not_of(" \t", i);
if (i == CString::npos || line[i] != '=') {
throw XConfigRead("missing = in argument");
}
i = line.find_first_not_of(" \t", i + 1);
CString value;
if (i != CString::npos) {
value = line.substr(i);
}
// handle argument
if (name == "left") {
if (!isScreen(value)) {
throw XConfigRead("unknown screen");
}
connect(screen, kLeft, value);
}
else if (name == "right") {
if (!isScreen(value)) {
throw XConfigRead("unknown screen");
}
connect(screen, kRight, value);
}
else if (name == "up") {
if (!isScreen(value)) {
throw XConfigRead("unknown screen");
}
connect(screen, kTop, value);
}
else if (name == "down") {
if (!isScreen(value)) {
throw XConfigRead("unknown screen");
}
connect(screen, kBottom, value);
}
else {
// unknown argument
throw XConfigRead("unknown argument");
}
}
}
throw XConfigRead("unexpected end of links section");
}
//
// CConfig I/O
//
istream& operator>>(istream& s, CConfig& config)
{
// FIXME -- should track line and column to improve error reporting
CConfig tmp;
while (s) {
tmp.readSection(s);
}
config = tmp;
return s;
}
ostream& operator<<(ostream& s, const CConfig& config)
{
// screens section
s << "section: screens" << endl;
for (CConfig::const_iterator screen = config.begin();
screen != config.end(); ++screen) {
s << "\t" << screen->c_str() << ":" << endl;
}
s << "end" << endl;
// links section
CString neighbor;
s << "section: links" << endl;
for (CConfig::const_iterator screen = config.begin();
screen != config.end(); ++screen) {
s << "\t" << screen->c_str() << ":" << endl;
neighbor = config.getNeighbor(*screen, CConfig::kLeft);
if (!neighbor.empty()) {
s << "\t\tleft=" << neighbor.c_str() << endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kRight);
if (!neighbor.empty()) {
s << "\t\tright=" << neighbor.c_str() << endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kTop);
if (!neighbor.empty()) {
s << "\t\tup=" << neighbor.c_str() << endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kBottom);
if (!neighbor.empty()) {
s << "\t\tdown=" << neighbor.c_str() << endl;
}
}
s << "end" << endl;
return s;
}
//
// CConfig I/O exceptions
//
XConfigRead::XConfigRead(const CString& error) : m_error(error)
{
// do nothing
}
XConfigRead::~XConfigRead()
{
// do nothing
}
CString XConfigRead::getWhat() const throw()
{
return m_error;
}

View File

@ -3,6 +3,8 @@
#include "BasicTypes.h"
#include "CString.h"
#include "XBase.h"
#include <iosfwd>
#include <map>
class CConfig {
@ -53,6 +55,9 @@ public:
// manipulators
// note that case is preserved in screen names but has no effect
// FIXME -- make that true
// add/remove screens
void addScreen(const CString& name);
void removeScreen(const CString& name);
@ -67,19 +72,49 @@ public:
// accessors
// returns true iff the given name is a valid screen name.
bool isValidScreenName(const CString&) const;
// iterators over screen names
const_iterator begin() const;
const_iterator end() const;
// returns true iff name names a screen
bool isScreen(const CString& name);
// get the neighbor in the given direction. returns the empty string
// if there is no neighbor in that direction.
CString getNeighbor(const CString&, EDirection) const;
// read/write a configuration. operator>> will throw XConfigRead
// on error.
friend istream& operator>>(istream&, CConfig&);
friend ostream& operator<<(ostream&, const CConfig&);
// get the name of a direction (for debugging)
static const char* dirName(EDirection);
private:
static bool readLine(istream&, CString&);
void readSection(istream&);
void readSectionScreens(istream&);
void readSectionLinks(istream&);
private:
CCellMap m_map;
};
class XConfigRead : public XBase {
public:
XConfigRead(const CString&);
~XConfigRead();
protected:
// XBase overrides
virtual CString getWhat() const throw();
private:
CString m_error;
};
#endif

View File

@ -4,6 +4,14 @@
#include "CMutex.h"
#include "CNetwork.h"
#include "CThread.h"
#include <fstream>
//
// config file stuff
//
static const char* s_configFileName = "synergy.conf";
//
// logging thread safety
@ -41,17 +49,19 @@ void realMain()
// initialize network library
CNetwork::init();
CConfig config;
config.addScreen("primary");
config.addScreen("secondary");
config.addScreen("secondary2");
config.connect("primary", CConfig::kRight, "secondary");
config.connect("secondary", CConfig::kLeft, "primary");
config.connect("secondary", CConfig::kRight, "secondary2");
config.connect("secondary2", CConfig::kLeft, "secondary");
CServer* server = NULL;
try {
CConfig config;
{
log((CLOG_DEBUG "opening configuration"));
ifstream configStream(s_configFileName);
if (!configStream) {
throw XConfigRead("cannot open configuration");
}
configStream >> config;
log((CLOG_DEBUG "configuration read successfully"));
}
server = new CServer();
server->setConfig(config);
server->run();
@ -93,6 +103,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
CString msg = "failed: ";
msg += e.what();
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
@ -116,6 +127,7 @@ int main(int argc, char** argv)
return 0;
}
catch (XBase& e) {
log((CLOG_CRIT "failed: %s", e.what()));
fprintf(stderr, "failed: %s\n", e.what());
return 1;
}