Compare commits

...

209 Commits

Author SHA1 Message Date
Automatic Release Builder
277dab0d55 new version: 2016.4.1 2016-11-17 13:43:29 +01:00
Erik Hofman
321a3fdaba Fix a typo 2016-11-17 13:05:08 +01:00
Erik Hofman
2eb17d0083 Revetr previous paatch for SGVec4 so close to the release 2016-11-17 11:50:15 +01:00
Erik Hofman
ff7e4597e7 Maximize simd optimization 2016-11-17 11:48:16 +01:00
Erik Hofman
4a4baf1b42 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2016-11-17 09:15:02 +01:00
Erik Hofman
2672d5cd11 include simd.hxx 2016-11-17 09:14:51 +01:00
Erik Hofman
1bf3d7c9b1 Add a copyright header 2016-11-16 15:34:25 +01:00
Erik Hofman
f7c0a7f933 Add a simd_t<int> class and more inlining 2016-11-16 14:00:25 +01:00
Erik Hofman
6bf864babb Add class assingment functions 2016-11-16 13:33:51 +01:00
Erik Hofman
43bd1b15ee Add a SIMD accelerator class for float[4] and double[4] types 2016-11-16 12:45:14 +01:00
Rebecca N. Palmer
14c79d9ffb Nasal: disable NASAL_NAN64 on non-x86
Not every architecture has <=48-bit virtual address spaces:
http://meetings-archive.debian.net/pub/debian-meetings/2016/miniconf_cambridge16/Thanks_for_the_Memory.webm
2016-11-15 22:50:41 +00:00
James Turner
3009aadaa6 Simplify Boost auto-detection on MSVC. 2016-11-14 23:05:10 +01:00
James Turner
fca64343ae Attempting to fix Windows include issues.
Only handle PackageRef by reference in the header file, so we don’t
create calls to the copy constructor and/or destructor. If this doesn’t
work will need to create a stub implementation file.
2016-11-14 07:52:15 +01:00
James Turner
042a2659f6 Disable Codecvt for now, libstdc++ is problematic.
Need to research which libstdc++ versions implement codecvt.
2016-11-14 07:45:23 +01:00
James Turner
fe54af405c Zlib is a public, not private include in Simgear.
Should fix Windows compilation as reported by Alan Teeder on the list.
2016-11-14 07:31:54 +01:00
James Turner
ab70090a0a Finally fix bogus terrasync download numbers.
(Staying a hotel with sufficiently slow wi-fi to debug this!)
2016-11-13 22:18:32 +01:00
James Turner
ee02750e95 Enforce VS2013/2015 requirement on Windows. 2016-11-13 14:57:39 +00:00
James Turner
f55007394e Make more includes target-specific. 2016-11-13 14:57:21 +00:00
James Turner
60a9e8fb7e Implement UTF-8 conversion using codecvt 2016-11-13 14:56:52 +00:00
James Turner
7f8455f731 Multi-thumbnail support in packages.
Allow multiple thumbnails per variant / package, including
tagging with types so we can require certain thumbnails in the future.
2016-11-13 14:56:31 +00:00
James Turner
6ae86fc4ca Fix a warning. 2016-11-13 12:02:12 +00:00
Torsten Dreyer
e1fb13bed8 Allow passing of service and protocol for DNS SRV requests 2016-11-09 13:30:58 +01:00
Torsten Dreyer
27fff3b72a Sort DNS TXT entries and provide an attribute map 2016-11-09 12:03:37 +01:00
Torsten Dreyer
6a9235223e Set correct class for DNS TXT queries 2016-11-09 09:44:10 +01:00
Stuart Buchanan
c1e50e9a9c Correct mesh size correction for building LOD ranges 2016-11-08 20:06:34 +00:00
Stuart Buchanan
dcbf5b7c11 Fix for OBJECT_BUILDING_MESH_DETAILED 2016-11-08 19:56:08 +00:00
Torsten Dreyer
4b571f2a24 Implement SRV and TXT DNS records 2016-11-08 17:09:37 +01:00
Erik Hofman
679b7b845c Always define the class destructor 2016-11-08 13:17:15 +01:00
Edward d'Auvergne
50d7127c51 Calculation of the illuminance factor for the moon.
This is a number ranging between 0 and 1 based on the log of the illuminance of
the moon outside the atmosphere.  It is calculated as

    factor = (log(I) - max_loglux) / (max_loglux - min_loglux) + 1.0,

where I is the illuminance of the moon outside the atmosphere and min_loglux and
max_loglux are hardcoded to -0.504030345621 and -4.39964634562 respectively.
Although the value should never be outside of [0, 1], for safety it is clipped
to be between these values.  For more background, see
http://forum.flightgear.org/viewtopic.php?f=47&t=28201&start=60#p270516 .
2016-11-08 09:49:57 +01:00
Edward d'Auvergne
0e09ee4bce Calculation of the log of the illuminance of the moon outside the atmosphere.
This is the base 10 log of equation 20, converted from foot-candles to lux,
from:

   Krisciunas K. and Schaefer B.E. (1991). A model of the brightness of
moonlight, Publ. Astron.  Soc. Pacif. 103(667), 1033-1039 (DOI:
http://dx.doi.org/10.1086/132921).
2016-11-08 09:49:57 +01:00
Edward d'Auvergne
76ebd569d5 Calculation and exposure of the moon's age and phase.
To obtain the sun's true longitude, the Star::getlonEcl() function has been
created.  The moon's age is then calculated as the difference between the moon's
and sun's true longitudes.  The phase is then simply half of one minus the
cosine of the age.  Hence these calculations are very cheap compared to the rest
of the moon position calculations.  The algorithm is from:

    Duffett-Smith, Peter. Practical Astronomy With Your Calculator. 3rd ed.
Cambridge: Cambridge University Press, 1981. ISBN 0-521-28411-2.

The code can replicate the example in the book of Feb 26, 1979 at 16h UT, with
an age of -0.4767 degrees a phase of 0.0:

$ fgfs --aircraft=UFO --start-date-gmt=1979:02:26:16:00:00 --airport=EGLL \
--altitude=50000 --enable-hud

The calculated phase is 1.459e-5 and the age is -6.2908 (which is -0.43628
degrees).  For a recent full moon:

$ fgfs --aircraft=UFO --start-date-gmt=2015:11:25:22:44:00 --airport=EGLL \
--altitude=50000 --enable-hud

The calculated age is -3.1413 and the phase is 0.9999999778.
2016-11-08 09:49:57 +01:00
Edward d'Auvergne
edcd42bc2d Optimisation of the celestialBody ephemeris code.
By storing repetitive intermediate calculations, the number of mathematical
operations for a single call to CelestialBody::updatePosition() has decreased by
12.  This matches the changes to MoonPos::updatePosition().
2016-11-08 09:49:57 +01:00
Edward d'Auvergne
f04e501472 Exposure of the moon position.
The following functions have been added:  MoonPos::getM(), MoonPos::getw(),
MoonPos::getxg(), MoonPos::getyg(), MoonPos::getye(), MoonPos::getze(),
MoonPos::getDistance().  These are copied from and match the Star class
functions (but with xs and ys replaced by xg and yg).
2016-11-08 09:49:57 +01:00
Edward d'Auvergne
32d152ba38 A few spelling fixes for the moon position ephemeris code. 2016-11-08 09:49:57 +01:00
Edward d'Auvergne
94c4c44d92 Optimisation of the moon position ephemeris code.
By storing repetitive intermediate calculations, the number of mathematical
operations for a single call to MoonPos::updatePosition() has decreased by 32.
2016-11-08 09:49:57 +01:00
James Turner
ab1d4e651e Bugfix: avoid bogus download size on start
Missed a default init of the HTTP repo download content size, so until
the request response header was received, this could report a very
large value.
2016-11-07 15:49:07 +01:00
James Turner
5cd250e452 Packages: notify delegate of uninstalls. 2016-11-06 21:58:01 +01:00
James Turner
2dcff4bb8e Ensure build include location is used first.
Otherwise PREFIX_PATH headers might be found first, but these could be
from an older SimGear version.
2016-11-06 21:57:30 +01:00
James Turner
604a9ff614 Hopefully fix Win32 linkage. 2016-11-02 22:57:39 +00:00
James Turner
a09630bcca Cmake export fixes.
Should hopefully fix importing in FlightGear
2016-11-02 13:59:43 +00:00
James Turner
b44c70b3f4 Simplify isnan detection with C++11 library. 2016-10-30 22:49:38 +00:00
James Turner
e4cddb100e Drastically simplify compiler.h 2016-10-30 22:49:11 +00:00
Erik Hofman
e3a4144e6c Switch to SGMisc::isNaN 2016-10-28 11:18:40 +02:00
James Turner
51e7d95bf2 Require CMake 3.0, enable C++11
Let’s see what this breaks.
2016-10-24 22:52:51 +02:00
James Turner
202571386b Exported package fix.
https://sourceforge.net/p/flightgear/codetickets/1892/
2016-10-20 20:26:51 +01:00
Florent Rougon
7837bd0e11 Add parameter 'use_exact_name' to sg_gzifstream's constructor and open() method
This allows one to be sure about which file is opened.
2016-10-20 14:07:08 +02:00
Florent Rougon
11c6e5bf04 Require zlib 1.2.4 or compatible
Commit 8277857827 relies on zlib's
gzoffset() function (not just offset(): that was a typo in the commit
message, sorry). This function appeared in zlib 1.2.4 (which dates from
2010). Enforce this requirement with CMake.
2016-10-19 21:17:54 +02:00
Florent Rougon
8277857827 Add gzfilebuf::approxOffset() and sg_gzifstream::approxOffset()
gzfilebuf::approxOffset() is a wrapper for zlib's offset() function.
It can be useful to implement progress indicators and such.
2016-10-19 00:30:55 +02:00
Florent Rougon
412111ba5a Change sgSearchPathSep into a public static member: SGPath::pathListSep
This way, one can easily use the OS-dependent separarator for path lists in
other places to build and split path lists in an optimal way (e.g., not
sacrificing ';' on Unix, since the path list separator is ':' on this
platform).
2016-10-12 09:05:21 +02:00
James Turner
dd52b6af50 Remove some archaic code. 2016-10-09 11:08:17 +02:00
Erik Hofman
a97c145f56 Always return a value 2016-10-04 09:08:43 +02:00
Erik Hofman
b9deebb59d It's perfectly valid to call is_sample_stopped() even if the sample was already stopped. So remove the assert and replace it with an if-test 2016-10-03 09:55:31 +02:00
Richard Harrison
906813c90b Console handling: VS2015 seems to only work with redirection when both stdout and stderr are redirected; so show a message box error when redirecting only one stream. 2016-10-02 02:29:22 +02:00
Florent Rougon
1711592e64 A few more tests for simgear::strutils::split() 2016-10-01 10:44:33 +02:00
James Turner
7b2507cb19 Config changes to warn on older tools.
Warn on to-be-deprecated versions of Cmake, Visual Studio and GCC
2016-09-28 17:20:51 -05:00
James Turner
2e19aaaff9 Remove multi-arch workaround in CMake
Since we now require 2.8.11, we can rely on this bug being fixed.
2016-09-27 20:43:03 -05:00
James Turner
6854598b79 Initial pieces for full OBJ support.
Needs testing by aircraft developers, this is just the first piece.
2016-09-27 20:41:31 -05:00
Stuart Buchanan
809ddb21c9 Add STG verbs for building mesh integration.
Add OBJECT_BUILDING_MESH_ROUGH and OBJECT_BUILDING_MESH_DETAILED

Intended for use by OSM buildings.
2016-09-22 20:38:58 +01:00
Richard Harrison
38bab59c1a Revised Windows console handling
- When started from the console use the console (when no --console)

- When started from the GUI (with --console) open a new console window

- When started from the GUI (without --console) don't open a new console window; stdout/stderr will not appear (except in logfiles as they do now). This opens stderr/stdout on the NUL device to alleviate any potential issues

- When started from the Console (with --console) open a new console window

- Ensure that IO redirection still works when started from the console. When redirecting stdout stderr will also be redirected (providing it wasn't already via 2>&1) - otherwise output from stderr will be lost.

- When using redirection from the command prompt --console will produce an error message box.

Notes:
- fgfs needs to be a linked as Win32 GUI subsystem app - which it already is
- What can't be done is to make the cmd prompt run fgfs synchronously; this is only something that can be done via "start /wait fgfs"

Basically the way that Win32 works is quite sensible, but slightly at odds with the unix nature of the C-RTL; so the standard streams sort of get lost for GUI apps. AttachConsole and AllocConsole are provided to address this - but they do slightly different things. AttachConsole will attach to the cmd.exe (or any console related to the parent process), whereas AllocConsole will open a new one. Depending on where the application was launched from it makes sense to use AttachConsole for a cmd.exe launch and do nothing (unless --console is given) for a GUI launch.

Redirection is not available from the GUI (unless set in the Process Create block) - so really only available when launched from the command line. If any stream is redirected then all must be otherwise it appears that AttachConsole will undo the redirection by changing the standard handles.
2016-09-21 00:50:13 +02:00
James Turner
87590cafb2 Fix terrasync behaviour when net is down. 2016-09-07 22:49:03 +01:00
Automatic Release Builder
6e5cbd7fc5 new version: 2016.4.0 2016-09-06 12:50:55 +02:00
Automatic Release Builder
4200572cad new version: 2016.3.1 2016-09-06 12:50:55 +02:00
James Turner
9b997ea1f7 Only reopen the streams if AllocConsole succeeds.
Avoid a crash with FGRun when OSG tries to use the console.
2016-09-02 00:05:13 +01:00
Torsten Dreyer
c1ba974538 Make requested tsync scenery-version settable from prop
set /sim/terrasync/scenery-version=ws30 to filter the DNS NAPTR
records for service=ws30
current default (and only available) scenery version is ws20

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

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

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

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 2.8.11)
cmake_minimum_required (VERSION 3.0)
if(COMMAND cmake_policy)
if(POLICY CMP0054)
@@ -16,18 +16,28 @@ include (CheckCXXSourceCompiles)
include (CheckCXXCompilerFlag)
include (GenerateExportHeader)
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
# only relevant for building shared libs but let's set it regardless
set(CMAKE_OSX_RPATH 1)
project(SimGear)
# let's use & require C++11 - note these are only functional with CMake 3.1
# we do manual fallbacks for CMake 3.0 in the compilers section
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
# read 'version' file into a variable (stripping any newlines or spaces)
file(READ version versionFile)
string(STRIP ${versionFile} SIMGEAR_VERSION)
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
# Cmake documentation says we must set this before calling project(), but
# it only seems to be picked up setting it /after/ the call to project()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
# add a dependency on the versino file
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
# use simgear version also as the SO version (if building SOs)
@@ -63,8 +73,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "
set(CPACK_SOURCE_IGNORE_FILES
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
include (CPack)
# We have some custom .cmake scripts not in the official distribution.
@@ -87,38 +95,30 @@ message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
# Configure library search paths
#####################################################################################
if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
# Workaround for Ubuntu/Debian which introduced the "multiarch" library
# directory structure, which is unsupported by CMake < 2.8.10, so we need to
# add paths manually
# see http://www.cmake.org/Bug/view.php?id=12049 and
# http://www.cmake.org/Bug/view.php?id=12037
list(APPEND ADDITIONAL_LIBRARY_PATHS
/usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE}
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
/lib/${CMAKE_LIBRARY_ARCHITECTURE})
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
endif()
#####################################################################################
if (NOT MSVC)
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system libExpat" OFF)
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system expat library" OFF)
option(SYSTEM_UDNS "Set to ON to build SimGear using the system udns library" OFF)
else()
# Building SimGear DLLs is currently not supported for MSVC.
set(SIMGEAR_SHARED OFF)
# Using a system expat is currently not supported for MSVC - it would require shared simgear (DLL).
# Using external 3rd party libraries is currently not supported for MSVC - it would require shared simgear (DLL).
set(SYSTEM_EXPAT OFF)
set(SYSTEM_UDNS OFF)
endif()
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
# until the fstream fix is applied and generally available in OSG,
# keep the compatability link option as the default
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
if (CMAKE_CL_64)
@@ -137,14 +137,15 @@ endif (MSVC)
if (MSVC AND MSVC_3RDPARTY_ROOT)
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
set( OSG_MSVC "msvc" )
if (${MSVC_VERSION} EQUAL 1700)
set( OSG_MSVC ${OSG_MSVC}110 )
elseif (${MSVC_VERSION} EQUAL 1600)
set( OSG_MSVC ${OSG_MSVC}100 )
else (${MSVC_VERSION} EQUAL 1700)
set( OSG_MSVC ${OSG_MSVC}90 )
endif (${MSVC_VERSION} EQUAL 1700)
if (${MSVC_VERSION} EQUAL 1900)
set( OSG_MSVC ${OSG_MSVC}140 )
elseif (${MSVC_VERSION} EQUAL 1800)
set( OSG_MSVC ${OSG_MSVC}120 )
else ()
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
endif ()
if (CMAKE_CL_64)
set( OSG_MSVC ${OSG_MSVC}-64 )
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
@@ -154,44 +155,34 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
find_path(BOOST_ROOT boost/version.hpp
${MSVC_3RDPARTY_ROOT}/boost
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
)
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
if(NOT BOOST_INCLUDEDIR)
# if this variable was not set by the user, set it to 3rdparty root's
# parent dir, which is the normal location for people using our
# windows-3rd-party repo
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
endif()
if (NOT USE_AEONWAVE)
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
endif()
endif (MSVC AND MSVC_3RDPARTY_ROOT)
if(APPLE)
find_library(COCOA_LIBRARY Cocoa)
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
# but it's not working reliably, so forcing it for now
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
find_package(Threads REQUIRED)
endif()
# Somehow this only works if included before searching for Boost...
include(BoostTestTargets)
find_package(Boost REQUIRED)
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
include(BoostTestTargets)
if(SIMGEAR_HEADLESS)
message(STATUS "SimGear mode: HEADLESS")
@@ -201,14 +192,34 @@ else()
find_package(OpenGL REQUIRED)
if (ENABLE_SOUND)
find_package(OpenAL REQUIRED)
if (USE_AEONWAVE)
find_package(AAX COMPONENTS aax REQUIRED)
else()
find_package(OpenAL REQUIRED)
endif()
message(STATUS "Sound support: ENABLED")
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
if (MSVC)
set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS})
# ensure OSG was compiled with OSG_USE_UTF8_FILENAME set
check_cxx_source_compiles(
"#include <osg/Config>
#if !defined(OSG_USE_UTF8_FILENAME)
#error OSG UTF8 support not enabled
#endif
int main() { return 0; }"
SIMGEAR_OSG_USE_UTF8_FILENAME)
if (NOT SIMGEAR_OSG_USE_UTF8_FILENAME)
message(FATAL_ERROR "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
endif()
endif()
endif(SIMGEAR_HEADLESS)
find_package(ZLIB REQUIRED)
find_package(ZLIB 1.2.4 REQUIRED)
find_package(CURL REQUIRED)
if (SYSTEM_EXPAT)
@@ -226,8 +237,6 @@ else()
${PROJECT_BINARY_DIR}/3rdparty/expat)
endif(SYSTEM_EXPAT)
include_directories(${EXPAT_INCLUDE_DIRS})
check_include_file(inttypes.h HAVE_INTTYPES_H)
check_include_file(sys/time.h HAVE_SYS_TIME_H)
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
@@ -299,19 +308,26 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
# isnan might not be real symbol, so can't check using function_exists
check_cxx_source_compiles(
"#include <cmath>
int main() { return isnan(0.0);} "
HAVE_ISNAN)
check_cxx_source_compiles(
"#include <cmath>
"#include <cstdlib>
int main() { return std::isnan(0.0);} "
HAVE_STD_ISNAN)
if (NOT ${HAVE_STD_ISNAN})
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
set(WARNING_FLAGS_CXX "-Wall")
set(WARNING_FLAGS_C "-Wall")
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
message(WARNING "GCC 4.4 will be required soon, please upgrade")
endif()
# certain GCC versions don't provide the atomic builds, and hence
# require is to provide them in SGAtomic.cxx
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
@@ -320,13 +336,17 @@ if(CMAKE_COMPILER_IS_GNUCXX)
GCC_ATOMIC_BUILTINS_FOUND)
endif(CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# Boost redeclares class members
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
# fix Boost compilation :(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@@ -344,21 +364,26 @@ if(WIN32)
endif()
if(MSVC)
# turn off various warnings
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
# endforeach(warning)
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
if (${MSVC_VERSION} GREATER 1599)
if (NOT OSG_FSTREAM_EXPORT_FIXED)
message(STATUS "For better linking performance, use OSG with patched fstream header")
# needed to avoid link errors on multiply-defined standard C++
# symbols. Suspect this may be an OSG-DB export bug
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
endif (${MSVC_VERSION} GREATER 1599)
endif ()
if (${MSVC_VERSION} GREATER 1899)
# needed for debug builds with VS2015
set( MSVC_FLAGS "${MSVC_FLAGS} /bigobj" )
endif()
endif(MSVC)
# assumed on Windows
set(HAVE_GETLOCALTIME 1)
set( WINSOCK_LIBRARY "ws2_32.lib" )
set( SHLWAPI_LIBRARY "Shlwapi.lib" )
set( RT_LIBRARY "winmm" )
endif(WIN32)
@@ -368,17 +393,8 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
# use BEFORE to ensure local directories are used first,
# ahead of system-installed libs
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${OPENAL_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
)
add_definitions(-DHAVE_CONFIG_H)
# configure a header file to pass some of the CMake settings
@@ -403,6 +419,7 @@ set(TEST_LIBS_INTERNAL_CORE
${CMAKE_THREAD_LIBS_INIT}
${ZLIB_LIBRARY}
${WINSOCK_LIBRARY}
${SHLWAPI_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY}
@@ -415,10 +432,15 @@ endif()
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
include_directories(3rdparty/utf8/source)
if (ENABLE_DNS)
message(STATUS "DNS resolver: ENABLED")
include_directories(3rdparty/udns)
if(ENABLE_DNS)
if(SYSTEM_UDNS)
message(STATUS "Requested to use system udns library, forcing SIMGEAR_SHARED to true")
set(SIMGEAR_SHARED ON)
find_package(Udns REQUIRED)
else()
message(STATUS "DNS resolver: ENABLED")
include_directories(3rdparty/udns)
endif()
else()
message(STATUS "DNS resolver: DISABLED")
endif()
@@ -449,7 +471,7 @@ configure_file(SimGearConfig.cmake.in
@ONLY
)
set(ConfigPackageLocation lib/cmake/SimGear)
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/SimGear)
install(EXPORT SimGearTargets
DESTINATION ${ConfigPackageLocation}
)

View File

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

View File

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

View File

@@ -54,24 +54,14 @@ if(SIMGEAR_SHARED)
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
install(TARGETS SimGearCore
EXPORT SimGearTargets
LIBRARY DESTINATION
${CMAKE_INSTALL_LIBDIR})
if(NOT SIMGEAR_HEADLESS)
add_library(SimGearScene SHARED ${sceneSources})
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
# EXPORT SimGearSceneConfig
install(TARGETS SimGearScene
EXPORT SimGearTargets
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
endif()
else()
message(STATUS "Library building mode: STATIC LIBRARIES")
@@ -94,9 +84,6 @@ else()
endforeach()
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
install(TARGETS SimGearCore
EXPORT SimGearTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
if(NOT SIMGEAR_HEADLESS)
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
@@ -118,22 +105,65 @@ else()
endforeach()
add_library(SimGearScene STATIC ${sceneSources})
install(TARGETS SimGearScene
EXPORT SimGearTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif(NOT SIMGEAR_HEADLESS)
endif(SIMGEAR_SHARED)
target_include_directories(SimGearCore BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
target_include_directories(SimGearCore PUBLIC
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
target_include_directories(SimGearCore PRIVATE
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
install(TARGETS SimGearCore
EXPORT SimGearTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
if (NOT SIMGEAR_HEADLESS)
install(TARGETS SimGearScene
EXPORT SimGearTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
target_include_directories(SimGearScene BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
target_include_directories(SimGearScene PUBLIC ${OPENSCENEGRAPH_INCLUDE_DIRS})
if (USE_AEONWAVE)
target_include_directories(SimGearScene PRIVATE ${AAX_INCLUDE_DIR} )
else()
target_include_directories(SimGearScene PRIVATE ${OPENAL_INCLUDE_DIR} )
endif()
endif()
target_link_libraries(SimGearCore
${ZLIB_LIBRARY}
${RT_LIBRARY}
${DL_LIBRARY}
${EXPAT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${COCOA_LIBRARY}
${CURL_LIBRARIES})
${CURL_LIBRARIES}
${WINSOCK_LIBRARY})
if(SYSTEM_EXPAT)
target_link_libraries(SimGearCore
${EXPAT_LIBRARIES})
endif()
if(ENABLE_DNS AND SYSTEM_UDNS)
target_link_libraries(SimGearCore
${UDNS_LIBRARIES})
endif()
if(NOT SIMGEAR_HEADLESS)
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
target_link_libraries(SimGearScene
SimGearCore
${ZLIB_LIBRARY}
@@ -141,6 +171,9 @@ if(NOT SIMGEAR_HEADLESS)
${OPENAL_LIBRARY}
${OPENGL_LIBRARY}
${JPEG_LIBRARY})
# only actually needed by canvas/KeyboardEvent.cxx
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
endif()
if(ENABLE_RTI)

View File

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

View File

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

View File

@@ -40,96 +40,40 @@
#define SG_DO_STRINGIZE(X) #X
#ifdef __GNUC__
# if __GNUC__ < 3
# error Time to upgrade. GNU compilers < 3.0 not supported
# elif (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
# warning GCC compilers prior to 3.4 are suspect
# endif
# define SG_GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
#endif // __GNUC__
/* KAI C++ */
#if defined(__KCC)
# define SG_COMPILER_STR "Kai C++ version " SG_STRINGIZE(__KCC_VERSION)
#endif // __KCC
//
// Microsoft compilers.
//
#ifdef _MSC_VER
# define bcopy(from, to, n) memcpy(to, from, n)
# define strcasecmp stricmp
# if _MSC_VER >= 1200 // msvc++ 6.0 or greater
# define isnan _isnan
# define snprintf _snprintf
# if _MSC_VER < 1500
# define vsnprintf _vsnprintf
# if _MSC_VER >= 1200 // msvc++ 6.0 up to MSVC2013
# if _MSC_VER < 1900
# define bcopy(from, to, n) memcpy(to, from, n)
# define snprintf _snprintf
# define strdup _strdup
# define copysign _copysign
# endif
# define copysign _copysign
# define strcasecmp stricmp
# undef min
# undef max
# pragma warning(disable: 4786) // identifier was truncated to '255' characters
# pragma warning(disable: 4244) // conversion from double to float
# pragma warning(disable: 4305) //
# pragma warning(disable: 4305) // truncation from larger type to smaller
# pragma warning(disable: 4267) // conversion from size_t to int / 32-bit type
# pragma warning(disable: 4996) // don't require _ prefix for standard library functions
# pragma warning(disable: 4800) // don't warn about int -> bool performance
# else
# error What version of MSVC++ is this?
# endif
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
#endif // _MSC_VER
//
// Native SGI compilers
//
#if defined ( sgi ) && !defined( __GNUC__ )
# if (_COMPILER_VERSION < 740)
# error Need MipsPro 7.4.0 or higher now
# endif
#define SG_HAVE_NATIVE_SGI_COMPILERS
#pragma set woff 1001,1012,1014,1116,1155,1172,1174
#pragma set woff 1401,1460,1551,1552,1681
#ifdef __cplusplus
# pragma set woff 1682,3303
# pragma set woff 3624
#endif
# define SG_COMPILER_STR "SGI MipsPro compiler version " SG_STRINGIZE(_COMPILER_VERSION)
#endif // Native SGI compilers
#if defined (__sun)
# define SG_UNIX
# include <strings.h>
# include <memory.h>
# if defined ( __cplusplus )
// typedef unsigned int size_t;
extern "C" {
extern void *memmove(void *, const void *, size_t);
}
# else
extern void *memmove(void *, const void *, size_t);
# endif // __cplusplus
# if !defined( __GNUC__ )
# define SG_COMPILER_STR "Sun compiler version " SG_STRINGIZE(__SUNPRO_CC)
# endif
#endif // sun
//
// Intel C++ Compiler
//
@@ -144,29 +88,10 @@
#ifdef __APPLE__
# define SG_MAC
# define SG_UNIX
# ifdef __GNUC__
# if ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 3 )
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
# else
// any C++ header file undefines isinf and isnan
// so this should be included before <iostream>
// the functions are STILL in libm (libSystem on mac os x)
extern "C" int (isnan)(double);
extern "C" int (isinf)(double);
# endif
# else
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
# endif
#endif
#if defined (__FreeBSD__)
# define SG_UNIX
#include <sys/param.h>
# if __FreeBSD_version < 500000
extern "C" {
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
}
# endif
#endif
#if defined (__CYGWIN__)
@@ -201,4 +126,3 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
//
#endif // _SG_COMPILER_H

View File

@@ -211,8 +211,10 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
/** for backwards compatibility */
#define SG_SCENERY_FILE_FORMAT "0.4"
/** Default range in m at which all objects are displayed. Overridden by /sim/rendering/static-lod/rough **/
#define SG_OBJECT_RANGE 9000.0
/** Default object ranges. Overridden by /sim/rendering/static-lod/[bare|rough|detailed] **/
#define SG_OBJECT_RANGE_BARE 30000.0
#define SG_OBJECT_RANGE_ROUGH 9000.0
#define SG_OBJECT_RANGE_DETAILED 1500.0
/** Radius of scenery tiles in m **/
#define SG_TILE_RADIUS 14000.0

View File

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

View File

@@ -36,11 +36,14 @@
#include <simgear/threads/SGQueue.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#ifdef SG_WINDOWS
#if defined (SG_WINDOWS)
// for AllocConsole, OutputDebugString
#include "windows.h"
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#endif
const char* debugClassToString(sgDebugClass c)
@@ -106,13 +109,13 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
class FileLogCallback : public simgear::LogCallback
{
public:
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p),
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
{
if (!shouldLog(c, p)) return;
@@ -120,28 +123,29 @@ public:
<< ":" << file << ":" << line << ":" << message << std::endl;
}
private:
std::ofstream m_file;
sg_ofstream m_file;
};
class StderrLogCallback : public simgear::LogCallback
{
public:
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
#ifdef SG_WINDOWS
AllocConsole(); // but only if we want a console
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
#endif
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
#if defined (SG_WINDOWS)
~StderrLogCallback()
{
FreeConsole();
}
#endif
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
fprintf(stderr, "%s\n", aMessage.c_str());
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
// file, line, aMessage.c_str());
@@ -159,12 +163,12 @@ public:
simgear::LogCallback(c, p)
{
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
std::ostringstream os;
os << debugClassToString(c) << ":" << aMessage << std::endl;
OutputDebugStringA(os.str().c_str());
@@ -188,16 +192,16 @@ private:
const char* f, int l, const std::string& msg) :
debugClass(c), debugPriority(p), file(f), line(l),
message(msg)
{
{
}
sgDebugClass debugClass;
sgDebugPriority debugPriority;
const char* file;
int line;
std::string message;
};
class PauseThread
{
public:
@@ -205,7 +209,7 @@ private:
{
m_wasRunning = m_parent->stop();
}
~PauseThread()
{
if (m_wasRunning) {
@@ -218,29 +222,126 @@ private:
};
public:
LogStreamPrivate() :
m_logClass(SG_ALL),
m_logClass(SG_ALL),
m_logPriority(SG_ALERT),
m_isRunning(false),
m_consoleRequested(false)
{
#if defined (SG_WINDOWS)
m_stdout_isRedirectedAlready(false),
m_stderr_isRedirectedAlready(false),
#endif
m_isRunning(false)
{
#if defined (SG_WINDOWS)
/*
* 2016-09-20(RJH) - Reworked console handling
* 1) When started from the console use the console (when no --console)
* 2) When started from the GUI (with --console) open a new console window
* 3) When started from the GUI (without --console) don't open a new console
* window; stdout/stderr will not appear (except in logfiles as they do now)
* 4) When started from the Console (with --console) open a new console window
* 5) Ensure that IO redirection still works when started from the console
*
* Notes:
* 1) fgfs needs to be a GUI subsystem app - which it already is
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
* this is only something that can be done via "start /wait fgfs".
*/
#if !defined(SG_WINDOWS)
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
m_consoleRequested = true;
int stderr_handle_type = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
int stdout_handle_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
int stdout_isNull = 0;
int stderr_isNull = 0;
m_stderr_isRedirectedAlready = stderr_handle_type == FILE_TYPE_DISK || stderr_handle_type == FILE_TYPE_PIPE || stderr_handle_type == FILE_TYPE_CHAR;
m_stdout_isRedirectedAlready = stdout_handle_type == FILE_TYPE_DISK || stdout_handle_type == FILE_TYPE_PIPE || stdout_handle_type == FILE_TYPE_CHAR;
/*
* We don't want to attach to the console if either stream has been redirected - so in this case ensure that both streams
* are redirected as otherwise something will be lost (as Alloc or Attach Console will cause the handles that were bound
* to disappear)
*/
if (m_stdout_isRedirectedAlready){
if (!m_stderr_isRedirectedAlready) {
MessageBox(0, "Redirection only works when you use 2>&1 before using > or |\r\n(e.g. fgfs 2>&1 | more)", "Simgear Error", MB_OK | MB_ICONERROR);
exit(3);
}
} else {
/*
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
* when launched via the RUN menu explorer, or another GUI app that wasn't started from the console this will fail.
* When it fails we will redirect to the NUL device. This is to ensure that we have valid streams.
* Later on in the initialisation sequence the --console option will be processed and this will cause the requestConsole() to
* always open a new console, except for streams that are redirected. The same rules apply there, if both streams are redirected
* the console will be opened, and it will contain a message to indicate that no output will be present because the streams are redirected
*/
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
/*
* attach failed - so ensure that the streams are bound to the null device - but only when not already redirected
*/
if (!m_stdout_isRedirectedAlready)
{
stdout_isNull = true;
freopen("NUL", "w", stdout);
}
if (!m_stderr_isRedirectedAlready)
{
stderr_isNull = true;
freopen("NUL", "w", stderr);
}
}
/*
* providing that AttachConsole succeeded - we can then either reopen the stream onto the console, or use
* _fdopen to attached to the currently redirected (and open stream)
*/
if (!stdout_isNull){
if (!m_stdout_isRedirectedAlready)
freopen("conout$", "w", stdout);
else
/*
* for already redirected streams we need to attach the stream to the OS handle that is open.
* - this comes from part of the answer http://stackoverflow.com/a/13841522
* _open_osfhandle returns an FD for the Win32 Handle, which is then opened using fdopen and
* hopefully safely assigned to the stream (although it does look wrong to me it works)
* Removing this bit will stop pipes and command line redirection (> 2> and 2>&1 from working)
*/
*stdout = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), "a");
}
if (!stderr_isNull){
if (!m_stderr_isRedirectedAlready)
freopen("conout$", "w", stderr);
else
*stderr = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), "a");
}
}
//http://stackoverflow.com/a/25927081
//Clear the error state for each of the C++ standard stream objects.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
#endif
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
#if defined (SG_WINDOWS) && !defined(NDEBUG)
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
#endif
}
~LogStreamPrivate()
{
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
delete cb;
}
}
SGMutex m_lock;
SGBlockingQueue<LogEntry> m_entries;
typedef std::vector<simgear::LogCallback*> CallbackVec;
CallbackVec m_callbacks;
CallbackVec m_callbacks;
/// subset of callbacks which correspond to stdout / console,
/// and hence should dynamically reflect console logging settings
CallbackVec m_consoleCallbacks;
@@ -248,8 +349,12 @@ public:
sgDebugClass m_logClass;
sgDebugPriority m_logPriority;
bool m_isRunning;
bool m_consoleRequested;
#if defined (SG_WINDOWS)
// track whether the console was redirected on launch (in the constructor, which is called early on)
bool m_stderr_isRedirectedAlready;
bool m_stdout_isRedirectedAlready;
#endif
void startLog()
{
SGGuard<SGMutex> g(m_lock);
@@ -257,7 +362,7 @@ public:
m_isRunning = true;
start();
}
virtual void run()
{
while (1) {
@@ -267,37 +372,37 @@ public:
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
return;
}
// submit to each installed callback in turn
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
}
}
} // of main thread loop
}
bool stop()
{
SGGuard<SGMutex> g(m_lock);
if (!m_isRunning) {
return false;
}
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
join();
m_isRunning = false;
return true;
}
void addCallback(simgear::LogCallback* cb)
{
PauseThread pause(this);
m_callbacks.push_back(cb);
}
void removeCallback(simgear::LogCallback* cb)
{
PauseThread pause(this);
@@ -306,7 +411,7 @@ public:
m_callbacks.erase(it);
}
}
void setLogLevels( sgDebugClass c, sgDebugPriority p )
{
PauseThread pause(this);
@@ -316,37 +421,26 @@ public:
cb->setLogLevels(c, p);
}
}
bool would_log( sgDebugClass c, sgDebugPriority p ) const
{
if (p >= SG_INFO) return true;
return ((c & m_logClass) != 0 && p >= m_logPriority);
}
void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
LogEntry entry(c, p, fileName, line, msg);
m_entries.push(entry);
}
void requestConsole()
{
PauseThread pause(this);
if (m_consoleRequested) {
return;
}
m_consoleRequested = true;
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
}
};
/////////////////////////////////////////////////////////////////////////////
static logstream* global_logstream = NULL;
static LogStreamPrivate* global_privateLogstream = NULL;
static SGMutex global_logStreamLock;
logstream::logstream()
{
@@ -354,6 +448,13 @@ logstream::logstream()
global_privateLogstream->startLog();
}
logstream::~logstream()
{
popup_msgs.clear();
global_privateLogstream->stop();
delete global_privateLogstream;
}
void
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
@@ -362,13 +463,13 @@ logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
void
logstream::addCallback(simgear::LogCallback* cb)
{
{
global_privateLogstream->addCallback(cb);
}
void
logstream::removeCallback(simgear::LogCallback* cb)
{
{
global_privateLogstream->removeCallback(cb);
}
@@ -379,6 +480,30 @@ logstream::log( sgDebugClass c, sgDebugPriority p,
global_privateLogstream->log(c, p, fileName, line, msg);
}
void
logstream::popup( const std::string& msg)
{
popup_msgs.push_back(msg);
}
std::string
logstream::get_popup()
{
std::string rv = "";
if (!popup_msgs.empty())
{
rv = popup_msgs.front();
popup_msgs.erase(popup_msgs.begin());
}
return rv;
}
bool
logstream::has_popup()
{
return (popup_msgs.size() > 0) ? true : false;
}
bool
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
{
@@ -390,7 +515,7 @@ logstream::get_log_classes() const
{
return global_privateLogstream->m_logClass;
}
sgDebugPriority
logstream::get_log_priority() const
{
@@ -402,25 +527,25 @@ logstream::set_log_priority( sgDebugPriority p)
{
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
}
void
logstream::set_log_classes( sgDebugClass c)
{
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
}
logstream&
sglog()
{
// Force initialization of cerr.
static std::ios_base::Init initializer;
// http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
// in the absence of portable memory barrier ops in Simgear,
// let's keep this correct & safe
static SGMutex m;
SGGuard<SGMutex> g(m);
SGGuard<SGMutex> g(global_logStreamLock);
if( !global_logstream )
global_logstream = new logstream();
return *global_logstream;
@@ -429,16 +554,59 @@ sglog()
void
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
{
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
}
namespace simgear
{
void requestConsole()
{
#if defined (SG_WINDOWS)
/*
* 2016-09-20(RJH) - Reworked console handling
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
* we don't need to handle that.
* The new handling is quite simple:
* 1. The constructor will ensure that these streams exists. It will attach to the
* parent command prompt if started from the command prompt, otherwise the
* stdout/stderr will be bound to the NUL device.
* 2. with --console a window will always appear regardless of where the process was
* started from. Any non redirected streams will be redirected
* 3. You cannot use --console and either redirected stream.
*
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
*/
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
FreeConsole();
if (AllocConsole()) {
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
freopen("conout$", "w", stdout);
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
freopen("conout$", "w", stderr);
//http://stackoverflow.com/a/25927081
//Clear the error state for each of the C++ standard stream objects.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
}
} else {
MessageBox(0, "--console ignored because stdout or stderr redirected with > or 2>", "Simgear Error", MB_OK | MB_ICONERROR);
}
#endif
}
void shutdownLogging()
{
sglog(); // force creation
global_privateLogstream->requestConsole();
SGGuard<SGMutex> g(global_logStreamLock);
delete global_logstream;
global_logstream = 0;
}
} // of namespace simgear

View File

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

View File

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

View File

@@ -52,7 +52,8 @@
void CelestialBody::updatePosition(double mjd, Star *ourSun)
{
double eccAnom, v, ecl, actTime,
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze;
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze,
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl;
updateOrbElements(mjd);
actTime = sgCalcActTime(mjd);
@@ -66,10 +67,19 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
v = atan2(yv, xv); // the planet's true anomaly
r = sqrt (xv*xv + yv*yv); // the planet's distance
// repetitive calculations, minimised for speed
cosN = cos(N);
sinN = sin(N);
cosvw = cos(v+w);
sinvw = sin(v+w);
sinvw_cosi = sinvw * cos(i);
cosecl = cos(ecl);
sinecl = sin(ecl);
// calculate the planet's position in 3D space
xh = r * (cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i));
yh = r * (sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i));
zh = r * (sin(v+w) * sin(i));
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
zh = r * (sinvw * sin(i));
// calculate the ecliptic longitude and latitude
xg = xh + ourSun->getxs();
@@ -80,8 +90,8 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
latEcl = atan2(zh, sqrt(xh*xh+yh*yh));
xe = xg;
ye = yg * cos(ecl) - zg * sin(ecl);
ze = yg * sin(ecl) + zg * cos(ecl);
ye = yg * cosecl - zg * sinecl;
ze = yg * sinecl + zg * cosecl;
rightAscension = atan2(ye, xe);
declination = atan2(ze, sqrt(xe*xe + ye*ye));
/* SG_LOG(SG_GENERAL, SG_INFO, "Planet found at : "

View File

@@ -26,6 +26,7 @@
#include <string.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/SGMath.hxx>
#include <math.h>
@@ -78,69 +79,90 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
{
double
eccAnom, ecl, actTime,
xv, yv, v, r, xh, yh, zh, xg, yg, zg, xe, ye, ze,
xv, yv, v, r, xh, yh, zh, zg, xe,
Ls, Lm, D, F, mpar, gclat, rho, HA, g,
geoRa, geoDec;
geoRa, geoDec,
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl, rcoslatEcl,
FlesstwoD, MlesstwoD, twoD, twoM, twolat, alpha;
double max_loglux = -0.504030345621;
double min_loglux = -4.39964634562;
double conv = 1.0319696543787917; // The log foot-candle to log lux conversion factor.
updateOrbElements(mjd);
actTime = sgCalcActTime(mjd);
// calculate the angle between ecliptic and equatorial coordinate system
// in Radians
ecl = ((SGD_DEGREES_TO_RADIANS * 23.4393) - (SGD_DEGREES_TO_RADIANS * 3.563E-7) * actTime);
ecl = SGD_DEGREES_TO_RADIANS * (23.4393 - 3.563E-7 * actTime);
eccAnom = sgCalcEccAnom(M, e); // Calculate the eccentric anomaly
xv = a * (cos(eccAnom) - e);
yv = a * (sqrt(1.0 - e*e) * sin(eccAnom));
v = atan2(yv, xv); // the moon's true anomaly
r = sqrt (xv*xv + yv*yv); // and its distance
// repetitive calculations, minimised for speed
cosN = cos(N);
sinN = sin(N);
cosvw = cos(v+w);
sinvw = sin(v+w);
sinvw_cosi = sinvw * cos(i);
cosecl = cos(ecl);
sinecl = sin(ecl);
// estimate the geocentric rectangular coordinates here
xh = r * (cos(N) * cos (v+w) - sin (N) * sin(v+w) * cos(i));
yh = r * (sin(N) * cos (v+w) + cos (N) * sin(v+w) * cos(i));
zh = r * (sin(v+w) * sin(i));
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
zh = r * (sinvw * sin(i));
// calculate the ecliptic latitude and longitude here
lonEcl = atan2 (yh, xh);
latEcl = atan2(zh, sqrt(xh*xh + yh*yh));
/* Calculate a number of perturbatioin, i.e. disturbances caused by the
* gravitational infuence of the sun and the other major planets.
/* Calculate a number of perturbation, i.e. disturbances caused by the
* gravitational influence of the sun and the other major planets.
* The largest of these even have a name */
Ls = ourSun->getM() + ourSun->getw();
Lm = M + w + N;
D = Lm - Ls;
F = Lm - N;
twoD = 2 * D;
twoM = 2 * M;
FlesstwoD = F - twoD;
MlesstwoD = M - twoD;
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin (M - 2*D)
+0.658 * sin (2*D)
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin(MlesstwoD)
+0.658 * sin(twoD)
-0.186 * sin(ourSun->getM())
-0.059 * sin(2*M - 2*D)
-0.057 * sin(M - 2*D + ourSun->getM())
+0.053 * sin(M + 2*D)
+0.046 * sin(2*D - ourSun->getM())
-0.059 * sin(twoM - twoD)
-0.057 * sin(MlesstwoD + ourSun->getM())
+0.053 * sin(M + twoD)
+0.046 * sin(twoD - ourSun->getM())
+0.041 * sin(M - ourSun->getM())
-0.035 * sin(D)
-0.031 * sin(M + ourSun->getM())
-0.015 * sin(2*F - 2*D)
-0.015 * sin(2*F - twoD)
+0.011 * sin(M - 4*D)
);
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(F-2*D)
-0.055 * sin(M - F - 2*D)
-0.046 * sin(M + F - 2*D)
+0.033 * sin(F + 2*D)
+0.017 * sin(2*M + F)
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(FlesstwoD)
-0.055 * sin(M - FlesstwoD)
-0.046 * sin(M + FlesstwoD)
+0.033 * sin(F + twoD)
+0.017 * sin(twoM + F)
);
r += (-0.58 * cos(M - 2*D)
-0.46 * cos(2*D)
r += (-0.58 * cos(MlesstwoD)
-0.46 * cos(twoD)
);
distance = r;
// SG_LOG(SG_GENERAL, SG_INFO, "Running moon update");
xg = r * cos(lonEcl) * cos(latEcl);
yg = r * sin(lonEcl) * cos(latEcl);
rcoslatEcl = r * cos(latEcl);
xg = cos(lonEcl) * rcoslatEcl;
yg = sin(lonEcl) * rcoslatEcl;
zg = r * sin(latEcl);
xe = xg;
ye = yg * cos(ecl) -zg * sin(ecl);
ze = yg * sin(ecl) +zg * cos(ecl);
ye = yg * cosecl -zg * sinecl;
ze = yg * sinecl +zg * cosecl;
geoRa = atan2(ye, xe);
geoDec = atan2(ze, sqrt(xe*xe + ye*ye));
@@ -154,17 +176,17 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
// topocentric ra and dec. i.e. the position as seen from the
// surface of the earth, instead of the center of the earth
// First calculate the moon's parrallax, that is, the apparent size of the
// First calculate the moon's parallax, that is, the apparent size of the
// (equatorial) radius of the earth, as seen from the moon
mpar = asin ( 1 / r);
// SG_LOG( SG_GENERAL, SG_INFO, "r = " << r << " mpar = " << mpar );
// SG_LOG( SG_GENERAL, SG_INFO, "lat = " << f->get_Latitude() );
gclat = lat - 0.003358 *
sin (2 * SGD_DEGREES_TO_RADIANS * lat );
twolat = 2 * SGD_DEGREES_TO_RADIANS * lat;
gclat = lat - 0.003358 * sin(twolat);
// SG_LOG( SG_GENERAL, SG_INFO, "gclat = " << gclat );
rho = 0.99883 + 0.00167 * cos(2 * SGD_DEGREES_TO_RADIANS * lat);
rho = 0.99883 + 0.00167 * cos(twolat);
// SG_LOG( SG_GENERAL, SG_INFO, "rho = " << rho );
if (geoRa < 0)
@@ -193,4 +215,22 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
/* SG_LOG( SG_GENERAL, SG_INFO,
"Ra = (" << (SGD_RADIANS_TO_DEGREES *rightAscension)
<< "), Dec= (" << (SGD_RADIANS_TO_DEGREES *declination) << ")" ); */
// Moon age and phase calculation
age = lonEcl - ourSun->getlonEcl();
phase = (1 - cos(age)) / 2;
// The log of the illuminance of the moon outside the atmosphere.
// This is the base 10 log of equation 20 from Krisciunas K. and Schaefer B.E.
// (1991). A model of the brightness of moonlight, Publ. Astron. Soc. Pacif.
// 103(667), 1033-1039 (DOI: http://dx.doi.org/10.1086/132921).
alpha = SGD_RADIANS_TO_DEGREES * SGMiscd::normalizeAngle(age + SGMiscd::pi());
log_I = -0.4 * (3.84 + 0.026*fabs(alpha) + 4e-9*pow(alpha, 4.0));
// Convert from foot-candles to lux.
log_I += conv;
// The moon's illuminance factor, bracketed between 0 and 1.
I_factor = (log_I - max_loglux) / (max_loglux - min_loglux) + 1.0;
I_factor = SGMiscd::clip(I_factor, 0, 1);
}

View File

@@ -36,6 +36,13 @@ class MoonPos : public CelestialBody
private:
double xg, yg; // the moon's rectangular geocentric coordinates
double ye, ze; // the moon's rectangular equatorial coordinates
double distance; // the moon's distance to the earth
double age; // the moon's age from 0 to 2pi
double phase; // the moon's phase
double log_I; // the moon's illuminance outside the atmosphere (logged)
double I_factor; // the illuminance factor for the moon, between 0 and 1.
// void TexInit(); // This should move to the constructor eventually.
// GLUquadricObj *moonObject;
@@ -54,7 +61,72 @@ public:
~MoonPos();
void updatePosition(double mjd, double lst, double lat, Star *ourSun);
// void newImage();
double getM() const;
double getw() const;
double getxg() const;
double getyg() const;
double getye() const;
double getze() const;
double getDistance() const;
double getAge() const;
double getPhase() const;
double getLogIlluminance() const;
double getIlluminanceFactor() const;
};
inline double MoonPos::getM() const
{
return M;
}
inline double MoonPos::getw() const
{
return w;
}
inline double MoonPos::getxg() const
{
return xg;
}
inline double MoonPos::getyg() const
{
return yg;
}
inline double MoonPos::getye() const
{
return ye;
}
inline double MoonPos::getze() const
{
return ze;
}
inline double MoonPos::getDistance() const
{
return distance;
}
inline double MoonPos::getAge() const
{
return age;
}
inline double MoonPos::getPhase() const
{
return phase;
}
inline double MoonPos::getLogIlluminance() const
{
return log_I;
}
inline double MoonPos::getIlluminanceFactor() const
{
return I_factor;
}
#endif // _MOONPOS_HXX_

