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:
parent
0eccd52226
commit
942e57fc8d
|
@ -1,5 +1,12 @@
|
||||||
#include "CConfig.h"
|
#include "CConfig.h"
|
||||||
#include <assert.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
|
// CConfig
|
||||||
|
@ -75,6 +82,50 @@ void CConfig::disconnect(const CString& srcName,
|
||||||
index->second.m_neighbor[srcSide - kFirstDirection].erase();
|
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
|
CConfig::const_iterator CConfig::begin() const
|
||||||
{
|
{
|
||||||
return const_iterator(m_map.begin());
|
return const_iterator(m_map.begin());
|
||||||
|
@ -85,6 +136,11 @@ CConfig::const_iterator CConfig::end() const
|
||||||
return const_iterator(m_map.end());
|
return const_iterator(m_map.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CConfig::isScreen(const CString& name)
|
||||||
|
{
|
||||||
|
return (m_map.count(name) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
CString CConfig::getNeighbor(const CString& srcName,
|
CString CConfig::getNeighbor(const CString& srcName,
|
||||||
EDirection srcSide) const
|
EDirection srcSide) const
|
||||||
{
|
{
|
||||||
|
@ -103,3 +159,255 @@ const char* CConfig::dirName(EDirection dir)
|
||||||
static const char* s_name[] = { "left", "right", "top", "bottom" };
|
static const char* s_name[] = { "left", "right", "top", "bottom" };
|
||||||
return s_name[dir - kFirstDirection];
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "BasicTypes.h"
|
#include "BasicTypes.h"
|
||||||
#include "CString.h"
|
#include "CString.h"
|
||||||
|
#include "XBase.h"
|
||||||
|
#include <iosfwd>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class CConfig {
|
class CConfig {
|
||||||
|
@ -53,6 +55,9 @@ public:
|
||||||
|
|
||||||
// manipulators
|
// manipulators
|
||||||
|
|
||||||
|
// note that case is preserved in screen names but has no effect
|
||||||
|
// FIXME -- make that true
|
||||||
|
|
||||||
// add/remove screens
|
// add/remove screens
|
||||||
void addScreen(const CString& name);
|
void addScreen(const CString& name);
|
||||||
void removeScreen(const CString& name);
|
void removeScreen(const CString& name);
|
||||||
|
@ -67,19 +72,49 @@ public:
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
|
|
||||||
|
// returns true iff the given name is a valid screen name.
|
||||||
|
bool isValidScreenName(const CString&) const;
|
||||||
|
|
||||||
// iterators over screen names
|
// iterators over screen names
|
||||||
const_iterator begin() const;
|
const_iterator begin() const;
|
||||||
const_iterator end() 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
|
// get the neighbor in the given direction. returns the empty string
|
||||||
// if there is no neighbor in that direction.
|
// if there is no neighbor in that direction.
|
||||||
CString getNeighbor(const CString&, EDirection) const;
|
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)
|
// get the name of a direction (for debugging)
|
||||||
static const char* dirName(EDirection);
|
static const char* dirName(EDirection);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool readLine(istream&, CString&);
|
||||||
|
void readSection(istream&);
|
||||||
|
void readSectionScreens(istream&);
|
||||||
|
void readSectionLinks(istream&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CCellMap m_map;
|
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
|
#endif
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
#include "CMutex.h"
|
#include "CMutex.h"
|
||||||
#include "CNetwork.h"
|
#include "CNetwork.h"
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
//
|
||||||
|
// config file stuff
|
||||||
|
//
|
||||||
|
|
||||||
|
static const char* s_configFileName = "synergy.conf";
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// logging thread safety
|
// logging thread safety
|
||||||
|
@ -41,17 +49,19 @@ void realMain()
|
||||||
// initialize network library
|
// initialize network library
|
||||||
CNetwork::init();
|
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;
|
CServer* server = NULL;
|
||||||
try {
|
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 = new CServer();
|
||||||
server->setConfig(config);
|
server->setConfig(config);
|
||||||
server->run();
|
server->run();
|
||||||
|
@ -93,6 +103,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (XBase& e) {
|
catch (XBase& e) {
|
||||||
|
log((CLOG_CRIT "failed: %s", e.what()));
|
||||||
CString msg = "failed: ";
|
CString msg = "failed: ";
|
||||||
msg += e.what();
|
msg += e.what();
|
||||||
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
|
MessageBox(NULL, msg.c_str(), "error", MB_OK | MB_ICONERROR);
|
||||||
|
@ -116,6 +127,7 @@ int main(int argc, char** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (XBase& e) {
|
catch (XBase& e) {
|
||||||
|
log((CLOG_CRIT "failed: %s", e.what()));
|
||||||
fprintf(stderr, "failed: %s\n", e.what());
|
fprintf(stderr, "failed: %s\n", e.what());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue