Compare commits

..

149 Commits

Author SHA1 Message Date
Automatic Release Builder
4200572cad new version: 2016.3.1 2016-09-06 12:50:55 +02:00
James Turner
9b997ea1f7 Only reopen the streams if AllocConsole succeeds.
Avoid a crash with FGRun when OSG tries to use the console.
2016-09-02 00:05:13 +01:00
Torsten Dreyer
c1ba974538 Make requested tsync scenery-version settable from prop
set /sim/terrasync/scenery-version=ws30 to filter the DNS NAPTR
records for service=ws30
current default (and only available) scenery version is ws20

This enables usage of multiple scenery repositories
2016-08-29 21:00:03 +02:00
James Turner
d7d59b08a2 Copy-from-install support in TerraSync
Allows repositories to be initialised based on data in the install.
This avoids duplicate downloading of the Model and Airport data,
and the starting scenery.

Requires a corresponding FlightGear change to be functional.
2016-08-18 16:21:31 +01:00
James Turner
6b82b78c7c Warn when OSG is not in UTF-8 mode.
This will become an error in the near future.
2016-08-15 22:38:00 +01:00
James Turner
8201301064 Improve Windows linkage.
When using a patched OSG, avoid /FORCE:MULTIPLE work-around.
2016-08-15 22:38:00 +01:00
James Turner
899778b354 Fix inverted logic in any-bindings test. 2016-08-08 16:59:06 +01:00
James Turner
a31d1342d5 Simplify Aeonwave/OpenAL logic in Cmake slightly 2016-08-06 14:40:14 +01:00
Erik Hofman
8d266491c5 Fix the header location 2016-08-06 11:26:47 +02:00
Erik Hofman
0acbe1f087 Clean up the code a bit and combine soundmgr_openal.hxx and soundmgr_aeonwave.hxx into a single file 2016-08-06 11:25:27 +02:00
Erik Hofman
f8d5e58ccc Clean up the code a bit and combine soundmgr_openal.hxx and soundmgr_aeonwave.hxx into a single file 2016-08-06 11:24:58 +02:00
Erik Hofman
fcd0f15ff2 Remove an unused variable 2016-08-05 11:20:33 +02:00
Erik Hofman
f092f000fa AeonWave is off by default 2016-08-05 10:57:20 +02:00
Alessandro Menti
69b127e2e6 Have the SimGear HTTP client follow redirects 2016-08-04 20:36:38 +02:00
Erik Hofman
65a3d9ed6c Search for the right header file 2016-08-04 18:40:12 +02:00
Erik Hofman
85c4e03823 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-08-04 18:39:54 +02:00
Erik Hofman
7754f88be7 Finish AeonWave integration 2016-08-04 18:38:38 +02:00
Erik Hofman
1e24245d6c Reflect the latest header changes 2016-08-03 10:52:40 +02:00
James Turner
5ea01039f9 Improve pick-callback enabled testing. 2016-07-31 22:21:03 +01:00
James Turner
8b8dbeb00d Quiet a log message 2016-07-31 22:21:03 +01:00
Erik Hofman
52ec6cee85 Add the compile time option to test the return values 2016-07-29 12:36:13 +02:00
Erik Hofman
eb53d4ca78 Add get_no_tracks() 2016-07-29 12:35:43 +02:00
Erik Hofman
f3c3b7ec1b Split include dirs 2016-07-29 12:35:20 +02:00
Erik Hofman
f6e92ac9e5 Update to the latestaienwave.hpp header file 2016-07-26 15:22:09 +02:00
Erik Hofman
48c5e5e43b Update to the latestaienwave.hpp header file 2016-07-26 15:21:26 +02:00
Erik Hofman
7cc9a1753c updates for the aeonwave.hpp header changes 2016-07-25 11:40:40 +02:00
Erik Hofman
068745617c Add support for stereo 2016-07-21 13:05:40 +02:00
Erik Hofman
935c3f901d Add some tests for AeonWave 2016-07-21 13:05:40 +02:00
Erik Hofman
0b60669075 Add some tests for AeonWave 2016-07-21 13:05:40 +02:00
Erik Hofman
488039d1de Either install soundmgr_openal.hxx or soundmgr_aeonwave.hxx as soundmgr.hxx 2016-07-21 13:05:40 +02:00
Erik Hofman
efe9648afa Activate AeonWave by setting USE_AEONWAVE to ON 2016-07-21 13:05:40 +02:00
Erik Hofman
d12cd4945e Get soundmgr_aeonwave in a compilig state 2016-07-21 13:05:40 +02:00
Erik Hofman
968e0b4cd2 Make stereo files a SG_POPUP message 2016-07-21 09:54:11 +02:00
Erik Hofman
d902fffa46 Make a failed wav file a SG_POPUP message 2016-07-20 15:02:24 +02:00
Erik Hofman
3092274cac Add support for SG_POPUP messages which show a dialog at startup 2016-07-20 15:01:30 +02:00
Erik Hofman
5f54388ed9 Be more specific about what audio type is detected and for which file if it is not supported. 2016-07-20 08:37:07 +02:00
Erik Hofman
9b4f1b0ff8 Reluctantly add support for stereo files again: there are external hangars which did not update to the mono-files only principle 2016-07-19 10:44:46 +02:00
Erik Hofman
c48a28beb9 Remove some unusal ocde..??? 2016-07-18 13:49:40 +02:00
Erik Hofman
96986c9377 Try to prevent a crash in unusual situations 2016-07-18 11:59:47 +02:00
Maciej Mrozowski
33bd02f926 FindUdns.cmake: fix check for cached paths 2016-07-17 03:48:32 +02:00
James Turner
93226fc500 Change location used for path tests
Jenkins seems to dislike non-Latin-1 characters inside the build
tree on Linux, so use a location inside /tmp instead.
2016-07-15 17:57:32 +01:00
James Turner
31ba9dfa70 Further SGPath API usage cleanups. 2016-07-15 16:33:52 +01:00
James Turner
19df18fefb Use wide-string APIs on Windows.
SGPath and simgear::Dir use ‘w’ versions of POSIX APIs on Windows,
and convert UTF-8 SGPath to wide-strings as part of this.

Includes improved unit-tests for this code, with some very basic
tests of creating and iterating files with Unicode characters in
their names.

No user-visible changes should result from this, on any platform; in
particular wide-string support is still incomplete so FlightGear will
not yet work with arbitrary Unicode paths on Windows.
2016-07-15 09:50:44 +01:00
James Turner
a5a4bf6d41 Another HLA/SGPath fix 2016-07-04 09:31:34 +01:00
James Turner
9812315d96 realpath returns a path, not a string. 2016-07-04 09:04:46 +01:00
James Turner
c40044feeb Fix HLAFederate for readXML API change 2016-07-04 07:20:23 +01:00
James Turner
a636da6959 SGPath in easyXML API 2016-07-03 23:41:07 +01:00
James Turner
ca84d2046a Fix sg_gzofstream path type (now SGPath) 2016-07-03 23:05:42 +01:00
James Turner
c037a0e461 Windows string conversion for SGPath 2016-07-03 22:56:04 +01:00
Bertrand Coconnier
5ab595b401 The Shlwapi library is now needed for the Windows build (required in simgear/misc/sg_dir.cxx which calls to PathIsDirectoryEmpty) 2016-07-03 14:36:22 +02:00
Bertrand Coconnier
7e06e5382a One (last?) fix for compilation errors with MSVC++ 2016-07-03 12:38:56 +02:00
Bertrand Coconnier
d3c5c45262 Fixed compilation errors with MSVC++ 2016-07-03 11:53:23 +02:00
Erik Hofman
efa1292b2d Fix a compiler wrning 2016-07-03 09:44:04 +02:00
Erik Hofman
98de216878 No need to count all 150 or so entries, 3 is enough 2016-07-03 09:39:52 +02:00
Erik Hofman
82a9491de4 Much shorter version of Dir::isEmpty() 2016-07-03 09:35:03 +02:00
Erik Hofman
b23e9a3424 Detect the actual number of wchars required for the buffer and allocate it properly 2016-07-03 09:34:19 +02:00
Erik Hofman
0e62c11fd0 AeonWave based sound manager 2016-07-03 09:03:37 +02:00
Erik Hofman
5b54481555 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-07-02 11:07:11 +02:00
Erik Hofman
f4344c5c6a Fix directory creation: 'ds' remains the same while 'dir' gets updated so use 'dir' instead 2016-07-02 11:06:36 +02:00
Erik Hofman
372dead21a Convert _filename to an SGPath 2016-07-02 11:03:32 +02:00
James Turner
cf18d4eaaf SGPath can convert to std::wstring 2016-07-02 09:34:27 +01:00
James Turner
32735428bb More SGPath APIs 2016-07-01 17:14:34 +01:00
James Turner
b862cf7e54 Fix repository test. 2016-07-01 09:02:52 +01:00
James Turner
f21eac8473 Building with clean SGPath API 2016-06-30 16:17:52 +01:00
James Turner
38c8931950 Fix debug runtime assert 2016-06-29 16:26:42 +01:00
James Turner
cfe1c0933f Further SG stream APIs 2016-06-27 12:29:23 -05:00
Erik Hofman
a8d8158fac Properly et frequency, format and buffer size 2016-06-27 13:19:37 +02:00
Erik Hofman
2321d9783d Move the isNaN function to soundmgr_openal_private.hxx to make it avaiable for the soundmanager too 2016-06-27 12:35:58 +02:00
James Turner
a3b3280123 iostream overloads taking an SGPath 2016-06-25 16:30:24 +01:00
James Turner
8cfe5a2e08 Native SGPath API on SGInterpTable
- string-based version will be removed in the future.
2016-06-23 15:21:26 +01:00
James Turner
bd896096cc Changing SGPath APIs, using SGPath in more places.
Change most places we control (i.e not helper libs) to use SGPath
to represent a path, instead of using std::string. Extend SGPath
API to explicitly expose the path in either UTF-8 or the
system 8-bit encoding.
2016-06-22 17:15:32 +01:00
James Turner
855ff5a8b0 Fix Linux compilation of untar.hxx 2016-06-16 04:46:37 -05:00
James Turner
e695505e62 Tests for un-tar code. 2016-06-15 22:27:01 +01:00
James Turner
f824cf85a4 Fix Untar namespacing. 2016-06-14 15:13:58 +01:00
James Turner
fb8b60b6fe Export untar header
Needed for scenery installation helper.
2016-06-14 15:12:20 +01:00
James Turner
516d76d41b VS215 tweaks, warning fixes 2016-06-09 20:38:01 +01:00
Jasin Colegrove
d0e31c5cf5 Use STD_ERROR_HANDLE since SG_LOG uses stderr stream
Fixed spacing, cleaned up uneccessary #ifdef's
2016-06-09 14:27:56 -04:00
James Turner
daa10503e6 Fixes to Windows console interaction.
Thanks To Jasin Colegrove for helping me understand the issues here!
2016-06-09 15:11:44 +01:00
Thomas Geymayer
1c25d343a0 Fix missing throw. 2016-06-09 13:03:40 +02:00
James Turner
c762dbe864 Fix dependency on ‘version’ file. 2016-06-09 11:03:35 +01:00
James Turner
37c551bae7 Fix for HTTP/curl waiting on update
This improves responsiveness of TerraSync with small files and general
throughout dramatically.
2016-06-08 15:43:59 +01:00
James Turner
0ccf3e1629 Threadsafe terrasync state updates/reading. 2016-06-08 15:27:47 +01:00
Erik Hofman
da1aeece14 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-06-07 21:51:21 +02:00
Erik Hofman
c722f90848 Only throw an expection if buf == NULL.. 2016-06-07 12:59:16 +02:00
James Turner
5ba9004853 More repository test tweaks for Jenkins. 2016-06-07 11:24:01 +01:00
Erik Hofman
dc1696dfd5 Throw an exception when the current working directory can not be retrieved 2016-06-07 11:51:28 +02:00
James Turner
cb80af0ebe Improving channel lifetime in HTTP-based tests.
Previously, closed channels were not cleaned up, which looks to be
the caused of the test failures on Jenkins.
2016-06-06 17:26:50 +01:00
James Turner
1492de4391 Fix a leak / potential crash 2016-06-06 11:36:47 +01:00
James Turner
fad905e7e0 Increase test timeout value.
- investigating failing tests on Jenkins
2016-06-03 15:26:48 +01:00
James Turner
fb1b1b9c5e Fix uninitialized var 2016-06-03 08:44:25 -05:00
James Turner
b7b304ecfb Fix a warning with GCC 2016-06-03 08:40:55 -05:00
James Turner
8690e4617f CMake tweaks for MSVC detection 2016-06-03 12:19:21 +01:00
James Turner
a6bed69d19 Fix user-after-free in HTTP repo code 2016-06-02 23:53:15 +01:00
James Turner
100e327684 More permissive catalog version checks
- support wildcard prefixes on FlightGear versions
- drop catalog version equality check
2016-06-01 22:46:17 +01:00
James Turner
729c9e3faa More VS2015 fixes 2016-06-01 22:36:36 +01:00
Erik Hofman
add3a934af XMerge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-06-01 23:13:59 +02:00
Erik Hofman
e82e4f8c93 Merge branch 'aeonwave' into next 2016-06-01 23:13:09 +02:00
Erik Hofman
6582d041e6 Make sure block align is in samples when calling alBufferi with AL_UNPACK_BLOCK_ALIGNMENT_SOFT 2016-06-01 23:12:55 +02:00
Jasin Colegrove
92878f37f9 MSVC 12 still requires snprintf to be defined 2016-06-01 10:24:30 -04:00
Erik Hofman
4224d2b86b block_alignment for AL_SOFT_block_alignment is in samples, not in bytes 2016-06-01 14:16:49 +02:00
Erik Hofman
c3db9b9d86 Fix a comment 2016-06-01 10:10:34 +02:00
Erik Hofman
3223f16fe6 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-06-01 09:52:00 +02:00
Erik Hofman
03f7f82856 Revert to the previous way of handling OpenAL. The reason was to find alext.h but that reason has gone by copying a small number of defines 2016-06-01 09:51:41 +02:00
James Turner
be4ebddb60 Fix for VS2015 compilation 2016-05-31 13:41:28 +01:00
James Turner
34e804b784 Fix VS2015 compilation 2016-05-31 13:40:32 +01:00
James Turner
75b2ef9372 Fix VS2015 compilation 2016-05-31 13:40:00 +01:00
Erik Hofman
f71e2e0e9f Merge branch 'next' into aeonwave 2016-05-31 13:40:50 +02:00
Erik Hofman
ce0cdcdcb0 Add the option to define volume and pitch using an expression 2016-05-31 13:40:46 +02:00
Erik Hofman
426912173a Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-05-30 18:36:20 +02:00
Erik Hofman
976c85ff57 Add IMA4 support 2016-05-30 14:17:16 +02:00
James Turner
1ae9e74539 More libCurl version guards. 2016-05-30 13:02:25 +01:00
James Turner
980ae3115c Whitespace fixes. 2016-05-30 13:02:24 +01:00
James Turner
5ca9a06273 FreeBSD fixes from Ganael LAPLANCHE 2016-05-30 13:02:24 +01:00
Erik Hofman
469097ed5b Add test files for mulaw en IMA4 ADPCM 2016-05-30 11:50:11 +02:00
Erik Hofman
ae375f44f2 Add support for native mulaw encoded samples if the OpenAL implementation supports it 2016-05-30 11:46:41 +02:00
Erik Hofman
d1a808c630 Find OpenAL the proper way 2016-05-30 10:50:22 +02:00
Erik Hofman
733c283b1a Do not forget to alter the function declaration as well 2016-05-27 22:43:52 +02:00
Erik Hofman
f65a970d2e Fix two OpenAL related compiler errors 2016-05-27 19:08:20 +02:00
Erik Hofman
ef5b9ee66b Move all OpenAL function calls from SampleMgr to soundMgr 2016-05-27 14:40:49 +02:00
Erik Hofman
c5a94e8899 Switch to out own audio format defines 2016-05-27 11:58:01 +02:00
Erik Hofman
b05488649a Remove unused sample queue code 2016-05-27 11:37:47 +02:00
Erik Hofman
c654c82a3f Remove support for stereo sounds 2016-05-27 10:52:04 +02:00
Erik Hofman
4b7d577883 Merge branch 'next' into aeonwave 2016-05-27 10:03:56 +02:00
James Turner
1b0289b11f HTTP: Always use absolute paths for hashes
Ensure we only compute / retrieve hashes for absolute paths. Fixes
duplicate paths in repository hash cache.
2016-05-26 23:36:03 +01:00
James Turner
00f4248137 Partial update support for HTTP repos.
Not used yet, we set full-update mode on repositories for the
moment. Pending Terrasync change will enable partial mode.
2016-05-26 23:35:11 +01:00
James Turner
8211f1c482 Allow updating a Catalog URL explicitly.
Needed for fallback Catalog URL support.
2016-05-26 17:38:02 +01:00
James Turner
43ebde9914 Expose pending count from terrasync
Total and expose HTTP pending download value as a new
property under terrasync-root.
2016-05-26 17:38:02 +01:00
James Turner
7d59dd977f AbstractRepository interface removed. 2016-05-26 17:38:02 +01:00
James Turner
3e2f37418a Remove SVN sync code. 2016-05-26 17:38:02 +01:00
James Turner
9d2df12ab8 Remove old terraysnc backend methods.
Only in-process HTTP access is supported now, SVN and rsync
and removed. This is to allow changes for better use of the
HTTP API.
2016-05-26 17:38:02 +01:00
Maciej Mrozowski
2b15b6b8ad Add SYSTEM_UDNS CMake option. Remove spurious EXPAT_LIBRARIES linking when using bundled expat. 2016-05-26 02:33:24 +02:00
James Turner
49bd96c55d Guard usage of CURLMOPT_MAX_TOTAL_CONNECTIONS
Check the Curl patch version to avoid breaking on older
sustems.
2016-05-25 20:08:40 +01:00
Erik Hofman
b3d95c0754 Probably a better fix 2016-05-25 14:30:06 +02:00
Erik Hofman
87c2427cfe MSVC 2013 and later define std::isnan 2016-05-25 09:08:52 +02:00
Erik Hofman
857f7c9e61 Revert to c++98 2016-05-24 21:37:12 +02:00
Erik Hofman
91f184caff Do not expose both std::isnan() and isnan() 2016-05-24 17:32:22 +02:00
Erik Hofman
7cab98cf29 First layer of separation between SimGear an OpenAL 2016-05-24 15:47:20 +02:00
Erik Hofman
e9ea5e9036 Revert the check fro std::isnan() and isnan() 2016-05-24 14:51:17 +02:00
Erik Hofman
03a8a2a3fb Switch to c++11 2016-05-24 10:42:01 +02:00
Erik Hofman
f1bffc7397 Add more missing header files 2016-05-24 10:12:39 +02:00
James Turner
5fdc756a64 Fix missing <cstdint> on non-Mac 2016-05-24 08:16:40 +01:00
James Turner
5b71d84a3a Merge /u/elgaton/simgear/ branch specify-standards-fixes into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/15/
2016-05-24 07:02:08 +00:00
Alessandro Menti
b1270376c9 Indentation fixes (and signed/unsigned comparison fix) to silence GCC 6 warnings 2016-05-24 00:11:22 +02:00
James Turner
871b418242 Initial Tar package support.
Needs proper testing, but basic unit-test passes.
2016-05-23 22:23:16 +01:00
Alessandro Menti
fd124e91de CMakeLists.txt: explicitly set the standard to C++98
Set the C++ standard to C++98 to avoid GCC 6 compilation failures.
2016-05-23 23:16:35 +02:00
Florent Rougon
5b71ede2ea Fix missing includes
When SimGear header files are included in a particular order, these
missing includes can cause the compilation to fail.
2016-05-23 12:24:37 +02:00
James Turner
6285a409ed Fix a crash with mismatch package versions. 2016-05-18 19:02:41 +01:00
James Turner
9818a123ca Fix removal of directories. 2016-05-18 10:50:57 +01:00
Automatic Release Builder
09ab029e2f new version: 2016.3.0 2016-05-17 10:03:44 +02:00
132 changed files with 4885 additions and 4526 deletions

View File

@@ -4,6 +4,6 @@ endif()
add_subdirectory(utf8)
if (ENABLE_DNS)
if (ENABLE_DNS AND NOT SYSTEM_UDNS)
add_subdirectory(udns)
endif()

View File

@@ -45,6 +45,7 @@
#endif
#include <sys/types.h> /* for time_t */
#include <time.h>
#ifdef __cplusplus
extern "C" {

View File

@@ -22,12 +22,25 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
# only relevant for building shared libs but let's set it regardless
set(CMAKE_OSX_RPATH 1)
# Set the C++ standard to C++98 to avoid compilation errors on GCC 6 (which
# defaults to C++14).
if(CMAKE_VERSION VERSION_LESS "3.1")
if(CMAKE_COMPILER_IS_GNUCXX)
set (CMAKE_CXX_FLAGS "-std=gnu++98 ${CMAKE_CXX_FLAGS}")
endif()
else()
set (CMAKE_CXX_STANDARD 98)
endif()
project(SimGear)
# read 'version' file into a variable (stripping any newlines or spaces)
file(READ version versionFile)
string(STRIP ${versionFile} SIMGEAR_VERSION)
# add a dependency on the versino file
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
# use simgear version also as the SO version (if building SOs)
@@ -104,21 +117,28 @@ endif()
if (NOT MSVC)
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system libExpat" OFF)
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system expat library" OFF)
option(SYSTEM_UDNS "Set to ON to build SimGear using the system udns library" OFF)
else()
# Building SimGear DLLs is currently not supported for MSVC.
set(SIMGEAR_SHARED OFF)
# Using a system expat is currently not supported for MSVC - it would require shared simgear (DLL).
# Using external 3rd party libraries is currently not supported for MSVC - it would require shared simgear (DLL).
set(SYSTEM_EXPAT OFF)
set(SYSTEM_UDNS OFF)
endif()
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
# until the fstream fix is applied and generally available in OSG,
# keep the compatability link option as the default
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
if (CMAKE_CL_64)
@@ -137,14 +157,17 @@ endif (MSVC)
if (MSVC AND MSVC_3RDPARTY_ROOT)
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
set( OSG_MSVC "msvc" )
if (${MSVC_VERSION} EQUAL 1700)
if (${MSVC_VERSION} EQUAL 1900)
set( OSG_MSVC ${OSG_MSVC}140 )
elseif (${MSVC_VERSION} EQUAL 1800)
set( OSG_MSVC ${OSG_MSVC}120 )
elseif (${MSVC_VERSION} EQUAL 1700)
set( OSG_MSVC ${OSG_MSVC}110 )
elseif (${MSVC_VERSION} EQUAL 1600)
set( OSG_MSVC ${OSG_MSVC}100 )
else (${MSVC_VERSION} EQUAL 1700)
set( OSG_MSVC ${OSG_MSVC}90 )
endif (${MSVC_VERSION} EQUAL 1700)
endif ()
if (CMAKE_CL_64)
set( OSG_MSVC ${OSG_MSVC}-64 )
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
@@ -154,7 +177,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
find_path(BOOST_ROOT boost/version.hpp
PATHS
${MSVC_ROOT_PARENT_DIR}
${MSVC_3RDPARTY_ROOT}/boost
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
@@ -169,8 +196,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
)
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
if (NOT USE_AEONWAVE)
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
endif()
endif (MSVC AND MSVC_3RDPARTY_ROOT)
if(APPLE)
@@ -187,11 +217,9 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
find_package(Threads REQUIRED)
endif()
# Somehow this only works if included before searching for Boost...
include(BoostTestTargets)
find_package(Boost REQUIRED)
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
include(BoostTestTargets)
if(SIMGEAR_HEADLESS)
message(STATUS "SimGear mode: HEADLESS")
@@ -201,11 +229,34 @@ else()
find_package(OpenGL REQUIRED)
if (ENABLE_SOUND)
find_package(OpenAL REQUIRED)
if (USE_AEONWAVE)
find_package(AAX COMPONENTS aax REQUIRED)
include_directories( ${AAX_INCLUDE_DIR} )
else()
find_package(OpenAL REQUIRED)
include_directories( ${OPENAL_INCLUDE_DIR} )
endif()
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
if (MSVC)
set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS})
# ensure OSG was compiled with OSG_USE_UTF8_FILENAME set
check_cxx_source_compiles(
"#include <osg/Config>
#if !defined(OSG_USE_UTF8_FILENAME)
#error OSG UTF8 support not enabled
#endif
int main() { return 0; }"
SIMGEAR_OSG_USE_UTF8_FILENAME)
if (NOT SIMGEAR_OSG_USE_UTF8_FILENAME)
message(WARNING "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
endif()
endif()
endif(SIMGEAR_HEADLESS)
find_package(ZLIB REQUIRED)
@@ -344,21 +395,26 @@ if(WIN32)
endif()
if(MSVC)
# turn off various warnings
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
# endforeach(warning)
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
if (${MSVC_VERSION} GREATER 1599)
if (NOT OSG_FSTREAM_EXPORT_FIXED AND ${MSVC_VERSION} GREATER 1599)
message(STATUS "For better linking performance, use OSG with patched fstream header")
# needed to avoid link errors on multiply-defined standard C++
# symbols. Suspect this may be an OSG-DB export bug
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
endif (${MSVC_VERSION} GREATER 1599)
endif ()
if (${MSVC_VERSION} GREATER 1899)
# needed for debug builds with VS2015
set( MSVC_FLAGS "${MSVC_FLAGS} /bigobj" )
endif()
endif(MSVC)
# assumed on Windows
set(HAVE_GETLOCALTIME 1)
set( WINSOCK_LIBRARY "ws2_32.lib" )
set( SHLWAPI_LIBRARY "Shlwapi.lib" )
set( RT_LIBRARY "winmm" )
endif(WIN32)
@@ -372,10 +428,9 @@ include_directories(BEFORE ${PROJECT_SOURCE_DIR})
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
include_directories(
${Boost_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${OPENAL_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
)
@@ -403,6 +458,7 @@ set(TEST_LIBS_INTERNAL_CORE
${CMAKE_THREAD_LIBS_INIT}
${ZLIB_LIBRARY}
${WINSOCK_LIBRARY}
${SHLWAPI_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY}
@@ -416,9 +472,16 @@ endif()
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
include_directories(3rdparty/utf8/source)
if (ENABLE_DNS)
message(STATUS "DNS resolver: ENABLED")
include_directories(3rdparty/udns)
if(ENABLE_DNS)
if(SYSTEM_UDNS)
message(STATUS "Requested to use system udns library, forcing SIMGEAR_SHARED to true")
set(SIMGEAR_SHARED ON)
find_package(Udns REQUIRED)
else()
message(STATUS "DNS resolver: ENABLED")
include_directories(3rdparty/udns)
endif()
else()
message(STATUS "DNS resolver: DISABLED")
endif()

View File

@@ -0,0 +1,48 @@
# Locate AAX
# This module defines
# AAX_LIBRARIES
# AAX_FOUND, if false, do not try to link to AAX
# AAX_INCLUDE_DIR, where to find the headers
#
# $AAXDIR is an environment variable that would
# correspond to the ./configure --prefix=$AAXDIR
# used in building AAX.
#
# Created by Erik Hofman.
FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
FIND_LIBRARY(AAX_LIBRARY
NAMES AAX aax AAX32 libAAX32
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr
/opt
/usr/local
)
SET(AAX_FOUND "NO")
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
SET(AAX_FOUND "YES")
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)

View File

@@ -0,0 +1,42 @@
# - Try to find UDNS library
# Once done this will define
#
# UDNS_FOUND - system has UDNS
# UDNS_INCLUDE_DIRS - the UDNS include directory
# UDNS_LIBRARIES - Link these to use UDNS
# UDNS_DEFINITIONS - Compiler switches required for using UDNS
#
# Copyright (c) 2016 Maciej Mrozowski <reavertm@gmail.com>
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if (UDNS_LIBRARIES AND UDNS_INCLUDE_DIRS)
# in cache already
set(UDNS_FOUND TRUE)
else ()
set(UDNS_DEFINITIONS "")
find_path(UDNS_INCLUDE_DIRS NAMES udns.h)
find_library(UDNS_LIBRARIES NAMES udns)
if (UDNS_INCLUDE_DIRS AND UDNS_LIBRARIES)
set(UDNS_FOUND TRUE)
endif ()
if (UDNS_FOUND)
if (NOT Udns_FIND_QUIETLY)
message(STATUS "Found UDNS: ${UDNS_LIBRARIES}")
endif ()
else ()
if (Udns_FIND_REQUIRED)
message(FATAL_ERROR "Could not find UDNS")
endif ()
endif ()
# show the UDNS_INCLUDE_DIRS and UDNS_LIBRARIES variables only in the advanced view
mark_as_advanced(UDNS_INCLUDE_DIRS UDNS_LIBRARIES)
endif ()

View File

@@ -128,11 +128,20 @@ target_link_libraries(SimGearCore
${ZLIB_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${EXPAT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${COCOA_LIBRARY}
${CURL_LIBRARIES})
if(SYSTEM_EXPAT)
target_link_libraries(SimGearCore
${EXPAT_LIBRARIES})
endif()
if(ENABLE_DNS AND SYSTEM_UDNS)
target_link_libraries(SimGearCore
${UDNS_LIBRARIES})
endif()
if(NOT SIMGEAR_HEADLESS)
target_link_libraries(SimGearScene
SimGearCore

View File

@@ -227,9 +227,7 @@ std::string SGBucket::gen_base_path() const {
hem, top_lon, pole, top_lat,
hem, main_lon, pole, main_lat);
SGPath path( raw_path );
return path.str();
return raw_path;
}

View File

@@ -80,8 +80,12 @@ void updateBlendingStateGL(VGContext *c, int alphaIsOne)
case VG_BLEND_SRC_OVER: default:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (alphaIsOne) glDisable(GL_BLEND);
else glEnable(GL_BLEND); break;
if (alphaIsOne) {
glDisable(GL_BLEND);
} else {
glEnable(GL_BLEND);
}
break;
};
}

View File

@@ -43,7 +43,7 @@
# if __GNUC__ < 3
# error Time to upgrade. GNU compilers < 3.0 not supported
# elif (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
# warning GCC compilers prior to 3.4 are suspect
# warning GCC compilers prior to 3.4 are suspect
# endif
# define SG_GCC_VERSION (__GNUC__ * 10000 \
@@ -61,29 +61,33 @@
// Microsoft compilers.
//
#ifdef _MSC_VER
# define bcopy(from, to, n) memcpy(to, from, n)
# define strcasecmp stricmp
# if _MSC_VER >= 1200 // msvc++ 6.0 or greater
# define isnan _isnan
# define snprintf _snprintf
# if _MSC_VER >= 1200 // msvc++ 6.0 up to MSVC2013
# if _MSC_VER < 1900
# define bcopy(from, to, n) memcpy(to, from, n)
# define snprintf _snprintf
# define strdup _strdup
# define copysign _copysign
# endif
# if _MSC_VER < 1800
# define isnan _isnan
# endif
# if _MSC_VER < 1500
# define vsnprintf _vsnprintf
# endif
# define copysign _copysign
# define strcasecmp stricmp
# undef min
# undef max
# pragma warning(disable: 4786) // identifier was truncated to '255' characters
# pragma warning(disable: 4244) // conversion from double to float
# pragma warning(disable: 4305) //
# pragma warning(disable: 4305) // truncation from larger type to smaller
# pragma warning(disable: 4267) // conversion from size_t to int / 32-bit type
# pragma warning(disable: 4996) // don't require _ prefix for standard library functions
# pragma warning(disable: 4800) // don't warn about int -> bool performance
# else
# error What version of MSVC++ is this?
# endif
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
#endif // _MSC_VER
@@ -201,4 +205,3 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
//
#endif // _SG_COMPILER_H

View File

@@ -48,7 +48,8 @@ typedef enum {
SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem
SG_ALERT // Very possible impending problem
SG_ALERT, // Very possible impending problem
SG_POPUP // Severe enough to alert using a pop-up window
// SG_EXIT, // Problem (no core)
// SG_ABORT // Abandon ship (core)
} sgDebugPriority;

View File

@@ -36,11 +36,12 @@
#include <simgear/threads/SGQueue.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#ifdef SG_WINDOWS
#if defined (SG_WINDOWS)
// for AllocConsole, OutputDebugString
#include "windows.h"
#include <windows.h>
#endif
const char* debugClassToString(sgDebugClass c)
@@ -106,13 +107,13 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
class FileLogCallback : public simgear::LogCallback
{
public:
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p),
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
{
if (!shouldLog(c, p)) return;
@@ -120,28 +121,29 @@ public:
<< ":" << file << ":" << line << ":" << message << std::endl;
}
private:
std::ofstream m_file;
sg_ofstream m_file;
};
class StderrLogCallback : public simgear::LogCallback
{
public:
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
#ifdef SG_WINDOWS
AllocConsole(); // but only if we want a console
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
#endif
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
#if defined (SG_WINDOWS)
~StderrLogCallback()
{
FreeConsole();
}
#endif
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
fprintf(stderr, "%s\n", aMessage.c_str());
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
// file, line, aMessage.c_str());
@@ -159,12 +161,12 @@ public:
simgear::LogCallback(c, p)
{
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
std::ostringstream os;
os << debugClassToString(c) << ":" << aMessage << std::endl;
OutputDebugStringA(os.str().c_str());
@@ -188,16 +190,16 @@ private:
const char* f, int l, const std::string& msg) :
debugClass(c), debugPriority(p), file(f), line(l),
message(msg)
{
{
}
sgDebugClass debugClass;
sgDebugPriority debugPriority;
const char* file;
int line;
std::string message;
};
class PauseThread
{
public:
@@ -205,7 +207,7 @@ private:
{
m_wasRunning = m_parent->stop();
}
~PauseThread()
{
if (m_wasRunning) {
@@ -218,29 +220,46 @@ private:
};
public:
LogStreamPrivate() :
m_logClass(SG_ALL),
m_logClass(SG_ALL),
m_logPriority(SG_ALERT),
m_isRunning(false),
m_consoleRequested(false)
{
#if !defined(SG_WINDOWS)
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
m_consoleRequested = true;
m_isRunning(false)
{
bool addStderr = true;
#if defined (SG_WINDOWS)
// Check for stream redirection, has to be done before we call
// Attach / AllocConsole
const bool isFile = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_DISK); // Redirect to file?
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
// attach failed, don't install the callback
addStderr = false;
} else if (!isFile) {
// No - OK! now set streams to attached console
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
}
#endif
if (addStderr) {
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
}
#if defined (SG_WINDOWS) && !defined(NDEBUG)
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
#endif
}
~LogStreamPrivate()
{
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
delete cb;
}
}
SGMutex m_lock;
SGBlockingQueue<LogEntry> m_entries;
typedef std::vector<simgear::LogCallback*> CallbackVec;
CallbackVec m_callbacks;
CallbackVec m_callbacks;
/// subset of callbacks which correspond to stdout / console,
/// and hence should dynamically reflect console logging settings
CallbackVec m_consoleCallbacks;
@@ -248,8 +267,7 @@ public:
sgDebugClass m_logClass;
sgDebugPriority m_logPriority;
bool m_isRunning;
bool m_consoleRequested;
void startLog()
{
SGGuard<SGMutex> g(m_lock);
@@ -257,7 +275,7 @@ public:
m_isRunning = true;
start();
}
virtual void run()
{
while (1) {
@@ -267,37 +285,37 @@ public:
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
return;
}
// submit to each installed callback in turn
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
}
}
} // of main thread loop
}
bool stop()
{
SGGuard<SGMutex> g(m_lock);
if (!m_isRunning) {
return false;
}
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
join();
m_isRunning = false;
return true;
}
void addCallback(simgear::LogCallback* cb)
{
PauseThread pause(this);
m_callbacks.push_back(cb);
}
void removeCallback(simgear::LogCallback* cb)
{
PauseThread pause(this);
@@ -306,7 +324,7 @@ public:
m_callbacks.erase(it);
}
}
void setLogLevels( sgDebugClass c, sgDebugPriority p )
{
PauseThread pause(this);
@@ -316,37 +334,26 @@ public:
cb->setLogLevels(c, p);
}
}
bool would_log( sgDebugClass c, sgDebugPriority p ) const
{
if (p >= SG_INFO) return true;
return ((c & m_logClass) != 0 && p >= m_logPriority);
}
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
LogEntry entry(c, p, fileName, line, msg);
m_entries.push(entry);
}
void requestConsole()
{
PauseThread pause(this);
if (m_consoleRequested) {
return;
}
m_consoleRequested = true;
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
}
};
/////////////////////////////////////////////////////////////////////////////
static logstream* global_logstream = NULL;
static LogStreamPrivate* global_privateLogstream = NULL;
static SGMutex global_logStreamLock;
logstream::logstream()
{
@@ -354,6 +361,13 @@ logstream::logstream()
global_privateLogstream->startLog();
}
logstream::~logstream()
{
popup_msgs.clear();
global_privateLogstream->stop();
delete global_privateLogstream;
}
void
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
@@ -362,13 +376,13 @@ logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
void
logstream::addCallback(simgear::LogCallback* cb)
{
{
global_privateLogstream->addCallback(cb);
}
void
logstream::removeCallback(simgear::LogCallback* cb)
{
{
global_privateLogstream->removeCallback(cb);
}
@@ -379,6 +393,30 @@ logstream::log( sgDebugClass c, sgDebugPriority p,
global_privateLogstream->log(c, p, fileName, line, msg);
}
void
logstream::popup( const std::string& msg)
{
popup_msgs.push_back(msg);
}
std::string
logstream::get_popup()
{
std::string rv = "";
if (!popup_msgs.empty())
{
rv = popup_msgs.front();
popup_msgs.erase(popup_msgs.begin());
}
return rv;
}
bool
logstream::has_popup()
{
return (popup_msgs.size() > 0) ? true : false;
}
bool
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
{
@@ -390,7 +428,7 @@ logstream::get_log_classes() const
{
return global_privateLogstream->m_logClass;
}
sgDebugPriority
logstream::get_log_priority() const
{
@@ -402,25 +440,25 @@ logstream::set_log_priority( sgDebugPriority p)
{
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
}
void
logstream::set_log_classes( sgDebugClass c)
{
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
}
logstream&
sglog()
{
// Force initialization of cerr.
static std::ios_base::Init initializer;
// http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
// in the absence of portable memory barrier ops in Simgear,
// let's keep this correct & safe
static SGMutex m;
SGGuard<SGMutex> g(m);
SGGuard<SGMutex> g(global_logStreamLock);
if( !global_logstream )
global_logstream = new logstream();
return *global_logstream;
@@ -429,7 +467,7 @@ sglog()
void
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
{
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
}
namespace simgear
@@ -437,8 +475,14 @@ namespace simgear
void requestConsole()
{
sglog(); // force creation
global_privateLogstream->requestConsole();
// this is a no-op now, stub exists for compatability for the moment.
}
void shutdownLogging()
{
SGGuard<SGMutex> g(global_logStreamLock);
delete global_logstream;
global_logstream = 0;
}
} // of namespace simgear

View File

@@ -29,6 +29,7 @@
#include <simgear/debug/debug_types.h>
#include <sstream>
#include <vector>
// forward decls
class SGPath;
@@ -59,7 +60,9 @@ private:
* moment - on other plaforms it's a no-op
*/
void requestConsole();
void shutdownLogging();
} // of namespace simgear
/**
@@ -68,6 +71,8 @@ void requestConsole();
class logstream
{
public:
~logstream();
static void initGlobalLogstream();
/**
* Set the global log class and priority level.
@@ -94,6 +99,23 @@ public:
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg);
/**
* support for the SG_POPUP logging class
* set the content of the popup message
*/
void popup( const std::string& msg);
/**
* retrieve the contents of the popup message and clear it's internal
* content. The return value may be an empty string.
*/
std::string get_popup();
/**
* return true if a new popup message is available. false otherwise.
*/
bool has_popup();
/**
* \relates logstream
* Return the one and only logstream instance.
@@ -115,6 +137,8 @@ public:
private:
// constructor
logstream();
std::vector<std::string> popup_msgs;
};
logstream& sglog();
@@ -127,16 +151,16 @@ logstream& sglog();
* @param P priority
* @param M message
*/
#ifdef FG_NDEBUG
# define SG_LOG(C,P,M)
#else
# define SG_LOG(C,P,M) do { \
if(sglog().would_log(C,P)) { \
std::ostringstream os; \
os << M; \
# define SG_LOGX(C,P,M) \
do { if(sglog().would_log(C,P)) { \
std::ostringstream os; os << M; \
sglog().log(C, P, __FILE__, __LINE__, os.str()); \
} \
} while(0)
if (P == SG_POPUP) sglog().popup(os.str()); \
} } while(0)
#ifdef FG_NDEBUG
# define SG_LOG(C,P,M) do { if(P == SG_POPUP) SG_LOGX(C,P,M) } while(0)
#else
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
#endif
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)

View File

@@ -648,10 +648,11 @@ bool SGMetar::scanWeather()
weather = pre + weather + post;
weather.erase(weather.length() - 1);
_weather.push_back(weather);
if( ! w.phenomena.empty() )
if( ! w.phenomena.empty() ) {
_weather2.push_back( w );
_grpcount++;
return true;
}
_grpcount++;
return true;
}

