From 2a73e6c0d530720913982ea6a912ec1479a52788 Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Sun, 17 Sep 2017 12:14:00 +0200 Subject: [PATCH 01/11] new version: 2017.3.1 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 35e518f1..24dff939 100644 --- a/version +++ b/version @@ -1 +1 @@ -2017.3.0 +2017.3.1 From 8e29cae30921c0c9275dda488a16276c3c1c8a02 Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Sun, 17 Sep 2017 12:14:00 +0200 Subject: [PATCH 02/11] new version: 2017.4.0 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 24dff939..310bd2fc 100644 --- a/version +++ b/version @@ -1 +1 @@ -2017.3.1 +2017.4.0 From 602244979b2f4cf2c410af30460a9531a9dd7290 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 21 Sep 2017 17:47:28 +0100 Subject: [PATCH 03/11] Tar extractor can filter file paths. --- simgear/io/untar.cxx | 61 ++++++++++++++++++++++++++++++++------------ simgear/io/untar.hxx | 10 ++++++++ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/simgear/io/untar.cxx b/simgear/io/untar.cxx index 9a1dfe61..0eeab2f8 100644 --- a/simgear/io/untar.cxx +++ b/simgear/io/untar.cxx @@ -28,6 +28,7 @@ #include +#include #include #include @@ -92,7 +93,8 @@ public: END_OF_ARCHIVE, ERROR_STATE, ///< states above this are error conditions BAD_ARCHIVE, - BAD_DATA + BAD_DATA, + FILTER_STOPPED } State; SGPath path; @@ -110,10 +112,13 @@ public: bool haveInitedZLib; bool uncompressedData; // set if reading a plain .tar (not tar.gz) uint8_t* headerPtr; - - TarExtractorPrivate() : + TarExtractor* outer; + bool skipCurrentEntry = false; + + TarExtractorPrivate(TarExtractor* o) : haveInitedZLib(false), - uncompressedData(false) + uncompressedData(false), + outer(o) { } @@ -129,7 +134,10 @@ public: } if (state == READING_FILE) { - currentFile->close(); + if (currentFile) { + currentFile->close(); + currentFile.reset(); + } size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE; if (pad) { bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad; @@ -177,26 +185,36 @@ public: return; } + skipCurrentEntry = false; std::string tarPath = std::string(header.prefix) + std::string(header.fileName); if (!isSafePath(tarPath)) { - //state = BAD_ARCHIVE; SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath); - //return; + skipCurrentEntry = true; } - SGPath p = path; - p.append(tarPath); - + auto result = outer->filterPath(tarPath); + if (result == TarExtractor::Stop) { + setState(FILTER_STOPPED); + return; + } else if (result == TarExtractor::Skipped) { + skipCurrentEntry = true; + } + + SGPath p = path / tarPath; if (header.typeflag == DIRTYPE) { - Dir dir(p); - dir.create(0755); + if (!skipCurrentEntry) { + Dir dir(p); + dir.create(0755); + } setState(READING_HEADER); } else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) { currentFileSize = ::strtol(header.size, NULL, 8); bytesRemaining = currentFileSize; - currentFile.reset(new SGBinaryFile(p)); - currentFile->open(SG_IO_OUT); + if (!skipCurrentEntry) { + currentFile.reset(new SGBinaryFile(p)); + currentFile->open(SG_IO_OUT); + } setState(READING_FILE); } else { SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag); @@ -212,7 +230,9 @@ public: size_t curBytes = std::min(bytesRemaining, count); if (state == READING_FILE) { - currentFile->write(bytes, curBytes); + if (currentFile) { + currentFile->write(bytes, curBytes); + } bytesRemaining -= curBytes; } else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) { memcpy(headerPtr, bytes, curBytes); @@ -264,7 +284,7 @@ public: }; TarExtractor::TarExtractor(const SGPath& rootPath) : - d(new TarExtractorPrivate) + d(new TarExtractorPrivate(this)) { d->path = rootPath; @@ -397,7 +417,7 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count) SG_LOG(SG_IO, SG_WARN, "insufficient data for header"); return false; } - + header = reinterpret_cast(zlibOutput); } else { // uncompressed tar @@ -417,4 +437,11 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count) return true; } +auto TarExtractor::filterPath(std::string& pathToExtract) + -> PathResult +{ + SG_UNUSED(pathToExtract); + return Accepted; +} + } // of simgear diff --git a/simgear/io/untar.hxx b/simgear/io/untar.hxx index b1db9063..0d963df2 100644 --- a/simgear/io/untar.hxx +++ b/simgear/io/untar.hxx @@ -43,7 +43,17 @@ public: bool hasError() const; +protected: + enum PathResult { + Accepted, + Skipped, + Modified, + Stop + }; + + virtual PathResult filterPath(std::string& pathToExtract); private: + friend class TarExtractorPrivate; std::unique_ptr d; }; From 7bfbda7188b58a5f8a3ba88e20dff1019ff9a760 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 24 Sep 2017 22:10:44 +0200 Subject: [PATCH 04/11] Fix for AMD / Radeon rendering of lights as point sprites. Tested against R9-290 and GTX 550 Ti (thanks Nikolai V. Chr.) --- simgear/scene/tgdb/pt_lights.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index 7c40d313..be82127e 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -275,7 +275,7 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights) //stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); osg::DrawArrays* drawArrays; - drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); return geometry; From 721aa544c9400693ea6b59a5c9ad8e44a8a376e2 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 26 Sep 2017 16:39:04 +0200 Subject: [PATCH 05/11] Use C++11 in the state-machine code --- simgear/structure/StateMachine.cxx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/simgear/structure/StateMachine.cxx b/simgear/structure/StateMachine.cxx index 178b9c61..2bff0f3f 100644 --- a/simgear/structure/StateMachine.cxx +++ b/simgear/structure/StateMachine.cxx @@ -19,16 +19,13 @@ * */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "StateMachine.hxx" #include #include #include -#include #include #include @@ -44,7 +41,7 @@ typedef std::vector StatePtrVec; static void readBindingList(SGPropertyNode* desc, const std::string& name, SGPropertyNode* root, SGBindingList& result) { - BOOST_FOREACH(SGPropertyNode* b, desc->getChildren(name)) { + for (auto b : desc->getChildren(name)) { SGBinding* bind = new SGBinding; bind->read(b, root); result.push_back(bind); @@ -85,7 +82,7 @@ public: void computeEligibleTransitions() { _eligible.clear(); - BOOST_FOREACH(Transition_ptr t, _transitions) { + for (Transition_ptr t : _transitions) { if (t->applicableForState(_currentState)) { _eligible.push_back(t.ptr()); } @@ -358,7 +355,7 @@ void StateMachine::update(double aDt) Transition_ptr trigger; - BOOST_FOREACH(Transition* trans, d->_eligible) { + for (auto trans : d->_eligible) { if (trans->evaluate()) { if (trigger != Transition_ptr()) { SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! " @@ -379,7 +376,7 @@ void StateMachine::update(double aDt) StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const { - BOOST_FOREACH(State_ptr sp, d->_states) { + for (auto sp : d->_states) { if (sp->name() == aName) { return sp; } @@ -435,7 +432,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root) assert(d->_root); } - BOOST_FOREACH(SGPropertyNode* stateDesc, desc->getChildren("state")) { + for (auto stateDesc : desc->getChildren("state")) { std::string nm = stateDesc->getStringValue("name"); State_ptr st(new State(nm)); @@ -446,7 +443,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root) addState(st); } // of states iteration - BOOST_FOREACH(SGPropertyNode* tDesc, desc->getChildren("transition")) { + for (auto tDesc : desc->getChildren("transition")) { std::string nm = tDesc->getStringValue("name"); State_ptr target = findStateByName(tDesc->getStringValue("target")); @@ -456,7 +453,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root) t->setTriggerCondition(cond); t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true)); - BOOST_FOREACH(SGPropertyNode* src, tDesc->getChildren("source")) { + for (auto src : tDesc->getChildren("source")) { State_ptr srcState = findStateByName(src->getStringValue()); t->addSourceState(srcState); } From 872a48dacbfe04534380277eb62159ee03eb231b Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 27 Sep 2017 14:33:57 +0100 Subject: [PATCH 06/11] Extend the state-machine parsing test slightly --- simgear/structure/state_machine_test.cxx | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/simgear/structure/state_machine_test.cxx b/simgear/structure/state_machine_test.cxx index 73b434c3..558c72b8 100644 --- a/simgear/structure/state_machine_test.cxx +++ b/simgear/structure/state_machine_test.cxx @@ -212,15 +212,23 @@ void testBindings() void testParse() { - const char* xml = "" - "" - "" - "one" - "" - "" - "two" - "" - ""; + const char* xml = R"( + + + one + + nasal + + + + nasal + + + + + two + + )"; SGPropertyNode* desc = new SGPropertyNode; readProperties(xml, strlen(xml), desc); From 6cd5ac2d0dc89c13fda7ff2046f41d64e2cf3420 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 27 Sep 2017 15:06:37 +0100 Subject: [PATCH 07/11] Move readTime (parse_time) from options.cxx --- simgear/misc/strutils.cxx | 27 +++++++++++++++++++++++++++ simgear/misc/strutils.hxx | 10 ++++++++++ simgear/misc/strutils_test.cxx | 27 ++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 1f854ef6..f45e0ac8 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -538,6 +538,33 @@ namespace simgear { unsigned long long readNonNegativeInt( const std::string& s); #endif + + // parse a time string ([+/-]%f[:%f[:%f]]) into hours + double readTime(const string& time_in) + { + if (time_in.empty()) { + return 0.0; + } + + const bool negativeSign = time_in.front() == '-'; + const string_list pieces = split(time_in, ":"); + if (pieces.size() > 3) { + throw sg_format_exception("Unable to parse time string, too many pieces", time_in); + } + + const int hours = std::abs(to_int(pieces.front())); + int minutes = 0, seconds = 0; + if (pieces.size() > 1) { + minutes = to_int(pieces.at(1)); + if (pieces.size() > 2) { + seconds = to_int(pieces.at(2)); + } + } + + double result = hours + (minutes / 60.0) + (seconds / 3600.0); + return negativeSign ? -result : result; + } + int compare_versions(const string& v1, const string& v2, int maxComponents) { diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index ed0ff014..b6126cd5 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -209,6 +209,16 @@ namespace simgear { typename = typename std::enable_if::value, T>::type > T readNonNegativeInt(const std::string& s); + + /** + * Read a time value, seperated by colons, as a value in hours. + * Allowable input is ([+/-]%f[:%f[:%f]]) + * i.e 15:04:35 is parsed as 15 + (04 / 60) + (35 / 2600) + * This code is moved from flightgear's options.cxx where it was called + * parse_time(), + */ + double readTime(const std::string& s); + /** * Convert a string representing a boolean, to a bool. * Accepted values include YES, true, 0, 1, false, no, True, diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index dd686fa2..84978da5 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -19,6 +19,7 @@ #include #include #include +#include using std::string; using std::vector; @@ -581,6 +582,29 @@ void test_error_string() SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0); } +void test_readTime() +{ + SG_CHECK_EQUAL_EP(strutils::readTime(""), 0.0); + + SG_CHECK_EQUAL_EP(strutils::readTime("11"), 11.0); + SG_CHECK_EQUAL_EP(strutils::readTime("+11"), 11.0); + SG_CHECK_EQUAL_EP(strutils::readTime("-11"), -11.0); + + SG_CHECK_EQUAL_EP(strutils::readTime("11:30"), 11.5); + SG_CHECK_EQUAL_EP(strutils::readTime("+11:15"), 11.25); + SG_CHECK_EQUAL_EP(strutils::readTime("-11:45"), -11.75); + + const double seconds = 1 / 3600.0; + SG_CHECK_EQUAL_EP(strutils::readTime("11:30:00"), 11.5); + SG_CHECK_EQUAL_EP(strutils::readTime("+11:15:05"), 11.25 + 5 * seconds); + SG_CHECK_EQUAL_EP(strutils::readTime("-11:45:15"), -(11.75 + 15 * seconds)); + + SG_CHECK_EQUAL_EP(strutils::readTime("0:0:0"), 0); + + SG_CHECK_EQUAL_EP(strutils::readTime("0:0:28"), 28 * seconds); + SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds); +} + int main(int argc, char* argv[]) { test_strip(); @@ -599,6 +623,7 @@ int main(int argc, char* argv[]) test_md5_hex(); test_error_string(); test_propPathMatch(); - + test_readTime(); + return EXIT_SUCCESS; } From b3422456193d051f545ca22ed79f5c81ea498f5b Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 28 Sep 2017 15:10:21 +0100 Subject: [PATCH 08/11] Fix lib64 mode on Ubuntu 17.04 / CMake 3.7 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 988dbf8f..5c25884a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7") # add a dependency on the versino file set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version) -set(FIND_LIBRARY_USE_LIB64_PATHS ON) +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE) # use simgear version also as the SO version (if building SOs) SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION}) From a800189c25a3fb4610c1e36655940a95694c4a84 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Thu, 28 Sep 2017 15:32:47 +0100 Subject: [PATCH 09/11] Add support for Slippy Map Canvas layers. - Add so-called Web Mercator projection and ability to choose projection for a Canvas Map. - Add supporting functions for Slippy Maps (i.e. OSM) --- simgear/canvas/elements/CanvasMap.cxx | 44 ++++++++++++++++------ simgear/canvas/elements/map/projection.hxx | 26 +++++++++++++ simgear/nasal/mathlib.c | 37 +++++++++++------- 3 files changed, 83 insertions(+), 24 deletions(-) diff --git a/simgear/canvas/elements/CanvasMap.cxx b/simgear/canvas/elements/CanvasMap.cxx index cca235b4..e15dc13b 100644 --- a/simgear/canvas/elements/CanvasMap.cxx +++ b/simgear/canvas/elements/CanvasMap.cxx @@ -47,6 +47,12 @@ namespace canvas const std::string GEO = "-geo"; const std::string HDG = "hdg"; const std::string Map::TYPE_NAME = "map"; + const std::string WEB_MERCATOR = "webmercator"; + const std::string REF_LAT = "ref-lat"; + const std::string REF_LON = "ref-lon"; + const std::string SCREEN_RANGE = "screen-range"; + const std::string RANGE = "range"; + const std::string PROJECTION = "projection"; //---------------------------------------------------------------------------- void Map::staticInit() @@ -64,12 +70,16 @@ namespace canvas const SGPropertyNode_ptr& node, const Style& parent_style, ElementWeakPtr parent ): - Group(canvas, node, parent_style, parent), - // TODO make projection configurable - _projection(new SansonFlamsteedProjection), - _projection_dirty(true) + Group(canvas, node, parent_style, parent) { staticInit(); + if (node->getChild(PROJECTION) && + (node->getChild(PROJECTION)->getStringValue() == WEB_MERCATOR)) { + _projection = (boost::shared_ptr) new WebMercatorProjection(); + } else { + _projection = (boost::shared_ptr) new SansonFlamsteedProjection(); + } + _projection_dirty = true; } //---------------------------------------------------------------------------- @@ -162,10 +172,10 @@ namespace canvas if( child->getParent() != _node ) return Group::childChanged(child); - if( child->getNameString() == "ref-lat" - || child->getNameString() == "ref-lon" ) - _projection->setWorldPosition( _node->getDoubleValue("ref-lat"), - _node->getDoubleValue("ref-lon") ); + if( child->getNameString() == REF_LAT + || child->getNameString() == REF_LON ) + _projection->setWorldPosition( _node->getDoubleValue(REF_LAT), + _node->getDoubleValue(REF_LON) ); else if( child->getNameString() == HDG ) { _projection->setOrientation(child->getFloatValue()); @@ -174,11 +184,23 @@ namespace canvas ++it ) hdgNodeChanged(*it); } - else if( child->getNameString() == "range" ) + else if( child->getNameString() == RANGE ) _projection->setRange(child->getDoubleValue()); - else if( child->getNameString() == "screen-range" ) + else if( child->getNameString() == SCREEN_RANGE ) _projection->setScreenRange(child->getDoubleValue()); - else + else if( child->getNameString() == PROJECTION ) { + if (child->getStringValue() == WEB_MERCATOR) { + _projection = (boost::shared_ptr) new WebMercatorProjection(); + } else { + _projection = (boost::shared_ptr) new SansonFlamsteedProjection(); + } + _projection->setWorldPosition(_node->getDoubleValue(REF_LAT), + _node->getDoubleValue(REF_LON) ); + _projection->setOrientation(_node->getFloatValue(HDG)); + _projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE)); + _projection->setRange(_node->getDoubleValue(RANGE)); + _projection_dirty = true; + } else return Group::childChanged(child); _projection_dirty = true; diff --git a/simgear/canvas/elements/map/projection.hxx b/simgear/canvas/elements/map/projection.hxx index d60f7d5f..e58b9f94 100644 --- a/simgear/canvas/elements/map/projection.hxx +++ b/simgear/canvas/elements/map/projection.hxx @@ -190,6 +190,32 @@ namespace canvas } }; + /** + * WebMercator projection, relative to the projection center. + * Required for Slippy Maps - i.e. openstreetmap + */ + class WebMercatorProjection: + public HorizontalProjection + { + protected: + + virtual ScreenPosition project(double lat, double lon) const + { + double d_lat = lat - _ref_lat, + d_lon = lon - _ref_lon; + double r = 6378137.f / 1852; // Equatorial radius divided by ? + + ScreenPosition pos; + + pos.x = r * d_lon; + pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat))); + //pos.x = lon; + //pos.y = log(tan(lat) + 1.0 / cos(lat)); + return pos; + } + }; + + } // namespace canvas } // namespace simgear diff --git a/simgear/nasal/mathlib.c b/simgear/nasal/mathlib.c index 564168c5..7c8210f0 100644 --- a/simgear/nasal/mathlib.c +++ b/simgear/nasal/mathlib.c @@ -109,7 +109,7 @@ static naRef f_fmod(naContext c, naRef me, int argc, naRef* args) naRef b = naNumValue(argc > 1 ? args[1] : naNil()); if(naIsNil(a) || naIsNil(b)) naRuntimeError(c, "non numeric arguments to fmod()"); - + a.num = fmod(a.num, b.num); return VALIDATE(a); } @@ -119,7 +119,7 @@ static naRef f_clamp(naContext c, naRef me, int argc, naRef* args) naRef a = naNumValue(argc > 0 ? args[0] : naNil()); naRef min = naNumValue(argc > 1 ? args[1] : naNil()); naRef max = naNumValue(argc > 2 ? args[2] : naNil()); - + if(naIsNil(a) || naIsNil(min) || naIsNil(max)) naRuntimeError(c, "non numeric arguments to clamp()"); @@ -133,10 +133,10 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args) naRef a = naNumValue(argc > 0 ? args[0] : naNil()); naRef b = naNumValue(argc > 1 ? args[1] : naNil()); naRef x = naNumValue(argc > 2 ? args[2] : naNil()); - + if(naIsNil(a) || naIsNil(b) || naIsNil(x)) naRuntimeError(c, "non numeric arguments to periodic()"); - + range = b.num - a.num; x.num = x.num - range*floor((x.num - a.num)/range); // two security checks that can only happen due to roundoff @@ -145,7 +145,7 @@ static naRef f_periodic(naContext c, naRef me, int argc, naRef* args) if (b.num <= x.num) x.num = b.num; return VALIDATE(x); - + // x.num = SGMiscd::normalizePeriodic(a, b, x); return VALIDATE(x); } @@ -156,7 +156,7 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args) naRef b = naNumValue(argc > 1 ? args[1] : naNil()); #ifdef _MSC_VER double x,y; -#endif +#endif if(naIsNil(a)) naRuntimeError(c, "non numeric arguments to round()"); if (naIsNil(b)) @@ -169,21 +169,31 @@ static naRef f_round(naContext c, naRef me, int argc, naRef* args) double x = round(a.num / b.num); #endif a.num = x * b.num; - + return VALIDATE(a); } static naRef f_tan(naContext c, naRef me, int argc, naRef* args) { - naRef a = naNumValue(argc > 0 ? args[0] : naNil()); + naRef a = naNumValue(argc > 0 ? args[0] : naNil()); if(naIsNil(a)) naRuntimeError(c, "non numeric arguments to tan()"); - + a.num = tan(a.num); return VALIDATE(a); } +static naRef f_atan(naContext c, naRef me, int argc, naRef* args) +{ + naRef a = naNumValue(argc > 0 ? args[0] : naNil()); + if(naIsNil(a)) + naRuntimeError(c, "non numeric arguments to tan()"); + + a.num = atan(a.num); + return VALIDATE(a); +} + static naRef f_asin(naContext c, naRef me, int argc, naRef* args) { naRef a = naNumValue(argc > 0 ? args[0] : naNil()); @@ -210,15 +220,16 @@ static naCFuncItem funcs[] = { { "pow", f_pow }, { "sqrt", f_sqrt }, { "atan2", f_atan2 }, + { "atan", f_atan }, { "floor", f_floor }, { "ceil", f_ceil }, { "fmod", f_fmod }, { "clamp", f_clamp }, - { "periodic", f_periodic }, - { "round", f_round }, - { "tan", f_tan }, + { "periodic", f_periodic }, + { "round", f_round }, + { "tan", f_tan }, { "acos", f_acos }, - { "asin", f_asin }, + { "asin", f_asin }, { 0 } }; From dd3cdf63e64e5a388d7de4ed9d80fa84eebd4c0b Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 29 Sep 2017 12:47:42 +0100 Subject: [PATCH 10/11] Terrasync: Fix exception deleting orphaned directories. --- simgear/io/HTTPRepository.cxx | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/simgear/io/HTTPRepository.cxx b/simgear/io/HTTPRepository.cxx index 86ee4d29..5e588a58 100644 --- a/simgear/io/HTTPRepository.cxx +++ b/simgear/io/HTTPRepository.cxx @@ -142,7 +142,7 @@ public: void finishedRequest(const RepoRequestPtr& req); HTTPDirectory* getOrCreateDirectory(const std::string& path); - bool deleteDirectory(const std::string& path); + bool deleteDirectory(const std::string& relPath, const SGPath& absPath); typedef std::vector DirectoryVector; DirectoryVector directories; @@ -317,7 +317,8 @@ public: ChildInfoList::iterator c = findIndexChild(it->file()); if (c == children.end()) { SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" ); - orphans.push_back(it->file()); + + orphans.push_back(it->file()); } else if (c->hash != hash) { SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() ); // file exists, but hash mismatch, schedule update @@ -534,7 +535,7 @@ private: std::string fpath = _relativePath + "/" + name; if (p.isDir()) { - ok = _repository->deleteDirectory(fpath); + ok = _repository->deleteDirectory(fpath, p); } else { // remove the hash cache entry _repository->updatedFileContents(p, std::string()); @@ -1044,25 +1045,26 @@ HTTPRepository::failure() const return d; } - bool HTTPRepoPrivate::deleteDirectory(const std::string& path) + bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath) { - DirectoryWithPath p(path); - DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p); + DirectoryWithPath p(relPath); + auto it = std::find_if(directories.begin(), directories.end(), p); if (it != directories.end()) { HTTPDirectory* d = *it; + assert(d->absolutePath() == absPath); directories.erase(it); - Dir dir(d->absolutePath()); - bool result = dir.remove(true); - - // update the hash cache too - updatedFileContents(d->absolutePath(), std::string()); - delete d; + } else { + // we encounter this code path when deleting an orphaned directory + } + + Dir dir(absPath); + bool result = dir.remove(true); - return result; - } + // update the hash cache too + updatedFileContents(absPath, std::string()); - return false; + return result; } void HTTPRepoPrivate::makeRequest(RepoRequestPtr req) From e5e112c3c2b5aafa3f179880558abd2011cf08c8 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sat, 12 Aug 2017 11:42:21 +0200 Subject: [PATCH 11/11] Add a ResourceProxy class The ResourceProxy class allows one to access real files or embedded resources in a unified way. When using it, one can switch from one data source to the other with minimal code changes, possibly even at runtime (in which case there is obviously no code change at all). Sample usage (from FlightGear for the globals->get_fg_root() bit): simgear::ResourceProxy proxy(globals->get_fg_root(), "/FGData"); std::string s = proxy.getString("/some/path"); std::unique_ptr streamp = proxy.getIStream("/some/path"); The methods ResourceProxy::getString(const std::string& path) and ResourceProxy::getIStream(const std::string& path) decide whether to use embedded resources or real files depending on the boolean value passed to ResourceProxy::setUseEmbeddedResources() (also available as an optional parameter to the ResourceProxy constructor, defaulting to true). It is often most convenient to set this boolean once and don't worry about it anymore---it's stored inside the ResourceProxy object. Otherwise, if you want to fetch resources some times from real files, other times from embedded resources, you may use the following methods: // Retrieve contents using embedded resources std:string s = proxy.getString("/some/path", true); std:string s = proxy.getStringDecideOnPrefix(":/some/path"); // Retrieve contents using real files std:string s = proxy.getString("/some/path", false); std:string s = proxy.getStringDecideOnPrefix("/some/path"); (alternatively, you could use several ResourceProxy objects with different values for the constructor's third parameter) --- simgear/embedded_resources/CMakeLists.txt | 4 +- simgear/embedded_resources/ResourceProxy.cxx | 247 ++++++++++++++++++ simgear/embedded_resources/ResourceProxy.hxx | 152 +++++++++++ .../embedded_resources_test.cxx | 100 +++++++ 4 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 simgear/embedded_resources/ResourceProxy.cxx create mode 100644 simgear/embedded_resources/ResourceProxy.hxx diff --git a/simgear/embedded_resources/CMakeLists.txt b/simgear/embedded_resources/CMakeLists.txt index 8ae2253b..ffe34cbe 100644 --- a/simgear/embedded_resources/CMakeLists.txt +++ b/simgear/embedded_resources/CMakeLists.txt @@ -1,7 +1,7 @@ include (SimGearComponent) -set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx) -set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx) +set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx ResourceProxy.hxx) +set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx ResourceProxy.cxx) simgear_component(embedded_resources embedded_resources "${SOURCES}" "${HEADERS}") diff --git a/simgear/embedded_resources/ResourceProxy.cxx b/simgear/embedded_resources/ResourceProxy.cxx new file mode 100644 index 00000000..d4623db8 --- /dev/null +++ b/simgear/embedded_resources/ResourceProxy.cxx @@ -0,0 +1,247 @@ +// -*- coding: utf-8 -*- +// +// ResourceProxy.cxx --- Unified access to real files or embedded resources +// Copyright (C) 2017 Florent Rougon +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA. + +#include + +#include // std::find() +#include // std::streamsize +#include +#include // std::numeric_limits +#include +#include +#include +#include // std::size_t +#include + +#include +#include +#include +#include +#include "EmbeddedResourceManager.hxx" +#include "ResourceProxy.hxx" + +using std::string; +using std::vector; +using std::shared_ptr; +using std::unique_ptr; + + +namespace simgear +{ + +ResourceProxy::ResourceProxy(const SGPath& realRoot, const string& virtualRoot, + bool useEmbeddedResourcesByDefault) + : _realRoot(realRoot), + _virtualRoot(normalizeVirtualRoot(virtualRoot)), + _useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault) +{ } + +SGPath +ResourceProxy::getRealRoot() const +{ return _realRoot; } + +void +ResourceProxy::setRealRoot(const SGPath& realRoot) +{ _realRoot = realRoot; } + +string +ResourceProxy::getVirtualRoot() const +{ return _virtualRoot; } + +void +ResourceProxy::setVirtualRoot(const string& virtualRoot) +{ _virtualRoot = normalizeVirtualRoot(virtualRoot); } + +bool +ResourceProxy::getUseEmbeddedResources() const +{ return _useEmbeddedResourcesByDefault; } + +void +ResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources) +{ _useEmbeddedResourcesByDefault = useEmbeddedResources; } + +// Static method: normalize the 'virtualRoot' argument of the constructor +// +// The argument must start with a slash and mustn't contain any '.' or '..' +// component. The return value never ends with a slash. +string +ResourceProxy::normalizeVirtualRoot(const string& path) +{ + ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */); + string res = path; + + // Make sure 'res' doesn't end with a '/'. + while (!res.empty() && res.back() == '/') { + res.pop_back(); // This will ease path concatenation + } + + return res; +} + +// Static method +void +ResourceProxy::checkPath(const string& callerMethod, const string& path, + bool allowStartWithColon) { + if (path.empty()) { + throw sg_format_exception( + "Invalid empty path for ResourceProxy::" + callerMethod + "(): '" + + path + "'", path); + } else if (allowStartWithColon && + !simgear::strutils::starts_with(path, ":/") && path[0] != '/') { + throw sg_format_exception( + "Invalid path for ResourceProxy::" + callerMethod + "(): it should " + "start with either ':/' or '/'", path); + } else if (!allowStartWithColon && path[0] != '/') { + throw sg_format_exception( + "Invalid path for ResourceProxy::" + callerMethod + "(): it should " + "start with a slash ('/')", path); + } else { + const vector components = simgear::strutils::split(path, "/"); + auto find = [&components](const string& s) -> bool { + return (std::find(components.begin(), components.end(), s) != + components.end()); + }; + + if (find(".") || find("..")) { + throw sg_format_exception( + "Invalid path for ResourceProxy::" + callerMethod + "(): " + "'.' and '..' components are not allowed", path); + } + } +} + +unique_ptr +ResourceProxy::getIStream(const string& path, bool fromEmbeddedResource) const +{ + ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */); + assert(!path.empty() && path.front() == '/'); + + if (fromEmbeddedResource) { + const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance(); + return embeddedResMgr->getIStream( + _virtualRoot + path, + ""); // fetch the default-locale version of the resource + } else { + const SGPath sgPath = _realRoot / path.substr(std::size_t(1)); + return unique_ptr(new sg_ifstream(sgPath)); + } +} + +unique_ptr +ResourceProxy::getIStream(const string& path) const +{ + return getIStream(path, _useEmbeddedResourcesByDefault); +} + +unique_ptr +ResourceProxy::getIStreamDecideOnPrefix(const string& path) const +{ + ResourceProxy::checkPath(__func__, path, true /* allowStartWithColon */); + + // 'path' is non-empty + if (path.front() == '/') { + return getIStream(path, false /* fromEmbeddedResource */); + } else if (path.front() == ':') { + assert(path.size() >= 2 && path[1] == '/'); + // Skip the leading ':' + return getIStream(path.substr(std::size_t(1)), + true /* fromEmbeddedResource */); + } else { + // The checkPath() call should make it impossible to reach this point. + std::abort(); + } +} + +string +ResourceProxy::getString(const string& path, bool fromEmbeddedResource) const +{ + string result; + + ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */); + assert(!path.empty() && path.front() == '/'); + + if (fromEmbeddedResource) { + const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance(); + // Fetch the default-locale version of the resource + result = embeddedResMgr->getString(_virtualRoot + path, ""); + } else { + const SGPath sgPath = _realRoot / path.substr(std::size_t(1)); + result.reserve(sgPath.sizeInBytes()); + const unique_ptr streamp = getIStream(path, + fromEmbeddedResource); + std::streamsize nbCharsRead; + + // Allocate a buffer + static constexpr std::size_t bufSize = 65536; + static_assert(bufSize <= std::numeric_limits::max(), + "Type std::streamsize is unexpectedly small"); + static_assert(bufSize <= std::numeric_limits::max(), + "Type std::string::size_type is unexpectedly small"); + unique_ptr buf(new char[bufSize]); + + do { + streamp->read(buf.get(), bufSize); + nbCharsRead = streamp->gcount(); + + if (nbCharsRead > 0) { + result.append(buf.get(), nbCharsRead); + } + } while (*streamp); + + // streamp->fail() would *not* indicate an error, due to the semantics + // of std::istream::read(). + if (streamp->bad()) { + throw sg_io_exception("Error reading from file", sg_location(path)); + } + } + + return result; +} + +string +ResourceProxy::getString(const string& path) const +{ + return getString(path, _useEmbeddedResourcesByDefault); +} + +string +ResourceProxy::getStringDecideOnPrefix(const string& path) const +{ + string result; + + ResourceProxy::checkPath(__func__, path, true /* allowStartWithColon */); + + // 'path' is non-empty + if (path.front() == '/') { + result = getString(path, false /* fromEmbeddedResource */); + } else if (path.front() == ':') { + assert(path.size() >= 2 && path[1] == '/'); + // Skip the leading ':' + result = getString(path.substr(std::size_t(1)), + true /* fromEmbeddedResource */); + } else { + // The checkPath() call should make it impossible to reach this point. + std::abort(); + } + + return result; +} + +} // of namespace simgear diff --git a/simgear/embedded_resources/ResourceProxy.hxx b/simgear/embedded_resources/ResourceProxy.hxx new file mode 100644 index 00000000..79e560bb --- /dev/null +++ b/simgear/embedded_resources/ResourceProxy.hxx @@ -0,0 +1,152 @@ +// -*- coding: utf-8 -*- +// +// ResourceProxy.hxx --- Unified access to real files or embedded resources +// Copyright (C) 2017 Florent Rougon +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA. + +#ifndef FG_RESOURCEPROXY_HXX +#define FG_RESOURCEPROXY_HXX + +#include +#include +#include + +#include + +// The ResourceProxy class allows one to access real files or embedded +// resources in a unified way. When using it, one can switch from one data +// source to the other with minimal code changes, possibly even at runtime (in +// which case there is obviously no code change at all). +// +// Sample usage of the ResourceProxy class (from FlightGear): +// +// simgear::ResourceProxy proxy(globals->get_fg_root(), "/FGData"); +// std::string s = proxy.getString("/some/path"); +// std::unique_ptr streamp = proxy.getIStream("/some/path"); +// +// The methods ResourceProxy::getString(const std::string& path) and +// ResourceProxy::getIStream(const std::string& path) decide whether to use +// embedded resources or real files depending on the boolean value passed to +// ResourceProxy::setUseEmbeddedResources() (also available as an optional +// parameter to the ResourceProxy constructor, defaulting to true). It is +// often most convenient to set this boolean once and then don't worry about +// it anymore (it is stored inside ResourceProxy). Otherwise, if you want to +// fetch resources some times from real files, other times from embedded +// resources, you may use the following methods: +// +// // Retrieve contents using embedded resources +// std:string s = proxy.getString("/some/path", true); +// std:string s = proxy.getStringDecideOnPrefix(":/some/path"); +// +// // Retrieve contents using real files +// std:string s = proxy.getString("/some/path", false); +// std:string s = proxy.getStringDecideOnPrefix("/some/path"); +// +// You can do exactly the same with ResourceProxy::getIStream() and +// ResourceProxy::getIStreamDecideOnPrefix(), except they return an +// std::unique_ptr instead of an std::string. +// +// Given how the 'proxy' object was constructed above, each of these calls +// will fetch data from either the real file $FG_ROOT/some/path or the +// embedded resource whose virtual path is '/FGData/some/path' (more +// precisely: the default-locale version of this resource). +// +// The 'path' argument of ResourceProxy's methods getString(), getIStream(), +// getStringDecideOnPrefix() and getIStreamDecideOnPrefix() must: +// +// - use UTF-8 encoding; +// +// - start with: +// * either '/' or ':/' for the 'DecideOnPrefix' variants; +// * only '/' for the other methods. +// +// - have its components separated by slashes; +// +// - not contain any '.' or '..' component. +// +// For the 'DecideOnPrefix' variants: +// +// - if the path starts with a slash ('/'), a real file access is done; +// +// - if, on the other hand, it starts with ':/', ResourceProxy uses the +// embedded resource whose virtual path is the specified path without its +// leading ':' (more precisely: the default-locale version of this +// resource). +namespace simgear +{ + +class ResourceProxy +{ +public: + // 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends + // with a '/' doesn't make a difference. + explicit ResourceProxy(const SGPath& realRoot, + const std::string& virtualRoot, + bool useEmbeddedResourcesByDefault = true); + + // Getters and setters for the corresponding data members + SGPath getRealRoot() const; + void setRealRoot(const SGPath& realRoot); + + std::string getVirtualRoot() const; + void setVirtualRoot(const std::string& virtualRoot); + + bool getUseEmbeddedResources() const; + void setUseEmbeddedResources(bool useEmbeddedResources); + + // Get an std::istream to read from a file or from an embedded resource. + std::unique_ptr + getIStream(const std::string& path, bool fromEmbeddedResource) const; + + std::unique_ptr + getIStream(const std::string& path) const; + + std::unique_ptr + getIStreamDecideOnPrefix(const std::string& path) const; + + // Get a file or embedded resource contents as a string. + std::string + getString(const std::string& path, bool fromEmbeddedResource) const; + + std::string + getString(const std::string& path) const; + + std::string + getStringDecideOnPrefix(const std::string& path) const; + +private: + // Check that 'path' starts with either ':/' or '/', and doesn't contain any + // '..' component ('path' may only start with ':/' if 'allowStartWithColon' + // is true). + static void + checkPath(const std::string& callerMethod, const std::string& path, + bool allowStartWithColon); + + // Normalize the 'virtualRoot' argument of the constructor. The argument + // must start with a '/' and mustn't contain any '.' or '..' component. The + // return value never ends with a '/'. + static std::string + normalizeVirtualRoot(const std::string& path); + + SGPath _realRoot; + std::string _virtualRoot; + bool _useEmbeddedResourcesByDefault; +}; + +} // of namespace simgear + +#endif // of FG_RESOURCEPROXY_HXX diff --git a/simgear/embedded_resources/embedded_resources_test.cxx b/simgear/embedded_resources/embedded_resources_test.cxx index 47bcd342..8f89d2d0 100644 --- a/simgear/embedded_resources/embedded_resources_test.cxx +++ b/simgear/embedded_resources/embedded_resources_test.cxx @@ -33,11 +33,14 @@ #include // std::size_t #include +#include #include #include +#include #include #include "EmbeddedResource.hxx" #include "EmbeddedResourceManager.hxx" +#include "ResourceProxy.hxx" using std::cout; using std::cerr; @@ -395,6 +398,102 @@ void test_getLocaleAndSelectLocale() } } +// Auxiliary function for test_ResourceProxy() +void auxTest_ResourceProxy_getIStream(unique_ptr iStream, + const string& contents) +{ + cout << "Testing ResourceProxy::getIStream()" << endl; + + iStream->exceptions(std::ios_base::badbit); + static constexpr std::size_t bufSize = 65536; + unique_ptr buf(new char[bufSize]); // intermediate buffer + string result; + + do { + iStream->read(buf.get(), bufSize); + result.append(buf.get(), iStream->gcount()); + } while (*iStream); // iStream *points* to an std::istream + + // 1) If set, badbit would have caused an exception to be raised (see above). + // 2) failbit doesn't necessarily indicate an error here: it is set as soon + // as the read() call can't provide the requested number of characters. + SG_VERIFY(iStream->eof() && !iStream->bad()); + SG_CHECK_EQUAL(result, contents); +} + +void test_ResourceProxy() +{ + cout << "Testing the ResourceProxy class" << endl; + + // Initialize stuff we need and create two files containing the contents of + // the default-locale version of two embedded resources: those with virtual + // paths '/path/to/resource1' and '/path/to/resource2'. + const auto& resMgr = EmbeddedResourceManager::instance(); + simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear"); + tmpDir.setRemoveOnDestroy(); + + const SGPath path1 = tmpDir.path() / "resource1"; + const SGPath path2 = tmpDir.path() / "resource2"; + + sg_ofstream out1(path1); + sg_ofstream out2(path2); + const string s1 = resMgr->getString("/path/to/resource1", ""); + // To make sure in these tests that we can tell whether something came from + // a real file or from an embedded resource. + const string rs1 = s1 + " from real file"; + const string rlipsum = lipsum + " from real file"; + + out1 << rs1; + out1.close(); + if (!out1) { + throw sg_io_exception("Error writing to file", sg_location(path1)); + } + + out2 << rlipsum; + out2.close(); + if (!out2) { + throw sg_io_exception("Error writing to file", sg_location(path2)); + } + + // 'proxy' defaults to using embedded resources + const simgear::ResourceProxy proxy(tmpDir.path(), + "/path/to", + true /* useEmbeddedResourcesByDefault */); + simgear::ResourceProxy rproxy(tmpDir.path(), "/path/to"); + // 'rproxy' defaults to using real files + rproxy.setUseEmbeddedResources(false); // could be done from the ctor too + + // Test ResourceProxy::getString() + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1); + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1); + SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1); + SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1); + SG_CHECK_EQUAL(proxy.getString("/resource1"), s1); + SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1); + + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum); + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum); + SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum); + + // Test ResourceProxy::getIStream() + auxTest_ResourceProxy_getIStream(proxy.getIStreamDecideOnPrefix("/resource1"), + rs1); + auxTest_ResourceProxy_getIStream(proxy.getIStreamDecideOnPrefix(":/resource1"), + s1); + auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource1"), s1); + auxTest_ResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1); + + auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2", false), + rlipsum); + auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2", true), + lipsum); + auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2"), lipsum); + auxTest_ResourceProxy_getIStream(rproxy.getIStream("/resource2"), rlipsum); +} + int main(int argc, char **argv) { // Initialize the EmbeddedResourceManager instance, add a few resources @@ -407,6 +506,7 @@ int main(int argc, char **argv) test_addAlreadyExistingResource(); test_localeDependencyOfResourceFetching(); test_getLocaleAndSelectLocale(); + test_ResourceProxy(); return EXIT_SUCCESS; }