Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next

This commit is contained in:
gallaert
2017-10-01 21:25:16 +01:00
18 changed files with 746 additions and 82 deletions

View File

@@ -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})

View File

@@ -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<HorizontalProjection>) new WebMercatorProjection();
} else {
_projection = (boost::shared_ptr<HorizontalProjection>) 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<HorizontalProjection>) new WebMercatorProjection();
} else {
_projection = (boost::shared_ptr<HorizontalProjection>) 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;

View File

@@ -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

View File

@@ -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}")

View File

@@ -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 <simgear_config.h>
#include <algorithm> // std::find()
#include <ios> // std::streamsize
#include <istream>
#include <limits> // std::numeric_limits
#include <memory>
#include <vector>
#include <string>
#include <cstdlib> // std::size_t
#include <cassert>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#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<string> 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<std::istream>
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<std::istream>(new sg_ifstream(sgPath));
}
}
unique_ptr<std::istream>
ResourceProxy::getIStream(const string& path) const
{
return getIStream(path, _useEmbeddedResourcesByDefault);
}
unique_ptr<std::istream>
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<std::istream> streamp = getIStream(path,
fromEmbeddedResource);
std::streamsize nbCharsRead;
// Allocate a buffer
static constexpr std::size_t bufSize = 65536;
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
"Type std::streamsize is unexpectedly small");
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
"Type std::string::size_type is unexpectedly small");
unique_ptr<char[]> 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

View File

@@ -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 <istream>
#include <memory>
#include <string>
#include <simgear/misc/sg_path.hxx>
// 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<std::istream> 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<std::istream> 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<std::istream>
getIStream(const std::string& path, bool fromEmbeddedResource) const;
std::unique_ptr<std::istream>
getIStream(const std::string& path) const;
std::unique_ptr<std::istream>
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

View File

@@ -33,11 +33,14 @@
#include <cstddef> // std::size_t
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#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<std::istream> iStream,
const string& contents)
{
cout << "Testing ResourceProxy::getIStream()" << endl;
iStream->exceptions(std::ios_base::badbit);
static constexpr std::size_t bufSize = 65536;
unique_ptr<char[]> 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;
}

View File

@@ -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<HTTPDirectory*> 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)

View File

@@ -28,6 +28,7 @@
#include <zlib.h>
#include <simgear/sg_inlines.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/misc/sg_dir.hxx>
@@ -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<UstarHeaderBlock*>(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

View File

@@ -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<TarExtractorPrivate> d;
};

View File

@@ -538,6 +538,33 @@ namespace simgear {
unsigned long long readNonNegativeInt<unsigned long long, 16>(
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)
{

View File

@@ -209,6 +209,16 @@ namespace simgear {
typename = typename std::enable_if<std::is_integral<T>::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,

View File

@@ -19,6 +19,7 @@
#include <simgear/compiler.h>
#include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/constants.h>
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;
}

View File

@@ -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 }
};

View File

@@ -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;

View File

@@ -19,16 +19,13 @@
*
*/
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear_config.h>
#include "StateMachine.hxx"
#include <algorithm>
#include <cassert>
#include <set>
#include <boost/foreach.hpp>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGBinding.hxx>
@@ -44,7 +41,7 @@ typedef std::vector<StateMachine::State_ptr> 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);
}

View File

@@ -212,15 +212,23 @@ void testBindings()
void testParse()
{
const char* xml = "<?xml version=\"1.0\"?>"
"<PropertyList>"
"<state>"
"<name>one</name>"
"</state>"
"<state>"
"<name>two</name>"
"</state>"
"</PropertyList>";
const char* xml = R"(<?xml version="1.0"?>
<PropertyList>
<state>
<name>one</name>
<enter>
<command>nasal</command>
<script>print('Foo');</script>
</enter>
<exit>
<command>nasal</command>
<script>print('bar');</script>
</exit>
</state>
<state>
<name>two</name>
</state>
</PropertyList>)";
SGPropertyNode* desc = new SGPropertyNode;
readProperties(xml, strlen(xml), desc);

View File

@@ -1 +1 @@
2017.3.0
2017.4.0