Compare commits
210 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
277dab0d55 | ||
|
|
321a3fdaba | ||
|
|
2eb17d0083 | ||
|
|
ff7e4597e7 | ||
|
|
4a4baf1b42 | ||
|
|
2672d5cd11 | ||
|
|
1bf3d7c9b1 | ||
|
|
f7c0a7f933 | ||
|
|
6bf864babb | ||
|
|
43bd1b15ee | ||
|
|
14c79d9ffb | ||
|
|
3009aadaa6 | ||
|
|
fca64343ae | ||
|
|
042a2659f6 | ||
|
|
fe54af405c | ||
|
|
ab70090a0a | ||
|
|
ee02750e95 | ||
|
|
f55007394e | ||
|
|
60a9e8fb7e | ||
|
|
7f8455f731 | ||
|
|
6ae86fc4ca | ||
|
|
e1fb13bed8 | ||
|
|
27fff3b72a | ||
|
|
6a9235223e | ||
|
|
c1e50e9a9c | ||
|
|
dcbf5b7c11 | ||
|
|
4b571f2a24 | ||
|
|
679b7b845c | ||
|
|
50d7127c51 | ||
|
|
0e09ee4bce | ||
|
|
76ebd569d5 | ||
|
|
edcd42bc2d | ||
|
|
f04e501472 | ||
|
|
32d152ba38 | ||
|
|
94c4c44d92 | ||
|
|
ab1d4e651e | ||
|
|
5cd250e452 | ||
|
|
2dcff4bb8e | ||
|
|
604a9ff614 | ||
|
|
a09630bcca | ||
|
|
b44c70b3f4 | ||
|
|
e4cddb100e | ||
|
|
e3a4144e6c | ||
|
|
51e7d95bf2 | ||
|
|
202571386b | ||
|
|
7837bd0e11 | ||
|
|
11c6e5bf04 | ||
|
|
8277857827 | ||
|
|
412111ba5a | ||
|
|
dd52b6af50 | ||
|
|
a97c145f56 | ||
|
|
b9deebb59d | ||
|
|
906813c90b | ||
|
|
1711592e64 | ||
|
|
7b2507cb19 | ||
|
|
2e19aaaff9 | ||
|
|
6854598b79 | ||
|
|
809ddb21c9 | ||
|
|
38bab59c1a | ||
|
|
87590cafb2 | ||
|
|
6e5cbd7fc5 | ||
|
|
4200572cad | ||
|
|
9b997ea1f7 | ||
|
|
c1ba974538 | ||
|
|
d7d59b08a2 | ||
|
|
6b82b78c7c | ||
|
|
8201301064 | ||
|
|
899778b354 | ||
|
|
a31d1342d5 | ||
|
|
8d266491c5 | ||
|
|
0acbe1f087 | ||
|
|
f8d5e58ccc | ||
|
|
fcd0f15ff2 | ||
|
|
f092f000fa | ||
|
|
69b127e2e6 | ||
|
|
65a3d9ed6c | ||
|
|
85c4e03823 | ||
|
|
7754f88be7 | ||
|
|
1e24245d6c | ||
|
|
5ea01039f9 | ||
|
|
8b8dbeb00d | ||
|
|
52ec6cee85 | ||
|
|
eb53d4ca78 | ||
|
|
f3c3b7ec1b | ||
|
|
f6e92ac9e5 | ||
|
|
48c5e5e43b | ||
|
|
7cc9a1753c | ||
|
|
068745617c | ||
|
|
935c3f901d | ||
|
|
0b60669075 | ||
|
|
488039d1de | ||
|
|
efe9648afa | ||
|
|
d12cd4945e | ||
|
|
968e0b4cd2 | ||
|
|
d902fffa46 | ||
|
|
3092274cac | ||
|
|
5f54388ed9 | ||
|
|
9b4f1b0ff8 | ||
|
|
c48a28beb9 | ||
|
|
96986c9377 | ||
|
|
33bd02f926 | ||
|
|
93226fc500 | ||
|
|
31ba9dfa70 | ||
|
|
19df18fefb | ||
|
|
a5a4bf6d41 | ||
|
|
9812315d96 | ||
|
|
c40044feeb | ||
|
|
a636da6959 | ||
|
|
ca84d2046a | ||
|
|
c037a0e461 | ||
|
|
5ab595b401 | ||
|
|
7e06e5382a | ||
|
|
d3c5c45262 | ||
|
|
efa1292b2d | ||
|
|
98de216878 | ||
|
|
82a9491de4 | ||
|
|
b23e9a3424 | ||
|
|
0e62c11fd0 | ||
|
|
5b54481555 | ||
|
|
f4344c5c6a | ||
|
|
372dead21a | ||
|
|
cf18d4eaaf | ||
|
|
32735428bb | ||
|
|
b862cf7e54 | ||
|
|
f21eac8473 | ||
|
|
38c8931950 | ||
|
|
cfe1c0933f | ||
|
|
a8d8158fac | ||
|
|
2321d9783d | ||
|
|
a3b3280123 | ||
|
|
8cfe5a2e08 | ||
|
|
bd896096cc | ||
|
|
855ff5a8b0 | ||
|
|
e695505e62 | ||
|
|
f824cf85a4 | ||
|
|
fb8b60b6fe | ||
|
|
516d76d41b | ||
|
|
d0e31c5cf5 | ||
|
|
daa10503e6 | ||
|
|
1c25d343a0 | ||
|
|
c762dbe864 | ||
|
|
37c551bae7 | ||
|
|
0ccf3e1629 | ||
|
|
da1aeece14 | ||
|
|
c722f90848 | ||
|
|
5ba9004853 | ||
|
|
dc1696dfd5 | ||
|
|
cb80af0ebe | ||
|
|
1492de4391 | ||
|
|
fad905e7e0 | ||
|
|
fb1b1b9c5e | ||
|
|
b7b304ecfb | ||
|
|
8690e4617f | ||
|
|
a6bed69d19 | ||
|
|
100e327684 | ||
|
|
729c9e3faa | ||
|
|
add3a934af | ||
|
|
e82e4f8c93 | ||
|
|
6582d041e6 | ||
|
|
92878f37f9 | ||
|
|
4224d2b86b | ||
|
|
c3db9b9d86 | ||
|
|
3223f16fe6 | ||
|
|
03f7f82856 | ||
|
|
be4ebddb60 | ||
|
|
34e804b784 | ||
|
|
75b2ef9372 | ||
|
|
f71e2e0e9f | ||
|
|
ce0cdcdcb0 | ||
|
|
426912173a | ||
|
|
976c85ff57 | ||
|
|
1ae9e74539 | ||
|
|
980ae3115c | ||
|
|
5ca9a06273 | ||
|
|
469097ed5b | ||
|
|
ae375f44f2 | ||
|
|
d1a808c630 | ||
|
|
733c283b1a | ||
|
|
f65a970d2e | ||
|
|
ef5b9ee66b | ||
|
|
c5a94e8899 | ||
|
|
b05488649a | ||
|
|
c654c82a3f | ||
|
|
4b7d577883 | ||
|
|
1b0289b11f | ||
|
|
00f4248137 | ||
|
|
8211f1c482 | ||
|
|
43ebde9914 | ||
|
|
7d59dd977f | ||
|
|
3e2f37418a | ||
|
|
9d2df12ab8 | ||
|
|
2b15b6b8ad | ||
|
|
49bd96c55d | ||
|
|
b3d95c0754 | ||
|
|
87c2427cfe | ||
|
|
857f7c9e61 | ||
|
|
91f184caff | ||
|
|
7cab98cf29 | ||
|
|
e9ea5e9036 | ||
|
|
03a8a2a3fb | ||
|
|
f1bffc7397 | ||
|
|
5fdc756a64 | ||
|
|
5b71d84a3a | ||
|
|
b1270376c9 | ||
|
|
871b418242 | ||
|
|
fd124e91de | ||
|
|
5b71ede2ea | ||
|
|
6285a409ed | ||
|
|
9818a123ca | ||
|
|
09ab029e2f |
2
3rdparty/CMakeLists.txt
vendored
2
3rdparty/CMakeLists.txt
vendored
@@ -4,6 +4,6 @@ endif()
|
||||
|
||||
add_subdirectory(utf8)
|
||||
|
||||
if (ENABLE_DNS)
|
||||
if (ENABLE_DNS AND NOT SYSTEM_UDNS)
|
||||
add_subdirectory(udns)
|
||||
endif()
|
||||
|
||||
1
3rdparty/udns/udns.h
vendored
1
3rdparty/udns/udns.h
vendored
@@ -45,6 +45,7 @@
|
||||
#endif
|
||||
|
||||
#include <sys/types.h> /* for time_t */
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
206
CMakeLists.txt
206
CMakeLists.txt
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 2.8.11)
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0054)
|
||||
@@ -16,18 +16,28 @@ include (CheckCXXSourceCompiles)
|
||||
include (CheckCXXCompilerFlag)
|
||||
include (GenerateExportHeader)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
|
||||
project(SimGear)
|
||||
# let's use & require C++11 - note these are only functional with CMake 3.1
|
||||
# we do manual fallbacks for CMake 3.0 in the compilers section
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
# read 'version' file into a variable (stripping any newlines or spaces)
|
||||
file(READ version versionFile)
|
||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
# Cmake documentation says we must set this before calling project(), but
|
||||
# it only seems to be picked up setting it /after/ the call to project()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
||||
|
||||
# add a dependency on the versino file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||
|
||||
# use simgear version also as the SO version (if building SOs)
|
||||
@@ -63,8 +73,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
include (CPack)
|
||||
|
||||
# We have some custom .cmake scripts not in the official distribution.
|
||||
@@ -87,38 +95,30 @@ message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
|
||||
# Configure library search paths
|
||||
#####################################################################################
|
||||
|
||||
if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
||||
# Workaround for Ubuntu/Debian which introduced the "multiarch" library
|
||||
# directory structure, which is unsupported by CMake < 2.8.10, so we need to
|
||||
# add paths manually
|
||||
# see http://www.cmake.org/Bug/view.php?id=12049 and
|
||||
# http://www.cmake.org/Bug/view.php?id=12037
|
||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
||||
/usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/lib/${CMAKE_LIBRARY_ARCHITECTURE})
|
||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||
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 +137,15 @@ 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)
|
||||
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)
|
||||
if (${MSVC_VERSION} EQUAL 1900)
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
else ()
|
||||
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
|
||||
endif ()
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
|
||||
@@ -154,44 +155,34 @@ 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)
|
||||
find_path(BOOST_ROOT boost/version.hpp
|
||||
${MSVC_3RDPARTY_ROOT}/boost
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
|
||||
)
|
||||
# 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 BOOST_INCLUDEDIR)
|
||||
# if this variable was not set by the user, set it to 3rdparty root's
|
||||
# parent dir, which is the normal location for people using our
|
||||
# windows-3rd-party repo
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
|
||||
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
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)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
# but it's not working reliably, so forcing it for now
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
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,14 +192,34 @@ else()
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
find_package(OpenAL REQUIRED)
|
||||
if (USE_AEONWAVE)
|
||||
find_package(AAX COMPONENTS aax REQUIRED)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
|
||||
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(FATAL_ERROR "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
|
||||
endif()
|
||||
endif()
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(ZLIB 1.2.4 REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
if (SYSTEM_EXPAT)
|
||||
@@ -226,8 +237,6 @@ else()
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||
|
||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||
@@ -299,19 +308,26 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
|
||||
|
||||
# isnan might not be real symbol, so can't check using function_exists
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
int main() { return isnan(0.0);} "
|
||||
HAVE_ISNAN)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
"#include <cstdlib>
|
||||
int main() { return std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
if (NOT ${HAVE_STD_ISNAN})
|
||||
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
|
||||
# certain GCC versions don't provide the atomic builds, and hence
|
||||
# require is to provide them in SGAtomic.cxx
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
|
||||
@@ -320,13 +336,17 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
GCC_ATOMIC_BUILTINS_FOUND)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -344,21 +364,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)
|
||||
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)
|
||||
|
||||
@@ -368,17 +393,8 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
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}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
@@ -403,6 +419,7 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${WINSOCK_LIBRARY}
|
||||
${SHLWAPI_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
@@ -415,10 +432,15 @@ 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()
|
||||
@@ -449,7 +471,7 @@ configure_file(SimGearConfig.cmake.in
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(ConfigPackageLocation lib/cmake/SimGear)
|
||||
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/SimGear)
|
||||
install(EXPORT SimGearTargets
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
|
||||
48
CMakeModules/FindAAX.cmake
Normal file
48
CMakeModules/FindAAX.cmake
Normal 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)
|
||||
|
||||
42
CMakeModules/FindUdns.cmake
Normal file
42
CMakeModules/FindUdns.cmake
Normal 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 ()
|
||||
@@ -54,24 +54,14 @@ if(SIMGEAR_SHARED)
|
||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_library(SimGearScene SHARED ${sceneSources})
|
||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
|
||||
# EXPORT SimGearSceneConfig
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||
|
||||
@@ -94,9 +84,6 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
|
||||
@@ -118,22 +105,65 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearScene STATIC ${sceneSources})
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(NOT SIMGEAR_HEADLESS)
|
||||
endif(SIMGEAR_SHARED)
|
||||
|
||||
target_include_directories(SimGearCore BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearCore PUBLIC
|
||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
||||
target_include_directories(SimGearCore PRIVATE
|
||||
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
|
||||
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (NOT SIMGEAR_HEADLESS)
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
target_include_directories(SimGearScene BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearScene PUBLIC ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if (USE_AEONWAVE)
|
||||
target_include_directories(SimGearScene PRIVATE ${AAX_INCLUDE_DIR} )
|
||||
else()
|
||||
target_include_directories(SimGearScene PRIVATE ${OPENAL_INCLUDE_DIR} )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
${CURL_LIBRARIES}
|
||||
${WINSOCK_LIBRARY})
|
||||
|
||||
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_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
@@ -141,6 +171,9 @@ if(NOT SIMGEAR_HEADLESS)
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
# only actually needed by canvas/KeyboardEvent.cxx
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -40,96 +40,40 @@
|
||||
#define SG_DO_STRINGIZE(X) #X
|
||||
|
||||
#ifdef __GNUC__
|
||||
# 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
|
||||
# endif
|
||||
|
||||
# define SG_GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
|
||||
#endif // __GNUC__
|
||||
|
||||
/* KAI C++ */
|
||||
#if defined(__KCC)
|
||||
# define SG_COMPILER_STR "Kai C++ version " SG_STRINGIZE(__KCC_VERSION)
|
||||
#endif // __KCC
|
||||
|
||||
//
|
||||
// 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 < 1500
|
||||
# define vsnprintf _vsnprintf
|
||||
# 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
|
||||
# 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
|
||||
|
||||
//
|
||||
// Native SGI compilers
|
||||
//
|
||||
|
||||
#if defined ( sgi ) && !defined( __GNUC__ )
|
||||
# if (_COMPILER_VERSION < 740)
|
||||
# error Need MipsPro 7.4.0 or higher now
|
||||
# endif
|
||||
|
||||
#define SG_HAVE_NATIVE_SGI_COMPILERS
|
||||
|
||||
#pragma set woff 1001,1012,1014,1116,1155,1172,1174
|
||||
#pragma set woff 1401,1460,1551,1552,1681
|
||||
|
||||
#ifdef __cplusplus
|
||||
# pragma set woff 1682,3303
|
||||
# pragma set woff 3624
|
||||
#endif
|
||||
|
||||
# define SG_COMPILER_STR "SGI MipsPro compiler version " SG_STRINGIZE(_COMPILER_VERSION)
|
||||
|
||||
#endif // Native SGI compilers
|
||||
|
||||
|
||||
#if defined (__sun)
|
||||
# define SG_UNIX
|
||||
# include <strings.h>
|
||||
# include <memory.h>
|
||||
# if defined ( __cplusplus )
|
||||
// typedef unsigned int size_t;
|
||||
extern "C" {
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
}
|
||||
# else
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
# endif // __cplusplus
|
||||
|
||||
# if !defined( __GNUC__ )
|
||||
# define SG_COMPILER_STR "Sun compiler version " SG_STRINGIZE(__SUNPRO_CC)
|
||||
# endif
|
||||
|
||||
#endif // sun
|
||||
|
||||
//
|
||||
// Intel C++ Compiler
|
||||
//
|
||||
@@ -144,29 +88,10 @@
|
||||
#ifdef __APPLE__
|
||||
# define SG_MAC
|
||||
# define SG_UNIX
|
||||
# ifdef __GNUC__
|
||||
# if ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 3 )
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# else
|
||||
// any C++ header file undefines isinf and isnan
|
||||
// so this should be included before <iostream>
|
||||
// the functions are STILL in libm (libSystem on mac os x)
|
||||
extern "C" int (isnan)(double);
|
||||
extern "C" int (isinf)(double);
|
||||
# endif
|
||||
# else
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__)
|
||||
# define SG_UNIX
|
||||
#include <sys/param.h>
|
||||
# if __FreeBSD_version < 500000
|
||||
extern "C" {
|
||||
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
@@ -201,4 +126,3 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
//
|
||||
|
||||
#endif // _SG_COMPILER_H
|
||||
|
||||
|
||||
@@ -211,8 +211,10 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** for backwards compatibility */
|
||||
#define SG_SCENERY_FILE_FORMAT "0.4"
|
||||
|
||||
/** Default range in m at which all objects are displayed. Overridden by /sim/rendering/static-lod/rough **/
|
||||
#define SG_OBJECT_RANGE 9000.0
|
||||
/** Default object ranges. Overridden by /sim/rendering/static-lod/[bare|rough|detailed] **/
|
||||
#define SG_OBJECT_RANGE_BARE 30000.0
|
||||
#define SG_OBJECT_RANGE_ROUGH 9000.0
|
||||
#define SG_OBJECT_RANGE_DETAILED 1500.0
|
||||
|
||||
/** Radius of scenery tiles in m **/
|
||||
#define SG_TILE_RADIUS 14000.0
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -36,11 +36,14 @@
|
||||
#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>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
@@ -106,13 +109,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 +123,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 +163,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 +192,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 +209,7 @@ private:
|
||||
{
|
||||
m_wasRunning = m_parent->stop();
|
||||
}
|
||||
|
||||
|
||||
~PauseThread()
|
||||
{
|
||||
if (m_wasRunning) {
|
||||
@@ -218,29 +222,126 @@ 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_stdout_isRedirectedAlready(false),
|
||||
m_stderr_isRedirectedAlready(false),
|
||||
#endif
|
||||
m_isRunning(false)
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* 1) When started from the console use the console (when no --console)
|
||||
* 2) When started from the GUI (with --console) open a new console window
|
||||
* 3) When started from the GUI (without --console) don't open a new console
|
||||
* window; stdout/stderr will not appear (except in logfiles as they do now)
|
||||
* 4) When started from the Console (with --console) open a new console window
|
||||
* 5) Ensure that IO redirection still works when started from the console
|
||||
*
|
||||
* Notes:
|
||||
* 1) fgfs needs to be a GUI subsystem app - which it already is
|
||||
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
|
||||
* this is only something that can be done via "start /wait fgfs".
|
||||
*/
|
||||
|
||||
#if !defined(SG_WINDOWS)
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
m_consoleRequested = true;
|
||||
int stderr_handle_type = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
|
||||
int stdout_handle_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
int stdout_isNull = 0;
|
||||
int stderr_isNull = 0;
|
||||
|
||||
m_stderr_isRedirectedAlready = stderr_handle_type == FILE_TYPE_DISK || stderr_handle_type == FILE_TYPE_PIPE || stderr_handle_type == FILE_TYPE_CHAR;
|
||||
m_stdout_isRedirectedAlready = stdout_handle_type == FILE_TYPE_DISK || stdout_handle_type == FILE_TYPE_PIPE || stdout_handle_type == FILE_TYPE_CHAR;
|
||||
|
||||
/*
|
||||
* We don't want to attach to the console if either stream has been redirected - so in this case ensure that both streams
|
||||
* are redirected as otherwise something will be lost (as Alloc or Attach Console will cause the handles that were bound
|
||||
* to disappear)
|
||||
*/
|
||||
if (m_stdout_isRedirectedAlready){
|
||||
if (!m_stderr_isRedirectedAlready) {
|
||||
MessageBox(0, "Redirection only works when you use 2>&1 before using > or |\r\n(e.g. fgfs 2>&1 | more)", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
exit(3);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
|
||||
* when launched via the RUN menu explorer, or another GUI app that wasn't started from the console this will fail.
|
||||
* When it fails we will redirect to the NUL device. This is to ensure that we have valid streams.
|
||||
* Later on in the initialisation sequence the --console option will be processed and this will cause the requestConsole() to
|
||||
* always open a new console, except for streams that are redirected. The same rules apply there, if both streams are redirected
|
||||
* the console will be opened, and it will contain a message to indicate that no output will be present because the streams are redirected
|
||||
*/
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
|
||||
/*
|
||||
* attach failed - so ensure that the streams are bound to the null device - but only when not already redirected
|
||||
*/
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
{
|
||||
stdout_isNull = true;
|
||||
freopen("NUL", "w", stdout);
|
||||
}
|
||||
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
{
|
||||
stderr_isNull = true;
|
||||
freopen("NUL", "w", stderr);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* providing that AttachConsole succeeded - we can then either reopen the stream onto the console, or use
|
||||
* _fdopen to attached to the currently redirected (and open stream)
|
||||
*/
|
||||
if (!stdout_isNull){
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
else
|
||||
/*
|
||||
* for already redirected streams we need to attach the stream to the OS handle that is open.
|
||||
* - this comes from part of the answer http://stackoverflow.com/a/13841522
|
||||
* _open_osfhandle returns an FD for the Win32 Handle, which is then opened using fdopen and
|
||||
* hopefully safely assigned to the stream (although it does look wrong to me it works)
|
||||
* Removing this bit will stop pipes and command line redirection (> 2> and 2>&1 from working)
|
||||
*/
|
||||
*stdout = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
|
||||
if (!stderr_isNull){
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
freopen("conout$", "w", stderr);
|
||||
else
|
||||
*stderr = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
}
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
#endif
|
||||
|
||||
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 +349,12 @@ public:
|
||||
sgDebugClass m_logClass;
|
||||
sgDebugPriority m_logPriority;
|
||||
bool m_isRunning;
|
||||
bool m_consoleRequested;
|
||||
|
||||
#if defined (SG_WINDOWS)
|
||||
// track whether the console was redirected on launch (in the constructor, which is called early on)
|
||||
bool m_stderr_isRedirectedAlready;
|
||||
bool m_stdout_isRedirectedAlready;
|
||||
#endif
|
||||
|
||||
void startLog()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
@@ -257,7 +362,7 @@ public:
|
||||
m_isRunning = true;
|
||||
start();
|
||||
}
|
||||
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
while (1) {
|
||||
@@ -267,37 +372,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 +411,7 @@ public:
|
||||
m_callbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
PauseThread pause(this);
|
||||
@@ -316,37 +421,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 +448,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 +463,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 +480,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 +515,7 @@ logstream::get_log_classes() const
|
||||
{
|
||||
return global_privateLogstream->m_logClass;
|
||||
}
|
||||
|
||||
|
||||
sgDebugPriority
|
||||
logstream::get_log_priority() const
|
||||
{
|
||||
@@ -402,25 +527,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,16 +554,59 @@ 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
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
|
||||
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
|
||||
* we don't need to handle that.
|
||||
* The new handling is quite simple:
|
||||
* 1. The constructor will ensure that these streams exists. It will attach to the
|
||||
* parent command prompt if started from the command prompt, otherwise the
|
||||
* stdout/stderr will be bound to the NUL device.
|
||||
* 2. with --console a window will always appear regardless of where the process was
|
||||
* started from. Any non redirected streams will be redirected
|
||||
* 3. You cannot use --console and either redirected stream.
|
||||
*
|
||||
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
|
||||
*/
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
|
||||
FreeConsole();
|
||||
if (AllocConsole()) {
|
||||
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
|
||||
freopen("conout$", "w", stderr);
|
||||
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
} else {
|
||||
MessageBox(0, "--console ignored because stdout or stderr redirected with > or 2>", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
sglog(); // force creation
|
||||
global_privateLogstream->requestConsole();
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
delete global_logstream;
|
||||
global_logstream = 0;
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
{
|
||||
double eccAnom, v, ecl, actTime,
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze;
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl;
|
||||
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
@@ -66,10 +67,19 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
v = atan2(yv, xv); // the planet's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // the planet's distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// calculate the planet's position in 3D space
|
||||
xh = r * (cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic longitude and latitude
|
||||
xg = xh + ourSun->getxs();
|
||||
@@ -80,8 +90,8 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
latEcl = atan2(zh, sqrt(xh*xh+yh*yh));
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) - zg * sin(ecl);
|
||||
ze = yg * sin(ecl) + zg * cos(ecl);
|
||||
ye = yg * cosecl - zg * sinecl;
|
||||
ze = yg * sinecl + zg * cosecl;
|
||||
rightAscension = atan2(ye, xe);
|
||||
declination = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
/* SG_LOG(SG_GENERAL, SG_INFO, "Planet found at : "
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -78,69 +79,90 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
{
|
||||
double
|
||||
eccAnom, ecl, actTime,
|
||||
xv, yv, v, r, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
xv, yv, v, r, xh, yh, zh, zg, xe,
|
||||
Ls, Lm, D, F, mpar, gclat, rho, HA, g,
|
||||
geoRa, geoDec;
|
||||
geoRa, geoDec,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl, rcoslatEcl,
|
||||
FlesstwoD, MlesstwoD, twoD, twoM, twolat, alpha;
|
||||
|
||||
double max_loglux = -0.504030345621;
|
||||
double min_loglux = -4.39964634562;
|
||||
double conv = 1.0319696543787917; // The log foot-candle to log lux conversion factor.
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
|
||||
// calculate the angle between ecliptic and equatorial coordinate system
|
||||
// in Radians
|
||||
ecl = ((SGD_DEGREES_TO_RADIANS * 23.4393) - (SGD_DEGREES_TO_RADIANS * 3.563E-7) * actTime);
|
||||
ecl = SGD_DEGREES_TO_RADIANS * (23.4393 - 3.563E-7 * actTime);
|
||||
eccAnom = sgCalcEccAnom(M, e); // Calculate the eccentric anomaly
|
||||
xv = a * (cos(eccAnom) - e);
|
||||
yv = a * (sqrt(1.0 - e*e) * sin(eccAnom));
|
||||
v = atan2(yv, xv); // the moon's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // and its distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// estimate the geocentric rectangular coordinates here
|
||||
xh = r * (cos(N) * cos (v+w) - sin (N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos (v+w) + cos (N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic latitude and longitude here
|
||||
lonEcl = atan2 (yh, xh);
|
||||
latEcl = atan2(zh, sqrt(xh*xh + yh*yh));
|
||||
|
||||
/* Calculate a number of perturbatioin, i.e. disturbances caused by the
|
||||
* gravitational infuence of the sun and the other major planets.
|
||||
/* Calculate a number of perturbation, i.e. disturbances caused by the
|
||||
* gravitational influence of the sun and the other major planets.
|
||||
* The largest of these even have a name */
|
||||
Ls = ourSun->getM() + ourSun->getw();
|
||||
Lm = M + w + N;
|
||||
D = Lm - Ls;
|
||||
F = Lm - N;
|
||||
|
||||
twoD = 2 * D;
|
||||
twoM = 2 * M;
|
||||
FlesstwoD = F - twoD;
|
||||
MlesstwoD = M - twoD;
|
||||
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin (M - 2*D)
|
||||
+0.658 * sin (2*D)
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin(MlesstwoD)
|
||||
+0.658 * sin(twoD)
|
||||
-0.186 * sin(ourSun->getM())
|
||||
-0.059 * sin(2*M - 2*D)
|
||||
-0.057 * sin(M - 2*D + ourSun->getM())
|
||||
+0.053 * sin(M + 2*D)
|
||||
+0.046 * sin(2*D - ourSun->getM())
|
||||
-0.059 * sin(twoM - twoD)
|
||||
-0.057 * sin(MlesstwoD + ourSun->getM())
|
||||
+0.053 * sin(M + twoD)
|
||||
+0.046 * sin(twoD - ourSun->getM())
|
||||
+0.041 * sin(M - ourSun->getM())
|
||||
-0.035 * sin(D)
|
||||
-0.031 * sin(M + ourSun->getM())
|
||||
-0.015 * sin(2*F - 2*D)
|
||||
-0.015 * sin(2*F - twoD)
|
||||
+0.011 * sin(M - 4*D)
|
||||
);
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(F-2*D)
|
||||
-0.055 * sin(M - F - 2*D)
|
||||
-0.046 * sin(M + F - 2*D)
|
||||
+0.033 * sin(F + 2*D)
|
||||
+0.017 * sin(2*M + F)
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(FlesstwoD)
|
||||
-0.055 * sin(M - FlesstwoD)
|
||||
-0.046 * sin(M + FlesstwoD)
|
||||
+0.033 * sin(F + twoD)
|
||||
+0.017 * sin(twoM + F)
|
||||
);
|
||||
r += (-0.58 * cos(M - 2*D)
|
||||
-0.46 * cos(2*D)
|
||||
r += (-0.58 * cos(MlesstwoD)
|
||||
-0.46 * cos(twoD)
|
||||
);
|
||||
distance = r;
|
||||
// SG_LOG(SG_GENERAL, SG_INFO, "Running moon update");
|
||||
xg = r * cos(lonEcl) * cos(latEcl);
|
||||
yg = r * sin(lonEcl) * cos(latEcl);
|
||||
rcoslatEcl = r * cos(latEcl);
|
||||
xg = cos(lonEcl) * rcoslatEcl;
|
||||
yg = sin(lonEcl) * rcoslatEcl;
|
||||
zg = r * sin(latEcl);
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) -zg * sin(ecl);
|
||||
ze = yg * sin(ecl) +zg * cos(ecl);
|
||||
ye = yg * cosecl -zg * sinecl;
|
||||
ze = yg * sinecl +zg * cosecl;
|
||||
|
||||
geoRa = atan2(ye, xe);
|
||||
geoDec = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
@@ -154,17 +176,17 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
// topocentric ra and dec. i.e. the position as seen from the
|
||||
// surface of the earth, instead of the center of the earth
|
||||
|
||||
// First calculate the moon's parrallax, that is, the apparent size of the
|
||||
// First calculate the moon's parallax, that is, the apparent size of the
|
||||
// (equatorial) radius of the earth, as seen from the moon
|
||||
mpar = asin ( 1 / r);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "r = " << r << " mpar = " << mpar );
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "lat = " << f->get_Latitude() );
|
||||
|
||||
gclat = lat - 0.003358 *
|
||||
sin (2 * SGD_DEGREES_TO_RADIANS * lat );
|
||||
twolat = 2 * SGD_DEGREES_TO_RADIANS * lat;
|
||||
gclat = lat - 0.003358 * sin(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "gclat = " << gclat );
|
||||
|
||||
rho = 0.99883 + 0.00167 * cos(2 * SGD_DEGREES_TO_RADIANS * lat);
|
||||
rho = 0.99883 + 0.00167 * cos(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "rho = " << rho );
|
||||
|
||||
if (geoRa < 0)
|
||||
@@ -193,4 +215,22 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
/* SG_LOG( SG_GENERAL, SG_INFO,
|
||||
"Ra = (" << (SGD_RADIANS_TO_DEGREES *rightAscension)
|
||||
<< "), Dec= (" << (SGD_RADIANS_TO_DEGREES *declination) << ")" ); */
|
||||
|
||||
// Moon age and phase calculation
|
||||
age = lonEcl - ourSun->getlonEcl();
|
||||
phase = (1 - cos(age)) / 2;
|
||||
|
||||
// The log of the illuminance of the moon outside the atmosphere.
|
||||
// This is the base 10 log of equation 20 from Krisciunas K. and Schaefer B.E.
|
||||
// (1991). A model of the brightness of moonlight, Publ. Astron. Soc. Pacif.
|
||||
// 103(667), 1033-1039 (DOI: http://dx.doi.org/10.1086/132921).
|
||||
alpha = SGD_RADIANS_TO_DEGREES * SGMiscd::normalizeAngle(age + SGMiscd::pi());
|
||||
log_I = -0.4 * (3.84 + 0.026*fabs(alpha) + 4e-9*pow(alpha, 4.0));
|
||||
|
||||
// Convert from foot-candles to lux.
|
||||
log_I += conv;
|
||||
|
||||
// The moon's illuminance factor, bracketed between 0 and 1.
|
||||
I_factor = (log_I - max_loglux) / (max_loglux - min_loglux) + 1.0;
|
||||
I_factor = SGMiscd::clip(I_factor, 0, 1);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@ class MoonPos : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double xg, yg; // the moon's rectangular geocentric coordinates
|
||||
double ye, ze; // the moon's rectangular equatorial coordinates
|
||||
double distance; // the moon's distance to the earth
|
||||
double age; // the moon's age from 0 to 2pi
|
||||
double phase; // the moon's phase
|
||||
double log_I; // the moon's illuminance outside the atmosphere (logged)
|
||||
double I_factor; // the illuminance factor for the moon, between 0 and 1.
|
||||
// void TexInit(); // This should move to the constructor eventually.
|
||||
|
||||
// GLUquadricObj *moonObject;
|
||||
@@ -54,7 +61,72 @@ public:
|
||||
~MoonPos();
|
||||
void updatePosition(double mjd, double lst, double lat, Star *ourSun);
|
||||
// void newImage();
|
||||
double getM() const;
|
||||
double getw() const;
|
||||
double getxg() const;
|
||||
double getyg() const;
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getAge() const;
|
||||
double getPhase() const;
|
||||
double getLogIlluminance() const;
|
||||
double getIlluminanceFactor() const;
|
||||
};
|
||||
|
||||
inline double MoonPos::getM() const
|
||||
{
|
||||
return M;
|
||||
}
|
||||
|
||||
inline double MoonPos::getw() const
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
inline double MoonPos::getxg() const
|
||||
{
|
||||
return xg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getyg() const
|
||||
{
|
||||
return yg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getye() const
|
||||
{
|
||||
return ye;
|
||||
}
|
||||
|
||||
inline double MoonPos::getze() const
|
||||
{
|
||||
return ze;
|
||||
}
|
||||
|
||||
inline double MoonPos::getDistance() const
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double MoonPos::getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
|
||||
inline double MoonPos::getPhase() const
|
||||
{
|
||||
return phase;
|
||||
}
|
||||
|
||||
inline double MoonPos::getLogIlluminance() const
|
||||
{
|
||||
return log_I;
|
||||
}
|
||||
|
||||
inline double MoonPos::getIlluminanceFactor() const
|
||||
{
|
||||
return I_factor;
|
||||
}
|
||||
|
||||
#endif // _MOONPOS_HXX_
|
||||
|
||||
@@ -33,6 +33,7 @@ class Star : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double lonEcl; // the sun's true longitude
|
||||
double xs, ys; // the sun's rectangular geocentric coordinates
|
||||
double ye, ze; // the sun's rectangularequatorial rectangular geocentric coordinates
|
||||
double distance; // the sun's distance to the earth
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getlonEcl() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -88,6 +90,10 @@ inline double Star::getDistance() const
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double Star::getlonEcl() const
|
||||
{
|
||||
return lonEcl;
|
||||
}
|
||||
|
||||
#endif // _STAR_HXX_
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -69,6 +69,96 @@ NAPTRRequest::NAPTRRequest( const std::string & dn ) :
|
||||
_type = DNS_T_NAPTR;
|
||||
}
|
||||
|
||||
SRVRequest::SRVRequest( const std::string & dn ) :
|
||||
Request(dn)
|
||||
{
|
||||
_type = DNS_T_SRV;
|
||||
}
|
||||
|
||||
SRVRequest::SRVRequest( const std::string & dn, const string & service, const string & protocol ) :
|
||||
Request(dn),
|
||||
_service(service),
|
||||
_protocol(protocol)
|
||||
{
|
||||
_type = DNS_T_SRV;
|
||||
}
|
||||
|
||||
static bool sortSRV( const SRVRequest::SRV_ptr a, const SRVRequest::SRV_ptr b )
|
||||
{
|
||||
if( a->priority > b->priority ) return false;
|
||||
if( a->priority < b->priority ) return true;
|
||||
return a->weight > b->weight;
|
||||
}
|
||||
|
||||
static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
|
||||
{
|
||||
SRVRequest * r = static_cast<SRVRequest*>(data);
|
||||
if (result) {
|
||||
r->cname = result->dnssrv_cname;
|
||||
r->qname = result->dnssrv_qname;
|
||||
r->ttl = result->dnssrv_ttl;
|
||||
for (int i = 0; i < result->dnssrv_nrr; i++) {
|
||||
SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
|
||||
r->entries.push_back(srv);
|
||||
srv->priority = result->dnssrv_srv[i].priority;
|
||||
srv->weight = result->dnssrv_srv[i].weight;
|
||||
srv->port = result->dnssrv_srv[i].port;
|
||||
srv->target = result->dnssrv_srv[i].name;
|
||||
}
|
||||
std::sort( r->entries.begin(), r->entries.end(), sortSRV );
|
||||
free(result);
|
||||
}
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void SRVRequest::submit()
|
||||
{
|
||||
// if service is defined, pass service and protocol
|
||||
if (!dns_submit_srv(NULL, getDn().c_str(), _service.empty() ? NULL : _service.c_str(), _service.empty() ? NULL : _protocol.c_str(), 0, dnscbSRV, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
_start = time(NULL);
|
||||
}
|
||||
|
||||
TXTRequest::TXTRequest( const std::string & dn ) :
|
||||
Request(dn)
|
||||
{
|
||||
_type = DNS_T_TXT;
|
||||
}
|
||||
|
||||
static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
|
||||
{
|
||||
TXTRequest * r = static_cast<TXTRequest*>(data);
|
||||
if (result) {
|
||||
r->cname = result->dnstxt_cname;
|
||||
r->qname = result->dnstxt_qname;
|
||||
r->ttl = result->dnstxt_ttl;
|
||||
for (int i = 0; i < result->dnstxt_nrr; i++) {
|
||||
//TODO: interprete the .len field of dnstxt_txt?
|
||||
string txt = string((char*)result->dnstxt_txt[i].txt);
|
||||
r->entries.push_back( txt );
|
||||
string_list tokens = simgear::strutils::split( txt, "=", 1 );
|
||||
if( tokens.size() == 2 ) {
|
||||
r->attributes[tokens[0]] = tokens[1];
|
||||
}
|
||||
}
|
||||
free(result);
|
||||
}
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void TXTRequest::submit()
|
||||
{
|
||||
// protocol and service an already encoded in DN so pass in NULL for both
|
||||
if (!dns_submit_txt(NULL, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
_start = time(NULL);
|
||||
}
|
||||
|
||||
|
||||
static bool sortNAPTR( const NAPTRRequest::NAPTR_ptr a, const NAPTRRequest::NAPTR_ptr b )
|
||||
{
|
||||
if( a->order > b->order ) return false;
|
||||
@@ -130,7 +220,6 @@ void Client::makeRequest(const Request_ptr& r)
|
||||
r->submit();
|
||||
}
|
||||
|
||||
|
||||
void Client::update(int waitTimeout)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
@@ -27,8 +27,11 @@
|
||||
#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>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -59,6 +62,7 @@ protected:
|
||||
time_t _timeout_secs;
|
||||
time_t _start;
|
||||
};
|
||||
typedef SGSharedPtr<Request> Request_ptr;
|
||||
|
||||
class NAPTRRequest : public Request
|
||||
{
|
||||
@@ -82,7 +86,38 @@ public:
|
||||
std::string qservice;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<Request> Request_ptr;
|
||||
class SRVRequest : public Request
|
||||
{
|
||||
public:
|
||||
SRVRequest( const std::string & dn );
|
||||
SRVRequest( const std::string & dn, const string & service, const string & protocol );
|
||||
virtual void submit();
|
||||
|
||||
struct SRV : SGReferenced {
|
||||
int priority;
|
||||
int weight;
|
||||
int port;
|
||||
std::string target;
|
||||
};
|
||||
typedef SGSharedPtr<SRV> SRV_ptr;
|
||||
typedef std::vector<SRV_ptr> SRV_list;
|
||||
SRV_list entries;
|
||||
private:
|
||||
std::string _service;
|
||||
std::string _protocol;
|
||||
};
|
||||
|
||||
class TXTRequest : public Request
|
||||
{
|
||||
public:
|
||||
TXTRequest( const std::string & dn );
|
||||
virtual void submit();
|
||||
|
||||
typedef std::vector<string> TXT_list;
|
||||
typedef std::map<std::string,std::string> TXT_Attribute_map;
|
||||
TXT_list entries;
|
||||
TXT_Attribute_map attributes;
|
||||
};
|
||||
|
||||
class Client
|
||||
{
|
||||
@@ -97,7 +132,6 @@ public:
|
||||
// void cancelRequest(const Request_ptr& r, std::string reason = std::string());
|
||||
|
||||
private:
|
||||
|
||||
class ClientPrivate;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
@@ -211,8 +224,7 @@ void Client::makeRequest(const Request_ptr& r)
|
||||
|
||||
r->_client = this;
|
||||
|
||||
ClientPrivate::RequestCurlMap::iterator rit = d->requests.find(r);
|
||||
assert(rit == d->requests.end());
|
||||
assert(d->requests.find(r) == d->requests.end());
|
||||
|
||||
CURL* curlRequest = curl_easy_init();
|
||||
curl_easy_setopt(curlRequest, CURLOPT_URL, r->url().c_str());
|
||||
@@ -231,6 +243,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 +314,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);
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -57,8 +57,6 @@ namespace simgear
|
||||
{
|
||||
}
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
size_t contentSize() const
|
||||
{
|
||||
return _contentSize;
|
||||
@@ -70,7 +68,7 @@ namespace simgear
|
||||
}
|
||||
protected:
|
||||
HTTPDirectory* _directory;
|
||||
size_t _contentSize;
|
||||
size_t _contentSize = 0;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
|
||||
@@ -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
|
||||
@@ -562,7 +756,12 @@ size_t HTTPRepository::bytesToDownload() const
|
||||
}
|
||||
|
||||
for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
|
||||
result += (*r)->contentSize() - (*r)->responseBytesReceived();
|
||||
if ((*r)->contentSize() > 0) {
|
||||
// Content size for root dirindex of a repository is zero,
|
||||
// and returing a negative value breaks everyting, so just ignore
|
||||
// it
|
||||
result += (*r)->contentSize() - (*r)->responseBytesReceived();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -580,7 +779,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 +794,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 +803,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 +825,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 +847,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 +874,6 @@ HTTPRepository::failure() const
|
||||
_targetHash(targetHash)
|
||||
{
|
||||
sha1_init(&hashContext);
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "will GET dir " << url());
|
||||
}
|
||||
|
||||
void setIsRootDir()
|
||||
@@ -701,7 +898,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 +914,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 +936,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 +950,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 +975,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 +999,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 +1010,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 +1019,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 +1044,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 +1061,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 +1077,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 +1094,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 +1113,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 +1124,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 +1133,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 +1151,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 +1166,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 +1197,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 +1222,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 +1242,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);
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st);
|
||||
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 +1280,21 @@ 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()) {
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
BIN
simgear/io/test.tar.gz
Normal file
Binary file not shown.
BIN
simgear/io/test2.tar
Normal file
BIN
simgear/io/test2.tar
Normal file
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
58
simgear/io/test_untar.cxx
Normal 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
418
simgear/io/untar.cxx
Normal 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
52
simgear/io/untar.hxx
Normal 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
|
||||
@@ -32,6 +32,7 @@ set(HEADERS
|
||||
sg_geodesy.hxx
|
||||
sg_types.hxx
|
||||
sg_random.h
|
||||
simd.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define SGGeod_H
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/math/SGVec3.hxx>
|
||||
|
||||
// #define SG_GEOD_NATIVE_DEGREE
|
||||
|
||||
|
||||
@@ -153,17 +153,7 @@ 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
|
||||
return std::isnan(v);
|
||||
#else
|
||||
// Use that every compare involving a NaN returns false
|
||||
// But be careful, some usual compiler switches like for example
|
||||
// -fast-math from gcc might optimize that expression to v != v which
|
||||
// behaves exactly like the opposite ...
|
||||
return !(v == v);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include <simgear/math/SGVec2.hxx>
|
||||
#include <simgear/math/SGGeodesy.hxx>
|
||||
|
||||
/// 3D Vector Class
|
||||
template<typename T>
|
||||
class SGVec3 {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
868
simgear/math/simd.hxx
Normal file
868
simgear/math/simd.hxx
Normal file
@@ -0,0 +1,868 @@
|
||||
// Copyright (C) 2016 Erik Hofman - erik@ehofman.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 __SIMD_H__
|
||||
#define __SIMD_H__ 1
|
||||
|
||||
#include <cstring>
|
||||
|
||||
# ifdef __SSE__
|
||||
# include <xmmintrin.h>
|
||||
# endif
|
||||
|
||||
template<typename T>
|
||||
class simd4_t
|
||||
{
|
||||
public:
|
||||
~simd4_t() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
class simd4_t<float>
|
||||
{
|
||||
private:
|
||||
typedef float __vec4_t[4];
|
||||
|
||||
# ifdef __SSE__
|
||||
union {
|
||||
__m128 v4;
|
||||
__vec4_t vec;
|
||||
};
|
||||
# else
|
||||
__vec4_t vec;
|
||||
# endif
|
||||
|
||||
public:
|
||||
simd4_t() {}
|
||||
simd4_t(float f)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = f;
|
||||
}
|
||||
simd4_t(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(float[4]));
|
||||
}
|
||||
simd4_t(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(float[4]));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float (&ptr(void))[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline const float (&ptr(void) const)[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t& operator=(float f)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = f;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(float[4]));
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(float[4]));
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t operator+(float f)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += f;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator-()
|
||||
{
|
||||
simd4_t r(0.0f);
|
||||
r -= vec;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(float f)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= f;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator*(float f)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= f;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator/(float f)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= f;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t& operator+=(float f)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += f;
|
||||
# else
|
||||
vec[0] += f;
|
||||
vec[1] += f;
|
||||
vec[2] += f;
|
||||
vec[3] += f;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this += r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += v.v4;
|
||||
# else
|
||||
vec[0] += v[0];
|
||||
vec[1] += v[1];
|
||||
vec[2] += v[2];
|
||||
vec[3] += v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator-=(float f)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= f;
|
||||
# else
|
||||
vec[0] -= f;
|
||||
vec[1] -= f;
|
||||
vec[2] -= f;
|
||||
vec[3] -= f;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this -= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= v.v4;
|
||||
# else
|
||||
vec[0] -= v[0];
|
||||
vec[1] -= v[1];
|
||||
vec[2] -= v[2];
|
||||
vec[3] -= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator *=(float f)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= f;
|
||||
# else
|
||||
vec[0] *= f;
|
||||
vec[1] *= f;
|
||||
vec[2] *= f;
|
||||
vec[3] *= f;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this *= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= v.v4;
|
||||
# else
|
||||
vec[0] *= v[0];
|
||||
vec[1] *= v[1];
|
||||
vec[2] *= v[2];
|
||||
vec[3] *= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator/=(float f)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= f;
|
||||
# else
|
||||
vec[0] /= f;
|
||||
vec[1] /= f;
|
||||
vec[2] /= f;
|
||||
vec[3] /= f;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this /= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= v.v4;
|
||||
# else
|
||||
vec[0] /= v[0];
|
||||
vec[1] /= v[1];
|
||||
vec[2] /= v[2];
|
||||
vec[3] /= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator const float*() const {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator float*() {
|
||||
return vec;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
class simd4_t<double>
|
||||
{
|
||||
private:
|
||||
typedef double __vec4_t[4];
|
||||
|
||||
# ifdef __SSE__
|
||||
union {
|
||||
__m128d v4;
|
||||
__vec4_t vec;
|
||||
};
|
||||
# else
|
||||
__vec4_t vec;
|
||||
# endif
|
||||
|
||||
public:
|
||||
simd4_t() {}
|
||||
simd4_t(double d)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = d;
|
||||
}
|
||||
simd4_t(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(double[4]));
|
||||
}
|
||||
simd4_t(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(double[4]));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double (&ptr(void))[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline const double (&ptr(void) const)[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t& operator=(double d)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = d;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(double[4]));
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(double[4]));
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t operator+(double d)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += d;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator-()
|
||||
{
|
||||
simd4_t r(0.0f);
|
||||
r -= vec;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(double d)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= d;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator*(double d)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= d;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator/(double d)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= d;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t& operator+=(double d)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += d;
|
||||
# else
|
||||
vec[0] += d;
|
||||
vec[1] += d;
|
||||
vec[2] += d;
|
||||
vec[3] += d;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this += r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += v.v4;
|
||||
# else
|
||||
vec[0] += v[0];
|
||||
vec[1] += v[1];
|
||||
vec[2] += v[2];
|
||||
vec[3] += v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator-=(double d)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= d;
|
||||
# else
|
||||
vec[0] -= d;
|
||||
vec[1] -= d;
|
||||
vec[2] -= d;
|
||||
vec[3] -= d;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this -= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= v.v4;
|
||||
# else
|
||||
vec[0] -= v[0];
|
||||
vec[1] -= v[1];
|
||||
vec[2] -= v[2];
|
||||
vec[3] -= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator *=(double d)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= d;
|
||||
# else
|
||||
vec[0] *= d;
|
||||
vec[1] *= d;
|
||||
vec[2] *= d;
|
||||
vec[3] *= d;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this *= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= v.v4;
|
||||
# else
|
||||
vec[0] *= v[0];
|
||||
vec[1] *= v[1];
|
||||
vec[2] *= v[2];
|
||||
vec[3] *= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator/=(double d)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= d;
|
||||
# else
|
||||
vec[0] /= d;
|
||||
vec[1] /= d;
|
||||
vec[2] /= d;
|
||||
vec[3] /= d;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this /= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= v.v4;
|
||||
# else
|
||||
vec[0] /= v[0];
|
||||
vec[1] /= v[1];
|
||||
vec[2] /= v[2];
|
||||
vec[3] /= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator const double*() const {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator double*() {
|
||||
return vec;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class simd4_t<int>
|
||||
{
|
||||
private:
|
||||
typedef int __vec4_t[4];
|
||||
|
||||
# ifdef __SSE__
|
||||
union {
|
||||
__m128i v4;
|
||||
__vec4_t vec;
|
||||
};
|
||||
# else
|
||||
__vec4_t vec;
|
||||
# endif
|
||||
|
||||
public:
|
||||
simd4_t() {}
|
||||
simd4_t(int i)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = i;
|
||||
}
|
||||
simd4_t(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(int[4]));
|
||||
}
|
||||
simd4_t(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(int[4]));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int (&ptr(void))[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline const int (&ptr(void) const)[4] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t& operator=(int i)
|
||||
{
|
||||
vec[0] = vec[1] = vec[2] = vec[3] = i;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const __vec4_t v)
|
||||
{
|
||||
std::memcpy(vec, v, sizeof(int[4]));
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 = v.v4;
|
||||
#else
|
||||
std::memcpy(vec, v.vec, sizeof(int[4]));
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t operator+(int i)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += i;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator+(const simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r += v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator-()
|
||||
{
|
||||
simd4_t r(0.0f);
|
||||
r -= vec;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(int i)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= i;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator-(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r -= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator*(int i)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= i;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator*(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r *= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t operator/(int i)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= i;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
inline simd4_t operator/(simd4_t& v)
|
||||
{
|
||||
simd4_t r(*this);
|
||||
r /= v;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline simd4_t& operator+=(int i)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += i;
|
||||
# else
|
||||
vec[0] += i;
|
||||
vec[1] += i;
|
||||
vec[2] += i;
|
||||
vec[3] += i;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this += r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator+=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 += v.v4;
|
||||
# else
|
||||
vec[0] += v[0];
|
||||
vec[1] += v[1];
|
||||
vec[2] += v[2];
|
||||
vec[3] += v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator-=(int i)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= i;
|
||||
# else
|
||||
vec[0] -= i;
|
||||
vec[1] -= i;
|
||||
vec[2] -= i;
|
||||
vec[3] -= i;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this -= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator-=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 -= v.v4;
|
||||
# else
|
||||
vec[0] -= v[0];
|
||||
vec[1] -= v[1];
|
||||
vec[2] -= v[2];
|
||||
vec[3] -= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator *=(int i)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= i;
|
||||
# else
|
||||
vec[0] *= i;
|
||||
vec[1] *= i;
|
||||
vec[2] *= i;
|
||||
vec[3] *= i;
|
||||
# endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this *= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator*=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 *= v.v4;
|
||||
# else
|
||||
vec[0] *= v[0];
|
||||
vec[1] *= v[1];
|
||||
vec[2] *= v[2];
|
||||
vec[3] *= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t& operator/=(int i)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= i;
|
||||
# else
|
||||
vec[0] /= i;
|
||||
vec[1] /= i;
|
||||
vec[2] /= i;
|
||||
vec[3] /= i;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const __vec4_t v)
|
||||
{
|
||||
simd4_t r(v);
|
||||
*this /= r;
|
||||
return *this;
|
||||
}
|
||||
inline simd4_t& operator/=(const simd4_t& v)
|
||||
{
|
||||
# ifdef __SSE__
|
||||
v4 /= v.v4;
|
||||
# else
|
||||
vec[0] /= v[0];
|
||||
vec[1] /= v[1];
|
||||
vec[2] /= v[2];
|
||||
vec[3] /= v[3];
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator const int*() const {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator int*() {
|
||||
return vec;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __SIMD_H__ */
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -50,9 +53,9 @@ static const char sgDirPathSep = '/';
|
||||
static const char sgDirPathSepBad = '\\';
|
||||
|
||||
#ifdef _WIN32
|
||||
static const char sgSearchPathSep = ';';
|
||||
const char SGPath::pathListSep = ';';
|
||||
#else
|
||||
static const char sgSearchPathSep = ':';
|
||||
const char SGPath::pathListSep = ':';
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -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 );
|
||||
append( SGPath::pathListSep+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;
|
||||
}
|
||||
|
||||
@@ -608,7 +644,7 @@ string_list sgPathSplit( const string &search_path ) {
|
||||
bool done = false;
|
||||
|
||||
while ( !done ) {
|
||||
int index = tmp.find(sgSearchPathSep);
|
||||
int index = tmp.find(SGPath::pathListSep);
|
||||
if (index >= 0) {
|
||||
result.push_back( tmp.substr(0, index) );
|
||||
tmp = tmp.substr( index + 1 );
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ class SGPath {
|
||||
|
||||
public:
|
||||
|
||||
// OS-dependent separator used in paths lists
|
||||
static const char pathListSep;
|
||||
|
||||
struct Permissions
|
||||
{
|
||||
bool read : 1;
|
||||
@@ -73,6 +76,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 +136,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 +191,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 +274,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 +302,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 +321,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 +355,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.
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
#include <ctype.h> // isspace()
|
||||
#include <cerrno>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "sgstream.hxx"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
|
||||
@@ -39,10 +43,11 @@ 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,
|
||||
bool use_exact_name )
|
||||
: istream(&gzbuf)
|
||||
{
|
||||
this->open( name, io_mode );
|
||||
this->open( name, io_mode, use_exact_name );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -58,32 +63,35 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Open a possibly gzipped file for reading.
|
||||
// If the initial open fails and the filename has a ".gz" extension then
|
||||
// remove the extension and try again.
|
||||
// If the initial open fails and the filename doesn't have a ".gz" extension
|
||||
// then append ".gz" and try again.
|
||||
// If 'use_exact_name' is true, just try to open the indicated file, nothing
|
||||
// else. Otherwise:
|
||||
// - if the initial open fails and the filename has a ".gz" extension, then
|
||||
// remove it and try again;
|
||||
// - if the initial open fails and the filename doesn't have a ".gz"
|
||||
// extension, 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,
|
||||
bool use_exact_name )
|
||||
{
|
||||
gzbuf.open( name.c_str(), io_mode );
|
||||
if ( ! gzbuf.is_open() )
|
||||
std::string s = name.utf8Str();
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
if ( ! (gzbuf.is_open() || use_exact_name) )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +101,11 @@ sg_gzifstream::attach( int fd, ios_openmode io_mode )
|
||||
gzbuf.attach( fd, io_mode );
|
||||
}
|
||||
|
||||
z_off_t
|
||||
sg_gzifstream::approxOffset() {
|
||||
return gzbuf.approxOffset();
|
||||
}
|
||||
|
||||
//
|
||||
// Manipulators
|
||||
//
|
||||
@@ -159,7 +172,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 +193,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 +204,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);
|
||||
}
|
||||
|
||||
@@ -33,13 +33,17 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
# include <istream>
|
||||
# include <ostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <simgear/misc/zfstream.hxx>
|
||||
|
||||
class SGPath;
|
||||
|
||||
/**
|
||||
* An envelope class for gzifstream.
|
||||
*/
|
||||
@@ -50,13 +54,15 @@ public:
|
||||
sg_gzifstream();
|
||||
|
||||
/**
|
||||
* Constructor that attempt to open a file with and without
|
||||
* ".gz" extension.
|
||||
* Constructor that attempts to open a file.
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
* @param use_exact_name if false, try to add or remove a ".gz" extension
|
||||
* in case the indicated file can't be opened
|
||||
*/
|
||||
sg_gzifstream( const std::string& name,
|
||||
ios_openmode io_mode = ios_in | ios_binary );
|
||||
sg_gzifstream( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in | ios_binary,
|
||||
bool use_exact_name = false );
|
||||
|
||||
/**
|
||||
* Constructor that attaches itself to an existing file descriptor.
|
||||
@@ -66,12 +72,15 @@ public:
|
||||
sg_gzifstream( int fd, ios_openmode io_mode = ios_in|ios_binary );
|
||||
|
||||
/**
|
||||
* Attempt to open a file with and without ".gz" extension.
|
||||
* Attempt to open a file.
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
* @param use_exact_name if false, try to add or remove a ".gz" extension
|
||||
* in case the indicated file can't be opened
|
||||
*/
|
||||
void open( const std::string& name,
|
||||
ios_openmode io_mode = ios_in|ios_binary );
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in|ios_binary,
|
||||
bool use_exact_name = false );
|
||||
|
||||
/**
|
||||
* Attach to an existing file descriptor.
|
||||
@@ -88,10 +97,19 @@ public:
|
||||
/** @return true if the file is successfully opened, false otherwise. */
|
||||
bool is_open() { return gzbuf.is_open(); }
|
||||
|
||||
/**
|
||||
* @return the current offset in the file being read or written.
|
||||
* The offset corresponds to compressed data if the file is compressed,
|
||||
* and is influenced by buffering performed in zlib, hence the "approx"
|
||||
* qualifier. It should be suitable for progress indicators and such,
|
||||
* though.
|
||||
*/
|
||||
z_off_t approxOffset();
|
||||
|
||||
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 +148,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 +163,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 +187,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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include <string.h> // strerror_r() and strerror_s()
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(HAVE_CPP11_CODECVT)
|
||||
#include <codecvt> // new in C++11
|
||||
#endif
|
||||
|
||||
#include "strutils.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
@@ -34,6 +38,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 +88,7 @@ namespace simgear {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
static vector<string>
|
||||
split_whitespace( const string& str, int maxsplit )
|
||||
@@ -125,7 +134,7 @@ namespace simgear {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
vector<string>
|
||||
split( const string& str, const char* sep, int maxsplit )
|
||||
@@ -229,7 +238,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 +247,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 +277,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 +290,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 +310,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 +334,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 +345,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 +370,73 @@ 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)
|
||||
{
|
||||
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);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.from_bytes(a);
|
||||
#else
|
||||
return std::wstring();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string convertWStringToUtf8(const std::wstring& w)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertWStringToMultiByte(CP_UTF8, w);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.to_bytes(w);
|
||||
#else
|
||||
return std::string();
|
||||
#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 +489,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 +506,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 +566,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 +632,7 @@ string sanitizePrintfFormat(const string& input)
|
||||
SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input);
|
||||
return string();
|
||||
}
|
||||
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -615,7 +650,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 +680,5 @@ std::string error_string(int errnum)
|
||||
}
|
||||
|
||||
} // end namespace strutils
|
||||
|
||||
|
||||
} // end namespace simgear
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -40,20 +40,39 @@ BOOST_AUTO_TEST_CASE( strutils_functions )
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
|
||||
|
||||
string_list la = strutils::split("zero one two three four five");
|
||||
BOOST_CHECK_EQUAL(la[2], "two");
|
||||
BOOST_CHECK_EQUAL(la[5], "five");
|
||||
BOOST_CHECK_EQUAL(la.size(), 6);
|
||||
string_list l = strutils::split("zero one two three four five");
|
||||
BOOST_CHECK_EQUAL(l[2], "two");
|
||||
BOOST_CHECK_EQUAL(l[5], "five");
|
||||
BOOST_CHECK_EQUAL(l.size(), 6);
|
||||
|
||||
string_list lb = strutils::split("alpha:beta:gamma:delta", ":", 2);
|
||||
BOOST_CHECK_EQUAL(lb.size(), 3);
|
||||
BOOST_CHECK_EQUAL(lb[0], "alpha");
|
||||
BOOST_CHECK_EQUAL(lb[1], "beta");
|
||||
BOOST_CHECK_EQUAL(lb[2], "gamma:delta");
|
||||
|
||||
std::string j = strutils::join(la, "&");
|
||||
std::string j = strutils::join(l, "&");
|
||||
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
|
||||
|
||||
l = strutils::split("alpha:beta:gamma:delta", ":", 2);
|
||||
BOOST_CHECK_EQUAL(l.size(), 3);
|
||||
BOOST_CHECK_EQUAL(l[0], "alpha");
|
||||
BOOST_CHECK_EQUAL(l[1], "beta");
|
||||
BOOST_CHECK_EQUAL(l[2], "gamma:delta");
|
||||
|
||||
l = strutils::split("", ",");
|
||||
BOOST_CHECK_EQUAL(l.size(), 1);
|
||||
BOOST_CHECK_EQUAL(l[0], "");
|
||||
|
||||
l = strutils::split(",", ",");
|
||||
BOOST_CHECK_EQUAL(l.size(), 2);
|
||||
BOOST_CHECK_EQUAL(l[0], "");
|
||||
BOOST_CHECK_EQUAL(l[1], "");
|
||||
|
||||
l = strutils::split(",,", ",");
|
||||
BOOST_CHECK_EQUAL(l.size(), 3);
|
||||
BOOST_CHECK_EQUAL(l[0], "");
|
||||
BOOST_CHECK_EQUAL(l[1], "");
|
||||
BOOST_CHECK_EQUAL(l[2], "");
|
||||
|
||||
l = strutils::split(" ", ",");
|
||||
BOOST_CHECK_EQUAL(l.size(), 1);
|
||||
BOOST_CHECK_EQUAL(l[0], " ");
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
}
|
||||
|
||||
|
||||
@@ -35,4 +35,9 @@
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_TEST_FAIL(msg) \
|
||||
std::cerr << "failure:" << msg; \
|
||||
exit(1);
|
||||
|
||||
|
||||
#endif // of SG_MISC_TEST_MACROS_HXX
|
||||
|
||||
@@ -22,11 +22,18 @@
|
||||
// $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 <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <zlib.h>
|
||||
#include "zfstream.hxx"
|
||||
|
||||
//
|
||||
@@ -110,7 +117,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;
|
||||
@@ -169,6 +183,27 @@ gzfilebuf::setcompressionstrategy( int comp_strategy )
|
||||
return gzsetparams(file, -2, comp_strategy);
|
||||
}
|
||||
|
||||
z_off_t
|
||||
gzfilebuf::approxOffset() {
|
||||
z_off_t res = gzoffset(file);
|
||||
|
||||
if (res == -1) {
|
||||
int errnum;
|
||||
std::string errMsg = "gzoffset() error: ";
|
||||
const char *gzMsg = gzerror(file, &errnum);
|
||||
|
||||
if (errnum == Z_ERRNO) {
|
||||
errMsg += simgear::strutils::error_string(errno);
|
||||
} else {
|
||||
errMsg += std::string(gzMsg);
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, errMsg );
|
||||
throw sg_io_exception(errMsg);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::streampos
|
||||
gzfilebuf::seekoff( std::streamoff, ios_seekdir, ios_openmode )
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -89,6 +89,15 @@ public:
|
||||
/** @return true if open, false otherwise */
|
||||
bool is_open() const { return (file != NULL); }
|
||||
|
||||
/**
|
||||
* @return the current offset in the file being read or written.
|
||||
* The offset corresponds to compressed data if the file is compressed,
|
||||
* and is influenced by buffering performed in zlib, hence the "approx"
|
||||
* qualifier. It should be suitable for progress indicators and such,
|
||||
* though.
|
||||
*/
|
||||
z_off_t approxOffset();
|
||||
|
||||
/** @return stream position */
|
||||
virtual std::streampos seekoff( std::streamoff off, ios_seekdir way, ios_openmode which );
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#ifndef _NAREF_H
|
||||
#define _NAREF_H
|
||||
|
||||
#if (defined(__x86_64) && defined(__linux__)) || defined(__sparcv9) || \
|
||||
defined(__powerpc64__)
|
||||
#if (defined(__x86_64) && defined(__linux__))
|
||||
/* NASAL_NAN64 mode requires 64 bit pointers that only use the
|
||||
* lower 48 bits; Win64 and Irix should work with this too, but
|
||||
* lower 48 bits; x86 Win64 and Irix should work with this too, but
|
||||
* have not been tested */
|
||||
# define NASAL_NAN64
|
||||
#elif defined(__BYTE_ORDER__)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
@@ -150,6 +150,43 @@ int parseTest()
|
||||
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
COMPARE(p2->description(), "A plane made by Cessna");
|
||||
|
||||
pkg::Package::ThumbnailVec thumbs = p2->thumbnailsForVariant(0);
|
||||
COMPARE(thumbs.size(), 3);
|
||||
|
||||
auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
|
||||
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
|
||||
VERIFY(index != thumbs.end());
|
||||
COMPARE(index->path, "thumb-exterior.png");
|
||||
COMPARE(index->url, "http://foo.bar.com/thumb-exterior.png");
|
||||
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
|
||||
|
||||
index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
|
||||
{ return (t.type == pkg::Package::Thumbnail::Type::PANEL); });
|
||||
VERIFY(index != thumbs.end());
|
||||
COMPARE(index->path, "thumb-panel.png");
|
||||
COMPARE(index->url, "http://foo.bar.com/thumb-panel.png");
|
||||
VERIFY(index->type == pkg::Package::Thumbnail::Type::PANEL);
|
||||
|
||||
// test variants
|
||||
try {
|
||||
p2->indexOfVariant("fofofo");
|
||||
SG_TEST_FAIL("lookup of non-existant variant did not throw");
|
||||
} catch (sg_exception& e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
unsigned int skisVariant = p2->indexOfVariant("c172p-skis");
|
||||
VERIFY(skisVariant > 0);
|
||||
|
||||
pkg::Package::ThumbnailVec thumbs2 = p2->thumbnailsForVariant(skisVariant);
|
||||
COMPARE(thumbs2.size(), 2);
|
||||
|
||||
index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Thumbnail& t)
|
||||
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
|
||||
VERIFY(index != thumbs2.end());
|
||||
COMPARE(index->path, "thumb-exterior-skis.png");
|
||||
COMPARE(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
|
||||
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
|
||||
|
||||
|
||||
// test filtering / searching too
|
||||
@@ -208,7 +245,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 +440,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[])
|
||||
{
|
||||
@@ -418,13 +491,15 @@ int main(int argc, char* argv[])
|
||||
parseTest();
|
||||
|
||||
testInstallPackage(&cl);
|
||||
|
||||
|
||||
testUninstall(&cl);
|
||||
|
||||
testRemoveCatalog(&cl);
|
||||
|
||||
testRefreshCatalog(&cl);
|
||||
|
||||
testInstallTarPackage(&cl);
|
||||
|
||||
std::cout << "Successfully passed all tests!" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,11 @@ namespace pkg
|
||||
|
||||
class Install;
|
||||
class Catalog;
|
||||
class Package;
|
||||
|
||||
typedef SGSharedPtr<Catalog> CatalogRef;
|
||||
typedef SGSharedPtr<Install> InstallRef;
|
||||
typedef SGSharedPtr<Package> PackageRef;
|
||||
|
||||
/**
|
||||
* package delegate is the mechanism to discover progress / completion /
|
||||
@@ -68,6 +70,8 @@ public:
|
||||
virtual void installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal) = 0;
|
||||
virtual void finishInstall(InstallRef aInstall, StatusCode aReason) = 0;
|
||||
|
||||
virtual void finishUninstall(const PackageRef& aPackage) {};
|
||||
|
||||
/**
|
||||
* Notification when catalogs/packages are added or removed
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
namespace pkg {
|
||||
|
||||
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
|
||||
@@ -103,7 +103,7 @@ bool Package::matches(const SGPropertyNode* aFilter) const
|
||||
else
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
|
||||
} // of filter props iteration
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ InstallRef Package::install()
|
||||
if (ins) {
|
||||
return ins;
|
||||
}
|
||||
|
||||
|
||||
// start a new install
|
||||
ins = new Install(this, pathOnDisk());
|
||||
m_catalog->root()->scheduleToUpdate(ins);
|
||||
@@ -190,10 +190,10 @@ unsigned int Package::revision() const
|
||||
if (!m_props) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return m_props->getIntValue("revision");
|
||||
}
|
||||
|
||||
|
||||
std::string Package::name() const
|
||||
{
|
||||
return m_props->getStringValue("name");
|
||||
@@ -203,7 +203,7 @@ size_t Package::fileSizeBytes() const
|
||||
{
|
||||
return m_props->getIntValue("file-size-bytes");
|
||||
}
|
||||
|
||||
|
||||
std::string Package::description() const
|
||||
{
|
||||
return getLocalisedProp("description");
|
||||
@@ -213,7 +213,7 @@ string_set Package::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode* Package::properties() const
|
||||
{
|
||||
return m_props.ptr();
|
||||
@@ -225,7 +225,7 @@ string_list Package::thumbnailUrls() const
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
@@ -238,20 +238,20 @@ string_list Package::thumbnails() const
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
string_list Package::downloadUrls() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
@@ -272,41 +272,41 @@ std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char*
|
||||
return localeRoot->getStringValue(aName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return aRoot->getStringValue(aName);
|
||||
}
|
||||
|
||||
PackageList Package::dependencies() const
|
||||
{
|
||||
PackageList result;
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
|
||||
std::string depName = dep->getStringValue("id");
|
||||
unsigned int rev = dep->getIntValue("revision", 0);
|
||||
|
||||
|
||||
// prefer local hangar package if possible, in case someone does something
|
||||
// silly with naming. Of course flightgear's aircraft search doesn't know
|
||||
// about hangars, so names still need to be unique.
|
||||
PackageRef depPkg = m_catalog->getPackageById(depName);
|
||||
if (!depPkg) {
|
||||
if (!depPkg) {
|
||||
Root* rt = m_catalog->root();
|
||||
depPkg = rt->getPackageById(depName);
|
||||
if (!depPkg) {
|
||||
throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (depPkg->revision() < rev) {
|
||||
throw sg_range_exception("Couldn't find suitable revision of " + depName);
|
||||
}
|
||||
|
||||
|
||||
// forbid recursive dependency graphs, we don't need that level
|
||||
// of complexity for aircraft resources
|
||||
assert(depPkg->dependencies() == PackageList());
|
||||
|
||||
|
||||
result.push_back(depPkg);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -338,6 +338,24 @@ std::string Package::nameForVariant(const std::string& vid) const
|
||||
throw sg_exception("Unknow variant +" + vid + " in package " + id());
|
||||
}
|
||||
|
||||
unsigned int Package::indexOfVariant(const std::string& vid) const
|
||||
{
|
||||
if (vid == id()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int result = 1;
|
||||
for (SGPropertyNode* var : m_props->getChildren("variant")) {
|
||||
if (var->getStringValue("id") == vid) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result++;
|
||||
}
|
||||
|
||||
throw sg_exception("Unknow variant " + vid + " in package " + id());
|
||||
}
|
||||
|
||||
std::string Package::nameForVariant(const unsigned int vIndex) const
|
||||
{
|
||||
if (vIndex == 0)
|
||||
@@ -351,6 +369,48 @@ std::string Package::nameForVariant(const unsigned int vIndex) const
|
||||
throw sg_exception("Unknow variant in package " + id());
|
||||
}
|
||||
|
||||
Package::ThumbnailVec Package::thumbnailsForVariant(unsigned int vIndex) const
|
||||
{
|
||||
if (vIndex == 0) {
|
||||
return thumbnailsFromProps(m_props);
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
|
||||
if (!var) {
|
||||
throw sg_exception("Unknow variant in package " + id());
|
||||
}
|
||||
|
||||
return thumbnailsFromProps(var);
|
||||
}
|
||||
|
||||
Package::Thumbnail::Type thumbnailTypeFromString(const std::string& s)
|
||||
{
|
||||
if (s == "exterior") return Package::Thumbnail::Type::EXTERIOR;
|
||||
if (s == "interior") return Package::Thumbnail::Type::INTERIOR;
|
||||
if (s == "panel") return Package::Thumbnail::Type::PANEL;
|
||||
return Package::Thumbnail::Type::UNKNOWN;
|
||||
}
|
||||
|
||||
Package::Thumbnail::Thumbnail(const std::string& aUrl, const std::string& aPath, Type aType) :
|
||||
url(aUrl),
|
||||
path(aPath),
|
||||
type(aType)
|
||||
{
|
||||
}
|
||||
|
||||
Package::ThumbnailVec Package::thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const
|
||||
{
|
||||
ThumbnailVec result;
|
||||
|
||||
for (auto thumbNode : ptr->getChildren("thumbnail")) {
|
||||
Thumbnail t(thumbNode->getStringValue("url"),
|
||||
thumbNode->getStringValue("path"),
|
||||
thumbnailTypeFromString(thumbNode->getStringValue("type")));
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
|
||||
@@ -80,7 +80,12 @@ public:
|
||||
* Fully-qualified ID, including our catalog'd ID
|
||||
*/
|
||||
std::string qualifiedVariantId(const unsigned int variantIndex) const;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
unsigned int indexOfVariant(const std::string& vid) const;
|
||||
|
||||
/**
|
||||
* human-readable name - note this is probably not localised,
|
||||
* although this is not ruled out for the future.
|
||||
@@ -133,6 +138,35 @@ public:
|
||||
* thumbnail file paths within the package on disk
|
||||
*/
|
||||
string_list thumbnails() const;
|
||||
|
||||
/**
|
||||
* information about a thumbnail
|
||||
*/
|
||||
struct Thumbnail {
|
||||
enum class Type
|
||||
{
|
||||
UNKNOWN,
|
||||
PANEL,
|
||||
INTERIOR,
|
||||
EXTERIOR
|
||||
|
||||
// NIGHT / GROUND as modifiers? does this add any
|
||||
// actual value for GUIs?
|
||||
};
|
||||
|
||||
Thumbnail(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN);
|
||||
|
||||
std::string url;
|
||||
std::string path;
|
||||
Type type = Type::UNKNOWN;
|
||||
};
|
||||
|
||||
typedef std::vector<Thumbnail> ThumbnailVec;
|
||||
|
||||
/**
|
||||
* retrieve all the thumbnails for a variant
|
||||
*/
|
||||
ThumbnailVec thumbnailsForVariant(unsigned int vIndex) const;
|
||||
|
||||
/**
|
||||
* Packages we depend upon.
|
||||
@@ -159,7 +193,9 @@ private:
|
||||
void updateFromProps(const SGPropertyNode* aProps);
|
||||
|
||||
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
|
||||
|
||||
|
||||
ThumbnailVec thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const;
|
||||
|
||||
SGPropertyNode_ptr m_props;
|
||||
std::string m_id;
|
||||
string_set m_tags;
|
||||
|
||||
@@ -153,6 +153,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void fireFinishUninstall(PackageRef pkg)
|
||||
{
|
||||
std::for_each(delegates.begin(), delegates.end(),
|
||||
[pkg](Delegate* d) {d->finishUninstall(pkg);});
|
||||
}
|
||||
|
||||
DelegateVec delegates;
|
||||
|
||||
SGPath path;
|
||||
@@ -384,7 +390,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);
|
||||
}
|
||||
@@ -652,6 +658,7 @@ void Root::unregisterInstall(InstallRef ins)
|
||||
}
|
||||
|
||||
d->m_installs.erase(ins->package());
|
||||
d->fireFinishUninstall(ins->package());
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
BIN
simgear/package/catalogTest1/b737.tar.gz
Normal file
BIN
simgear/package/catalogTest1/b737.tar.gz
Normal file
Binary file not shown.
7
simgear/package/catalogTest1/b737NG/b737-700-set.xml
Normal file
7
simgear/package/catalogTest1/b737NG/b737-700-set.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<sim>
|
||||
<description>Boeing 737-700</description>
|
||||
</sim>
|
||||
</PropertyList>
|
||||
7
simgear/package/catalogTest1/b737NG/b737-800-set.xml
Normal file
7
simgear/package/catalogTest1/b737NG/b737-800-set.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<sim>
|
||||
<description>Boeing 737-800</description>
|
||||
</sim>
|
||||
</PropertyList>
|
||||
7
simgear/package/catalogTest1/b737NG/b737-900-set.xml
Normal file
7
simgear/package/catalogTest1/b737NG/b737-900-set.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<sim>
|
||||
<description>Boeing 737-900</description>
|
||||
</sim>
|
||||
</PropertyList>
|
||||
9
simgear/package/catalogTest1/b737NG/b737-common-set.xml
Normal file
9
simgear/package/catalogTest1/b737NG/b737-common-set.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<sim>
|
||||
<tags>
|
||||
<tag>boeing</tag>
|
||||
</tags>
|
||||
</sim>
|
||||
</PropertyList>
|
||||
@@ -48,6 +48,23 @@
|
||||
<revision>10</revision>
|
||||
</depends>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<path>thumb-something.png</path>
|
||||
<url>http://foo.bar.com/thumb-something.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<variant>
|
||||
<id>c172p-2d-panel</id>
|
||||
<name>C172 with 2d panel only</name>
|
||||
@@ -56,11 +73,35 @@
|
||||
<variant>
|
||||
<id>c172p-floats</id>
|
||||
<name>C172 with floats</name>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-floats.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-skis</id>
|
||||
<name>C172 with skis</name>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-skis.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
</variant>
|
||||
|
||||
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||
@@ -78,6 +119,42 @@
|
||||
<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>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
</package>
|
||||
|
||||
|
||||
<package>
|
||||
<id>dc3</id>
|
||||
<name>DC-3</name>
|
||||
|
||||
@@ -48,6 +48,23 @@
|
||||
<revision>10</revision>
|
||||
</depends>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<path>thumb-something.png</path>
|
||||
<url>http://foo.bar.com/thumb-something.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<variant>
|
||||
<id>c172p-2d-panel</id>
|
||||
<name>C172 with 2d panel only</name>
|
||||
@@ -56,11 +73,35 @@
|
||||
<variant>
|
||||
<id>c172p-floats</id>
|
||||
<name>C172 with floats</name>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-floats.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-skis</id>
|
||||
<name>C172 with skis</name>
|
||||
|
||||
<thumbnail>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-skis.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
|
||||
</thumbnail>
|
||||
|
||||
<thumbnail>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</thumbnail>
|
||||
</variant>
|
||||
|
||||
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||
@@ -68,6 +109,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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,12 +38,6 @@ using std::cerr;
|
||||
# include "PropertyInterpolationMgr.hxx"
|
||||
# include "vectorPropTemplates.hxx"
|
||||
|
||||
# if ( _MSC_VER == 1200 )
|
||||
// MSVC 6 is buggy, and needs something strange here
|
||||
using std::vector<SGPropertyNode_ptr>;
|
||||
using std::vector<SGPropertyChangeListener *>;
|
||||
using std::vector<SGPropertyNode *>;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
using std::endl;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user