View File

@@ -55,12 +55,11 @@ bool SGStarData::load( const SGPath& path ) {
// build the full path name to the stars data base file
SGPath tmp = path;
tmp.append( "stars" );
SG_LOG( SG_ASTRO, SG_INFO, " Loading stars from " << tmp.str() );
SG_LOG( SG_ASTRO, SG_INFO, " Loading stars from " << tmp );
sg_gzifstream in( tmp.str() );
sg_gzifstream in( tmp );
if ( ! in.is_open() ) {
SG_LOG( SG_ASTRO, SG_ALERT, "Cannot open star file: "
<< tmp.str() );
SG_LOG( SG_ASTRO, SG_ALERT, "Cannot open star file: " << tmp );
return false;
}

View File

@@ -26,6 +26,7 @@
#include <algorithm>
#include "simgear/debug/logstream.hxx"
#include "simgear/misc/sg_path.hxx"
#include "RTIFederate.hxx"
#include "RTIFederateFactoryRegistry.hxx"
@@ -798,7 +799,7 @@ HLAFederate::readRTI1516ObjectModelTemplate(const std::string& objectModel)
// This one covers the generic attributes, parameters and data types.
HLAOMTXmlVisitor omtXmlVisitor;
try {
readXML(objectModel, omtXmlVisitor);
readXML(SGPath(objectModel), omtXmlVisitor);
} catch (const sg_throwable& e) {
SG_LOG(SG_IO, SG_ALERT, "Could not open HLA XML object model file: "
<< e.getMessage());

View File

@@ -1,34 +0,0 @@
// AbstractRepository.cxx -- abstract API for TerraSync remote
//
// Copyright (C) 2016 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "AbstractRepository.hxx"
namespace simgear
{
AbstractRepository::~AbstractRepository()
{
}
size_t AbstractRepository::bytesStillToDownload() const
{
return 0;
}
} // of namespace simgear

View File

@@ -1,72 +0,0 @@
// AbstractRepository.hxx - API for terrasyc to access remote server
//
// Copyright (C) 2016 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef SG_IO_ABSTRACT_REPOSITORY_HXX
#define SG_IO_ABSTRACT_REPOSITORY_HXX
#include <string>
#include <simgear/misc/sg_path.hxx>
namespace simgear {
namespace HTTP {
class Client;
}
class AbstractRepository
{
public:
virtual ~AbstractRepository();
virtual SGPath fsBase() const = 0;
virtual void setBaseUrl(const std::string& url) =0;
virtual std::string baseUrl() const = 0;;
virtual HTTP::Client* http() const = 0;
virtual void update() = 0;
virtual bool isDoingSync() const = 0;
virtual size_t bytesStillToDownload() const;
enum ResultCode {
REPO_NO_ERROR = 0,
REPO_ERROR_NOT_FOUND,
REPO_ERROR_SOCKET,
SVN_ERROR_XML,
SVN_ERROR_TXDELTA,
REPO_ERROR_IO,
REPO_ERROR_CHECKSUM,
REPO_ERROR_FILE_NOT_FOUND,
REPO_ERROR_HTTP,
REPO_PARTIAL_UPDATE
};
virtual ResultCode failure() const = 0;
protected:
};
} // of namespace simgear
#endif // of SG_IO_ABSTRACT_REPOSITORY_HXX

View File

@@ -18,12 +18,8 @@ set(HEADERS
HTTPFileRequest.hxx
HTTPMemoryRequest.hxx
HTTPRequest.hxx
AbstractRepository.hxx
DAVMultiStatus.hxx
SVNRepository.hxx
SVNDirectory.hxx
SVNReportParser.hxx
HTTPRepository.hxx
untar.hxx
)
set(SOURCES
@@ -42,12 +38,8 @@ set(SOURCES
HTTPFileRequest.cxx
HTTPMemoryRequest.cxx
HTTPRequest.cxx
AbstractRepository.cxx
DAVMultiStatus.cxx
SVNRepository.cxx
SVNDirectory.cxx
SVNReportParser.cxx
HTTPRepository.cxx
untar.cxx
)
if(ENABLE_DNS)
@@ -59,9 +51,6 @@ simgear_component(io io "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(http_svn http_svn.cxx)
target_link_libraries(http_svn ${TEST_LIBS})
add_executable(test_sock socktest.cxx)
target_link_libraries(test_sock ${TEST_LIBS})
@@ -94,4 +83,12 @@ add_executable(test_repository test_repository.cxx)
target_link_libraries(test_repository ${TEST_LIBS})
add_test(http_repository ${EXECUTABLE_OUTPUT_PATH}/test_repository)
add_executable(test_untar test_untar.cxx)
set_target_properties(test_untar PROPERTIES
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
target_link_libraries(test_untar ${TEST_LIBS})
add_test(untar ${EXECUTABLE_OUTPUT_PATH}/test_untar)
endif(ENABLE_TESTS)

View File

@@ -1,402 +0,0 @@
// DAVMultiStatus.cxx -- parser for WebDAV MultiStatus XML data
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "DAVMultiStatus.hxx"
#include <iostream>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <sstream>
#include <boost/foreach.hpp>
#include "simgear/debug/logstream.hxx"
#include "simgear/misc/strutils.hxx"
#include "simgear/structure/exception.hxx"
#ifdef SYSTEM_EXPAT
# include <expat.h>
#else
# include "sg_expat.h"
#endif
using std::string;
using namespace simgear;
#define DAV_NS "DAV::"
#define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
const char* DAV_MULTISTATUS_TAG = DAV_NS "multistatus";
const char* DAV_RESPONSE_TAG = DAV_NS "response";
const char* DAV_PROPSTAT_TAG = DAV_NS "propstat";
const char* DAV_PROP_TAG = DAV_NS "prop";
const char* DAV_HREF_TAG = DAV_NS "href";
const char* DAV_RESOURCE_TYPE_TAG = DAV_NS "resourcetype";
const char* DAV_CONTENT_TYPE_TAG = DAV_NS "getcontenttype";
const char* DAV_CONTENT_LENGTH_TAG = DAV_NS "getcontentlength";
const char* DAV_VERSIONNAME_TAG = DAV_NS "version-name";
const char* DAV_COLLECTION_TAG = DAV_NS "collection";
const char* DAV_VCC_TAG = DAV_NS "version-controlled-configuration";
const char* SUBVERSION_MD5_CHECKSUM_TAG = SUBVERSION_DAV_NS ":md5-checksum";
DAVResource::DAVResource(const string& href) :
_type(Unknown),
_url(href),
_container(NULL)
{
assert(!href.empty());
if (strutils::ends_with(href, "/")) {
_url = href.substr(0, _url.size() - 1);
}
}
void DAVResource::setVersionName(const std::string& aVersion)
{
_versionName = aVersion;
}
void DAVResource::setVersionControlledConfiguration(const std::string& vcc)
{
_vcc = vcc;
}
void DAVResource::setMD5(const std::string& md5Hex)
{
_md5 = md5Hex;
}
std::string DAVResource::name() const
{
string::size_type index = _url.rfind('/');
if (index != string::npos) {
return _url.substr(index + 1);
}
throw sg_exception("bad DAV resource HREF:" + _url);
}
////////////////////////////////////////////////////////////////////////////
DAVCollection::DAVCollection(const string& href) :
DAVResource(href)
{
_type = DAVResource::Collection;
}
DAVCollection::~DAVCollection()
{
BOOST_FOREACH(DAVResource* c, _contents) {
delete c;
}
}
void DAVCollection::addChild(DAVResource *res)
{
assert(res);
if (res->container() == this) {
return;
}
assert(res->container() == NULL);
assert(std::find(_contents.begin(), _contents.end(), res) == _contents.end());
assert(strutils::starts_with(res->url(), _url));
assert(childWithUrl(res->url()) == NULL);
res->_container = this;
_contents.push_back(res);
}
void DAVCollection::removeChild(DAVResource* res)
{
assert(res);
assert(res->container() == this);
res->_container = NULL;
DAVResourceList::iterator it = std::find(_contents.begin(), _contents.end(), res);
assert(it != _contents.end());
_contents.erase(it);
}
DAVCollection*
DAVCollection::createChildCollection(const std::string& name)
{
DAVCollection* child = new DAVCollection(urlForChildWithName(name));
addChild(child);
return child;
}
DAVResourceList DAVCollection::contents() const
{
return _contents;
}
DAVResource* DAVCollection::childWithUrl(const string& url) const
{
if (url.empty())
return NULL;
BOOST_FOREACH(DAVResource* c, _contents) {
if (c->url() == url) {
return c;
}
}
return NULL;
}
DAVResource* DAVCollection::childWithName(const string& name) const
{
return childWithUrl(urlForChildWithName(name));
}
std::string DAVCollection::urlForChildWithName(const std::string& name) const
{
return url() + "/" + name;
}
///////////////////////////////////////////////////////////////////////////////
class DAVMultiStatus::DAVMultiStatusPrivate
{
public:
DAVMultiStatusPrivate() :
parserInited(false),
valid(false)
{
rootResource = NULL;
}
void startElement (const char * name)
{
if (tagStack.empty()) {
if (strcmp(name, DAV_MULTISTATUS_TAG)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "root element is not " <<
DAV_MULTISTATUS_TAG << ", got:" << name);
} else {
}
} else {
// not at the root element
if (tagStack.back() == DAV_MULTISTATUS_TAG) {
if (strcmp(name, DAV_RESPONSE_TAG)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "multistatus child is not response: saw:"
<< name);
}
}
if (tagStack.back() == DAV_RESOURCE_TYPE_TAG) {
if (!strcmp(name, DAV_COLLECTION_TAG)) {
currentElementType = DAVResource::Collection;
} else {
currentElementType = DAVResource::Unknown;
}
}
}
tagStack.push_back(name);
if (!strcmp(name, DAV_RESPONSE_TAG)) {
currentElementType = DAVResource::Unknown;
currentElementUrl.clear();
currentElementMD5.clear();
currentVersionName.clear();
currentVCC.clear();
}
}
void endElement (const char * name)
{
assert(tagStack.back() == name);
tagStack.pop_back();
if (!strcmp(name, DAV_RESPONSE_TAG)) {
// finish complete response
currentElementUrl = strutils::strip(currentElementUrl);
DAVResource* res = NULL;
if (currentElementType == DAVResource::Collection) {
DAVCollection* col = new DAVCollection(currentElementUrl);
res = col;
} else {
res = new DAVResource(currentElementUrl);
}
res->setVersionName(strutils::strip(currentVersionName));
res->setVersionControlledConfiguration(currentVCC);
if (rootResource &&
strutils::starts_with(currentElementUrl, rootResource->url()))
{
static_cast<DAVCollection*>(rootResource)->addChild(res);
}
if (!rootResource) {
rootResource = res;
}
}
}
void data (const char * s, int length)
{
if (tagStack.back() == DAV_HREF_TAG) {
if (tagN(1) == DAV_RESPONSE_TAG) {
currentElementUrl += string(s, length);
} else if (tagN(1) == DAV_VCC_TAG) {
currentVCC += string(s, length);
}
} else if (tagStack.back() == SUBVERSION_MD5_CHECKSUM_TAG) {
currentElementMD5 = string(s, length);
} else if (tagStack.back() == DAV_VERSIONNAME_TAG) {
currentVersionName = string(s, length);
} else if (tagStack.back() == DAV_CONTENT_LENGTH_TAG) {
std::istringstream is(string(s, length));
is >> currentElementLength;
}
}
void pi (const char * target, const char * data) {}
string tagN(const unsigned int n) const
{
size_t sz = tagStack.size();
if (n >= sz) {
return string();
}
return tagStack[sz - (1 + n)];
}
bool parserInited;
bool valid;
XML_Parser xmlParser;
DAVResource* rootResource;
// in-flight data
string_list tagStack;
DAVResource::Type currentElementType;
string currentElementUrl,
currentVersionName,
currentVCC;
int currentElementLength;
string currentElementMD5;
};
////////////////////////////////////////////////////////////////////////
// Static callback functions for Expat.
////////////////////////////////////////////////////////////////////////
#define VISITOR static_cast<DAVMultiStatus::DAVMultiStatusPrivate *>(userData)
static void
start_element (void * userData, const char * name, const char ** atts)
{
VISITOR->startElement(name);
}
static void
end_element (void * userData, const char * name)
{
VISITOR->endElement(name);
}
static void
character_data (void * userData, const char * s, int len)
{
VISITOR->data(s, len);
}
static void
processing_instruction (void * userData,
const char * target,
const char * data)
{
VISITOR->pi(target, data);
}
#undef VISITOR
///////////////////////////////////////////////////////////////////////////////
DAVMultiStatus::DAVMultiStatus() :
_d(new DAVMultiStatusPrivate)
{
}
DAVMultiStatus::~DAVMultiStatus()
{
}
void DAVMultiStatus::parseXML(const char* data, int size)
{
if (!_d->parserInited) {
_d->xmlParser = XML_ParserCreateNS(0, ':');
XML_SetUserData(_d->xmlParser, _d.get());
XML_SetElementHandler(_d->xmlParser, start_element, end_element);
XML_SetCharacterDataHandler(_d->xmlParser, character_data);
XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
_d->parserInited = true;
}
if (!XML_Parse(_d->xmlParser, data, size, false)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
XML_ParserFree(_d->xmlParser);
_d->parserInited = false;
_d->valid = false;
}
}
void DAVMultiStatus::finishParse()
{
if (_d->parserInited) {
if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
_d->valid = false;
} else {
_d->valid = true;
}
XML_ParserFree(_d->xmlParser);
}
_d->parserInited = false;
}
DAVResource* DAVMultiStatus::resource()
{
return _d->rootResource;
}
bool DAVMultiStatus::isValid() const
{
return _d->valid;
}

View File

@@ -1,143 +0,0 @@
// DAVMultiStatus.hxx -- parser for WebDAV MultiStatus XML data
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef SG_IO_DAVMULTISTATUS_HXX
#define SG_IO_DAVMULTISTATUS_HXX
#include <string>
#include <vector>
#include <memory> // for auto_ptr
namespace simgear
{
class DAVCollection;
class DAVResource
{
public:
DAVResource(const std::string& url);
virtual ~DAVResource() { }
typedef enum {
Unknown = 0,
Collection = 1
} Type;
const Type type() const
{ return _type; }
const std::string& url() const
{ return _url; }
std::string name() const;
/**
* SVN servers use this field to expose the head revision
* of the resource, which is useful
*/
const std::string& versionName() const
{ return _versionName; }
void setVersionName(const std::string& aVersion);
DAVCollection* container() const
{ return _container; }
virtual bool isCollection() const
{ return false; }
void setVersionControlledConfiguration(const std::string& vcc);
const std::string& versionControlledConfiguration() const
{ return _vcc; }
void setMD5(const std::string& md5Hex);
const std::string& md5() const
{ return _md5; }
protected:
friend class DAVCollection;
Type _type;
std::string _url;
std::string _versionName;
std::string _vcc;
std::string _md5;
DAVCollection* _container;
};
typedef std::vector<DAVResource*> DAVResourceList;
class DAVCollection : public DAVResource
{
public:
DAVCollection(const std::string& url);
virtual ~DAVCollection();
DAVResourceList contents() const;
void addChild(DAVResource* res);
void removeChild(DAVResource* res);
DAVCollection* createChildCollection(const std::string& name);
/**
* find the collection member with the specified URL, or return NULL
* if no such member of this collection exists.
*/
DAVResource* childWithUrl(const std::string& url) const;
/**
* find the collection member with the specified name, or return NULL
*/
DAVResource* childWithName(const std::string& name) const;
/**
* wrapper around URL manipulation
*/
std::string urlForChildWithName(const std::string& name) const;
virtual bool isCollection() const
{ return true; }
private:
DAVResourceList _contents;
};
class DAVMultiStatus
{
public:
DAVMultiStatus();
~DAVMultiStatus();
// incremental XML parsing
void parseXML(const char* data, int size);
void finishParse();
bool isValid() const;
DAVResource* resource();
class DAVMultiStatusPrivate;
private:
std::auto_ptr<DAVMultiStatusPrivate> _d;
};
} // of namespace simgear
#endif // of SG_IO_DAVMULTISTATUS_HXX

View File

@@ -27,6 +27,8 @@
#include <memory> // for std::auto_ptr
#include <string>
#include <vector>
#include <ctime> // for time_t
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>

View File

@@ -80,13 +80,13 @@ public:
// see https://curl.haxx.se/libcurl/c/CURLMOPT_PIPELINING.html
// we request HTTP 1.1 pipelining
curl_multi_setopt(curlMulti, CURLMOPT_PIPELINING, 1 /* aka CURLPIPE_HTTP1 */);
#if (LIBCURL_VERSION_MINOR >= 30)
curl_multi_setopt(curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long) maxConnections);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH,
(long) maxPipelineDepth);
curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS,
(long) maxHostConnections);
#endif
}
typedef std::map<Request_ptr, CURL*> RequestCurlMap;
@@ -138,24 +138,38 @@ Client::~Client()
void Client::setMaxConnections(unsigned int maxCon)
{
d->maxConnections = maxCon;
#if (LIBCURL_VERSION_MINOR >= 30)
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long) maxCon);
#endif
}
void Client::setMaxHostConnections(unsigned int maxHostCon)
{
d->maxHostConnections = maxHostCon;
#if (LIBCURL_VERSION_MINOR >= 30)
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS, (long) maxHostCon);
#endif
}
void Client::setMaxPipelineDepth(unsigned int depth)
{
d->maxPipelineDepth = depth;
#if (LIBCURL_VERSION_MINOR >= 30)
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH, (long) depth);
#endif
}
void Client::update(int waitTimeout)
{
int remainingActive, messagesInQueue;
if (d->requests.empty()) {
// curl_multi_wait returns immediately if there's no requests active,
// but that can cause high CPU usage for us.
SGTimeStamp::sleepForMSec(waitTimeout);
return;
}
int remainingActive, messagesInQueue, numFds;
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
curl_multi_perform(d->curlMulti, &remainingActive);
CURLMsg* msg;
@@ -196,7 +210,6 @@ void Client::update(int waitTimeout)
SG_LOG(SG_IO, SG_ALERT, "unknown CurlMSG:" << msg->msg);
}
} // of curl message processing loop
SGTimeStamp::sleepForMSec(waitTimeout);
}
void Client::makeRequest(const Request_ptr& r)
@@ -231,6 +244,8 @@ void Client::makeRequest(const Request_ptr& r)
curl_easy_setopt(curlRequest, CURLOPT_USERAGENT, d->userAgent.c_str());
curl_easy_setopt(curlRequest, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
if (!d->proxy.empty()) {
curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str());
curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort);
@@ -300,7 +315,9 @@ void Client::cancelRequest(const Request_ptr &r, std::string reason)
}
CURLMcode err = curl_multi_remove_handle(d->curlMulti, it->second);
assert(err == CURLM_OK);
if (err != CURLM_OK) {
SG_LOG(SG_IO, SG_WARN, "curl_multi_remove_handle failed:" << err);
}
// clear the request pointer form the curl-easy object
curl_easy_setopt(it->second, CURLOPT_PRIVATE, 0);

View File

@@ -41,14 +41,13 @@ namespace HTTP
if( responseCode() != 200 )
return setFailure(responseCode(), responseReason());
if( !_filename.empty() )
if( !_filename.isNull() )
{
// TODO validate path? (would require to expose fgValidatePath somehow to
// simgear)
SGPath path(_filename);
path.create_dir(0755);
_filename.create_dir(0755);
_file.open(_filename.c_str(), std::ios::binary | std::ios::trunc);
_file.open(_filename, std::ios::binary | std::ios::trunc | std::ios::out);
}
if( !_file )

View File