View File

@@ -33,6 +33,7 @@ class Star : public CelestialBody
private:
double lonEcl; // the sun's true longitude
double xs, ys; // the sun's rectangular geocentric coordinates
double ye, ze; // the sun's rectangularequatorial rectangular geocentric coordinates
double distance; // the sun's distance to the earth
@@ -50,6 +51,7 @@ public:
double getye() const;
double getze() const;
double getDistance() const;
double getlonEcl() const;
};
@@ -88,6 +90,10 @@ inline double Star::getDistance() const
return distance;
}
inline double Star::getlonEcl() const
{
return lonEcl;
}
#endif // _STAR_HXX_

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -69,6 +69,96 @@ NAPTRRequest::NAPTRRequest( const std::string & dn ) :
_type = DNS_T_NAPTR;
}
SRVRequest::SRVRequest( const std::string & dn ) :
Request(dn)
{
_type = DNS_T_SRV;
}
SRVRequest::SRVRequest( const std::string & dn, const string & service, const string & protocol ) :
Request(dn),
_service(service),
_protocol(protocol)
{
_type = DNS_T_SRV;
}
static bool sortSRV( const SRVRequest::SRV_ptr a, const SRVRequest::SRV_ptr b )
{
if( a->priority > b->priority ) return false;
if( a->priority < b->priority ) return true;
return a->weight > b->weight;
}
static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
{
SRVRequest * r = static_cast<SRVRequest*>(data);
if (result) {
r->cname = result->dnssrv_cname;
r->qname = result->dnssrv_qname;
r->ttl = result->dnssrv_ttl;
for (int i = 0; i < result->dnssrv_nrr; i++) {
SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
r->entries.push_back(srv);
srv->priority = result->dnssrv_srv[i].priority;
srv->weight = result->dnssrv_srv[i].weight;
srv->port = result->dnssrv_srv[i].port;
srv->target = result->dnssrv_srv[i].name;
}
std::sort( r->entries.begin(), r->entries.end(), sortSRV );
free(result);
}
r->setComplete();
}
void SRVRequest::submit()
{
// if service is defined, pass service and protocol
if (!dns_submit_srv(NULL, getDn().c_str(), _service.empty() ? NULL : _service.c_str(), _service.empty() ? NULL : _protocol.c_str(), 0, dnscbSRV, this )) {
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
return;
}
_start = time(NULL);
}
TXTRequest::TXTRequest( const std::string & dn ) :
Request(dn)
{
_type = DNS_T_TXT;
}
static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
{
TXTRequest * r = static_cast<TXTRequest*>(data);
if (result) {
r->cname = result->dnstxt_cname;
r->qname = result->dnstxt_qname;
r->ttl = result->dnstxt_ttl;
for (int i = 0; i < result->dnstxt_nrr; i++) {
//TODO: interprete the .len field of dnstxt_txt?
string txt = string((char*)result->dnstxt_txt[i].txt);
r->entries.push_back( txt );
string_list tokens = simgear::strutils::split( txt, "=", 1 );
if( tokens.size() == 2 ) {
r->attributes[tokens[0]] = tokens[1];
}
}
free(result);
}
r->setComplete();
}
void TXTRequest::submit()
{
// protocol and service an already encoded in DN so pass in NULL for both
if (!dns_submit_txt(NULL, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) {
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
return;
}
_start = time(NULL);
}
static bool sortNAPTR( const NAPTRRequest::NAPTR_ptr a, const NAPTRRequest::NAPTR_ptr b )
{
if( a->order > b->order ) return false;
@@ -130,7 +220,6 @@ void Client::makeRequest(const Request_ptr& r)
r->submit();
}
void Client::update(int waitTimeout)
{
time_t now = time(NULL);

View File

@@ -27,8 +27,11 @@
#include <memory> // for std::auto_ptr
#include <string>
#include <vector>
#include <ctime> // for time_t
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/structure/event_mgr.hxx>
namespace simgear
{
@@ -59,6 +62,7 @@ protected:
time_t _timeout_secs;
time_t _start;
};
typedef SGSharedPtr<Request> Request_ptr;
class NAPTRRequest : public Request
{
@@ -82,7 +86,38 @@ public:
std::string qservice;
};
typedef SGSharedPtr<Request> Request_ptr;
class SRVRequest : public Request
{
public:
SRVRequest( const std::string & dn );
SRVRequest( const std::string & dn, const string & service, const string & protocol );
virtual void submit();
struct SRV : SGReferenced {
int priority;
int weight;
int port;
std::string target;
};
typedef SGSharedPtr<SRV> SRV_ptr;
typedef std::vector<SRV_ptr> SRV_list;
SRV_list entries;
private:
std::string _service;
std::string _protocol;
};
class TXTRequest : public Request
{
public:
TXTRequest( const std::string & dn );
virtual void submit();
typedef std::vector<string> TXT_list;
typedef std::map<std::string,std::string> TXT_Attribute_map;
TXT_list entries;
TXT_Attribute_map attributes;
};
class Client
{
@@ -97,7 +132,6 @@ public:
// void cancelRequest(const Request_ptr& r, std::string reason = std::string());
private:
class ClientPrivate;
std::auto_ptr<ClientPrivate> d;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

Binary file not shown.

BIN
simgear/io/test2.tar Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

@@ -0,0 +1,52 @@
// Copyright (C) 2016 James Turner - <zakalawe@mac.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SG_IO_UNTAR_HXX
#define SG_IO_UNTAR_HXX
#include <memory>
#include <cstdlib>
#include <stdint.h> // for uint8_t
#include <simgear/misc/sg_path.hxx>
namespace simgear
{
class TarExtractorPrivate;
class TarExtractor
{
public:
TarExtractor(const SGPath& rootPath);
~TarExtractor();
static bool isTarData(const uint8_t* bytes, size_t count);
void extractBytes(const char* bytes, size_t count);
bool isAtEndOfArchive() const;
bool hasError() const;
private:
std::auto_ptr<TarExtractorPrivate> d;
};
} // of namespace simgear
#endif // of SG_IO_UNTAR_HXX

View File

@@ -32,6 +32,7 @@ set(HEADERS
sg_geodesy.hxx
sg_types.hxx
sg_random.h
simd.hxx
)
set(SOURCES

View File

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

View File

@@ -153,17 +153,7 @@ public:
/// Use with care: allways code that you do not need to use that!
static bool isNaN(const T& v)
{
#ifdef HAVE_ISNAN
return (isnan(v) != 0);
#elif defined HAVE_STD_ISNAN
return std::isnan(v);
#else
// Use that every compare involving a NaN returns false
// But be careful, some usual compiler switches like for example
// -fast-math from gcc might optimize that expression to v != v which
// behaves exactly like the opposite ...
return !(v == v);
#endif
}
};

View File

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

View File

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

View File

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

View File

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

868
simgear/math/simd.hxx Normal file
View File

@@ -0,0 +1,868 @@
// Copyright (C) 2016 Erik Hofman - erik@ehofman.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __SIMD_H__
#define __SIMD_H__ 1
#include <cstring>
# ifdef __SSE__
# include <xmmintrin.h>
# endif
template<typename T>
class simd4_t
{
public:
~simd4_t() {}
};
template<>
class simd4_t<float>
{
private:
typedef float __vec4_t[4];
# ifdef __SSE__
union {
__m128 v4;
__vec4_t vec;
};
# else
__vec4_t vec;
# endif
public:
simd4_t() {}
simd4_t(float f)
{
vec[0] = vec[1] = vec[2] = vec[3] = f;
}
simd4_t(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(float[4]));
}
simd4_t(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(float[4]));
#endif
}
inline float (&ptr(void))[4] {
return vec;
}
inline const float (&ptr(void) const)[4] {
return vec;
}
inline simd4_t& operator=(float f)
{
vec[0] = vec[1] = vec[2] = vec[3] = f;
return *this;
}
inline simd4_t& operator=(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(float[4]));
return *this;
}
inline simd4_t& operator=(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(float[4]));
#endif
return *this;
}
inline simd4_t operator+(float f)
{
simd4_t r(*this);
r += f;
return r;
}
inline simd4_t operator+(const __vec4_t v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator+(const simd4_t& v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator-()
{
simd4_t r(0.0f);
r -= vec;
return r;
}
inline simd4_t operator-(float f)
{
simd4_t r(*this);
r -= f;
return r;
}
inline simd4_t operator-(const __vec4_t v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator-(simd4_t& v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator*(float f)
{
simd4_t r(*this);
r *= f;
return r;
}
inline simd4_t operator*(const __vec4_t v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator*(simd4_t& v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator/(float f)
{
simd4_t r(*this);
r /= f;
return r;
}
inline simd4_t operator/(const __vec4_t v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t operator/(simd4_t& v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t& operator+=(float f)
{
# ifdef __SSE__
v4 += f;
# else
vec[0] += f;
vec[1] += f;
vec[2] += f;
vec[3] += f;
# endif
return *this;
}
inline simd4_t& operator+=(const __vec4_t v)
{
simd4_t r(v);
*this += r;
return *this;
}
inline simd4_t& operator+=(const simd4_t& v)
{
# ifdef __SSE__
v4 += v.v4;
# else
vec[0] += v[0];
vec[1] += v[1];
vec[2] += v[2];
vec[3] += v[3];
#endif
return *this;
}
inline simd4_t& operator-=(float f)
{
# ifdef __SSE__
v4 -= f;
# else
vec[0] -= f;
vec[1] -= f;
vec[2] -= f;
vec[3] -= f;
# endif
return *this;
}
inline simd4_t& operator-=(const __vec4_t v)
{
simd4_t r(v);
*this -= r;
return *this;
}
inline simd4_t& operator-=(const simd4_t& v)
{
# ifdef __SSE__
v4 -= v.v4;
# else
vec[0] -= v[0];
vec[1] -= v[1];
vec[2] -= v[2];
vec[3] -= v[3];
#endif
return *this;
}
inline simd4_t& operator *=(float f)
{
# ifdef __SSE__
v4 *= f;
# else
vec[0] *= f;
vec[1] *= f;
vec[2] *= f;
vec[3] *= f;
# endif
return *this;
}
inline simd4_t& operator*=(const __vec4_t v)
{
simd4_t r(v);
*this *= r;
return *this;
}
inline simd4_t& operator*=(const simd4_t& v)
{
# ifdef __SSE__
v4 *= v.v4;
# else
vec[0] *= v[0];
vec[1] *= v[1];
vec[2] *= v[2];
vec[3] *= v[3];
#endif
return *this;
}
inline simd4_t& operator/=(float f)
{
# ifdef __SSE__
v4 /= f;
# else
vec[0] /= f;
vec[1] /= f;
vec[2] /= f;
vec[3] /= f;
#endif
return *this;
}
inline simd4_t& operator/=(const __vec4_t v)
{
simd4_t r(v);
*this /= r;
return *this;
}
inline simd4_t& operator/=(const simd4_t& v)
{
# ifdef __SSE__
v4 /= v.v4;
# else
vec[0] /= v[0];
vec[1] /= v[1];
vec[2] /= v[2];
vec[3] /= v[3];
#endif
return *this;
}
inline operator const float*() const {
return vec;
}
inline operator float*() {
return vec;
}
};
template<>
class simd4_t<double>
{
private:
typedef double __vec4_t[4];
# ifdef __SSE__
union {
__m128d v4;
__vec4_t vec;
};
# else
__vec4_t vec;
# endif
public:
simd4_t() {}
simd4_t(double d)
{
vec[0] = vec[1] = vec[2] = vec[3] = d;
}
simd4_t(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(double[4]));
}
simd4_t(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(double[4]));
#endif
}
inline double (&ptr(void))[4] {
return vec;
}
inline const double (&ptr(void) const)[4] {
return vec;
}
inline simd4_t& operator=(double d)
{
vec[0] = vec[1] = vec[2] = vec[3] = d;
return *this;
}
inline simd4_t& operator=(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(double[4]));
return *this;
}
inline simd4_t& operator=(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(double[4]));
#endif
return *this;
}
inline simd4_t operator+(double d)
{
simd4_t r(*this);
r += d;
return r;
}
inline simd4_t operator+(const __vec4_t v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator+(const simd4_t& v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator-()
{
simd4_t r(0.0f);
r -= vec;
return r;
}
inline simd4_t operator-(double d)
{
simd4_t r(*this);
r -= d;
return r;
}
inline simd4_t operator-(const __vec4_t v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator-(simd4_t& v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator*(double d)
{
simd4_t r(*this);
r *= d;
return r;
}
inline simd4_t operator*(const __vec4_t v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator*(simd4_t& v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator/(double d)
{
simd4_t r(*this);
r /= d;
return r;
}
inline simd4_t operator/(const __vec4_t v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t operator/(simd4_t& v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t& operator+=(double d)
{
# ifdef __SSE__
v4 += d;
# else
vec[0] += d;
vec[1] += d;
vec[2] += d;
vec[3] += d;
# endif
return *this;
}
inline simd4_t& operator+=(const __vec4_t v)
{
simd4_t r(v);
*this += r;
return *this;
}
inline simd4_t& operator+=(const simd4_t& v)
{
# ifdef __SSE__
v4 += v.v4;
# else
vec[0] += v[0];
vec[1] += v[1];
vec[2] += v[2];
vec[3] += v[3];
#endif
return *this;
}
inline simd4_t& operator-=(double d)
{
# ifdef __SSE__
v4 -= d;
# else
vec[0] -= d;
vec[1] -= d;
vec[2] -= d;
vec[3] -= d;
# endif
return *this;
}
inline simd4_t& operator-=(const __vec4_t v)
{
simd4_t r(v);
*this -= r;
return *this;
}
inline simd4_t& operator-=(const simd4_t& v)
{
# ifdef __SSE__
v4 -= v.v4;
# else
vec[0] -= v[0];
vec[1] -= v[1];
vec[2] -= v[2];
vec[3] -= v[3];
#endif
return *this;
}
inline simd4_t& operator *=(double d)
{
# ifdef __SSE__
v4 *= d;
# else
vec[0] *= d;
vec[1] *= d;
vec[2] *= d;
vec[3] *= d;
# endif
return *this;
}
inline simd4_t& operator*=(const __vec4_t v)
{
simd4_t r(v);
*this *= r;
return *this;
}
inline simd4_t& operator*=(const simd4_t& v)
{
# ifdef __SSE__
v4 *= v.v4;
# else
vec[0] *= v[0];
vec[1] *= v[1];
vec[2] *= v[2];
vec[3] *= v[3];
#endif
return *this;
}
inline simd4_t& operator/=(double d)
{
# ifdef __SSE__
v4 /= d;
# else
vec[0] /= d;
vec[1] /= d;
vec[2] /= d;
vec[3] /= d;
#endif
return *this;
}
inline simd4_t& operator/=(const __vec4_t v)
{
simd4_t r(v);
*this /= r;
return *this;
}
inline simd4_t& operator/=(const simd4_t& v)
{
# ifdef __SSE__
v4 /= v.v4;
# else
vec[0] /= v[0];
vec[1] /= v[1];
vec[2] /= v[2];
vec[3] /= v[3];
#endif
return *this;
}
inline operator const double*() const {
return vec;
}
inline operator double*() {
return vec;
}
};
template<>
class simd4_t<int>
{
private:
typedef int __vec4_t[4];
# ifdef __SSE__
union {
__m128i v4;
__vec4_t vec;
};
# else
__vec4_t vec;
# endif
public:
simd4_t() {}
simd4_t(int i)
{
vec[0] = vec[1] = vec[2] = vec[3] = i;
}
simd4_t(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(int[4]));
}
simd4_t(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(int[4]));
#endif
}
inline int (&ptr(void))[4] {
return vec;
}
inline const int (&ptr(void) const)[4] {
return vec;
}
inline simd4_t& operator=(int i)
{
vec[0] = vec[1] = vec[2] = vec[3] = i;
return *this;
}
inline simd4_t& operator=(const __vec4_t v)
{
std::memcpy(vec, v, sizeof(int[4]));
return *this;
}
inline simd4_t& operator=(const simd4_t& v)
{
# ifdef __SSE__
v4 = v.v4;
#else
std::memcpy(vec, v.vec, sizeof(int[4]));
#endif
return *this;
}
inline simd4_t operator+(int i)
{
simd4_t r(*this);
r += i;
return r;
}
inline simd4_t operator+(const __vec4_t v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator+(const simd4_t& v)
{
simd4_t r(*this);
r += v;
return r;
}
inline simd4_t operator-()
{
simd4_t r(0.0f);
r -= vec;
return r;
}
inline simd4_t operator-(int i)
{
simd4_t r(*this);
r -= i;
return r;
}
inline simd4_t operator-(const __vec4_t v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator-(simd4_t& v)
{
simd4_t r(*this);
r -= v;
return r;
}
inline simd4_t operator*(int i)
{
simd4_t r(*this);
r *= i;
return r;
}
inline simd4_t operator*(const __vec4_t v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator*(simd4_t& v)
{
simd4_t r(*this);
r *= v;
return r;
}
inline simd4_t operator/(int i)
{
simd4_t r(*this);
r /= i;
return r;
}
inline simd4_t operator/(const __vec4_t v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t operator/(simd4_t& v)
{
simd4_t r(*this);
r /= v;
return r;
}
inline simd4_t& operator+=(int i)
{
# ifdef __SSE__
v4 += i;
# else
vec[0] += i;
vec[1] += i;
vec[2] += i;
vec[3] += i;
# endif
return *this;
}
inline simd4_t& operator+=(const __vec4_t v)
{
simd4_t r(v);
*this += r;
return *this;
}
inline simd4_t& operator+=(const simd4_t& v)
{
# ifdef __SSE__
v4 += v.v4;
# else
vec[0] += v[0];
vec[1] += v[1];
vec[2] += v[2];
vec[3] += v[3];
#endif
return *this;
}
inline simd4_t& operator-=(int i)
{
# ifdef __SSE__
v4 -= i;
# else
vec[0] -= i;
vec[1] -= i;
vec[2] -= i;
vec[3] -= i;
# endif
return *this;
}
inline simd4_t& operator-=(const __vec4_t v)
{
simd4_t r(v);
*this -= r;
return *this;
}
inline simd4_t& operator-=(const simd4_t& v)
{
# ifdef __SSE__
v4 -= v.v4;
# else
vec[0] -= v[0];
vec[1] -= v[1];
vec[2] -= v[2];
vec[3] -= v[3];
#endif
return *this;
}
inline simd4_t& operator *=(int i)
{
# ifdef __SSE__
v4 *= i;
# else
vec[0] *= i;
vec[1] *= i;
vec[2] *= i;
vec[3] *= i;
# endif
return *this;
}
inline simd4_t& operator*=(const __vec4_t v)
{
simd4_t r(v);
*this *= r;
return *this;
}
inline simd4_t& operator*=(const simd4_t& v)
{
# ifdef __SSE__
v4 *= v.v4;
# else
vec[0] *= v[0];
vec[1] *= v[1];
vec[2] *= v[2];
vec[3] *= v[3];
#endif
return *this;
}
inline simd4_t& operator/=(int i)
{
# ifdef __SSE__
v4 /= i;
# else
vec[0] /= i;
vec[1] /= i;
vec[2] /= i;
vec[3] /= i;
#endif
return *this;
}
inline simd4_t& operator/=(const __vec4_t v)
{
simd4_t r(v);
*this /= r;
return *this;
}
inline simd4_t& operator/=(const simd4_t& v)
{
# ifdef __SSE__
v4 /= v.v4;
# else
vec[0] /= v[0];
vec[1] /= v[1];
vec[2] /= v[2];
vec[3] /= v[3];
#endif
return *this;
}
inline operator const int*() const {
return vec;
}
inline operator int*() {
return vec;
}
};
#endif /* __SIMD_H__ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,8 +25,12 @@
#include <ctype.h> // isspace()
#include <cerrno>
#include <zlib.h>
#include "sgstream.hxx"
#include <simgear/misc/sg_path.hxx>
using std::istream;
using std::ostream;
@@ -39,10 +43,11 @@ sg_gzifstream::sg_gzifstream()
//
// Open a possibly gzipped file for reading.
//
sg_gzifstream::sg_gzifstream( const std::string& name, ios_openmode io_mode )
sg_gzifstream::sg_gzifstream( const SGPath& name, ios_openmode io_mode,
bool use_exact_name )
: istream(&gzbuf)
{
this->open( name, io_mode );
this->open( name, io_mode, use_exact_name );
}
//-----------------------------------------------------------------------------
@@ -58,32 +63,35 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
//-----------------------------------------------------------------------------
//
// Open a possibly gzipped file for reading.
// If the initial open fails and the filename has a ".gz" extension then
// remove the extension and try again.
// If the initial open fails and the filename doesn't have a ".gz" extension
// then append ".gz" and try again.
// If 'use_exact_name' is true, just try to open the indicated file, nothing
// else. Otherwise:
// - if the initial open fails and the filename has a ".gz" extension, then
// remove it and try again;
// - if the initial open fails and the filename doesn't have a ".gz"
// extension, then append ".gz" and try again.
//
void
sg_gzifstream::open( const std::string& name, ios_openmode io_mode )
sg_gzifstream::open( const SGPath& name, ios_openmode io_mode,
bool use_exact_name )
{
gzbuf.open( name.c_str(), io_mode );
if ( ! gzbuf.is_open() )
std::string s = name.utf8Str();
gzbuf.open( s.c_str(), io_mode );
if ( ! (gzbuf.is_open() || use_exact_name) )
{
std::string s = name;
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
{
// remove ".gz" suffix
s.replace( s.length() - 3, 3, "" );
// s.erase( s.length() - 3, 3 );
}
else
{
// Append ".gz" suffix
s += ".gz";
}
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
{
// remove ".gz" suffix
s.replace( s.length() - 3, 3, "" );
// s.erase( s.length() - 3, 3 );
}
else
{
// Append ".gz" suffix
s += ".gz";
}
// Try again.
gzbuf.open( s.c_str(), io_mode );
// Try again.
gzbuf.open( s.c_str(), io_mode );
}
}
@@ -93,6 +101,11 @@ sg_gzifstream::attach( int fd, ios_openmode io_mode )
gzbuf.attach( fd, io_mode );
}
z_off_t
sg_gzifstream::approxOffset() {
return gzbuf.approxOffset();
}
//
// Manipulators
//
@@ -159,7 +172,7 @@ sg_gzofstream::sg_gzofstream()
//
// Open a file for gzipped writing.
//
sg_gzofstream::sg_gzofstream( const std::string& name, ios_openmode io_mode )
sg_gzofstream::sg_gzofstream( const SGPath& name, ios_openmode io_mode )
: ostream(&gzbuf)
{
this->open( name, io_mode );
@@ -180,9 +193,10 @@ sg_gzofstream::sg_gzofstream( int fd, ios_openmode io_mode )
// Open a file for gzipped writing.
//
void
sg_gzofstream::open( const std::string& name, ios_openmode io_mode )
sg_gzofstream::open( const SGPath& name, ios_openmode io_mode )
{
gzbuf.open( name.c_str(), io_mode );
std::string s = name.utf8Str();
gzbuf.open( s.c_str(), io_mode );
}
void
@@ -190,3 +204,44 @@ sg_gzofstream::attach( int fd, ios_openmode io_mode )
{
gzbuf.attach( fd, io_mode );
}
sg_ifstream::sg_ifstream(const SGPath& path, ios_openmode io_mode)
{
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
void sg_ifstream::open( const SGPath& name, ios_openmode io_mode )
{
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
sg_ofstream::sg_ofstream(const SGPath& path, ios_openmode io_mode)
{
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}
void sg_ofstream::open( const SGPath& name, ios_openmode io_mode )
{
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}

View File

@@ -33,13 +33,17 @@
#include <simgear/compiler.h>
# include <istream>
# include <ostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <string>
#include <zlib.h>
#include <simgear/misc/zfstream.hxx>
class SGPath;
/**
* An envelope class for gzifstream.
*/
@@ -50,13 +54,15 @@ public:
sg_gzifstream();
/**
* Constructor that attempt to open a file with and without
* ".gz" extension.
* Constructor that attempts to open a file.
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
* @param use_exact_name if false, try to add or remove a ".gz" extension
* in case the indicated file can't be opened
*/
sg_gzifstream( const std::string& name,
ios_openmode io_mode = ios_in | ios_binary );
sg_gzifstream( const SGPath& name,
ios_openmode io_mode = ios_in | ios_binary,
bool use_exact_name = false );
/**
* Constructor that attaches itself to an existing file descriptor.
@@ -66,12 +72,15 @@ public:
sg_gzifstream( int fd, ios_openmode io_mode = ios_in|ios_binary );
/**
* Attempt to open a file with and without ".gz" extension.
* Attempt to open a file.
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
* @param use_exact_name if false, try to add or remove a ".gz" extension
* in case the indicated file can't be opened
*/
void open( const std::string& name,
ios_openmode io_mode = ios_in|ios_binary );
void open( const SGPath& name,
ios_openmode io_mode = ios_in|ios_binary,
bool use_exact_name = false );
/**
* Attach to an existing file descriptor.
@@ -88,10 +97,19 @@ public:
/** @return true if the file is successfully opened, false otherwise. */
bool is_open() { return gzbuf.is_open(); }
/**
* @return the current offset in the file being read or written.
* The offset corresponds to compressed data if the file is compressed,
* and is influenced by buffering performed in zlib, hence the "approx"
* qualifier. It should be suitable for progress indicators and such,
* though.
*/
z_off_t approxOffset();
private:
// Not defined!
sg_gzifstream( const sg_gzifstream& );
void operator= ( const sg_gzifstream& );
sg_gzifstream( const sg_gzifstream& );
void operator= ( const sg_gzifstream& );
};
/**
@@ -130,7 +148,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
sg_gzofstream( const std::string& name,
sg_gzofstream( const SGPath& name,
ios_openmode io_mode = ios_out | ios_binary );
/**
@@ -145,7 +163,7 @@ public:
* @param name name of file
* @param io_mode file open mode(s) "or'd" together
*/
void open( const std::string& name,
void open( const SGPath& name,
ios_openmode io_mode = ios_out|ios_binary );
/**
@@ -169,5 +187,25 @@ private:
void operator= ( const sg_gzofstream& );
};
#endif /* _SGSTREAM_HXX */
class sg_ifstream : public std::ifstream
{
public:
sg_ifstream() {}
sg_ifstream(const SGPath& path, ios_openmode io_mode = ios_in | ios_binary);
void open( const SGPath& name,
ios_openmode io_mode = ios_in|ios_binary );
};
class sg_ofstream : public std::ofstream
{
public:
sg_ofstream() { }
sg_ofstream(const SGPath& path, ios_openmode io_mode = ios_out | ios_binary);
void open( const SGPath& name,
ios_openmode io_mode = ios_out|ios_binary );
};
#endif /* _SGSTREAM_HXX */

View File

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

View File

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

View File

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

View File

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

View File

@@ -40,20 +40,39 @@ BOOST_AUTO_TEST_CASE( strutils_functions )
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
string_list la = strutils::split("zero one two three four five");
BOOST_CHECK_EQUAL(la[2], "two");
BOOST_CHECK_EQUAL(la[5], "five");
BOOST_CHECK_EQUAL(la.size(), 6);
string_list l = strutils::split("zero one two three four five");
BOOST_CHECK_EQUAL(l[2], "two");
BOOST_CHECK_EQUAL(l[5], "five");
BOOST_CHECK_EQUAL(l.size(), 6);
string_list lb = strutils::split("alpha:beta:gamma:delta", ":", 2);
BOOST_CHECK_EQUAL(lb.size(), 3);
BOOST_CHECK_EQUAL(lb[0], "alpha");
BOOST_CHECK_EQUAL(lb[1], "beta");
BOOST_CHECK_EQUAL(lb[2], "gamma:delta");
std::string j = strutils::join(la, "&");
std::string j = strutils::join(l, "&");
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
l = strutils::split("alpha:beta:gamma:delta", ":", 2);
BOOST_CHECK_EQUAL(l.size(), 3);
BOOST_CHECK_EQUAL(l[0], "alpha");
BOOST_CHECK_EQUAL(l[1], "beta");
BOOST_CHECK_EQUAL(l[2], "gamma:delta");
l = strutils::split("", ",");
BOOST_CHECK_EQUAL(l.size(), 1);
BOOST_CHECK_EQUAL(l[0], "");
l = strutils::split(",", ",");
BOOST_CHECK_EQUAL(l.size(), 2);
BOOST_CHECK_EQUAL(l[0], "");
BOOST_CHECK_EQUAL(l[1], "");
l = strutils::split(",,", ",");
BOOST_CHECK_EQUAL(l.size(), 3);
BOOST_CHECK_EQUAL(l[0], "");
BOOST_CHECK_EQUAL(l[1], "");
BOOST_CHECK_EQUAL(l[2], "");
l = strutils::split(" ", ",");
BOOST_CHECK_EQUAL(l.size(), 1);
BOOST_CHECK_EQUAL(l[0], " ");
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
}

View File

@@ -35,4 +35,9 @@
exit(1); \
}
#define SG_TEST_FAIL(msg) \
std::cerr << "failure:" << msg; \
exit(1);
#endif // of SG_MISC_TEST_MACROS_HXX

View File

@@ -22,11 +22,18 @@
// $Id$
#include <simgear/compiler.h>
#include <simgear_config.h>
#include <cerrno>
#include <memory.h>
#include <stdio.h>
#include <fcntl.h>
#include <simgear/misc/strutils.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
#include <zlib.h>
#include "zfstream.hxx"
//
@@ -110,7 +117,14 @@ gzfilebuf::open( const char *name, ios_openmode io_mode )
char char_mode[10];
cvt_iomode( char_mode, io_mode );
#if defined(SG_WINDOWS)
std::wstring ws = simgear::strutils::convertUtf8ToWString(std::string(name));
if ( (file = gzopen_w(ws.c_str(), char_mode)) == NULL ) {
#else
if ( (file = gzopen(name, char_mode)) == NULL ) {
#endif
// perror( "gzfilebuf::open(): " );
errno = 0;
return NULL;
@@ -169,6 +183,27 @@ gzfilebuf::setcompressionstrategy( int comp_strategy )
return gzsetparams(file, -2, comp_strategy);
}
z_off_t
gzfilebuf::approxOffset() {
z_off_t res = gzoffset(file);
if (res == -1) {
int errnum;
std::string errMsg = "gzoffset() error: ";
const char *gzMsg = gzerror(file, &errnum);
if (errnum == Z_ERRNO) {
errMsg += simgear::strutils::error_string(errno);
} else {
errMsg += std::string(gzMsg);
}
SG_LOG( SG_GENERAL, SG_ALERT, errMsg );
throw sg_io_exception(errMsg);
}
return res;
}
std::streampos
gzfilebuf::seekoff( std::streamoff, ios_seekdir, ios_openmode )

View File

@@ -66,7 +66,7 @@ public:
/**
* Open a stream
* @param name file name
* @param name file name, UTF-8 encoded
* @param io_mode mode flags
* @return file stream
*/
@@ -89,6 +89,15 @@ public:
/** @return true if open, false otherwise */
bool is_open() const { return (file != NULL); }
/**
* @return the current offset in the file being read or written.
* The offset corresponds to compressed data if the file is compressed,
* and is influenced by buffering performed in zlib, hence the "approx"
* qualifier. It should be suitable for progress indicators and such,
* though.
*/
z_off_t approxOffset();
/** @return stream position */
virtual std::streampos seekoff( std::streamoff off, ios_seekdir way, ios_openmode which );

View File

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

View File

@@ -1,10 +1,9 @@
#ifndef _NAREF_H
#define _NAREF_H
#if (defined(__x86_64) && defined(__linux__)) || defined(__sparcv9) || \
defined(__powerpc64__)
#if (defined(__x86_64) && defined(__linux__))
/* NASAL_NAN64 mode requires 64 bit pointers that only use the
* lower 48 bits; Win64 and Irix should work with this too, but
* lower 48 bits; x86 Win64 and Irix should work with this too, but
* have not been tested */
# define NASAL_NAN64
#elif defined(__BYTE_ORDER__)

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ std::string readFileIntoString(const SGPath& path)
std::string contents;
size_t readLen;
SGBinaryFile f(path.str());
SGBinaryFile f(path);
if (!f.open(SG_IO_IN)) {
throw sg_io_exception("Couldn't open file", path);
}
@@ -133,7 +133,7 @@ int parseTest()
COMPARE(cat->description(), "First test catalog");
// check the packages too
COMPARE(cat->packages().size(), 3);
COMPARE(cat->packages().size(), 4);
pkg::PackageRef p1 = cat->packages().front();
COMPARE(p1->catalog(), cat.ptr());
@@ -150,6 +150,43 @@ int parseTest()
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
COMPARE(p2->description(), "A plane made by Cessna");
pkg::Package::ThumbnailVec thumbs = p2->thumbnailsForVariant(0);
COMPARE(thumbs.size(), 3);
auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
VERIFY(index != thumbs.end());
COMPARE(index->path, "thumb-exterior.png");
COMPARE(index->url, "http://foo.bar.com/thumb-exterior.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::PANEL); });
VERIFY(index != thumbs.end());
COMPARE(index->path, "thumb-panel.png");
COMPARE(index->url, "http://foo.bar.com/thumb-panel.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::PANEL);
// test variants
try {
p2->indexOfVariant("fofofo");
SG_TEST_FAIL("lookup of non-existant variant did not throw");
} catch (sg_exception& e) {
// expected
}
unsigned int skisVariant = p2->indexOfVariant("c172p-skis");
VERIFY(skisVariant > 0);
pkg::Package::ThumbnailVec thumbs2 = p2->thumbnailsForVariant(skisVariant);
COMPARE(thumbs2.size(), 2);
index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
VERIFY(index != thumbs2.end());
COMPARE(index->path, "thumb-exterior-skis.png");
COMPARE(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
// test filtering / searching too
@@ -208,7 +245,7 @@ void testAddCatalog(HTTP::Client* cl)
p.append("org.flightgear.test.catalog1");
p.append("catalog.xml");
VERIFY(p.exists());
COMPARE(root->allPackages().size(), 3);
COMPARE(root->allPackages().size(), 4);
COMPARE(root->catalogs().size(), 1);
pkg::PackageRef p1 = root->getPackageById("alpha");
@@ -403,6 +440,42 @@ void testRefreshCatalog(HTTP::Client* cl)
COMPARE(root->getPackageById("common-sounds")->revision(), 11);
}
void testInstallTarPackage(HTTP::Client* cl)
{
SGPath rootPath(simgear::Dir::current().path());
rootPath.append("pkg_install_tar");
simgear::Dir pd(rootPath);
pd.removeChildren();
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
// specify a test dir
root->setHTTPClient(cl);
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
waitForUpdateComplete(cl, root);
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
COMPARE(p1->id(), "b737-NG");
pkg::InstallRef ins = p1->install();
VERIFY(ins->isQueued());
waitForUpdateComplete(cl, root);
VERIFY(p1->isInstalled());
VERIFY(p1->existingInstall() == ins);
// verify on disk state
SGPath p(rootPath);
p.append("org.flightgear.test.catalog1");
p.append("Aircraft");
p.append("b737NG");
COMPARE(p, ins->path());
p.append("b737-900-set.xml");
VERIFY(p.exists());
}
int main(int argc, char* argv[])
{
@@ -418,13 +491,15 @@ int main(int argc, char* argv[])
parseTest();
testInstallPackage(&cl);
testUninstall(&cl);
testRemoveCatalog(&cl);
testRefreshCatalog(&cl);
testInstallTarPackage(&cl);
std::cout << "Successfully passed all tests!" << std::endl;
return EXIT_SUCCESS;
}

View File

@@ -29,9 +29,11 @@ namespace pkg
class Install;
class Catalog;
class Package;
typedef SGSharedPtr<Catalog> CatalogRef;
typedef SGSharedPtr<Install> InstallRef;
typedef SGSharedPtr<Package> PackageRef;
/**
* package delegate is the mechanism to discover progress / completion /
@@ -68,6 +70,8 @@ public:
virtual void installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal) = 0;
virtual void finishInstall(InstallRef aInstall, StatusCode aReason) = 0;
virtual void finishUninstall(const PackageRef& aPackage) {};
/**
* Notification when catalogs/packages are added or removed
*/

View File

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

View File

@@ -21,7 +21,7 @@
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <simgear/debug/logstream.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/package/Catalog.hxx>
@@ -29,7 +29,7 @@
#include <simgear/package/Root.hxx>
namespace simgear {
namespace pkg {
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
@@ -103,7 +103,7 @@ bool Package::matches(const SGPropertyNode* aFilter) const
else
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
} // of filter props iteration
return true;
}
@@ -127,7 +127,7 @@ InstallRef Package::install()
if (ins) {
return ins;
}
// start a new install
ins = new Install(this, pathOnDisk());
m_catalog->root()->scheduleToUpdate(ins);
@@ -190,10 +190,10 @@ unsigned int Package::revision() const
if (!m_props) {
return 0;
}
return m_props->getIntValue("revision");
}
std::string Package::name() const
{
return m_props->getStringValue("name");
@@ -203,7 +203,7 @@ size_t Package::fileSizeBytes() const
{
return m_props->getIntValue("file-size-bytes");
}
std::string Package::description() const
{
return getLocalisedProp("description");
@@ -213,7 +213,7 @@ string_set Package::tags() const
{
return m_tags;
}
SGPropertyNode* Package::properties() const
{
return m_props.ptr();
@@ -225,7 +225,7 @@ string_list Package::thumbnailUrls() const
if (!m_props) {
return r;
}
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
r.push_back(dl->getStringValue());
}
@@ -238,20 +238,20 @@ string_list Package::thumbnails() const
if (!m_props) {
return r;
}
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
r.push_back(dl->getStringValue());
}
return r;
}
string_list Package::downloadUrls() const
{
string_list r;
if (!m_props) {
return r;
}
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
r.push_back(dl->getStringValue());
}
@@ -272,41 +272,41 @@ std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char*
return localeRoot->getStringValue(aName);
}
}
return aRoot->getStringValue(aName);
}
PackageList Package::dependencies() const
{
PackageList result;
BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
std::string depName = dep->getStringValue("id");
unsigned int rev = dep->getIntValue("revision", 0);
// prefer local hangar package if possible, in case someone does something
// silly with naming. Of course flightgear's aircraft search doesn't know
// about hangars, so names still need to be unique.
PackageRef depPkg = m_catalog->getPackageById(depName);
if (!depPkg) {
if (!depPkg) {
Root* rt = m_catalog->root();
depPkg = rt->getPackageById(depName);
if (!depPkg) {
throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
}
}
if (depPkg->revision() < rev) {
throw sg_range_exception("Couldn't find suitable revision of " + depName);
}
// forbid recursive dependency graphs, we don't need that level
// of complexity for aircraft resources
assert(depPkg->dependencies() == PackageList());
result.push_back(depPkg);
}
return result;
}
@@ -338,6 +338,24 @@ std::string Package::nameForVariant(const std::string& vid) const
throw sg_exception("Unknow variant +" + vid + " in package " + id());
}
unsigned int Package::indexOfVariant(const std::string& vid) const
{
if (vid == id()) {
return 0;
}
unsigned int result = 1;
for (SGPropertyNode* var : m_props->getChildren("variant")) {
if (var->getStringValue("id") == vid) {
return result;
}
result++;
}
throw sg_exception("Unknow variant " + vid + " in package " + id());
}
std::string Package::nameForVariant(const unsigned int vIndex) const
{
if (vIndex == 0)
@@ -351,6 +369,48 @@ std::string Package::nameForVariant(const unsigned int vIndex) const
throw sg_exception("Unknow variant in package " + id());
}
Package::ThumbnailVec Package::thumbnailsForVariant(unsigned int vIndex) const
{
if (vIndex == 0) {
return thumbnailsFromProps(m_props);
}
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
if (!var) {
throw sg_exception("Unknow variant in package " + id());
}
return thumbnailsFromProps(var);
}
Package::Thumbnail::Type thumbnailTypeFromString(const std::string& s)
{
if (s == "exterior") return Package::Thumbnail::Type::EXTERIOR;
if (s == "interior") return Package::Thumbnail::Type::INTERIOR;
if (s == "panel") return Package::Thumbnail::Type::PANEL;
return Package::Thumbnail::Type::UNKNOWN;
}
Package::Thumbnail::Thumbnail(const std::string& aUrl, const std::string& aPath, Type aType) :
url(aUrl),
path(aPath),
type(aType)
{
}
Package::ThumbnailVec Package::thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const
{
ThumbnailVec result;
for (auto thumbNode : ptr->getChildren("thumbnail")) {
Thumbnail t(thumbNode->getStringValue("url"),
thumbNode->getStringValue("path"),
thumbnailTypeFromString(thumbNode->getStringValue("type")));
result.push_back(t);
}
return result;
}
} // of namespace pkg

View File

@@ -80,7 +80,12 @@ public:
* Fully-qualified ID, including our catalog'd ID
*/
std::string qualifiedVariantId(const unsigned int variantIndex) const;
/**
*
*/
unsigned int indexOfVariant(const std::string& vid) const;
/**
* human-readable name - note this is probably not localised,
* although this is not ruled out for the future.
@@ -133,6 +138,35 @@ public:
* thumbnail file paths within the package on disk
*/
string_list thumbnails() const;
/**
* information about a thumbnail
*/
struct Thumbnail {
enum class Type
{
UNKNOWN,
PANEL,
INTERIOR,
EXTERIOR
// NIGHT / GROUND as modifiers? does this add any
// actual value for GUIs?
};
Thumbnail(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN);
std::string url;
std::string path;
Type type = Type::UNKNOWN;
};
typedef std::vector<Thumbnail> ThumbnailVec;
/**
* retrieve all the thumbnails for a variant
*/
ThumbnailVec thumbnailsForVariant(unsigned int vIndex) const;
/**
* Packages we depend upon.
@@ -159,7 +193,9 @@ private:
void updateFromProps(const SGPropertyNode* aProps);
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
ThumbnailVec thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const;
SGPropertyNode_ptr m_props;
std::string m_id;
string_set m_tags;

View File

@@ -153,6 +153,12 @@ public:
}
}
void fireFinishUninstall(PackageRef pkg)
{
std::for_each(delegates.begin(), delegates.end(),
[pkg](Delegate* d) {d->finishUninstall(pkg);});
}
DelegateVec delegates;
SGPath path;
@@ -384,7 +390,7 @@ void Root::refresh(bool aForce)
CatalogList toRefresh;
CatalogDict::iterator it = d->catalogs.begin();
for (; it != d->catalogs.end(); ++it) {
int age = it->second->ageInSeconds();
unsigned int age = it->second->ageInSeconds();
if (aForce || (age > maxAgeSeconds())) {
toRefresh.push_back(it->second);
}
@@ -652,6 +658,7 @@ void Root::unregisterInstall(InstallRef ins)
}
d->m_installs.erase(ins->package());
d->fireFinishUninstall(ins->package());
}
} // of namespace pkg

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,6 +48,23 @@
<revision>10</revision>
</depends>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
<thumbnail>
<path>thumb-something.png</path>
<url>http://foo.bar.com/thumb-something.png</url>
</thumbnail>
<variant>
<id>c172p-2d-panel</id>
<name>C172 with 2d panel only</name>
@@ -56,11 +73,35 @@
<variant>
<id>c172p-floats</id>
<name>C172 with floats</name>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-floats.png</path>
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
</variant>
<variant>
<id>c172p-skis</id>
<name>C172 with skis</name>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-skis.png</path>
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
</variant>
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
@@ -78,6 +119,42 @@
<md5>acf9eb89cf396eb42f8823d9cdf17584</md5>
</package>
<package>
<id>b737-NG</id>
<name>Boeing 737 NG</name>
<dir>b737NG</dir>
<description>A popular twin-engined narrow body jet</description>
<revision type="int">112</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
</package>
<package>
<id>dc3</id>
<name>DC-3</name>

View File

@@ -48,6 +48,23 @@
<revision>10</revision>
</depends>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
<thumbnail>
<path>thumb-something.png</path>
<url>http://foo.bar.com/thumb-something.png</url>
</thumbnail>
<variant>
<id>c172p-2d-panel</id>
<name>C172 with 2d panel only</name>
@@ -56,11 +73,35 @@
<variant>
<id>c172p-floats</id>
<name>C172 with floats</name>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-floats.png</path>
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
</variant>
<variant>
<id>c172p-skis</id>
<name>C172 with skis</name>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-skis.png</path>
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
</thumbnail>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</thumbnail>
</variant>
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
@@ -68,6 +109,29 @@
</package>
<package>
<id>b737-NG</id>
<name>Boeing 737 NG</name>
<dir>b737NG</dir>
<description>A popular twin-engined narrow body jet</description>
<revision type="int">111</revision>
<file-size-bytes type="int">860</file-size-bytes>
<tag>boeing</tag>
<tag>jet</tag>
<tag>ifr</tag>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>
<model type="int">4</model>
<cockpit type="int">4</cockpit>
</rating>
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
</package>
<package>
<id>common-sounds</id>
<name>Common sound files for test catalog aircraft</name>

View File

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

View File

@@ -38,12 +38,6 @@ using std::cerr;
# include "PropertyInterpolationMgr.hxx"
# include "vectorPropTemplates.hxx"
# if ( _MSC_VER == 1200 )
// MSVC 6 is buggy, and needs something strange here
using std::vector<SGPropertyNode_ptr>;
using std::vector<SGPropertyChangeListener *>;
using std::vector<SGPropertyNode *>;
# endif
#endif
using std::endl;

View File

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

View File

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

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