Compare commits
149 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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" {
|
||||
|
||||
111
CMakeLists.txt
111
CMakeLists.txt
@@ -22,12 +22,25 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
|
||||
# Set the C++ standard to C++98 to avoid compilation errors on GCC 6 (which
|
||||
# defaults to C++14).
|
||||
if(CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set (CMAKE_CXX_FLAGS "-std=gnu++98 ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
else()
|
||||
set (CMAKE_CXX_STANDARD 98)
|
||||
endif()
|
||||
|
||||
project(SimGear)
|
||||
|
||||
# read 'version' file into a variable (stripping any newlines or spaces)
|
||||
file(READ version versionFile)
|
||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
# add a dependency on the versino file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||
|
||||
# use simgear version also as the SO version (if building SOs)
|
||||
@@ -104,21 +117,28 @@ endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system libExpat" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system expat library" OFF)
|
||||
option(SYSTEM_UDNS "Set to ON to build SimGear using the system udns library" OFF)
|
||||
else()
|
||||
# Building SimGear DLLs is currently not supported for MSVC.
|
||||
set(SIMGEAR_SHARED OFF)
|
||||
# Using a system expat is currently not supported for MSVC - it would require shared simgear (DLL).
|
||||
# Using external 3rd party libraries is currently not supported for MSVC - it would require shared simgear (DLL).
|
||||
set(SYSTEM_EXPAT OFF)
|
||||
set(SYSTEM_UDNS OFF)
|
||||
endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
|
||||
|
||||
# until the fstream fix is applied and generally available in OSG,
|
||||
# keep the compatability link option as the default
|
||||
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
if (CMAKE_CL_64)
|
||||
@@ -137,14 +157,17 @@ endif (MSVC)
|
||||
|
||||
if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
|
||||
|
||||
set( OSG_MSVC "msvc" )
|
||||
if (${MSVC_VERSION} EQUAL 1700)
|
||||
if (${MSVC_VERSION} EQUAL 1900)
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}110 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1600)
|
||||
set( OSG_MSVC ${OSG_MSVC}100 )
|
||||
else (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}90 )
|
||||
endif (${MSVC_VERSION} EQUAL 1700)
|
||||
endif ()
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
|
||||
@@ -154,7 +177,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
|
||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
|
||||
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
find_path(BOOST_ROOT boost/version.hpp
|
||||
PATHS
|
||||
${MSVC_ROOT_PARENT_DIR}
|
||||
${MSVC_3RDPARTY_ROOT}/boost
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
||||
@@ -169,8 +196,11 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
)
|
||||
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
|
||||
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
if (NOT USE_AEONWAVE)
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
|
||||
endif()
|
||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
@@ -187,11 +217,9 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
# Somehow this only works if included before searching for Boost...
|
||||
include(BoostTestTargets)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
include(BoostTestTargets)
|
||||
|
||||
if(SIMGEAR_HEADLESS)
|
||||
message(STATUS "SimGear mode: HEADLESS")
|
||||
@@ -201,11 +229,34 @@ else()
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
find_package(OpenAL REQUIRED)
|
||||
if (USE_AEONWAVE)
|
||||
find_package(AAX COMPONENTS aax REQUIRED)
|
||||
include_directories( ${AAX_INCLUDE_DIR} )
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
include_directories( ${OPENAL_INCLUDE_DIR} )
|
||||
endif()
|
||||
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
# ensure OSG was compiled with OSG_USE_UTF8_FILENAME set
|
||||
check_cxx_source_compiles(
|
||||
"#include <osg/Config>
|
||||
#if !defined(OSG_USE_UTF8_FILENAME)
|
||||
#error OSG UTF8 support not enabled
|
||||
#endif
|
||||
int main() { return 0; }"
|
||||
SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
if (NOT SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
message(WARNING "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
|
||||
endif()
|
||||
endif()
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -344,21 +395,26 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# turn off various warnings
|
||||
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
|
||||
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
||||
# endforeach(warning)
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
|
||||
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
|
||||
if (${MSVC_VERSION} GREATER 1599)
|
||||
if (NOT OSG_FSTREAM_EXPORT_FIXED AND ${MSVC_VERSION} GREATER 1599)
|
||||
message(STATUS "For better linking performance, use OSG with patched fstream header")
|
||||
# needed to avoid link errors on multiply-defined standard C++
|
||||
# symbols. Suspect this may be an OSG-DB export bug
|
||||
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
|
||||
endif (${MSVC_VERSION} GREATER 1599)
|
||||
endif ()
|
||||
|
||||
if (${MSVC_VERSION} GREATER 1899)
|
||||
# needed for debug builds with VS2015
|
||||
set( MSVC_FLAGS "${MSVC_FLAGS} /bigobj" )
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
# assumed on Windows
|
||||
set(HAVE_GETLOCALTIME 1)
|
||||
|
||||
set( WINSOCK_LIBRARY "ws2_32.lib" )
|
||||
set( SHLWAPI_LIBRARY "Shlwapi.lib" )
|
||||
set( RT_LIBRARY "winmm" )
|
||||
endif(WIN32)
|
||||
|
||||
@@ -372,10 +428,9 @@ include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
include_directories(
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -403,6 +458,7 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${WINSOCK_LIBRARY}
|
||||
${SHLWAPI_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
@@ -416,9 +472,16 @@ endif()
|
||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||
|
||||
include_directories(3rdparty/utf8/source)
|
||||
if (ENABLE_DNS)
|
||||
message(STATUS "DNS resolver: ENABLED")
|
||||
include_directories(3rdparty/udns)
|
||||
|
||||
if(ENABLE_DNS)
|
||||
if(SYSTEM_UDNS)
|
||||
message(STATUS "Requested to use system udns library, forcing SIMGEAR_SHARED to true")
|
||||
set(SIMGEAR_SHARED ON)
|
||||
find_package(Udns REQUIRED)
|
||||
else()
|
||||
message(STATUS "DNS resolver: ENABLED")
|
||||
include_directories(3rdparty/udns)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "DNS resolver: DISABLED")
|
||||
endif()
|
||||
|
||||
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 ()
|
||||
@@ -128,11 +128,20 @@ target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
|
||||
if(SYSTEM_EXPAT)
|
||||
target_link_libraries(SimGearCore
|
||||
${EXPAT_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(ENABLE_DNS AND SYSTEM_UDNS)
|
||||
target_link_libraries(SimGearCore
|
||||
${UDNS_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
# if __GNUC__ < 3
|
||||
# error Time to upgrade. GNU compilers < 3.0 not supported
|
||||
# elif (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
|
||||
# warning GCC compilers prior to 3.4 are suspect
|
||||
# warning GCC compilers prior to 3.4 are suspect
|
||||
# endif
|
||||
|
||||
# define SG_GCC_VERSION (__GNUC__ * 10000 \
|
||||
@@ -61,29 +61,33 @@
|
||||
// Microsoft compilers.
|
||||
//
|
||||
#ifdef _MSC_VER
|
||||
# define bcopy(from, to, n) memcpy(to, from, n)
|
||||
# define strcasecmp stricmp
|
||||
|
||||
# if _MSC_VER >= 1200 // msvc++ 6.0 or greater
|
||||
# define isnan _isnan
|
||||
# define snprintf _snprintf
|
||||
# if _MSC_VER >= 1200 // msvc++ 6.0 up to MSVC2013
|
||||
# if _MSC_VER < 1900
|
||||
# define bcopy(from, to, n) memcpy(to, from, n)
|
||||
# define snprintf _snprintf
|
||||
# define strdup _strdup
|
||||
# define copysign _copysign
|
||||
# endif
|
||||
# if _MSC_VER < 1800
|
||||
# define isnan _isnan
|
||||
# endif
|
||||
# if _MSC_VER < 1500
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
# define copysign _copysign
|
||||
# define strcasecmp stricmp
|
||||
|
||||
# undef min
|
||||
# undef max
|
||||
|
||||
# pragma warning(disable: 4786) // identifier was truncated to '255' characters
|
||||
# pragma warning(disable: 4244) // conversion from double to float
|
||||
# pragma warning(disable: 4305) //
|
||||
# pragma warning(disable: 4305) // truncation from larger type to smaller
|
||||
# pragma warning(disable: 4267) // conversion from size_t to int / 32-bit type
|
||||
# pragma warning(disable: 4996) // don't require _ prefix for standard library functions
|
||||
# pragma warning(disable: 4800) // don't warn about int -> bool performance
|
||||
|
||||
# else
|
||||
# error What version of MSVC++ is this?
|
||||
# endif
|
||||
|
||||
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
|
||||
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
@@ -201,4 +205,3 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
//
|
||||
|
||||
#endif // _SG_COMPILER_H
|
||||
|
||||
|
||||
@@ -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,12 @@
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#ifdef SG_WINDOWS
|
||||
#if defined (SG_WINDOWS)
|
||||
// for AllocConsole, OutputDebugString
|
||||
#include "windows.h"
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
@@ -106,13 +107,13 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
class FileLogCallback : public simgear::LogCallback
|
||||
{
|
||||
public:
|
||||
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p),
|
||||
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
|
||||
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& message)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
@@ -120,28 +121,29 @@ public:
|
||||
<< ":" << file << ":" << line << ":" << message << std::endl;
|
||||
}
|
||||
private:
|
||||
std::ofstream m_file;
|
||||
sg_ofstream m_file;
|
||||
};
|
||||
|
||||
|
||||
class StderrLogCallback : public simgear::LogCallback
|
||||
{
|
||||
public:
|
||||
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
AllocConsole(); // but only if we want a console
|
||||
freopen("conin$", "r", stdin);
|
||||
freopen("conout$", "w", stdout);
|
||||
freopen("conout$", "w", stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
#if defined (SG_WINDOWS)
|
||||
~StderrLogCallback()
|
||||
{
|
||||
FreeConsole();
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& aMessage)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
|
||||
|
||||
fprintf(stderr, "%s\n", aMessage.c_str());
|
||||
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
||||
// file, line, aMessage.c_str());
|
||||
@@ -159,12 +161,12 @@ public:
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& aMessage)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
|
||||
|
||||
std::ostringstream os;
|
||||
os << debugClassToString(c) << ":" << aMessage << std::endl;
|
||||
OutputDebugStringA(os.str().c_str());
|
||||
@@ -188,16 +190,16 @@ private:
|
||||
const char* f, int l, const std::string& msg) :
|
||||
debugClass(c), debugPriority(p), file(f), line(l),
|
||||
message(msg)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
sgDebugClass debugClass;
|
||||
sgDebugPriority debugPriority;
|
||||
const char* file;
|
||||
int line;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
|
||||
class PauseThread
|
||||
{
|
||||
public:
|
||||
@@ -205,7 +207,7 @@ private:
|
||||
{
|
||||
m_wasRunning = m_parent->stop();
|
||||
}
|
||||
|
||||
|
||||
~PauseThread()
|
||||
{
|
||||
if (m_wasRunning) {
|
||||
@@ -218,29 +220,46 @@ private:
|
||||
};
|
||||
public:
|
||||
LogStreamPrivate() :
|
||||
m_logClass(SG_ALL),
|
||||
m_logClass(SG_ALL),
|
||||
m_logPriority(SG_ALERT),
|
||||
m_isRunning(false),
|
||||
m_consoleRequested(false)
|
||||
{
|
||||
|
||||
#if !defined(SG_WINDOWS)
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
m_consoleRequested = true;
|
||||
m_isRunning(false)
|
||||
{
|
||||
bool addStderr = true;
|
||||
#if defined (SG_WINDOWS)
|
||||
// Check for stream redirection, has to be done before we call
|
||||
// Attach / AllocConsole
|
||||
const bool isFile = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_DISK); // Redirect to file?
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
|
||||
// attach failed, don't install the callback
|
||||
addStderr = false;
|
||||
} else if (!isFile) {
|
||||
// No - OK! now set streams to attached console
|
||||
freopen("conout$", "w", stdout);
|
||||
freopen("conout$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (addStderr) {
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
}
|
||||
#if defined (SG_WINDOWS) && !defined(NDEBUG)
|
||||
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
~LogStreamPrivate()
|
||||
{
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
|
||||
SGMutex m_lock;
|
||||
SGBlockingQueue<LogEntry> m_entries;
|
||||
|
||||
|
||||
typedef std::vector<simgear::LogCallback*> CallbackVec;
|
||||
CallbackVec m_callbacks;
|
||||
CallbackVec m_callbacks;
|
||||
/// subset of callbacks which correspond to stdout / console,
|
||||
/// and hence should dynamically reflect console logging settings
|
||||
CallbackVec m_consoleCallbacks;
|
||||
@@ -248,8 +267,7 @@ public:
|
||||
sgDebugClass m_logClass;
|
||||
sgDebugPriority m_logPriority;
|
||||
bool m_isRunning;
|
||||
bool m_consoleRequested;
|
||||
|
||||
|
||||
void startLog()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
@@ -257,7 +275,7 @@ public:
|
||||
m_isRunning = true;
|
||||
start();
|
||||
}
|
||||
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
while (1) {
|
||||
@@ -267,37 +285,37 @@ public:
|
||||
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// submit to each installed callback in turn
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
}
|
||||
} // of main thread loop
|
||||
}
|
||||
|
||||
|
||||
bool stop()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
if (!m_isRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// log a special marker value, which will cause the thread to wakeup,
|
||||
// and then exit
|
||||
log(SG_NONE, SG_ALERT, "done", -1, "");
|
||||
join();
|
||||
|
||||
|
||||
m_isRunning = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
PauseThread pause(this);
|
||||
m_callbacks.push_back(cb);
|
||||
}
|
||||
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
PauseThread pause(this);
|
||||
@@ -306,7 +324,7 @@ public:
|
||||
m_callbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
PauseThread pause(this);
|
||||
@@ -316,37 +334,26 @@ public:
|
||||
cb->setLogLevels(c, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
if (p >= SG_INFO) return true;
|
||||
return ((c & m_logClass) != 0 && p >= m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
LogEntry entry(c, p, fileName, line, msg);
|
||||
m_entries.push(entry);
|
||||
}
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
PauseThread pause(this);
|
||||
if (m_consoleRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_consoleRequested = true;
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static logstream* global_logstream = NULL;
|
||||
static LogStreamPrivate* global_privateLogstream = NULL;
|
||||
static SGMutex global_logStreamLock;
|
||||
|
||||
logstream::logstream()
|
||||
{
|
||||
@@ -354,6 +361,13 @@ logstream::logstream()
|
||||
global_privateLogstream->startLog();
|
||||
}
|
||||
|
||||
logstream::~logstream()
|
||||
{
|
||||
popup_msgs.clear();
|
||||
global_privateLogstream->stop();
|
||||
delete global_privateLogstream;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
@@ -362,13 +376,13 @@ logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
{
|
||||
global_privateLogstream->addCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
{
|
||||
global_privateLogstream->removeCallback(cb);
|
||||
}
|
||||
|
||||
@@ -379,6 +393,30 @@ logstream::log( sgDebugClass c, sgDebugPriority p,
|
||||
global_privateLogstream->log(c, p, fileName, line, msg);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::popup( const std::string& msg)
|
||||
{
|
||||
popup_msgs.push_back(msg);
|
||||
}
|
||||
|
||||
std::string
|
||||
logstream::get_popup()
|
||||
{
|
||||
std::string rv = "";
|
||||
if (!popup_msgs.empty())
|
||||
{
|
||||
rv = popup_msgs.front();
|
||||
popup_msgs.erase(popup_msgs.begin());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
logstream::has_popup()
|
||||
{
|
||||
return (popup_msgs.size() > 0) ? true : false;
|
||||
}
|
||||
|
||||
bool
|
||||
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
@@ -390,7 +428,7 @@ logstream::get_log_classes() const
|
||||
{
|
||||
return global_privateLogstream->m_logClass;
|
||||
}
|
||||
|
||||
|
||||
sgDebugPriority
|
||||
logstream::get_log_priority() const
|
||||
{
|
||||
@@ -402,25 +440,25 @@ logstream::set_log_priority( sgDebugPriority p)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::set_log_classes( sgDebugClass c)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
logstream&
|
||||
sglog()
|
||||
{
|
||||
// Force initialization of cerr.
|
||||
static std::ios_base::Init initializer;
|
||||
|
||||
|
||||
// http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
|
||||
// in the absence of portable memory barrier ops in Simgear,
|
||||
// let's keep this correct & safe
|
||||
static SGMutex m;
|
||||
SGGuard<SGMutex> g(m);
|
||||
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
|
||||
if( !global_logstream )
|
||||
global_logstream = new logstream();
|
||||
return *global_logstream;
|
||||
@@ -429,7 +467,7 @@ sglog()
|
||||
void
|
||||
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
@@ -437,8 +475,14 @@ namespace simgear
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
sglog(); // force creation
|
||||
global_privateLogstream->requestConsole();
|
||||
// this is a no-op now, stub exists for compatability for the moment.
|
||||
}
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
delete global_logstream;
|
||||
global_logstream = 0;
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ctime> // for time_t
|
||||
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
|
||||
@@ -80,13 +80,13 @@ public:
|
||||
// see https://curl.haxx.se/libcurl/c/CURLMOPT_PIPELINING.html
|
||||
// we request HTTP 1.1 pipelining
|
||||
curl_multi_setopt(curlMulti, CURLMOPT_PIPELINING, 1 /* aka CURLPIPE_HTTP1 */);
|
||||
#if (LIBCURL_VERSION_MINOR >= 30)
|
||||
curl_multi_setopt(curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long) maxConnections);
|
||||
curl_multi_setopt(curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH,
|
||||
(long) maxPipelineDepth);
|
||||
curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS,
|
||||
(long) maxHostConnections);
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef std::map<Request_ptr, CURL*> RequestCurlMap;
|
||||
@@ -138,24 +138,38 @@ Client::~Client()
|
||||
void Client::setMaxConnections(unsigned int maxCon)
|
||||
{
|
||||
d->maxConnections = maxCon;
|
||||
#if (LIBCURL_VERSION_MINOR >= 30)
|
||||
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long) maxCon);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Client::setMaxHostConnections(unsigned int maxHostCon)
|
||||
{
|
||||
d->maxHostConnections = maxHostCon;
|
||||
#if (LIBCURL_VERSION_MINOR >= 30)
|
||||
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS, (long) maxHostCon);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Client::setMaxPipelineDepth(unsigned int depth)
|
||||
{
|
||||
d->maxPipelineDepth = depth;
|
||||
#if (LIBCURL_VERSION_MINOR >= 30)
|
||||
curl_multi_setopt(d->curlMulti, CURLMOPT_MAX_PIPELINE_LENGTH, (long) depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Client::update(int waitTimeout)
|
||||
{
|
||||
int remainingActive, messagesInQueue;
|
||||
if (d->requests.empty()) {
|
||||
// curl_multi_wait returns immediately if there's no requests active,
|
||||
// but that can cause high CPU usage for us.
|
||||
SGTimeStamp::sleepForMSec(waitTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
int remainingActive, messagesInQueue, numFds;
|
||||
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
|
||||
curl_multi_perform(d->curlMulti, &remainingActive);
|
||||
|
||||
CURLMsg* msg;
|
||||
@@ -196,7 +210,6 @@ void Client::update(int waitTimeout)
|
||||
SG_LOG(SG_IO, SG_ALERT, "unknown CurlMSG:" << msg->msg);
|
||||
}
|
||||
} // of curl message processing loop
|
||||
SGTimeStamp::sleepForMSec(waitTimeout);
|
||||
}
|
||||
|
||||
void Client::makeRequest(const Request_ptr& r)
|
||||
@@ -231,6 +244,8 @@ void Client::makeRequest(const Request_ptr& r)
|
||||
curl_easy_setopt(curlRequest, CURLOPT_USERAGENT, d->userAgent.c_str());
|
||||
curl_easy_setopt(curlRequest, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||
|
||||
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
||||
if (!d->proxy.empty()) {
|
||||
curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str());
|
||||
curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort);
|
||||
@@ -300,7 +315,9 @@ void Client::cancelRequest(const Request_ptr &r, std::string reason)
|
||||
}
|
||||
|
||||
CURLMcode err = curl_multi_remove_handle(d->curlMulti, it->second);
|
||||
assert(err == CURLM_OK);
|
||||
if (err != CURLM_OK) {
|
||||
SG_LOG(SG_IO, SG_WARN, "curl_multi_remove_handle failed:" << err);
|
||||
}
|
||||
|
||||
// clear the request pointer form the curl-easy object
|
||||
curl_easy_setopt(it->second, CURLOPT_PRIVATE, 0);
|
||||
|
||||
@@ -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;
|
||||
@@ -94,7 +92,7 @@ public:
|
||||
struct Failure
|
||||
{
|
||||
SGPath path;
|
||||
AbstractRepository::ResultCode error;
|
||||
HTTPRepository::ResultCode error;
|
||||
};
|
||||
|
||||
typedef std::vector<Failure> FailureList;
|
||||
@@ -104,7 +102,8 @@ public:
|
||||
hashCacheDirty(false),
|
||||
p(parent),
|
||||
isUpdating(false),
|
||||
status(AbstractRepository::REPO_NO_ERROR),
|
||||
updateEverything(false),
|
||||
status(HTTPRepository::REPO_NO_ERROR),
|
||||
totalDownloaded(0)
|
||||
{ ; }
|
||||
|
||||
@@ -115,10 +114,14 @@ public:
|
||||
std::string baseUrl;
|
||||
SGPath basePath;
|
||||
bool isUpdating;
|
||||
AbstractRepository::ResultCode status;
|
||||
bool updateEverything;
|
||||
string_list updatePaths;
|
||||
HTTPRepository::ResultCode status;
|
||||
HTTPDirectory* rootDir;
|
||||
size_t totalDownloaded;
|
||||
|
||||
void updateWaiting();
|
||||
|
||||
HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
|
||||
size_t sz);
|
||||
HTTP::Request_ptr updateDir(HTTPDirectory* dir, const std::string& hash,
|
||||
@@ -130,9 +133,9 @@ public:
|
||||
std::string computeHashForPath(const SGPath& p);
|
||||
void writeHashCache();
|
||||
|
||||
void failedToGetRootIndex(AbstractRepository::ResultCode st);
|
||||
void failedToGetRootIndex(HTTPRepository::ResultCode st);
|
||||
void failedToUpdateChild(const SGPath& relativePath,
|
||||
AbstractRepository::ResultCode fileStatus);
|
||||
HTTPRepository::ResultCode fileStatus);
|
||||
|
||||
typedef std::vector<RepoRequestPtr> RequestVector;
|
||||
RequestVector queuedRequests,
|
||||
@@ -147,6 +150,7 @@ public:
|
||||
typedef std::vector<HTTPDirectory*> DirectoryVector;
|
||||
DirectoryVector directories;
|
||||
|
||||
SGPath installedCopyPath;
|
||||
};
|
||||
|
||||
class HTTPDirectory
|
||||
@@ -192,10 +196,12 @@ class HTTPDirectory
|
||||
typedef std::vector<ChildInfo> ChildInfoList;
|
||||
ChildInfoList children;
|
||||
|
||||
|
||||
public:
|
||||
HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
|
||||
_repository(repo),
|
||||
_relativePath(path)
|
||||
_relativePath(path),
|
||||
_state(DoNotUpdate)
|
||||
{
|
||||
assert(repo);
|
||||
|
||||
@@ -219,27 +225,30 @@ public:
|
||||
|
||||
std::string url() const
|
||||
{
|
||||
if (_relativePath.str().empty()) {
|
||||
if (_relativePath.empty()) {
|
||||
return _repository->baseUrl;
|
||||
}
|
||||
|
||||
return _repository->baseUrl + "/" + _relativePath.str();
|
||||
return _repository->baseUrl + "/" + _relativePath;
|
||||
}
|
||||
|
||||
void dirIndexUpdated(const std::string& hash)
|
||||
{
|
||||
SGPath fpath(_relativePath);
|
||||
SGPath fpath(absolutePath());
|
||||
fpath.append(".dirindex");
|
||||
_repository->updatedFileContents(fpath, hash);
|
||||
|
||||
_state = Updated;
|
||||
|
||||
children.clear();
|
||||
parseDirIndex(children);
|
||||
std::sort(children.begin(), children.end());
|
||||
}
|
||||
|
||||
void failedToUpdate(AbstractRepository::ResultCode status)
|
||||
void failedToUpdate(HTTPRepository::ResultCode status)
|
||||
{
|
||||
if (_relativePath.isNull()) {
|
||||
_state = UpdateFailed;
|
||||
if (_relativePath.empty()) {
|
||||
// root dir failed
|
||||
_repository->failedToGetRootIndex(status);
|
||||
} else {
|
||||
@@ -247,9 +256,63 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void copyInstalledChildren()
|
||||
{
|
||||
if (_repository->installedCopyPath.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
string_list indexNames = indexChildren();
|
||||
const_string_list_iterator nameIt = indexNames.begin();
|
||||
for (; nameIt != indexNames.end(); ++nameIt) {
|
||||
SGPath p(absolutePath());
|
||||
p.append(*nameIt);
|
||||
if (p.exists()) {
|
||||
continue; // only copy if the file is missing entirely
|
||||
}
|
||||
|
||||
ChildInfoList::iterator c = findIndexChild(*nameIt);
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
continue; // only care about files
|
||||
}
|
||||
|
||||
SGPath cp = _repository->installedCopyPath;
|
||||
cp.append(relativePath());
|
||||
cp.append(*nameIt);
|
||||
if (!cp.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_BULK, "new child, copying existing file" << cp << p);
|
||||
|
||||
SGBinaryFile src(cp);
|
||||
SGBinaryFile dst(p);
|
||||
src.open(SG_IO_IN);
|
||||
dst.open(SG_IO_OUT);
|
||||
|
||||
char* buf = (char*) malloc(cp.sizeInBytes());
|
||||
if (!buf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
src.read(buf, cp.sizeInBytes());
|
||||
dst.write(buf, cp.sizeInBytes());
|
||||
src.close();
|
||||
dst.close();
|
||||
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void updateChildrenBasedOnHash()
|
||||
{
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
|
||||
// if we got here for a dir which is still updating or excluded
|
||||
// from updates, just bail out right now.
|
||||
if (_state != Updated) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyInstalledChildren();
|
||||
|
||||
string_list indexNames = indexChildren(),
|
||||
toBeUpdated, orphans;
|
||||
@@ -281,9 +344,10 @@ public:
|
||||
// perform a recursive check.
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << it->file() );
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
SGPath p(relativePath());
|
||||
p.append(it->file());
|
||||
HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
|
||||
HTTPDirectory* childDir = childDirectory(it->file());
|
||||
if (childDir->_state == NotUpdated) {
|
||||
childDir->_state = Updated;
|
||||
}
|
||||
childDir->updateChildrenBasedOnHash();
|
||||
}
|
||||
}
|
||||
@@ -301,6 +365,101 @@ public:
|
||||
scheduleUpdates(toBeUpdated);
|
||||
}
|
||||
|
||||
void markAsUpToDate()
|
||||
{
|
||||
_state = Updated;
|
||||
}
|
||||
|
||||
void markAsUpdating()
|
||||
{
|
||||
assert(_state == NotUpdated);
|
||||
_state = HTTPDirectory::UpdateInProgress;
|
||||
}
|
||||
|
||||
void markAsEnabled()
|
||||
{
|
||||
// assert because this should only get invoked on newly created
|
||||
// directory objects which are inside the sub-tree(s) to be updated
|
||||
assert(_state == DoNotUpdate);
|
||||
_state = NotUpdated;
|
||||
}
|
||||
|
||||
void markSubtreeAsNeedingUpdate()
|
||||
{
|
||||
if (_state == Updated) {
|
||||
_state = NotUpdated; // reset back to not-updated
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->markSubtreeAsNeedingUpdate();
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
void markSubtreeAsEnabled()
|
||||
{
|
||||
if (_state == DoNotUpdate) {
|
||||
markAsEnabled();
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->markSubtreeAsEnabled();
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
|
||||
void markAncestorChainAsEnabled()
|
||||
{
|
||||
if (_state == DoNotUpdate) {
|
||||
markAsEnabled();
|
||||
}
|
||||
|
||||
if (_relativePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string prPath = SGPath(_relativePath).dir();
|
||||
if (prPath.empty()) {
|
||||
_repository->rootDir->markAncestorChainAsEnabled();
|
||||
} else {
|
||||
HTTPDirectory* prDir = _repository->getOrCreateDirectory(prPath);
|
||||
prDir->markAncestorChainAsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
void updateIfWaiting(const std::string& hash, size_t sz)
|
||||
{
|
||||
if (_state == NotUpdated) {
|
||||
_repository->updateDir(this, hash, sz);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_state == DoNotUpdate) || (_state == UpdateInProgress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->updateIfWaiting(cit->hash, cit->sizeInBytes);
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
HTTPDirectory* childDirectory(const std::string& name)
|
||||
{
|
||||
std::string childPath = relativePath().empty() ? name : relativePath() + "/" + name;
|
||||
return _repository->getOrCreateDirectory(childPath);
|
||||
}
|
||||
|
||||
void removeOrphans(const string_list& orphans)
|
||||
{
|
||||
string_list::const_iterator it;
|
||||
@@ -334,9 +493,12 @@ public:
|
||||
if (cit->type == ChildInfo::FileType) {
|
||||
_repository->updateFile(this, *it, cit->sizeInBytes);
|
||||
} else {
|
||||
SGPath p(relativePath());
|
||||
p.append(*it);
|
||||
HTTPDirectory* childDir = _repository->getOrCreateDirectory(p.str());
|
||||
HTTPDirectory* childDir = childDirectory(*it);
|
||||
if (childDir->_state == DoNotUpdate) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, child:" << *it << " is marked do not update so skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
_repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
|
||||
}
|
||||
}
|
||||
@@ -345,11 +507,11 @@ public:
|
||||
SGPath absolutePath() const
|
||||
{
|
||||
SGPath r(_repository->basePath);
|
||||
r.append(_relativePath.str());
|
||||
r.append(_relativePath);
|
||||
return r;
|
||||
}
|
||||
|
||||
SGPath relativePath() const
|
||||
std::string relativePath() const
|
||||
{
|
||||
return _relativePath;
|
||||
}
|
||||
@@ -361,21 +523,22 @@ public:
|
||||
if (it == children.end()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
|
||||
} else {
|
||||
SGPath fpath(_relativePath);
|
||||
SGPath fpath(absolutePath());
|
||||
fpath.append(file);
|
||||
|
||||
if (it->hash != hash) {
|
||||
_repository->failedToUpdateChild(_relativePath, AbstractRepository::REPO_ERROR_CHECKSUM);
|
||||
// we don't erase the file on a hash mismatch, becuase if we're syncing during the
|
||||
// middle of a server-side update, the downloaded file may actually become valid.
|
||||
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
|
||||
} else {
|
||||
_repository->updatedFileContents(fpath, hash);
|
||||
_repository->totalDownloaded += sz;
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "did update:" << fpath);
|
||||
} // of hash matches
|
||||
} // of found in child list
|
||||
}
|
||||
|
||||
void didFailToUpdateFile(const std::string& file,
|
||||
AbstractRepository::ResultCode status)
|
||||
HTTPRepository::ResultCode status)
|
||||
{
|
||||
SGPath fpath(_relativePath);
|
||||
fpath.append(file);
|
||||
@@ -405,7 +568,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream indexStream( p.c_str(), std::ios::in );
|
||||
sg_ifstream indexStream(p, std::ios::in );
|
||||
|
||||
if ( !indexStream.is_open() ) {
|
||||
throw sg_io_exception("cannot open dirIndex file", p);
|
||||
@@ -465,14 +628,12 @@ private:
|
||||
p.append(name);
|
||||
bool ok;
|
||||
|
||||
SGPath fpath(_relativePath);
|
||||
fpath.append(name);
|
||||
|
||||
std::string fpath = _relativePath + "/" + name;
|
||||
if (p.isDir()) {
|
||||
ok = _repository->deleteDirectory(fpath.str());
|
||||
ok = _repository->deleteDirectory(fpath);
|
||||
} else {
|
||||
// remove the hash cache entry
|
||||
_repository->updatedFileContents(fpath, std::string());
|
||||
_repository->updatedFileContents(p, std::string());
|
||||
ok = p.remove();
|
||||
}
|
||||
|
||||
@@ -492,10 +653,19 @@ private:
|
||||
return _repository->hashForPath(p);
|
||||
}
|
||||
|
||||
HTTPRepoPrivate* _repository;
|
||||
SGPath _relativePath; // in URL and file-system space
|
||||
HTTPRepoPrivate* _repository;
|
||||
std::string _relativePath; // in URL and file-system space
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NotUpdated,
|
||||
UpdateInProgress,
|
||||
Updated,
|
||||
UpdateFailed,
|
||||
DoNotUpdate
|
||||
} State;
|
||||
|
||||
State _state;
|
||||
};
|
||||
|
||||
HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
|
||||
@@ -533,14 +703,38 @@ SGPath HTTPRepository::fsBase() const
|
||||
|
||||
void HTTPRepository::update()
|
||||
{
|
||||
if (_d->isUpdating) {
|
||||
_d->rootDir->markSubtreeAsNeedingUpdate();
|
||||
_d->updateWaiting();
|
||||
}
|
||||
|
||||
void HTTPRepository::setEntireRepositoryMode()
|
||||
{
|
||||
if (!_d->updateEverything) {
|
||||
// this is a one-way decision
|
||||
_d->updateEverything = true;
|
||||
}
|
||||
|
||||
// probably overkill but not expensive so let's check everything
|
||||
// we have in case someone did something funky and switched from partial
|
||||
// to 'whole repo' updating.
|
||||
_d->rootDir->markSubtreeAsEnabled();
|
||||
}
|
||||
|
||||
|
||||
void HTTPRepository::addSubpath(const std::string& relPath)
|
||||
{
|
||||
if (_d->updateEverything) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "called HTTPRepository::addSubpath but updating everything");
|
||||
return;
|
||||
}
|
||||
|
||||
_d->status = REPO_NO_ERROR;
|
||||
_d->isUpdating = true;
|
||||
_d->failures.clear();
|
||||
_d->updateDir(_d->rootDir, std::string(), 0);
|
||||
_d->updatePaths.push_back(relPath);
|
||||
|
||||
HTTPDirectory* dir = _d->getOrCreateDirectory(relPath);
|
||||
dir->markSubtreeAsEnabled();
|
||||
dir->markAncestorChainAsEnabled();
|
||||
|
||||
_d->updateWaiting();
|
||||
}
|
||||
|
||||
bool HTTPRepository::isDoingSync() const
|
||||
@@ -580,7 +774,12 @@ size_t HTTPRepository::bytesDownloaded() const
|
||||
return result;
|
||||
}
|
||||
|
||||
AbstractRepository::ResultCode
|
||||
void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
|
||||
{
|
||||
_d->installedCopyPath = copyPath;
|
||||
}
|
||||
|
||||
HTTPRepository::ResultCode
|
||||
HTTPRepository::failure() const
|
||||
{
|
||||
if ((_d->status == REPO_NO_ERROR) && !_d->failures.empty()) {
|
||||
@@ -590,12 +789,6 @@ HTTPRepository::failure() const
|
||||
return _d->status;
|
||||
}
|
||||
|
||||
void HTTPRepoGetRequest::cancel()
|
||||
{
|
||||
_directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
|
||||
_directory = 0;
|
||||
}
|
||||
|
||||
class FileGetRequest : public HTTPRepoGetRequest
|
||||
{
|
||||
public:
|
||||
@@ -605,14 +798,13 @@ HTTPRepository::failure() const
|
||||
{
|
||||
pathInRepo = _directory->absolutePath();
|
||||
pathInRepo.append(fileName);
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "will GET file " << url());
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
{
|
||||
if (!file.get()) {
|
||||
file.reset(new SGBinaryFile(pathInRepo.str()));
|
||||
file.reset(new SGBinaryFile(pathInRepo));
|
||||
if (!file->open(SG_IO_OUT)) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "unable to create file " << pathInRepo);
|
||||
_directory->repository()->http->cancelRequest(this, "Unable to create output file");
|
||||
@@ -628,16 +820,17 @@ HTTPRepository::failure() const
|
||||
virtual void onDone()
|
||||
{
|
||||
file->close();
|
||||
|
||||
if (responseCode() == 200) {
|
||||
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
||||
_directory->didUpdateFile(fileName, hash, contentSize());
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "got file " << fileName << " in " << _directory->absolutePath());
|
||||
} else if (responseCode() == 404) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file not found on server: " << fileName << " for " << _directory->absolutePath());
|
||||
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||
} else {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file download error on server: " << fileName << " for " << _directory->absolutePath() << ": " << responseCode() );
|
||||
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_HTTP);
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_HTTP);
|
||||
}
|
||||
|
||||
_directory->repository()->finishedRequest(this);
|
||||
@@ -649,9 +842,9 @@ HTTPRepository::failure() const
|
||||
if (pathInRepo.exists()) {
|
||||
pathInRepo.remove();
|
||||
}
|
||||
|
||||
|
||||
if (_directory) {
|
||||
_directory->didFailToUpdateFile(fileName, AbstractRepository::REPO_ERROR_SOCKET);
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_SOCKET);
|
||||
_directory->repository()->finishedRequest(this);
|
||||
}
|
||||
}
|
||||
@@ -676,7 +869,6 @@ HTTPRepository::failure() const
|
||||
_targetHash(targetHash)
|
||||
{
|
||||
sha1_init(&hashContext);
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "will GET dir " << url());
|
||||
}
|
||||
|
||||
void setIsRootDir()
|
||||
@@ -701,7 +893,7 @@ HTTPRepository::failure() const
|
||||
if (responseCode() == 200) {
|
||||
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
||||
if (!_targetHash.empty() && (hash != _targetHash)) {
|
||||
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_CHECKSUM);
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_CHECKSUM);
|
||||
_directory->repository()->finishedRequest(this);
|
||||
return;
|
||||
}
|
||||
@@ -717,16 +909,16 @@ HTTPRepository::failure() const
|
||||
|
||||
// dir index data has changed, so write to disk and update
|
||||
// the hash accordingly
|
||||
std::ofstream of(pathInRepo().c_str(), std::ios::trunc | std::ios::out);
|
||||
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out);
|
||||
if (!of.is_open()) {
|
||||
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo().c_str());
|
||||
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo());
|
||||
}
|
||||
|
||||
of.write(body.data(), body.size());
|
||||
of.close();
|
||||
_directory->dirIndexUpdated(hash);
|
||||
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
|
||||
} else {
|
||||
_directory->markAsUpToDate();
|
||||
}
|
||||
|
||||
_directory->repository()->totalDownloaded += contentSize();
|
||||
@@ -739,12 +931,12 @@ HTTPRepository::failure() const
|
||||
_directory->updateChildrenBasedOnHash();
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
|
||||
} catch (sg_exception& ) {
|
||||
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_IO);
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_IO);
|
||||
}
|
||||
} else if (responseCode() == 404) {
|
||||
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||
} else {
|
||||
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_HTTP);
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_HTTP);
|
||||
}
|
||||
|
||||
_directory->repository()->finishedRequest(this);
|
||||
@@ -753,7 +945,7 @@ HTTPRepository::failure() const
|
||||
virtual void onFail()
|
||||
{
|
||||
if (_directory) {
|
||||
_directory->failedToUpdate(AbstractRepository::REPO_ERROR_SOCKET);
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_SOCKET);
|
||||
_directory->repository()->finishedRequest(this);
|
||||
}
|
||||
}
|
||||
@@ -778,15 +970,18 @@ HTTPRepository::failure() const
|
||||
|
||||
HTTPRepoPrivate::~HTTPRepoPrivate()
|
||||
{
|
||||
// take a copy since cancelRequest will fail and hence remove
|
||||
// remove activeRequests, invalidating any iterator to it.
|
||||
RequestVector copyOfActive(activeRequests);
|
||||
RequestVector::iterator rq;
|
||||
for (rq = copyOfActive.begin(); rq != copyOfActive.end(); ++rq) {
|
||||
http->cancelRequest(*rq, "Repository object deleted");
|
||||
}
|
||||
|
||||
DirectoryVector::iterator it;
|
||||
for (it=directories.begin(); it != directories.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
|
||||
RequestVector::iterator r;
|
||||
for (r=activeRequests.begin(); r != activeRequests.end(); ++r) {
|
||||
(*r)->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz)
|
||||
@@ -799,6 +994,7 @@ HTTPRepository::failure() const
|
||||
|
||||
HTTP::Request_ptr HTTPRepoPrivate::updateDir(HTTPDirectory* dir, const std::string& hash, size_t sz)
|
||||
{
|
||||
dir->markAsUpdating();
|
||||
RepoRequestPtr r(new DirGetRequest(dir, hash));
|
||||
r->setContentSize(sz);
|
||||
makeRequest(r);
|
||||
@@ -809,7 +1005,7 @@ HTTPRepository::failure() const
|
||||
class HashEntryWithPath
|
||||
{
|
||||
public:
|
||||
HashEntryWithPath(const std::string& p) : path(p) {}
|
||||
HashEntryWithPath(const SGPath& p) : path(p.utf8Str()) {}
|
||||
bool operator()(const HTTPRepoPrivate::HashCacheEntry& entry) const
|
||||
{ return entry.filePath == path; }
|
||||
private:
|
||||
@@ -818,7 +1014,7 @@ HTTPRepository::failure() const
|
||||
|
||||
std::string HTTPRepoPrivate::hashForPath(const SGPath& p)
|
||||
{
|
||||
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
|
||||
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p));
|
||||
if (it != hashes.end()) {
|
||||
// ensure data on disk hasn't changed.
|
||||
// we could also use the file type here if we were paranoid
|
||||
@@ -843,7 +1039,7 @@ HTTPRepository::failure() const
|
||||
sha1_init(&info);
|
||||
char* buf = static_cast<char*>(malloc(1024 * 1024));
|
||||
size_t readLen;
|
||||
SGBinaryFile f(p.str());
|
||||
SGBinaryFile f(p);
|
||||
if (!f.open(SG_IO_IN)) {
|
||||
throw sg_io_exception("Couldn't open file for compute hash", p);
|
||||
}
|
||||
@@ -860,7 +1056,7 @@ HTTPRepository::failure() const
|
||||
void HTTPRepoPrivate::updatedFileContents(const SGPath& p, const std::string& newHash)
|
||||
{
|
||||
// remove the existing entry
|
||||
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p.str()));
|
||||
HashCache::iterator it = std::find_if(hashes.begin(), hashes.end(), HashEntryWithPath(p));
|
||||
if (it != hashes.end()) {
|
||||
hashes.erase(it);
|
||||
hashCacheDirty = true;
|
||||
@@ -876,7 +1072,7 @@ HTTPRepository::failure() const
|
||||
p2.set_cached(true);
|
||||
|
||||
HashCacheEntry entry;
|
||||
entry.filePath = p.str();
|
||||
entry.filePath = p.utf8Str();
|
||||
entry.hashHex = newHash;
|
||||
entry.modTime = p2.modTime();
|
||||
entry.lengthBytes = p2.sizeInBytes();
|
||||
@@ -893,8 +1089,7 @@ HTTPRepository::failure() const
|
||||
|
||||
SGPath cachePath = basePath;
|
||||
cachePath.append(".hashes");
|
||||
|
||||
std::ofstream stream(cachePath.c_str(),std::ios::out | std::ios::trunc);
|
||||
sg_ofstream stream(cachePath, std::ios::out | std::ios::trunc);
|
||||
HashCache::const_iterator it;
|
||||
for (it = hashes.begin(); it != hashes.end(); ++it) {
|
||||
stream << it->filePath << ":" << it->modTime << ":"
|
||||
@@ -913,7 +1108,7 @@ HTTPRepository::failure() const
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream stream(cachePath.c_str(), std::ios::in);
|
||||
sg_ifstream stream(cachePath, std::ios::in);
|
||||
|
||||
while (!stream.eof()) {
|
||||
std::string line;
|
||||
@@ -924,7 +1119,7 @@ HTTPRepository::failure() const
|
||||
|
||||
string_list tokens = simgear::strutils::split( line, ":" );
|
||||
if( tokens.size() < 4 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath << "': '" << line << "' (ignoring line)");
|
||||
continue;
|
||||
}
|
||||
const std::string nameData = simgear::strutils::strip(tokens[0]);
|
||||
@@ -933,7 +1128,7 @@ HTTPRepository::failure() const
|
||||
const std::string hashData = simgear::strutils::strip(tokens[3]);
|
||||
|
||||
if (nameData.empty() || timeData.empty() || sizeData.empty() || hashData.empty() ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath.str() << "': '" << line << "' (ignoring line)");
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath << "': '" << line << "' (ignoring line)");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -951,7 +1146,7 @@ HTTPRepository::failure() const
|
||||
public:
|
||||
DirectoryWithPath(const std::string& p) : path(p) {}
|
||||
bool operator()(const HTTPDirectory* entry) const
|
||||
{ return entry->relativePath().str() == path; }
|
||||
{ return entry->relativePath() == path; }
|
||||
private:
|
||||
std::string path;
|
||||
};
|
||||
@@ -966,6 +1161,25 @@ HTTPRepository::failure() const
|
||||
|
||||
HTTPDirectory* d = new HTTPDirectory(this, path);
|
||||
directories.push_back(d);
|
||||
if (updateEverything) {
|
||||
d->markAsEnabled();
|
||||
} else {
|
||||
string_list::const_iterator s;
|
||||
bool shouldUpdate = false;
|
||||
|
||||
for (s = updatePaths.begin(); s != updatePaths.end(); ++s) {
|
||||
size_t minLen = std::min(path.size(), s->size());
|
||||
if (s->compare(0, minLen, path, 0, minLen) == 0) {
|
||||
shouldUpdate = true;
|
||||
break;
|
||||
}
|
||||
} // of paths iteration
|
||||
|
||||
if (shouldUpdate) {
|
||||
d->markAsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -978,10 +1192,11 @@ HTTPRepository::failure() const
|
||||
directories.erase(it);
|
||||
Dir dir(d->absolutePath());
|
||||
bool result = dir.remove(true);
|
||||
delete d;
|
||||
|
||||
// update the hash cache too
|
||||
updatedFileContents(path, std::string());
|
||||
updatedFileContents(d->absolutePath(), std::string());
|
||||
|
||||
delete d;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1002,10 +1217,11 @@ HTTPRepository::failure() const
|
||||
void HTTPRepoPrivate::finishedRequest(const RepoRequestPtr& req)
|
||||
{
|
||||
RequestVector::iterator it = std::find(activeRequests.begin(), activeRequests.end(), req);
|
||||
if (it == activeRequests.end()) {
|
||||
throw sg_exception("lost request somehow", req->url());
|
||||
// in some cases, for example a checksum failure, we clear the active
|
||||
// and queued request vectors, so the ::find above can fail
|
||||
if (it != activeRequests.end()) {
|
||||
activeRequests.erase(it);
|
||||
}
|
||||
activeRequests.erase(it);
|
||||
|
||||
if (!queuedRequests.empty()) {
|
||||
RepoRequestPtr rr = queuedRequests.front();
|
||||
@@ -1021,15 +1237,36 @@ HTTPRepository::failure() const
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRepoPrivate::failedToGetRootIndex(AbstractRepository::ResultCode st)
|
||||
void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st)
|
||||
{
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl);
|
||||
status = st;
|
||||
}
|
||||
|
||||
void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath,
|
||||
AbstractRepository::ResultCode fileStatus)
|
||||
HTTPRepository::ResultCode fileStatus)
|
||||
{
|
||||
if (fileStatus == HTTPRepository::REPO_ERROR_CHECKSUM) {
|
||||
// stop updating, and mark repository as failed, becuase this
|
||||
// usually indicates we need to start a fresh update from the
|
||||
// root.
|
||||
// (we could issue a retry here, but we leave that to higher layers)
|
||||
status = fileStatus;
|
||||
|
||||
queuedRequests.clear();
|
||||
|
||||
RequestVector copyOfActive(activeRequests);
|
||||
RequestVector::iterator rq;
|
||||
for (rq = copyOfActive.begin(); rq != copyOfActive.end(); ++rq) {
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "cancelling request for:" << (*rq)->url());
|
||||
http->cancelRequest(*rq, "Repository updated failed");
|
||||
}
|
||||
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update repository:" << baseUrl
|
||||
<< ", possibly modified during sync");
|
||||
}
|
||||
|
||||
Failure f;
|
||||
f.path = relativePath;
|
||||
f.error = fileStatus;
|
||||
@@ -1038,6 +1275,22 @@ HTTPRepository::failure() const
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
|
||||
}
|
||||
|
||||
void HTTPRepoPrivate::updateWaiting()
|
||||
{
|
||||
if (!isUpdating) {
|
||||
status = HTTPRepository::REPO_NO_ERROR;
|
||||
isUpdating = true;
|
||||
failures.clear();
|
||||
}
|
||||
|
||||
// find to-be-updated sub-trees and kick them off
|
||||
rootDir->updateIfWaiting(std::string(), 0);
|
||||
|
||||
// maybe there was nothing to do
|
||||
if (activeRequests.empty()) {
|
||||
status = HTTPRepository::REPO_NO_ERROR;
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -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
|
||||
@@ -19,6 +19,7 @@
|
||||
#define SGGeod_H
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/math/SGVec3.hxx>
|
||||
|
||||
// #define SG_GEOD_NATIVE_DEGREE
|
||||
|
||||
|
||||
@@ -153,10 +153,10 @@ public:
|
||||
/// Use with care: allways code that you do not need to use that!
|
||||
static bool isNaN(const T& v)
|
||||
{
|
||||
#ifdef HAVE_ISNAN
|
||||
return (isnan(v) != 0);
|
||||
#elif defined HAVE_STD_ISNAN
|
||||
#ifdef HAVE_STD_ISNAN
|
||||
return std::isnan(v);
|
||||
#elif defined HAVE_ISNAN
|
||||
return (isnan(v) != 0);
|
||||
#else
|
||||
// Use that every compare involving a NaN returns false
|
||||
// But be careful, some usual compiler switches like for example
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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>
|
||||
@@ -61,24 +64,26 @@ static const char sgSearchPathSep = ':';
|
||||
// included in the Windows 8.1 SDK
|
||||
#include "sgversionhelpers.hxx"
|
||||
|
||||
#define ENABLE_OLD_PATH_API 1
|
||||
|
||||
static SGPath pathForCSIDL(int csidl, const SGPath& def)
|
||||
{
|
||||
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
|
||||
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, PWSTR, int, BOOL);
|
||||
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
|
||||
|
||||
// lazy open+resolve of shell32
|
||||
if (!SHGetSpecialFolderPath) {
|
||||
HINSTANCE shellDll = ::LoadLibrary("shell32");
|
||||
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathA");
|
||||
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathW");
|
||||
}
|
||||
|
||||
if (!SHGetSpecialFolderPath){
|
||||
return def;
|
||||
}
|
||||
|
||||
char path[MAX_PATH];
|
||||
wchar_t path[MAX_PATH];
|
||||
if (SHGetSpecialFolderPath(0, path, csidl, false)) {
|
||||
return SGPath(path, def.getPermissionChecker());
|
||||
return SGPath(std::wstring(path), def.getPermissionChecker());
|
||||
}
|
||||
|
||||
return def;
|
||||
@@ -96,16 +101,7 @@ static SGPath pathForKnownFolder(REFKNOWNFOLDERID folderId, const SGPath& def)
|
||||
wchar_t* localFolder = 0;
|
||||
|
||||
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT_PATH, NULL, &localFolder) == S_OK) {
|
||||
// copy into local memory
|
||||
char path[MAX_PATH];
|
||||
size_t len;
|
||||
if (wcstombs_s(&len, path, localFolder, MAX_PATH) != S_OK) {
|
||||
path[0] = '\0';
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "WCS to MBS failed");
|
||||
}
|
||||
|
||||
SGPath folder_path = SGPath(path, def.getPermissionChecker());
|
||||
|
||||
SGPath folder_path = SGPath(localFolder, def.getPermissionChecker());
|
||||
// release dynamic memory
|
||||
CoTaskMemFree(static_cast<void*>(localFolder));
|
||||
|
||||
@@ -143,7 +139,7 @@ static SGPath getXDGDir( const std::string& name,
|
||||
// path. No other format is supported.
|
||||
const std::string XDG_ID = "XDG_" + name + "_DIR=\"";
|
||||
|
||||
std::ifstream user_dirs_file( user_dirs.c_str() );
|
||||
sg_ifstream user_dirs_file( user_dirs );
|
||||
std::string line;
|
||||
while( std::getline(user_dirs_file, line).good() )
|
||||
{
|
||||
@@ -208,6 +204,18 @@ SGPath::SGPath( const std::string& p, PermissionChecker validator )
|
||||
fix();
|
||||
}
|
||||
|
||||
// create a path based on "path"
|
||||
SGPath::SGPath(const std::wstring& p, PermissionChecker validator) :
|
||||
_permission_checker(validator),
|
||||
_cached(false),
|
||||
_rwCached(false),
|
||||
_cacheEnabled(true)
|
||||
{
|
||||
path = simgear::strutils::convertWStringToUtf8(p);
|
||||
fix();
|
||||
}
|
||||
|
||||
|
||||
// create a path based on "path" and a "subpath"
|
||||
SGPath::SGPath( const SGPath& p,
|
||||
const std::string& r,
|
||||
@@ -222,6 +230,17 @@ SGPath::SGPath( const SGPath& p,
|
||||
fix();
|
||||
}
|
||||
|
||||
SGPath SGPath::fromLocal8Bit(const char *name)
|
||||
{
|
||||
return SGPath(simgear::strutils::convertWindowsLocal8BitToUtf8(name));
|
||||
}
|
||||
|
||||
SGPath SGPath::fromUtf8(const std::string& bytes, PermissionChecker p)
|
||||
{
|
||||
return SGPath(bytes, p);
|
||||
}
|
||||
|
||||
|
||||
SGPath::SGPath(const SGPath& p) :
|
||||
path(p.path),
|
||||
_permission_checker(p._permission_checker),
|
||||
@@ -259,7 +278,7 @@ SGPath& SGPath::operator=(const SGPath& p)
|
||||
SGPath::~SGPath() {
|
||||
}
|
||||
|
||||
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
// set path
|
||||
void SGPath::set( const string& p ) {
|
||||
path = p;
|
||||
@@ -267,6 +286,7 @@ void SGPath::set( const string& p ) {
|
||||
_cached = false;
|
||||
_rwCached = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::setPermissionChecker(PermissionChecker validator)
|
||||
@@ -311,11 +331,12 @@ SGPath SGPath::operator/( const std::string& p ) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
//add a new path component to the existing path string
|
||||
void SGPath::add( const string& p ) {
|
||||
append( sgSearchPathSep+p );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// concatenate a string to the end of the path without inserting a
|
||||
// path separator
|
||||
@@ -330,6 +351,10 @@ void SGPath::concat( const string& p ) {
|
||||
_rwCached = false;
|
||||
}
|
||||
|
||||
std::string SGPath::local8BitStr() const
|
||||
{
|
||||
return simgear::strutils::convertUtf8ToWindowsLocal8Bit(path);
|
||||
}
|
||||
|
||||
// Get the file part of the path (everything after the last path sep)
|
||||
string SGPath::file() const
|
||||
@@ -341,7 +366,7 @@ string SGPath::file() const
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get the directory part of the path.
|
||||
string SGPath::dir() const {
|
||||
@@ -353,17 +378,22 @@ string SGPath::dir() const {
|
||||
}
|
||||
}
|
||||
|
||||
SGPath SGPath::dirPath() const
|
||||
{
|
||||
return SGPath::fromUtf8(dir());
|
||||
}
|
||||
|
||||
// get the base part of the path (everything but the extension.)
|
||||
string SGPath::base() const
|
||||
{
|
||||
string::size_type index = path.rfind(".");
|
||||
string::size_type lastSep = path.rfind(sgDirPathSep);
|
||||
|
||||
|
||||
// tolerate dots inside directory names
|
||||
if ((lastSep != string::npos) && (index < lastSep)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
if (index != string::npos) {
|
||||
return path.substr(0, index);
|
||||
} else {
|
||||
@@ -379,12 +409,12 @@ string SGPath::file_base() const
|
||||
} else {
|
||||
++index; // skip past the separator
|
||||
}
|
||||
|
||||
|
||||
string::size_type firstDot = path.find(".", index);
|
||||
if (firstDot == string::npos) {
|
||||
return path.substr(index); // no extensions
|
||||
}
|
||||
|
||||
|
||||
return path.substr(index, firstDot - index);
|
||||
}
|
||||
|
||||
@@ -412,7 +442,7 @@ string SGPath::complete_lower_extension() const
|
||||
} else {
|
||||
++index; // skip past the separator
|
||||
}
|
||||
|
||||
|
||||
string::size_type firstDot = path.find(".", index);
|
||||
if ((firstDot != string::npos) && (path.find(sgDirPathSep, firstDot) == string::npos)) {
|
||||
return boost::to_lower_copy(path.substr(firstDot + 1));
|
||||
@@ -436,12 +466,12 @@ void SGPath::validate() const
|
||||
#ifdef _WIN32
|
||||
struct _stat buf ;
|
||||
bool remove_trailing = false;
|
||||
string statPath(path);
|
||||
std::wstring statPath(wstr());
|
||||
if ((path.length() > 1) && (path.back() == '/')) {
|
||||
statPath.pop_back();
|
||||
}
|
||||
|
||||
if (_stat(statPath.c_str(), &buf ) < 0) {
|
||||
|
||||
if (_wstat(statPath.c_str(), &buf ) < 0) {
|
||||
_exists = false;
|
||||
} else {
|
||||
_exists = true;
|
||||
@@ -463,7 +493,7 @@ void SGPath::validate() const
|
||||
_modTime = buf.st_mtime;
|
||||
_size = buf.st_size;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
_cached = true;
|
||||
}
|
||||
@@ -522,62 +552,68 @@ bool SGPath::isFile() const
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#ifdef _WIN32
|
||||
# define sgMkDir(d,m) _mkdir(d)
|
||||
#else
|
||||
# define sgMkDir(d,m) mkdir(d,m)
|
||||
#endif
|
||||
|
||||
int SGPath::create_dir(mode_t mode)
|
||||
{
|
||||
if( !canWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_WARN, "Error creating directory for '" << str() << "'"
|
||||
SG_WARN, "Error creating directory for '" << *this << "'"
|
||||
" reason: access denied" );
|
||||
return -3;
|
||||
}
|
||||
|
||||
string_list dirlist = sgPathSplit(dir());
|
||||
if ( dirlist.empty() )
|
||||
SGPath dirP = dirPath();
|
||||
if (dirP.isNull() )
|
||||
return -1;
|
||||
string path = dirlist[0];
|
||||
string_list path_elements = sgPathBranchSplit(path);
|
||||
bool absolute = !path.empty() && path[0] == sgDirPathSep;
|
||||
|
||||
string_list path_elements = sgPathBranchSplit(dirP.utf8Str());
|
||||
bool absolute = dirP.isAbsolute();
|
||||
unsigned int i = 1;
|
||||
SGPath dir(absolute ? string( 1, sgDirPathSep ) : "", _permission_checker);
|
||||
dir.concat( path_elements[0] );
|
||||
#ifdef _WIN32
|
||||
if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) {
|
||||
dir.append( path_elements[1] );
|
||||
i = 2;
|
||||
}
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
SGPath dir(path_elements.front(), _permission_checker);
|
||||
// exists() does not work for drive letter paths, eg 'C:\'.
|
||||
// Detect this case and skip to the next element immediately
|
||||
if (absolute && path_elements.size() >= 2) {
|
||||
dir.append(path_elements[i++]);
|
||||
}
|
||||
#else
|
||||
SGPath dir((absolute ? "/" : "") + path_elements.front(), _permission_checker);
|
||||
#endif
|
||||
struct stat info;
|
||||
int r;
|
||||
for(; (r = stat(dir.c_str(), &info)) == 0 && i < path_elements.size(); ++i)
|
||||
dir.append(path_elements[i]);
|
||||
if( r == 0 )
|
||||
return 0; // Directory already exists
|
||||
while (dir.exists() && (i < path_elements.size())) {
|
||||
dir.append(path_elements[i++]);
|
||||
}
|
||||
|
||||
// already exists
|
||||
if (dir.exists() && (i == path_elements.size())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if( sgMkDir(dir.c_str(), mode) )
|
||||
#if defined (SG_WINDOWS)
|
||||
std::wstring ds = dir.wstr();
|
||||
if (_wmkdir(ds.c_str()))
|
||||
#else
|
||||
std::string ds = dir.utf8Str();
|
||||
if( mkdir(ds.c_str(), mode) )
|
||||
#endif
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
|
||||
return -2;
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error creating directory: (" << dir << "):"
|
||||
<< simgear::strutils::error_string(errno) );
|
||||
return errno;
|
||||
}
|
||||
else
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir.str());
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir);
|
||||
|
||||
if( i >= path_elements.size() )
|
||||
return 0;
|
||||
if (i >= path_elements.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
dir.append(path_elements[i++]);
|
||||
}
|
||||
|
||||
_cached = false; // re-stat on next query
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -627,7 +663,7 @@ bool SGPath::isAbsolute() const
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// detect '[A-Za-z]:/'
|
||||
if (path.size() > 2) {
|
||||
@@ -636,7 +672,7 @@ bool SGPath::isAbsolute() const
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return (path[0] == sgDirPathSep);
|
||||
}
|
||||
|
||||
@@ -645,10 +681,11 @@ bool SGPath::isNull() const
|
||||
return path.empty();
|
||||
}
|
||||
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
std::string SGPath::str_native() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::string s = str();
|
||||
std::string s = local8BitStr();
|
||||
std::string::size_type pos;
|
||||
std::string nativeSeparator;
|
||||
nativeSeparator = sgDirPathSepBad;
|
||||
@@ -658,24 +695,31 @@ std::string SGPath::str_native() const
|
||||
}
|
||||
return s;
|
||||
#else
|
||||
return str();
|
||||
return utf8Str();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::remove()
|
||||
{
|
||||
if( !canWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ")"
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ")"
|
||||
" reason: access denied" );
|
||||
return false;
|
||||
}
|
||||
|
||||
int err = ::unlink(c_str());
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = wstr();
|
||||
int err = _wunlink(ps.c_str());
|
||||
#else
|
||||
std::string ps = local8BitStr();
|
||||
int err = ::unlink(ps.c_str());
|
||||
#endif
|
||||
if( err )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ") "
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ") "
|
||||
" reason: " << strerror(errno) );
|
||||
// TODO check if failed unlink can really change any of the cached values
|
||||
}
|
||||
@@ -712,8 +756,8 @@ bool SGPath::rename(const SGPath& newName)
|
||||
{
|
||||
if( !canRead() || !canWrite() || !newName.canWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
|
||||
" to " << newName.str() <<
|
||||
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << *this <<
|
||||
" to " << newName <<
|
||||
" reason: access denied" );
|
||||
return false;
|
||||
}
|
||||
@@ -726,10 +770,20 @@ bool SGPath::rename(const SGPath& newName)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( ::rename(c_str(), newName.c_str()) != 0 )
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring p = wstr();
|
||||
std::wstring np = newName.wstr();
|
||||
if (_wrename(p.c_str(), np.c_str()) != 0)
|
||||
#else
|
||||
std::string p = local8BitStr();
|
||||
std::string np = newName.local8BitStr();
|
||||
if( ::rename(p.c_str(), np.c_str()) != 0 )
|
||||
#endif
|
||||
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
|
||||
" to " << newName.str() <<
|
||||
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << *this <<
|
||||
" to " << newName <<
|
||||
" reason: " << strerror(errno) );
|
||||
return false;
|
||||
}
|
||||
@@ -815,12 +869,69 @@ SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def)
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::fromEnv(const char* name, const SGPath& def)
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring wname = simgear::strutils::convertUtf8ToWString(name);
|
||||
const wchar_t* val = _wgetenv(wname.c_str());
|
||||
if (val && val[0])
|
||||
return SGPath(val, def._permission_checker);
|
||||
#else
|
||||
const char* val = getenv(name);
|
||||
if( val && val[0] )
|
||||
return SGPath(val, def._permission_checker);
|
||||
#endif
|
||||
return def;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::vector<SGPath> SGPath::pathsFromEnv(const char *name)
|
||||
{
|
||||
std::vector<SGPath> r;
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring wname = simgear::strutils::convertUtf8ToWString(name);
|
||||
const wchar_t* val = _wgetenv(wname.c_str());
|
||||
#else
|
||||
const char* val = getenv(name);
|
||||
#endif
|
||||
if (!val) {
|
||||
return r;
|
||||
}
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
return pathsFromUtf8(simgear::strutils::convertWStringToUtf8(val));
|
||||
#else
|
||||
return pathsFromUtf8(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::vector<SGPath> SGPath::pathsFromUtf8(const std::string& paths)
|
||||
{
|
||||
std::vector<SGPath> r;
|
||||
string_list items = sgPathSplit(paths);
|
||||
string_list_iterator it;
|
||||
for (it = items.begin(); it != items.end(); ++it) {
|
||||
r.push_back(SGPath::fromUtf8(it->c_str()));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::vector<SGPath> SGPath::pathsFromLocal8Bit(const std::string& paths)
|
||||
{
|
||||
std::vector<SGPath> r;
|
||||
string_list items = sgPathSplit(paths);
|
||||
string_list_iterator it;
|
||||
for (it = items.begin(); it != items.end(); ++it) {
|
||||
r.push_back(SGPath::fromLocal8Bit(it->c_str()));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::home(const SGPath& def)
|
||||
{
|
||||
@@ -844,11 +955,12 @@ SGPath SGPath::documents(const SGPath& def)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string SGPath::realpath() const
|
||||
SGPath SGPath::realpath() const
|
||||
{
|
||||
#if defined(_MSC_VER) /*for MS compilers */ || defined(_WIN32) /*needed for non MS windows compilers like MingW*/
|
||||
#if defined(SG_WINDOWS)
|
||||
// with absPath NULL, will allocate, and ignore length
|
||||
char *buf = _fullpath( NULL, path.c_str(), _MAX_PATH );
|
||||
std::wstring ws = wstr();
|
||||
wchar_t *buf = _wfullpath( NULL, ws.c_str(), _MAX_PATH );
|
||||
#else
|
||||
// POSIX
|
||||
char* buf = ::realpath(path.c_str(), NULL);
|
||||
@@ -864,21 +976,45 @@ std::string SGPath::realpath() const
|
||||
this_dir = "/";
|
||||
}
|
||||
if (file() == "..") {
|
||||
this_dir = SGPath(SGPath(this_dir).realpath()).dir();
|
||||
this_dir = SGPath(this_dir).realpath().dir();
|
||||
if (this_dir.empty()) { // invalid path: .. above root
|
||||
return "";
|
||||
return SGPath();
|
||||
}
|
||||
return SGPath(this_dir).realpath(); // use native path separator,
|
||||
// and handle 'existing/nonexisting/../symlink' paths
|
||||
}
|
||||
return SGPath(this_dir).realpath() +
|
||||
#if defined(_MSC_VER) || defined(_WIN32)
|
||||
"\\" + file();
|
||||
#else
|
||||
"/" + file();
|
||||
#endif
|
||||
return SGPath(this_dir).realpath() / file();
|
||||
}
|
||||
std::string p(buf);
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
SGPath p = SGPath(std::wstring(buf), NULL);
|
||||
#else
|
||||
SGPath p(SGPath::fromLocal8Bit(buf));
|
||||
#endif
|
||||
free(buf);
|
||||
return p;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::string SGPath::join(const std::vector<SGPath>& paths, const std::string& joinWith)
|
||||
{
|
||||
std::string r;
|
||||
if (paths.empty()) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = paths[0].utf8Str();
|
||||
for (size_t i=1; i<paths.size(); ++i) {
|
||||
r += joinWith + paths[i].utf8Str();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::wstring SGPath::wstr() const
|
||||
{
|
||||
return simgear::strutils::convertUtf8ToWString(path);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
*/
|
||||
SGPath( const std::string& p, PermissionChecker validator = NULL );
|
||||
|
||||
explicit SGPath(const std::wstring& p, PermissionChecker validator = NULL);
|
||||
|
||||
/**
|
||||
* Construct a path based on the starting path provided and a relative subpath
|
||||
* @param p initial path
|
||||
@@ -131,10 +133,10 @@ public:
|
||||
void concat( const std::string& p );
|
||||
|
||||
/**
|
||||
* Returns a string with the absolute pathname that names the same file, whose
|
||||
* Returns a path with the absolute pathname that names the same file, whose
|
||||
* resolution does not involve '.', '..', or symbolic links.
|
||||
*/
|
||||
std::string realpath() const;
|
||||
SGPath realpath() const;
|
||||
|
||||
/**
|
||||
* Get the file part of the path (everything after the last path sep)
|
||||
@@ -186,6 +188,11 @@ public:
|
||||
* @return path string
|
||||
*/
|
||||
std::string str() const { return path; }
|
||||
std::string utf8Str() const { return path; }
|
||||
|
||||
std::string local8BitStr() const;
|
||||
|
||||
std::wstring wstr() const;
|
||||
|
||||
/**
|
||||
* Get the path string
|
||||
@@ -264,6 +271,12 @@ public:
|
||||
* or if the destination already exists, or is not writeable
|
||||
*/
|
||||
bool rename(const SGPath& newName);
|
||||
|
||||
|
||||
/**
|
||||
* return the path of the parent directory of this path.
|
||||
*/
|
||||
SGPath dirPath() const;
|
||||
|
||||
enum StandardLocation
|
||||
{
|
||||
@@ -286,6 +299,10 @@ public:
|
||||
*/
|
||||
static SGPath fromEnv(const char* name, const SGPath& def = SGPath());
|
||||
|
||||
static SGPath fromUtf8(const std::string& bytes, PermissionChecker p = NULL);
|
||||
|
||||
static SGPath fromLocal8Bit(const char* name);
|
||||
|
||||
/**
|
||||
* Get path to user's home directory
|
||||
*/
|
||||
@@ -301,6 +318,13 @@ public:
|
||||
*/
|
||||
static SGPath documents(const SGPath& def = SGPath());
|
||||
|
||||
static std::vector<SGPath> pathsFromEnv(const char* name);
|
||||
|
||||
static std::vector<SGPath> pathsFromUtf8(const std::string& paths);
|
||||
|
||||
static std::vector<SGPath> pathsFromLocal8Bit(const std::string& paths);
|
||||
|
||||
static std::string join(const std::vector<SGPath>& paths, const std::string& joinWith);
|
||||
private:
|
||||
|
||||
void fix();
|
||||
@@ -328,8 +352,7 @@ template<typename char_type, typename traits_type>
|
||||
inline
|
||||
std::basic_ostream<char_type, traits_type>&
|
||||
operator<<(std::basic_ostream<char_type, traits_type>& s, const SGPath& p)
|
||||
{ return s << "Path \"" << p.str() << "\""; }
|
||||
|
||||
{ return s << "Path \"" << p.utf8Str() << "\""; }
|
||||
|
||||
/**
|
||||
* Split a directory string into a list of it's parent directories.
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "sgstream.hxx"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
|
||||
@@ -39,7 +41,7 @@ sg_gzifstream::sg_gzifstream()
|
||||
//
|
||||
// Open a possibly gzipped file for reading.
|
||||
//
|
||||
sg_gzifstream::sg_gzifstream( const std::string& name, ios_openmode io_mode )
|
||||
sg_gzifstream::sg_gzifstream( const SGPath& name, ios_openmode io_mode )
|
||||
: istream(&gzbuf)
|
||||
{
|
||||
this->open( name, io_mode );
|
||||
@@ -64,26 +66,26 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
|
||||
// then append ".gz" and try again.
|
||||
//
|
||||
void
|
||||
sg_gzifstream::open( const std::string& name, ios_openmode io_mode )
|
||||
sg_gzifstream::open( const SGPath& name, ios_openmode io_mode )
|
||||
{
|
||||
gzbuf.open( name.c_str(), io_mode );
|
||||
std::string s = name.utf8Str();
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
if ( ! gzbuf.is_open() )
|
||||
{
|
||||
std::string s = name;
|
||||
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
|
||||
{
|
||||
// remove ".gz" suffix
|
||||
s.replace( s.length() - 3, 3, "" );
|
||||
// s.erase( s.length() - 3, 3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append ".gz" suffix
|
||||
s += ".gz";
|
||||
}
|
||||
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
|
||||
{
|
||||
// remove ".gz" suffix
|
||||
s.replace( s.length() - 3, 3, "" );
|
||||
// s.erase( s.length() - 3, 3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append ".gz" suffix
|
||||
s += ".gz";
|
||||
}
|
||||
|
||||
// Try again.
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
// Try again.
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +161,7 @@ sg_gzofstream::sg_gzofstream()
|
||||
//
|
||||
// Open a file for gzipped writing.
|
||||
//
|
||||
sg_gzofstream::sg_gzofstream( const std::string& name, ios_openmode io_mode )
|
||||
sg_gzofstream::sg_gzofstream( const SGPath& name, ios_openmode io_mode )
|
||||
: ostream(&gzbuf)
|
||||
{
|
||||
this->open( name, io_mode );
|
||||
@@ -180,9 +182,10 @@ sg_gzofstream::sg_gzofstream( int fd, ios_openmode io_mode )
|
||||
// Open a file for gzipped writing.
|
||||
//
|
||||
void
|
||||
sg_gzofstream::open( const std::string& name, ios_openmode io_mode )
|
||||
sg_gzofstream::open( const SGPath& name, ios_openmode io_mode )
|
||||
{
|
||||
gzbuf.open( name.c_str(), io_mode );
|
||||
std::string s = name.utf8Str();
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -190,3 +193,44 @@ sg_gzofstream::attach( int fd, ios_openmode io_mode )
|
||||
{
|
||||
gzbuf.attach( fd, io_mode );
|
||||
}
|
||||
|
||||
|
||||
sg_ifstream::sg_ifstream(const SGPath& path, ios_openmode io_mode)
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = path.wstr();
|
||||
#else
|
||||
std::string ps = path.local8BitStr();
|
||||
#endif
|
||||
std::ifstream::open(ps.c_str(), io_mode);
|
||||
}
|
||||
|
||||
void sg_ifstream::open( const SGPath& name, ios_openmode io_mode )
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = name.wstr();
|
||||
#else
|
||||
std::string ps = name.local8BitStr();
|
||||
#endif
|
||||
std::ifstream::open(ps.c_str(), io_mode);
|
||||
}
|
||||
|
||||
sg_ofstream::sg_ofstream(const SGPath& path, ios_openmode io_mode)
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = path.wstr();
|
||||
#else
|
||||
std::string ps = path.local8BitStr();
|
||||
#endif
|
||||
std::ofstream::open(ps.c_str(), io_mode);
|
||||
}
|
||||
|
||||
void sg_ofstream::open( const SGPath& name, ios_openmode io_mode )
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = name.wstr();
|
||||
#else
|
||||
std::string ps = name.local8BitStr();
|
||||
#endif
|
||||
std::ofstream::open(ps.c_str(), io_mode);
|
||||
}
|
||||
|
||||
@@ -33,13 +33,16 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
# include <istream>
|
||||
# include <ostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <simgear/misc/zfstream.hxx>
|
||||
|
||||
class SGPath;
|
||||
|
||||
/**
|
||||
* An envelope class for gzifstream.
|
||||
*/
|
||||
@@ -55,7 +58,7 @@ public:
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
*/
|
||||
sg_gzifstream( const std::string& name,
|
||||
sg_gzifstream( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in | ios_binary );
|
||||
|
||||
/**
|
||||
@@ -70,7 +73,7 @@ public:
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
*/
|
||||
void open( const std::string& name,
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in|ios_binary );
|
||||
|
||||
/**
|
||||
@@ -90,8 +93,8 @@ public:
|
||||
|
||||
private:
|
||||
// Not defined!
|
||||
sg_gzifstream( const sg_gzifstream& );
|
||||
void operator= ( const sg_gzifstream& );
|
||||
sg_gzifstream( const sg_gzifstream& );
|
||||
void operator= ( const sg_gzifstream& );
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -130,7 +133,7 @@ public:
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
*/
|
||||
sg_gzofstream( const std::string& name,
|
||||
sg_gzofstream( const SGPath& name,
|
||||
ios_openmode io_mode = ios_out | ios_binary );
|
||||
|
||||
/**
|
||||
@@ -145,7 +148,7 @@ public:
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
*/
|
||||
void open( const std::string& name,
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_out|ios_binary );
|
||||
|
||||
/**
|
||||
@@ -169,5 +172,25 @@ private:
|
||||
void operator= ( const sg_gzofstream& );
|
||||
};
|
||||
|
||||
#endif /* _SGSTREAM_HXX */
|
||||
class sg_ifstream : public std::ifstream
|
||||
{
|
||||
public:
|
||||
sg_ifstream() {}
|
||||
|
||||
sg_ifstream(const SGPath& path, ios_openmode io_mode = ios_in | ios_binary);
|
||||
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in|ios_binary );
|
||||
};
|
||||
|
||||
class sg_ofstream : public std::ofstream
|
||||
{
|
||||
public:
|
||||
sg_ofstream() { }
|
||||
sg_ofstream(const SGPath& path, ios_openmode io_mode = ios_out | ios_binary);
|
||||
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_out|ios_binary );
|
||||
};
|
||||
|
||||
#endif /* _SGSTREAM_HXX */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <simgear/compiler.h> // SG_WINDOWS
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::stringstream;
|
||||
@@ -79,7 +84,7 @@ namespace simgear {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
static vector<string>
|
||||
split_whitespace( const string& str, int maxsplit )
|
||||
@@ -125,7 +130,7 @@ namespace simgear {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
vector<string>
|
||||
split( const string& str, const char* sep, int maxsplit )
|
||||
@@ -229,7 +234,7 @@ namespace simgear {
|
||||
return do_strip( s, BOTHSTRIP );
|
||||
}
|
||||
|
||||
string
|
||||
string
|
||||
rpad( const string & s, string::size_type length, char c )
|
||||
{
|
||||
string::size_type l = s.length();
|
||||
@@ -238,7 +243,7 @@ namespace simgear {
|
||||
return reply.append( length-l, c );
|
||||
}
|
||||
|
||||
string
|
||||
string
|
||||
lpad( const string & s, size_t length, char c )
|
||||
{
|
||||
string::size_type l = s.length();
|
||||
@@ -268,12 +273,12 @@ namespace simgear {
|
||||
string result; // reserve size of 's'?
|
||||
string::const_iterator it = s.begin(),
|
||||
end = s.end();
|
||||
|
||||
|
||||
// advance to first non-space char - simplifes logic in main loop,
|
||||
// since we can always prepend a single space when we see a
|
||||
// since we can always prepend a single space when we see a
|
||||
// space -> non-space transition
|
||||
for (; (it != end) && isspace(*it); ++it) { /* nothing */ }
|
||||
|
||||
|
||||
bool lastWasSpace = false;
|
||||
for (; it != end; ++it) {
|
||||
char c = *it;
|
||||
@@ -281,18 +286,18 @@ namespace simgear {
|
||||
lastWasSpace = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (lastWasSpace) {
|
||||
result.push_back(' ');
|
||||
}
|
||||
|
||||
|
||||
lastWasSpace = false;
|
||||
result.push_back(c);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int to_int(const std::string& s, int base)
|
||||
{
|
||||
stringstream ss(s);
|
||||
@@ -301,12 +306,12 @@ namespace simgear {
|
||||
case 16: ss >> std::hex; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
int result;
|
||||
ss >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int compare_versions(const string& v1, const string& v2)
|
||||
{
|
||||
vector<string> v1parts(split(v1, "."));
|
||||
@@ -325,7 +330,7 @@ namespace simgear {
|
||||
// reached end - longer wins
|
||||
return v1parts.size() - v2parts.size();
|
||||
}
|
||||
|
||||
|
||||
string join(const string_list& l, const string& joinWith)
|
||||
{
|
||||
string result;
|
||||
@@ -336,10 +341,10 @@ namespace simgear {
|
||||
result += joinWith;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string uppercase(const string &s) {
|
||||
string rslt(s);
|
||||
for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
|
||||
@@ -361,47 +366,66 @@ namespace simgear {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static WCharVec convertMultiByteToWString(DWORD encoding, const std::string& a)
|
||||
#if defined(SG_WINDOWS)
|
||||
static std::wstring convertMultiByteToWString(DWORD encoding, const std::string& a)
|
||||
{
|
||||
WCharVec result;
|
||||
std::vector<wchar_t> result;
|
||||
DWORD flags = 0;
|
||||
int requiredWideChars = MultiByteToWideChar(encoding, flags,
|
||||
int requiredWideChars = MultiByteToWideChar(encoding, flags,
|
||||
a.c_str(), a.size(),
|
||||
NULL, 0);
|
||||
result.resize(requiredWideChars);
|
||||
MultiByteToWideChar(encoding, flags, a.c_str(), a.size(),
|
||||
result.data(), result.size());
|
||||
return result;
|
||||
return std::wstring(result.data(), result.size());
|
||||
}
|
||||
|
||||
WCharVec convertUtf8ToWString(const std::string& a)
|
||||
static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring& w)
|
||||
{
|
||||
return convertMultiByteToWString(CP_UTF8, a);
|
||||
std::vector<char> result;
|
||||
DWORD flags = 0;
|
||||
int requiredMBChars = WideCharToMultiByte(encoding, flags,
|
||||
w.data(), w.size(),
|
||||
NULL, 0, NULL, NULL);
|
||||
result.resize(requiredMBChars);
|
||||
WideCharToMultiByte(encoding, flags,
|
||||
w.data(), w.size(),
|
||||
result.data(), result.size(), NULL, NULL);
|
||||
return std::string(result.data(), result.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
std::wstring convertUtf8ToWString(const std::string& a)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertMultiByteToWString(CP_UTF8, a);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string convertWStringToUtf8(const std::wstring& w)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertWStringToMultiByte(CP_UTF8, w);
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string convertWindowsLocal8BitToUtf8(const std::string& a)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
DWORD flags = 0;
|
||||
WCharVec wideString = convertMultiByteToWString(CP_ACP, a);
|
||||
|
||||
// convert down to UTF-8
|
||||
std::vector<char> result;
|
||||
int requiredUTF8Chars = WideCharToMultiByte(CP_UTF8, flags,
|
||||
wideString.data(), wideString.size(),
|
||||
NULL, 0, NULL, NULL);
|
||||
result.resize(requiredUTF8Chars);
|
||||
WideCharToMultiByte(CP_UTF8, flags,
|
||||
wideString.data(), wideString.size(),
|
||||
result.data(), result.size(), NULL, NULL);
|
||||
return std::string(result.data(), result.size());
|
||||
return convertWStringToMultiByte(CP_UTF8, convertMultiByteToWString(CP_ACP, a));
|
||||
#else
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string convertUtf8ToWindowsLocal8Bit(const std::string& a)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertWStringToMultiByte(CP_ACP, convertMultiByteToWString(CP_UTF8, a));
|
||||
#else
|
||||
return a;
|
||||
#endif
|
||||
@@ -454,8 +478,8 @@ static const unsigned char base64_decode_map[128] =
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 127, 127, 127, 127, 127
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
@@ -471,46 +495,46 @@ void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>&
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
|
||||
|
||||
while (in_len-- && ( encoded_string[in_] != '=')) {
|
||||
if (is_whitespace( encoded_string[in_])) {
|
||||
in_++;
|
||||
in_++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!is_base64(encoded_string[in_])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++)
|
||||
char_array_4[i] = base64_decode_map[char_array_4[i]];
|
||||
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret.push_back(char_array_3[i]);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (i) {
|
||||
for (j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
char_array_4[j] = base64_decode_map[char_array_4[j]];
|
||||
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
@@ -531,7 +555,7 @@ std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
|
||||
hex[i * 2] = hexChar[c >> 4];
|
||||
hex[i * 2 + 1] = hexChar[c & 0x0f];
|
||||
}
|
||||
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
@@ -597,7 +621,7 @@ string sanitizePrintfFormat(const string& input)
|
||||
SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input);
|
||||
return string();
|
||||
}
|
||||
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -615,7 +639,7 @@ std::string error_string(int errnum)
|
||||
retcode = strerror_s(buf, sizeof(buf), errnum);
|
||||
#elif defined(_GNU_SOURCE)
|
||||
return std::string(strerror_r(errnum, buf, sizeof(buf)));
|
||||
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC)
|
||||
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__)
|
||||
int retcode;
|
||||
// POSIX.1-2001 and POSIX.1-2008
|
||||
retcode = strerror_r(errnum, buf, sizeof(buf));
|
||||
@@ -645,5 +669,5 @@ std::string error_string(int errnum)
|
||||
}
|
||||
|
||||
} // end namespace strutils
|
||||
|
||||
|
||||
} // end namespace simgear
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
// $Id$
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include "zfstream.hxx"
|
||||
|
||||
@@ -110,7 +114,14 @@ gzfilebuf::open( const char *name, ios_openmode io_mode )
|
||||
|
||||
char char_mode[10];
|
||||
cvt_iomode( char_mode, io_mode );
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ws = simgear::strutils::convertUtf8ToWString(std::string(name));
|
||||
if ( (file = gzopen_w(ws.c_str(), char_mode)) == NULL ) {
|
||||
|
||||
#else
|
||||
if ( (file = gzopen(name, char_mode)) == NULL ) {
|
||||
#endif
|
||||
// perror( "gzfilebuf::open(): " );
|
||||
errno = 0;
|
||||
return NULL;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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,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());
|
||||
@@ -208,7 +208,7 @@ void testAddCatalog(HTTP::Client* cl)
|
||||
p.append("org.flightgear.test.catalog1");
|
||||
p.append("catalog.xml");
|
||||
VERIFY(p.exists());
|
||||
COMPARE(root->allPackages().size(), 3);
|
||||
COMPARE(root->allPackages().size(), 4);
|
||||
COMPARE(root->catalogs().size(), 1);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("alpha");
|
||||
@@ -403,6 +403,42 @@ void testRefreshCatalog(HTTP::Client* cl)
|
||||
COMPARE(root->getPackageById("common-sounds")->revision(), 11);
|
||||
}
|
||||
|
||||
void testInstallTarPackage(HTTP::Client* cl)
|
||||
{
|
||||
SGPath rootPath(simgear::Dir::current().path());
|
||||
rootPath.append("pkg_install_tar");
|
||||
simgear::Dir pd(rootPath);
|
||||
pd.removeChildren();
|
||||
|
||||
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
|
||||
// specify a test dir
|
||||
root->setHTTPClient(cl);
|
||||
|
||||
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
|
||||
COMPARE(p1->id(), "b737-NG");
|
||||
pkg::InstallRef ins = p1->install();
|
||||
|
||||
VERIFY(ins->isQueued());
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
VERIFY(p1->isInstalled());
|
||||
VERIFY(p1->existingInstall() == ins);
|
||||
|
||||
// verify on disk state
|
||||
SGPath p(rootPath);
|
||||
p.append("org.flightgear.test.catalog1");
|
||||
p.append("Aircraft");
|
||||
p.append("b737NG");
|
||||
|
||||
COMPARE(p, ins->path());
|
||||
|
||||
p.append("b737-900-set.xml");
|
||||
VERIFY(p.exists());
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
@@ -425,6 +461,8 @@ int main(int argc, char* argv[])
|
||||
|
||||
testRefreshCatalog(&cl);
|
||||
|
||||
testInstallTarPackage(&cl);
|
||||
|
||||
std::cout << "Successfully passed all tests!" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ void Root::refresh(bool aForce)
|
||||
CatalogList toRefresh;
|
||||
CatalogDict::iterator it = d->catalogs.begin();
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
int age = it->second->ageInSeconds();
|
||||
unsigned int age = it->second->ageInSeconds();
|
||||
if (aForce || (age > maxAgeSeconds())) {
|
||||
toRefresh.push_back(it->second);
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -78,6 +78,30 @@
|
||||
<md5>acf9eb89cf396eb42f8823d9cdf17584</md5>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>b737-NG</id>
|
||||
<name>Boeing 737 NG</name>
|
||||
<dir>b737NG</dir>
|
||||
<description>A popular twin-engined narrow body jet</description>
|
||||
<revision type="int">112</revision>
|
||||
<file-size-bytes type="int">860</file-size-bytes>
|
||||
|
||||
<tag>boeing</tag>
|
||||
<tag>jet</tag>
|
||||
<tag>ifr</tag>
|
||||
|
||||
<rating>
|
||||
<FDM type="int">5</FDM>
|
||||
<systems type="int">5</systems>
|
||||
<model type="int">4</model>
|
||||
<cockpit type="int">4</cockpit>
|
||||
</rating>
|
||||
|
||||
<md5>a94ca5704f305b90767f40617d194ed6</md5>
|
||||
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
|
||||
</package>
|
||||
|
||||
|
||||
<package>
|
||||
<id>dc3</id>
|
||||
<name>DC-3</name>
|
||||
|
||||
@@ -68,6 +68,29 @@
|
||||
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>b737-NG</id>
|
||||
<name>Boeing 737 NG</name>
|
||||
<dir>b737NG</dir>
|
||||
<description>A popular twin-engined narrow body jet</description>
|
||||
<revision type="int">111</revision>
|
||||
<file-size-bytes type="int">860</file-size-bytes>
|
||||
|
||||
<tag>boeing</tag>
|
||||
<tag>jet</tag>
|
||||
<tag>ifr</tag>
|
||||
|
||||
<rating>
|
||||
<FDM type="int">5</FDM>
|
||||
<systems type="int">5</systems>
|
||||
<model type="int">4</model>
|
||||
<cockpit type="int">4</cockpit>
|
||||
</rating>
|
||||
|
||||
<md5>a94ca5704f305b90767f40617d194ed6</md5>
|
||||
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>common-sounds</id>
|
||||
<name>Common sound files for test catalog aircraft</name>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "props.hxx"
|
||||
#include "props_io.hxx"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
@@ -394,7 +396,7 @@ int main (int ac, char ** av)
|
||||
try {
|
||||
cout << "Reading " << av[i] << endl;
|
||||
SGPropertyNode root;
|
||||
readProperties(av[i], &root);
|
||||
readProperties(SGPath::fromLocal8Bit(av[i]), &root);
|
||||
writeProperties(cout, &root, true);
|
||||
cout << endl;
|
||||
} catch (std::string &message) {
|
||||
|
||||
@@ -149,7 +149,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
|
||||
SGPath tpath("Textures");
|
||||
tpath.append(tname);
|
||||
std::string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
|
||||
std::string fullTexPath = SGModelLib::findDataFile(tpath.local8BitStr(), options);
|
||||
if (fullTexPath.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
<< tname << "\" in Textures folders.");
|
||||
@@ -181,7 +181,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
|
||||
SGPath tpath("Textures");
|
||||
tpath.append(tname);
|
||||
std::string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
|
||||
std::string fullTexPath = SGModelLib::findDataFile(tpath.local8BitStr(), options);
|
||||
if (fullTexPath.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
<< tname << "\" in Textures folders.");
|
||||
@@ -207,7 +207,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
SGPath tpath("Textures");
|
||||
tpath.append("Terrain");
|
||||
tpath.append("unknown.rgb");
|
||||
_internal_state st( NULL, tpath.str(), true, options );
|
||||
_internal_state st( NULL, tpath.local8BitStr(), true, options );
|
||||
_status.push_back( st );
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
if (! omname.empty()) {
|
||||
SGPath ompath("Textures");
|
||||
ompath.append(omname);
|
||||
std::string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
|
||||
std::string fullMaskPath = SGModelLib::findDataFile(ompath.local8BitStr(), options);
|
||||
|
||||
if (fullMaskPath.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
@@ -340,7 +340,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
if (! treeTexPath.empty()) {
|
||||
SGPath treePath("Textures");
|
||||
treePath.append(treeTexPath);
|
||||
tree_texture = SGModelLib::findDataFile(treePath.str(), options);
|
||||
tree_texture = SGModelLib::findDataFile(treePath.local8BitStr(), options);
|
||||
|
||||
if (tree_texture.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
|
||||
@@ -93,7 +93,7 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
|
||||
// Read name node purely for logging purposes
|
||||
const SGPropertyNode *nameNode = node->getChild("name");
|
||||
if (nameNode) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "Loading region "
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "Loading region "
|
||||
<< nameNode->getStringValue());
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ bool SGMaterialLib::load( const string &fg_root, const string& mpath,
|
||||
fabs(x2 - x1),
|
||||
fabs(y2 - y1));
|
||||
arealist->push_back(rect);
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " Area ("
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, " Area ("
|
||||
<< rect.x() << ","
|
||||
<< rect.y() << ") width:"
|
||||
<< rect.width() << " height:"
|
||||
|
||||
@@ -107,7 +107,11 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea)
|
||||
if (_buttons.find(button) == _buttons.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!anyBindingEnabled(_bindingsDown)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fireBindingList(_bindingsDown);
|
||||
_repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
|
||||
return true;
|
||||
@@ -136,7 +140,7 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea)
|
||||
virtual bool hover( const osg::Vec2d& windowPos,
|
||||
const Info& )
|
||||
{
|
||||
if (_hover.empty()) {
|
||||
if (!anyBindingEnabled(_hover)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
SGPropertyNode *overlay)
|
||||
{
|
||||
if (!path.exists()) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path.str() << "\"");
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path << "\"");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
// Check for an XML wrapper
|
||||
if (modelpath.extension() == "xml") {
|
||||
try {
|
||||
readProperties(modelpath.str(), props);
|
||||
readProperties(modelpath, props);
|
||||
} catch (const sg_exception &t) {
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: "
|
||||
<< t.getFormattedMessage());
|
||||
@@ -304,7 +304,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir);
|
||||
if (modelpath.isNull())
|
||||
throw sg_io_exception("Model file not found: '" + modelPathStr + "'",
|
||||
path.str());
|
||||
path);
|
||||
|
||||
if (props->hasValue("/texture-path")) {
|
||||
string texturePathStr = props->getStringValue("/texture-path");
|
||||
@@ -313,7 +313,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
texturepath = SGModelLib::findDataFile(texturePathStr, NULL, modelDir);
|
||||
if (texturepath.isNull())
|
||||
throw sg_io_exception("Texture file not found: '" + texturePathStr + "'",
|
||||
path.str());
|
||||
path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -333,12 +333,12 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
if (!texturepath.extension().empty())
|
||||
texturepath = texturepath.dir();
|
||||
|
||||
options->setDatabasePath(texturepath.str());
|
||||
options->setDatabasePath(texturepath.local8BitStr());
|
||||
osgDB::ReaderWriter::ReadResult modelResult;
|
||||
modelResult = osgDB::readNodeFile(modelpath.str(), options.get());
|
||||
modelResult = osgDB::readNodeFile(modelpath.local8BitStr(), options.get());
|
||||
if (!modelResult.validNode())
|
||||
throw sg_io_exception("Failed to load 3D model:" + modelResult.message(),
|
||||
modelpath.str());
|
||||
modelpath);
|
||||
model = copyModel(modelResult.getNode());
|
||||
// Add an extra reference to the model stored in the database.
|
||||
// That is to avoid expiring the object from the cache even if
|
||||
@@ -362,7 +362,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
SetNodeMaskVisitor setNodeMaskVisitor(0, simgear::MODELLIGHT_BIT);
|
||||
model->accept(setNodeMaskVisitor);
|
||||
}
|
||||
model->setName(modelpath.str());
|
||||
model->setName(modelpath.utf8Str());
|
||||
|
||||
bool needTransform=false;
|
||||
// Set up the alignment node if needed
|
||||
@@ -413,7 +413,7 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
bool isInterior = (std::string(sub_props->getStringValue("usage")) == "interior");
|
||||
bool isAI = (std::string(prop_root->getStringValue("type")) == "AI");
|
||||
if(isInterior && isAI){
|
||||
props->addChild("interior-path")->setStringValue(submodelPath.str());
|
||||
props->addChild("interior-path")->setStringValue(submodelPath.utf8Str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -485,14 +485,14 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
std::vector<SGPropertyNode_ptr> particle_nodes;
|
||||
particle_nodes = props->getChildren("particlesystem");
|
||||
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
|
||||
SG_LOG(SG_PARTICLES, SG_DEBUG, "Reading in particle " << i << " from file: " << path.str());
|
||||
SG_LOG(SG_PARTICLES, SG_DEBUG, "Reading in particle " << i << " from file: " << path);
|
||||
osg::ref_ptr<SGReaderWriterOptions> options2;
|
||||
options2 = new SGReaderWriterOptions(*options);
|
||||
if (i==0) {
|
||||
if (!texturepath.extension().empty())
|
||||
texturepath = texturepath.dir();
|
||||
|
||||
options2->setDatabasePath(texturepath.str());
|
||||
options2->setDatabasePath(texturepath.local8BitStr());
|
||||
}
|
||||
group->addChild(Particles::appendParticles(particle_nodes[i],
|
||||
prop_root,
|
||||
@@ -536,18 +536,18 @@ sgLoad3DModel_internal(const SGPath& path,
|
||||
|
||||
/// OSGFIXME: duh, why not only model?????
|
||||
SGAnimation::animate(group.get(), animation_nodes[i], prop_root,
|
||||
options.get(), path.str(), i);
|
||||
options.get(), path.local8BitStr(), i);
|
||||
}
|
||||
|
||||
if (!needTransform && group->getNumChildren() < 2) {
|
||||
model = group->getChild(0);
|
||||
group->removeChild(model.get());
|
||||
if (data.valid())
|
||||
data->modelLoaded(modelpath.str(), props, model.get());
|
||||
data->modelLoaded(modelpath.utf8Str(), props, model.get());
|
||||
return model.release();
|
||||
}
|
||||
if (data.valid())
|
||||
data->modelLoaded(modelpath.str(), props, group.get());
|
||||
data->modelLoaded(modelpath.utf8Str(), props, group.get());
|
||||
if (props->hasChild("debug-outfile")) {
|
||||
std::string outputfile = props->getStringValue("debug-outfile",
|
||||
"debug-model.osg");
|
||||
|
||||
@@ -97,7 +97,7 @@ osg::Node * SGText::appendText(const SGPropertyNode* configNode,
|
||||
|
||||
SGPath path("Fonts" );
|
||||
path.append( configNode->getStringValue( "font", "Helvetica" ));
|
||||
text->setFont( path.str() );
|
||||
text->setFont( path.local8BitStr() );
|
||||
|
||||
text->setCharacterSize(configNode->getDoubleValue("character-size", 1.0 ),
|
||||
configNode->getDoubleValue("character-aspect-ratio", 1.0 ));
|
||||
|
||||
@@ -47,7 +47,7 @@ SGLoadTexture2D(const SGPath& path,
|
||||
bool wrapu = true, bool wrapv = true,
|
||||
int mipmaplevels = -1)
|
||||
{
|
||||
return SGLoadTexture2D(true, path.str(), options, wrapu, wrapv,
|
||||
return SGLoadTexture2D(true, path.local8BitStr(), options, wrapu, wrapv,
|
||||
mipmaplevels);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
|
||||
bool wrapu = true, bool wrapv = true,
|
||||
int mipmaplevels = -1)
|
||||
{
|
||||
return SGLoadTexture2D(staticTexture, path.str(), options, wrapu, wrapv,
|
||||
return SGLoadTexture2D(staticTexture, path.local8BitStr(), options, wrapu, wrapv,
|
||||
mipmaplevels);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ std::string SGModelLib::findDataFile(const std::string& file,
|
||||
return file;
|
||||
SGPath p = ResourceManager::instance()->findPath(file, currentPath);
|
||||
if (p.exists()) {
|
||||
return p.str();
|
||||
return p.local8BitStr();
|
||||
}
|
||||
|
||||
// finally hand on to standard OSG behaviour
|
||||
|
||||
@@ -78,7 +78,7 @@ SGMakeState(const SGPath &path, const char* colorTexture,
|
||||
osg::StateSet *stateSet = new osg::StateSet;
|
||||
|
||||
osg::ref_ptr<SGReaderWriterOptions> options;
|
||||
options = SGReaderWriterOptions::fromPath(path.str());
|
||||
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
|
||||
stateSet->setTextureAttribute(0, SGLoadTexture2D(colorTexture,
|
||||
options.get()));
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
|
||||
|
||||
@@ -76,7 +76,7 @@ SGMoon::build( SGPath path, double moon_size ) {
|
||||
|
||||
// set up the orb state
|
||||
osg::ref_ptr<SGReaderWriterOptions> options;
|
||||
options = SGReaderWriterOptions::fromPath(path.str());
|
||||
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
|
||||
|
||||
osg::Texture2D* texture = SGLoadTexture2D("moon.png", options.get());
|
||||
stateSet->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
|
||||
|
||||
@@ -118,7 +118,7 @@ SGNewCloud::SGNewCloud(const SGPath &texture_root, const SGPropertyNode *cld_def
|
||||
"image"),
|
||||
texture);
|
||||
ref_ptr<SGReaderWriterOptions> options;
|
||||
options = SGReaderWriterOptions::fromPath(texture_root.str());
|
||||
options = SGReaderWriterOptions::fromPath(texture_root.local8BitStr());
|
||||
effect = makeEffect(pcloudEffect, true, options.get());
|
||||
if (effect.valid())
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ SGSun::build( SGPath path, double sun_size, SGPropertyNode *property_tree_Node )
|
||||
env_node = property_tree_Node;
|
||||
|
||||
osg::ref_ptr<SGReaderWriterOptions> options;
|
||||
options = SGReaderWriterOptions::fromPath(path.str());
|
||||
options = SGReaderWriterOptions::fromPath(path.local8BitStr());
|
||||
// build the ssg scene graph sub tree for the sky and connected
|
||||
// into the provide scene graph branch
|
||||
sun_transform = new osg::MatrixTransform;
|
||||
|
||||
@@ -221,7 +221,7 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
|
||||
SGPath path = filePath;
|
||||
path.append(".."); path.append(".."); path.append("..");
|
||||
sharedOptions->getDatabasePathList().push_back(path.str());
|
||||
sharedOptions->getDatabasePathList().push_back(path.local8BitStr());
|
||||
|
||||
// ensure Models directory synced via TerraSync is searched before the copy in
|
||||
// FG_ROOT, so that updated models can be used.
|
||||
@@ -346,7 +346,7 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
_Object obj;
|
||||
obj._errorLocation = absoluteFileName;
|
||||
obj._token = token;
|
||||
obj._name = path.str();
|
||||
obj._name = path.local8BitStr();
|
||||
obj._options = staticOptions(filePath, options);
|
||||
_objectList.push_back(obj);
|
||||
}
|
||||
@@ -356,7 +356,7 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
_Object obj;
|
||||
obj._errorLocation = absoluteFileName;
|
||||
obj._token = token;
|
||||
obj._name = path.str();
|
||||
obj._name = path.local8BitStr();
|
||||
obj._options = staticOptions(filePath, options);
|
||||
_objectList.push_back(obj);
|
||||
}
|
||||
@@ -555,13 +555,13 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
|
||||
objects.append("Objects");
|
||||
objects.append(basePath);
|
||||
objects.append(fileName);
|
||||
modelBin.read(objects.str(), options);
|
||||
modelBin.read(objects.local8BitStr(), options);
|
||||
|
||||
SGPath terrain(*i);
|
||||
terrain.append("Terrain");
|
||||
terrain.append(basePath);
|
||||
terrain.append(fileName);
|
||||
modelBin.read(terrain.str(), options);
|
||||
modelBin.read(terrain.local8BitStr(), options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ Geometry* createTreeGeometry(float width, float height, int varieties)
|
||||
{
|
||||
quadGeom->setSecondaryColorArray(new Vec3Array);
|
||||
quadGeom->setSecondaryColorBinding(Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
}
|
||||
FloatArray* rotation = new FloatArray(3);
|
||||
(*rotation)[0] = 0.0;
|
||||
(*rotation)[1] = PI_2;
|
||||
@@ -205,8 +205,8 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
|
||||
Geometry* geom
|
||||
= static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
|
||||
Vec3Array* posArray = static_cast<Vec3Array*>(geom->getColorArray());
|
||||
Vec3Array* tnormalArray;
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
Vec3Array* tnormalArray = NULL;
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
|
||||
if (posArray->size()
|
||||
>= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
|
||||
@@ -215,12 +215,12 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
|
||||
Vec3 params = (*paramsArray)[0];
|
||||
geom = createTreeGeometry(params.x(), params.y(), params.z());
|
||||
posArray = static_cast<Vec3Array*>(geom->getColorArray());
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
|
||||
geode->addDrawable(geom);
|
||||
}
|
||||
posArray->insert(posArray->end(), 4, pos);
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray->insert(tnormalArray->end(),4,ter);}
|
||||
size_t numVerts = posArray->size();
|
||||
int imax = 2;
|
||||
@@ -241,14 +241,14 @@ namespace
|
||||
{
|
||||
struct MakeTreesLeaf
|
||||
{
|
||||
MakeTreesLeaf(float range, int varieties, float width, float height,
|
||||
MakeTreesLeaf(float range, int varieties, float width, float height,
|
||||
Effect* effect) :
|
||||
_range(range), _varieties(varieties),
|
||||
_width(width), _height(height), _effect(effect) {}
|
||||
|
||||
MakeTreesLeaf(const MakeTreesLeaf& rhs) :
|
||||
_range(rhs._range),
|
||||
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
|
||||
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
|
||||
_effect(rhs._effect)
|
||||
{}
|
||||
|
||||
@@ -356,7 +356,7 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
|
||||
MatrixTransform* mt = new MatrixTransform(transform);
|
||||
|
||||
SGTreeBinList::iterator i;
|
||||
|
||||
|
||||
use_tree_shadows = false;
|
||||
use_tree_normals = false;
|
||||
if (options) {
|
||||
@@ -368,7 +368,7 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
|
||||
use_tree_normals
|
||||
= propertyNode->getBoolValue("/sim/rendering/random-vegetation-normals",
|
||||
use_tree_normals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = forestList.begin(); i != forestList.end(); ++i) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user