@@ -19,8 +19,11 @@
#ifndef SG_HTTP_FILEREQUEST_HXX_
#define SG_HTTP_FILEREQUEST_HXX_
#include <simgear/misc/sg_path.hxx>
#include "HTTPRequest.hxx"
#include <fstream>
#include <simgear/misc/sgstream.hxx>
namespace simgear
{
@@ -43,8 +46,8 @@ namespace HTTP
FileRequest(const std::string& url, const std::string& path);
protected:
std::string _filename;
std::ofstream _file;
SGPath _filename;
sg_ofstream _file;
virtual void responseHeadersComplete();
virtual void gotBodyData(const char* s, int n);

View File

@@ -57,8 +57,6 @@ namespace simgear
{
}
virtual void cancel();
size_t contentSize() const
{
return _contentSize;
@@ -94,7 +92,7 @@ public:
struct Failure
{
SGPath path;
AbstractRepository::ResultCode error;
HTTPRepository::ResultCode error;
};
typedef std::vector<Failure> FailureList;
@@ -104,7 +102,8 @@ public:
hashCacheDirty(false),
p(parent),
isUpdating(false),
status(AbstractRepository::REPO_NO_ERROR),
updateEverything(false),
status(HTTPRepository::REPO_NO_ERROR),
totalDownloaded(0)
{ ; }
@@ -115,10 +114,14 @@ public:
std::string baseUrl;
SGPath basePath;
bool isUpdating;
AbstractRepository::ResultCode status;
bool updateEverything;
string_list updatePaths;
HTTPRepository::ResultCode status;
HTTPDirectory* rootDir;
size_t totalDownloaded;
void updateWaiting();
HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
size_t sz);
HTTP::Request_ptr updateDir(HTTPDirectory* dir, const std::string& hash,
@@ -130,9 +133,9 @@ public:
std::string computeHashForPath(const SGPath& p);
void writeHashCache();
void failedToGetRootIndex(AbstractRepository::ResultCode st);
void failedToGetRootIndex(HTTPRepository::ResultCode st);
void failedToUpdateChild(const SGPath& relativePath,
AbstractRepository::ResultCode fileStatus);
HTTPRepository::ResultCode fileStatus);
typedef std::vector<RepoRequestPtr> RequestVector;
RequestVector queuedRequests,
@@ -147,6 +150,7 @@ public:
typedef std::vector<HTTPDirectory*> DirectoryVector;
DirectoryVector directories;
SGPath installedCopyPath;
};
class HTTPDirectory
@@ -192,10 +196,12 @@ class HTTPDirectory
typedef std::vector<ChildInfo> ChildInfoList;
ChildInfoList children;
public:
HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
_repository(repo),
_relativePath(path)
_relativePath(path),
_state(DoNotUpdate)
{
assert(repo);
@@ -219,27 +225,30 @@ public:
std::string url() const
{
if (_relativePath.str().empty()) {
if (_relativePath.empty()) {
return _repository->baseUrl;
}
return _repository->baseUrl + "/" + _relativePath.str();
return _repository->baseUrl + "/" + _relativePath;
}
void dirIndexUpdated(const std::string& hash)
{
SGPath fpath(_relativePath);
SGPath fpath(absolutePath());
fpath.append(".dirindex");
_repository->updatedFileContents(fpath, hash);
_state = Updated;
children.clear();
parseDirIndex(children);
std::sort(children.begin(), children.end());
}
void failedToUpdate(AbstractRepository::ResultCode status)
void failedToUpdate(HTTPRepository::ResultCode status)
{
if (_relativePath.isNull()) {
_state = UpdateFailed;
if (_relativePath.empty()) {
// root dir failed
_repository->failedToGetRootIndex(status);
} else {
@@ -247,9 +256,63 @@ public:
}
}
void copyInstalledChildren()
{
if (_repository->installedCopyPath.isNull()) {
return;
}
string_list indexNames = indexChildren();
const_string_list_iterator nameIt = indexNames.begin();
for (; nameIt != indexNames.end(); ++nameIt) {
SGPath p(absolutePath());
p.append(*nameIt);
if (p.exists()) {
continue; // only copy if the file is missing entirely
}
ChildInfoList::iterator c = findIndexChild(*nameIt);
if (c->type == ChildInfo::DirectoryType) {
continue; // only care about files
}
SGPath cp = _repository->installedCopyPath;
cp.append(relativePath());
cp.append(*nameIt);
if (!cp.exists()) {
continue;
}
SG_LOG(SG_TERRASYNC, SG_BULK, "new child, copying existing file" << cp << p);
SGBinaryFile src(cp);
SGBinaryFile dst(p);
src.open(SG_IO_IN);
dst.open(SG_IO_OUT);
char* buf = (char*) malloc(cp.sizeInBytes());
if (!buf) {
continue;
}
src.read(buf, cp.sizeInBytes());
dst.write(buf, cp.sizeInBytes());
src.close();
dst.close();
free(buf);
}
}
void updateChildrenBasedOnHash()
{
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
// if we got here for a dir which is still updating or excluded
// from updates, just bail out right now.
if (_state != Updated) {
return;
}
copyInstalledChildren();
string_list indexNames = indexChildren(),
toBeUpdated, orphans;
@@ -281,9 +344,10 @@ public:
// perform a recursive check.
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << it->file() );
if (c->type == ChildInfo::DirectoryType) {
SGPath p(relativePath());
p.append(it->file());
HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
HTTPDirectory* childDir = childDirectory(it->file());
if (childDir->_state == NotUpdated) {
childDir->_state = Updated;
}
childDir->updateChildrenBasedOnHash();
}
}
@@ -301,6 +365,101 @@ public:
scheduleUpdates(toBeUpdated);
}
void markAsUpToDate()
{
_state = Updated;
}
void markAsUpdating()
{
assert(_state == NotUpdated);
_state = HTTPDirectory::UpdateInProgress;
}
void markAsEnabled()
{
// assert because this should only get invoked on newly created
// directory objects which are inside the sub-tree(s) to be updated
assert(_state == DoNotUpdate);
_state = NotUpdated;
}
void markSubtreeAsNeedingUpdate()
{
if (_state == Updated) {
_state = NotUpdated; // reset back to not-updated
}
ChildInfoList::iterator cit;
for (cit = children.begin(); cit != children.end(); ++cit) {
if (cit->type == ChildInfo::DirectoryType) {
HTTPDirectory* childDir = childDirectory(cit->name);
childDir->markSubtreeAsNeedingUpdate();
}
} // of child iteration
}
void markSubtreeAsEnabled()
{
if (_state == DoNotUpdate) {
markAsEnabled();
}
ChildInfoList::iterator cit;
for (cit = children.begin(); cit != children.end(); ++cit) {
if (cit->type == ChildInfo::DirectoryType) {
HTTPDirectory* childDir = childDirectory(cit->name);
childDir->markSubtreeAsEnabled();
}
} // of child iteration
}
void markAncestorChainAsEnabled()
{
if (_state == DoNotUpdate) {
markAsEnabled();
}
if (_relativePath.empty()) {
return;
}
std::string prPath = SGPath(_relativePath).dir();
if (prPath.empty()) {
_repository->rootDir->markAncestorChainAsEnabled();
} else {
HTTPDirectory* prDir = _repository->getOrCreateDirectory(prPath);
prDir->markAncestorChainAsEnabled();
}
}
void updateIfWaiting(const std::string& hash, size_t sz)
{
if (_state == NotUpdated) {
_repository->updateDir(this, hash, sz);
return;
}
if ((_state == DoNotUpdate) || (_state == UpdateInProgress)) {
return;
}
ChildInfoList::iterator cit;
for (cit = children.begin(); cit != children.end(); ++cit) {
if (cit->type == ChildInfo::DirectoryType) {
HTTPDirectory* childDir = childDirectory(cit->name);
childDir->updateIfWaiting(cit->hash, cit->sizeInBytes);
}
} // of child iteration
}
HTTPDirectory* childDirectory(const std::string& name)
{
std::string childPath = relativePath().empty() ? name : relativePath() + "/" + name;
return _repository->getOrCreateDirectory(childPath);
}
void removeOrphans(const string_list& orphans)
{
string_list::const_iterator it;
@@ -334,9 +493,12 @@ public:
if (cit->type == ChildInfo::FileType) {
_repository->updateFile(this, *it, cit->sizeInBytes);
} else {
SGPath p(relativePath());
p.append(*it);
HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
HTTPDirectory* childDir = childDirectory(*it);
if (childDir->_state == DoNotUpdate) {
SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, child:" << *it << " is marked do not update so skipping");
continue;
}
_repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
}
}
@@ -345,11 +507,11 @@ public:
SGPath absolutePath() const
{
SGPath r(_repository->basePath);
r.append(_relativePath.str());
r.append(_relativePath);
return r;
}
SGPath relativePath() const
std::string relativePath() const
{
return _relativePath;
}
@@ -361,21 +523,22 @@ public:
if (it == children.end()) {
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
} else {
SGPath fpath(_relativePath);
SGPath fpath(absolutePath());
fpath.append(file);
if (it->hash != hash) {
_repository->failedToUpdateChild(_relativePath, AbstractRepository::REPO_ERROR_CHECKSUM);
// we don't erase the file on a hash mismatch, becuase if we're syncing during the
// middle of a server-side update, the downloaded file may actually become valid.
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
} else {
_repository->updatedFileContents(fpath, hash);
_repository->totalDownloaded += sz;
//SG_LOG(SG_TERRASYNC, SG_INFO, "did update:" << fpath);
} // of hash matches
} // of found in child list
}
void didFailToUpdateFile(const std::string& file,
AbstractRepository::ResultCode status)
HTTPRepository::ResultCode status)
{
SGPath fpath(_relativePath);
fpath.append(file);
@@ -405,7 +568,7 @@ private:
return false;
}
std::ifstream indexStream( p.c_str(), std::ios::in );
sg_ifstream indexStream(p, std::ios::in );
if ( !indexStream.is_open() ) {
throw sg_io_exception("cannot open dirIndex file", p);
@@ -465,14 +628,12 @@ private:
p.append(name);
bool ok;
SGPath fpath(_relativePath);
fpath.append(name);
std::string fpath = _relativePath + "/" + name;
if (p.isDir()) {
ok = _repository->deleteDirectory(fpath.str());
ok = _repository->deleteDirectory(fpath);
} else {
// remove the hash cache entry
_repository->updatedFileContents(fpath, std::string());
_repository->updatedFileContents(p, std::string());
ok = p.remove();
}
@@ -492,10 +653,19 @@ private:
return _repository->hashForPath(p);
}
HTTPRepoPrivate* _repository;
SGPath _relativePath; // in URL and file-system space
HTTPRepoPrivate* _repository;
std::string _relativePath; // in URL and file-system space
typedef enum
{
NotUpdated,
UpdateInProgress,
Updated,
UpdateFailed,
DoNotUpdate
} State;
State _state;
};
HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
@@ -533,14 +703,38 @@ SGPath HTTPRepository::fsBase() const
void HTTPRepository::update()
{
if (_d->isUpdating) {
_d->rootDir->markSubtreeAsNeedingUpdate();
_d->updateWaiting();
}
void HTTPRepository::setEntireRepositoryMode()
{
if (!_d->updateEverything) {
// this is a one-way decision
_d->updateEverything = true;
}
// probably overkill but not expensive so let's check everything
// we have in case someone did something funky and switched from partial
// to 'whole repo' updating.
_d->rootDir->markSubtreeAsEnabled();
}
void HTTPRepository::addSubpath(const std::string& relPath)
{
if (_d->updateEverything) {
SG_LOG(SG_TERRASYNC, SG_WARN, "called HTTPRepository::addSubpath but updating everything");
return;
}
_d->status = REPO_NO_ERROR;
_d->isUpdating = true;
_d->failures.clear();
_d->updateDir(_d->rootDir, std::string(), 0);
_d->updatePaths.push_back(relPath);
HTTPDirectory* dir = _d->getOrCreateDirectory(relPath);
dir->markSubtreeAsEnabled();
dir->markAncestorChainAsEnabled();
_d->updateWaiting();
}
bool HTTPRepository::isDoingSync() const
@@ -580,7 +774,12 @@ size_t HTTPRepository::bytesDownloaded() const
return result;
}
AbstractRepository::ResultCode
void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
{
_d->installedCopyPath = copyPath;
}
HTTPRepository::ResultCode
HTTPRepository::failure() const
{
if ((_d->status == REPO_NO_ERROR) && !_d->failures.empty()) {
@@ -590,12 +789,6 @@ HTTPRepository::failure() const
return _d->status;
}
void HTTPRepoGetRequest::cancel()
{
_directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
_directory = 0;
}
class FileGetRequest : public HTTPRepoGetRequest
{
public:
@@ -605,14 +798,13 @@ HTTPRepository::failure() const
{
pathInRepo = _directory->absolutePath();
pathInRepo.append(fileName);
//SG_LOG(SG_TERRASYNC, SG_INFO, "will GET file " << url());
}
protected:
virtual void gotBodyData(const char* s, int n)
{
if (!file.get()) {
file.reset(new SGBinaryFile(pathInRepo.str()));
file.reset(new SGBinaryFile(pathInRepo));
if (!file->open(SG_IO_OUT)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "unable to create file " << pathInRepo);
_directory->repository()->http->cancelRequest(this, "Unable to create output file");
@@ -628,16 +820,17 @@ HTTPRepository::failure() const
virtual void onDone()
{
file->close();
if (responseCode() == 200) {
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
_directory->didUpdateFile(fileName, hash, contentSize());
SG_LOG(SG_TERRASYNC, SG_DEBUG, "got file " << fileName << " in " << _directory->absolutePath());
} else if (responseCode() == 404) {
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file not found on server: " << fileName << " for " << _directory->absolutePath());
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file download error on server: " << fileName << " for " << _directory->absolutePath() << ": " << responseCode() );
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_HTTP);
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_HTTP);
}
_directory->repository()->finishedRequest(this);
@@ -649,9 +842,9 @@ HTTPRepository::failure() const
if (pathInRepo.exists()) {
pathInRepo.remove();
}
if (_directory) {
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_SOCKET);
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_SOCKET);
_directory->repository()->finishedRequest(this);
}
}
@@ -676,7 +869,6 @@ HTTPRepository::failure() const
_targetHash(targetHash)
{
sha1_init(&hashContext);
//SG_LOG(SG_TERRASYNC, SG_INFO, "will GET dir " << url());
}
void setIsRootDir()
@@ -701,7 +893,7 @@ HTTPRepository::failure() const
if (responseCode() == 200) {
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
if (!_targetHash.empty() && (hash != _targetHash)) {
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_CHECKSUM);
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_CHECKSUM);
_directory->repository()->finishedRequest(this);
return;
}
@@ -717,16 +909,16 @@ HTTPRepository::failure() const
// dir index data has changed, so write to disk and update
// the hash accordingly
std::ofstream of(pathInRepo().c_str(), std::ios::trunc | std::ios::out);
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out);
if (!of.is_open()) {
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo().c_str());
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo());
}
of.write(body.data(), body.size());
of.close();
_directory->dirIndexUpdated(hash);
//SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
} else {
_directory->markAsUpToDate();
}
_directory->repository()->totalDownloaded += contentSize();
@@ -739,12 +931,12 @@ HTTPRepository::failure() const
_directory->updateChildrenBasedOnHash();
SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
} catch (sg_exception& ) {
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_IO);
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_IO);
}
} else if (responseCode() == 404) {
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
} else {
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_HTTP);
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_HTTP);
}
_directory->repository()->finishedRequest(this);
@@ -753,7 +945,7 @@ HTTPRepository::failure() const
virtual void onFail()
{
if (_directory) {
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_SOCKET);
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_SOCKET);
_directory->repository()->finishedRequest(this);
}
}
@@ -778,15 +970,18 @@ HTTPRepository::failure() const
HTTPRepoPrivate::~HTTPRepoPrivate()
{
// take a copy since cancelRequest will fail and hence remove
// remove activeRequests, invalidating any iterator to it.
RequestVector copyOfActive(activeRequests);
RequestVector::iterator rq;
for (rq = copyOfActive.begin(); rq != copyOfActive.end(); ++rq) {
http->cancelRequest(*rq, "Repository object deleted");
}
DirectoryVector::iterator it;
for (it=directories.begin(); it != directories.end(); ++it) {
delete *it;
}
RequestVector::iterator r;
for (r=activeRequests.begin(); r != activeRequests.end(); ++r) {
(*r)->cancel();
}
}
HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz)
@@ -799,6 +994,7 @@ HTTPRepository::failure() const
HTTP::Request_ptr HTTPRepoPrivate::updateDir(HTTPDirectory* dir, const std::string& hash, size_t sz)
{
dir->markAsUpdating();
RepoRequestPtr r(new DirGetRequest(dir, hash));
r->setContentSize(sz);
makeRequest(r);
@@ -809,7 +1005,7 @@ HTTPRepository::failure() const
class HashEntryWithPath
{
public:
HashEntryWithPath(const std::string& p) : path(p) {}
HashEntryWithPath(const SGPath& p) : path(p.utf8Str()) {}
bool operator()(const HTTPRepoPrivate::HashCacheEntry& entry) const
{ return entry.filePath == path; }
private:
@@ -818,7 +1014,7 @@ HTTPRepository::failure() const
std::string HTTPRepoPrivate::hashForPath(const SGPath& p)
{
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p));
if (it != hashes.end()) {
// ensure data on disk hasn't changed.
// we could also use the file type here if we were paranoid
@@ -843,7 +1039,7 @@ HTTPRepository::failure() const
sha1_init(&info);
char* buf = static_cast<char*>(malloc(1024 * 1024));
size_t readLen;
SGBinaryFile f(p.str());
SGBinaryFile f(p);
if (!f.open(SG_IO_IN)) {
throw sg_io_exception("Couldn't open file for compute hash", p);
}
@@ -860,7 +1056,7 @@ HTTPRepository::failure() const
void HTTPRepoPrivate::updatedFileContents(const SGPath& p, const std::string& newHash)
{
// remove the existing entry
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p));
if (it != hashes.end()) {
hashes.erase(it);
hashCacheDirty = true;
@@ -876,7 +1072,7 @@ HTTPRepository::failure() const
p2.set_cached(true);
HashCacheEntry entry;
entry.filePath = p.str();
entry.filePath = p.utf8Str();
entry.hashHex = newHash;
entry.modTime = p2.modTime();
entry.lengthBytes = p2.sizeInBytes();
@@ -893,8 +1089,7 @@ HTTPRepository::failure() const
SGPath cachePath = basePath;
cachePath.append(".hashes");
std::ofstream stream(cachePath.c_str(),std::ios::out | std::ios::trunc);
sg_ofstream stream(cachePath, std::ios::out | std::ios::trunc);
HashCache::const_iterator it;
for (it = hashes.begin(); it != hashes.end(); ++it) {
stream << it->filePath << ":" << it->modTime << ":"
@@ -913,7 +1108,7 @@ HTTPRepository::failure() const
return;
}
std::ifstream stream(cachePath.c_str(), std::ios::in);
sg_ifstream stream(cachePath, std::ios::in);
while (!stream.eof()) {
std::string line;
@@ -924,7 +1119,7 @@ HTTPRepository::failure() const
string_list tokens = simgear::strutils::split( line, ":" );
if( tokens.size() < 4 ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath << "': '" << line << "' (ignoring line)");
continue;
}
const std::string nameData = simgear::strutils::strip(tokens[0]);
@@ -933,7 +1128,7 @@ HTTPRepository::failure() const
const std::string hashData = simgear::strutils::strip(tokens[3]);
if (nameData.empty() || timeData.empty() || sizeData.empty() || hashData.empty() ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath << "': '" << line << "' (ignoring line)");
continue;
}
@@ -951,7 +1146,7 @@ HTTPRepository::failure() const
public:
DirectoryWithPath(const std::string& p) : path(p) {}
bool operator()(const HTTPDirectory* entry) const
{ return entry->relativePath().str() == path; }
{ return entry->relativePath() == path; }
private:
std::string path;
};
@@ -966,6 +1161,25 @@ HTTPRepository::failure() const
HTTPDirectory* d = new HTTPDirectory(this, path);
directories.push_back(d);
if (updateEverything) {
d->markAsEnabled();
} else {
string_list::const_iterator s;
bool shouldUpdate = false;
for (s = updatePaths.begin(); s != updatePaths.end(); ++s) {
size_t minLen = std::min(path.size(), s->size());
if (s->compare(0, minLen, path, 0, minLen) == 0) {
shouldUpdate = true;
break;
}
} // of paths iteration
if (shouldUpdate) {
d->markAsEnabled();
}
}
return d;
}
@@ -978,10 +1192,11 @@ HTTPRepository::failure() const
directories.erase(it);
Dir dir(d->absolutePath());
bool result = dir.remove(true);
delete d;
// update the hash cache too
updatedFileContents(path, std::string());
updatedFileContents(d->absolutePath(), std::string());
delete d;
return result;
}
@@ -1002,10 +1217,11 @@ HTTPRepository::failure() const
void HTTPRepoPrivate::finishedRequest(const RepoRequestPtr& req)
{
RequestVector::iterator it = std::find(activeRequests.begin(), activeRequests.end(), req);
if (it == activeRequests.end()) {
throw sg_exception("lost request somehow", req->url());
// in some cases, for example a checksum failure, we clear the active
// and queued request vectors, so the ::find above can fail
if (it != activeRequests.end()) {
activeRequests.erase(it);
}
activeRequests.erase(it);
if (!queuedRequests.empty()) {
RepoRequestPtr rr = queuedRequests.front();
@@ -1021,15 +1237,36 @@ HTTPRepository::failure() const
}
}
void HTTPRepoPrivate::failedToGetRootIndex(AbstractRepository::ResultCode st)
void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st)
{
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl);
status = st;
}
void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath,
AbstractRepository::ResultCode fileStatus)
HTTPRepository::ResultCode fileStatus)
{
if (fileStatus == HTTPRepository::REPO_ERROR_CHECKSUM) {
// stop updating, and mark repository as failed, becuase this
// usually indicates we need to start a fresh update from the
// root.
// (we could issue a retry here, but we leave that to higher layers)
status = fileStatus;
queuedRequests.clear();
RequestVector copyOfActive(activeRequests);
RequestVector::iterator rq;
for (rq = copyOfActive.begin(); rq != copyOfActive.end(); ++rq) {
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "cancelling request for:" << (*rq)->url());
http->cancelRequest(*rq, "Repository updated failed");
}
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update repository:" << baseUrl
<< ", possibly modified during sync");
}
Failure f;
f.path = relativePath;
f.error = fileStatus;
@@ -1038,6 +1275,22 @@ HTTPRepository::failure() const
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
}
void HTTPRepoPrivate::updateWaiting()
{
if (!isUpdating) {
status = HTTPRepository::REPO_NO_ERROR;
isUpdating = true;
failures.clear();
}
// find to-be-updated sub-trees and kick them off
rootDir->updateIfWaiting(std::string(), 0);
// maybe there was nothing to do
if (activeRequests.empty()) {
status = HTTPRepository::REPO_NO_ERROR;
isUpdating = false;
}
}
} // of namespace simgear

View File

@@ -20,16 +20,30 @@
#ifndef SG_IO_HTTP_REPOSITORY_HXX
#define SG_IO_HTTP_REPOSITORY_HXX
#include <simgear/io/AbstractRepository.hxx>
#include <memory>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/HTTPClient.hxx>
namespace simgear {
class HTTPRepoPrivate;
class HTTPRepository : public AbstractRepository
class HTTPRepository
{
public:
enum ResultCode {
REPO_NO_ERROR = 0,
REPO_ERROR_NOT_FOUND,
REPO_ERROR_SOCKET,
SVN_ERROR_XML,
SVN_ERROR_TXDELTA,
REPO_ERROR_IO,
REPO_ERROR_CHECKSUM,
REPO_ERROR_FILE_NOT_FOUND,
REPO_ERROR_HTTP,
REPO_PARTIAL_UPDATE
};
HTTPRepository(const SGPath& root, HTTP::Client* cl);
virtual ~HTTPRepository();
@@ -43,6 +57,13 @@ public:
virtual void update();
/**
* set if we should sync the entire repository
*/
void setEntireRepositoryMode();
void addSubpath(const std::string& relPath);
virtual bool isDoingSync() const;
virtual ResultCode failure() const;
@@ -50,6 +71,12 @@ public:
virtual size_t bytesToDownload() const;
virtual size_t bytesDownloaded() const;
/**
* optionally provide the location of an installer copy of this
* repository. When a file is missing it will be copied from this tree.
*/
void setInstalledCopyPath(const SGPath& copyPath);
private:
bool isBare() const;

View File

@@ -1,400 +0,0 @@
#include "SVNDirectory.hxx"
#include <cassert>
#include <fstream>
#include <iostream>
#include <boost/foreach.hpp>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/io/DAVMultiStatus.hxx>
#include <simgear/io/SVNRepository.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/SVNReportParser.hxx>
#include <simgear/package/md5.h>
#include <simgear/structure/exception.hxx>
using std::string;
using std::cout;
using std::endl;
using namespace simgear;
typedef std::vector<HTTP::Request_ptr> RequestVector;
typedef std::map<std::string, DAVResource*> DAVResourceMap;
const char* DAV_CACHE_NAME = ".terrasync_cache";
const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
// important: with the Google servers, setting this higher than '1' causes
// server internal errors (500, the connection is closed). In other words we
// can only specify update report items one level deep at most and no more.
// (the root and its direct children, not NOT grand-children)
const unsigned int MAX_UPDATE_REPORT_DEPTH = 1;
enum LineState
{
LINESTATE_HREF = 0,
LINESTATE_VERSIONNAME
};
SVNDirectory::SVNDirectory(SVNRepository *r, const SGPath& path) :
localPath(path),
dav(NULL),
repo(r),
_doingUpdateReport(false),
_parent(NULL)
{
if (path.exists()) {
parseCache();
}
// don't create dir here, repo might not exist at all
}
SVNDirectory::SVNDirectory(SVNDirectory* pr, DAVCollection* col) :
dav(col),
repo(pr->repository()),
_doingUpdateReport(false),
_parent(pr)
{
assert(col->container());
assert(!col->url().empty());
assert(_parent);
localPath = pr->fsDir().file(col->name());
if (!localPath.exists()) {
Dir d(localPath);
d.create(0755);
writeCache();
} else {
parseCache();
}
}
SVNDirectory::~SVNDirectory()
{
// recursive delete our child directories
BOOST_FOREACH(SVNDirectory* d, _children) {
delete d;
}
}
void SVNDirectory::parseCache()
{
SGPath p(localPath);
p.append(DAV_CACHE_NAME);
if (!p.exists()) {
return;
}
char href[1024];
char versionName[128];
LineState lineState = LINESTATE_HREF;
std::ifstream file(p.c_str());
if (!file.is_open()) {
SG_LOG(SG_TERRASYNC, SG_WARN, "unable to open cache file for reading:" << p);
return;
}
bool doneSelf = false;
file.getline(href, 1024);
if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
return;
}
std::string vccUrl;
file.getline(href, 1024);
vccUrl = href;
while (!file.eof()) {
if (lineState == LINESTATE_HREF) {
file.getline(href, 1024);
lineState = LINESTATE_VERSIONNAME;
} else {
assert(lineState == LINESTATE_VERSIONNAME);
file.getline(versionName, 1024);
lineState = LINESTATE_HREF;
char* hrefPtr = href;
if (!doneSelf) {
if (!dav) {
dav = new DAVCollection(hrefPtr);
dav->setVersionName(versionName);
} else {
assert(string(hrefPtr) == dav->url());
}
if (!vccUrl.empty()) {
dav->setVersionControlledConfiguration(vccUrl);
}
_cachedRevision = versionName;
doneSelf = true;
} else {
DAVResource* child = parseChildDirectory(hrefPtr)->collection();
string s = strutils::strip(versionName);
if (!s.empty()) {
child->setVersionName(versionName);
}
} // of done self test
} // of line-state switching
} // of file get-line loop
}
void SVNDirectory::writeCache()
{
SGPath p(localPath);
if (!p.exists()) {
Dir d(localPath);
d.create(0755);
}
p.append(string(DAV_CACHE_NAME) + ".new");
std::ofstream file(p.c_str(), std::ios::trunc);
// first, cache file version header
file << CACHE_VERSION_4_TOKEN << '\n';
// second, the repository VCC url
file << dav->versionControlledConfiguration() << '\n';
// third, our own URL, and version
file << dav->url() << '\n' << _cachedRevision << '\n';
BOOST_FOREACH(DAVResource* child, dav->contents()) {
if (child->isCollection()) {
file << child->name() << '\n' << child->versionName() << "\n";
}
} // of child iteration
file.close();
// approximately atomic delete + rename operation
SGPath cacheName(localPath);
cacheName.append(DAV_CACHE_NAME);
p.rename(cacheName);
}
void SVNDirectory::setBaseUrl(const string& url)
{
if (_parent) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "setting base URL on non-root directory " << url);
return;
}
if (dav && (url == dav->url())) {
return;
}
dav = new DAVCollection(url);
}
std::string SVNDirectory::url() const
{
if (!_parent) {
return repo->baseUrl();
}
return _parent->url() + "/" + name();
}
std::string SVNDirectory::name() const
{
return dav->name();
}
DAVResource*
SVNDirectory::addChildFile(const std::string& fileName)
{
DAVResource* child = NULL;
child = new DAVResource(dav->urlForChildWithName(fileName));
dav->addChild(child);
writeCache();
return child;
}
SVNDirectory*
SVNDirectory::addChildDirectory(const std::string& dirName)
{
if (dav->childWithName(dirName)) {
// existing child, let's remove it
deleteChildByName(dirName);
}
DAVCollection* childCol = dav->createChildCollection(dirName);
SVNDirectory* child = new SVNDirectory(this, childCol);
childCol->setVersionName(child->cachedRevision());
_children.push_back(child);
writeCache();
return child;
}
SVNDirectory*
SVNDirectory::parseChildDirectory(const std::string& dirName)
{
assert(!dav->childWithName(dirName));
DAVCollection* childCol = dav->createChildCollection(dirName);
SVNDirectory* child = new SVNDirectory(this, childCol);
childCol->setVersionName(child->cachedRevision());
_children.push_back(child);
return child;
}
void SVNDirectory::deleteChildByName(const std::string& nm)
{
DAVResource* child = dav->childWithName(nm);
if (!child) {
return;
}
SGPath path = fsDir().file(nm);
if (child->isCollection()) {
Dir d(path);
bool ok = d.remove(true);
if (!ok) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove dir:"
<< nm << " at path:\n\t" << path);
}
DirectoryList::iterator it = findChildDir(nm);
if (it != _children.end()) {
SVNDirectory* c = *it;
delete c;
_children.erase(it);
}
} else {
bool ok = path.remove();
if (!ok) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove path:" << nm
<< " at path:\n\t" << path);
}
}
dav->removeChild(child);
delete child;
writeCache();
}
bool SVNDirectory::isDoingSync() const
{
if (_doingUpdateReport) {
return true;
}
BOOST_FOREACH(SVNDirectory* child, _children) {
if (child->isDoingSync()) {
return true;
} // of children
}
return false;
}
void SVNDirectory::beginUpdateReport()
{
_doingUpdateReport = true;
_cachedRevision.clear();
writeCache();
}
void SVNDirectory::updateReportComplete()
{
_cachedRevision = dav->versionName();
_doingUpdateReport = false;
writeCache();
SVNDirectory* pr = parent();
if (pr) {
pr->writeCache();
}
}
SVNRepository* SVNDirectory::repository() const
{
return repo;
}
void SVNDirectory::mergeUpdateReportDetails(unsigned int depth,
string_list& items)
{
// normal, easy case: we are fully in-sync at a revision
if (!_cachedRevision.empty()) {
std::ostringstream os;
os << "<S:entry rev=\"" << _cachedRevision << "\" depth=\"infinity\">"
<< repoPath() << "</S:entry>";
items.push_back(os.str());
return;
}
Dir d(localPath);
if (depth >= MAX_UPDATE_REPORT_DEPTH) {
d.removeChildren();
return;
}
PathList cs = d.children(Dir::NO_DOT_OR_DOTDOT | Dir::INCLUDE_HIDDEN | Dir::TYPE_DIR);
BOOST_FOREACH(SGPath path, cs) {
SVNDirectory* c = child(path.file());
if (!c) {
// ignore this child, if it's an incomplete download,
// it will be over-written on the update anyway
//std::cerr << "unknown SVN child" << path << std::endl;
} else {
// recurse down into children
c->mergeUpdateReportDetails(depth+1, items);
}
} // of child dir iteration
}
std::string SVNDirectory::repoPath() const
{
if (!_parent) {
return "/";
}
// find the length of the repository base URL, then
// trim that off our repo URL - job done!
size_t baseUrlLen = repo->baseUrl().size();
return dav->url().substr(baseUrlLen + 1);
}
SVNDirectory* SVNDirectory::parent() const
{
return _parent;
}
SVNDirectory* SVNDirectory::child(const std::string& dirName) const
{
BOOST_FOREACH(SVNDirectory* d, _children) {
if (d->name() == dirName) {
return d;
}
}
return NULL;
}
DirectoryList::iterator
SVNDirectory::findChildDir(const std::string& dirName)
{
DirectoryList::iterator it;
for (it=_children.begin(); it != _children.end(); ++it) {
if ((*it)->name() == dirName) {
return it;
}
}
return it;
}
simgear::Dir SVNDirectory::fsDir() const
{
return Dir(localPath);
}

View File

@@ -1,109 +0,0 @@
// DAVCollectionMirror.hxx - mirror a DAV collection to the local filesystem
//
// Copyright (C) 2013 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef SG_IO_DAVCOLLECTIONMIRROR_HXX
#define SG_IO_DAVCOLLECTIONMIRROR_HXX
#include <string>
#include <vector>
#include <memory>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/io/DAVMultiStatus.hxx>
namespace simgear {
class Dir;
namespace HTTP { class Request; }
// forward decls
class DAVMirror;
class SVNRepository;
class SVNDirectory;
typedef std::vector<SVNDirectory*> DirectoryList;
class SVNDirectory
{
public:
// init from local
SVNDirectory(SVNRepository *repo, const SGPath& path);
~SVNDirectory();
void setBaseUrl(const std::string& url);
// init from a collection
SVNDirectory(SVNDirectory* pr, DAVCollection* col);
void beginUpdateReport();
void updateReportComplete();
bool isDoingSync() const;
std::string url() const;
std::string name() const;
DAVResource* addChildFile(const std::string& fileName);
SVNDirectory* addChildDirectory(const std::string& dirName);
// void updateChild(DAVResource* child);
void deleteChildByName(const std::string& name);
SGPath fsPath() const
{ return localPath; }
simgear::Dir fsDir() const;
std::string repoPath() const;
SVNRepository* repository() const;
DAVCollection* collection() const
{ return dav; }
std::string cachedRevision() const
{ return _cachedRevision; }
void mergeUpdateReportDetails(unsigned int depth, string_list& items);
SVNDirectory* parent() const;
SVNDirectory* child(const std::string& dirName) const;
private:
void parseCache();
void writeCache();
DirectoryList::iterator findChildDir(const std::string& dirName);
SVNDirectory* parseChildDirectory(const std::string& dirName);
SGPath localPath;
DAVCollection* dav;
SVNRepository* repo;
std::string _cachedRevision;
bool _doingUpdateReport;
SVNDirectory* _parent;
DirectoryList _children;
};
} // of namespace simgear
#endif // of SG_IO_DAVCOLLECTIONMIRROR_HXX

View File

@@ -1,605 +0,0 @@
// SVNReportParser -- parser for SVN report XML data
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "SVNReportParser.hxx"
#include <iostream>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <sstream>
#include <fstream>
#include <boost/foreach.hpp>
#include "simgear/misc/sg_path.hxx"
#include "simgear/misc/sg_dir.hxx"
#include "simgear/debug/logstream.hxx"
#include "simgear/xml/easyxml.hxx"
#include "simgear/misc/strutils.hxx"
#include "simgear/package/md5.h"
#ifdef SYSTEM_EXPAT
# include <expat.h>
#else
# include "sg_expat.h"
#endif
#include "SVNDirectory.hxx"
#include "SVNRepository.hxx"
#include "DAVMultiStatus.hxx"
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using namespace simgear;
#define DAV_NS "DAV::"
#define SVN_NS "svn::"
#define SUBVERSION_DAV_NS "http://subversion.tigris.org/xmlns/dav/"
namespace {
#define MAX_ENCODED_INT_LEN 10
static size_t
decode_size(unsigned char* &p,
const unsigned char *end)
{
if (p + MAX_ENCODED_INT_LEN < end)
end = p + MAX_ENCODED_INT_LEN;
/* Decode bytes until we're done. */
size_t result = 0;
while (p < end) {
result = (result << 7) | (*p & 0x7f);
if (((*p++ >> 7) & 0x1) == 0) {
break;
}
}
return result;
}
static bool
try_decode_size(unsigned char* &p,
const unsigned char *end)
{
if (p + MAX_ENCODED_INT_LEN < end)
end = p + MAX_ENCODED_INT_LEN;
while (p < end) {
if (((*p++ >> 7) & 0x1) == 0) {
return true;
}
}
return false;
}
// const char* SVN_UPDATE_REPORT_TAG = SVN_NS "update-report";
// const char* SVN_TARGET_REVISION_TAG = SVN_NS "target-revision";
const char* SVN_OPEN_DIRECTORY_TAG = SVN_NS "open-directory";
const char* SVN_OPEN_FILE_TAG = SVN_NS "open-file";
const char* SVN_ADD_DIRECTORY_TAG = SVN_NS "add-directory";
const char* SVN_ADD_FILE_TAG = SVN_NS "add-file";
const char* SVN_TXDELTA_TAG = SVN_NS "txdelta";
const char* SVN_SET_PROP_TAG = SVN_NS "set-prop";
const char* SVN_PROP_TAG = SVN_NS "prop";
const char* SVN_DELETE_ENTRY_TAG = SVN_NS "delete-entry";
const char* SVN_DAV_MD5_CHECKSUM = SUBVERSION_DAV_NS ":md5-checksum";
const char* DAV_HREF_TAG = DAV_NS "href";
const char* DAV_CHECKED_IN_TAG = DAV_NS "checked-in";
const int svn_txdelta_source = 0;
const int svn_txdelta_target = 1;
const int svn_txdelta_new = 2;
const size_t DELTA_HEADER_SIZE = 4;
/**
* helper struct to decode and store the SVN delta header
* values
*/
struct SVNDeltaWindow
{
public:
static bool isWindowComplete(unsigned char* buffer, size_t bytes)
{
unsigned char* p = buffer;
unsigned char* pEnd = p + bytes;
// if we can't decode five sizes, certainly incomplete
for (int i=0; i<5; i++) {
if (!try_decode_size(p, pEnd)) {
return false;
}
}
p = buffer;
// ignore these three
decode_size(p, pEnd);
decode_size(p, pEnd);
decode_size(p, pEnd);
size_t instructionLen = decode_size(p, pEnd);
size_t newLength = decode_size(p, pEnd);
size_t headerLength = p - buffer;
return (bytes >= (instructionLen + newLength + headerLength));
}
SVNDeltaWindow(unsigned char* p) :
headerLength(0),
_ptr(p)
{
sourceViewOffset = decode_size(p, p+20);
sourceViewLength = decode_size(p, p+20);
targetViewLength = decode_size(p, p+20);
instructionLength = decode_size(p, p+20);
newLength = decode_size(p, p+20);
headerLength = p - _ptr;
_ptr = p;
}
bool apply(std::vector<unsigned char>& output, std::istream& source)
{
unsigned char* pEnd = _ptr + instructionLength;
unsigned char* newData = pEnd;
while (_ptr < pEnd) {
int op = ((*_ptr >> 6) & 0x3);
if (op >= 3) {
SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: bad opcode:" << op);
return false;
}
int length = *_ptr++ & 0x3f;
int offset = 0;
if (length == 0) {
length = decode_size(_ptr, pEnd);
}
if (length == 0) {
SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: malformed stream, 0 length" << op);
return false;
}
// if op != new, decode another size value
if (op != svn_txdelta_new) {
offset = decode_size(_ptr, pEnd);
}
if (op == svn_txdelta_target) {
// this is inefficent, but ranges can overlap.
while (length > 0) {
output.push_back(output[offset++]);
--length;
}
} else if (op == svn_txdelta_new) {
output.insert(output.end(), newData, newData + length);
newData += length;
} else if (op == svn_txdelta_source) {
source.seekg(offset);
char* sourceBuf = (char*) malloc(length);
assert(sourceBuf);
source.read(sourceBuf, length);
output.insert(output.end(), sourceBuf, sourceBuf + length);
free(sourceBuf);
} else {
SG_LOG(SG_IO, SG_WARN, "bad opcode logic");
return false;
}
} // of instruction loop
return true;
}
size_t size() const
{
return headerLength + instructionLength + newLength;
}
unsigned int sourceViewOffset;
size_t sourceViewLength,
targetViewLength;
size_t headerLength,
instructionLength,
newLength;
private:
unsigned char* _ptr;
};
} // of anonymous namespace
class SVNReportParser::SVNReportParserPrivate
{
public:
SVNReportParserPrivate(SVNRepository* repo) :
tree(repo),
status(AbstractRepository::REPO_NO_ERROR),
parserInited(false),
currentPath(repo->fsBase())
{
inFile = false;
currentDir = repo->rootDir();
}
~SVNReportParserPrivate()
{
}
void startElement (const char * name, const char** attributes)
{
if (status != AbstractRepository::REPO_NO_ERROR) {
return;
}
ExpatAtts attrs(attributes);
tagStack.push_back(name);
if (!strcmp(name, SVN_TXDELTA_TAG)) {
txDeltaData.clear();
} else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
string fileName(attrs.getValue("name"));
SGPath filePath(currentDir->fsDir().file(fileName));
currentPath = filePath;
inFile = true;
} else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
string fileName(attrs.getValue("name"));
SGPath filePath(Dir(currentPath).file(fileName));
currentPath = filePath;
if (!filePath.exists()) {
fail(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
return;
}
inFile = true;
} else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
string dirName(attrs.getValue("name"));
Dir d(currentDir->fsDir().file(dirName));
if (d.exists()) {
// policy decision : if we're doing an add, wipe the existing
d.remove(true);
}
currentDir = currentDir->addChildDirectory(dirName);
currentPath = currentDir->fsPath();
currentDir->beginUpdateReport();
//cout << "addDir:" << currentPath << endl;
} else if (!strcmp(name, SVN_SET_PROP_TAG)) {
setPropName = attrs.getValue("name");
setPropValue.clear();
} else if (!strcmp(name, SVN_DAV_MD5_CHECKSUM)) {
md5Sum.clear();
} else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
string dirName;
if (attrs.getValue("name")) {
dirName = string(attrs.getValue("name"));
}
openDirectory(dirName);
} else if (!strcmp(name, SVN_DELETE_ENTRY_TAG)) {
string entryName(attrs.getValue("name"));
deleteEntry(entryName);
} else if (!strcmp(name, DAV_CHECKED_IN_TAG) ||
!strcmp(name, DAV_HREF_TAG) ||
!strcmp(name, SVN_PROP_TAG)) {
// don't warn on these ones
} else {
//SG_LOG(SG_IO, SG_WARN, "SVNReportParser: unhandled tag:" << name);
}
} // of startElement
void openDirectory(const std::string& dirName)
{
if (dirName.empty()) {
// root directory, we shall assume
currentDir = tree->rootDir();
} else {
assert(currentDir);
currentDir = currentDir->child(dirName);
}
assert(currentDir);
currentPath = currentDir->fsPath();
currentDir->beginUpdateReport();
}
void deleteEntry(const std::string& entryName)
{
currentDir->deleteChildByName(entryName);
}
bool decodeTextDelta(const SGPath& outputPath)
{
std::vector<unsigned char> output, decoded;
strutils::decodeBase64(txDeltaData, decoded);
size_t bytesToDecode = decoded.size();
unsigned char* p = decoded.data();
if (memcmp(p, "SVN\0", DELTA_HEADER_SIZE) != 0) {
return false; // bad header
}
bytesToDecode -= DELTA_HEADER_SIZE;
p += DELTA_HEADER_SIZE;
std::ifstream source;
source.open(outputPath.c_str(), std::ios::in | std::ios::binary);
while (bytesToDecode > 0) {
if (!SVNDeltaWindow::isWindowComplete(p, bytesToDecode)) {
SG_LOG(SG_IO, SG_WARN, "SVN txdelta broken window");
return false;
}
SVNDeltaWindow window(p);
assert(bytesToDecode >= window.size());
window.apply(output, source);
bytesToDecode -= window.size();
p += window.size();
}
source.close();
std::ofstream f;
f.open(outputPath.c_str(),
std::ios::out | std::ios::trunc | std::ios::binary);
f.write((char*) output.data(), output.size());
// compute MD5 while we have the file in memory
memset(&md5Context, 0, sizeof(SG_MD5_CTX));
SG_MD5Init(&md5Context);
SG_MD5Update(&md5Context, (unsigned char*) output.data(), output.size());
unsigned char digest[MD5_DIGEST_LENGTH];
SG_MD5Final(digest, &md5Context);
decodedFileMd5 = strutils::encodeHex(digest, MD5_DIGEST_LENGTH);
return true;
}
void endElement (const char * name)
{
if (status != SVNRepository::REPO_NO_ERROR) {
return;
}
assert(tagStack.back() == name);
tagStack.pop_back();
if (!strcmp(name, SVN_TXDELTA_TAG)) {
if (!decodeTextDelta(currentPath)) {
fail(SVNRepository::SVN_ERROR_TXDELTA);
}
} else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
finishFile(currentPath);
} else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
finishFile(currentPath);
} else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
// pop directory
currentPath = currentPath.dir();
currentDir->updateReportComplete();
currentDir = currentDir->parent();
} else if (!strcmp(name, SVN_SET_PROP_TAG)) {
if (setPropName == "svn:entry:committed-rev") {
revision = strutils::to_int(setPropValue);
currentVersionName = setPropValue;
if (!inFile) {
// for directories we have the resource already
// for adding files, we might not; we set the version name
// above when ending the add/open-file element
currentDir->collection()->setVersionName(currentVersionName);
}
}
} else if (!strcmp(name, SVN_DAV_MD5_CHECKSUM)) {
// validate against (presumably) just written file
if (decodedFileMd5 != md5Sum) {
fail(SVNRepository::REPO_ERROR_CHECKSUM);
}
} else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
currentDir->updateReportComplete();
if (currentDir->parent()) {
// pop the collection stack
currentDir = currentDir->parent();
}
currentPath = currentDir->fsPath();
} else {
// std::cout << "element:" << name;
}
}
void finishFile(const SGPath& path)
{
currentPath = path.dir();
inFile = false;
}
void data (const char * s, int length)
{
if (status != SVNRepository::REPO_NO_ERROR) {
return;
}
if (tagStack.back() == SVN_SET_PROP_TAG) {
setPropValue.append(s, length);
} else if (tagStack.back() == SVN_TXDELTA_TAG) {
txDeltaData.append(s, length);
} else if (tagStack.back() == SVN_DAV_MD5_CHECKSUM) {
md5Sum.append(s, length);
}
}
void pi (const char * target, const char * data) {}
string tagN(const unsigned int n) const
{
size_t sz = tagStack.size();
if (n >= sz) {
return string();
}
return tagStack[sz - (1 + n)];
}
void fail(SVNRepository::ResultCode err)
{
status = err;
}
SVNRepository* tree;
DAVCollection* rootCollection;
SVNDirectory* currentDir;
SVNRepository::ResultCode status;
bool parserInited;
XML_Parser xmlParser;
// in-flight data
string_list tagStack;
string currentVersionName;
string txDeltaData;
SGPath currentPath;
bool inFile;
unsigned int revision;
SG_MD5_CTX md5Context;
string md5Sum, decodedFileMd5;
std::string setPropName, setPropValue;
};
////////////////////////////////////////////////////////////////////////
// Static callback functions for Expat.
////////////////////////////////////////////////////////////////////////
#define VISITOR static_cast<SVNReportParser::SVNReportParserPrivate *>(userData)
static void
start_element (void * userData, const char * name, const char ** atts)
{
VISITOR->startElement(name, atts);
}
static void
end_element (void * userData, const char * name)
{
VISITOR->endElement(name);
}
static void
character_data (void * userData, const char * s, int len)
{
VISITOR->data(s, len);
}
static void
processing_instruction (void * userData,
const char * target,
const char * data)
{
VISITOR->pi(target, data);
}
#undef VISITOR
///////////////////////////////////////////////////////////////////////////////
SVNReportParser::SVNReportParser(SVNRepository* repo) :
_d(new SVNReportParserPrivate(repo))
{
}
SVNReportParser::~SVNReportParser()
{
}
SVNRepository::ResultCode
SVNReportParser::innerParseXML(const char* data, int size)
{
if (_d->status != SVNRepository::REPO_NO_ERROR) {
return _d->status;
}
bool isEnd = (data == NULL);
if (!XML_Parse(_d->xmlParser, data, size, isEnd)) {
SG_LOG(SG_IO, SG_INFO, "SVN parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
XML_ParserFree(_d->xmlParser);
_d->parserInited = false;
return SVNRepository::SVN_ERROR_XML;
} else if (isEnd) {
XML_ParserFree(_d->xmlParser);
_d->parserInited = false;
}
return _d->status;
}
SVNRepository::ResultCode
SVNReportParser::parseXML(const char* data, int size)
{
if (_d->status != SVNRepository::REPO_NO_ERROR) {
return _d->status;
}
if (!_d->parserInited) {
_d->xmlParser = XML_ParserCreateNS(0, ':');
XML_SetUserData(_d->xmlParser, _d.get());
XML_SetElementHandler(_d->xmlParser, start_element, end_element);
XML_SetCharacterDataHandler(_d->xmlParser, character_data);
XML_SetProcessingInstructionHandler(_d->xmlParser, processing_instruction);
_d->parserInited = true;
}
return innerParseXML(data, size);
}
SVNRepository::ResultCode SVNReportParser::finishParse()
{
if (_d->status != SVNRepository::REPO_NO_ERROR) {
return _d->status;
}
return innerParseXML(NULL, 0);
}
std::string SVNReportParser::etagFromRevision(unsigned int revision)
{
// etags look like W/"7//", hopefully this is stable
// across different servers and similar
std::ostringstream os;
os << "W/\"" << revision << "//";
return os.str();
}

View File

@@ -1,57 +0,0 @@
// SVNReportParser -- parser for SVN report XML data
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef SG_IO_SVNREPORTPARSER_HXX
#define SG_IO_SVNREPORTPARSER_HXX
#include <string>
#include <memory> // for auto_ptr
#include "SVNRepository.hxx"
class SGPath;
namespace simgear
{
class SVNRepository;
class SVNReportParser
{
public:
SVNReportParser(SVNRepository* repo);
~SVNReportParser();
// incremental XML parsing
SVNRepository::ResultCode parseXML(const char* data, int size);
SVNRepository::ResultCode finishParse();
static std::string etagFromRevision(unsigned int revision);
class SVNReportParserPrivate;
private:
SVNRepository::ResultCode innerParseXML(const char* data, int size);
std::auto_ptr<SVNReportParserPrivate> _d;
};
} // of namespace simgear
#endif // of SG_IO_SVNREPORTPARSER_HXX

View File

@@ -1,386 +0,0 @@
// DAVMirrorTree -- mirror a DAV tree to the local file-system
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "SVNRepository.hxx"
#include <iostream>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <sstream>
#include <map>
#include <set>
#include <fstream>
#include <memory>
#include <boost/foreach.hpp>
#include "simgear/debug/logstream.hxx"
#include "simgear/misc/strutils.hxx"
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/io/DAVMultiStatus.hxx>
#include <simgear/io/SVNDirectory.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/SVNReportParser.hxx>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
namespace simgear
{
typedef std::vector<HTTP::Request_ptr> RequestVector;
class SVNRepoPrivate
{
public:
SVNRepoPrivate(SVNRepository* parent) :
p(parent),
isUpdating(false),
status(SVNRepository::REPO_NO_ERROR)
{ ; }
SVNRepository* p; // link back to outer
SVNDirectory* rootCollection;
HTTP::Client* http;
std::string baseUrl;
std::string vccUrl;
std::string targetRevision;
bool isUpdating;
SVNRepository::ResultCode status;
void svnUpdateDone()
{
isUpdating = false;
}
void updateFailed(HTTP::Request* req, SVNRepository::ResultCode err)
{
SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: failed to update from:" << req->url()
<< "\n(repository:" << p->baseUrl() << ")");
isUpdating = false;
status = err;
}
void propFindComplete(HTTP::Request* req, DAVCollection* col);
void propFindFailed(HTTP::Request* req, SVNRepository::ResultCode err);
};
namespace { // anonmouse
string makeAbsoluteUrl(const string& url, const string& base)
{
if (strutils::starts_with(url, "http://"))
return url; // already absolute
assert(strutils::starts_with(base, "http://"));
int schemeEnd = base.find("://");
int hostEnd = base.find('/', schemeEnd + 3);
if (hostEnd < 0) {
return url;
}
return base.substr(0, hostEnd) + url;
}
// keep the responses small by only requesting the properties we actually
// care about; the ETag, length and MD5-sum
const char* PROPFIND_REQUEST_BODY =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
"<D:propfind xmlns:D=\"DAV:\">"
"<D:prop xmlns:R=\"http://subversion.tigris.org/xmlns/dav/\">"
"<D:resourcetype/>"
"<D:version-name/>"
"<D:version-controlled-configuration/>"
"</D:prop>"
"</D:propfind>";
class PropFindRequest : public HTTP::Request
{
public:
PropFindRequest(SVNRepoPrivate* repo) :
Request(repo->baseUrl, "PROPFIND"),
_repo(repo)
{
assert(repo);
requestHeader("Depth") = "0";
setBodyData( PROPFIND_REQUEST_BODY,
"application/xml; charset=\"utf-8\"" );
}
protected:
virtual void responseHeadersComplete()
{
if (responseCode() == 207) {
// fine
} else if (responseCode() == 404) {
_repo->propFindFailed(this, SVNRepository::REPO_ERROR_NOT_FOUND);
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "request for:" << url() <<
" return code " << responseCode());
_repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
_repo = NULL;
}
Request::responseHeadersComplete();
}
virtual void onDone()
{
if (responseCode() == 207) {
_davStatus.finishParse();
if (_davStatus.isValid()) {
_repo->propFindComplete(this, (DAVCollection*) _davStatus.resource());
} else {
_repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
}
}
}
virtual void gotBodyData(const char* s, int n)
{
if (responseCode() != 207) {
return;
}
_davStatus.parseXML(s, n);
}
virtual void onFail()
{
HTTP::Request::onFail();
if (_repo) {
_repo->propFindFailed(this, SVNRepository::REPO_ERROR_SOCKET);
_repo = NULL;
}
}
private:
SVNRepoPrivate* _repo;
DAVMultiStatus _davStatus;
};
class UpdateReportRequest:
public HTTP::Request
{
public:
UpdateReportRequest(SVNRepoPrivate* repo,
const std::string& aVersionName,
bool startEmpty) :
HTTP::Request("", "REPORT"),
_parser(repo->p),
_repo(repo),
_failed(false)
{
setUrl(repo->vccUrl);
std::string request =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
"<S:update-report send-all=\"true\" xmlns:S=\"svn:\">\n"
"<S:src-path>" + repo->baseUrl + "</S:src-path>\n"
"<S:depth>unknown</S:depth>\n"
"<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
if( !startEmpty )
{
string_list entries;
_repo->rootCollection->mergeUpdateReportDetails(0, entries);
BOOST_FOREACH(string e, entries)
{
request += e + "\n";
}
}
request += "</S:update-report>";
setBodyData(request, "application/xml; charset=\"utf-8\"");
}
protected:
virtual void onDone()
{
if (_failed) {
return;
}
if (responseCode() == 200) {
SVNRepository::ResultCode err = _parser.finishParse();
if (err) {
_repo->updateFailed(this, err);
_failed = true;
} else {
_repo->svnUpdateDone();
}
} else if (responseCode() == 404) {
_repo->updateFailed(this, SVNRepository::REPO_ERROR_NOT_FOUND);
_failed = true;
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: request for:" << url() <<
" got HTTP status " << responseCode());
_repo->updateFailed(this, SVNRepository::REPO_ERROR_HTTP);
_failed = true;
}
}
virtual void gotBodyData(const char* s, int n)
{
if (_failed) {
return;
}
if (responseCode() != 200) {
return;
}
SVNRepository::ResultCode err = _parser.parseXML(s, n);
if (err) {
_failed = true;
SG_LOG(SG_IO, SG_WARN, this << ": SVN: request for:" << url() << " failed:" << err);
_repo->updateFailed(this, err);
_repo = NULL;
}
}
virtual void onFail()
{
HTTP::Request::onFail();
if (_repo) {
_repo->updateFailed(this, SVNRepository::REPO_ERROR_SOCKET);
_repo = NULL;
}
}
private:
SVNReportParser _parser;
SVNRepoPrivate* _repo;
bool _failed;
};
} // anonymous
SVNRepository::SVNRepository(const SGPath& base, HTTP::Client *cl) :
_d(new SVNRepoPrivate(this))
{
_d->http = cl;
_d->rootCollection = new SVNDirectory(this, base);
_d->baseUrl = _d->rootCollection->url();
}
SVNRepository::~SVNRepository()
{
delete _d->rootCollection;
}
void SVNRepository::setBaseUrl(const std::string &url)
{
_d->baseUrl = url;
_d->rootCollection->setBaseUrl(url);
}
std::string SVNRepository::baseUrl() const
{
return _d->baseUrl;
}
HTTP::Client* SVNRepository::http() const
{
return _d->http;
}
SGPath SVNRepository::fsBase() const
{
return _d->rootCollection->fsPath();
}
bool SVNRepository::isBare() const
{
if (!fsBase().exists() || Dir(fsBase()).isEmpty()) {
return true;
}
if (_d->vccUrl.empty()) {
return true;
}
return false;
}
void SVNRepository::update()
{
_d->status = REPO_NO_ERROR;
if (_d->targetRevision.empty() || _d->vccUrl.empty()) {
_d->isUpdating = true;
PropFindRequest* pfr = new PropFindRequest(_d.get());
http()->makeRequest(pfr);
return;
}
if (_d->targetRevision == rootDir()->cachedRevision()) {
SG_LOG(SG_TERRASYNC, SG_DEBUG, baseUrl() << " in sync at version " << _d->targetRevision);
_d->isUpdating = false;
return;
}
_d->isUpdating = true;
UpdateReportRequest* urr = new UpdateReportRequest(_d.get(),
_d->targetRevision, isBare());
http()->makeRequest(urr);
}
bool SVNRepository::isDoingSync() const
{
if (_d->status != REPO_NO_ERROR) {
return false;
}
return _d->isUpdating || _d->rootCollection->isDoingSync();
}
SVNDirectory* SVNRepository::rootDir() const
{
return _d->rootCollection;
}
SVNRepository::ResultCode
SVNRepository::failure() const
{
return _d->status;
}
///////////////////////////////////////////////////////////////////////////
void SVNRepoPrivate::propFindComplete(HTTP::Request* req, DAVCollection* c)
{
targetRevision = c->versionName();
vccUrl = makeAbsoluteUrl(c->versionControlledConfiguration(), baseUrl);
rootCollection->collection()->setVersionControlledConfiguration(vccUrl);
p->update();
}
void SVNRepoPrivate::propFindFailed(HTTP::Request *req, SVNRepository::ResultCode err)
{
if (err != SVNRepository::REPO_ERROR_NOT_FOUND) {
SG_LOG(SG_TERRASYNC, SG_WARN, "PropFind failed for:" << req->url());
}
isUpdating = false;
status = err;
}
} // of namespace simgear

View File

@@ -1,59 +0,0 @@
// DAVMirrorTree.hxx - mirror a DAV tree to the local file system
//
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef SG_IO_SVN_REPOSITORY_HXX
#define SG_IO_SVN_REPOSITORY_HXX
#include <simgear/io/AbstractRepository.hxx>
#include <memory>
namespace simgear {
class SVNDirectory;
class SVNRepoPrivate;
class SVNRepository : public AbstractRepository
{
public:
SVNRepository(const SGPath& root, HTTP::Client* cl);
virtual ~SVNRepository();
SVNDirectory* rootDir() const;
virtual SGPath fsBase() const;
virtual void setBaseUrl(const std::string& url);
virtual std::string baseUrl() const;
virtual HTTP::Client* http() const;
virtual void update();
virtual bool isDoingSync() const;
virtual ResultCode failure() const;
private:
bool isBare() const;
std::auto_ptr<SVNRepoPrivate> _d;
};
} // of namespace simgear
#endif // of SG_IO_SVN_REPOSITORY_HXX

View File

@@ -14,6 +14,7 @@
#include "sg_binobj.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
using std::cout;
using std::endl;
@@ -31,7 +32,7 @@ int main( int argc, char **argv ) {
sglog().setLogLevels( SG_ALL, SG_ALERT );
SGBinObject obj;
bool result = obj.read_bin( argv[1] );
bool result = obj.read_bin( SGPath::fromLocal8Bit(argv[1]) );
if ( !result ) {
cout << "error loading: " << argv[1] << endl;
exit(-1);

View File

@@ -73,7 +73,7 @@ int main(int argc, char* argv[])
SGTimeStamp::sleepForMSec(100);
}
if (repo->failure() != AbstractRepository::REPO_NO_ERROR) {
if (repo->failure() != HTTPRepository::REPO_NO_ERROR) {
cerr << "got response:" << repo->failure() << endl;
return EXIT_FAILURE;
}

View File

@@ -1,51 +0,0 @@
#include <cstdio>
#include <cstring>
#include <signal.h>
#include <iostream>
#include <boost/foreach.hpp>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/sg_netChannel.hxx>
#include <simgear/io/DAVMultiStatus.hxx>
#include <simgear/io/SVNRepository.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/timing/timestamp.hxx>
using namespace simgear;
using std::cout;
using std::endl;
using std::cerr;
using std::string;
HTTP::Client* httpClient;
int main(int argc, char* argv[])
{
sglog().setLogLevels( SG_ALL, SG_INFO );
HTTP::Client cl;
httpClient = &cl;
SGPath p("/Users/jmt/Desktop/traffic");
SVNRepository airports(p, &cl);
// airports.setBaseUrl("http://svn.goneabitbursar.com/testproject1");
// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Models");
airports.setBaseUrl("http://fgfs.goneabitbursar.com/fgfsai/trunk/AI/Traffic");
// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Airports");
airports.update();
while (airports.isDoingSync()) {
cl.update(100);
}
cout << "all done!" << endl;
return EXIT_SUCCESS;
}

View File

@@ -83,7 +83,7 @@ int main(int argc, char* argv[])
} else if (!strcmp(argv[a], "--auth")) {
proxyAuth = argv[++a];
} else if (!strcmp(argv[a], "-f") || !strcmp(argv[a], "--file")) {
outFile = new SGFile(argv[++a]);
outFile = new SGFile(SGPath::fromLocal8Bit(argv[++a]));
if (!outFile->open(SG_IO_OUT)) {
cerr << "failed to open output for writing:" << outFile->get_file_name() << endl;
return EXIT_FAILURE;

View File

@@ -125,115 +125,115 @@ public:
unsigned int get_size() const { return size; }
char *get_ptr() const { return ptr; }
void reset()
{
offset = 0;
}
void resize( unsigned int s )
{
if ( s > size ) {
if ( ptr != NULL ) {
delete [] ptr;
}
if ( size == 0) {
size = 16;
}
while ( size < s ) {
size = size << 1;
}
ptr = new char[size];
}
}
float readInt()
{
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p);
}
offset += sizeof(unsigned int);
return *p;
}
SGVec3d readVec3d()
{
double* p = reinterpret_cast<double*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint64_t *) p + 0);
sgEndianSwap((uint64_t *) p + 1);
sgEndianSwap((uint64_t *) p + 2);
}
offset += 3 * sizeof(double);
return SGVec3d(p);
}
float readFloat()
{
float* p = reinterpret_cast<float*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p);
}
offset += sizeof(float);
return *p;
}
SGVec2f readVec2f()
{
float* p = reinterpret_cast<float*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p + 0);
sgEndianSwap((uint32_t *) p + 1);
}
offset += 2 * sizeof(float);
return SGVec2f(p);
}
SGVec3f readVec3f()
{
float* p = reinterpret_cast<float*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p + 0);
sgEndianSwap((uint32_t *) p + 1);
sgEndianSwap((uint32_t *) p + 2);
}
offset += 3 * sizeof(float);
return SGVec3f(p);
}
SGVec4f readVec4f()
{
float* p = reinterpret_cast<float*>(ptr + offset);
if ( sgIsBigEndian() ) {
if ( sgIsBigEndian() ) {
sgEndianSwap((uint32_t *) p + 0);
sgEndianSwap((uint32_t *) p + 1);
sgEndianSwap((uint32_t *) p + 2);
sgEndianSwap((uint32_t *) p + 3);
}
offset += 4 * sizeof(float);
return SGVec4f(p);
}
};
template <class T>
static void read_indices(char* buffer,
static void read_indices(char* buffer,
size_t bytes,
int indexMask,
int vaMask,
int_list& vertices,
int_list& vertices,
int_list& normals,
int_list& colors,
tci_list& texCoords,
@@ -243,7 +243,7 @@ static void read_indices(char* buffer,
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
const int count = bytes / (indexSize + vaSize);
// fix endian-ness of the whole lot, if required
if (sgIsBigEndian()) {
int indices = bytes / sizeof(T);
@@ -252,7 +252,7 @@ static void read_indices(char* buffer,
sgEndianSwap(src++);
}
}
T* src = reinterpret_cast<T*>(buffer);
for (int i=0; i<count; ++i) {
if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
@@ -262,7 +262,7 @@ static void read_indices(char* buffer,
if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
if ( vaMask ) {
if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
@@ -274,11 +274,11 @@ static void read_indices(char* buffer,
if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
}
} // of elements in the index
// WS2.0 fix : toss zero area triangles
if ( ( count == 3 ) && (indexMask & SG_IDX_VERTICES) ) {
if ( (vertices[0] == vertices[1]) ||
(vertices[1] == vertices[2]) ||
if ( (vertices[0] == vertices[1]) ||
(vertices[1] == vertices[2]) ||
(vertices[2] == vertices[0]) ) {
vertices.clear();
}
@@ -306,11 +306,11 @@ void write_indice(gzFile fp, uint32_t value)
template <class T>
void write_indices(gzFile fp,
unsigned char indexMask,
void write_indices(gzFile fp,
unsigned char indexMask,
unsigned int vaMask,
const int_list& vertices,
const int_list& normals,
const int_list& vertices,
const int_list& normals,
const int_list& colors,
const tci_list& texCoords,
const vai_list& vas )
@@ -319,10 +319,10 @@ void write_indices(gzFile fp,
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
sgWriteUInt(fp, (indexSize + vaSize) * count);
for (unsigned int i=0; i < count; ++i) {
write_indice(fp, static_cast<T>(vertices[i]));
if (indexMask & SG_IDX_NORMALS) {
write_indice(fp, static_cast<T>(normals[i]));
}
@@ -341,7 +341,7 @@ void write_indices(gzFile fp,
if (indexMask & SG_IDX_TEXCOORDS_3) {
write_indice(fp, static_cast<T>(texCoords[3][i]));
}
if (vaMask) {
if (vaMask & SG_VA_INTEGER_0) {
write_indice(fp, static_cast<T>(vas[0][i]));
@@ -378,7 +378,7 @@ void SGBinObject::read_object( gzFile fp,
int obj_type,
int nproperties,
int nelements,
group_list& vertices,
group_list& vertices,
group_list& normals,
group_list& colors,
group_tci_list& texCoords,
@@ -399,15 +399,15 @@ void SGBinObject::read_object( gzFile fp,
idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
}
vertex_attrib_mask = 0;
for ( j = 0; j < nproperties; ++j ) {
char prop_type;
sgReadChar( fp, &prop_type );
sgReadUInt( fp, &nbytes );
buf.resize(nbytes);
char *ptr = buf.get_ptr();
switch( prop_type )
{
case SG_MATERIAL:
@@ -418,12 +418,12 @@ void SGBinObject::read_object( gzFile fp,
strncpy( material, ptr, nbytes );
material[nbytes] = '\0';
break;
case SG_INDEX_TYPES:
if (nbytes == 1) {
sgReadChar( fp, (char *)&idx_mask );
sgReadChar( fp, (char *)&idx_mask );
} else {
sgReadBytes( fp, nbytes, ptr );
sgReadBytes( fp, nbytes, ptr );
}
break;
@@ -434,7 +434,7 @@ void SGBinObject::read_object( gzFile fp,
sgReadBytes( fp, nbytes, ptr );
}
break;
default:
sgReadBytes( fp, nbytes, ptr );
SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
@@ -445,26 +445,26 @@ void SGBinObject::read_object( gzFile fp,
if ( sgReadError() ) {
throw sg_exception("Error reading object properties");
}
size_t indexCount = std::bitset<32>((int)idx_mask).count();
if (indexCount == 0) {
throw sg_exception("object index mask has no bits set");
}
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
if ( sgReadError() ) {
throw sg_exception("Error reading element size");
}
buf.resize( nbytes );
char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr );
if ( sgReadError() ) {
throw sg_exception("Error reading element bytes");
}
int_list vs;
int_list ns;
int_list cs;
@@ -491,7 +491,7 @@ void SGBinObject::read_object( gzFile fp,
// read a binary file and populate the provided structures.
bool SGBinObject::read_bin( const string& file ) {
bool SGBinObject::read_bin( const SGPath& file ) {
SGVec3d p;
int i, k;
size_t j;
@@ -535,13 +535,14 @@ bool SGBinObject::read_bin( const string& file ) {
fan_materials.clear();
gzFile fp;
if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
string filegz = file + ".gz";
string f = file.local8BitStr();
if ( (fp = gzopen( f.c_str(), "rb" )) == NULL ) {
string filegz = f + ".gz";
if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
SG_LOG( SG_EVENT, SG_ALERT,
"ERROR: opening " << file << " or " << filegz << " for reading!");
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(f));
}
}
@@ -552,7 +553,7 @@ bool SGBinObject::read_bin( const string& file ) {
sgReadUInt( fp, &header );
if ( ((header & 0xFF000000) >> 24) == 'S' &&
((header & 0x00FF0000) >> 16) == 'G' ) {
// read file version
version = (header & 0x0000FFFF);
} else {
@@ -560,7 +561,7 @@ bool SGBinObject::read_bin( const string& file ) {
gzclose(fp);
throw sg_io_exception("Bad BTG magic/version", sg_location(file));
}
// read creation time
unsigned int foo_calendar_time;
sgReadUInt( fp, &foo_calendar_time );
@@ -591,13 +592,13 @@ bool SGBinObject::read_bin( const string& file ) {
sgReadShort( fp, &v );
nobjects = v;
}
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
if ( sgReadError() ) {
throw sg_io_exception("Error reading BTG file header", sg_location(file));
}
// read in objects
for ( i = 0; i < nobjects; ++i ) {
// read object header
@@ -621,14 +622,14 @@ bool SGBinObject::read_bin( const string& file ) {
nelements = v;
}
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
" = " << (int)obj_type << " props = " << nproperties <<
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
" = " << (int)obj_type << " props = " << nproperties <<
" elements = " << nelements);
if ( obj_type == SG_BOUNDING_SPHERE ) {
// read bounding sphere properties
read_properties( fp, nproperties );
// read bounding sphere elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
@@ -688,10 +689,10 @@ bool SGBinObject::read_bin( const string& file ) {
sgReadBytes( fp, nbytes, ptr );
int count = nbytes / 3;
normals.reserve( count );
for ( k = 0; k < count; ++k ) {
SGVec3f normal( (ptr[0]) / 127.5 - 1.0,
(ptr[1]) / 127.5 - 1.0,
(ptr[1]) / 127.5 - 1.0,
(ptr[2]) / 127.5 - 1.0);
normals.push_back(normalize(normal));
ptr += 3;
@@ -717,7 +718,7 @@ bool SGBinObject::read_bin( const string& file ) {
} else if ( obj_type == SG_VA_FLOAT_LIST ) {
// read vertex attribute (float) properties
read_properties( fp, nproperties );
// read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
@@ -734,7 +735,7 @@ bool SGBinObject::read_bin( const string& file ) {
} else if ( obj_type == SG_VA_INTEGER_LIST ) {
// read vertex attribute (integer) properties
read_properties( fp, nproperties );
// read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
@@ -781,7 +782,7 @@ bool SGBinObject::read_bin( const string& file ) {
sgReadBytes( fp, nbytes, ptr );
}
}
if ( sgReadError() ) {
throw sg_io_exception("Error while reading object", sg_location(file, i));
}
@@ -811,38 +812,38 @@ unsigned int SGBinObject::count_objects(const string_list& materials)
unsigned int start = 0, end = 1;
unsigned int count = materials.size();
string m;
while ( start < count ) {
m = materials[start];
for (end = start+1; (end < count) && (m == materials[end]); ++end) { }
for (end = start+1; (end < count) && (m == materials[end]); ++end) { }
++result;
start = end;
start = end;
}
return result;
}
void SGBinObject::write_objects(gzFile fp, int type,
void SGBinObject::write_objects(gzFile fp, int type,
const group_list& verts,
const group_list& normals,
const group_list& colors,
const group_list& normals,
const group_list& colors,
const group_tci_list& texCoords,
const group_vai_list& vertexAttribs,
const group_vai_list& vertexAttribs,
const string_list& materials)
{
if (verts.empty()) {
return;
}
unsigned int start = 0, end = 1;
string m;
int_list emptyList;
while (start < materials.size()) {
m = materials[start];
// find range of objects with identical material, write out as a single object
for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
// calc the number of elements
const int count = end - start;
@@ -860,13 +861,13 @@ void SGBinObject::write_objects(gzFile fp, int type,
} else {
write_header(fp, type, 2, count);
}
// properties
// material property
sgWriteChar( fp, (char)SG_MATERIAL ); // property
sgWriteUInt( fp, m.length() ); // nbytes
sgWriteBytes( fp, m.length(), m.c_str() );
// index mask property
unsigned char idx_mask = 0;
if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
@@ -876,7 +877,7 @@ void SGBinObject::write_objects(gzFile fp, int type,
if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
if (idx_mask == 0) {
SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
<< m << "has no indices set");
@@ -892,28 +893,28 @@ void SGBinObject::write_objects(gzFile fp, int type,
sgWriteUInt( fp, 4 ); // nbytes
sgWriteChar( fp, va_mask );
}
// elements
for (unsigned int i=start; i < end; ++i) {
const int_list& va(verts[i]);
const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
// pass the whole texcoord array - we'll figure out which indicies to write
// pass the whole texcoord array - we'll figure out which indicies to write
// in write_indices
const tci_list& tca( texCoords[i] );
// pass the whole vertex array - we'll figure out which indicies to write
// pass the whole vertex array - we'll figure out which indicies to write
// in write_indices
const vai_list& vaa( vertexAttribs[i] );
if (version == 7) {
write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
} else {
write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
}
}
start = end;
} // of materials iteration
}
@@ -952,25 +953,26 @@ const unsigned int VERSION_7_MATERIAL_LIMIT = 0x7fff;
bool SGBinObject::write_bin_file(const SGPath& file)
{
int i;
SGPath file2(file);
file2.create_dir( 0755 );
gzFile fp;
if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
cout << "ERROR: opening " << file.str() << " for writing!" << endl;
std::string localPath = file.local8BitStr();
if ( (fp = gzopen( localPath.c_str(), "wb9" )) == NULL ) {
cout << "ERROR: opening " << file << " for writing!" << endl;
return false;
}
sgClearWriteError();
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
<< " pt_materials = " << pt_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
<< " tri_materials = " << tri_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
<< " strip_materials = " << strip_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
<< " fan_materials = " << fan_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
@@ -979,12 +981,12 @@ bool SGBinObject::write_bin_file(const SGPath& file)
SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
version = 10;
bool shortMaterialsRanges =
bool shortMaterialsRanges =
(max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
(max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
(max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
(max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
if ((wgs84_nodes.size() < 0xffff) &&
(normals.size() < 0xffff) &&
(texcoords.size() < 0xffff) &&
@@ -993,10 +995,10 @@ bool SGBinObject::write_bin_file(const SGPath& file)
}
// write header magic
/** Magic Number for our file format */
#define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version )
sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
time_t calendar_time = time(NULL);
sgWriteLong( fp, (int32_t)calendar_time );
@@ -1014,8 +1016,8 @@ bool SGBinObject::write_bin_file(const SGPath& file)
sgWriteUShort( fp, (uint16_t) nobjects );
} else {
sgWriteInt( fp, nobjects );
}
}
// write bounding sphere
write_header( fp, SG_BOUNDING_SPHERE, 0, 1);
sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
@@ -1059,12 +1061,12 @@ bool SGBinObject::write_bin_file(const SGPath& file)
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
// close the file
gzclose(fp);
if ( sgWriteError() ) {
cout << "Error while writing file " << file.str() << endl;
cout << "Error while writing file " << file << endl;
return false;
}
@@ -1082,19 +1084,20 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
SGPath file = base + "/" + b.gen_base_path() + "/" + name;
file.create_dir( 0755 );
cout << "Output file = " << file.str() << endl;
cout << "Output file = " << file << endl;
FILE *fp;
if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
cout << "ERROR: opening " << file.str() << " for writing!" << endl;
std::string path = file.local8BitStr();
if ( (fp = fopen( path.c_str(), "w" )) == NULL ) {
cout << "ERROR: opening " << file << " for writing!" << endl;
return false;
}
cout << "triangles size = " << tris_v.size() << " tri_materials = "
cout << "triangles size = " << tris_v.size() << " tri_materials = "
<< tri_materials.size() << endl;
cout << "strips size = " << strips_v.size() << " strip_materials = "
cout << "strips size = " << strips_v.size() << " strip_materials = "
<< strip_materials.size() << endl;
cout << "fans size = " << fans_v.size() << " fan_materials = "
cout << "fans size = " << fans_v.size() << " fan_materials = "
<< fan_materials.size() << endl;
cout << "points = " << wgs84_nodes.size() << endl;
@@ -1120,7 +1123,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
fprintf(fp, "# vertex list\n");
for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
SGVec3d p = wgs84_nodes[i] - gbs_center;
fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
}
fprintf(fp, "\n");
@@ -1150,7 +1153,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
while ( start < (int)tri_materials.size() ) {
// find next group
material = tri_materials[start];
while ( (end < (int)tri_materials.size()) &&
while ( (end < (int)tri_materials.size()) &&
(material == tri_materials[end]) )
{
// cout << "end = " << end << endl;
@@ -1164,10 +1167,10 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
}
}
SGVec3d bs_center = d.getCenter();
double bs_radius = d.getRadius();
// write group headers
fprintf(fp, "\n");
fprintf(fp, "# usemtl %s\n", material.c_str());
@@ -1198,7 +1201,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
while ( start < (int)strip_materials.size() ) {
// find next group
material = strip_materials[start];
while ( (end < (int)strip_materials.size()) &&
while ( (end < (int)strip_materials.size()) &&
(material == strip_materials[end]) )
{
// cout << "end = " << end << endl;
@@ -1213,7 +1216,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
}
}
SGVec3d bs_center = d.getCenter();
double bs_radius = d.getRadius();
@@ -1231,7 +1234,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
}
fprintf(fp, "\n");
}
start = end;
end = start + 1;
}
@@ -1240,11 +1243,11 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
// close the file
fclose(fp);
string command = "gzip --force --best " + file.str();
string command = "gzip --force --best " + file.local8BitStr();
int err = system(command.c_str());
if (err)
{
cout << "ERROR: gzip " << file.str() << " failed!" << endl;
cout << "ERROR: gzip " << file << " failed!" << endl;
}
return (err == 0);
@@ -1254,7 +1257,7 @@ void SGBinObject::read_properties(gzFile fp, int nproperties)
{
sgSimpleBuffer buf;
uint32_t nbytes;
// read properties
for ( int j = 0; j < nproperties; ++j ) {
char prop_type;
@@ -1271,11 +1274,11 @@ bool SGBinObject::add_point( const SGBinObjectPoint& pt )
{
// add the point info
pt_materials.push_back( pt.material );
pts_v.push_back( pt.v_list );
pts_n.push_back( pt.n_list );
pts_n.push_back( pt.n_list );
pts_c.push_back( pt.c_list );
return true;
}
@@ -1288,6 +1291,6 @@ bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
tris_c.push_back( tri.c_list );
tris_tcs.push_back( tri.tc_list );
tris_vas.push_back( tri.va_list );
return true;
}
}

View File

@@ -268,7 +268,7 @@ public:
* @param file input file name
* @return result of read
*/
bool read_bin( const std::string& file );
bool read_bin( const SGPath& file );
/**
* Write out the structures to a binary file. We assume that the

View File

@@ -45,7 +45,7 @@
#include "sg_file.hxx"
SGFile::SGFile(const std::string &file, int repeat_, int extraoflags_ )
SGFile::SGFile(const SGPath &file, int repeat_, int extraoflags_ )
: file_name(file), fp(-1), eof_flag(true), repeat(repeat_), iteration(0),
extraoflags(extraoflags_)
{
@@ -69,24 +69,25 @@ SGFile::~SGFile() {
bool SGFile::open( const SGProtocolDir d ) {
set_dir( d );
std::string n = file_name.local8BitStr();
if ( get_dir() == SG_IO_OUT ) {
#ifdef _WIN32
int mode = _S_IREAD | _S_IWRITE;
#else
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
#endif
fp = ::open( file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode );
fp = ::open( n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode );
} else if ( get_dir() == SG_IO_IN ) {
fp = ::open( file_name.c_str(), O_RDONLY | extraoflags );
fp = ::open( n.c_str(), O_RDONLY | extraoflags );
} else {
SG_LOG( SG_IO, SG_ALERT,
"Error: bidirection mode not available for files." );
return false;
SG_LOG( SG_IO, SG_ALERT,
"Error: bidirection mode not available for files." );
return false;
}
if ( fp == -1 ) {
SG_LOG( SG_IO, SG_ALERT, "Error opening file: " << file_name );
return false;
SG_LOG( SG_IO, SG_ALERT, "Error opening file: " << file_name );
return false;
}
eof_flag = false;
@@ -180,7 +181,7 @@ bool SGFile::close() {
return true;
}
SGBinaryFile::SGBinaryFile( const std::string& file, int repeat_ ) :
SGBinaryFile::SGBinaryFile( const SGPath& file, int repeat_ ) :
#ifdef _WIN32
SGFile(file,repeat_, _O_BINARY)
#else

View File

@@ -23,6 +23,9 @@
#define _SG_FILE_HXX
#include <simgear/compiler.h>
#include <simgear/misc/sg_path.hxx>
#include "iochannel.hxx"
#include <string>
@@ -32,7 +35,7 @@
*/
class SGFile : public SGIOChannel {
std::string file_name;
SGPath file_name;
int fp;
bool eof_flag;
// Number of repetitions to play. -1 means loop infinitely.
@@ -51,7 +54,7 @@ public:
* @param file name of file to open
* @param repeat On eof restart at the beginning of the file
*/
SGFile( const std::string& file, int repeat_ = 1, int extraoflags = 0);
SGFile( const SGPath& file, int repeat_ = 1, int extraoflags = 0);
/**
* Create an SGFile from an existing, open file-descriptor
@@ -80,7 +83,7 @@ public:
bool close();
/** @return the name of the file being manipulated. */
inline std::string get_file_name() const { return file_name; }
std::string get_file_name() const { return file_name.utf8Str(); }
/** @return true of eof conditions exists */
virtual bool eof() const { return eof_flag; };
@@ -88,7 +91,7 @@ public:
class SGBinaryFile : public SGFile {
public:
SGBinaryFile( const std::string& file, int repeat_ = 1 );
SGBinaryFile( const SGPath& file, int repeat_ = 1 );
};
#endif // _SG_FILE_HXX

BIN
simgear/io/test.tar.gz Normal file

Binary file not shown.

BIN
simgear/io/test2.tar Normal file

Binary file not shown.

View File

@@ -577,7 +577,7 @@ cout << "testing proxy close" << endl;
cout << "testing HTTP 1.1 pipelining" << endl;
{
testServer.resetConnectCount();
testServer.disconnectAll();
cl.clearAllConnections();
cl.setProxy("", 80);
@@ -689,7 +689,7 @@ cout << "testing proxy close" << endl;
// test cancel
{
cout << "cancel request" << endl;
testServer.resetConnectCount();
testServer.disconnectAll();
cl.clearAllConnections();
cl.setProxy("", 80);
@@ -722,7 +722,7 @@ cout << "testing proxy close" << endl;
// test cancel
{
cout << "cancel middle request" << endl;
testServer.resetConnectCount();
testServer.disconnectAll();
cl.clearAllConnections();
cl.setProxy("", 80);

View File

@@ -2,6 +2,7 @@
#define SIMGEAR_IO_TEST_HTTP_HXX
#include <sstream>
#include <vector>
#include <simgear/io/sg_netChat.hxx>
#include <simgear/misc/strutils.hxx>
@@ -27,6 +28,11 @@ public:
}
virtual ~TestServerChannel()
{
std::cerr << "dtor test server channel" << std::endl;
}
virtual void collectIncomingData(const char* s, int n)
{
buffer += std::string(s, n);
@@ -160,6 +166,13 @@ public:
}
}
virtual void handleClose (void)
{
std::cerr << "channel close" << std::endl;
NetBufferChannel::handleClose();
}
State state;
std::string buffer;
std::string method;
@@ -170,17 +183,25 @@ public:
int requestContentLength;
};
class EraseIfClosed
{
public:
bool operator()(simgear::NetChannel* chan) const
{
return chan->isClosed();
}
};
template <class T>
class TestServer : public NetChannel
{
simgear::NetChannelPoller _poller;
int _connectCount;
std::vector<T*> _channels;
public:
TestServer()
{
Socket::initSockets();
_connectCount = 0;
open();
bind(NULL, 2000); // localhost, any port
@@ -199,27 +220,42 @@ public:
{
simgear::IPAddress addr ;
int handle = accept ( &addr ) ;
TestServerChannel* chan = new T();
T* chan = new T();
chan->setHandle(handle);
_channels.push_back(chan);
_poller.addChannel(chan);
_connectCount++;
}
void poll()
{
_poller.poll();
}
void resetConnectCount()
{
_connectCount = 0;
typename std::vector<T*>::iterator it;
it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed());
for (typename std::vector<T*>::iterator it2 = it; it2 != _channels.end(); ++it2) {
delete *it2;
}
_channels.erase(it, _channels.end());
}
int connectCount()
{
return _connectCount;
return _channels.size();
}
void disconnectAll()
{
typename std::vector<T*>::iterator it;
for (it = _channels.begin(); it != _channels.end(); ++it) {
_poller.removeChannel(*it);
delete *it;
}
_channels.clear();
}
};

View File

@@ -63,7 +63,7 @@ void test_empty()
bool ok = empty.write_bin_file(path);
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_wgs84_nodes().size(), 0);
@@ -192,7 +192,7 @@ void test_basic()
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_version(), 7); // should be version 7 since indices are < 2^16
COMPARE(rd.get_gbs_center(), center);
@@ -229,7 +229,7 @@ void test_many_tcs()
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
COMPARE(rd.get_wgs84_nodes().size(), points.size());
@@ -266,7 +266,7 @@ void test_big()
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
COMPARE(rd.get_wgs84_nodes().size(), points.size());
@@ -303,7 +303,7 @@ void test_some_objects()
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_version(), 7); // since we have less than 2^15 tris
COMPARE(rd.get_wgs84_nodes().size(), points.size());
@@ -340,7 +340,7 @@ void test_many_objects()
VERIFY( ok );
SGBinObject rd;
ok = rd.read_bin(path.str()) ;
ok = rd.read_bin(path) ;
VERIFY( ok);
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
COMPARE(rd.get_wgs84_nodes().size(), points.size());

View File

@@ -18,7 +18,10 @@
#include <simgear/timing/timestamp.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/callback.hxx>
#include <simgear/io/sg_file.hxx>
using namespace simgear;
@@ -68,9 +71,12 @@ public:
int requestCount;
bool getWillFail;
bool returnCorruptData;
std::auto_ptr<SGCallback> accessCallback;
void clearRequestCounts();
void clearFailFlags();
void setGetWillFail(bool b)
{
getWillFail = b;
@@ -113,7 +119,7 @@ public:
if (path.empty()) {
return this;
}
string_list pathParts = strutils::split(path, "/");
TestRepoEntry* entry = childEntry(pathParts.front());
if (pathParts.size() == 1) {
@@ -213,6 +219,18 @@ void TestRepoEntry::clearRequestCounts()
}
}
void TestRepoEntry::clearFailFlags()
{
getWillFail = false;
returnCorruptData = false;
if (isDir) {
for (size_t i=0; i<children.size(); ++i) {
children[i]->clearFailFlags();
}
}
}
TestRepoEntry* global_repo = NULL;
class TestRepositoryChannel : public TestServerChannel
@@ -238,6 +256,10 @@ public:
repoPath = repoPath.substr(0, suffix);
}
if (repoPath.find("/") == 0) { // trim leading /
repoPath = repoPath.substr(1);
}
TestRepoEntry* entry = global_repo->findEntry(repoPath);
if (!entry) {
sendErrorResponse(404, false, "unknown repo path:" + repoPath);
@@ -249,6 +271,10 @@ public:
return;
}
if (entry->accessCallback.get()) {
(*entry->accessCallback)();
}
if (entry->getWillFail) {
sendErrorResponse(404, false, "entry marked to fail explicitly:" + repoPath);
return;
@@ -286,7 +312,7 @@ std::string test_computeHashForPath(const SGPath& p)
char* buf = static_cast<char*>(alloca(1024 * 1024));
size_t readLen;
SGBinaryFile f(p.str());
SGBinaryFile f(p);
f.open(SG_IO_IN);
while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
@@ -316,6 +342,15 @@ void verifyFileState(const SGPath& fsRoot, const std::string& relPath)
}
}
void verifyFileNotPresent(const SGPath& fsRoot, const std::string& relPath)
{
SGPath p(fsRoot);
p.append(relPath);
if (p.exists()) {
throw sg_error("Present file system entry", relPath);
}
}
void verifyRequestCount(const std::string& relPath, int count)
{
TestRepoEntry* entry = global_repo->findEntry(relPath);
@@ -340,7 +375,7 @@ void createFile(const SGPath& basePath, const std::string& relPath, int revision
std::string prName = comps.at(comps.size() - 2);
{
std::ofstream f(p.c_str(), std::ios::trunc | std::ios::out);
sg_ofstream f(p, std::ios::trunc | std::ios::out);
f << dataForFile(prName, comps.back(), revision);
}
}
@@ -350,7 +385,7 @@ TestServer<TestRepositoryChannel> testServer;
void waitForUpdateComplete(HTTP::Client* cl, HTTPRepository* repo)
{
SGTimeStamp start(SGTimeStamp::now());
while (start.elapsedMSec() < 10000) {
while (start.elapsedMSec() < 20000) {
cl->update();
testServer.poll();
@@ -370,9 +405,10 @@ void testBasicClone(HTTP::Client* cl)
p.append("http_repo_basic");
simgear::Dir pd(p);
pd.removeChildren();
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
@@ -410,6 +446,7 @@ void testModifyLocalFiles(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
@@ -418,7 +455,7 @@ void testModifyLocalFiles(HTTP::Client* cl)
SGPath modFile(p);
modFile.append("dirB/subdirA/fileBAA");
{
std::ofstream of(modFile.c_str(), std::ios::out | std::ios::trunc);
sg_ofstream of(modFile, std::ios::out | std::ios::trunc);
of << "complete nonsense";
of.close();
}
@@ -451,6 +488,7 @@ void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
createFile(p, "dirC/fileCB", 4); // should match
createFile(p, "dirC/fileCC", 3); // mismatch
@@ -493,6 +531,7 @@ void testLossOfLocalFiles(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
verifyFileState(p, "dirB/subdirA/fileBAA");
@@ -530,9 +569,10 @@ void testAbandonMissingFiles(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
if (repo->failure() != AbstractRepository::REPO_PARTIAL_UPDATE) {
if (repo->failure() != HTTPRepository::REPO_PARTIAL_UPDATE) {
throw sg_exception("Bad result from missing files test");
}
@@ -554,18 +594,285 @@ void testAbandonCorruptFiles(HTTP::Client* cl)
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
if (repo->failure() != AbstractRepository::REPO_PARTIAL_UPDATE) {
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
throw sg_exception("Bad result from corrupt files test");
}
repo.reset();
if (cl->hasActiveRequests()) {
cl->debugDumpRequests();
throw sg_exception("Connection still has requests active");
}
std::cout << "Passed test: detect corrupted download" << std::endl;
}
void testPartialUpdateBasic(HTTP::Client* cl)
{
std::auto_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_partial_update");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
global_repo->defineFile("dirA/subdirF/fileAFA");
global_repo->defineFile("dirA/subdirF/fileAFB");
global_repo->defineFile("dirA/subdirH/fileAHA");
global_repo->defineFile("dirA/subdirH/fileAHB");
global_repo->defineFile("dirG/subdirA/subsubA/fileGAAB");
// request subdir of A
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->addSubpath("dirA/subdirF");
waitForUpdateComplete(cl, repo.get());
verifyFileState(p, "dirA/subdirF/fileAFA");
verifyFileState(p, "dirA/subdirF/fileAFB");
verifyFileState(p, "fileA"); // files are always synced
verifyFileState(p, "dirA/fileAB");
verifyFileNotPresent(p, "dirB/subdirB/fileBBB");
verifyFileNotPresent(p, "dirD");
verifyFileNotPresent(p, "dirA/subdirH/fileAHB");
verifyRequestCount("dirA", 1);
verifyRequestCount("dirA/fileAA", 1);
verifyRequestCount("dirA/subdirF", 1);
verifyRequestCount("dirA/subdirF/fileAFA", 1);
verifyRequestCount("dirA/subdirF/fileAFB", 1);
verifyRequestCount("dirB", 0);
verifyRequestCount("dirG", 0);
// now request dir B
repo->addSubpath("dirB");
waitForUpdateComplete(cl, repo.get());
verifyFileState(p, "dirA/subdirF/fileAFB");
verifyFileState(p, "dirB/subdirB/fileBBA");
verifyFileState(p, "dirB/subdirB/fileBBB");
verifyRequestCount("dirB", 1);
verifyRequestCount("dirB/subdirA/fileBAC", 1);
verifyRequestCount("dirA", 1);
verifyRequestCount("dirA/fileAA", 1);
verifyRequestCount("dirG", 0);
// widen subdir to parent
repo->addSubpath("dirA");
waitForUpdateComplete(cl, repo.get());
verifyFileState(p, "dirA/subdirH/fileAHA");
verifyFileState(p, "dirA/subdirH/fileAHB");
verifyRequestCount("dirA", 1);
verifyRequestCount("dirB/subdirA/fileBAC", 1);
verifyRequestCount("dirA/subdirF/fileAFA", 1);
// request an already fetched subdir - should be a no-op
repo->addSubpath("dirB/subdirB");
waitForUpdateComplete(cl, repo.get());
verifyRequestCount("dirB", 1);
verifyRequestCount("dirB/subdirB/fileBBB", 1);
// add new / modify files inside
global_repo->defineFile("dirA/subdirF/fileAFC");
global_repo->defineFile("dirA/subdirF/fileAFD");
repo->update();
waitForUpdateComplete(cl, repo.get());
if (global_repo->requestCount != 2) {
throw sg_exception("Bad root request count");
}
verifyFileState(p, "dirA/subdirF/fileAFC");
verifyFileState(p, "dirA/subdirF/fileAFD");
std::cout << "Passed test: basic partial clone and update" << std::endl;
}
void testPartialUpdateExisting(HTTP::Client* cl)
{
std::auto_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_partial_update_existing");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
// full update to sync everything
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
waitForUpdateComplete(cl, repo.get());
// new repo for partial
global_repo->clearRequestCounts();
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->addSubpath("dirA/subdirF");
waitForUpdateComplete(cl, repo.get());
if (global_repo->requestCount != 1) {
throw sg_exception("Bad root request count");
}
verifyRequestCount("dirA", 0);
verifyRequestCount("dirA/fileAA", 0);
verifyRequestCount("dirA/subdirF", 0);
verifyRequestCount("dirA/subdirF/fileAFA", 0);
verifyRequestCount("dirA/subdirF/fileAFB", 0);
// and request more dirs
// this is a good simulation of terrasync requesting more subdirs of
// an already created and in sync tree. should not generate any more
// network trip
repo->addSubpath("dirC");
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
verifyRequestCount("dirC/subdirA/subsubA/fileCAAA", 0);
if (global_repo->requestCount != 1) {
throw sg_exception("Bad root request count");
}
std::cout << "Passed test: partial update of existing" << std::endl;
}
void modifyBTree()
{
std::cout << "Modifying sub-tree" << std::endl;
global_repo->findEntry("dirB/subdirA/fileBAC")->revision++;
global_repo->defineFile("dirB/subdirZ/fileBZA");
global_repo->findEntry("dirB/subdirB/fileBBB")->revision++;
}
void testPartialUpdateWidenWhileInProgress(HTTP::Client* cl)
{
std::auto_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_partial_update_widen");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
// full update to sync everything
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->addSubpath("dirA/subdirF");
repo->addSubpath("dirB/subdirB");
waitForUpdateComplete(cl, repo.get());
verifyRequestCount("dirA/subdirF", 1);
if (global_repo->requestCount != 1) {
throw sg_exception("Bad root request count");
}
repo->addSubpath("dirA");
repo->addSubpath("dirB");
repo->addSubpath("dirC");
waitForUpdateComplete(cl, repo.get());
// should not request the root again
verifyRequestCount("dirA/subdirF", 1);
if (global_repo->requestCount != 1) {
throw sg_exception("Bad root request count");
}
verifyFileState(p, "dirA/subdirF/fileAFA");
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
std::cout << "Passed test: partial update with widen" << std::endl;
}
void testServerModifyDuringSync(HTTP::Client* cl)
{
std::auto_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_server_modify_during_sync");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
global_repo->findEntry("dirA/fileAA")->accessCallback.reset(make_callback(&modifyBTree));
repo->update();
waitForUpdateComplete(cl, repo.get());
global_repo->findEntry("dirA/fileAA")->accessCallback.reset();
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
throw sg_exception("Bad result from corrupt files test");
}
std::cout << "Passed test modify server during sync" << std::endl;
}
void testDestroyDuringSync(HTTP::Client* cl)
{
std::auto_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_destory_during_sync");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
}
global_repo->clearRequestCounts();
global_repo->clearFailFlags();
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->setEntireRepositoryMode();
repo->update();
// would ideally spin slightly here
repo.reset();
if (cl->hasActiveRequests()) {
throw sg_exception("destory of repo didn't clean up requests");
}
std::cout << "Passed test destory during sync" << std::endl;
}
int main(int argc, char* argv[])
{
sglog().setLogLevels( SG_ALL, SG_INFO );
sglog().setLogLevels( SG_ALL, SG_DEBUG );
HTTP::Client cl;
cl.setMaxConnections(1);
@@ -581,6 +888,8 @@ int main(int argc, char* argv[])
global_repo->defineFile("dirB/subdirA/fileBAA");
global_repo->defineFile("dirB/subdirA/fileBAB");
global_repo->defineFile("dirB/subdirA/fileBAC");
global_repo->defineFile("dirB/subdirB/fileBBA");
global_repo->defineFile("dirB/subdirB/fileBBB");
global_repo->defineFile("dirC/subdirA/subsubA/fileCAAA");
testBasicClone(&cl);
@@ -595,5 +904,16 @@ int main(int argc, char* argv[])
testAbandonCorruptFiles(&cl);
testServer.disconnectAll();
cl.clearAllConnections();
testPartialUpdateBasic(&cl);
testPartialUpdateExisting(&cl);
testPartialUpdateWidenWhileInProgress(&cl);
testServerModifyDuringSync(&cl);
testDestroyDuringSync(&cl);
return 0;
}

58
simgear/io/test_untar.cxx Normal file
View File

@@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////////////////
// Test harness.
////////////////////////////////////////////////////////////////////////
#include <simgear/compiler.h>
#include <iostream>
#include "untar.hxx"
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/sg_file.hxx>
using std::cout;
using std::cerr;
using std::endl;
using namespace simgear;
void testTarGz()
{
SGPath p = SGPath(SRC_DIR);
p.append("test.tar.gz");
SGBinaryFile f(p);
f.open(SG_IO_IN);
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
VERIFY(TarExtractor::isTarData(buf, bufSize));
}
void testPlainTar()
{
SGPath p = SGPath(SRC_DIR);
p.append("test2.tar");
SGBinaryFile f(p);
f.open(SG_IO_IN);
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
VERIFY(TarExtractor::isTarData(buf, bufSize));
}
int main (int ac, char ** av)
{
testTarGz();
testPlainTar();
return 0;
}

418
simgear/io/untar.cxx Normal file
View File

@@ -0,0 +1,418 @@
// Copyright (C) 2016 James Turner - <zakalawe@mac.com>
//
// 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 General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "untar.hxx"
#include <cstdlib>
#include <cassert>
#include <stdint.h>
#include <cstring>
#include <cstddef>
#include <algorithm>
#include <zlib.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/debug/logstream.hxx>
namespace simgear
{
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
const int ZLIB_INFLATE_WINDOW_BITS = MAX_WBITS;
const int ZLIB_DECODE_GZIP_HEADER = 16;
/* tar Header Block, from POSIX 1003.1-1990. */
typedef struct
{
char fileName[100];
char mode[8]; /* 100 */
char uid[8]; /* 108 */
char gid[8]; /* 116 */
char size[12]; /* 124 */
char mtime[12]; /* 136 */
char chksum[8]; /* 148 */
char typeflag; /* 156 */
char linkname[100]; /* 157 */
char magic[6]; /* 257 */
char version[2]; /* 263 */
char uname[32]; /* 265 */
char gname[32]; /* 297 */
char devmajor[8]; /* 329 */
char devminor[8]; /* 337 */
char prefix[155]; /* 345 */
} UstarHeaderBlock;
const size_t TAR_HEADER_BLOCK_SIZE = 512;
#define TMAGIC "ustar" /* ustar and a null */
#define TMAGLEN 5 // 5, not 6, becuase some files use 'ustar '
#define TVERSION "00" /* 00 and no null */
#define TVERSLEN 2
/* Values used in typeflag field. */
#define REGTYPE '0' /* regular file */
#define AREGTYPE '\0' /* regular file */
#define LNKTYPE '1' /* link */
#define SYMTYPE '2' /* reserved */
#define CHRTYPE '3' /* character special */
#define BLKTYPE '4' /* block special */
#define DIRTYPE '5' /* directory */
#define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */
class TarExtractorPrivate
{
public:
typedef enum {
INVALID = 0,
READING_HEADER,
READING_FILE,
READING_PADDING,
PRE_END_OF_ARCHVE,
END_OF_ARCHIVE,
ERROR_STATE, ///< states above this are error conditions
BAD_ARCHIVE,
BAD_DATA
} State;
SGPath path;
State state;
union {
UstarHeaderBlock header;
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
};
size_t bytesRemaining;
std::auto_ptr<SGFile> currentFile;
size_t currentFileSize;
z_stream zlibStream;
uint8_t* zlibOutput;
bool haveInitedZLib;
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
uint8_t* headerPtr;
TarExtractorPrivate() :
haveInitedZLib(false),
uncompressedData(false)
{
}
~TarExtractorPrivate()
{
free(zlibOutput);
}
void checkEndOfState()
{
if (bytesRemaining > 0) {
return;
}
if (state == READING_FILE) {
currentFile->close();
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
if (pad) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
setState(READING_PADDING);
} else {
setState(READING_HEADER);
}
} else if (state == READING_HEADER) {
processHeader();
} else if (state == PRE_END_OF_ARCHVE) {
if (headerIsAllZeros()) {
setState(END_OF_ARCHIVE);
} else {
// what does the spec say here?
}
} else if (state == READING_PADDING) {
setState(READING_HEADER);
}
}
void setState(State newState)
{
if ((newState == READING_HEADER) || (newState == PRE_END_OF_ARCHVE)) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE;
headerPtr = headerBytes;
}
state = newState;
}
void processHeader()
{
if (headerIsAllZeros()) {
if (state == PRE_END_OF_ARCHVE) {
setState(END_OF_ARCHIVE);
} else {
setState(PRE_END_OF_ARCHVE);
}
return;
}
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "magic is wrong");
state = BAD_ARCHIVE;
return;
}
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;
}
SGPath p = path;
p.append(tarPath);
if (header.typeflag == DIRTYPE) {
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);
setState(READING_FILE);
} else {
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
state = BAD_ARCHIVE;
}
}
void processBytes(const char* bytes, size_t count)
{
if ((state >= ERROR_STATE) || (state == END_OF_ARCHIVE)) {
return;
}
size_t curBytes = std::min(bytesRemaining, count);
if (state == READING_FILE) {
currentFile->write(bytes, curBytes);
bytesRemaining -= curBytes;
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
memcpy(headerPtr, bytes, curBytes);
bytesRemaining -= curBytes;
headerPtr += curBytes;
} else if (state == READING_PADDING) {
bytesRemaining -= curBytes;
}
checkEndOfState();
if (count > curBytes) {
// recurse with unprocessed bytes
processBytes(bytes + curBytes, count - curBytes);
}
}
bool headerIsAllZeros() const
{
char* headerAsChar = (char*) &header;
for (size_t i=0; i < offsetof(UstarHeaderBlock, magic); ++i) {
if (*headerAsChar++ != 0) {
return false;
}
}
return true;
}
bool isSafePath(const std::string& p) const
{
if (p.empty()) {
return false;
}
// reject absolute paths
if (p.at(0) == '/') {
return false;
}
// reject paths containing '..'
size_t doubleDot = p.find("..");
if (doubleDot != std::string::npos) {
return false;
}
// on POSIX could use realpath to sanity check
return true;
}
};
TarExtractor::TarExtractor(const SGPath& rootPath) :
d(new TarExtractorPrivate)
{
d->path = rootPath;
d->state = TarExtractorPrivate::INVALID;
memset(&d->zlibStream, 0, sizeof(z_stream));
d->zlibOutput = (unsigned char*) malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
d->zlibStream.zalloc = Z_NULL;
d->zlibStream.zfree = Z_NULL;
d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
d->zlibStream.next_out = d->zlibOutput;
}
TarExtractor::~TarExtractor()
{
}
void TarExtractor::extractBytes(const char* bytes, size_t count)
{
if (d->state >= TarExtractorPrivate::ERROR_STATE) {
return;
}
d->zlibStream.next_in = (uint8_t*) bytes;
d->zlibStream.avail_in = count;
if (!d->haveInitedZLib) {
// now we have data, see if we're dealing with GZ-compressed data or not
uint8_t* ubytes = (uint8_t*) bytes;
if ((ubytes[0] == 0x1f) && (ubytes[1] == 0x8b)) {
// GZIP identification bytes
if (inflateInit2(&d->zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
} else {
UstarHeaderBlock* header = (UstarHeaderBlock*) bytes;
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "didn't find tar magic in header");
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
d->uncompressedData = true;
}
d->haveInitedZLib = true;
d->setState(TarExtractorPrivate::READING_HEADER);
} // of init on first-bytes case
if (d->uncompressedData) {
d->processBytes(bytes, count);
} else {
size_t writtenSize;
// loop, running zlib() inflate and sending output bytes to
// our request body handler. Keep calling inflate until no bytes are
// written, and ZLIB has consumed all available input
do {
d->zlibStream.next_out = d->zlibOutput;
d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
int result = inflate(&d->zlibStream, Z_NO_FLUSH);
if (result == Z_OK || result == Z_STREAM_END) {
// nothing to do
} else if (result == Z_BUF_ERROR) {
// transient error, fall through
} else {
// _error = result;
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << d->zlibStream.msg);
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - d->zlibStream.avail_out;
if (writtenSize > 0) {
d->processBytes((const char*) d->zlibOutput, writtenSize);
}
if (result == Z_STREAM_END) {
break;
}
} while ((d->zlibStream.avail_in > 0) || (writtenSize > 0));
} // of Zlib-compressed data
}
bool TarExtractor::isAtEndOfArchive() const
{
return (d->state == TarExtractorPrivate::END_OF_ARCHIVE);
}
bool TarExtractor::hasError() const
{
return (d->state >= TarExtractorPrivate::ERROR_STATE);
}
bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
{
if (count < 2) {
return false;
}
UstarHeaderBlock* header = 0;
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
// GZIP identification bytes
z_stream z;
uint8_t* zlibOutput = static_cast<uint8_t*>(alloca(4096));
memset(&z, 0, sizeof(z_stream));
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.avail_out = 4096;
z.next_out = zlibOutput;
z.next_in = (uint8_t*) bytes;
z.avail_in = count;
if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
return false;
}
int result = inflate(&z, Z_SYNC_FLUSH);
if (result != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
return false; // not tar data
}
size_t written = 4096 - z.avail_out;
if (written < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
return false;
}
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
} else {
// uncompressed tar
if (count < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
return false;
}
header = (UstarHeaderBlock*) bytes;
}
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "not a tar file");
return false;
}
return true;
}
} // of simgear

52
simgear/io/untar.hxx Normal file
View File

@@ -0,0 +1,52 @@
// Copyright (C) 2016 James Turner - <zakalawe@mac.com>
//
// 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 General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SG_IO_UNTAR_HXX
#define SG_IO_UNTAR_HXX
#include <memory>
#include <cstdlib>
#include <stdint.h> // for uint8_t
#include <simgear/misc/sg_path.hxx>
namespace simgear
{
class TarExtractorPrivate;
class TarExtractor
{
public:
TarExtractor(const SGPath& rootPath);
~TarExtractor();
static bool isTarData(const uint8_t* bytes, size_t count);
void extractBytes(const char* bytes, size_t count);
bool isAtEndOfArchive() const;
bool hasError() const;
private:
std::auto_ptr<TarExtractorPrivate> d;
};
} // of namespace simgear
#endif // of SG_IO_UNTAR_HXX

View File

@@ -19,6 +19,7 @@
#define SGGeod_H
#include <simgear/constants.h>
#include <simgear/math/SGVec3.hxx>
// #define SG_GEOD_NATIVE_DEGREE

View File

@@ -153,10 +153,10 @@ public:
/// Use with care: allways code that you do not need to use that!
static bool isNaN(const T& v)
{
#ifdef HAVE_ISNAN
return (isnan(v) != 0);
#elif defined HAVE_STD_ISNAN
#ifdef HAVE_STD_ISNAN
return std::isnan(v);
#elif defined HAVE_ISNAN
return (isnan(v) != 0);
#else
// Use that every compare involving a NaN returns false
// But be careful, some usual compiler switches like for example

View File

@@ -20,6 +20,10 @@
#include <iosfwd>
#include <simgear/math/SGLimits.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/math/SGMathFwd.hxx>
/// 2D Vector Class
template<typename T>
class SGVec2 {

View File

@@ -20,6 +20,9 @@
#include <iosfwd>
#include <simgear/math/SGVec2.hxx>
#include <simgear/math/SGGeodesy.hxx>
/// 3D Vector Class
template<typename T>
class SGVec3 {

View File

@@ -32,6 +32,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include "interpolater.hxx"
@@ -57,6 +58,28 @@ SGInterpTable::SGInterpTable( const std::string& file )
{
SG_LOG( SG_MATH, SG_INFO, "Initializing Interpolator for " << file );
sg_gzifstream in( SGPath::fromUtf8(file) );
if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );
return;
}
in >> skipcomment;
while ( in ) {
double ind, dep;
in >> ind >> dep;
in >> std::skipws;
_table[ind] = dep;
}
}
// Constructor -- loads the interpolation table from the specified
// file
SGInterpTable::SGInterpTable( const SGPath& file )
{
SG_LOG( SG_MATH, SG_INFO, "Initializing Interpolator for " << file );
sg_gzifstream in( file );
if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );

View File

@@ -41,6 +41,7 @@
#include <string>
class SGPropertyNode;
class SGPath;
/**
* A class that provids a simple linear 2d interpolation lookup table.
@@ -69,7 +70,12 @@ public:
*/
SGInterpTable( const std::string& file );
/**
* Constructor. Loads the interpolation table from the specified file.
* @param file name of interpolation file
*/
SGInterpTable( const SGPath& path );
/**
* Add an entry to the table, extending the table's length.
*

View File

@@ -47,6 +47,7 @@
#include <simgear/props/props_io.hxx>
#include <simgear/misc/stdint.hxx>
#include <simgear/misc/sg_path.hxx>
#include <string.h>
@@ -139,7 +140,7 @@ gzContainerWriter::writeContainer(ContainerType Type, SGPropertyNode* root)
gzContainerReader::gzContainerReader(const std::string& name,
const std::string& fileMagic) :
sg_gzifstream(name, ios_in | ios_binary),
sg_gzifstream(SGPath(name), ios_in | ios_binary),
filename(name)
{
bool ok = (good() && !eof());

View File

@@ -9,20 +9,10 @@ using std::cout;
using std::cerr;
using std::endl;
#define COMPARE(a, b) \
if ((a) != (b)) { \
cerr << "failed:" << #a << " != " << #b << endl; \
exit(1); \
}
#define VERIFY(a) \
if (!(a)) { \
cerr << "failed:" << #a << endl; \
exit(1); \
}
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/sgstream.hxx>
void test_dir()
{
@@ -81,6 +71,69 @@ SGPath::Permissions validateWrite(const SGPath&)
return p;
}
void test_path_dir()
{
simgear::Dir temp = simgear::Dir::tempDir("path_dir");
temp.remove(true);
SGPath p = temp.path();
VERIFY(p.isAbsolute());
COMPARE(p.create_dir(0755), 0);
SGPath sub = p / "subA" / "subB";
VERIFY(!sub.exists());
SGPath subFile = sub / "fileABC.txt";
COMPARE(subFile.create_dir(0755), 0);
VERIFY(!subFile.exists());
sub.set_cached(false);
VERIFY(sub.exists());
VERIFY(sub.isDir());
SGPath sub2 = p / "subA" / "fileA";
{
sg_ofstream os(sub2);
VERIFY(os.is_open());
for (int i = 0; i < 50; ++i) {
os << "ABCD" << endl;
}
}
VERIFY(sub2.isFile());
COMPARE(sub2.sizeInBytes(), 250);
SGPath sub3 = p / "subß" / "file𝕽";
sub3.create_dir(0755);
{
sg_ofstream os(sub3);
VERIFY(os.is_open());
for (int i = 0; i < 20; ++i) {
os << "EFGH" << endl;
}
}
sub3.set_cached(false);
VERIFY(sub3.exists());
COMPARE(sub3.sizeInBytes(), 100);
COMPARE(sub3.file(), "file𝕽");
simgear::Dir subD(p / "subA");
simgear::PathList dirChildren = subD.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
COMPARE(dirChildren.size(), 1);
COMPARE(dirChildren[0], subD.path() / "subB");
simgear::PathList fileChildren = subD.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
COMPARE(fileChildren.size(), 1);
COMPARE(fileChildren[0], subD.path() / "fileA");
simgear::Dir subS(sub3.dirPath());
fileChildren = subS.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
COMPARE(fileChildren.size(), 1);
COMPARE(fileChildren[0], subS.path() / "file𝕽");
}
int main(int argc, char* argv[])
{
SGPath pa;
@@ -89,8 +142,8 @@ int main(int argc, char* argv[])
// test basic parsing
SGPath pb("/Foo/bar/something.png");
COMPARE(pb.str(), std::string("/Foo/bar/something.png"));
COMPARE(strcmp(pb.c_str(), "/Foo/bar/something.png"), 0);
COMPARE(pb.utf8Str(), std::string("/Foo/bar/something.png"));
COMPARE(pb.local8BitStr(), std::string("/Foo/bar/something.png"));
COMPARE(pb.dir(), std::string("/Foo/bar"));
COMPARE(pb.file(), std::string("something.png"));
COMPARE(pb.base(), std::string("/Foo/bar/something"));
@@ -101,7 +154,8 @@ int main(int argc, char* argv[])
// relative paths
SGPath ra("where/to/begin.txt");
COMPARE(ra.str(), std::string("where/to/begin.txt"));
COMPARE(ra.utf8Str(), std::string("where/to/begin.txt"));
COMPARE(ra.local8BitStr(), std::string("where/to/begin.txt"));
COMPARE(ra.dir(), std::string("where/to"));
COMPARE(ra.file(), std::string("begin.txt"));
COMPARE(ra.file_base(), std::string("begin"));
@@ -127,34 +181,26 @@ int main(int argc, char* argv[])
// path fixing
SGPath rd("where\\to\\begin.txt");
COMPARE(rd.str(), std::string("where/to/begin.txt"));
COMPARE(rd.utf8Str(), std::string("where/to/begin.txt"));
// test modification
// append
SGPath d1("/usr/local");
SGPath pc = d1;
COMPARE(pc.str(), std::string("/usr/local"));
COMPARE(pc.utf8Str(), std::string("/usr/local"));
pc.append("include");
COMPARE(pc.str(), std::string("/usr/local/include"));
COMPARE(pc.utf8Str(), std::string("/usr/local/include"));
COMPARE(pc.file(), std::string("include"));
// add
pc.add("/opt/local");
#ifdef _WIN32
COMPARE(pc.str(), std::string("/usr/local/include/;/opt/local"));
#else
COMPARE(pc.str(), std::string("/usr/local/include/:/opt/local"));
#endif
// concat
SGPath pd = pb;
pd.concat("-1");
COMPARE(pd.str(), std::string("/Foo/bar/something.png-1"));
COMPARE(pd.utf8Str(), std::string("/Foo/bar/something.png-1"));
// create with relative path
SGPath rb(d1, "include/foo");
COMPARE(rb.str(), std::string("/usr/local/include/foo"));
COMPARE(rb.utf8Str(), std::string("/usr/local/include/foo"));
VERIFY(rb.isAbsolute());
// lower-casing of file extensions
@@ -170,12 +216,9 @@ int main(int argc, char* argv[])
COMPARE(extB.lower_extension(), "gz");
COMPARE(extB.complete_lower_extension(), "html.gz");
#ifdef _WIN32
COMPARE(d1.str_native(), std::string("\\usr\\local"));
SGPath winAbs("C:\\Windows\\System32");
COMPARE(winAbs.str(), std::string("C:/Windows/System32"));
#else
COMPARE(d1.str_native(), std::string("/usr/local"));
COMPARE(winAbs.local8BitStr(), std::string("C:/Windows/System32"));
#endif
// paths with only the file components
@@ -207,6 +250,8 @@ int main(int argc, char* argv[])
test_dir();
test_path_dir();
cout << "all tests passed OK" << endl;
return 0; // passed
}

View File

@@ -18,11 +18,11 @@
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/structure/exception.hxx>
#include <math.h>
#include <stdlib.h>
#include <cstdio>
@@ -31,6 +31,7 @@
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <direct.h>
# include <Shlwapi.h>
#else
# include <sys/types.h>
# include <dirent.h>
@@ -39,6 +40,7 @@
# include <errno.h>
#endif
#include <simgear/misc/strutils.hxx>
#include <simgear/debug/logstream.hxx>
#include <boost/foreach.hpp>
@@ -65,7 +67,7 @@ Dir::Dir(const SGPath& path) :
}
Dir::Dir(const Dir& rel, const SGPath& relPath) :
_path(rel.file(relPath.str())),
_path(rel.file(relPath.utf8Str())),
_removeOnDestroy(false)
{
_path.set_cached(false); // disable caching, so create/remove work
@@ -85,11 +87,16 @@ void Dir::setRemoveOnDestroy()
Dir Dir::current()
{
#ifdef _WIN32
char* buf = _getcwd(NULL, 0);
#if defined(SG_WINDOWS)
wchar_t* buf = _wgetcwd(NULL, 0);
#else
char* buf = ::getcwd(NULL, 0);
char *buf = ::getcwd(NULL, 0);
#endif
if (!buf) {
if (errno == 2) throw sg_exception("The current directory is invalid");
else throw sg_exception(strerror(errno));
}
SGPath p(buf);
free(buf);
return Dir(p);
@@ -108,18 +115,26 @@ Dir Dir::tempDir(const std::string& templ)
// Mac OS-X / BSD manual says any number of 'X's, but GLibc manual
// says exactly six, so that's what I'm going with
p.concat("-XXXXXX");
::snprintf(buf, 1024, "%s", p.c_str());
std::string s = p.local8BitStr();
::snprintf(buf, 1024, "%s", s.c_str());
if (!mkdtemp(buf)) {
SG_LOG(SG_IO, SG_WARN, "mkdtemp failed:" << strerror(errno));
return Dir();
}
return Dir(SGPath(buf));
#else
#if defined(SG_WINDOWS)
std::wstring wideTemplate = simgear::strutils::convertUtf8ToWString(templ);
wchar_t* buf = _wtempnam(0, wideTemplate.c_str());
SGPath p(buf);
free(buf); // unlike tempnam(), _wtempnam mallocs its result buffer
#else
SGPath p(tempnam(0, templ.c_str()));
#endif
Dir t(p);
if (!t.create(0700)) {
SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p.str());
SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p);
}
return t;
@@ -138,36 +153,37 @@ PathList Dir::children(int types, const std::string& nameFilter) const
types = TYPE_FILE | TYPE_DIR | NO_DOT_OR_DOTDOT;
}
#ifdef _WIN32
std::string search(_path.str());
#if defined(SG_WINDOWS)
std::wstring search(_path.wstr());
if (nameFilter.empty()) {
search += "\\*"; // everything
search += simgear::strutils::convertUtf8ToWString("\\*"); // everything
} else {
search += "\\*" + nameFilter;
search += simgear::strutils::convertUtf8ToWString("\\*" + nameFilter);
}
WIN32_FIND_DATA fData;
HANDLE find = FindFirstFile(search.c_str(), &fData);
WIN32_FIND_DATAW fData;
HANDLE find = FindFirstFileW(search.c_str(), &fData);
if (find == INVALID_HANDLE_VALUE) {
int err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND) {
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" <<
_path.str() << " with error:" << err);
_path << " with error:" << err);
}
return result;
}
bool done = false;
for (bool done = false; !done; done = (FindNextFile(find, &fData) == 0)) {
for (bool done = false; !done; done = (FindNextFileW(find, &fData) == 0)) {
if (fData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
if (!(types & INCLUDE_HIDDEN)) {
continue;
}
}
std::string utf8File = simgear::strutils::convertWStringToUtf8(fData.cFileName);
if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (types & NO_DOT_OR_DOTDOT) {
if (!strcmp(fData.cFileName,".") || !strcmp(fData.cFileName,"..")) {
if ((utf8File == ".") || (utf8File == "..")) {
continue;
}
}
@@ -183,14 +199,15 @@ PathList Dir::children(int types, const std::string& nameFilter) const
continue;
}
result.push_back(file(fData.cFileName));
result.push_back(file(utf8File));
}
FindClose(find);
#else
DIR* dp = opendir(_path.c_str());
std::string ps = _path.local8BitStr();
DIR* dp = opendir(ps.c_str());
if (!dp) {
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: opendir failed:" << _path.str());
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: opendir failed:" << _path);
return result;
}
@@ -262,51 +279,21 @@ PathList Dir::children(int types, const std::string& nameFilter) const
bool Dir::isEmpty() const
{
bool empty= true;
#ifdef _WIN32
WIN32_FIND_DATA fData;
HANDLE find = FindFirstFile("\\*", &fData);
if (find == INVALID_HANDLE_VALUE) {
return true;
}
// since an empty dir will still have . and .. children, we need
// watch for those - anything else means the dir is really non-empty
bool done = false;
for (; !done; done = (FindNextFile(find, &fData) == 0)) {
if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!strcmp(fData.cFileName,".") || !strcmp(fData.cFileName,"..")) {
continue;
}
}
empty = false;
break;
}
FindClose(find);
#if defined(SG_WINDOWS)
std::wstring ps = _path.wstr();
return PathIsDirectoryEmptyW( ps.c_str() );
#else
DIR* dp = opendir(_path.c_str());
if (!dp) {
return true;
}
while (true) {
struct dirent* entry = readdir(dp);
if (!entry) {
break; // done iteration
}
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
empty = false;
break;
}
std::string ps = _path.local8BitStr();
DIR* dp = opendir( ps.c_str() );
if (!dp) return true;
int n = 0;
dirent* d;
while( (d = readdir(dp)) !=NULL && (n < 4) ) n++;
closedir(dp);
return (n == 2); // '.' and '..' always exist
#endif
return empty;
}
bool Dir::exists() const
@@ -322,12 +309,6 @@ SGPath Dir::file(const std::string& name) const
return childPath;
}
#ifdef _WIN32
# define sgMkDir(d,m) _mkdir(d)
#else
# define sgMkDir(d,m) mkdir(d,m)
#endif
bool Dir::create(mode_t mode)
{
if (exists()) {
@@ -344,9 +325,15 @@ bool Dir::create(mode_t mode)
}
// finally, create ourselves
int err = sgMkDir(_path.c_str(), mode);
#if defined(SG_WINDOWS)
std::wstring ps = _path.wstr();
int err = _wmkdir(ps.c_str());
#else
std::string ps = _path.local8BitStr();
int err = mkdir(ps.c_str(), mode);
#endif
if (err) {
SG_LOG(SG_IO, SG_WARN, "directory creation failed: (" << _path.str() << ") " << strerror(errno) );
SG_LOG(SG_IO, SG_WARN, "directory creation failed: (" << _path << ") " << strerror(errno) );
}
return (err == 0);
@@ -386,14 +373,16 @@ bool Dir::remove(bool recursive)
return false;
}
} // of recursive deletion
#ifdef _WIN32
int err = _rmdir(_path.c_str());
#if defined(SG_WINDOWS)
std::wstring ps = _path.wstr();
int err = _wrmdir(ps.c_str());
#else
int err = rmdir(_path.c_str());
std::string ps = _path.local8BitStr();
int err = rmdir(ps.c_str());
#endif
if (err) {
SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path.str() << ":" << strerror(errno));
SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path << ":" << strerror(errno));
}
return (err == 0);
}

View File

@@ -27,10 +27,13 @@
#include <simgear_config.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sgstream.hxx>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <fstream>
#include <cstdlib>
#ifdef _WIN32
# include <direct.h>
@@ -61,24 +64,26 @@ static const char sgSearchPathSep = ':';
// included in the Windows 8.1 SDK
#include "sgversionhelpers.hxx"
#define ENABLE_OLD_PATH_API 1
static SGPath pathForCSIDL(int csidl, const SGPath& def)
{
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, PWSTR, int, BOOL);
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
// lazy open+resolve of shell32
if (!SHGetSpecialFolderPath) {
HINSTANCE shellDll = ::LoadLibrary("shell32");
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathA");
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathW");
}
if (!SHGetSpecialFolderPath){
return def;
}
char path[MAX_PATH];
wchar_t path[MAX_PATH];
if (SHGetSpecialFolderPath(0, path, csidl, false)) {
return SGPath(path, def.getPermissionChecker());
return SGPath(std::wstring(path), def.getPermissionChecker());
}
return def;
@@ -96,16 +101,7 @@ static SGPath pathForKnownFolder(REFKNOWNFOLDERID folderId, const SGPath& def)
wchar_t* localFolder = 0;
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT_PATH, NULL, &localFolder) == S_OK) {
// copy into local memory
char path[MAX_PATH];
size_t len;
if (wcstombs_s(&len, path, localFolder, MAX_PATH) != S_OK) {
path[0] = '\0';
SG_LOG(SG_GENERAL, SG_WARN, "WCS to MBS failed");
}
SGPath folder_path = SGPath(path, def.getPermissionChecker());
SGPath folder_path = SGPath(localFolder, def.getPermissionChecker());
// release dynamic memory
CoTaskMemFree(static_cast<void*>(localFolder));
@@ -143,7 +139,7 @@ static SGPath getXDGDir( const std::string& name,
// path. No other format is supported.
const std::string XDG_ID = "XDG_" + name + "_DIR=\"";
std::ifstream user_dirs_file( user_dirs.c_str() );
sg_ifstream user_dirs_file( user_dirs );
std::string line;
while( std::getline(user_dirs_file, line).good() )
{
@@ -208,6 +204,18 @@ SGPath::SGPath( const std::string& p, PermissionChecker validator )
fix();
}
// create a path based on "path"
SGPath::SGPath(const std::wstring& p, PermissionChecker validator) :
_permission_checker(validator),
_cached(false),
_rwCached(false),
_cacheEnabled(true)
{
path = simgear::strutils::convertWStringToUtf8(p);
fix();
}
// create a path based on "path" and a "subpath"
SGPath::SGPath( const SGPath& p,
const std::string& r,
@@ -222,6 +230,17 @@ SGPath::SGPath( const SGPath& p,
fix();
}
SGPath SGPath::fromLocal8Bit(const char *name)
{
return SGPath(simgear::strutils::convertWindowsLocal8BitToUtf8(name));
}
SGPath SGPath::fromUtf8(const std::string& bytes, PermissionChecker p)
{
return SGPath(bytes, p);
}
SGPath::SGPath(const SGPath& p) :
path(p.path),
_permission_checker(p._permission_checker),
@@ -259,7 +278,7 @@ SGPath& SGPath::operator=(const SGPath& p)
SGPath::~SGPath() {
}
#if defined(ENABLE_OLD_PATH_API)
// set path
void SGPath::set( const string& p ) {
path = p;
@@ -267,6 +286,7 @@ void SGPath::set( const string& p ) {
_cached = false;
_rwCached = false;
}
#endif
//------------------------------------------------------------------------------
void SGPath::setPermissionChecker(PermissionChecker validator)
@@ -311,11 +331,12 @@ SGPath SGPath::operator/( const std::string& p ) const
return ret;
}
#if defined(ENABLE_OLD_PATH_API)
//add a new path component to the existing path string
void SGPath::add( const string& p ) {
append( sgSearchPathSep+p );
}
#endif
// concatenate a string to the end of the path without inserting a
// path separator
@@ -330,6 +351,10 @@ void SGPath::concat( const string& p ) {
_rwCached = false;
}
std::string SGPath::local8BitStr() const
{
return simgear::strutils::convertUtf8ToWindowsLocal8Bit(path);
}
// Get the file part of the path (everything after the last path sep)
string SGPath::file() const
@@ -341,7 +366,7 @@ string SGPath::file() const
return path;
}
}
// get the directory part of the path.
string SGPath::dir() const {
@@ -353,17 +378,22 @@ string SGPath::dir() const {
}
}
SGPath SGPath::dirPath() const
{
return SGPath::fromUtf8(dir());
}
// get the base part of the path (everything but the extension.)
string SGPath::base() const
{
string::size_type index = path.rfind(".");
string::size_type lastSep = path.rfind(sgDirPathSep);
// tolerate dots inside directory names
if ((lastSep != string::npos) && (index < lastSep)) {
return path;
}
if (index != string::npos) {
return path.substr(0, index);
} else {
@@ -379,12 +409,12 @@ string SGPath::file_base() const
} else {
++index; // skip past the separator
}
string::size_type firstDot = path.find(".", index);
if (firstDot == string::npos) {
return path.substr(index); // no extensions
}
return path.substr(index, firstDot - index);
}
@@ -412,7 +442,7 @@ string SGPath::complete_lower_extension() const
} else {
++index; // skip past the separator
}
string::size_type firstDot = path.find(".", index);
if ((firstDot != string::npos) && (path.find(sgDirPathSep, firstDot) == string::npos)) {
return boost::to_lower_copy(path.substr(firstDot + 1));
@@ -436,12 +466,12 @@ void SGPath::validate() const
#ifdef _WIN32
struct _stat buf ;
bool remove_trailing = false;
string statPath(path);
std::wstring statPath(wstr());
if ((path.length() > 1) && (path.back() == '/')) {
statPath.pop_back();
}
if (_stat(statPath.c_str(), &buf ) < 0) {
if (_wstat(statPath.c_str(), &buf ) < 0) {
_exists = false;
} else {
_exists = true;
@@ -463,7 +493,7 @@ void SGPath::validate() const
_modTime = buf.st_mtime;
_size = buf.st_size;
}
#endif
_cached = true;
}
@@ -522,62 +552,68 @@ bool SGPath::isFile() const
}
//------------------------------------------------------------------------------
#ifdef _WIN32
# define sgMkDir(d,m) _mkdir(d)
#else
# define sgMkDir(d,m) mkdir(d,m)
#endif
int SGPath::create_dir(mode_t mode)
{
if( !canWrite() )
{
SG_LOG( SG_IO,
SG_WARN, "Error creating directory for '" << str() << "'"
SG_WARN, "Error creating directory for '" << *this << "'"
" reason: access denied" );
return -3;
}
string_list dirlist = sgPathSplit(dir());
if ( dirlist.empty() )
SGPath dirP = dirPath();
if (dirP.isNull() )
return -1;
string path = dirlist[0];
string_list path_elements = sgPathBranchSplit(path);
bool absolute = !path.empty() && path[0] == sgDirPathSep;
string_list path_elements = sgPathBranchSplit(dirP.utf8Str());
bool absolute = dirP.isAbsolute();
unsigned int i = 1;
SGPath dir(absolute ? string( 1, sgDirPathSep ) : "", _permission_checker);
dir.concat( path_elements[0] );
#ifdef _WIN32
if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) {
dir.append( path_elements[1] );
i = 2;
}
#if defined(SG_WINDOWS)
SGPath dir(path_elements.front(), _permission_checker);
// exists() does not work for drive letter paths, eg 'C:\'.
// Detect this case and skip to the next element immediately
if (absolute && path_elements.size() >= 2) {
dir.append(path_elements[i++]);
}
#else
SGPath dir((absolute ? "/" : "") + path_elements.front(), _permission_checker);
#endif
struct stat info;
int r;
for(; (r = stat(dir.c_str(), &info)) == 0 && i < path_elements.size(); ++i)
dir.append(path_elements[i]);
if( r == 0 )
return 0; // Directory already exists
while (dir.exists() && (i < path_elements.size())) {
dir.append(path_elements[i++]);
}
// already exists
if (dir.exists() && (i == path_elements.size())) {
return 0;
}
for(;;)
{
if( sgMkDir(dir.c_str(), mode) )
#if defined (SG_WINDOWS)
std::wstring ds = dir.wstr();
if (_wmkdir(ds.c_str()))
#else
std::string ds = dir.utf8Str();
if( mkdir(ds.c_str(), mode) )
#endif
{
SG_LOG( SG_IO,
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
return -2;
SG_LOG( SG_IO, SG_ALERT, "Error creating directory: (" << dir << "):"
<< simgear::strutils::error_string(errno) );
return errno;
}
else
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir.str());
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir);
if( i >= path_elements.size() )
return 0;
if (i >= path_elements.size()) {
break;
}
dir.append(path_elements[i++]);
}
_cached = false; // re-stat on next query
return 0;
}
@@ -627,7 +663,7 @@ bool SGPath::isAbsolute() const
if (path.empty()) {
return false;
}
#ifdef _WIN32
// detect '[A-Za-z]:/'
if (path.size() > 2) {
@@ -636,7 +672,7 @@ bool SGPath::isAbsolute() const
}
}
#endif
return (path[0] == sgDirPathSep);
}
@@ -645,10 +681,11 @@ bool SGPath::isNull() const
return path.empty();
}
#if defined(ENABLE_OLD_PATH_API)
std::string SGPath::str_native() const
{
#ifdef _WIN32
std::string s = str();
std::string s = local8BitStr();
std::string::size_type pos;
std::string nativeSeparator;
nativeSeparator = sgDirPathSepBad;
@@ -658,24 +695,31 @@ std::string SGPath::str_native() const
}
return s;
#else
return str();
return utf8Str();
#endif
}
#endif
//------------------------------------------------------------------------------
bool SGPath::remove()
{
if( !canWrite() )
{
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ")"
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ")"
" reason: access denied" );
return false;
}
int err = ::unlink(c_str());
#if defined(SG_WINDOWS)
std::wstring ps = wstr();
int err = _wunlink(ps.c_str());
#else
std::string ps = local8BitStr();
int err = ::unlink(ps.c_str());
#endif
if( err )
{
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ") "
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ") "
" reason: " << strerror(errno) );
// TODO check if failed unlink can really change any of the cached values
}
@@ -712,8 +756,8 @@ bool SGPath::rename(const SGPath& newName)
{
if( !canRead() || !canWrite() || !newName.canWrite() )
{
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
" to " << newName.str() <<
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << *this <<
" to " << newName <<
" reason: access denied" );
return false;
}
@@ -726,10 +770,20 @@ bool SGPath::rename(const SGPath& newName)
}
}
#endif
if( ::rename(c_str(), newName.c_str()) != 0 )
#if defined(SG_WINDOWS)
std::wstring p = wstr();
std::wstring np = newName.wstr();
if (_wrename(p.c_str(), np.c_str()) != 0)
#else
std::string p = local8BitStr();
std::string np = newName.local8BitStr();
if( ::rename(p.c_str(), np.c_str()) != 0 )
#endif
{
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
" to " << newName.str() <<
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << *this <<
" to " << newName <<
" reason: " << strerror(errno) );
return false;
}
@@ -815,12 +869,69 @@ SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def)
//------------------------------------------------------------------------------
SGPath SGPath::fromEnv(const char* name, const SGPath& def)
{
#if defined(SG_WINDOWS)
std::wstring wname = simgear::strutils::convertUtf8ToWString(name);
const wchar_t* val = _wgetenv(wname.c_str());
if (val && val[0])
return SGPath(val, def._permission_checker);
#else
const char* val = getenv(name);
if( val && val[0] )
return SGPath(val, def._permission_checker);
#endif
return def;
}
//------------------------------------------------------------------------------
std::vector<SGPath> SGPath::pathsFromEnv(const char *name)
{
std::vector<SGPath> r;
#if defined(SG_WINDOWS)
std::wstring wname = simgear::strutils::convertUtf8ToWString(name);
const wchar_t* val = _wgetenv(wname.c_str());
#else
const char* val = getenv(name);
#endif
if (!val) {
return r;
}
#if defined(SG_WINDOWS)
return pathsFromUtf8(simgear::strutils::convertWStringToUtf8(val));
#else
return pathsFromUtf8(val);
#endif
}
//------------------------------------------------------------------------------
std::vector<SGPath> SGPath::pathsFromUtf8(const std::string& paths)
{
std::vector<SGPath> r;
string_list items = sgPathSplit(paths);
string_list_iterator it;
for (it = items.begin(); it != items.end(); ++it) {
r.push_back(SGPath::fromUtf8(it->c_str()));
}
return r;
}
//------------------------------------------------------------------------------
std::vector<SGPath> SGPath::pathsFromLocal8Bit(const std::string& paths)
{
std::vector<SGPath> r;
string_list items = sgPathSplit(paths);
string_list_iterator it;
for (it = items.begin(); it != items.end(); ++it) {
r.push_back(SGPath::fromLocal8Bit(it->c_str()));
}
return r;
}
//------------------------------------------------------------------------------
SGPath SGPath::home(const SGPath& def)
{
@@ -844,11 +955,12 @@ SGPath SGPath::documents(const SGPath& def)
}
//------------------------------------------------------------------------------
std::string SGPath::realpath() const
SGPath SGPath::realpath() const
{
#if defined(_MSC_VER) /*for MS compilers */ || defined(_WIN32) /*needed for non MS windows compilers like MingW*/
#if defined(SG_WINDOWS)
// with absPath NULL, will allocate, and ignore length
char *buf = _fullpath( NULL, path.c_str(), _MAX_PATH );
std::wstring ws = wstr();
wchar_t *buf = _wfullpath( NULL, ws.c_str(), _MAX_PATH );
#else
// POSIX
char* buf = ::realpath(path.c_str(), NULL);
@@ -864,21 +976,45 @@ std::string SGPath::realpath() const
this_dir = "/";
}
if (file() == "..") {
this_dir = SGPath(SGPath(this_dir).realpath()).dir();
this_dir = SGPath(this_dir).realpath().dir();
if (this_dir.empty()) { // invalid path: .. above root
return "";
return SGPath();
}
return SGPath(this_dir).realpath(); // use native path separator,
// and handle 'existing/nonexisting/../symlink' paths
}
return SGPath(this_dir).realpath() +
#if defined(_MSC_VER) || defined(_WIN32)
"\\" + file();
#else
"/" + file();
#endif
return SGPath(this_dir).realpath() / file();
}
std::string p(buf);
#if defined(SG_WINDOWS)
SGPath p = SGPath(std::wstring(buf), NULL);
#else
SGPath p(SGPath::fromLocal8Bit(buf));
#endif
free(buf);
return p;
}
//------------------------------------------------------------------------------
std::string SGPath::join(const std::vector<SGPath>& paths, const std::string& joinWith)
{
std::string r;
if (paths.empty()) {
return r;
}
r = paths[0].utf8Str();
for (size_t i=1; i<paths.size(); ++i) {
r += joinWith + paths[i].utf8Str();
}
return r;
}
//------------------------------------------------------------------------------
std::wstring SGPath::wstr() const
{
return simgear::strutils::convertUtf8ToWString(path);
}

View File

@@ -73,6 +73,8 @@ public:
*/
SGPath( const std::string& p, PermissionChecker validator = NULL );
explicit SGPath(const std::wstring& p, PermissionChecker validator = NULL);
/**
* Construct a path based on the starting path provided and a relative subpath
* @param p initial path
@@ -131,10 +133,10 @@ public:
void concat( const std::string& p );
/**
* Returns a string with the absolute pathname that names the same file, whose
* Returns a path with the absolute pathname that names the same file, whose
* resolution does not involve '.', '..', or symbolic links.
*/
std::string realpath() const;
SGPath realpath() const;
/**
* Get the file part of the path (everything after the last path sep)
@@ -186,6 +188,11 @@ public:
* @return path string
*/
std::string str() const { return path; }
std::string utf8Str() const { return path; }
std::string local8BitStr() const;
std::wstring wstr() const;
/**
* Get the path string
@@ -264,6 +271,12 @@ public:
* or if the destination already exists, or is not writeable
*/
bool rename(const SGPath& newName);
/**
* return the path of the parent directory of this path.
*/
SGPath dirPath() const;
enum StandardLocation
{
@@ -286,6 +299,10 @@ public:
*/
static SGPath fromEnv(const char* name, const SGPath& def = SGPath());
static SGPath fromUtf8(const std::string& bytes, PermissionChecker p = NULL);
static SGPath fromLocal8Bit(const char* name);
/**
* Get path to user's home directory
*/
@@ -301,6 +318,13 @@ public:
*/
static SGPath documents(const SGPath& def = SGPath());
static std::vector<SGPath> pathsFromEnv(const char* name);
static std::vector<SGPath> pathsFromUtf8(const std::string& paths);
static std::vector<SGPath> pathsFromLocal8Bit(const std::string& paths);
static std::string join(const std::vector<SGPath>& paths, const std::string& joinWith);
private:
void fix();
@@ -328,8 +352,7 @@ template<typename char_type, typename traits_type>
inline
std::basic_ostream<char_type, traits_type>&
operator<<(std::basic_ostream<char_type, traits_type>& s, const SGPath& p)
{ return s << "Path \"" << p.str() << "\""; }
{ return s << "Path \"" << p.utf8Str() << "\""; }
/**
* Split a directory string into a list of it's parent directories.

View File

@@ -27,6 +27,8 @@
#include "sgstream.hxx"
#include <simgear/misc/sg_path.hxx>
using std::istream;
using std::ostream;
@@ -39,7 +41,7 @@ sg_gzifstream::sg_gzifstream()
//
// Open a possibly gzipped file for reading.
//
sg_gzifstream::sg_gzifstream( const std::string& name, ios_openmode io_mode )
sg_gzifstream::sg_gzifstream( const SGPath& name, ios_openmode io_mode )
: istream(&gzbuf)
{
this->open( name, io_mode );
@@ -64,26 +66,26 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
// then append ".gz" and try again.
//
void
sg_gzifstream::open( const std::string& name, ios_openmode io_mode )
sg_gzifstream::open( const SGPath& name, ios_openmode io_mode )
{
gzbuf.open( name.c_str(), io_mode );
std::string s = name.utf8Str();
gzbuf.open( s.c_str(), io_mode );
if ( ! gzbuf.is_open() )
{
std::string s = name;
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
{
// remove ".gz" suffix
s.replace( s.length() - 3, 3, "" );
// s.erase( s.length() - 3, 3 );
}
else
{
// Append ".gz" suffix
s += ".gz";
}
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
{
// remove ".gz" suffix
s.replace( s.length() - 3, 3, "" );
// s.erase( s.length() - 3, 3 );
}
else
{
// Append ".gz" suffix
s += ".gz";
}
// Try again.
gzbuf.open( s.c_str(), io_mode );
// Try again.
gzbuf.open( s.c_str(), io_mode );
}
}
@@ -159,7 +161,7 @@ sg_gzofstream::sg_gzofstream()
//
// Open a file for gzipped writing.
//
sg_gzofstream::sg_gzofstream( const std::string& name, ios_openmode io_mode )
sg_gzofstream::sg_gzofstream( const SGPath& name, ios_openmode io_mode )
: ostream(&gzbuf)
{
this->open( name, io_mode );
@@ -180,9 +182,10 @@ sg_gzofstream::sg_gzofstream( int fd, ios_openmode io_mode )
// Open a file for gzipped writing.
//
void
sg_gzofstream::open( const std::string& name, ios_openmode io_mode )
sg_gzofstream::open( const SGPath& name, ios_openmode io_mode )
{
gzbuf.open( name.c_str(), io_mode );
std::string s = name.utf8Str();
gzbuf.open( s.c_str(), io_mode );
}
void
@@ -190,3 +193,44 @@ sg_gzofstream::attach( int fd, ios_openmode io_mode )
{
gzbuf.attach( fd, io_mode );
}
sg_ifstream::sg_ifstream(const SGPath& path, ios_openmode io_mode)
{
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
void sg_ifstream::open( const SGPath& name, ios_openmode io_mode )
{
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
sg_ofstream::sg_ofstream(const SGPath& path, ios_openmode io_mode)
{
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}
void sg_ofstream::open( const SGPath& name, ios_openmode io_mode )
{
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}

View File

@@ -33,13 +33,16 @@
#include <simgear/compiler.h>
# include <istream>
# include <ostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <string>
#include <simgear/misc/zfstream.hxx>
class SGPath;
/**
* An envelope class for gzifstream.
*/
@@ -55,7 +58,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
sg_gzifstream( const std::string& name,
sg_gzifstream( const SGPath& name,
ios_openmode io_mode = ios_in | ios_binary );
/**
@@ -70,7 +73,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
void open( const std::string& name,
void open( const SGPath& name,
ios_openmode io_mode = ios_in|ios_binary );
/**
@@ -90,8 +93,8 @@ public:
private:
// Not defined!
sg_gzifstream( const sg_gzifstream& );
void operator= ( const sg_gzifstream& );
sg_gzifstream( const sg_gzifstream& );
void operator= ( const sg_gzifstream& );
};
/**
@@ -130,7 +133,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
sg_gzofstream( const std::string& name,
sg_gzofstream( const SGPath& name,
ios_openmode io_mode = ios_out | ios_binary );
/**
@@ -145,7 +148,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
void open( const std::string& name,
void open( const SGPath& name,
ios_openmode io_mode = ios_out|ios_binary );
/**
@@ -169,5 +172,25 @@ private:
void operator= ( const sg_gzofstream& );
};
#endif /* _SGSTREAM_HXX */
class sg_ifstream : public std::ifstream
{
public:
sg_ifstream() {}
sg_ifstream(const SGPath& path, ios_openmode io_mode = ios_in | ios_binary);
void open( const SGPath& name,
ios_openmode io_mode = ios_in|ios_binary );
};
class sg_ofstream : public std::ofstream
{
public:
sg_ofstream() { }
sg_ofstream(const SGPath& path, ios_openmode io_mode = ios_out | ios_binary);
void open( const SGPath& name,
ios_openmode io_mode = ios_out|ios_binary );
};
#endif /* _SGSTREAM_HXX */

View File

@@ -8,6 +8,7 @@ using std::cout;
using std::endl;
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
int main()
{
@@ -24,7 +25,8 @@ int main()
f.close();
}
sg_gzifstream sg(fileName);
SGPath p(fileName);
sg_gzifstream sg(p);
std::string stuff;
sg >> skipeol;
sg >> stuff;

View File

@@ -6,25 +6,24 @@
//#include <stdint.h>
//#include <string.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
# include <machine/endian.h>
#elif !defined(_WIN32)
# include <endian.h>
#endif
#ifdef __BIG_ENDIAN__
# define SHA_BIG_ENDIAN
#elif defined __LITTLE_ENDIAN__
/* override */
#elif defined __BYTE_ORDER
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define SHA_BIG_ENDIAN
# endif
#elif defined _WIN32
/* assume little-endian, there is no endian.h on MSVC */
#else // ! defined __LITTLE_ENDIAN__
# include <endian.h> // machine/endian.h
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define SHA_BIG_ENDIAN
#elif defined BYTE_ORDER
# if BYTE_ORDER == BIG_ENDIAN
# define SHA_BIG_ENDIAN
# endif
#endif
/* code */
#define SHA1_K0 0x5a827999
#define SHA1_K20 0x6ed9eba1

View File

@@ -34,6 +34,11 @@
#include <simgear/compiler.h> // SG_WINDOWS
#include <simgear/structure/exception.hxx>
#if defined(SG_WINDOWS)
#include <windows.h>
#endif
using std::string;
using std::vector;
using std::stringstream;
@@ -79,7 +84,7 @@ namespace simgear {
}
/**
*
*
*/
static vector<string>
split_whitespace( const string& str, int maxsplit )
@@ -125,7 +130,7 @@ namespace simgear {
}
/**
*
*
*/
vector<string>
split( const string& str, const char* sep, int maxsplit )
@@ -229,7 +234,7 @@ namespace simgear {
return do_strip( s, BOTHSTRIP );
}
string
string
rpad( const string & s, string::size_type length, char c )
{
string::size_type l = s.length();
@@ -238,7 +243,7 @@ namespace simgear {
return reply.append( length-l, c );
}
string
string
lpad( const string & s, size_t length, char c )
{
string::size_type l = s.length();
@@ -268,12 +273,12 @@ namespace simgear {
string result; // reserve size of 's'?
string::const_iterator it = s.begin(),
end = s.end();
// advance to first non-space char - simplifes logic in main loop,
// since we can always prepend a single space when we see a
// since we can always prepend a single space when we see a
// space -> non-space transition
for (; (it != end) && isspace(*it); ++it) { /* nothing */ }
bool lastWasSpace = false;
for (; it != end; ++it) {
char c = *it;
@@ -281,18 +286,18 @@ namespace simgear {
lastWasSpace = true;
continue;
}
if (lastWasSpace) {
result.push_back(' ');
}
lastWasSpace = false;
result.push_back(c);
}
return result;
}
int to_int(const std::string& s, int base)
{
stringstream ss(s);
@@ -301,12 +306,12 @@ namespace simgear {
case 16: ss >> std::hex; break;
default: break;
}
int result;
ss >> result;
return result;
}
int compare_versions(const string& v1, const string& v2)
{
vector<string> v1parts(split(v1, "."));
@@ -325,7 +330,7 @@ namespace simgear {
// reached end - longer wins
return v1parts.size() - v2parts.size();
}
string join(const string_list& l, const string& joinWith)
{
string result;
@@ -336,10 +341,10 @@ namespace simgear {
result += joinWith;
}
}
return result;
}
string uppercase(const string &s) {
string rslt(s);
for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
@@ -361,47 +366,66 @@ namespace simgear {
*p = tolower(*p);
}
}
#if defined(SG_WINDOWS)
#include <windows.h>
static WCharVec convertMultiByteToWString(DWORD encoding, const std::string& a)
#if defined(SG_WINDOWS)
static std::wstring convertMultiByteToWString(DWORD encoding, const std::string& a)
{
WCharVec result;
std::vector<wchar_t> result;
DWORD flags = 0;
int requiredWideChars = MultiByteToWideChar(encoding, flags,
int requiredWideChars = MultiByteToWideChar(encoding, flags,
a.c_str(), a.size(),
NULL, 0);
result.resize(requiredWideChars);
MultiByteToWideChar(encoding, flags, a.c_str(), a.size(),
result.data(), result.size());
return result;
return std::wstring(result.data(), result.size());
}
WCharVec convertUtf8ToWString(const std::string& a)
static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring& w)
{
return convertMultiByteToWString(CP_UTF8, a);
std::vector<char> result;
DWORD flags = 0;
int requiredMBChars = WideCharToMultiByte(encoding, flags,
w.data(), w.size(),
NULL, 0, NULL, NULL);
result.resize(requiredMBChars);
WideCharToMultiByte(encoding, flags,
w.data(), w.size(),
result.data(), result.size(), NULL, NULL);
return std::string(result.data(), result.size());
}
#endif
std::wstring convertUtf8ToWString(const std::string& a)
{
#ifdef SG_WINDOWS
return convertMultiByteToWString(CP_UTF8, a);
#else
#endif
}
std::string convertWStringToUtf8(const std::wstring& w)
{
#ifdef SG_WINDOWS
return convertWStringToMultiByte(CP_UTF8, w);
#else
#endif
}
std::string convertWindowsLocal8BitToUtf8(const std::string& a)
{
#ifdef SG_WINDOWS
DWORD flags = 0;
WCharVec wideString = convertMultiByteToWString(CP_ACP, a);
// convert down to UTF-8
std::vector<char> result;
int requiredUTF8Chars = WideCharToMultiByte(CP_UTF8, flags,
wideString.data(), wideString.size(),
NULL, 0, NULL, NULL);
result.resize(requiredUTF8Chars);
WideCharToMultiByte(CP_UTF8, flags,
wideString.data(), wideString.size(),
result.data(), result.size(), NULL, NULL);
return std::string(result.data(), result.size());
return convertWStringToMultiByte(CP_UTF8, convertMultiByteToWString(CP_ACP, a));
#else
return a;
#endif
}
std::string convertUtf8ToWindowsLocal8Bit(const std::string& a)
{
#ifdef SG_WINDOWS
return convertWStringToMultiByte(CP_ACP, convertMultiByteToWString(CP_UTF8, a));
#else
return a;
#endif
@@ -454,8 +478,8 @@ static const unsigned char base64_decode_map[128] =
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 127, 127, 127, 127, 127
};
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
@@ -471,46 +495,46 @@ void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>&
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
while (in_len-- && ( encoded_string[in_] != '=')) {
if (is_whitespace( encoded_string[in_])) {
in_++;
in_++;
continue;
}
if (!is_base64(encoded_string[in_])) {
break;
}
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_decode_map[char_array_4[i]];
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_decode_map[char_array_4[j]];
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
}
}
}
//------------------------------------------------------------------------------
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
@@ -531,7 +555,7 @@ std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
hex[i * 2] = hexChar[c >> 4];
hex[i * 2 + 1] = hexChar[c & 0x0f];
}
return hex;
}
@@ -597,7 +621,7 @@ string sanitizePrintfFormat(const string& input)
SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input);
return string();
}
return input;
}
@@ -615,7 +639,7 @@ std::string error_string(int errnum)
retcode = strerror_s(buf, sizeof(buf), errnum);
#elif defined(_GNU_SOURCE)
return std::string(strerror_r(errnum, buf, sizeof(buf)));
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC)
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__)
int retcode;
// POSIX.1-2001 and POSIX.1-2008
retcode = strerror_r(errnum, buf, sizeof(buf));
@@ -645,5 +669,5 @@ std::string error_string(int errnum)
}
} // end namespace strutils
} // end namespace simgear

View File

@@ -171,10 +171,13 @@ namespace simgear {
*/
std::string convertWindowsLocal8BitToUtf8(const std::string& a);
#if defined(SG_WINDOWS)
typedef std::vector<wchar_t> WCharVec;
WCharVec convertUtf8ToWString(const std::string& a);
#endif
/**
*
*/
std::string convertUtf8ToWindowsLocal8Bit(const std::string& a);
std::wstring convertUtf8ToWString(const std::string& a);
std::string convertWStringToUtf8(const std::wstring& w);
/**
* Get md5 hash of raw data.

View File

@@ -22,10 +22,14 @@
// $Id$
#include <simgear/compiler.h>
#include <simgear_config.h>
#include <cerrno>
#include <memory.h>
#include <stdio.h>
#include <fcntl.h>
#include <simgear/misc/strutils.hxx>
#include "zfstream.hxx"
@@ -110,7 +114,14 @@ gzfilebuf::open( const char *name, ios_openmode io_mode )
char char_mode[10];
cvt_iomode( char_mode, io_mode );
#if defined(SG_WINDOWS)
std::wstring ws = simgear::strutils::convertUtf8ToWString(std::string(name));
if ( (file = gzopen_w(ws.c_str(), char_mode)) == NULL ) {
#else
if ( (file = gzopen(name, char_mode)) == NULL ) {
#endif
// perror( "gzfilebuf::open(): " );
errno = 0;
return NULL;

View File

@@ -66,7 +66,7 @@ public:
/**
* Open a stream
* @param name file name
* @param name file name, UTF-8 encoded
* @param io_mode mode flags
* @return file stream
*/

View File

@@ -66,7 +66,7 @@ namespace nasal
//----------------------------------------------------------------------------
naRef to_nasal_helper(naContext c, const SGPath& path)
{
return to_nasal_helper(c, path.str());
return to_nasal_helper(c, path.utf8Str());
}
//----------------------------------------------------------------------------

View File

@@ -1,7 +1,7 @@
include (SimGearComponent)
set(HEADERS
set(HEADERS
Catalog.hxx
Package.hxx
Install.hxx
@@ -9,7 +9,7 @@ set(HEADERS
Delegate.hxx
)
set(SOURCES
set(SOURCES
Catalog.cxx
Package.cxx
Install.cxx
@@ -34,7 +34,7 @@ target_link_libraries(catalog_test ${TEST_LIBS})
set_target_properties(catalog_test PROPERTIES
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
add_test(catalog_test ${EXECUTABLE_OUTPUT_PATH}/catalog_test)
endif(ENABLE_TESTS)
endif(ENABLE_TESTS)

View File

@@ -27,6 +27,7 @@
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/package/Package.hxx>
#include <simgear/package/Root.hxx>
@@ -37,34 +38,43 @@ namespace simgear {
namespace pkg {
bool checkVersionString(const std::string& aVersion, const std::string& aCandidate)
{
if (aCandidate == aVersion) {
return true;
}
// examine each dot-seperated component in turn, supporting a wildcard
// in the versions from the catalog.
string_list appVersionParts = simgear::strutils::split(aVersion, ".");
string_list catVersionParts = simgear::strutils::split(aCandidate, ".");
size_t partCount = appVersionParts.size();
bool previousCandidatePartWasWildcard = false;
for (unsigned int p=0; p < partCount; ++p) {
// candidate string is too short, can match if it ended with wildcard
// part. This allows candidate '2016.*' to match '2016.1.2' and so on
if (catVersionParts.size() <= p) {
return previousCandidatePartWasWildcard;
}
if (catVersionParts[p] == "*") {
// always passes
previousCandidatePartWasWildcard = true;
} else if (appVersionParts[p] != catVersionParts[p]) {
return false;
}
}
return true;
}
bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
{
BOOST_FOREACH(SGPropertyNode* v, props->getChildren("version")) {
std::string s(v->getStringValue());
if (s == aVersion) {
return true;
}
// examine each dot-seperated component in turn, supporting a wildcard
// in the versions from the catalog.
string_list appVersionParts = simgear::strutils::split(aVersion, ".");
string_list catVersionParts = simgear::strutils::split(s, ".");
size_t partCount = appVersionParts.size();
if (partCount != catVersionParts.size()) {
continue;
}
bool ok = true;
for (unsigned int p=0; p < partCount; ++p) {
if (catVersionParts[p] == "*") {
// always passes
} else if (appVersionParts[p] != catVersionParts[p]) {
ok = false;
}
}
if (ok) {
if (checkVersionString(aVersion, s)) {
return true;
}
}
@@ -74,8 +84,9 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
{
BOOST_FOREACH(SGPropertyNode* v, props->getChildren("alternate-version")) {
if (v->getStringValue("version") == aVersion) {
return v->getStringValue("url");
std::string s(v->getStringValue("version"));
if (checkVersionString(aVersion, s)) {
return v->getStringValue("url");;
}
}
@@ -114,7 +125,7 @@ protected:
return;
}
SGPropertyNode* props = new SGPropertyNode;
SGPropertyNode_ptr props = new SGPropertyNode;
try {
readProperties(m_buffer.data(), m_buffer.size(), props);
@@ -125,18 +136,9 @@ protected:
return;
}
if (m_owner->root()->catalogVersion() != props->getIntValue("catalog-version")) {
SG_LOG(SG_GENERAL, SG_WARN, "catalog:" << m_owner->url() << " is not version "
<< m_owner->root()->catalogVersion());
m_owner->refreshComplete(Delegate::FAIL_VERSION);
return;
}
std::string ver(m_owner->root()->applicationVersion());
if (!checkVersion(ver, props)) {
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t"
<< props->getStringValue("version") << " vs required " << ver);
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version required " << ver);
// check for a version redirect entry
std::string url = redirectUrlForVersion(ver, props);
@@ -145,7 +147,7 @@ protected:
" to \n\t" << url);
// update the URL and kick off a new request
m_owner->m_url = url;
m_owner->setUrl(url);
Downloader* dl = new Downloader(m_owner, url);
m_owner->root()->makeHTTPRequest(dl);
} else {
@@ -158,8 +160,7 @@ protected:
// cache the catalog data, now we have a valid install root
Dir d(m_owner->installRoot());
SGPath p = d.file("catalog.xml");
std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
sg_ofstream f(p, std::ios::out | std::ios::trunc);
f.write(m_buffer.data(), m_buffer.size());
f.close();
@@ -206,13 +207,12 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
SGPropertyNode_ptr props;
try {
props = new SGPropertyNode;
readProperties(xml.str(), props);
readProperties(xml, props);
} catch (sg_exception& ) {
return NULL;
}
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
if (!versionCheckOk) {
SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need" << aRoot->applicationVersion());
// keep the catalog but mark it as needing an update
@@ -438,6 +438,14 @@ std::string Catalog::url() const
return m_url;
}
void Catalog::setUrl(const std::string &url)
{
m_url = url;
if (m_status == Delegate::FAIL_NOT_FOUND) {
m_status = Delegate::FAIL_UNKNOWN;
}
}
std::string Catalog::name() const
{
return getLocalisedString(m_props, "name");
@@ -457,7 +465,7 @@ void Catalog::parseTimestamp()
{
SGPath timestampFile = m_installRoot;
timestampFile.append(".timestamp");
std::ifstream f(timestampFile.c_str(), std::ios::in);
sg_ifstream f(timestampFile, std::ios::in);
f >> m_retrievedTime;
}
@@ -465,7 +473,7 @@ void Catalog::writeTimestamp()
{
SGPath timestampFile = m_installRoot;
timestampFile.append(".timestamp");
std::ofstream f(timestampFile.c_str(), std::ios::out | std::ios::trunc);
sg_ofstream f(timestampFile, std::ios::out | std::ios::trunc);
f << m_retrievedTime << std::endl;
}
@@ -506,8 +514,8 @@ std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char*
void Catalog::refreshComplete(Delegate::StatusCode aReason)
{
changeStatus(aReason);
m_refreshRequest.reset();
changeStatus(aReason);
}
void Catalog::changeStatus(Delegate::StatusCode newStatus)

View File

@@ -106,6 +106,12 @@ public:
std::string url() const;
/**
* update the URL of a package. Does not trigger a refresh, but resets
* error state if the previous URL was not found.
*/
void setUrl(const std::string& url);
std::string name() const;
std::string description() const;

View File

@@ -46,7 +46,7 @@ std::string readFileIntoString(const SGPath& path)
std::string contents;
size_t readLen;
SGBinaryFile f(path.str());
SGBinaryFile f(path);
if (!f.open(SG_IO_IN)) {
throw sg_io_exception("Couldn't open file", path);
}
@@ -133,7 +133,7 @@ int parseTest()
COMPARE(cat->description(), "First test catalog");
// check the packages too
COMPARE(cat->packages().size(), 3);
COMPARE(cat->packages().size(), 4);
pkg::PackageRef p1 = cat->packages().front();
COMPARE(p1->catalog(), cat.ptr());
@@ -208,7 +208,7 @@ void testAddCatalog(HTTP::Client* cl)
p.append("org.flightgear.test.catalog1");
p.append("catalog.xml");
VERIFY(p.exists());
COMPARE(root->allPackages().size(), 3);
COMPARE(root->allPackages().size(), 4);
COMPARE(root->catalogs().size(), 1);
pkg::PackageRef p1 = root->getPackageById("alpha");
@@ -403,6 +403,42 @@ void testRefreshCatalog(HTTP::Client* cl)
COMPARE(root->getPackageById("common-sounds")->revision(), 11);
}
void testInstallTarPackage(HTTP::Client* cl)
{
SGPath rootPath(simgear::Dir::current().path());
rootPath.append("pkg_install_tar");
simgear::Dir pd(rootPath);
pd.removeChildren();
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
// specify a test dir
root->setHTTPClient(cl);
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
waitForUpdateComplete(cl, root);
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
COMPARE(p1->id(), "b737-NG");
pkg::InstallRef ins = p1->install();
VERIFY(ins->isQueued());
waitForUpdateComplete(cl, root);
VERIFY(p1->isInstalled());
VERIFY(p1->existingInstall() == ins);
// verify on disk state
SGPath p(rootPath);
p.append("org.flightgear.test.catalog1");
p.append("Aircraft");
p.append("b737NG");
COMPARE(p, ins->path());
p.append("b737-900-set.xml");
VERIFY(p.exists());
}
int main(int argc, char* argv[])
{
@@ -425,6 +461,8 @@ int main(int argc, char* argv[])
testRefreshCatalog(&cl);
testInstallTarPackage(&cl);
std::cout << "Successfully passed all tests!" << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -23,6 +23,7 @@
#include <simgear/package/unzip.h>
#include <simgear/package/md5.h>
#include <simgear/io/untar.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/package/Catalog.hxx>
@@ -32,6 +33,7 @@
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sgstream.hxx>
extern "C" {
void fill_memory_filefunc (zlib_filefunc_def*);
@@ -144,8 +146,8 @@ protected:
return;
}
if (!extractUnzip()) {
SG_LOG(SG_GENERAL, SG_WARN, "zip extraction failed");
if (!extract()) {
SG_LOG(SG_GENERAL, SG_WARN, "archive extraction failed");
doFailure(Delegate::FAIL_EXTRACT);
return;
}
@@ -167,7 +169,9 @@ protected:
}
// extract_xxxx directory is now empty, so remove it
m_extractPath.remove();
if (m_extractPath.exists()) {
simgear::Dir(m_extractPath).remove();
}
m_owner->m_revision = m_owner->package()->revision();
m_owner->writeRevisionFile();
@@ -214,7 +218,7 @@ private:
throw sg_io_exception("opening current zip file failed", sg_location(name));
}
std::ofstream outFile;
sg_ofstream outFile;
bool eof = false;
SGPath path(m_extractPath);
path.append(name);
@@ -224,13 +228,13 @@ private:
if (!parentDir.exists()) {
bool ok = parentDir.create(0755);
if (!ok) {
throw sg_io_exception("failed to create directory heirarchy for extraction", path.c_str());
throw sg_io_exception("failed to create directory heirarchy for extraction", path);
}
}
outFile.open(path.c_str(), std::ios::binary | std::ios::trunc | std::ios::out);
outFile.open(path, std::ios::binary | std::ios::trunc | std::ios::out);
if (outFile.fail()) {
throw sg_io_exception("failed to open output file for writing", path.c_str());
throw sg_io_exception("failed to open output file for writing", path);
}
while (!eof) {
@@ -248,6 +252,22 @@ private:
unzCloseCurrentFile(zip);
}
bool extract()
{
const std::string u(url());
const size_t ul(u.length());
if (u.rfind(".zip") == (ul - 4)) {
return extractUnzip();
}
if (u.rfind(".tar.gz") == (ul - 7)) {
return extractTar();
}
SG_LOG(SG_IO, SG_WARN, "unsupported archive format:" << u);
return false;
}
bool extractUnzip()
{
bool result = true;
@@ -285,6 +305,13 @@ private:
return result;
}
bool extractTar()
{
TarExtractor tx(m_extractPath);
tx.extractBytes(m_buffer.data(), m_buffer.size());
return !tx.hasError() && tx.isAtEndOfArchive();
}
void doFailure(Delegate::StatusCode aReason)
{
Dir dir(m_extractPath);
@@ -338,7 +365,7 @@ void Install::parseRevision()
return;
}
std::ifstream f(revisionFile.c_str(), std::ios::in);
sg_ifstream f(revisionFile, std::ios::in);
f >> m_revision;
}
@@ -346,7 +373,7 @@ void Install::writeRevisionFile()
{
SGPath revisionFile = m_path;
revisionFile.append(".revision");
std::ofstream f(revisionFile.c_str(), std::ios::out | std::ios::trunc);
sg_ofstream f(revisionFile, std::ios::out | std::ios::trunc);
f << m_revision << std::endl;
}

View File

@@ -384,7 +384,7 @@ void Root::refresh(bool aForce)
CatalogList toRefresh;
CatalogDict::iterator it = d->catalogs.begin();
for (; it != d->catalogs.end(); ++it) {
int age = it->second->ageInSeconds();
unsigned int age = it->second->ageInSeconds();
if (aForce || (age > maxAgeSeconds())) {
toRefresh.push_back(it->second);
}

Binary file not shown.

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<PropertyList>
<sim>
<description>Boeing 737-700</description>
</sim>
</PropertyList>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<PropertyList>
<sim>
<description>Boeing 737-800</description>
</sim>
</PropertyList>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<PropertyList>
<sim>
<description>Boeing 737-900</description>
</sim>
</PropertyList>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<PropertyList>
<sim>
<tags>
<tag>boeing</tag>
</tags>
</sim>
</PropertyList>

View File

@@ -78,6 +78,30 @@
<md5>acf9eb89cf396eb42f8823d9cdf17584</md5>
</package>
<package>
<id>b737-NG</id>
<name>Boeing 737 NG</name>
<dir>b737NG</dir>
<description>A popular twin-engined narrow body jet</description>
<revision type="int">112</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
</package>
<package>
<id>dc3</id>
<name>DC-3</name>

View File

@@ -68,6 +68,29 @@
</package>
<package>
<id>b737-NG</id>
<name>Boeing 737 NG</name>
<dir>b737NG</dir>
<description>A popular twin-engined narrow body jet</description>
<revision type="int">111</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
</package>
<package>
<id>common-sounds</id>
<name>Common sound files for test catalog aircraft</name>

View File

@@ -70,6 +70,12 @@ namespace simgear
break;
}
}
// needed to avoid incrementing an invalid iterator when we
// erase the last interpolator
if (it == _interpolators.end()) {
break;
}
}
}

View File

@@ -191,7 +191,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
throw sg_io_exception(message, location,
"SimGear Property Reader");
}
readProperties(path.str(), _root, 0, _extended);
readProperties(path, _root, 0, _extended);
} catch (sg_io_exception &e) {
setException(e);
}
@@ -278,7 +278,7 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
message += val;
throw sg_io_exception(message, location, "SimGear Property Reader");
}
readProperties(path.str(), node, 0, _extended);
readProperties(path, node, 0, _extended);
}
catch (sg_io_exception &e)
{
@@ -434,10 +434,10 @@ readProperties (istream &input, SGPropertyNode * start_node,
* @return true if the read succeeded, false otherwise.
*/
void
readProperties (const string &file, SGPropertyNode * start_node,
readProperties (const SGPath &file, SGPropertyNode * start_node,
int default_mode, bool extended)
{
PropsVisitor visitor(start_node, file, default_mode, extended);
PropsVisitor visitor(start_node, file.utf8Str(), default_mode, extended);
readXML(file, visitor);
if (visitor.hasException())
throw visitor.getException();
@@ -690,17 +690,17 @@ writeProperties (ostream &output, const SGPropertyNode * start_node,
void
writeProperties (const string &file, const SGPropertyNode * start_node,
writeProperties (const SGPath &path, const SGPropertyNode * start_node,
bool write_all, SGPropertyNode::Attribute archive_flag)
{
SGPath path(file.c_str());
path.create_dir(0755);
SGPath dpath(path);
dpath.create_dir(0755);
ofstream output(file.c_str());
ofstream output(path.local8BitStr().c_str());
if (output.good()) {
writeProperties(output, start_node, write_all, archive_flag);
} else {
throw sg_io_exception("Cannot open file", sg_location(file));
throw sg_io_exception("Cannot open file", sg_location(path.utf8Str()));
}
}

View File

@@ -29,7 +29,7 @@ void readProperties (std::istream &input, SGPropertyNode * start_node,
/**
* Read properties from an XML file.
*/
void readProperties (const std::string &file, SGPropertyNode * start_node,
void readProperties (const SGPath &file, SGPropertyNode * start_node,
int default_mode = 0, bool extended = false);
@@ -52,7 +52,7 @@ void writeProperties (std::ostream &output, const SGPropertyNode * start_node,
/**
* Write properties to an XML file.
*/
void writeProperties (const std::string &file,
void writeProperties (const SGPath &file,
const SGPropertyNode * start_node,
bool write_all = false,
SGPropertyNode::Attribute archive_flag = SGPropertyNode::ARCHIVE);

View File

@@ -14,6 +14,8 @@
#include "props.hxx"
#include "props_io.hxx"
#include <simgear/misc/sg_path.hxx>
using std::cout;
using std::cerr;
using std::endl;
@@ -394,7 +396,7 @@ int main (int ac, char ** av)
try {
cout << "Reading " << av[i] << endl;
SGPropertyNode root;
readProperties(av[i], &root);
readProperties(SGPath::fromLocal8Bit(av[i]), &root);
writeProperties(cout, &root, true);
cout << endl;
} catch (std::string &message) {

View File

@@ -149,7 +149,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
SGPath tpath("Textures");
tpath.append(tname);
std::string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
std::string fullTexPath = SGModelLib::findDataFile(tpath.local8BitStr(), options);
if (fullTexPath.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
<< tname << "\" in Textures folders.");
@@ -181,7 +181,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
SGPath tpath("Textures");
tpath.append(tname);
std::string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
std::string fullTexPath = SGModelLib::findDataFile(tpath.local8BitStr(), options);
if (fullTexPath.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
<< tname << "\" in Textures folders.");
@@ -207,7 +207,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
SGPath tpath("Textures");
tpath.append("Terrain");
tpath.append("unknown.rgb");
_internal_state st( NULL, tpath.str(), true, options );
_internal_state st( NULL, tpath.local8BitStr(), true, options );
_status.push_back( st );
}
@@ -219,7 +219,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
if (! omname.empty()) {
SGPath ompath("Textures");
ompath.append(omname);
std::string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
std::string fullMaskPath = SGModelLib::findDataFile(ompath.local8BitStr(), options);
if (fullMaskPath.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
@@ -340,7 +340,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
if (! treeTexPath.empty()) {
SGPath treePath("Textures");
treePath.append(treeTexPath);
tree_texture = SGModelLib::findDataFile(treePath.str(), options);
tree_texture = SGModelLib::findDataFile(treePath.local8BitStr(), options);
if (tree_texture.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""

View File

@@ -93,7 +93,7 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
// Read name node purely for logging purposes
const SGPropertyNode *nameNode = node->getChild("name");
if (nameNode) {
SG_LOG( SG_TERRAIN, SG_INFO, "Loading region "
SG_LOG( SG_TERRAIN, SG_DEBUG, "Loading region "
<< nameNode->getStringValue());
}
@@ -113,7 +113,7 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
fabs(x2 - x1),
fabs(y2 - y1));
arealist->push_back(rect);
SG_LOG( SG_TERRAIN, SG_INFO, " Area ("
SG_LOG( SG_TERRAIN, SG_DEBUG, " Area ("
<< rect.x() << ","
<< rect.y() << ") width:"
<< rect.width() << " height:"

View File

@@ -107,7 +107,11 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea)
if (_buttons.find(button) == _buttons.end()) {
return false;
}
if (!anyBindingEnabled(_bindingsDown)) {
return false;
}
fireBindingList(_bindingsDown);
_repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
return true;
@@ -136,7 +140,7 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea)
virtual bool hover( const osg::Vec2d& windowPos,
const Info& )
{
if (_hover.empty()) {
if (!anyBindingEnabled(_hover)) {
return false;
}

View File

@@ -260,7 +260,7 @@ sgLoad3DModel_internal(const SGPath& path,
SGPropertyNode *overlay)
{
if (!path.exists()) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path.str() << "\"");
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path << "\"");
return NULL;
}
@@ -286,7 +286,7 @@ sgLoad3DModel_internal(const SGPath& path,
// Check for an XML wrapper
if (modelpath.extension() == "xml") {
try {
readProperties(modelpath.str(), props);
readProperties(modelpath, props);
} catch (const sg_exception &t) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: "
<< t.getFormattedMessage());
@@ -304,7 +304,7 @@ sgLoad3DModel_internal(const SGPath& path,
modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir);
if (modelpath.isNull())
throw sg_io_exception("Model file not found: '" + modelPathStr + "'",
path.str());
path);
if (props->hasValue("/texture-path")) {
string texturePathStr = props->getStringValue("/texture-path");
@@ -313,7 +313,7 @@ sgLoad3DModel_internal(const SGPath& path,
texturepath = SGModelLib::findDataFile(texturePathStr, NULL, modelDir);
if (texturepath.isNull())
throw sg_io_exception("Texture file not found: '" + texturePathStr + "'",
path.str());
path);
}
}
} else {
@@ -333,12 +333,12 @@ sgLoad3DModel_internal(const SGPath& path,
if (!texturepath.extension().empty())
texturepath = texturepath.dir();
options->setDatabasePath(texturepath.str());
options->setDatabasePath(texturepath.local8BitStr());
osgDB::ReaderWriter::ReadResult modelResult;
modelResult = osgDB::readNodeFile(modelpath.str(), options.get());
modelResult = osgDB::readNodeFile(modelpath.local8BitStr(), options.get());
if (!modelResult.validNode())
throw sg_io_exception("Failed to load 3D model:" + modelResult.message(),
modelpath.str());
modelpath);
model = copyModel(modelResult.getNode());
// Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if
@@ -362,7 +362,7 @@ sgLoad3DModel_internal(const SGPath& path,
SetNodeMaskVisitor setNodeMaskVisitor(0, simgear::MODELLIGHT_BIT);
model->accept(setNodeMaskVisitor);
}
model->setName(modelpath.str());
model->setName(modelpath.utf8Str());
bool needTransform=false;
// Set up the alignment node if needed
@@ -413,7 +413,7 @@ sgLoad3DModel_internal(const SGPath& path,
bool isInterior = (std::string(sub_props->getStringValue("usage")) == "interior");
bool isAI = (std::string(prop_root->getStringValue("type")) == "AI");
if(isInterior && isAI){
props->addChild("interior-path")->setStringValue(submodelPath.str());
props->addChild("interior-path")->setStringValue(submodelPath.utf8Str());
continue;
}
}
@@ -485,14 +485,14 @@ sgLoad3DModel_internal(const SGPath& path,
std::vector<SGPropertyNode_ptr> particle_nodes;
particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
SG_LOG(SG_PARTICLES, SG_DEBUG, "Reading in particle " << i << " from file: " << path.str());
SG_LOG(SG_PARTICLES, SG_DEBUG, "Reading in particle " << i << " from file: " << path);
osg::ref_ptr<SGReaderWriterOptions> options2;
options2 = new SGReaderWriterOptions(*options);
if (i==0) {
if (!texturepath.extension().empty())
texturepath = texturepath.dir();
options2->setDatabasePath(texturepath.str());
options2->setDatabasePath(texturepath.local8BitStr());
}
group->addChild(Particles::appendParticles(particle_nodes[i],
prop_root,
@@ -536,18 +536,18 @@ sgLoad3DModel_internal(const SGPath& path,
/// OSGFIXME: duh, why not only model?????
SGAnimation::animate(group.get(), animation_nodes[i], prop_root,
options.get(), path.str(), i);
options.get(), path.local8BitStr(), i);
}
if (!needTransform && group->getNumChildren() < 2) {
model = group->getChild(0);
group->removeChild(model.get());
if (data.valid())
data->modelLoaded(modelpath.str(), props, model.get());
data->modelLoaded(modelpath.utf8Str(), props, model.get());
return model.release();
}
if (data.valid())
data->modelLoaded(modelpath.str(), props, group.get());
data->modelLoaded(modelpath.utf8Str(), props, group.get());
if (props->hasChild("debug-outfile")) {
std::string outputfile = props->getStringValue("debug-outfile",
"debug-model.osg");

View File

@@ -97,7 +97,7 @@ osg::Node * SGText::appendText(const SGPropertyNode* configNode,
SGPath path("Fonts" );
path.append( configNode->getStringValue( "font", "Helvetica" ));
text->setFont( path.str() );
text->setFont( path.local8BitStr() );
text->setCharacterSize(configNode->getDoubleValue("character-size", 1.0 ),
configNode->getDoubleValue("character-aspect-ratio", 1.0 ));

View File

@@ -47,7 +47,7 @@ SGLoadTexture2D(const SGPath& path,
bool wrapu = true, bool wrapv = true,
int mipmaplevels = -1)
{
return SGLoadTexture2D(true, path.str(), options, wrapu, wrapv,
return SGLoadTexture2D(true, path.local8BitStr(), options, wrapu, wrapv,
mipmaplevels);
}
@@ -57,7 +57,7 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
bool wrapu = true, bool wrapv = true,
int mipmaplevels = -1)
{
return SGLoadTexture2D(staticTexture, path.str(), options, wrapu, wrapv,
return SGLoadTexture2D(staticTexture, path.local8BitStr(), options, wrapu, wrapv,
mipmaplevels);
}

View File

@@ -76,7 +76,7 @@ std::string SGModelLib::findDataFile(const std::string& file,
return file;
SGPath p = ResourceManager::instance()->findPath(file, currentPath);
if (p.exists()) {
return p.str();
return p.local8BitStr();
}
// finally hand on to standard OSG behaviour

View File

@@ -78,7 +78,7 @@ SGMakeState(const SGPath &path, const char* colorTexture,
osg::StateSet *stateSet = new osg::StateSet;
osg::ref_ptr<SGReaderWriterOptions> options;
options = SGReaderWriterOptions::fromPath(path.str());
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
stateSet->setTextureAttribute(0, SGLoadTexture2D(colorTexture,
options.get()));
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);

View File

@@ -76,7 +76,7 @@ SGMoon::build( SGPath path, double moon_size ) {
// set up the orb state
osg::ref_ptr<SGReaderWriterOptions> options;
options = SGReaderWriterOptions::fromPath(path.str());
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
osg::Texture2D* texture = SGLoadTexture2D("moon.png", options.get());
stateSet->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);

View File

@@ -118,7 +118,7 @@ SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def
"image"),
texture);
ref_ptr<SGReaderWriterOptions> options;
options = SGReaderWriterOptions::fromPath(texture_root.str());
options = SGReaderWriterOptions::fromPath(texture_root.local8BitStr());
effect = makeEffect(pcloudEffect, true, options.get());
if (effect.valid())
{

View File

@@ -70,7 +70,7 @@ SGSun::build( SGPath path, double sun_size, SGPropertyNode *property_tree_Node )
env_node = property_tree_Node;
osg::ref_ptr<SGReaderWriterOptions> options;
options = SGReaderWriterOptions::fromPath(path.str());
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
// build the ssg scene graph sub tree for the sky and connected
// into the provide scene graph branch
sun_transform = new osg::MatrixTransform;

View File

@@ -221,7 +221,7 @@ struct ReaderWriterSTG::_ModelBin {
SGPath path = filePath;
path.append(".."); path.append(".."); path.append("..");
sharedOptions->getDatabasePathList().push_back(path.str());
sharedOptions->getDatabasePathList().push_back(path.local8BitStr());
// ensure Models directory synced via TerraSync is searched before the copy in
// FG_ROOT, so that updated models can be used.
@@ -346,7 +346,7 @@ struct ReaderWriterSTG::_ModelBin {
_Object obj;
obj._errorLocation = absoluteFileName;
obj._token = token;
obj._name = path.str();
obj._name = path.local8BitStr();
obj._options = staticOptions(filePath, options);
_objectList.push_back(obj);
}
@@ -356,7 +356,7 @@ struct ReaderWriterSTG::_ModelBin {
_Object obj;
obj._errorLocation = absoluteFileName;
obj._token = token;
obj._name = path.str();
obj._name = path.local8BitStr();
obj._options = staticOptions(filePath, options);
_objectList.push_back(obj);
}
@@ -555,13 +555,13 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
objects.append("Objects");
objects.append(basePath);
objects.append(fileName);
modelBin.read(objects.str(), options);
modelBin.read(objects.local8BitStr(), options);
SGPath terrain(*i);
terrain.append("Terrain");
terrain.append(basePath);
terrain.append(fileName);
modelBin.read(terrain.str(), options);
modelBin.read(terrain.local8BitStr(), options);
}
}

View File

@@ -173,7 +173,7 @@ Geometry* createTreeGeometry(float width, float height, int varieties)
{
quadGeom->setSecondaryColorArray(new Vec3Array);
quadGeom->setSecondaryColorBinding(Geometry::BIND_PER_VERTEX);
}
}
FloatArray* rotation = new FloatArray(3);
(*rotation)[0] = 0.0;
(*rotation)[1] = PI_2;
@@ -205,8 +205,8 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
Geometry* geom
= static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
Vec3Array* posArray = static_cast<Vec3Array*>(geom->getColorArray());
Vec3Array* tnormalArray;
if (use_tree_shadows || use_tree_normals)
Vec3Array* tnormalArray = NULL;
if (use_tree_shadows || use_tree_normals)
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
if (posArray->size()
>= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
@@ -215,12 +215,12 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
Vec3 params = (*paramsArray)[0];
geom = createTreeGeometry(params.x(), params.y(), params.z());
posArray = static_cast<Vec3Array*>(geom->getColorArray());
if (use_tree_shadows || use_tree_normals)
if (use_tree_shadows || use_tree_normals)
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
geode->addDrawable(geom);
}
posArray->insert(posArray->end(), 4, pos);
if (use_tree_shadows || use_tree_normals)
if (use_tree_shadows || use_tree_normals)
{tnormalArray->insert(tnormalArray->end(),4,ter);}
size_t numVerts = posArray->size();
int imax = 2;
@@ -241,14 +241,14 @@ namespace
{
struct MakeTreesLeaf
{
MakeTreesLeaf(float range, int varieties, float width, float height,
MakeTreesLeaf(float range, int varieties, float width, float height,
Effect* effect) :
_range(range), _varieties(varieties),
_width(width), _height(height), _effect(effect) {}
MakeTreesLeaf(const MakeTreesLeaf& rhs) :
_range(rhs._range),
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
_effect(rhs._effect)
{}
@@ -356,7 +356,7 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
MatrixTransform* mt = new MatrixTransform(transform);
SGTreeBinList::iterator i;
use_tree_shadows = false;
use_tree_normals = false;
if (options) {
@@ -368,7 +368,7 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
use_tree_normals
= propertyNode->getBoolValue("/sim/rendering/random-vegetation-normals",
use_tree_normals);
}
}
}
for (i = forestList.begin(); i != forestList.end(); ++i) {

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More